From 298f00728d60f9c46cc2658e49b674647e1c1061 Mon Sep 17 00:00:00 2001 From: kyro <60421602+mucjustin@users.noreply.github.com> Date: Tue, 29 Sep 2026 16:10:09 +0800 Subject: [PATCH] fix: preserve workspace config and make analysis portable --- README.en.md | 3 ++- README.md | 3 ++- ml-analysis/mpa_ml_analysis.py | 3 ++- .../scripts/Initialize-MpaWorkspace.ps1 | 12 ++++++++++- tests/Test-WorkspaceScripts.ps1 | 20 +++++++++++++++++++ 5 files changed, 37 insertions(+), 4 deletions(-) diff --git a/README.en.md b/README.en.md index 7a7aa52..d9c6a78 100644 --- a/README.en.md +++ b/README.en.md @@ -56,7 +56,7 @@ Two behavioral gates run throughout: | | Feature | One-liner | |---|---|---| | 🧭 | MPA Research Spine | Eight-step verifiable research thread | -| 🛣️ | Ten research routes | Course/case/policy/lit/design/fieldwork/data/thesis/defence | +| 🛣️ | Twelve task routes | Course, case, policy, literature, design, fieldwork, data, thesis, defence, and related tasks | | 🔗 | Course-to-capstone reuse | Notes, cases, code become assets; re-verify before reuse | | 🧠 | MPA knowledge ontology | Theory map + China governance contexts + thinking checklist | | 🛡️ | Data before writing | Audit first, write second, never mask gaps | @@ -83,6 +83,7 @@ $skillRoot = Join-Path $HOME '.agents\skills\mpa-skill' ``` Default config goes to `%APPDATA%\mpa-skill\config.json`. Does not modify Zotero database or Obsidian settings. Add `-WhatIf` to preview; run `Test-MpaEnvironment.ps1` to check your setup. +If the config file already exists, initialization stops and preserves it. Pass a new `-ConfigPath` to create another configuration. ### Update and uninstall diff --git a/README.md b/README.md index 69c831e..f7e257d 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ | | 特性 | 一句话 | |---|---|---| | 🧭 | MPA Research Spine | 八步可核验研究主线,先识别缺口再动笔 | -| 🛣️ | 十条研究路由 | 课程/案例/政策/文献/设计/田野/数据/论文/答辩,按需组合 | +| 🛣️ | 十二条任务路由 | 课程、案例、政策、文献、设计、田野、数据、论文、答辩等,按需组合 | | 🔗 | 课程→毕业复用 | 笔记、案例、代码可沉淀为研究资产,复用前回原始来源核验 | | 🧠 | MPA 知识本体 | 理论地图 + 中国治理情境 + 思维清单 + 课程映射 | | 🛡️ | 数据先于写作 | 先审计再写作,不靠改写掩盖缺口 | @@ -83,6 +83,7 @@ $skillRoot = Join-Path $HOME '.agents\skills\mpa-skill' ``` 默认配置写入 `%APPDATA%\mpa-skill\config.json`,不修改 Zotero 数据库或 Obsidian 设置。预览操作加 `-WhatIf`;检查环境用 `Test-MpaEnvironment.ps1`。 +若配置文件已存在,初始化会停止并保留原文件;要创建另一套配置,请指定新的 `-ConfigPath`。 ### 更新与卸载 (Update & Uninstall) diff --git a/ml-analysis/mpa_ml_analysis.py b/ml-analysis/mpa_ml_analysis.py index 1555920..e9dc30f 100644 --- a/ml-analysis/mpa_ml_analysis.py +++ b/ml-analysis/mpa_ml_analysis.py @@ -5,6 +5,7 @@ """ import json, re, os, warnings +from pathlib import Path warnings.filterwarnings("ignore") import numpy as np @@ -25,7 +26,7 @@ from scipy.stats import chi2 # ── paths ── -REPO = r"E:\CodexProjects\mpa-skill-public" +REPO = Path(__file__).resolve().parents[1] OUT = os.path.join(REPO, "ml-analysis", "output") os.makedirs(OUT, exist_ok=True) diff --git a/skills/mpa-skill/scripts/Initialize-MpaWorkspace.ps1 b/skills/mpa-skill/scripts/Initialize-MpaWorkspace.ps1 index a74463c..7c3e031 100644 --- a/skills/mpa-skill/scripts/Initialize-MpaWorkspace.ps1 +++ b/skills/mpa-skill/scripts/Initialize-MpaWorkspace.ps1 @@ -30,6 +30,9 @@ if ([string]::IsNullOrWhiteSpace($ConfigPath)) { $ConfigPath = Join-Path $env:APPDATA 'mpa-skill\config.json' } $resolvedConfig = Resolve-FullPath $ConfigPath +if (Test-Path -LiteralPath $resolvedConfig) { + throw "ConfigPath already exists: $resolvedConfig. Choose another ConfigPath to preserve the existing configuration." +} if ([string]::IsNullOrWhiteSpace($ZoteroExecutable)) { $candidates = [System.Collections.Generic.List[string]]::new() @@ -75,7 +78,14 @@ if (-not $WhatIf) { [IO.Directory]::CreateDirectory($configDirectory) | Out-Null $json = [pscustomobject]$paths | ConvertTo-Json $utf8 = New-Object Text.UTF8Encoding($false) - [IO.File]::WriteAllText($resolvedConfig, $json + [Environment]::NewLine, $utf8) + $stream = [IO.File]::Open($resolvedConfig, [IO.FileMode]::CreateNew, [IO.FileAccess]::Write) + try { + $writer = New-Object IO.StreamWriter($stream, $utf8) + $writer.Write($json + [Environment]::NewLine) + $writer.Dispose() + } finally { + $stream.Dispose() + } } [pscustomobject]@{ diff --git a/tests/Test-WorkspaceScripts.ps1 b/tests/Test-WorkspaceScripts.ps1 index c6ebfdf..90d2a2f 100644 --- a/tests/Test-WorkspaceScripts.ps1 +++ b/tests/Test-WorkspaceScripts.ps1 @@ -50,6 +50,26 @@ try { Assert-True ($config.zotero_executable -ceq $zoteroPath) 'explicit Zotero executable was not preserved' Assert-True ($result.ConfigPath -ceq [IO.Path]::GetFullPath($configPath)) 'initializer summary config path differs' + $originalConfig = [IO.File]::ReadAllBytes($configPath) + $secondWorkspace = Join-Path $testRoot 'SecondWorkspace' + $existingConfigRejected = $false + try { + $null = & $initializeScript -WorkspaceRoot $secondWorkspace -ConfigPath $configPath + } catch { + $existingConfigRejected = $true + } + Assert-True $existingConfigRejected 'existing config was silently replaced' + Assert-True (-not (Test-Path -LiteralPath $secondWorkspace)) 'existing config rejection created a workspace' + Assert-True ([Convert]::ToBase64String([IO.File]::ReadAllBytes($configPath)) -ceq [Convert]::ToBase64String($originalConfig)) 'existing config changed' + + $whatIfRejected = $false + try { + $null = & $initializeScript -WorkspaceRoot $secondWorkspace -ConfigPath $configPath -WhatIf + } catch { + $whatIfRejected = $true + } + Assert-True $whatIfRejected 'WhatIf did not report existing config conflict' + $whatIfRoot = Join-Path $testRoot 'WhatIfWorkspace' $whatIfConfig = Join-Path $isolatedAppData 'what-if.json' $null = & $initializeScript -WorkspaceRoot $whatIfRoot -ConfigPath $whatIfConfig -WhatIf