maxsimcli 3.12.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/adapters/index.d.ts +0 -11
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +4 -40
- package/dist/adapters/index.js.map +1 -1
- package/dist/assets/CHANGELOG.md +17 -0
- package/dist/assets/dashboard/client/assets/{index-wtQDvXzr.js → index-C_eAetZJ.js} +60 -60
- package/dist/assets/dashboard/client/assets/index-CmiJKqOU.css +32 -0
- package/dist/assets/dashboard/client/index.html +2 -2
- package/dist/assets/dashboard/server.js +467 -271
- package/dist/assets/templates/agents/AGENTS.md +13 -1
- package/dist/assets/templates/agents/maxsim-debugger.md +2 -2
- package/dist/assets/templates/agents/maxsim-executor.md +5 -5
- package/dist/assets/templates/agents/maxsim-phase-researcher.md +2 -2
- package/dist/assets/templates/agents/maxsim-plan-checker.md +2 -2
- package/dist/assets/templates/agents/maxsim-planner.md +3 -3
- package/dist/assets/templates/commands/maxsim/add-todo.md +15 -5
- package/dist/assets/templates/commands/maxsim/discuss-phase.md +1 -0
- package/dist/assets/templates/commands/maxsim/init-existing.md +4 -0
- package/dist/assets/templates/commands/maxsim/new-project.md +4 -0
- package/dist/assets/templates/references/thinking-partner.md +41 -0
- package/dist/assets/templates/skills/batch-worktree/SKILL.md +137 -0
- package/dist/assets/templates/skills/brainstorming/SKILL.md +159 -0
- package/dist/assets/templates/skills/roadmap-writing/SKILL.md +198 -0
- package/dist/assets/templates/skills/sdd/SKILL.md +175 -0
- package/dist/assets/templates/skills/simplify/SKILL.md +48 -0
- package/dist/assets/templates/skills/using-maxsim/SKILL.md +6 -1
- package/dist/assets/templates/templates/acceptance-criteria.md +10 -0
- package/dist/assets/templates/templates/decisions.md +10 -0
- package/dist/assets/templates/templates/no-gos.md +9 -0
- package/dist/assets/templates/workflows/add-todo.md +89 -0
- package/dist/assets/templates/workflows/discuss-phase.md +85 -1
- package/dist/assets/templates/workflows/execute-phase.md +22 -2
- package/dist/assets/templates/workflows/execute-plan.md +166 -0
- package/dist/assets/templates/workflows/init-existing.md +116 -0
- package/dist/assets/templates/workflows/new-project.md +105 -1
- package/dist/assets/templates/workflows/plan-phase.md +3 -3
- package/dist/assets/templates/workflows/quick.md +2 -2
- package/dist/cli.cjs +1264 -882
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +97 -74
- package/dist/cli.js.map +1 -1
- package/dist/core/artefakte.d.ts +12 -0
- package/dist/core/artefakte.d.ts.map +1 -0
- package/dist/core/artefakte.js +136 -0
- package/dist/core/artefakte.js.map +1 -0
- package/dist/core/commands.d.ts +13 -13
- package/dist/core/commands.d.ts.map +1 -1
- package/dist/core/commands.js +44 -57
- package/dist/core/commands.js.map +1 -1
- package/dist/core/config.d.ts +4 -3
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +14 -18
- package/dist/core/config.js.map +1 -1
- package/dist/core/context-loader.d.ts +20 -0
- package/dist/core/context-loader.d.ts.map +1 -0
- package/dist/core/context-loader.js +154 -0
- package/dist/core/context-loader.js.map +1 -0
- package/dist/core/core.d.ts +8 -2
- package/dist/core/core.d.ts.map +1 -1
- package/dist/core/core.js +47 -11
- package/dist/core/core.js.map +1 -1
- package/dist/core/dashboard-launcher.d.ts +1 -1
- package/dist/core/dashboard-launcher.d.ts.map +1 -1
- package/dist/core/dashboard-launcher.js +18 -15
- package/dist/core/dashboard-launcher.js.map +1 -1
- package/dist/core/frontmatter.d.ts +5 -5
- package/dist/core/frontmatter.d.ts.map +1 -1
- package/dist/core/frontmatter.js +21 -26
- package/dist/core/frontmatter.js.map +1 -1
- package/dist/core/index.d.ts +8 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +23 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/init.d.ts +14 -14
- package/dist/core/init.d.ts.map +1 -1
- package/dist/core/init.js +93 -154
- package/dist/core/init.js.map +1 -1
- package/dist/core/milestone.d.ts +3 -3
- package/dist/core/milestone.d.ts.map +1 -1
- package/dist/core/milestone.js +9 -9
- package/dist/core/milestone.js.map +1 -1
- package/dist/core/phase.d.ts +9 -9
- package/dist/core/phase.d.ts.map +1 -1
- package/dist/core/phase.js +64 -68
- package/dist/core/phase.js.map +1 -1
- package/dist/core/roadmap.d.ts +4 -3
- package/dist/core/roadmap.d.ts.map +1 -1
- package/dist/core/roadmap.js +46 -109
- package/dist/core/roadmap.js.map +1 -1
- package/dist/core/skills.d.ts +19 -0
- package/dist/core/skills.d.ts.map +1 -0
- package/dist/core/skills.js +145 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/start.d.ts +15 -0
- package/dist/core/start.d.ts.map +1 -0
- package/dist/core/start.js +80 -0
- package/dist/core/start.js.map +1 -0
- package/dist/core/state.d.ts +13 -13
- package/dist/core/state.d.ts.map +1 -1
- package/dist/core/state.js +119 -126
- package/dist/core/state.js.map +1 -1
- package/dist/core/template.d.ts +3 -3
- package/dist/core/template.d.ts.map +1 -1
- package/dist/core/template.js +12 -14
- package/dist/core/template.js.map +1 -1
- package/dist/core/types.d.ts +14 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +8 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/verify.d.ts +10 -9
- package/dist/core/verify.d.ts.map +1 -1
- package/dist/core/verify.js +38 -48
- package/dist/core/verify.js.map +1 -1
- package/dist/core-TFSlUjV1.cjs +4312 -0
- package/dist/core-TFSlUjV1.cjs.map +1 -0
- package/dist/install/adapters.d.ts +2 -11
- package/dist/install/adapters.d.ts.map +1 -1
- package/dist/install/adapters.js +16 -154
- package/dist/install/adapters.js.map +1 -1
- package/dist/install/copy.d.ts +1 -10
- package/dist/install/copy.d.ts.map +1 -1
- package/dist/install/copy.js +5 -125
- package/dist/install/copy.js.map +1 -1
- package/dist/install/hooks.d.ts +4 -5
- package/dist/install/hooks.d.ts.map +1 -1
- package/dist/install/hooks.js +46 -71
- package/dist/install/hooks.js.map +1 -1
- package/dist/install/index.js +163 -226
- package/dist/install/index.js.map +1 -1
- package/dist/install/manifest.d.ts +5 -2
- package/dist/install/manifest.d.ts.map +1 -1
- package/dist/install/manifest.js +20 -26
- package/dist/install/manifest.js.map +1 -1
- package/dist/install/patches.d.ts +1 -2
- package/dist/install/patches.d.ts.map +1 -1
- package/dist/install/patches.js +4 -16
- package/dist/install/patches.js.map +1 -1
- package/dist/install/shared.d.ts +24 -18
- package/dist/install/shared.d.ts.map +1 -1
- package/dist/install/shared.js +65 -35
- package/dist/install/shared.js.map +1 -1
- package/dist/install/uninstall.d.ts +2 -3
- package/dist/install/uninstall.d.ts.map +1 -1
- package/dist/install/uninstall.js +24 -82
- package/dist/install/uninstall.js.map +1 -1
- package/dist/install.cjs +321 -1230
- package/dist/install.cjs.map +1 -1
- package/dist/mcp-server.cjs +38 -14
- package/dist/mcp-server.cjs.map +1 -1
- package/dist/skills-BOSxYUzf.cjs +6812 -0
- package/dist/skills-BOSxYUzf.cjs.map +1 -0
- package/package.json +1 -1
- package/dist/adapters/codex.d.ts +0 -19
- package/dist/adapters/codex.d.ts.map +0 -1
- package/dist/adapters/codex.js +0 -94
- package/dist/adapters/codex.js.map +0 -1
- package/dist/adapters/gemini.d.ts +0 -19
- package/dist/adapters/gemini.d.ts.map +0 -1
- package/dist/adapters/gemini.js +0 -96
- package/dist/adapters/gemini.js.map +0 -1
- package/dist/adapters/opencode.d.ts +0 -17
- package/dist/adapters/opencode.d.ts.map +0 -1
- package/dist/adapters/opencode.js +0 -111
- package/dist/adapters/opencode.js.map +0 -1
- package/dist/adapters/transforms/content.d.ts +0 -39
- package/dist/adapters/transforms/content.d.ts.map +0 -1
- package/dist/adapters/transforms/content.js +0 -125
- package/dist/adapters/transforms/content.js.map +0 -1
- package/dist/adapters/transforms/frontmatter.d.ts +0 -42
- package/dist/adapters/transforms/frontmatter.d.ts.map +0 -1
- package/dist/adapters/transforms/frontmatter.js +0 -204
- package/dist/adapters/transforms/frontmatter.js.map +0 -1
- package/dist/adapters/transforms/tool-maps.d.ts +0 -20
- package/dist/adapters/transforms/tool-maps.d.ts.map +0 -1
- package/dist/adapters/transforms/tool-maps.js +0 -64
- package/dist/adapters/transforms/tool-maps.js.map +0 -1
- package/dist/assets/dashboard/client/assets/index-CxFKStBk.css +0 -32
package/dist/cli.cjs
CHANGED
|
@@ -44,6 +44,19 @@ node_tty = __toESM(node_tty);
|
|
|
44
44
|
let node_module = require("node:module");
|
|
45
45
|
|
|
46
46
|
//#region src/core/types.ts
|
|
47
|
+
function cmdOk(result, rawValue) {
|
|
48
|
+
return {
|
|
49
|
+
ok: true,
|
|
50
|
+
result,
|
|
51
|
+
rawValue
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function cmdErr(error) {
|
|
55
|
+
return {
|
|
56
|
+
ok: false,
|
|
57
|
+
error
|
|
58
|
+
};
|
|
59
|
+
}
|
|
47
60
|
const PLANNING_CONFIG_DEFAULTS = {
|
|
48
61
|
model_profile: "balanced",
|
|
49
62
|
commit_docs: true,
|
|
@@ -4783,9 +4796,28 @@ function listSubDirs(dir, sortByPhase = false) {
|
|
|
4783
4796
|
const dirs = node_fs.default.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
4784
4797
|
return sortByPhase ? dirs.sort((a, b) => comparePhaseNum(a, b)) : dirs;
|
|
4785
4798
|
}
|
|
4786
|
-
/**
|
|
4787
|
-
function
|
|
4788
|
-
|
|
4799
|
+
/** Async version of listSubDirs using fs.promises. */
|
|
4800
|
+
async function listSubDirsAsync(dir, sortByPhase = false) {
|
|
4801
|
+
const dirs = (await node_fs.promises.readdir(dir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
4802
|
+
return sortByPhase ? dirs.sort((a, b) => comparePhaseNum(a, b)) : dirs;
|
|
4803
|
+
}
|
|
4804
|
+
/** Async version of safeReadFile using fs.promises. */
|
|
4805
|
+
async function safeReadFileAsync(filePath) {
|
|
4806
|
+
try {
|
|
4807
|
+
return await node_fs.promises.readFile(filePath, "utf-8");
|
|
4808
|
+
} catch {
|
|
4809
|
+
return null;
|
|
4810
|
+
}
|
|
4811
|
+
}
|
|
4812
|
+
/** Extract a human-readable message from an unknown thrown value. */
|
|
4813
|
+
function errorMsg(e) {
|
|
4814
|
+
return e instanceof Error ? e.message : String(e);
|
|
4815
|
+
}
|
|
4816
|
+
/** Log only when MAXSIM_DEBUG is set. Accepts an optional context label. */
|
|
4817
|
+
function debugLog(contextOrError, error) {
|
|
4818
|
+
if (!process.env.MAXSIM_DEBUG) return;
|
|
4819
|
+
if (error !== void 0) console.error(`[maxsim:${contextOrError}]`, error);
|
|
4820
|
+
else console.error(contextOrError);
|
|
4789
4821
|
}
|
|
4790
4822
|
/** Escape a phase number for use in regex. */
|
|
4791
4823
|
function escapePhaseNum(phase) {
|
|
@@ -4877,7 +4909,11 @@ function loadConfig(cwd) {
|
|
|
4877
4909
|
config: result
|
|
4878
4910
|
};
|
|
4879
4911
|
return result;
|
|
4880
|
-
} catch {
|
|
4912
|
+
} catch (e) {
|
|
4913
|
+
if (node_fs.default.existsSync(cfgPath)) {
|
|
4914
|
+
console.warn(`[maxsim] Warning: config.json exists but could not be parsed — using defaults.`);
|
|
4915
|
+
debugLog("config-load-failed", e);
|
|
4916
|
+
}
|
|
4881
4917
|
_configCache = {
|
|
4882
4918
|
cwd,
|
|
4883
4919
|
config: defaults
|
|
@@ -4900,10 +4936,15 @@ async function execGit(cwd, args) {
|
|
|
4900
4936
|
stderr: ""
|
|
4901
4937
|
};
|
|
4902
4938
|
} catch (thrown) {
|
|
4939
|
+
const message = thrown instanceof Error ? thrown.message : String(thrown);
|
|
4940
|
+
debugLog("exec-git-failed", {
|
|
4941
|
+
args,
|
|
4942
|
+
error: message
|
|
4943
|
+
});
|
|
4903
4944
|
return {
|
|
4904
4945
|
exitCode: 1,
|
|
4905
4946
|
stdout: "",
|
|
4906
|
-
stderr:
|
|
4947
|
+
stderr: message
|
|
4907
4948
|
};
|
|
4908
4949
|
}
|
|
4909
4950
|
}
|
|
@@ -4978,7 +5019,12 @@ function searchPhaseInDir(baseDir, relBase, normalized) {
|
|
|
4978
5019
|
has_context: hasContext,
|
|
4979
5020
|
has_verification: hasVerification
|
|
4980
5021
|
};
|
|
4981
|
-
} catch {
|
|
5022
|
+
} catch (e) {
|
|
5023
|
+
debugLog("search-phase-in-dir-failed", {
|
|
5024
|
+
dir: baseDir,
|
|
5025
|
+
phase: normalized,
|
|
5026
|
+
error: errorMsg(e)
|
|
5027
|
+
});
|
|
4982
5028
|
return null;
|
|
4983
5029
|
}
|
|
4984
5030
|
}
|
|
@@ -5007,7 +5053,7 @@ function findPhaseInternal(cwd, phase) {
|
|
|
5007
5053
|
}
|
|
5008
5054
|
}
|
|
5009
5055
|
} catch (e) {
|
|
5010
|
-
debugLog(e);
|
|
5056
|
+
debugLog("find-phase-milestone-search-failed", e);
|
|
5011
5057
|
}
|
|
5012
5058
|
return null;
|
|
5013
5059
|
}
|
|
@@ -5030,7 +5076,7 @@ function getArchivedPhaseDirs(cwd) {
|
|
|
5030
5076
|
});
|
|
5031
5077
|
}
|
|
5032
5078
|
} catch (e) {
|
|
5033
|
-
debugLog(e);
|
|
5079
|
+
debugLog("get-archived-phase-dirs-failed", e);
|
|
5034
5080
|
}
|
|
5035
5081
|
return results;
|
|
5036
5082
|
}
|
|
@@ -5056,7 +5102,11 @@ function getRoadmapPhaseInternal(cwd, phaseNum) {
|
|
|
5056
5102
|
goal,
|
|
5057
5103
|
section
|
|
5058
5104
|
};
|
|
5059
|
-
} catch {
|
|
5105
|
+
} catch (e) {
|
|
5106
|
+
debugLog("get-roadmap-phase-failed", {
|
|
5107
|
+
phase: phaseNum,
|
|
5108
|
+
error: errorMsg(e)
|
|
5109
|
+
});
|
|
5060
5110
|
return null;
|
|
5061
5111
|
}
|
|
5062
5112
|
}
|
|
@@ -11832,39 +11882,30 @@ const FRONTMATTER_SCHEMAS = {
|
|
|
11832
11882
|
"score"
|
|
11833
11883
|
] }
|
|
11834
11884
|
};
|
|
11835
|
-
function cmdFrontmatterGet(cwd, filePath, field
|
|
11836
|
-
if (!filePath)
|
|
11885
|
+
function cmdFrontmatterGet(cwd, filePath, field) {
|
|
11886
|
+
if (!filePath) return cmdErr("file path required");
|
|
11837
11887
|
const content = safeReadFile(node_path.default.isAbsolute(filePath) ? filePath : node_path.default.join(cwd, filePath));
|
|
11838
|
-
if (!content) {
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
}, raw);
|
|
11843
|
-
return;
|
|
11844
|
-
}
|
|
11888
|
+
if (!content) return cmdOk({
|
|
11889
|
+
error: "File not found",
|
|
11890
|
+
path: filePath
|
|
11891
|
+
});
|
|
11845
11892
|
const fm = extractFrontmatter(content);
|
|
11846
11893
|
if (field) {
|
|
11847
11894
|
const value = fm[field];
|
|
11848
|
-
if (value === void 0) {
|
|
11849
|
-
|
|
11850
|
-
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
|
|
11854
|
-
}
|
|
11855
|
-
output({ [field]: value }, raw, JSON.stringify(value));
|
|
11856
|
-
} else output(fm, raw);
|
|
11895
|
+
if (value === void 0) return cmdOk({
|
|
11896
|
+
error: "Field not found",
|
|
11897
|
+
field
|
|
11898
|
+
});
|
|
11899
|
+
return cmdOk({ [field]: value }, JSON.stringify(value));
|
|
11900
|
+
} else return cmdOk(fm);
|
|
11857
11901
|
}
|
|
11858
|
-
function cmdFrontmatterSet(cwd, filePath, field, value
|
|
11859
|
-
if (!filePath || !field || value === void 0)
|
|
11902
|
+
function cmdFrontmatterSet(cwd, filePath, field, value) {
|
|
11903
|
+
if (!filePath || !field || value === void 0) return cmdErr("file, field, and value required");
|
|
11860
11904
|
const fullPath = node_path.default.isAbsolute(filePath) ? filePath : node_path.default.join(cwd, filePath);
|
|
11861
|
-
if (!node_fs.default.existsSync(fullPath)) {
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
}, raw);
|
|
11866
|
-
return;
|
|
11867
|
-
}
|
|
11905
|
+
if (!node_fs.default.existsSync(fullPath)) return cmdOk({
|
|
11906
|
+
error: "File not found",
|
|
11907
|
+
path: filePath
|
|
11908
|
+
});
|
|
11868
11909
|
const content = node_fs.default.readFileSync(fullPath, "utf-8");
|
|
11869
11910
|
const fm = extractFrontmatter(content);
|
|
11870
11911
|
let parsedValue;
|
|
@@ -11876,60 +11917,53 @@ function cmdFrontmatterSet(cwd, filePath, field, value, raw) {
|
|
|
11876
11917
|
fm[field] = parsedValue;
|
|
11877
11918
|
const newContent = spliceFrontmatter(content, fm);
|
|
11878
11919
|
node_fs.default.writeFileSync(fullPath, newContent, "utf-8");
|
|
11879
|
-
|
|
11920
|
+
return cmdOk({
|
|
11880
11921
|
updated: true,
|
|
11881
11922
|
field,
|
|
11882
11923
|
value: parsedValue
|
|
11883
|
-
},
|
|
11924
|
+
}, "true");
|
|
11884
11925
|
}
|
|
11885
|
-
function cmdFrontmatterMerge(cwd, filePath, data
|
|
11886
|
-
if (!filePath || !data)
|
|
11926
|
+
function cmdFrontmatterMerge(cwd, filePath, data) {
|
|
11927
|
+
if (!filePath || !data) return cmdErr("file and data required");
|
|
11887
11928
|
const fullPath = node_path.default.isAbsolute(filePath) ? filePath : node_path.default.join(cwd, filePath);
|
|
11888
|
-
if (!node_fs.default.existsSync(fullPath)) {
|
|
11889
|
-
|
|
11890
|
-
|
|
11891
|
-
|
|
11892
|
-
}, raw);
|
|
11893
|
-
return;
|
|
11894
|
-
}
|
|
11929
|
+
if (!node_fs.default.existsSync(fullPath)) return cmdOk({
|
|
11930
|
+
error: "File not found",
|
|
11931
|
+
path: filePath
|
|
11932
|
+
});
|
|
11895
11933
|
const content = node_fs.default.readFileSync(fullPath, "utf-8");
|
|
11896
11934
|
const fm = extractFrontmatter(content);
|
|
11897
11935
|
let mergeData;
|
|
11898
11936
|
try {
|
|
11899
11937
|
mergeData = JSON.parse(data);
|
|
11900
11938
|
} catch {
|
|
11901
|
-
|
|
11902
|
-
return;
|
|
11939
|
+
return cmdErr("Invalid JSON for --data");
|
|
11903
11940
|
}
|
|
11904
11941
|
Object.assign(fm, mergeData);
|
|
11905
11942
|
const newContent = spliceFrontmatter(content, fm);
|
|
11906
11943
|
node_fs.default.writeFileSync(fullPath, newContent, "utf-8");
|
|
11907
|
-
|
|
11944
|
+
return cmdOk({
|
|
11908
11945
|
merged: true,
|
|
11909
11946
|
fields: Object.keys(mergeData)
|
|
11910
|
-
},
|
|
11947
|
+
}, "true");
|
|
11911
11948
|
}
|
|
11912
|
-
function cmdFrontmatterValidate(cwd, filePath, schemaName
|
|
11913
|
-
if (!filePath || !schemaName)
|
|
11949
|
+
function cmdFrontmatterValidate(cwd, filePath, schemaName) {
|
|
11950
|
+
if (!filePath || !schemaName) return cmdErr("file and schema required");
|
|
11914
11951
|
const schema = FRONTMATTER_SCHEMAS[schemaName];
|
|
11915
|
-
if (!schema)
|
|
11952
|
+
if (!schema) return cmdErr(`Unknown schema: ${schemaName}. Available: ${Object.keys(FRONTMATTER_SCHEMAS).join(", ")}`);
|
|
11916
11953
|
const content = safeReadFile(node_path.default.isAbsolute(filePath) ? filePath : node_path.default.join(cwd, filePath));
|
|
11917
|
-
if (!content) {
|
|
11918
|
-
|
|
11919
|
-
|
|
11920
|
-
|
|
11921
|
-
}, raw);
|
|
11922
|
-
return;
|
|
11923
|
-
}
|
|
11954
|
+
if (!content) return cmdOk({
|
|
11955
|
+
error: "File not found",
|
|
11956
|
+
path: filePath
|
|
11957
|
+
});
|
|
11924
11958
|
const fm = extractFrontmatter(content);
|
|
11925
11959
|
const missing = schema.required.filter((f) => fm[f] === void 0);
|
|
11926
11960
|
const present = schema.required.filter((f) => fm[f] !== void 0);
|
|
11927
|
-
|
|
11961
|
+
return cmdOk({
|
|
11928
11962
|
valid: missing.length === 0,
|
|
11929
11963
|
missing,
|
|
11930
11964
|
present,
|
|
11931
11965
|
schema: schemaName
|
|
11932
|
-
},
|
|
11966
|
+
}, missing.length === 0 ? "valid" : "invalid");
|
|
11933
11967
|
}
|
|
11934
11968
|
|
|
11935
11969
|
//#endregion
|
|
@@ -11945,15 +11979,12 @@ function cmdConfigEnsureSection(cwd, raw) {
|
|
|
11945
11979
|
try {
|
|
11946
11980
|
if (!node_fs.default.existsSync(planningDir)) node_fs.default.mkdirSync(planningDir, { recursive: true });
|
|
11947
11981
|
} catch (err) {
|
|
11948
|
-
|
|
11949
|
-
}
|
|
11950
|
-
if (node_fs.default.existsSync(configPath)) {
|
|
11951
|
-
output({
|
|
11952
|
-
created: false,
|
|
11953
|
-
reason: "already_exists"
|
|
11954
|
-
}, raw, "exists");
|
|
11955
|
-
return;
|
|
11982
|
+
return cmdErr("Failed to create .planning directory: " + err.message);
|
|
11956
11983
|
}
|
|
11984
|
+
if (node_fs.default.existsSync(configPath)) return cmdOk({
|
|
11985
|
+
created: false,
|
|
11986
|
+
reason: "already_exists"
|
|
11987
|
+
}, raw ? "exists" : void 0);
|
|
11957
11988
|
const homedir = node_os.default.homedir();
|
|
11958
11989
|
const braveKeyFile = node_path.default.join(homedir, ".maxsim", "brave_api_key");
|
|
11959
11990
|
const hasBraveSearch = !!(process.env.BRAVE_API_KEY || node_fs.default.existsSync(braveKeyFile));
|
|
@@ -11976,17 +12007,17 @@ function cmdConfigEnsureSection(cwd, raw) {
|
|
|
11976
12007
|
};
|
|
11977
12008
|
try {
|
|
11978
12009
|
node_fs.default.writeFileSync(configPath, JSON.stringify(defaults, null, 2), "utf-8");
|
|
11979
|
-
|
|
12010
|
+
return cmdOk({
|
|
11980
12011
|
created: true,
|
|
11981
12012
|
path: ".planning/config.json"
|
|
11982
|
-
}, raw
|
|
12013
|
+
}, raw ? "created" : void 0);
|
|
11983
12014
|
} catch (err) {
|
|
11984
|
-
|
|
12015
|
+
return cmdErr("Failed to create config.json: " + err.message);
|
|
11985
12016
|
}
|
|
11986
12017
|
}
|
|
11987
12018
|
function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
11988
12019
|
const configPath = node_path.default.join(cwd, ".planning", "config.json");
|
|
11989
|
-
if (!keyPath)
|
|
12020
|
+
if (!keyPath) return cmdErr("Usage: config-set <key.path> <value>");
|
|
11990
12021
|
let parsedValue = value;
|
|
11991
12022
|
if (value === "true") parsedValue = true;
|
|
11992
12023
|
else if (value === "false") parsedValue = false;
|
|
@@ -11995,7 +12026,7 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
|
11995
12026
|
try {
|
|
11996
12027
|
if (node_fs.default.existsSync(configPath)) config = JSON.parse(node_fs.default.readFileSync(configPath, "utf-8"));
|
|
11997
12028
|
} catch (err) {
|
|
11998
|
-
|
|
12029
|
+
return cmdErr("Failed to read config.json: " + err.message);
|
|
11999
12030
|
}
|
|
12000
12031
|
const keys = keyPath.split(".");
|
|
12001
12032
|
let current = config;
|
|
@@ -12007,34 +12038,33 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
|
12007
12038
|
current[keys[keys.length - 1]] = parsedValue;
|
|
12008
12039
|
try {
|
|
12009
12040
|
node_fs.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
12010
|
-
|
|
12041
|
+
return cmdOk({
|
|
12011
12042
|
updated: true,
|
|
12012
12043
|
key: keyPath,
|
|
12013
12044
|
value: parsedValue
|
|
12014
|
-
}, raw
|
|
12045
|
+
}, raw ? `${keyPath}=${parsedValue}` : void 0);
|
|
12015
12046
|
} catch (err) {
|
|
12016
|
-
|
|
12047
|
+
return cmdErr("Failed to write config.json: " + err.message);
|
|
12017
12048
|
}
|
|
12018
12049
|
}
|
|
12019
12050
|
function cmdConfigGet(cwd, keyPath, raw) {
|
|
12020
12051
|
const configPath = node_path.default.join(cwd, ".planning", "config.json");
|
|
12021
|
-
if (!keyPath)
|
|
12052
|
+
if (!keyPath) return cmdErr("Usage: config-get <key.path>");
|
|
12022
12053
|
let config = {};
|
|
12023
12054
|
try {
|
|
12024
12055
|
if (node_fs.default.existsSync(configPath)) config = JSON.parse(node_fs.default.readFileSync(configPath, "utf-8"));
|
|
12025
|
-
else
|
|
12056
|
+
else return cmdErr("No config.json found at " + configPath);
|
|
12026
12057
|
} catch (err) {
|
|
12027
|
-
|
|
12028
|
-
error("Failed to read config.json: " + err.message);
|
|
12058
|
+
return cmdErr("Failed to read config.json: " + err.message);
|
|
12029
12059
|
}
|
|
12030
12060
|
const keys = keyPath.split(".");
|
|
12031
12061
|
let current = config;
|
|
12032
12062
|
for (const key of keys) {
|
|
12033
|
-
if (current === void 0 || current === null || typeof current !== "object")
|
|
12063
|
+
if (current === void 0 || current === null || typeof current !== "object") return cmdErr(`Key not found: ${keyPath}`);
|
|
12034
12064
|
current = current[key];
|
|
12035
12065
|
}
|
|
12036
|
-
if (current === void 0)
|
|
12037
|
-
|
|
12066
|
+
if (current === void 0) return cmdErr(`Key not found: ${keyPath}`);
|
|
12067
|
+
return cmdOk(current, raw ? String(current) : void 0);
|
|
12038
12068
|
}
|
|
12039
12069
|
|
|
12040
12070
|
//#endregion
|
|
@@ -12051,16 +12081,31 @@ function escapeStringRegexp(string) {
|
|
|
12051
12081
|
*
|
|
12052
12082
|
* Ported from maxsim/bin/lib/state.cjs
|
|
12053
12083
|
*/
|
|
12084
|
+
/**
|
|
12085
|
+
* Parse a markdown table row into cells, handling escaped pipes (`\|`) within cell content.
|
|
12086
|
+
* Strips leading/trailing pipe characters and trims each cell.
|
|
12087
|
+
*/
|
|
12088
|
+
function parseTableRow(row) {
|
|
12089
|
+
const placeholder = "\0PIPE\0";
|
|
12090
|
+
return row.replace(/\\\|/g, placeholder).split("|").map((c) => c.replaceAll(placeholder, "|").trim()).filter(Boolean);
|
|
12091
|
+
}
|
|
12054
12092
|
function stateExtractField(content, fieldName) {
|
|
12055
|
-
const
|
|
12056
|
-
const
|
|
12057
|
-
|
|
12093
|
+
const escaped = escapeStringRegexp(fieldName);
|
|
12094
|
+
const boldPattern = new RegExp(`\\*\\*\\s*${escaped}\\s*:\\s*\\*\\*\\s*(.+)`, "i");
|
|
12095
|
+
const boldMatch = content.match(boldPattern);
|
|
12096
|
+
if (boldMatch) return boldMatch[1].trim();
|
|
12097
|
+
const plainPattern = new RegExp(`^\\s*${escaped}\\s*:\\s*(.+)`, "im");
|
|
12098
|
+
const plainMatch = content.match(plainPattern);
|
|
12099
|
+
return plainMatch ? plainMatch[1].trim() : null;
|
|
12058
12100
|
}
|
|
12059
12101
|
function stateReplaceField(content, fieldName, newValue) {
|
|
12060
12102
|
const escaped = escapeStringRegexp(fieldName);
|
|
12061
|
-
const
|
|
12062
|
-
|
|
12063
|
-
return
|
|
12103
|
+
const boldPattern = new RegExp(`(\\*\\*\\s*${escaped}\\s*:\\s*\\*\\*\\s*)(.*)`, "i");
|
|
12104
|
+
let replaced = content.replace(boldPattern, (_match, prefix) => `${prefix}${newValue}`);
|
|
12105
|
+
if (replaced !== content) return replaced;
|
|
12106
|
+
const plainPattern = new RegExp(`(^[ \\t]*${escaped}\\s*:\\s*)(.*)`, "im");
|
|
12107
|
+
replaced = content.replace(plainPattern, (_match, prefix) => `${prefix}${newValue}`);
|
|
12108
|
+
return replaced !== content ? replaced : null;
|
|
12064
12109
|
}
|
|
12065
12110
|
function readTextArgOrFile(cwd, value, filePath, label) {
|
|
12066
12111
|
if (!filePath) return value;
|
|
@@ -12087,16 +12132,14 @@ function appendToStateSection(content, sectionPattern, entry, placeholderPattern
|
|
|
12087
12132
|
sectionBody = sectionBody.trimEnd() + "\n" + entry + "\n";
|
|
12088
12133
|
return content.replace(sectionPattern, (_m, header) => `${header}${sectionBody}`);
|
|
12089
12134
|
}
|
|
12090
|
-
function cmdStateLoad(cwd, raw) {
|
|
12135
|
+
async function cmdStateLoad(cwd, raw) {
|
|
12091
12136
|
const config = loadConfig(cwd);
|
|
12092
|
-
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
|
|
12096
|
-
|
|
12097
|
-
|
|
12098
|
-
const configExists = node_fs.default.existsSync(configPath(cwd));
|
|
12099
|
-
const roadmapExists = node_fs.default.existsSync(roadmapPath(cwd));
|
|
12137
|
+
const [stateContent, configExists, roadmapExists] = await Promise.all([
|
|
12138
|
+
safeReadFileAsync(statePath(cwd)),
|
|
12139
|
+
node_fs.default.promises.access(configPath(cwd)).then(() => true, () => false),
|
|
12140
|
+
node_fs.default.promises.access(roadmapPath(cwd)).then(() => true, () => false)
|
|
12141
|
+
]);
|
|
12142
|
+
const stateRaw = stateContent ?? "";
|
|
12100
12143
|
const stateExists = stateRaw.length > 0;
|
|
12101
12144
|
const result = {
|
|
12102
12145
|
config,
|
|
@@ -12107,7 +12150,7 @@ function cmdStateLoad(cwd, raw) {
|
|
|
12107
12150
|
};
|
|
12108
12151
|
if (raw) {
|
|
12109
12152
|
const c = config;
|
|
12110
|
-
|
|
12153
|
+
return cmdOk(result, [
|
|
12111
12154
|
`model_profile=${c.model_profile}`,
|
|
12112
12155
|
`commit_docs=${c.commit_docs}`,
|
|
12113
12156
|
`branching_strategy=${c.branching_strategy}`,
|
|
@@ -12122,33 +12165,23 @@ function cmdStateLoad(cwd, raw) {
|
|
|
12122
12165
|
`state_exists=${stateExists}`
|
|
12123
12166
|
].join("\n"));
|
|
12124
12167
|
}
|
|
12125
|
-
|
|
12168
|
+
return cmdOk(result);
|
|
12126
12169
|
}
|
|
12127
12170
|
function cmdStateGet(cwd, section, raw) {
|
|
12128
12171
|
const statePath$2 = statePath(cwd);
|
|
12129
12172
|
try {
|
|
12130
12173
|
const content = node_fs.default.readFileSync(statePath$2, "utf-8");
|
|
12131
|
-
if (!section) {
|
|
12132
|
-
|
|
12133
|
-
|
|
12134
|
-
}
|
|
12174
|
+
if (!section) return cmdOk({ content }, raw ? content : void 0);
|
|
12175
|
+
const fieldValue = stateExtractField(content, section);
|
|
12176
|
+
if (fieldValue !== null) return cmdOk({ [section]: fieldValue }, raw ? fieldValue : void 0);
|
|
12135
12177
|
const fieldEscaped = escapeStringRegexp(section);
|
|
12136
|
-
const
|
|
12137
|
-
const fieldMatch = content.match(fieldPattern);
|
|
12138
|
-
if (fieldMatch) {
|
|
12139
|
-
output({ [section]: fieldMatch[1].trim() }, raw, fieldMatch[1].trim());
|
|
12140
|
-
return;
|
|
12141
|
-
}
|
|
12142
|
-
const sectionPattern = new RegExp(`##\\s*${fieldEscaped}\\s*\n([\\s\\S]*?)(?=\\n##|$)`, "i");
|
|
12178
|
+
const sectionPattern = new RegExp(`#{2,3}\\s*${fieldEscaped}\\s*\\n\\s*\\n?([\\s\\S]*?)(?=\\n#{2,3}\\s|$)`, "i");
|
|
12143
12179
|
const sectionMatch = content.match(sectionPattern);
|
|
12144
|
-
if (sectionMatch) {
|
|
12145
|
-
|
|
12146
|
-
return;
|
|
12147
|
-
}
|
|
12148
|
-
output({ error: `Section or field "${section}" not found` }, raw, "");
|
|
12180
|
+
if (sectionMatch) return cmdOk({ [section]: sectionMatch[1].trim() }, raw ? sectionMatch[1].trim() : void 0);
|
|
12181
|
+
return cmdOk({ error: `Section or field "${section}" not found` }, raw ? "" : void 0);
|
|
12149
12182
|
} catch (e) {
|
|
12150
12183
|
rethrowCliSignals(e);
|
|
12151
|
-
|
|
12184
|
+
return cmdErr("STATE.md not found");
|
|
12152
12185
|
}
|
|
12153
12186
|
}
|
|
12154
12187
|
function cmdStatePatch(cwd, patches, raw) {
|
|
@@ -12160,38 +12193,34 @@ function cmdStatePatch(cwd, patches, raw) {
|
|
|
12160
12193
|
failed: []
|
|
12161
12194
|
};
|
|
12162
12195
|
for (const [field, value] of Object.entries(patches)) {
|
|
12163
|
-
const
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
content = content.replace(pattern, (_match, prefix) => `${prefix}${value}`);
|
|
12196
|
+
const result = stateReplaceField(content, field, value);
|
|
12197
|
+
if (result) {
|
|
12198
|
+
content = result;
|
|
12167
12199
|
results.updated.push(field);
|
|
12168
12200
|
} else results.failed.push(field);
|
|
12169
12201
|
}
|
|
12170
12202
|
if (results.updated.length > 0) node_fs.default.writeFileSync(statePath$3, content, "utf-8");
|
|
12171
|
-
|
|
12203
|
+
return cmdOk(results, raw ? results.updated.length > 0 ? "true" : "false" : void 0);
|
|
12172
12204
|
} catch (e) {
|
|
12173
12205
|
rethrowCliSignals(e);
|
|
12174
|
-
|
|
12206
|
+
return cmdErr("STATE.md not found");
|
|
12175
12207
|
}
|
|
12176
12208
|
}
|
|
12177
12209
|
function cmdStateUpdate(cwd, field, value) {
|
|
12178
|
-
if (!field || value === void 0)
|
|
12210
|
+
if (!field || value === void 0) return cmdErr("field and value required for state update");
|
|
12179
12211
|
const statePath$4 = statePath(cwd);
|
|
12180
12212
|
try {
|
|
12181
|
-
|
|
12182
|
-
|
|
12183
|
-
|
|
12184
|
-
|
|
12185
|
-
|
|
12186
|
-
node_fs.default.writeFileSync(statePath$4, content, "utf-8");
|
|
12187
|
-
output({ updated: true });
|
|
12188
|
-
} else output({
|
|
12213
|
+
const result = stateReplaceField(node_fs.default.readFileSync(statePath$4, "utf-8"), field, value);
|
|
12214
|
+
if (result) {
|
|
12215
|
+
node_fs.default.writeFileSync(statePath$4, result, "utf-8");
|
|
12216
|
+
return cmdOk({ updated: true });
|
|
12217
|
+
} else return cmdOk({
|
|
12189
12218
|
updated: false,
|
|
12190
12219
|
reason: `Field "${field}" not found in STATE.md`
|
|
12191
12220
|
});
|
|
12192
12221
|
} catch (e) {
|
|
12193
12222
|
rethrowCliSignals(e);
|
|
12194
|
-
|
|
12223
|
+
return cmdOk({
|
|
12195
12224
|
updated: false,
|
|
12196
12225
|
reason: "STATE.md not found"
|
|
12197
12226
|
});
|
|
@@ -12199,56 +12228,44 @@ function cmdStateUpdate(cwd, field, value) {
|
|
|
12199
12228
|
}
|
|
12200
12229
|
function cmdStateAdvancePlan(cwd, raw) {
|
|
12201
12230
|
const statePath$5 = statePath(cwd);
|
|
12202
|
-
if (!node_fs.default.existsSync(statePath$5)) {
|
|
12203
|
-
output({ error: "STATE.md not found" }, raw);
|
|
12204
|
-
return;
|
|
12205
|
-
}
|
|
12231
|
+
if (!node_fs.default.existsSync(statePath$5)) return cmdOk({ error: "STATE.md not found" });
|
|
12206
12232
|
let content = node_fs.default.readFileSync(statePath$5, "utf-8");
|
|
12207
12233
|
const currentPlan = parseInt(stateExtractField(content, "Current Plan") ?? "", 10);
|
|
12208
12234
|
const totalPlans = parseInt(stateExtractField(content, "Total Plans in Phase") ?? "", 10);
|
|
12209
12235
|
const today = todayISO();
|
|
12210
|
-
if (isNaN(currentPlan) || isNaN(totalPlans)) {
|
|
12211
|
-
output({ error: "Cannot parse Current Plan or Total Plans in Phase from STATE.md" }, raw);
|
|
12212
|
-
return;
|
|
12213
|
-
}
|
|
12236
|
+
if (isNaN(currentPlan) || isNaN(totalPlans)) return cmdOk({ error: "Cannot parse Current Plan or Total Plans in Phase from STATE.md" });
|
|
12214
12237
|
if (currentPlan >= totalPlans) {
|
|
12215
12238
|
content = stateReplaceField(content, "Status", "Phase complete — ready for verification") || content;
|
|
12216
12239
|
content = stateReplaceField(content, "Last Activity", today) || content;
|
|
12217
12240
|
node_fs.default.writeFileSync(statePath$5, content, "utf-8");
|
|
12218
|
-
|
|
12241
|
+
return cmdOk({
|
|
12219
12242
|
advanced: false,
|
|
12220
12243
|
reason: "last_plan",
|
|
12221
12244
|
current_plan: currentPlan,
|
|
12222
12245
|
total_plans: totalPlans,
|
|
12223
12246
|
status: "ready_for_verification"
|
|
12224
|
-
}, raw
|
|
12247
|
+
}, raw ? "false" : void 0);
|
|
12225
12248
|
} else {
|
|
12226
12249
|
const newPlan = currentPlan + 1;
|
|
12227
12250
|
content = stateReplaceField(content, "Current Plan", String(newPlan)) || content;
|
|
12228
12251
|
content = stateReplaceField(content, "Status", "Ready to execute") || content;
|
|
12229
12252
|
content = stateReplaceField(content, "Last Activity", today) || content;
|
|
12230
12253
|
node_fs.default.writeFileSync(statePath$5, content, "utf-8");
|
|
12231
|
-
|
|
12254
|
+
return cmdOk({
|
|
12232
12255
|
advanced: true,
|
|
12233
12256
|
previous_plan: currentPlan,
|
|
12234
12257
|
current_plan: newPlan,
|
|
12235
12258
|
total_plans: totalPlans
|
|
12236
|
-
}, raw
|
|
12259
|
+
}, raw ? "true" : void 0);
|
|
12237
12260
|
}
|
|
12238
12261
|
}
|
|
12239
12262
|
function cmdStateRecordMetric(cwd, options, raw) {
|
|
12240
12263
|
const statePath$6 = statePath(cwd);
|
|
12241
|
-
if (!node_fs.default.existsSync(statePath$6)) {
|
|
12242
|
-
output({ error: "STATE.md not found" }, raw);
|
|
12243
|
-
return;
|
|
12244
|
-
}
|
|
12264
|
+
if (!node_fs.default.existsSync(statePath$6)) return cmdOk({ error: "STATE.md not found" });
|
|
12245
12265
|
let content = node_fs.default.readFileSync(statePath$6, "utf-8");
|
|
12246
12266
|
const { phase, plan, duration, tasks, files } = options;
|
|
12247
|
-
if (!phase || !plan || !duration) {
|
|
12248
|
-
|
|
12249
|
-
return;
|
|
12250
|
-
}
|
|
12251
|
-
const metricsPattern = /(##\s*Performance Metrics[\s\S]*?\n\|[^\n]+\n\|[-|\s]+\n)([\s\S]*?)(?=\n##|\n$|$)/i;
|
|
12267
|
+
if (!phase || !plan || !duration) return cmdOk({ error: "phase, plan, and duration required" });
|
|
12268
|
+
const metricsPattern = /(#{2,3}\s*Performance Metrics[\s\S]*?\n\|[^\n]+\n\|[\s:|\-]+\n)([\s\S]*?)(?=\n#{2,3}\s|\n$|$)/i;
|
|
12252
12269
|
const metricsMatch = content.match(metricsPattern);
|
|
12253
12270
|
if (metricsMatch) {
|
|
12254
12271
|
let tableBody = metricsMatch[2].trimEnd();
|
|
@@ -12257,23 +12274,20 @@ function cmdStateRecordMetric(cwd, options, raw) {
|
|
|
12257
12274
|
else tableBody = tableBody + "\n" + newRow;
|
|
12258
12275
|
content = content.replace(metricsPattern, (_match, header) => `${header}${tableBody}\n`);
|
|
12259
12276
|
node_fs.default.writeFileSync(statePath$6, content, "utf-8");
|
|
12260
|
-
|
|
12277
|
+
return cmdOk({
|
|
12261
12278
|
recorded: true,
|
|
12262
12279
|
phase,
|
|
12263
12280
|
plan,
|
|
12264
12281
|
duration
|
|
12265
|
-
}, raw
|
|
12266
|
-
} else
|
|
12282
|
+
}, raw ? "true" : void 0);
|
|
12283
|
+
} else return cmdOk({
|
|
12267
12284
|
recorded: false,
|
|
12268
12285
|
reason: "Performance Metrics section not found in STATE.md"
|
|
12269
|
-
}, raw
|
|
12286
|
+
}, raw ? "false" : void 0);
|
|
12270
12287
|
}
|
|
12271
12288
|
function cmdStateUpdateProgress(cwd, raw) {
|
|
12272
12289
|
const statePath$7 = statePath(cwd);
|
|
12273
|
-
if (!node_fs.default.existsSync(statePath$7)) {
|
|
12274
|
-
output({ error: "STATE.md not found" }, raw);
|
|
12275
|
-
return;
|
|
12276
|
-
}
|
|
12290
|
+
if (!node_fs.default.existsSync(statePath$7)) return cmdOk({ error: "STATE.md not found" });
|
|
12277
12291
|
let content = node_fs.default.readFileSync(statePath$7, "utf-8");
|
|
12278
12292
|
const phasesDir = phasesPath(cwd);
|
|
12279
12293
|
let totalPlans = 0;
|
|
@@ -12290,28 +12304,24 @@ function cmdStateUpdateProgress(cwd, raw) {
|
|
|
12290
12304
|
const barWidth = 10;
|
|
12291
12305
|
const filled = Math.round(percent / 100 * barWidth);
|
|
12292
12306
|
const progressStr = `[${"█".repeat(filled) + "░".repeat(barWidth - filled)}] ${percent}%`;
|
|
12293
|
-
const
|
|
12294
|
-
if (
|
|
12295
|
-
|
|
12296
|
-
|
|
12297
|
-
output({
|
|
12307
|
+
const result = stateReplaceField(content, "Progress", progressStr);
|
|
12308
|
+
if (result) {
|
|
12309
|
+
node_fs.default.writeFileSync(statePath$7, result, "utf-8");
|
|
12310
|
+
return cmdOk({
|
|
12298
12311
|
updated: true,
|
|
12299
12312
|
percent,
|
|
12300
12313
|
completed: totalSummaries,
|
|
12301
12314
|
total: totalPlans,
|
|
12302
12315
|
bar: progressStr
|
|
12303
|
-
}, raw
|
|
12304
|
-
} else
|
|
12316
|
+
}, raw ? progressStr : void 0);
|
|
12317
|
+
} else return cmdOk({
|
|
12305
12318
|
updated: false,
|
|
12306
12319
|
reason: "Progress field not found in STATE.md"
|
|
12307
|
-
}, raw
|
|
12320
|
+
}, raw ? "false" : void 0);
|
|
12308
12321
|
}
|
|
12309
12322
|
function cmdStateAddDecision(cwd, options, raw) {
|
|
12310
12323
|
const statePath$8 = statePath(cwd);
|
|
12311
|
-
if (!node_fs.default.existsSync(statePath$8)) {
|
|
12312
|
-
output({ error: "STATE.md not found" }, raw);
|
|
12313
|
-
return;
|
|
12314
|
-
}
|
|
12324
|
+
if (!node_fs.default.existsSync(statePath$8)) return cmdOk({ error: "STATE.md not found" });
|
|
12315
12325
|
const { phase, summary, summary_file, rationale, rationale_file } = options;
|
|
12316
12326
|
let summaryText;
|
|
12317
12327
|
let rationaleText = "";
|
|
@@ -12319,99 +12329,79 @@ function cmdStateAddDecision(cwd, options, raw) {
|
|
|
12319
12329
|
summaryText = readTextArgOrFile(cwd, summary, summary_file, "summary");
|
|
12320
12330
|
rationaleText = readTextArgOrFile(cwd, rationale || "", rationale_file, "rationale") || "";
|
|
12321
12331
|
} catch (thrown) {
|
|
12322
|
-
|
|
12332
|
+
return cmdOk({
|
|
12323
12333
|
added: false,
|
|
12324
12334
|
reason: thrown.message
|
|
12325
|
-
}, raw
|
|
12326
|
-
return;
|
|
12327
|
-
}
|
|
12328
|
-
if (!summaryText) {
|
|
12329
|
-
output({ error: "summary required" }, raw);
|
|
12330
|
-
return;
|
|
12335
|
+
}, raw ? "false" : void 0);
|
|
12331
12336
|
}
|
|
12337
|
+
if (!summaryText) return cmdOk({ error: "summary required" });
|
|
12332
12338
|
const content = node_fs.default.readFileSync(statePath$8, "utf-8");
|
|
12333
12339
|
const entry = `- [Phase ${phase || "?"}]: ${summaryText}${rationaleText ? ` — ${rationaleText}` : ""}`;
|
|
12334
12340
|
const updated = appendToStateSection(content, /(###?\s*(?:Decisions|Decisions Made|Accumulated.*Decisions)\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i, entry, [/None yet\.?\s*\n?/gi, /No decisions yet\.?\s*\n?/gi]);
|
|
12335
12341
|
if (updated) {
|
|
12336
12342
|
node_fs.default.writeFileSync(statePath$8, updated, "utf-8");
|
|
12337
|
-
|
|
12343
|
+
return cmdOk({
|
|
12338
12344
|
added: true,
|
|
12339
12345
|
decision: entry
|
|
12340
|
-
}, raw
|
|
12341
|
-
} else
|
|
12346
|
+
}, raw ? "true" : void 0);
|
|
12347
|
+
} else return cmdOk({
|
|
12342
12348
|
added: false,
|
|
12343
12349
|
reason: "Decisions section not found in STATE.md"
|
|
12344
|
-
}, raw
|
|
12350
|
+
}, raw ? "false" : void 0);
|
|
12345
12351
|
}
|
|
12346
12352
|
function cmdStateAddBlocker(cwd, text, raw) {
|
|
12347
12353
|
const statePath$9 = statePath(cwd);
|
|
12348
|
-
if (!node_fs.default.existsSync(statePath$9)) {
|
|
12349
|
-
output({ error: "STATE.md not found" }, raw);
|
|
12350
|
-
return;
|
|
12351
|
-
}
|
|
12354
|
+
if (!node_fs.default.existsSync(statePath$9)) return cmdOk({ error: "STATE.md not found" });
|
|
12352
12355
|
const blockerOptions = typeof text === "object" && text !== null ? text : { text };
|
|
12353
12356
|
let blockerText;
|
|
12354
12357
|
try {
|
|
12355
12358
|
blockerText = readTextArgOrFile(cwd, blockerOptions.text, blockerOptions.text_file, "blocker");
|
|
12356
12359
|
} catch (thrown) {
|
|
12357
|
-
|
|
12360
|
+
return cmdOk({
|
|
12358
12361
|
added: false,
|
|
12359
12362
|
reason: thrown.message
|
|
12360
|
-
}, raw
|
|
12361
|
-
return;
|
|
12362
|
-
}
|
|
12363
|
-
if (!blockerText) {
|
|
12364
|
-
output({ error: "text required" }, raw);
|
|
12365
|
-
return;
|
|
12363
|
+
}, raw ? "false" : void 0);
|
|
12366
12364
|
}
|
|
12365
|
+
if (!blockerText) return cmdOk({ error: "text required" });
|
|
12367
12366
|
const updated = appendToStateSection(node_fs.default.readFileSync(statePath$9, "utf-8"), /(###?\s*(?:Blockers|Blockers\/Concerns|Concerns)\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i, `- ${blockerText}`, [/None\.?\s*\n?/gi, /None yet\.?\s*\n?/gi]);
|
|
12368
12367
|
if (updated) {
|
|
12369
12368
|
node_fs.default.writeFileSync(statePath$9, updated, "utf-8");
|
|
12370
|
-
|
|
12369
|
+
return cmdOk({
|
|
12371
12370
|
added: true,
|
|
12372
12371
|
blocker: blockerText
|
|
12373
|
-
}, raw
|
|
12374
|
-
} else
|
|
12372
|
+
}, raw ? "true" : void 0);
|
|
12373
|
+
} else return cmdOk({
|
|
12375
12374
|
added: false,
|
|
12376
12375
|
reason: "Blockers section not found in STATE.md"
|
|
12377
|
-
}, raw
|
|
12376
|
+
}, raw ? "false" : void 0);
|
|
12378
12377
|
}
|
|
12379
12378
|
function cmdStateResolveBlocker(cwd, text, raw) {
|
|
12380
12379
|
const statePath$10 = statePath(cwd);
|
|
12381
|
-
if (!node_fs.default.existsSync(statePath$10)) {
|
|
12382
|
-
|
|
12383
|
-
return;
|
|
12384
|
-
}
|
|
12385
|
-
if (!text) {
|
|
12386
|
-
output({ error: "text required" }, raw);
|
|
12387
|
-
return;
|
|
12388
|
-
}
|
|
12380
|
+
if (!node_fs.default.existsSync(statePath$10)) return cmdOk({ error: "STATE.md not found" });
|
|
12381
|
+
if (!text) return cmdOk({ error: "text required" });
|
|
12389
12382
|
let content = node_fs.default.readFileSync(statePath$10, "utf-8");
|
|
12390
|
-
const sectionPattern = /(
|
|
12383
|
+
const sectionPattern = /(#{2,3}\s*(?:Blockers|Blockers\/Concerns|Concerns)\s*\n\s*\n?)([\s\S]*?)(?=\n#{2,3}\s|$)/i;
|
|
12391
12384
|
const match = content.match(sectionPattern);
|
|
12392
12385
|
if (match) {
|
|
12393
12386
|
let newBody = match[2].split("\n").filter((line) => {
|
|
12394
|
-
if (
|
|
12387
|
+
if (!/^\s*[-*]\s+/.test(line)) return true;
|
|
12395
12388
|
return !line.toLowerCase().includes(text.toLowerCase());
|
|
12396
12389
|
}).join("\n");
|
|
12397
|
-
if (!newBody.trim() ||
|
|
12390
|
+
if (!newBody.trim() || !/^\s*[-*]\s+/m.test(newBody)) newBody = "None\n";
|
|
12398
12391
|
content = content.replace(sectionPattern, (_match, header) => `${header}${newBody}`);
|
|
12399
12392
|
node_fs.default.writeFileSync(statePath$10, content, "utf-8");
|
|
12400
|
-
|
|
12393
|
+
return cmdOk({
|
|
12401
12394
|
resolved: true,
|
|
12402
12395
|
blocker: text
|
|
12403
|
-
}, raw
|
|
12404
|
-
} else
|
|
12396
|
+
}, raw ? "true" : void 0);
|
|
12397
|
+
} else return cmdOk({
|
|
12405
12398
|
resolved: false,
|
|
12406
12399
|
reason: "Blockers section not found in STATE.md"
|
|
12407
|
-
}, raw
|
|
12400
|
+
}, raw ? "false" : void 0);
|
|
12408
12401
|
}
|
|
12409
12402
|
function cmdStateRecordSession(cwd, options, raw) {
|
|
12410
12403
|
const statePath$11 = statePath(cwd);
|
|
12411
|
-
if (!node_fs.default.existsSync(statePath$11)) {
|
|
12412
|
-
output({ error: "STATE.md not found" }, raw);
|
|
12413
|
-
return;
|
|
12414
|
-
}
|
|
12404
|
+
if (!node_fs.default.existsSync(statePath$11)) return cmdOk({ error: "STATE.md not found" });
|
|
12415
12405
|
let content = node_fs.default.readFileSync(statePath$11, "utf-8");
|
|
12416
12406
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12417
12407
|
const updated = [];
|
|
@@ -12442,27 +12432,20 @@ function cmdStateRecordSession(cwd, options, raw) {
|
|
|
12442
12432
|
}
|
|
12443
12433
|
if (updated.length > 0) {
|
|
12444
12434
|
node_fs.default.writeFileSync(statePath$11, content, "utf-8");
|
|
12445
|
-
|
|
12435
|
+
return cmdOk({
|
|
12446
12436
|
recorded: true,
|
|
12447
12437
|
updated
|
|
12448
|
-
}, raw
|
|
12449
|
-
} else
|
|
12438
|
+
}, raw ? "true" : void 0);
|
|
12439
|
+
} else return cmdOk({
|
|
12450
12440
|
recorded: false,
|
|
12451
12441
|
reason: "No session fields found in STATE.md"
|
|
12452
|
-
}, raw
|
|
12442
|
+
}, raw ? "false" : void 0);
|
|
12453
12443
|
}
|
|
12454
12444
|
function cmdStateSnapshot(cwd, raw) {
|
|
12455
12445
|
const statePath$12 = statePath(cwd);
|
|
12456
|
-
if (!node_fs.default.existsSync(statePath$12)) {
|
|
12457
|
-
output({ error: "STATE.md not found" }, raw);
|
|
12458
|
-
return;
|
|
12459
|
-
}
|
|
12446
|
+
if (!node_fs.default.existsSync(statePath$12)) return cmdOk({ error: "STATE.md not found" });
|
|
12460
12447
|
const content = node_fs.default.readFileSync(statePath$12, "utf-8");
|
|
12461
|
-
const extractField = (fieldName) =>
|
|
12462
|
-
const pattern = new RegExp(`\\*\\*${fieldName}:\\*\\*\\s*(.+)`, "i");
|
|
12463
|
-
const match = content.match(pattern);
|
|
12464
|
-
return match ? match[1].trim() : null;
|
|
12465
|
-
};
|
|
12448
|
+
const extractField = (fieldName) => stateExtractField(content, fieldName);
|
|
12466
12449
|
const currentPhase = extractField("Current Phase");
|
|
12467
12450
|
const currentPhaseName = extractField("Current Phase Name");
|
|
12468
12451
|
const totalPhasesRaw = extractField("Total Phases");
|
|
@@ -12477,11 +12460,12 @@ function cmdStateSnapshot(cwd, raw) {
|
|
|
12477
12460
|
const totalPlansInPhase = totalPlansRaw ? parseInt(totalPlansRaw, 10) : null;
|
|
12478
12461
|
const progressPercent = progressRaw ? parseInt(progressRaw.replace("%", ""), 10) : null;
|
|
12479
12462
|
const decisions = [];
|
|
12480
|
-
const decisionsMatch = content.match(
|
|
12463
|
+
const decisionsMatch = content.match(/#{2,3}\s*Decisions Made[\s\S]*?\n\|[^\n]+\n\|[\s:|\-]+\n([\s\S]*?)(?=\n#{2,3}\s|\n$|$)/i);
|
|
12481
12464
|
if (decisionsMatch) {
|
|
12482
|
-
const rows = decisionsMatch[1].trim().split("\n").filter((r) => r.includes("|"));
|
|
12465
|
+
const rows = decisionsMatch[1].trim().split("\n").filter((r) => r.includes("|") && !r.match(/^\s*$/));
|
|
12483
12466
|
for (const row of rows) {
|
|
12484
|
-
|
|
12467
|
+
if (/^\s*\|[\s:\-|]+\|\s*$/.test(row)) continue;
|
|
12468
|
+
const cells = parseTableRow(row);
|
|
12485
12469
|
if (cells.length >= 3) decisions.push({
|
|
12486
12470
|
phase: cells[0],
|
|
12487
12471
|
summary: cells[1],
|
|
@@ -12490,27 +12474,24 @@ function cmdStateSnapshot(cwd, raw) {
|
|
|
12490
12474
|
}
|
|
12491
12475
|
}
|
|
12492
12476
|
const blockers = [];
|
|
12493
|
-
const blockersMatch = content.match(
|
|
12477
|
+
const blockersMatch = content.match(/#{2,3}\s*Blockers\s*\n([\s\S]*?)(?=\n#{2,3}\s|$)/i);
|
|
12494
12478
|
if (blockersMatch) {
|
|
12495
|
-
const items = blockersMatch[1].match(
|
|
12496
|
-
for (const item of items) blockers.push(item.replace(
|
|
12479
|
+
const items = blockersMatch[1].match(/^\s*[-*]\s+(.+)$/gm) || [];
|
|
12480
|
+
for (const item of items) blockers.push(item.replace(/^\s*[-*]\s+/, "").trim());
|
|
12497
12481
|
}
|
|
12498
12482
|
const session = {
|
|
12499
12483
|
last_date: null,
|
|
12500
12484
|
stopped_at: null,
|
|
12501
12485
|
resume_file: null
|
|
12502
12486
|
};
|
|
12503
|
-
const sessionMatch = content.match(
|
|
12487
|
+
const sessionMatch = content.match(/#{2,3}\s*Session\s*\n\s*\n?([\s\S]*?)(?=\n#{2,3}\s|$)/i);
|
|
12504
12488
|
if (sessionMatch) {
|
|
12505
12489
|
const sessionSection = sessionMatch[1];
|
|
12506
|
-
|
|
12507
|
-
|
|
12508
|
-
|
|
12509
|
-
if (lastDateMatch) session.last_date = lastDateMatch[1].trim();
|
|
12510
|
-
if (stoppedAtMatch) session.stopped_at = stoppedAtMatch[1].trim();
|
|
12511
|
-
if (resumeFileMatch) session.resume_file = resumeFileMatch[1].trim();
|
|
12490
|
+
session.last_date = stateExtractField(sessionSection, "Last Date");
|
|
12491
|
+
session.stopped_at = stateExtractField(sessionSection, "Stopped At") || stateExtractField(sessionSection, "Stopped at");
|
|
12492
|
+
session.resume_file = stateExtractField(sessionSection, "Resume File") || stateExtractField(sessionSection, "Resume file");
|
|
12512
12493
|
}
|
|
12513
|
-
|
|
12494
|
+
return cmdOk({
|
|
12514
12495
|
current_phase: currentPhase,
|
|
12515
12496
|
current_phase_name: currentPhaseName,
|
|
12516
12497
|
total_phases: totalPhases,
|
|
@@ -12524,7 +12505,7 @@ function cmdStateSnapshot(cwd, raw) {
|
|
|
12524
12505
|
blockers,
|
|
12525
12506
|
paused_at: pausedAt,
|
|
12526
12507
|
session
|
|
12527
|
-
}
|
|
12508
|
+
});
|
|
12528
12509
|
}
|
|
12529
12510
|
|
|
12530
12511
|
//#endregion
|
|
@@ -12534,15 +12515,12 @@ function cmdStateSnapshot(cwd, raw) {
|
|
|
12534
12515
|
*
|
|
12535
12516
|
* Ported from maxsim/bin/lib/roadmap.cjs
|
|
12536
12517
|
*/
|
|
12537
|
-
function cmdRoadmapGetPhase(cwd, phaseNum
|
|
12518
|
+
function cmdRoadmapGetPhase(cwd, phaseNum) {
|
|
12538
12519
|
const rmPath = roadmapPath(cwd);
|
|
12539
|
-
if (!node_fs.default.existsSync(rmPath)) {
|
|
12540
|
-
|
|
12541
|
-
|
|
12542
|
-
|
|
12543
|
-
}, raw, "");
|
|
12544
|
-
return;
|
|
12545
|
-
}
|
|
12520
|
+
if (!node_fs.default.existsSync(rmPath)) return cmdOk({
|
|
12521
|
+
found: false,
|
|
12522
|
+
error: "ROADMAP.md not found"
|
|
12523
|
+
}, "");
|
|
12546
12524
|
try {
|
|
12547
12525
|
const content = node_fs.default.readFileSync(rmPath, "utf-8");
|
|
12548
12526
|
const escapedPhase = phaseNum.replace(/\./g, "\\.");
|
|
@@ -12551,21 +12529,17 @@ function cmdRoadmapGetPhase(cwd, phaseNum, raw) {
|
|
|
12551
12529
|
if (!headerMatch) {
|
|
12552
12530
|
const checklistPattern = new RegExp(`-\\s*\\[[ x]\\]\\s*\\*\\*Phase\\s+${escapedPhase}:\\s*([^*]+)\\*\\*`, "i");
|
|
12553
12531
|
const checklistMatch = content.match(checklistPattern);
|
|
12554
|
-
if (checklistMatch) {
|
|
12555
|
-
|
|
12556
|
-
|
|
12557
|
-
|
|
12558
|
-
|
|
12559
|
-
|
|
12560
|
-
|
|
12561
|
-
|
|
12562
|
-
return;
|
|
12563
|
-
}
|
|
12564
|
-
output({
|
|
12532
|
+
if (checklistMatch) return cmdOk({
|
|
12533
|
+
found: false,
|
|
12534
|
+
phase_number: phaseNum,
|
|
12535
|
+
phase_name: checklistMatch[1].trim(),
|
|
12536
|
+
error: "malformed_roadmap",
|
|
12537
|
+
message: `Phase ${phaseNum} exists in summary list but missing "### Phase ${phaseNum}:" detail section. ROADMAP.md needs both formats.`
|
|
12538
|
+
}, "");
|
|
12539
|
+
return cmdOk({
|
|
12565
12540
|
found: false,
|
|
12566
12541
|
phase_number: phaseNum
|
|
12567
|
-
},
|
|
12568
|
-
return;
|
|
12542
|
+
}, "");
|
|
12569
12543
|
}
|
|
12570
12544
|
const phaseName = headerMatch[1].trim();
|
|
12571
12545
|
const headerIndex = headerMatch.index;
|
|
@@ -12575,34 +12549,29 @@ function cmdRoadmapGetPhase(cwd, phaseNum, raw) {
|
|
|
12575
12549
|
const goalMatch = section.match(/\*\*Goal(?::\*\*|\*\*:)\s*([^\n]+)/i);
|
|
12576
12550
|
const goal = goalMatch ? goalMatch[1].trim() : null;
|
|
12577
12551
|
const criteriaMatch = section.match(/\*\*Success Criteria\*\*[^\n]*:\s*\n((?:\s*\d+\.\s*[^\n]+\n?)+)/i);
|
|
12578
|
-
|
|
12552
|
+
return cmdOk({
|
|
12579
12553
|
found: true,
|
|
12580
12554
|
phase_number: phaseNum,
|
|
12581
12555
|
phase_name: phaseName,
|
|
12582
12556
|
goal,
|
|
12583
12557
|
success_criteria: criteriaMatch ? criteriaMatch[1].trim().split("\n").map((line) => line.replace(/^\s*\d+\.\s*/, "").trim()).filter(Boolean) : [],
|
|
12584
12558
|
section
|
|
12585
|
-
},
|
|
12559
|
+
}, section);
|
|
12586
12560
|
} catch (e) {
|
|
12587
|
-
|
|
12588
|
-
error("Failed to read ROADMAP.md: " + e.message);
|
|
12561
|
+
return cmdErr("Failed to read ROADMAP.md: " + e.message);
|
|
12589
12562
|
}
|
|
12590
12563
|
}
|
|
12591
|
-
function cmdRoadmapAnalyze(cwd
|
|
12592
|
-
const
|
|
12593
|
-
if (!
|
|
12594
|
-
|
|
12595
|
-
|
|
12596
|
-
|
|
12597
|
-
|
|
12598
|
-
|
|
12599
|
-
}, raw);
|
|
12600
|
-
return;
|
|
12601
|
-
}
|
|
12602
|
-
const content = node_fs.default.readFileSync(rmPath, "utf-8");
|
|
12564
|
+
async function cmdRoadmapAnalyze(cwd) {
|
|
12565
|
+
const content = await safeReadFileAsync(roadmapPath(cwd));
|
|
12566
|
+
if (!content) return cmdOk({
|
|
12567
|
+
error: "ROADMAP.md not found",
|
|
12568
|
+
milestones: [],
|
|
12569
|
+
phases: [],
|
|
12570
|
+
current_phase: null
|
|
12571
|
+
});
|
|
12603
12572
|
const phasesDir = phasesPath(cwd);
|
|
12604
12573
|
const phasePattern = getPhasePattern();
|
|
12605
|
-
const
|
|
12574
|
+
const parsedPhases = [];
|
|
12606
12575
|
let match;
|
|
12607
12576
|
while ((match = phasePattern.exec(content)) !== null) {
|
|
12608
12577
|
const phaseNum = match[1];
|
|
@@ -12615,16 +12584,29 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
12615
12584
|
const goal = goalMatch ? goalMatch[1].trim() : null;
|
|
12616
12585
|
const dependsMatch = section.match(/\*\*Depends on:\*\*\s*([^\n]+)/i);
|
|
12617
12586
|
const depends_on = dependsMatch ? dependsMatch[1].trim() : null;
|
|
12618
|
-
|
|
12587
|
+
parsedPhases.push({
|
|
12588
|
+
phaseNum,
|
|
12589
|
+
phaseName,
|
|
12590
|
+
goal,
|
|
12591
|
+
depends_on,
|
|
12592
|
+
normalized: normalizePhaseName(phaseNum),
|
|
12593
|
+
checkboxPattern: new RegExp(`-\\s*\\[(x| )\\]\\s*.*Phase\\s+${phaseNum.replace(".", "\\.")}`, "i")
|
|
12594
|
+
});
|
|
12595
|
+
}
|
|
12596
|
+
let allDirs = [];
|
|
12597
|
+
try {
|
|
12598
|
+
allDirs = await listSubDirsAsync(phasesDir);
|
|
12599
|
+
} catch {}
|
|
12600
|
+
const phases = await Promise.all(parsedPhases.map(async (p) => {
|
|
12619
12601
|
let diskStatus = "no_directory";
|
|
12620
12602
|
let planCount = 0;
|
|
12621
12603
|
let summaryCount = 0;
|
|
12622
12604
|
let hasContext = false;
|
|
12623
12605
|
let hasResearch = false;
|
|
12624
12606
|
try {
|
|
12625
|
-
const dirMatch =
|
|
12607
|
+
const dirMatch = allDirs.find((d) => d.startsWith(p.normalized + "-") || d === p.normalized);
|
|
12626
12608
|
if (dirMatch) {
|
|
12627
|
-
const phaseFiles = node_fs.default.
|
|
12609
|
+
const phaseFiles = await node_fs.default.promises.readdir(node_path.default.join(phasesDir, dirMatch));
|
|
12628
12610
|
planCount = phaseFiles.filter((f) => isPlanFile(f)).length;
|
|
12629
12611
|
summaryCount = phaseFiles.filter((f) => isSummaryFile(f)).length;
|
|
12630
12612
|
hasContext = phaseFiles.some((f) => f.endsWith("-CONTEXT.md") || f === "CONTEXT.md");
|
|
@@ -12639,22 +12621,21 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
12639
12621
|
} catch (e) {
|
|
12640
12622
|
debugLog(e);
|
|
12641
12623
|
}
|
|
12642
|
-
const
|
|
12643
|
-
const checkboxMatch = content.match(checkboxPattern);
|
|
12624
|
+
const checkboxMatch = content.match(p.checkboxPattern);
|
|
12644
12625
|
const roadmapComplete = checkboxMatch ? checkboxMatch[1] === "x" : false;
|
|
12645
|
-
|
|
12646
|
-
number: phaseNum,
|
|
12647
|
-
name: phaseName,
|
|
12648
|
-
goal,
|
|
12649
|
-
depends_on,
|
|
12626
|
+
return {
|
|
12627
|
+
number: p.phaseNum,
|
|
12628
|
+
name: p.phaseName,
|
|
12629
|
+
goal: p.goal,
|
|
12630
|
+
depends_on: p.depends_on,
|
|
12650
12631
|
plan_count: planCount,
|
|
12651
12632
|
summary_count: summaryCount,
|
|
12652
12633
|
has_context: hasContext,
|
|
12653
12634
|
has_research: hasResearch,
|
|
12654
12635
|
disk_status: diskStatus,
|
|
12655
12636
|
roadmap_complete: roadmapComplete
|
|
12656
|
-
}
|
|
12657
|
-
}
|
|
12637
|
+
};
|
|
12638
|
+
}));
|
|
12658
12639
|
const milestones = [];
|
|
12659
12640
|
const milestonePattern = /##\s*(.*v(\d+\.\d+)[^(\n]*)/gi;
|
|
12660
12641
|
let mMatch;
|
|
@@ -12673,7 +12654,7 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
12673
12654
|
while ((checklistMatch = checklistPattern.exec(content)) !== null) checklistPhases.add(checklistMatch[1]);
|
|
12674
12655
|
const detailPhases = new Set(phases.map((p) => p.number));
|
|
12675
12656
|
const missingDetails = [...checklistPhases].filter((p) => !detailPhases.has(p));
|
|
12676
|
-
|
|
12657
|
+
return cmdOk({
|
|
12677
12658
|
milestones,
|
|
12678
12659
|
phases,
|
|
12679
12660
|
phase_count: phases.length,
|
|
@@ -12684,57 +12665,46 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
12684
12665
|
current_phase: currentPhase ? currentPhase.number : null,
|
|
12685
12666
|
next_phase: nextPhase ? nextPhase.number : null,
|
|
12686
12667
|
missing_phase_details: missingDetails.length > 0 ? missingDetails : null
|
|
12687
|
-
}
|
|
12668
|
+
});
|
|
12688
12669
|
}
|
|
12689
|
-
function cmdRoadmapUpdatePlanProgress(cwd, phaseNum
|
|
12690
|
-
if (!phaseNum)
|
|
12670
|
+
function cmdRoadmapUpdatePlanProgress(cwd, phaseNum) {
|
|
12671
|
+
if (!phaseNum) return cmdErr("phase number required for roadmap update-plan-progress");
|
|
12691
12672
|
const rmPath = roadmapPath(cwd);
|
|
12692
12673
|
const phaseInfo = findPhaseInternal(cwd, phaseNum);
|
|
12693
|
-
if (!phaseInfo)
|
|
12674
|
+
if (!phaseInfo) return cmdErr(`Phase ${phaseNum} not found`);
|
|
12694
12675
|
const planCount = phaseInfo.plans.length;
|
|
12695
12676
|
const summaryCount = phaseInfo.summaries.length;
|
|
12696
|
-
if (planCount === 0) {
|
|
12697
|
-
|
|
12698
|
-
|
|
12699
|
-
|
|
12700
|
-
|
|
12701
|
-
|
|
12702
|
-
}, raw, "no plans");
|
|
12703
|
-
return;
|
|
12704
|
-
}
|
|
12677
|
+
if (planCount === 0) return cmdOk({
|
|
12678
|
+
updated: false,
|
|
12679
|
+
reason: "No plans found",
|
|
12680
|
+
plan_count: 0,
|
|
12681
|
+
summary_count: 0
|
|
12682
|
+
}, "no plans");
|
|
12705
12683
|
const isComplete = summaryCount >= planCount;
|
|
12706
12684
|
const status = isComplete ? "Complete" : summaryCount > 0 ? "In Progress" : "Planned";
|
|
12707
12685
|
const today = todayISO();
|
|
12708
|
-
if (!node_fs.default.existsSync(rmPath)) {
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
|
|
12714
|
-
}, raw, "no roadmap");
|
|
12715
|
-
return;
|
|
12716
|
-
}
|
|
12686
|
+
if (!node_fs.default.existsSync(rmPath)) return cmdOk({
|
|
12687
|
+
updated: false,
|
|
12688
|
+
reason: "ROADMAP.md not found",
|
|
12689
|
+
plan_count: planCount,
|
|
12690
|
+
summary_count: summaryCount
|
|
12691
|
+
}, "no roadmap");
|
|
12717
12692
|
let roadmapContent = node_fs.default.readFileSync(rmPath, "utf-8");
|
|
12718
12693
|
const phaseEscaped = phaseNum.replace(".", "\\.");
|
|
12719
|
-
const tablePattern = new RegExp(`(\\|\\s*${phaseEscaped}\\.?\\s[^|]*\\|)[^|]*(\\|)\\s*[^|]*(\\|)\\s*[^|]*(\\|)`, "i");
|
|
12720
12694
|
const dateField = isComplete ? ` ${today} ` : " ";
|
|
12721
|
-
roadmapContent = roadmapContent.replace(
|
|
12722
|
-
const planCountPattern = new RegExp(`(#{2,4}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`, "i");
|
|
12695
|
+
roadmapContent = roadmapContent.replace(new RegExp(`(\\|\\s*${phaseEscaped}\\.?\\s[^|]*\\|)[^|]*(\\|)\\s*[^|]*(\\|)\\s*[^|]*(\\|)`, "i"), `$1 ${summaryCount}/${planCount} $2 ${status.padEnd(11)}$3${dateField}$4`);
|
|
12723
12696
|
const planCountText = isComplete ? `${summaryCount}/${planCount} plans complete` : `${summaryCount}/${planCount} plans executed`;
|
|
12724
|
-
roadmapContent = roadmapContent.replace(
|
|
12725
|
-
if (isComplete) {
|
|
12726
|
-
const checkboxPattern = new RegExp(`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${phaseEscaped}[:\\s][^\\n]*)`, "i");
|
|
12727
|
-
roadmapContent = roadmapContent.replace(checkboxPattern, `$1x$2 (completed ${today})`);
|
|
12728
|
-
}
|
|
12697
|
+
roadmapContent = roadmapContent.replace(new RegExp(`(#{2,4}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`, "i"), `$1${planCountText}`);
|
|
12698
|
+
if (isComplete) roadmapContent = roadmapContent.replace(new RegExp(`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${phaseEscaped}[:\\s][^\\n]*)`, "i"), `$1x$2 (completed ${today})`);
|
|
12729
12699
|
node_fs.default.writeFileSync(rmPath, roadmapContent, "utf-8");
|
|
12730
|
-
|
|
12700
|
+
return cmdOk({
|
|
12731
12701
|
updated: true,
|
|
12732
12702
|
phase: phaseNum,
|
|
12733
12703
|
plan_count: planCount,
|
|
12734
12704
|
summary_count: summaryCount,
|
|
12735
12705
|
status,
|
|
12736
12706
|
complete: isComplete
|
|
12737
|
-
},
|
|
12707
|
+
}, `${summaryCount}/${planCount} ${status}`);
|
|
12738
12708
|
}
|
|
12739
12709
|
|
|
12740
12710
|
//#endregion
|
|
@@ -12744,19 +12714,16 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
12744
12714
|
*
|
|
12745
12715
|
* Ported from maxsim/bin/lib/milestone.cjs
|
|
12746
12716
|
*/
|
|
12747
|
-
function cmdRequirementsMarkComplete(cwd, reqIdsRaw
|
|
12748
|
-
if (!reqIdsRaw || reqIdsRaw.length === 0)
|
|
12717
|
+
function cmdRequirementsMarkComplete(cwd, reqIdsRaw) {
|
|
12718
|
+
if (!reqIdsRaw || reqIdsRaw.length === 0) return cmdErr("requirement IDs required. Usage: requirements mark-complete REQ-01,REQ-02 or REQ-01 REQ-02");
|
|
12749
12719
|
const reqIds = reqIdsRaw.join(" ").replace(/[\[\]]/g, "").split(/[,\s]+/).map((r) => r.trim()).filter(Boolean);
|
|
12750
|
-
if (reqIds.length === 0)
|
|
12720
|
+
if (reqIds.length === 0) return cmdErr("no valid requirement IDs found");
|
|
12751
12721
|
const reqPath = planningPath(cwd, "REQUIREMENTS.md");
|
|
12752
|
-
if (!node_fs.default.existsSync(reqPath)) {
|
|
12753
|
-
|
|
12754
|
-
|
|
12755
|
-
|
|
12756
|
-
|
|
12757
|
-
}, raw, "no requirements file");
|
|
12758
|
-
return;
|
|
12759
|
-
}
|
|
12722
|
+
if (!node_fs.default.existsSync(reqPath)) return cmdOk({
|
|
12723
|
+
updated: false,
|
|
12724
|
+
reason: "REQUIREMENTS.md not found",
|
|
12725
|
+
ids: reqIds
|
|
12726
|
+
}, "no requirements file");
|
|
12760
12727
|
let reqContent = node_fs.default.readFileSync(reqPath, "utf-8");
|
|
12761
12728
|
const updated = [];
|
|
12762
12729
|
const notFound = [];
|
|
@@ -12775,15 +12742,15 @@ function cmdRequirementsMarkComplete(cwd, reqIdsRaw, raw) {
|
|
|
12775
12742
|
else notFound.push(reqId);
|
|
12776
12743
|
}
|
|
12777
12744
|
if (updated.length > 0) node_fs.default.writeFileSync(reqPath, reqContent, "utf-8");
|
|
12778
|
-
|
|
12745
|
+
return cmdOk({
|
|
12779
12746
|
updated: updated.length > 0,
|
|
12780
12747
|
marked_complete: updated,
|
|
12781
12748
|
not_found: notFound,
|
|
12782
12749
|
total: reqIds.length
|
|
12783
|
-
},
|
|
12750
|
+
}, `${updated.length}/${reqIds.length} requirements marked complete`);
|
|
12784
12751
|
}
|
|
12785
|
-
function cmdMilestoneComplete(cwd, version, options
|
|
12786
|
-
if (!version)
|
|
12752
|
+
function cmdMilestoneComplete(cwd, version, options) {
|
|
12753
|
+
if (!version) return cmdErr("version required for milestone complete (e.g., v1.0)");
|
|
12787
12754
|
const roadmapPath$1 = roadmapPath(cwd);
|
|
12788
12755
|
const reqPath = planningPath(cwd, "REQUIREMENTS.md");
|
|
12789
12756
|
const statePath$1 = statePath(cwd);
|
|
@@ -12852,7 +12819,7 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
12852
12819
|
} catch (e) {
|
|
12853
12820
|
debugLog(e);
|
|
12854
12821
|
}
|
|
12855
|
-
|
|
12822
|
+
return cmdOk({
|
|
12856
12823
|
version,
|
|
12857
12824
|
name: milestoneName,
|
|
12858
12825
|
date: today,
|
|
@@ -12868,7 +12835,7 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
|
|
|
12868
12835
|
},
|
|
12869
12836
|
milestones_updated: true,
|
|
12870
12837
|
state_updated: node_fs.default.existsSync(statePath$1)
|
|
12871
|
-
}
|
|
12838
|
+
});
|
|
12872
12839
|
}
|
|
12873
12840
|
|
|
12874
12841
|
//#endregion
|
|
@@ -13297,12 +13264,12 @@ function parseTodoFrontmatter(content) {
|
|
|
13297
13264
|
};
|
|
13298
13265
|
}
|
|
13299
13266
|
function cmdGenerateSlug(text, raw) {
|
|
13300
|
-
if (!text)
|
|
13267
|
+
if (!text) return cmdErr("text required for slug generation");
|
|
13301
13268
|
const slug = (0, import_slugify.default)(text, {
|
|
13302
13269
|
lower: true,
|
|
13303
13270
|
strict: true
|
|
13304
13271
|
});
|
|
13305
|
-
|
|
13272
|
+
return cmdOk({ slug }, raw ? slug : void 0);
|
|
13306
13273
|
}
|
|
13307
13274
|
function cmdCurrentTimestamp(format, raw) {
|
|
13308
13275
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -13318,7 +13285,7 @@ function cmdCurrentTimestamp(format, raw) {
|
|
|
13318
13285
|
result = now.toISOString();
|
|
13319
13286
|
break;
|
|
13320
13287
|
}
|
|
13321
|
-
|
|
13288
|
+
return cmdOk({ timestamp: result }, raw ? result : void 0);
|
|
13322
13289
|
}
|
|
13323
13290
|
function cmdListTodos(cwd, area, raw) {
|
|
13324
13291
|
const pendingDir = planningPath(cwd, "todos", "pending");
|
|
@@ -13343,26 +13310,26 @@ function cmdListTodos(cwd, area, raw) {
|
|
|
13343
13310
|
} catch (e) {
|
|
13344
13311
|
debugLog(e);
|
|
13345
13312
|
}
|
|
13346
|
-
|
|
13313
|
+
return cmdOk({
|
|
13347
13314
|
count,
|
|
13348
13315
|
todos
|
|
13349
|
-
}, raw
|
|
13316
|
+
}, raw ? count.toString() : void 0);
|
|
13350
13317
|
}
|
|
13351
13318
|
function cmdVerifyPathExists(cwd, targetPath, raw) {
|
|
13352
|
-
if (!targetPath)
|
|
13319
|
+
if (!targetPath) return cmdErr("path required for verification");
|
|
13353
13320
|
const fullPath = node_path.default.isAbsolute(targetPath) ? targetPath : node_path.default.join(cwd, targetPath);
|
|
13354
13321
|
try {
|
|
13355
13322
|
const stats = node_fs.default.statSync(fullPath);
|
|
13356
|
-
|
|
13323
|
+
return cmdOk({
|
|
13357
13324
|
exists: true,
|
|
13358
13325
|
type: stats.isDirectory() ? "directory" : stats.isFile() ? "file" : "other"
|
|
13359
|
-
}, raw
|
|
13326
|
+
}, raw ? "true" : void 0);
|
|
13360
13327
|
} catch (e) {
|
|
13361
13328
|
rethrowCliSignals(e);
|
|
13362
|
-
|
|
13329
|
+
return cmdOk({
|
|
13363
13330
|
exists: false,
|
|
13364
13331
|
type: null
|
|
13365
|
-
}, raw
|
|
13332
|
+
}, raw ? "false" : void 0);
|
|
13366
13333
|
}
|
|
13367
13334
|
}
|
|
13368
13335
|
function cmdHistoryDigest(cwd, raw) {
|
|
@@ -13389,14 +13356,11 @@ function cmdHistoryDigest(cwd, raw) {
|
|
|
13389
13356
|
} catch (e) {
|
|
13390
13357
|
debugLog(e);
|
|
13391
13358
|
}
|
|
13392
|
-
if (allPhaseDirs.length === 0) {
|
|
13393
|
-
|
|
13394
|
-
|
|
13395
|
-
|
|
13396
|
-
|
|
13397
|
-
}, raw);
|
|
13398
|
-
return;
|
|
13399
|
-
}
|
|
13359
|
+
if (allPhaseDirs.length === 0) return cmdOk({
|
|
13360
|
+
phases: {},
|
|
13361
|
+
decisions: [],
|
|
13362
|
+
tech_stack: []
|
|
13363
|
+
});
|
|
13400
13364
|
try {
|
|
13401
13365
|
for (const { name: dir, fullPath: dirPath } of allPhaseDirs) {
|
|
13402
13366
|
const summaries = node_fs.default.readdirSync(dirPath).filter((f) => isSummaryFile(f));
|
|
@@ -13437,49 +13401,40 @@ function cmdHistoryDigest(cwd, raw) {
|
|
|
13437
13401
|
affects: [...data.affects],
|
|
13438
13402
|
patterns: [...data.patterns]
|
|
13439
13403
|
};
|
|
13440
|
-
|
|
13404
|
+
return cmdOk(outputDigest);
|
|
13441
13405
|
} catch (e) {
|
|
13442
13406
|
rethrowCliSignals(e);
|
|
13443
|
-
|
|
13407
|
+
return cmdErr("Failed to generate history digest: " + e.message);
|
|
13444
13408
|
}
|
|
13445
13409
|
}
|
|
13446
13410
|
function cmdResolveModel(cwd, agentType, raw) {
|
|
13447
|
-
if (!agentType)
|
|
13411
|
+
if (!agentType) return cmdErr("agent-type required");
|
|
13448
13412
|
const profile = loadConfig(cwd).model_profile || "balanced";
|
|
13449
13413
|
const agentModels = MODEL_PROFILES[agentType];
|
|
13450
|
-
if (!agentModels) {
|
|
13451
|
-
|
|
13452
|
-
|
|
13453
|
-
|
|
13454
|
-
|
|
13455
|
-
}, raw, "sonnet");
|
|
13456
|
-
return;
|
|
13457
|
-
}
|
|
13414
|
+
if (!agentModels) return cmdOk({
|
|
13415
|
+
model: "sonnet",
|
|
13416
|
+
profile,
|
|
13417
|
+
unknown_agent: true
|
|
13418
|
+
}, raw ? "sonnet" : void 0);
|
|
13458
13419
|
const resolved = agentModels[profile] || agentModels["balanced"] || "sonnet";
|
|
13459
13420
|
const model = resolved === "opus" ? "inherit" : resolved;
|
|
13460
|
-
|
|
13421
|
+
return cmdOk({
|
|
13461
13422
|
model,
|
|
13462
13423
|
profile
|
|
13463
|
-
}, raw
|
|
13424
|
+
}, raw ? model : void 0);
|
|
13464
13425
|
}
|
|
13465
13426
|
async function cmdCommit(cwd, message, files, raw, amend) {
|
|
13466
|
-
if (!message && !amend)
|
|
13467
|
-
if (!loadConfig(cwd).commit_docs) {
|
|
13468
|
-
|
|
13469
|
-
|
|
13470
|
-
|
|
13471
|
-
|
|
13472
|
-
|
|
13473
|
-
|
|
13474
|
-
|
|
13475
|
-
|
|
13476
|
-
|
|
13477
|
-
committed: false,
|
|
13478
|
-
hash: null,
|
|
13479
|
-
reason: "skipped_gitignored"
|
|
13480
|
-
}, raw, "skipped");
|
|
13481
|
-
return;
|
|
13482
|
-
}
|
|
13427
|
+
if (!message && !amend) return cmdErr("commit message required");
|
|
13428
|
+
if (!loadConfig(cwd).commit_docs) return cmdOk({
|
|
13429
|
+
committed: false,
|
|
13430
|
+
hash: null,
|
|
13431
|
+
reason: "skipped_commit_docs_false"
|
|
13432
|
+
}, raw ? "skipped" : void 0);
|
|
13433
|
+
if (await isGitIgnored(cwd, ".planning")) return cmdOk({
|
|
13434
|
+
committed: false,
|
|
13435
|
+
hash: null,
|
|
13436
|
+
reason: "skipped_gitignored"
|
|
13437
|
+
}, raw ? "skipped" : void 0);
|
|
13483
13438
|
const filesToStage = files && files.length > 0 ? files : [".planning/"];
|
|
13484
13439
|
for (const file of filesToStage) await execGit(cwd, ["add", file]);
|
|
13485
13440
|
const commitResult = await execGit(cwd, amend ? [
|
|
@@ -13492,21 +13447,17 @@ async function cmdCommit(cwd, message, files, raw, amend) {
|
|
|
13492
13447
|
message
|
|
13493
13448
|
]);
|
|
13494
13449
|
if (commitResult.exitCode !== 0) {
|
|
13495
|
-
if (commitResult.stdout.includes("nothing to commit") || commitResult.stderr.includes("nothing to commit")) {
|
|
13496
|
-
|
|
13497
|
-
|
|
13498
|
-
|
|
13499
|
-
|
|
13500
|
-
|
|
13501
|
-
return;
|
|
13502
|
-
}
|
|
13503
|
-
output({
|
|
13450
|
+
if (commitResult.stdout.includes("nothing to commit") || commitResult.stderr.includes("nothing to commit")) return cmdOk({
|
|
13451
|
+
committed: false,
|
|
13452
|
+
hash: null,
|
|
13453
|
+
reason: "nothing_to_commit"
|
|
13454
|
+
}, raw ? "nothing" : void 0);
|
|
13455
|
+
return cmdOk({
|
|
13504
13456
|
committed: false,
|
|
13505
13457
|
hash: null,
|
|
13506
13458
|
reason: "nothing_to_commit",
|
|
13507
13459
|
error: commitResult.stderr
|
|
13508
|
-
}, raw
|
|
13509
|
-
return;
|
|
13460
|
+
}, raw ? "nothing" : void 0);
|
|
13510
13461
|
}
|
|
13511
13462
|
const hashResult = await execGit(cwd, [
|
|
13512
13463
|
"rev-parse",
|
|
@@ -13514,22 +13465,19 @@ async function cmdCommit(cwd, message, files, raw, amend) {
|
|
|
13514
13465
|
"HEAD"
|
|
13515
13466
|
]);
|
|
13516
13467
|
const hash = hashResult.exitCode === 0 ? hashResult.stdout : null;
|
|
13517
|
-
|
|
13468
|
+
return cmdOk({
|
|
13518
13469
|
committed: true,
|
|
13519
13470
|
hash,
|
|
13520
13471
|
reason: "committed"
|
|
13521
|
-
}, raw
|
|
13472
|
+
}, raw ? hash || "committed" : void 0);
|
|
13522
13473
|
}
|
|
13523
13474
|
function cmdSummaryExtract(cwd, summaryPath, fields, raw) {
|
|
13524
|
-
if (!summaryPath)
|
|
13475
|
+
if (!summaryPath) return cmdErr("summary-path required for summary-extract");
|
|
13525
13476
|
const fullPath = node_path.default.join(cwd, summaryPath);
|
|
13526
|
-
if (!node_fs.default.existsSync(fullPath)) {
|
|
13527
|
-
|
|
13528
|
-
|
|
13529
|
-
|
|
13530
|
-
}, raw);
|
|
13531
|
-
return;
|
|
13532
|
-
}
|
|
13477
|
+
if (!node_fs.default.existsSync(fullPath)) return cmdOk({
|
|
13478
|
+
error: "File not found",
|
|
13479
|
+
path: summaryPath
|
|
13480
|
+
});
|
|
13533
13481
|
const fm = extractFrontmatter(node_fs.default.readFileSync(fullPath, "utf-8"));
|
|
13534
13482
|
const parseDecisions = (decisionsList) => {
|
|
13535
13483
|
if (!decisionsList || !Array.isArray(decisionsList)) return [];
|
|
@@ -13558,27 +13506,20 @@ function cmdSummaryExtract(cwd, summaryPath, fields, raw) {
|
|
|
13558
13506
|
if (fields && fields.length > 0) {
|
|
13559
13507
|
const filtered = { path: summaryPath };
|
|
13560
13508
|
for (const field of fields) if (fullResult[field] !== void 0) filtered[field] = fullResult[field];
|
|
13561
|
-
|
|
13562
|
-
return;
|
|
13509
|
+
return cmdOk(filtered);
|
|
13563
13510
|
}
|
|
13564
|
-
|
|
13511
|
+
return cmdOk(fullResult);
|
|
13565
13512
|
}
|
|
13566
13513
|
async function cmdWebsearch(query, options, raw) {
|
|
13567
13514
|
const apiKey = process.env.BRAVE_API_KEY;
|
|
13568
|
-
if (!apiKey) {
|
|
13569
|
-
|
|
13570
|
-
|
|
13571
|
-
|
|
13572
|
-
|
|
13573
|
-
|
|
13574
|
-
|
|
13575
|
-
|
|
13576
|
-
output({
|
|
13577
|
-
available: false,
|
|
13578
|
-
error: "Query required"
|
|
13579
|
-
}, raw, "");
|
|
13580
|
-
return;
|
|
13581
|
-
}
|
|
13515
|
+
if (!apiKey) return cmdOk({
|
|
13516
|
+
available: false,
|
|
13517
|
+
reason: "BRAVE_API_KEY not set"
|
|
13518
|
+
}, raw ? "" : void 0);
|
|
13519
|
+
if (!query) return cmdOk({
|
|
13520
|
+
available: false,
|
|
13521
|
+
error: "Query required"
|
|
13522
|
+
}, raw ? "" : void 0);
|
|
13582
13523
|
const params = new URLSearchParams({
|
|
13583
13524
|
q: query,
|
|
13584
13525
|
count: String(options.limit || 10),
|
|
@@ -13592,31 +13533,28 @@ async function cmdWebsearch(query, options, raw) {
|
|
|
13592
13533
|
Accept: "application/json",
|
|
13593
13534
|
"X-Subscription-Token": apiKey
|
|
13594
13535
|
} });
|
|
13595
|
-
if (!response.ok) {
|
|
13596
|
-
|
|
13597
|
-
|
|
13598
|
-
|
|
13599
|
-
}, raw, "");
|
|
13600
|
-
return;
|
|
13601
|
-
}
|
|
13536
|
+
if (!response.ok) return cmdOk({
|
|
13537
|
+
available: false,
|
|
13538
|
+
error: `API error: ${response.status}`
|
|
13539
|
+
}, raw ? "" : void 0);
|
|
13602
13540
|
const results = ((await response.json()).web?.results || []).map((r) => ({
|
|
13603
13541
|
title: r.title,
|
|
13604
13542
|
url: r.url,
|
|
13605
13543
|
description: r.description,
|
|
13606
13544
|
age: r.age || null
|
|
13607
13545
|
}));
|
|
13608
|
-
|
|
13546
|
+
return cmdOk({
|
|
13609
13547
|
available: true,
|
|
13610
13548
|
query,
|
|
13611
13549
|
count: results.length,
|
|
13612
13550
|
results
|
|
13613
|
-
}, raw
|
|
13551
|
+
}, raw ? results.map((r) => `${r.title}\n${r.url}\n${r.description}`).join("\n\n") : void 0);
|
|
13614
13552
|
} catch (err) {
|
|
13615
13553
|
rethrowCliSignals(err);
|
|
13616
|
-
|
|
13554
|
+
return cmdOk({
|
|
13617
13555
|
available: false,
|
|
13618
13556
|
error: err.message
|
|
13619
|
-
}, raw
|
|
13557
|
+
}, raw ? "" : void 0);
|
|
13620
13558
|
}
|
|
13621
13559
|
}
|
|
13622
13560
|
function cmdProgressRender(cwd, format, raw) {
|
|
@@ -13664,17 +13602,17 @@ function cmdProgressRender(cwd, format, raw) {
|
|
|
13664
13602
|
out += `| Phase | Name | Plans | Status |\n`;
|
|
13665
13603
|
out += `|-------|------|-------|--------|\n`;
|
|
13666
13604
|
for (const p of phases) out += `| ${p.number} | ${p.name} | ${p.summaries}/${p.plans} | ${p.status} |\n`;
|
|
13667
|
-
|
|
13605
|
+
return cmdOk({ rendered: out }, raw ? out : void 0);
|
|
13668
13606
|
} else if (format === "bar") {
|
|
13669
13607
|
const barWidth = 20;
|
|
13670
13608
|
const filled = Math.round(percent / 100 * barWidth);
|
|
13671
13609
|
const text = `[${"█".repeat(filled) + "░".repeat(barWidth - filled)}] ${totalSummaries}/${totalPlans} plans (${percent}%)`;
|
|
13672
|
-
|
|
13610
|
+
return cmdOk({
|
|
13673
13611
|
bar: text,
|
|
13674
13612
|
percent,
|
|
13675
13613
|
completed: totalSummaries,
|
|
13676
13614
|
total: totalPlans
|
|
13677
|
-
}, raw
|
|
13615
|
+
}, raw ? text : void 0);
|
|
13678
13616
|
} else if (format === "phase-bars") {
|
|
13679
13617
|
const doneCount = phases.filter((p) => p.status === "Complete").length;
|
|
13680
13618
|
const inProgressCount = phases.filter((p) => p.status === "In Progress").length;
|
|
@@ -13694,39 +13632,39 @@ function cmdProgressRender(cwd, format, raw) {
|
|
|
13694
13632
|
lines.push(line);
|
|
13695
13633
|
}
|
|
13696
13634
|
const rendered = lines.join("\n");
|
|
13697
|
-
|
|
13635
|
+
return cmdOk({
|
|
13698
13636
|
rendered,
|
|
13699
13637
|
done: doneCount,
|
|
13700
13638
|
in_progress: inProgressCount,
|
|
13701
13639
|
total: totalCount,
|
|
13702
13640
|
percent
|
|
13703
|
-
}, raw
|
|
13704
|
-
} else
|
|
13641
|
+
}, raw ? rendered : void 0);
|
|
13642
|
+
} else return cmdOk({
|
|
13705
13643
|
milestone_version: milestone.version,
|
|
13706
13644
|
milestone_name: milestone.name,
|
|
13707
13645
|
phases,
|
|
13708
13646
|
total_plans: totalPlans,
|
|
13709
13647
|
total_summaries: totalSummaries,
|
|
13710
13648
|
percent
|
|
13711
|
-
}
|
|
13649
|
+
});
|
|
13712
13650
|
}
|
|
13713
13651
|
function cmdTodoComplete(cwd, filename, raw) {
|
|
13714
|
-
if (!filename)
|
|
13652
|
+
if (!filename) return cmdErr("filename required for todo complete");
|
|
13715
13653
|
const pendingDir = planningPath(cwd, "todos", "pending");
|
|
13716
13654
|
const completedDir = planningPath(cwd, "todos", "completed");
|
|
13717
13655
|
const sourcePath = node_path.default.join(pendingDir, filename);
|
|
13718
|
-
if (!node_fs.default.existsSync(sourcePath))
|
|
13656
|
+
if (!node_fs.default.existsSync(sourcePath)) return cmdErr(`Todo not found: ${filename}`);
|
|
13719
13657
|
node_fs.default.mkdirSync(completedDir, { recursive: true });
|
|
13720
13658
|
let content = node_fs.default.readFileSync(sourcePath, "utf-8");
|
|
13721
13659
|
const today = todayISO();
|
|
13722
13660
|
content = `completed: ${today}\n` + content;
|
|
13723
13661
|
node_fs.default.writeFileSync(node_path.default.join(completedDir, filename), content, "utf-8");
|
|
13724
13662
|
node_fs.default.unlinkSync(sourcePath);
|
|
13725
|
-
|
|
13663
|
+
return cmdOk({
|
|
13726
13664
|
completed: true,
|
|
13727
13665
|
file: filename,
|
|
13728
13666
|
date: today
|
|
13729
|
-
}, raw
|
|
13667
|
+
}, raw ? "completed" : void 0);
|
|
13730
13668
|
}
|
|
13731
13669
|
function cmdScaffold(cwd, type, options, raw) {
|
|
13732
13670
|
const { phase, name } = options;
|
|
@@ -13734,7 +13672,7 @@ function cmdScaffold(cwd, type, options, raw) {
|
|
|
13734
13672
|
const today = todayISO();
|
|
13735
13673
|
const phaseInfo = phase ? findPhaseInternal(cwd, phase) : null;
|
|
13736
13674
|
const phaseDir = phaseInfo ? node_path.default.join(cwd, phaseInfo.directory) : null;
|
|
13737
|
-
if (phase && !phaseDir && type !== "phase-dir")
|
|
13675
|
+
if (phase && !phaseDir && type !== "phase-dir") return cmdErr(`Phase ${phase} directory not found`);
|
|
13738
13676
|
let filePath;
|
|
13739
13677
|
let content;
|
|
13740
13678
|
switch (type) {
|
|
@@ -13751,37 +13689,31 @@ function cmdScaffold(cwd, type, options, raw) {
|
|
|
13751
13689
|
content = `---\nphase: "${padded}"\nname: "${name || phaseInfo?.phase_name || "Unnamed"}"\ncreated: ${today}\nstatus: pending\n---\n\n# Phase ${phase}: ${name || phaseInfo?.phase_name || "Unnamed"} — Verification\n\n## Goal-Backward Verification\n\n**Phase Goal:** [From ROADMAP.md]\n\n## Checks\n\n| # | Requirement | Status | Evidence |\n|---|------------|--------|----------|\n\n## Result\n\n_Pending verification_\n`;
|
|
13752
13690
|
break;
|
|
13753
13691
|
case "phase-dir": {
|
|
13754
|
-
if (!phase || !name)
|
|
13692
|
+
if (!phase || !name) return cmdErr("phase and name required for phase-dir scaffold");
|
|
13755
13693
|
const dirName = `${padded}-${generateSlugInternal(name)}`;
|
|
13756
13694
|
const phasesParent = phasesPath(cwd);
|
|
13757
13695
|
node_fs.default.mkdirSync(phasesParent, { recursive: true });
|
|
13758
13696
|
const dirPath = node_path.default.join(phasesParent, dirName);
|
|
13759
13697
|
node_fs.default.mkdirSync(dirPath, { recursive: true });
|
|
13760
|
-
|
|
13698
|
+
return cmdOk({
|
|
13761
13699
|
created: true,
|
|
13762
13700
|
directory: `.planning/phases/${dirName}`,
|
|
13763
13701
|
path: dirPath
|
|
13764
|
-
}, raw
|
|
13765
|
-
return;
|
|
13702
|
+
}, raw ? dirPath : void 0);
|
|
13766
13703
|
}
|
|
13767
|
-
default:
|
|
13768
|
-
error(`Unknown scaffold type: ${type}. Available: context, uat, verification, phase-dir`);
|
|
13769
|
-
return;
|
|
13770
|
-
}
|
|
13771
|
-
if (node_fs.default.existsSync(filePath)) {
|
|
13772
|
-
output({
|
|
13773
|
-
created: false,
|
|
13774
|
-
reason: "already_exists",
|
|
13775
|
-
path: filePath
|
|
13776
|
-
}, raw, "exists");
|
|
13777
|
-
return;
|
|
13704
|
+
default: return cmdErr(`Unknown scaffold type: ${type}. Available: context, uat, verification, phase-dir`);
|
|
13778
13705
|
}
|
|
13706
|
+
if (node_fs.default.existsSync(filePath)) return cmdOk({
|
|
13707
|
+
created: false,
|
|
13708
|
+
reason: "already_exists",
|
|
13709
|
+
path: filePath
|
|
13710
|
+
}, raw ? "exists" : void 0);
|
|
13779
13711
|
node_fs.default.writeFileSync(filePath, content, "utf-8");
|
|
13780
13712
|
const relPath = node_path.default.relative(cwd, filePath);
|
|
13781
|
-
|
|
13713
|
+
return cmdOk({
|
|
13782
13714
|
created: true,
|
|
13783
13715
|
path: relPath
|
|
13784
|
-
}, raw
|
|
13716
|
+
}, raw ? relPath : void 0);
|
|
13785
13717
|
}
|
|
13786
13718
|
|
|
13787
13719
|
//#endregion
|
|
@@ -13791,27 +13723,24 @@ function cmdScaffold(cwd, type, options, raw) {
|
|
|
13791
13723
|
*
|
|
13792
13724
|
* Ported from maxsim/bin/lib/verify.cjs
|
|
13793
13725
|
*/
|
|
13794
|
-
async function cmdVerifySummary(cwd, summaryPath, checkFileCount
|
|
13795
|
-
if (!summaryPath)
|
|
13726
|
+
async function cmdVerifySummary(cwd, summaryPath, checkFileCount) {
|
|
13727
|
+
if (!summaryPath) return cmdErr("summary-path required");
|
|
13796
13728
|
const fullPath = node_path.default.join(cwd, summaryPath);
|
|
13797
13729
|
const checkCount = checkFileCount || 2;
|
|
13798
|
-
if (!node_fs.default.existsSync(fullPath)) {
|
|
13799
|
-
|
|
13800
|
-
|
|
13801
|
-
|
|
13802
|
-
|
|
13803
|
-
|
|
13804
|
-
|
|
13805
|
-
|
|
13806
|
-
missing: []
|
|
13807
|
-
},
|
|
13808
|
-
commits_exist: false,
|
|
13809
|
-
self_check: "not_found"
|
|
13730
|
+
if (!node_fs.default.existsSync(fullPath)) return cmdOk({
|
|
13731
|
+
passed: false,
|
|
13732
|
+
checks: {
|
|
13733
|
+
summary_exists: false,
|
|
13734
|
+
files_created: {
|
|
13735
|
+
checked: 0,
|
|
13736
|
+
found: 0,
|
|
13737
|
+
missing: []
|
|
13810
13738
|
},
|
|
13811
|
-
|
|
13812
|
-
|
|
13813
|
-
|
|
13814
|
-
|
|
13739
|
+
commits_exist: false,
|
|
13740
|
+
self_check: "not_found"
|
|
13741
|
+
},
|
|
13742
|
+
errors: ["SUMMARY.md not found"]
|
|
13743
|
+
}, "failed");
|
|
13815
13744
|
const content = node_fs.default.readFileSync(fullPath, "utf-8");
|
|
13816
13745
|
const errors = [];
|
|
13817
13746
|
const mentionedFiles = /* @__PURE__ */ new Set();
|
|
@@ -13861,22 +13790,19 @@ async function cmdVerifySummary(cwd, summaryPath, checkFileCount, raw) {
|
|
|
13861
13790
|
self_check: selfCheck
|
|
13862
13791
|
};
|
|
13863
13792
|
const passed = missing.length === 0 && selfCheck !== "failed";
|
|
13864
|
-
|
|
13793
|
+
return cmdOk({
|
|
13865
13794
|
passed,
|
|
13866
13795
|
checks,
|
|
13867
13796
|
errors
|
|
13868
|
-
},
|
|
13797
|
+
}, passed ? "passed" : "failed");
|
|
13869
13798
|
}
|
|
13870
|
-
function cmdVerifyPlanStructure(cwd, filePath
|
|
13871
|
-
if (!filePath)
|
|
13799
|
+
function cmdVerifyPlanStructure(cwd, filePath) {
|
|
13800
|
+
if (!filePath) return cmdErr("file path required");
|
|
13872
13801
|
const content = safeReadFile(node_path.default.isAbsolute(filePath) ? filePath : node_path.default.join(cwd, filePath));
|
|
13873
|
-
if (!content) {
|
|
13874
|
-
|
|
13875
|
-
|
|
13876
|
-
|
|
13877
|
-
}, raw);
|
|
13878
|
-
return;
|
|
13879
|
-
}
|
|
13802
|
+
if (!content) return cmdOk({
|
|
13803
|
+
error: "File not found",
|
|
13804
|
+
path: filePath
|
|
13805
|
+
});
|
|
13880
13806
|
const fm = extractFrontmatter(content);
|
|
13881
13807
|
const errors = [];
|
|
13882
13808
|
const warnings = [];
|
|
@@ -13917,25 +13843,22 @@ function cmdVerifyPlanStructure(cwd, filePath, raw) {
|
|
|
13917
13843
|
if (tasks.length === 0) warnings.push("No <task> elements found");
|
|
13918
13844
|
if (fm.wave && parseInt(String(fm.wave)) > 1 && (!fm.depends_on || Array.isArray(fm.depends_on) && fm.depends_on.length === 0)) warnings.push("Wave > 1 but depends_on is empty");
|
|
13919
13845
|
if (/<task\s+type=["']?checkpoint/.test(content) && fm.autonomous !== "false" && fm.autonomous !== false) errors.push("Has checkpoint tasks but autonomous is not false");
|
|
13920
|
-
|
|
13846
|
+
return cmdOk({
|
|
13921
13847
|
valid: errors.length === 0,
|
|
13922
13848
|
errors,
|
|
13923
13849
|
warnings,
|
|
13924
13850
|
task_count: tasks.length,
|
|
13925
13851
|
tasks,
|
|
13926
13852
|
frontmatter_fields: Object.keys(fm)
|
|
13927
|
-
},
|
|
13853
|
+
}, errors.length === 0 ? "valid" : "invalid");
|
|
13928
13854
|
}
|
|
13929
|
-
function cmdVerifyPhaseCompleteness(cwd, phase
|
|
13930
|
-
if (!phase)
|
|
13855
|
+
function cmdVerifyPhaseCompleteness(cwd, phase) {
|
|
13856
|
+
if (!phase) return cmdErr("phase required");
|
|
13931
13857
|
const phaseInfo = findPhaseInternal(cwd, phase);
|
|
13932
|
-
if (!phaseInfo) {
|
|
13933
|
-
|
|
13934
|
-
|
|
13935
|
-
|
|
13936
|
-
}, raw);
|
|
13937
|
-
return;
|
|
13938
|
-
}
|
|
13858
|
+
if (!phaseInfo) return cmdOk({
|
|
13859
|
+
error: "Phase not found",
|
|
13860
|
+
phase
|
|
13861
|
+
});
|
|
13939
13862
|
const errors = [];
|
|
13940
13863
|
const warnings = [];
|
|
13941
13864
|
const phaseDir = node_path.default.join(cwd, phaseInfo.directory);
|
|
@@ -13943,8 +13866,7 @@ function cmdVerifyPhaseCompleteness(cwd, phase, raw) {
|
|
|
13943
13866
|
try {
|
|
13944
13867
|
files = node_fs.default.readdirSync(phaseDir);
|
|
13945
13868
|
} catch {
|
|
13946
|
-
|
|
13947
|
-
return;
|
|
13869
|
+
return cmdOk({ error: "Cannot read phase directory" });
|
|
13948
13870
|
}
|
|
13949
13871
|
const plans = files.filter((f) => isPlanFile(f));
|
|
13950
13872
|
const summaries = files.filter((f) => isSummaryFile(f));
|
|
@@ -13954,7 +13876,7 @@ function cmdVerifyPhaseCompleteness(cwd, phase, raw) {
|
|
|
13954
13876
|
if (incompletePlans.length > 0) errors.push(`Plans without summaries: ${incompletePlans.join(", ")}`);
|
|
13955
13877
|
const orphanSummaries = [...summaryIds].filter((id) => !planIds.has(id));
|
|
13956
13878
|
if (orphanSummaries.length > 0) warnings.push(`Summaries without plans: ${orphanSummaries.join(", ")}`);
|
|
13957
|
-
|
|
13879
|
+
return cmdOk({
|
|
13958
13880
|
complete: errors.length === 0,
|
|
13959
13881
|
phase: phaseInfo.phase_number,
|
|
13960
13882
|
plan_count: plans.length,
|
|
@@ -13963,18 +13885,15 @@ function cmdVerifyPhaseCompleteness(cwd, phase, raw) {
|
|
|
13963
13885
|
orphan_summaries: orphanSummaries,
|
|
13964
13886
|
errors,
|
|
13965
13887
|
warnings
|
|
13966
|
-
},
|
|
13888
|
+
}, errors.length === 0 ? "complete" : "incomplete");
|
|
13967
13889
|
}
|
|
13968
|
-
function cmdVerifyReferences(cwd, filePath
|
|
13969
|
-
if (!filePath)
|
|
13890
|
+
function cmdVerifyReferences(cwd, filePath) {
|
|
13891
|
+
if (!filePath) return cmdErr("file path required");
|
|
13970
13892
|
const content = safeReadFile(node_path.default.isAbsolute(filePath) ? filePath : node_path.default.join(cwd, filePath));
|
|
13971
|
-
if (!content) {
|
|
13972
|
-
|
|
13973
|
-
|
|
13974
|
-
|
|
13975
|
-
}, raw);
|
|
13976
|
-
return;
|
|
13977
|
-
}
|
|
13893
|
+
if (!content) return cmdOk({
|
|
13894
|
+
error: "File not found",
|
|
13895
|
+
path: filePath
|
|
13896
|
+
});
|
|
13978
13897
|
const found = [];
|
|
13979
13898
|
const missing = [];
|
|
13980
13899
|
const atRefs = content.match(/@([^\s\n,)]+\/[^\s\n,)]+)/g) || [];
|
|
@@ -13993,15 +13912,15 @@ function cmdVerifyReferences(cwd, filePath, raw) {
|
|
|
13993
13912
|
if (node_fs.default.existsSync(resolved)) found.push(cleanRef);
|
|
13994
13913
|
else missing.push(cleanRef);
|
|
13995
13914
|
}
|
|
13996
|
-
|
|
13915
|
+
return cmdOk({
|
|
13997
13916
|
valid: missing.length === 0,
|
|
13998
13917
|
found: found.length,
|
|
13999
13918
|
missing,
|
|
14000
13919
|
total: found.length + missing.length
|
|
14001
|
-
},
|
|
13920
|
+
}, missing.length === 0 ? "valid" : "invalid");
|
|
14002
13921
|
}
|
|
14003
|
-
async function cmdVerifyCommits(cwd, hashes
|
|
14004
|
-
if (!hashes || hashes.length === 0)
|
|
13922
|
+
async function cmdVerifyCommits(cwd, hashes) {
|
|
13923
|
+
if (!hashes || hashes.length === 0) return cmdErr("At least one commit hash required");
|
|
14005
13924
|
const valid = [];
|
|
14006
13925
|
const invalid = [];
|
|
14007
13926
|
for (const hash of hashes) {
|
|
@@ -14013,31 +13932,25 @@ async function cmdVerifyCommits(cwd, hashes, raw) {
|
|
|
14013
13932
|
if (result.exitCode === 0 && result.stdout.trim() === "commit") valid.push(hash);
|
|
14014
13933
|
else invalid.push(hash);
|
|
14015
13934
|
}
|
|
14016
|
-
|
|
13935
|
+
return cmdOk({
|
|
14017
13936
|
all_valid: invalid.length === 0,
|
|
14018
13937
|
valid,
|
|
14019
13938
|
invalid,
|
|
14020
13939
|
total: hashes.length
|
|
14021
|
-
},
|
|
13940
|
+
}, invalid.length === 0 ? "valid" : "invalid");
|
|
14022
13941
|
}
|
|
14023
|
-
function cmdVerifyArtifacts(cwd, planFilePath
|
|
14024
|
-
if (!planFilePath)
|
|
13942
|
+
function cmdVerifyArtifacts(cwd, planFilePath) {
|
|
13943
|
+
if (!planFilePath) return cmdErr("plan file path required");
|
|
14025
13944
|
const content = safeReadFile(node_path.default.isAbsolute(planFilePath) ? planFilePath : node_path.default.join(cwd, planFilePath));
|
|
14026
|
-
if (!content) {
|
|
14027
|
-
|
|
14028
|
-
|
|
14029
|
-
|
|
14030
|
-
}, raw);
|
|
14031
|
-
return;
|
|
14032
|
-
}
|
|
13945
|
+
if (!content) return cmdOk({
|
|
13946
|
+
error: "File not found",
|
|
13947
|
+
path: planFilePath
|
|
13948
|
+
});
|
|
14033
13949
|
const artifacts = parseMustHavesBlock(content, "artifacts");
|
|
14034
|
-
if (artifacts.length === 0) {
|
|
14035
|
-
|
|
14036
|
-
|
|
14037
|
-
|
|
14038
|
-
}, raw);
|
|
14039
|
-
return;
|
|
14040
|
-
}
|
|
13950
|
+
if (artifacts.length === 0) return cmdOk({
|
|
13951
|
+
error: "No must_haves.artifacts found in frontmatter",
|
|
13952
|
+
path: planFilePath
|
|
13953
|
+
});
|
|
14041
13954
|
const results = [];
|
|
14042
13955
|
for (const artifact of artifacts) {
|
|
14043
13956
|
if (typeof artifact === "string") continue;
|
|
@@ -14066,31 +13979,25 @@ function cmdVerifyArtifacts(cwd, planFilePath, raw) {
|
|
|
14066
13979
|
results.push(check);
|
|
14067
13980
|
}
|
|
14068
13981
|
const passed = results.filter((r) => r.passed).length;
|
|
14069
|
-
|
|
13982
|
+
return cmdOk({
|
|
14070
13983
|
all_passed: passed === results.length,
|
|
14071
13984
|
passed,
|
|
14072
13985
|
total: results.length,
|
|
14073
13986
|
artifacts: results
|
|
14074
|
-
},
|
|
13987
|
+
}, passed === results.length ? "valid" : "invalid");
|
|
14075
13988
|
}
|
|
14076
|
-
function cmdVerifyKeyLinks(cwd, planFilePath
|
|
14077
|
-
if (!planFilePath)
|
|
13989
|
+
function cmdVerifyKeyLinks(cwd, planFilePath) {
|
|
13990
|
+
if (!planFilePath) return cmdErr("plan file path required");
|
|
14078
13991
|
const content = safeReadFile(node_path.default.isAbsolute(planFilePath) ? planFilePath : node_path.default.join(cwd, planFilePath));
|
|
14079
|
-
if (!content) {
|
|
14080
|
-
|
|
14081
|
-
|
|
14082
|
-
|
|
14083
|
-
}, raw);
|
|
14084
|
-
return;
|
|
14085
|
-
}
|
|
13992
|
+
if (!content) return cmdOk({
|
|
13993
|
+
error: "File not found",
|
|
13994
|
+
path: planFilePath
|
|
13995
|
+
});
|
|
14086
13996
|
const keyLinks = parseMustHavesBlock(content, "key_links");
|
|
14087
|
-
if (keyLinks.length === 0) {
|
|
14088
|
-
|
|
14089
|
-
|
|
14090
|
-
|
|
14091
|
-
}, raw);
|
|
14092
|
-
return;
|
|
14093
|
-
}
|
|
13997
|
+
if (keyLinks.length === 0) return cmdOk({
|
|
13998
|
+
error: "No must_haves.key_links found in frontmatter",
|
|
13999
|
+
path: planFilePath
|
|
14000
|
+
});
|
|
14094
14001
|
const results = [];
|
|
14095
14002
|
for (const link of keyLinks) {
|
|
14096
14003
|
if (typeof link === "string") continue;
|
|
@@ -14126,26 +14033,25 @@ function cmdVerifyKeyLinks(cwd, planFilePath, raw) {
|
|
|
14126
14033
|
results.push(check);
|
|
14127
14034
|
}
|
|
14128
14035
|
const verified = results.filter((r) => r.verified).length;
|
|
14129
|
-
|
|
14036
|
+
return cmdOk({
|
|
14130
14037
|
all_verified: verified === results.length,
|
|
14131
14038
|
verified,
|
|
14132
14039
|
total: results.length,
|
|
14133
14040
|
links: results
|
|
14134
|
-
},
|
|
14041
|
+
}, verified === results.length ? "valid" : "invalid");
|
|
14135
14042
|
}
|
|
14136
|
-
function cmdValidateConsistency(cwd
|
|
14043
|
+
function cmdValidateConsistency(cwd) {
|
|
14137
14044
|
const rmPath = roadmapPath(cwd);
|
|
14138
14045
|
const phasesDir = phasesPath(cwd);
|
|
14139
14046
|
const errors = [];
|
|
14140
14047
|
const warnings = [];
|
|
14141
14048
|
if (!node_fs.default.existsSync(rmPath)) {
|
|
14142
14049
|
errors.push("ROADMAP.md not found");
|
|
14143
|
-
|
|
14050
|
+
return cmdOk({
|
|
14144
14051
|
passed: false,
|
|
14145
14052
|
errors,
|
|
14146
14053
|
warnings
|
|
14147
|
-
},
|
|
14148
|
-
return;
|
|
14054
|
+
}, "failed");
|
|
14149
14055
|
}
|
|
14150
14056
|
const roadmapContent = node_fs.default.readFileSync(rmPath, "utf-8");
|
|
14151
14057
|
const roadmapPhases = /* @__PURE__ */ new Set();
|
|
@@ -14197,14 +14103,14 @@ function cmdValidateConsistency(cwd, raw) {
|
|
|
14197
14103
|
debugLog(e);
|
|
14198
14104
|
}
|
|
14199
14105
|
const passed = errors.length === 0;
|
|
14200
|
-
|
|
14106
|
+
return cmdOk({
|
|
14201
14107
|
passed,
|
|
14202
14108
|
errors,
|
|
14203
14109
|
warnings,
|
|
14204
14110
|
warning_count: warnings.length
|
|
14205
|
-
},
|
|
14111
|
+
}, passed ? "passed" : "failed");
|
|
14206
14112
|
}
|
|
14207
|
-
function cmdValidateHealth(cwd, options
|
|
14113
|
+
function cmdValidateHealth(cwd, options) {
|
|
14208
14114
|
const planningDir = planningPath(cwd);
|
|
14209
14115
|
const projectPath = planningPath(cwd, "PROJECT.md");
|
|
14210
14116
|
const rmPath = roadmapPath(cwd);
|
|
@@ -14228,14 +14134,13 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
14228
14134
|
};
|
|
14229
14135
|
if (!node_fs.default.existsSync(planningDir)) {
|
|
14230
14136
|
addIssue("error", "E001", ".planning/ directory not found", "Run /maxsim:new-project to initialize");
|
|
14231
|
-
|
|
14137
|
+
return cmdOk({
|
|
14232
14138
|
status: "broken",
|
|
14233
14139
|
errors,
|
|
14234
14140
|
warnings,
|
|
14235
14141
|
info,
|
|
14236
14142
|
repairable_count: 0
|
|
14237
|
-
}
|
|
14238
|
-
return;
|
|
14143
|
+
});
|
|
14239
14144
|
}
|
|
14240
14145
|
if (!node_fs.default.existsSync(projectPath)) addIssue("error", "E002", "PROJECT.md not found", "Run /maxsim:new-project to create");
|
|
14241
14146
|
else {
|
|
@@ -14395,14 +14300,14 @@ function cmdValidateHealth(cwd, options, raw) {
|
|
|
14395
14300
|
else if (warnings.length > 0) status = "degraded";
|
|
14396
14301
|
else status = "healthy";
|
|
14397
14302
|
const repairableCount = errors.filter((e) => e.repairable).length + warnings.filter((w) => w.repairable).length;
|
|
14398
|
-
|
|
14303
|
+
return cmdOk({
|
|
14399
14304
|
status,
|
|
14400
14305
|
errors,
|
|
14401
14306
|
warnings,
|
|
14402
14307
|
info,
|
|
14403
14308
|
repairable_count: repairableCount,
|
|
14404
14309
|
repairs_performed: repairActions.length > 0 ? repairActions : void 0
|
|
14405
|
-
}
|
|
14310
|
+
});
|
|
14406
14311
|
}
|
|
14407
14312
|
|
|
14408
14313
|
//#endregion
|
|
@@ -14468,7 +14373,7 @@ function phaseInsertCore(cwd, afterPhase, description, options) {
|
|
|
14468
14373
|
if (dm) existingDecimals.push(parseInt(dm[1], 10));
|
|
14469
14374
|
}
|
|
14470
14375
|
} catch (e) {
|
|
14471
|
-
debugLog(e);
|
|
14376
|
+
debugLog("phase-insert-decimal-scan-failed", e);
|
|
14472
14377
|
}
|
|
14473
14378
|
const decimalPhase = `${normalizedBase}.${existingDecimals.length === 0 ? 1 : Math.max(...existingDecimals) + 1}`;
|
|
14474
14379
|
const dirName = `${decimalPhase}-${slug}`;
|
|
@@ -14514,7 +14419,9 @@ function phaseCompleteCore(cwd, phaseNum) {
|
|
|
14514
14419
|
roadmapContent = roadmapContent.replace(tablePattern, `$1 Complete $2 ${today} $3`);
|
|
14515
14420
|
const planCountPattern = new RegExp(`(#{2,4}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`, "i");
|
|
14516
14421
|
roadmapContent = roadmapContent.replace(planCountPattern, `$1${summaryCount}/${planCount} plans complete`);
|
|
14422
|
+
debugLog("phase-complete-write", `writing ROADMAP.md for phase ${phaseNum}`);
|
|
14517
14423
|
node_fs.default.writeFileSync(rmPath, roadmapContent, "utf-8");
|
|
14424
|
+
debugLog("phase-complete-write", `ROADMAP.md updated for phase ${phaseNum}`);
|
|
14518
14425
|
const reqPath = planningPath(cwd, "REQUIREMENTS.md");
|
|
14519
14426
|
if (node_fs.default.existsSync(reqPath)) {
|
|
14520
14427
|
const reqMatch = roadmapContent.match(new RegExp(`Phase\\s+${escapePhaseNum(phaseNum)}[\\s\\S]*?\\*\\*Requirements:\\*\\*\\s*([^\\n]+)`, "i"));
|
|
@@ -14525,7 +14432,9 @@ function phaseCompleteCore(cwd, phaseNum) {
|
|
|
14525
14432
|
reqContent = reqContent.replace(new RegExp(`(-\\s*\\[)[ ](\\]\\s*\\*\\*${reqId}\\*\\*)`, "gi"), "$1x$2");
|
|
14526
14433
|
reqContent = reqContent.replace(new RegExp(`(\\|\\s*${reqId}\\s*\\|[^|]+\\|)\\s*Pending\\s*(\\|)`, "gi"), "$1 Complete $2");
|
|
14527
14434
|
}
|
|
14435
|
+
debugLog("phase-complete-write", `writing REQUIREMENTS.md for phase ${phaseNum}`);
|
|
14528
14436
|
node_fs.default.writeFileSync(reqPath, reqContent, "utf-8");
|
|
14437
|
+
debugLog("phase-complete-write", `REQUIREMENTS.md updated for phase ${phaseNum}`);
|
|
14529
14438
|
requirementsUpdated = true;
|
|
14530
14439
|
}
|
|
14531
14440
|
}
|
|
@@ -14547,7 +14456,7 @@ function phaseCompleteCore(cwd, phaseNum) {
|
|
|
14547
14456
|
}
|
|
14548
14457
|
}
|
|
14549
14458
|
} catch (e) {
|
|
14550
|
-
debugLog(e);
|
|
14459
|
+
debugLog("phase-complete-next-phase-scan-failed", e);
|
|
14551
14460
|
}
|
|
14552
14461
|
if (node_fs.default.existsSync(stPath)) {
|
|
14553
14462
|
let stateContent = node_fs.default.readFileSync(stPath, "utf-8");
|
|
@@ -14557,7 +14466,9 @@ function phaseCompleteCore(cwd, phaseNum) {
|
|
|
14557
14466
|
stateContent = stateContent.replace(/(\*\*Current Plan:\*\*\s*).*/, `$1Not started`);
|
|
14558
14467
|
stateContent = stateContent.replace(/(\*\*Last Activity:\*\*\s*).*/, `$1${today}`);
|
|
14559
14468
|
stateContent = stateContent.replace(/(\*\*Last Activity Description:\*\*\s*).*/, `$1Phase ${phaseNum} complete${nextPhaseNum ? `, transitioned to Phase ${nextPhaseNum}` : ""}`);
|
|
14469
|
+
debugLog("phase-complete-write", `writing STATE.md for phase ${phaseNum}`);
|
|
14560
14470
|
node_fs.default.writeFileSync(stPath, stateContent, "utf-8");
|
|
14471
|
+
debugLog("phase-complete-write", `STATE.md updated for phase ${phaseNum}`);
|
|
14561
14472
|
}
|
|
14562
14473
|
return {
|
|
14563
14474
|
completed_phase: phaseNum,
|
|
@@ -14572,22 +14483,21 @@ function phaseCompleteCore(cwd, phaseNum) {
|
|
|
14572
14483
|
requirements_updated: requirementsUpdated
|
|
14573
14484
|
};
|
|
14574
14485
|
}
|
|
14575
|
-
function cmdPhasesList(cwd, options
|
|
14486
|
+
async function cmdPhasesList(cwd, options) {
|
|
14576
14487
|
const phasesDirPath = phasesPath(cwd);
|
|
14577
|
-
const { type, phase, includeArchived } = options;
|
|
14578
|
-
if (!node_fs.default.existsSync(phasesDirPath)) {
|
|
14579
|
-
|
|
14580
|
-
|
|
14581
|
-
|
|
14582
|
-
|
|
14583
|
-
|
|
14584
|
-
|
|
14585
|
-
|
|
14586
|
-
|
|
14587
|
-
|
|
14588
|
-
}
|
|
14488
|
+
const { type, phase, includeArchived, offset, limit } = options;
|
|
14489
|
+
if (!node_fs.default.existsSync(phasesDirPath)) if (type) return cmdOk({
|
|
14490
|
+
files: [],
|
|
14491
|
+
count: 0,
|
|
14492
|
+
total: 0
|
|
14493
|
+
}, "");
|
|
14494
|
+
else return cmdOk({
|
|
14495
|
+
directories: [],
|
|
14496
|
+
count: 0,
|
|
14497
|
+
total: 0
|
|
14498
|
+
}, "");
|
|
14589
14499
|
try {
|
|
14590
|
-
let dirs =
|
|
14500
|
+
let dirs = await listSubDirsAsync(phasesDirPath);
|
|
14591
14501
|
if (includeArchived) {
|
|
14592
14502
|
const archived = getArchivedPhaseDirs(cwd);
|
|
14593
14503
|
for (const a of archived) dirs.push(`${a.name} [${a.milestone}]`);
|
|
@@ -14596,56 +14506,53 @@ function cmdPhasesList(cwd, options, raw) {
|
|
|
14596
14506
|
if (phase) {
|
|
14597
14507
|
const normalized = normalizePhaseName(phase);
|
|
14598
14508
|
const match = dirs.find((d) => d.startsWith(normalized));
|
|
14599
|
-
if (!match) {
|
|
14600
|
-
|
|
14601
|
-
|
|
14602
|
-
|
|
14603
|
-
|
|
14604
|
-
|
|
14605
|
-
|
|
14606
|
-
return;
|
|
14607
|
-
}
|
|
14509
|
+
if (!match) return cmdOk({
|
|
14510
|
+
files: [],
|
|
14511
|
+
count: 0,
|
|
14512
|
+
total: 0,
|
|
14513
|
+
phase_dir: null,
|
|
14514
|
+
error: "Phase not found"
|
|
14515
|
+
}, "");
|
|
14608
14516
|
dirs = [match];
|
|
14609
14517
|
}
|
|
14610
14518
|
if (type) {
|
|
14611
|
-
const files =
|
|
14612
|
-
for (const dir of dirs) {
|
|
14519
|
+
const files = (await Promise.all(dirs.map(async (dir) => {
|
|
14613
14520
|
const dirPath = node_path.default.join(phasesDirPath, dir);
|
|
14614
|
-
const dirFiles = node_fs.default.
|
|
14521
|
+
const dirFiles = await node_fs.default.promises.readdir(dirPath);
|
|
14615
14522
|
let filtered;
|
|
14616
14523
|
if (type === "plans") filtered = dirFiles.filter(isPlanFile);
|
|
14617
14524
|
else if (type === "summaries") filtered = dirFiles.filter(isSummaryFile);
|
|
14618
14525
|
else filtered = dirFiles;
|
|
14619
|
-
|
|
14620
|
-
}
|
|
14621
|
-
|
|
14526
|
+
return filtered.sort();
|
|
14527
|
+
}))).flat();
|
|
14528
|
+
return cmdOk({
|
|
14622
14529
|
files,
|
|
14623
14530
|
count: files.length,
|
|
14531
|
+
total: files.length,
|
|
14624
14532
|
phase_dir: phase ? dirs[0].replace(/^\d+(?:\.\d+)?-?/, "") : null
|
|
14625
|
-
},
|
|
14626
|
-
|
|
14627
|
-
|
|
14628
|
-
|
|
14629
|
-
|
|
14630
|
-
|
|
14631
|
-
|
|
14533
|
+
}, files.join("\n"));
|
|
14534
|
+
}
|
|
14535
|
+
const total = dirs.length;
|
|
14536
|
+
const start = offset ?? 0;
|
|
14537
|
+
const paginated = limit !== void 0 ? dirs.slice(start, start + limit) : dirs.slice(start);
|
|
14538
|
+
return cmdOk({
|
|
14539
|
+
directories: paginated,
|
|
14540
|
+
count: paginated.length,
|
|
14541
|
+
total
|
|
14542
|
+
}, paginated.join("\n"));
|
|
14632
14543
|
} catch (e) {
|
|
14633
|
-
|
|
14634
|
-
error("Failed to list phases: " + e.message);
|
|
14544
|
+
return cmdErr("Failed to list phases: " + e.message);
|
|
14635
14545
|
}
|
|
14636
14546
|
}
|
|
14637
|
-
function cmdPhaseNextDecimal(cwd, basePhase
|
|
14547
|
+
function cmdPhaseNextDecimal(cwd, basePhase) {
|
|
14638
14548
|
const phasesDirPath = phasesPath(cwd);
|
|
14639
14549
|
const normalized = normalizePhaseName(basePhase);
|
|
14640
|
-
if (!node_fs.default.existsSync(phasesDirPath)) {
|
|
14641
|
-
|
|
14642
|
-
|
|
14643
|
-
|
|
14644
|
-
|
|
14645
|
-
|
|
14646
|
-
}, raw, `${normalized}.1`);
|
|
14647
|
-
return;
|
|
14648
|
-
}
|
|
14550
|
+
if (!node_fs.default.existsSync(phasesDirPath)) return cmdOk({
|
|
14551
|
+
found: false,
|
|
14552
|
+
base_phase: normalized,
|
|
14553
|
+
next: `${normalized}.1`,
|
|
14554
|
+
existing: []
|
|
14555
|
+
}, `${normalized}.1`);
|
|
14649
14556
|
try {
|
|
14650
14557
|
const dirs = listSubDirs(phasesDirPath);
|
|
14651
14558
|
const baseExists = dirs.some((d) => d.startsWith(normalized + "-") || d === normalized);
|
|
@@ -14664,19 +14571,18 @@ function cmdPhaseNextDecimal(cwd, basePhase, raw) {
|
|
|
14664
14571
|
const lastDecimal = existingDecimals[existingDecimals.length - 1];
|
|
14665
14572
|
nextDecimal = `${normalized}.${parseInt(lastDecimal.split(".")[1], 10) + 1}`;
|
|
14666
14573
|
}
|
|
14667
|
-
|
|
14574
|
+
return cmdOk({
|
|
14668
14575
|
found: baseExists,
|
|
14669
14576
|
base_phase: normalized,
|
|
14670
14577
|
next: nextDecimal,
|
|
14671
14578
|
existing: existingDecimals
|
|
14672
|
-
},
|
|
14579
|
+
}, nextDecimal);
|
|
14673
14580
|
} catch (e) {
|
|
14674
|
-
|
|
14675
|
-
error("Failed to calculate next decimal phase: " + e.message);
|
|
14581
|
+
return cmdErr("Failed to calculate next decimal phase: " + e.message);
|
|
14676
14582
|
}
|
|
14677
14583
|
}
|
|
14678
|
-
function cmdFindPhase(cwd, phase
|
|
14679
|
-
if (!phase)
|
|
14584
|
+
function cmdFindPhase(cwd, phase) {
|
|
14585
|
+
if (!phase) return cmdErr("phase identifier required");
|
|
14680
14586
|
const phasesDirPath = phasesPath(cwd);
|
|
14681
14587
|
const normalized = normalizePhaseName(phase);
|
|
14682
14588
|
const notFound = {
|
|
@@ -14689,10 +14595,7 @@ function cmdFindPhase(cwd, phase, raw) {
|
|
|
14689
14595
|
};
|
|
14690
14596
|
try {
|
|
14691
14597
|
const match = listSubDirs(phasesDirPath, true).find((d) => d.startsWith(normalized));
|
|
14692
|
-
if (!match)
|
|
14693
|
-
output(notFound, raw, "");
|
|
14694
|
-
return;
|
|
14695
|
-
}
|
|
14598
|
+
if (!match) return cmdOk(notFound, "");
|
|
14696
14599
|
const dirMatch = match.match(/^(\d+[A-Z]?(?:\.\d+)?)-?(.*)/i);
|
|
14697
14600
|
const phaseNumber = dirMatch ? dirMatch[1] : normalized;
|
|
14698
14601
|
const phaseName = dirMatch && dirMatch[2] ? dirMatch[2] : null;
|
|
@@ -14708,14 +14611,13 @@ function cmdFindPhase(cwd, phase, raw) {
|
|
|
14708
14611
|
plans,
|
|
14709
14612
|
summaries
|
|
14710
14613
|
};
|
|
14711
|
-
|
|
14614
|
+
return cmdOk(result, result.directory);
|
|
14712
14615
|
} catch (e) {
|
|
14713
|
-
|
|
14714
|
-
output(notFound, raw, "");
|
|
14616
|
+
return cmdOk(notFound, "");
|
|
14715
14617
|
}
|
|
14716
14618
|
}
|
|
14717
|
-
function cmdPhasePlanIndex(cwd, phase
|
|
14718
|
-
if (!phase)
|
|
14619
|
+
function cmdPhasePlanIndex(cwd, phase) {
|
|
14620
|
+
if (!phase) return cmdErr("phase required for phase-plan-index");
|
|
14719
14621
|
const phasesDirPath = phasesPath(cwd);
|
|
14720
14622
|
const normalized = normalizePhaseName(phase);
|
|
14721
14623
|
let phaseDir = null;
|
|
@@ -14723,19 +14625,16 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
|
|
|
14723
14625
|
const match = listSubDirs(phasesDirPath, true).find((d) => d.startsWith(normalized));
|
|
14724
14626
|
if (match) phaseDir = node_path.default.join(phasesDirPath, match);
|
|
14725
14627
|
} catch (e) {
|
|
14726
|
-
debugLog(e);
|
|
14727
|
-
}
|
|
14728
|
-
if (!phaseDir) {
|
|
14729
|
-
output({
|
|
14730
|
-
phase: normalized,
|
|
14731
|
-
error: "Phase not found",
|
|
14732
|
-
plans: [],
|
|
14733
|
-
waves: {},
|
|
14734
|
-
incomplete: [],
|
|
14735
|
-
has_checkpoints: false
|
|
14736
|
-
}, raw);
|
|
14737
|
-
return;
|
|
14628
|
+
debugLog("phase-plan-index-failed", e);
|
|
14738
14629
|
}
|
|
14630
|
+
if (!phaseDir) return cmdOk({
|
|
14631
|
+
phase: normalized,
|
|
14632
|
+
error: "Phase not found",
|
|
14633
|
+
plans: [],
|
|
14634
|
+
waves: {},
|
|
14635
|
+
incomplete: [],
|
|
14636
|
+
has_checkpoints: false
|
|
14637
|
+
});
|
|
14739
14638
|
const phaseFiles = node_fs.default.readdirSync(phaseDir);
|
|
14740
14639
|
const planFiles = phaseFiles.filter(isPlanFile).sort();
|
|
14741
14640
|
const summaryFiles = phaseFiles.filter(isSummaryFile);
|
|
@@ -14772,64 +14671,62 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
|
|
|
14772
14671
|
if (!waves[waveKey]) waves[waveKey] = [];
|
|
14773
14672
|
waves[waveKey].push(id);
|
|
14774
14673
|
}
|
|
14775
|
-
|
|
14674
|
+
return cmdOk({
|
|
14776
14675
|
phase: normalized,
|
|
14777
14676
|
plans,
|
|
14778
14677
|
waves,
|
|
14779
14678
|
incomplete,
|
|
14780
14679
|
has_checkpoints: hasCheckpoints
|
|
14781
|
-
}
|
|
14680
|
+
});
|
|
14782
14681
|
}
|
|
14783
|
-
function cmdPhaseAdd(cwd, description
|
|
14784
|
-
if (!description)
|
|
14682
|
+
function cmdPhaseAdd(cwd, description) {
|
|
14683
|
+
if (!description) return cmdErr("description required for phase add");
|
|
14785
14684
|
try {
|
|
14786
14685
|
const result = phaseAddCore(cwd, description, { includeStubs: false });
|
|
14787
|
-
|
|
14686
|
+
return cmdOk({
|
|
14788
14687
|
phase_number: result.phase_number,
|
|
14789
14688
|
padded: result.padded,
|
|
14790
14689
|
name: result.description,
|
|
14791
14690
|
slug: result.slug,
|
|
14792
14691
|
directory: result.directory
|
|
14793
|
-
},
|
|
14692
|
+
}, result.padded);
|
|
14794
14693
|
} catch (e) {
|
|
14795
|
-
|
|
14796
|
-
error(e.message);
|
|
14694
|
+
return cmdErr(e.message);
|
|
14797
14695
|
}
|
|
14798
14696
|
}
|
|
14799
|
-
function cmdPhaseInsert(cwd, afterPhase, description
|
|
14800
|
-
if (!afterPhase || !description)
|
|
14697
|
+
function cmdPhaseInsert(cwd, afterPhase, description) {
|
|
14698
|
+
if (!afterPhase || !description) return cmdErr("after-phase and description required for phase insert");
|
|
14801
14699
|
try {
|
|
14802
14700
|
const result = phaseInsertCore(cwd, afterPhase, description, { includeStubs: false });
|
|
14803
|
-
|
|
14701
|
+
return cmdOk({
|
|
14804
14702
|
phase_number: result.phase_number,
|
|
14805
14703
|
after_phase: result.after_phase,
|
|
14806
14704
|
name: result.description,
|
|
14807
14705
|
slug: result.slug,
|
|
14808
14706
|
directory: result.directory
|
|
14809
|
-
},
|
|
14707
|
+
}, result.phase_number);
|
|
14810
14708
|
} catch (e) {
|
|
14811
|
-
|
|
14812
|
-
error(e.message);
|
|
14709
|
+
return cmdErr(e.message);
|
|
14813
14710
|
}
|
|
14814
14711
|
}
|
|
14815
|
-
function cmdPhaseRemove(cwd, targetPhase, options
|
|
14816
|
-
if (!targetPhase)
|
|
14712
|
+
function cmdPhaseRemove(cwd, targetPhase, options) {
|
|
14713
|
+
if (!targetPhase) return cmdErr("phase number required for phase remove");
|
|
14817
14714
|
const rmPath = roadmapPath(cwd);
|
|
14818
14715
|
const phasesDirPath = phasesPath(cwd);
|
|
14819
14716
|
const force = options.force || false;
|
|
14820
|
-
if (!node_fs.default.existsSync(rmPath))
|
|
14717
|
+
if (!node_fs.default.existsSync(rmPath)) return cmdErr("ROADMAP.md not found");
|
|
14821
14718
|
const normalized = normalizePhaseName(targetPhase);
|
|
14822
14719
|
const isDecimal = targetPhase.includes(".");
|
|
14823
14720
|
let targetDir = null;
|
|
14824
14721
|
try {
|
|
14825
14722
|
targetDir = listSubDirs(phasesDirPath, true).find((d) => d.startsWith(normalized + "-") || d === normalized) || null;
|
|
14826
14723
|
} catch (e) {
|
|
14827
|
-
debugLog(e);
|
|
14724
|
+
debugLog("phase-remove-find-target-failed", e);
|
|
14828
14725
|
}
|
|
14829
14726
|
if (targetDir && !force) {
|
|
14830
14727
|
const targetPath = node_path.default.join(phasesDirPath, targetDir);
|
|
14831
14728
|
const summaries = node_fs.default.readdirSync(targetPath).filter(isSummaryFile);
|
|
14832
|
-
if (summaries.length > 0)
|
|
14729
|
+
if (summaries.length > 0) return cmdErr(`Phase ${targetPhase} has ${summaries.length} executed plan(s). Use --force to remove anyway.`);
|
|
14833
14730
|
}
|
|
14834
14731
|
if (targetDir) node_fs.default.rmSync(node_path.default.join(phasesDirPath, targetDir), {
|
|
14835
14732
|
recursive: true,
|
|
@@ -14875,7 +14772,10 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
|
14875
14772
|
}
|
|
14876
14773
|
}
|
|
14877
14774
|
} catch (e) {
|
|
14878
|
-
debugLog(
|
|
14775
|
+
debugLog("phase-remove-decimal-rename-failed", {
|
|
14776
|
+
phase: targetPhase,
|
|
14777
|
+
error: errorMsg(e)
|
|
14778
|
+
});
|
|
14879
14779
|
}
|
|
14880
14780
|
} else {
|
|
14881
14781
|
const removedInt = parseInt(normalized, 10);
|
|
@@ -14923,7 +14823,10 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
|
14923
14823
|
}
|
|
14924
14824
|
}
|
|
14925
14825
|
} catch (e) {
|
|
14926
|
-
debugLog(
|
|
14826
|
+
debugLog("phase-remove-int-rename-failed", {
|
|
14827
|
+
phase: targetPhase,
|
|
14828
|
+
error: errorMsg(e)
|
|
14829
|
+
});
|
|
14927
14830
|
}
|
|
14928
14831
|
}
|
|
14929
14832
|
let roadmapContent = node_fs.default.readFileSync(rmPath, "utf-8");
|
|
@@ -14967,20 +14870,20 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
|
14967
14870
|
}
|
|
14968
14871
|
node_fs.default.writeFileSync(stPath, stateContent, "utf-8");
|
|
14969
14872
|
}
|
|
14970
|
-
|
|
14873
|
+
return cmdOk({
|
|
14971
14874
|
removed: targetPhase,
|
|
14972
14875
|
directory_deleted: targetDir || null,
|
|
14973
14876
|
renamed_directories: renamedDirs,
|
|
14974
14877
|
renamed_files: renamedFiles,
|
|
14975
14878
|
roadmap_updated: true,
|
|
14976
14879
|
state_updated: node_fs.default.existsSync(stPath)
|
|
14977
|
-
}
|
|
14880
|
+
});
|
|
14978
14881
|
}
|
|
14979
|
-
function cmdPhaseComplete(cwd, phaseNum
|
|
14980
|
-
if (!phaseNum)
|
|
14882
|
+
function cmdPhaseComplete(cwd, phaseNum) {
|
|
14883
|
+
if (!phaseNum) return cmdErr("phase number required for phase complete");
|
|
14981
14884
|
try {
|
|
14982
14885
|
const result = phaseCompleteCore(cwd, phaseNum);
|
|
14983
|
-
|
|
14886
|
+
return cmdOk({
|
|
14984
14887
|
completed_phase: result.completed_phase,
|
|
14985
14888
|
phase_name: result.phase_name,
|
|
14986
14889
|
plans_executed: result.plans_executed,
|
|
@@ -14990,10 +14893,9 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
14990
14893
|
date: result.date,
|
|
14991
14894
|
roadmap_updated: result.roadmap_updated,
|
|
14992
14895
|
state_updated: result.state_updated
|
|
14993
|
-
}
|
|
14896
|
+
});
|
|
14994
14897
|
} catch (e) {
|
|
14995
|
-
|
|
14996
|
-
error(e.message);
|
|
14898
|
+
return cmdErr(e.message);
|
|
14997
14899
|
}
|
|
14998
14900
|
}
|
|
14999
14901
|
|
|
@@ -15004,8 +14906,8 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
15004
14906
|
*
|
|
15005
14907
|
* Ported from maxsim/bin/lib/template.cjs
|
|
15006
14908
|
*/
|
|
15007
|
-
function cmdTemplateSelect(cwd, planPath
|
|
15008
|
-
if (!planPath)
|
|
14909
|
+
function cmdTemplateSelect(cwd, planPath) {
|
|
14910
|
+
if (!planPath) return cmdErr("plan-path required");
|
|
15009
14911
|
try {
|
|
15010
14912
|
const fullPath = node_path.default.join(cwd, planPath);
|
|
15011
14913
|
const content = node_fs.default.readFileSync(fullPath, "utf-8");
|
|
@@ -15025,32 +14927,29 @@ function cmdTemplateSelect(cwd, planPath, raw) {
|
|
|
15025
14927
|
template = "templates/summary-complex.md";
|
|
15026
14928
|
type = "complex";
|
|
15027
14929
|
}
|
|
15028
|
-
|
|
14930
|
+
return cmdOk({
|
|
15029
14931
|
template,
|
|
15030
14932
|
type,
|
|
15031
14933
|
taskCount,
|
|
15032
14934
|
fileCount,
|
|
15033
14935
|
hasDecisions
|
|
15034
|
-
},
|
|
14936
|
+
}, template);
|
|
15035
14937
|
} catch (thrown) {
|
|
15036
|
-
|
|
14938
|
+
return cmdOk({
|
|
15037
14939
|
template: "templates/summary-standard.md",
|
|
15038
14940
|
type: "standard",
|
|
15039
14941
|
error: thrown.message
|
|
15040
|
-
},
|
|
14942
|
+
}, "templates/summary-standard.md");
|
|
15041
14943
|
}
|
|
15042
14944
|
}
|
|
15043
|
-
function cmdTemplateFill(cwd, templateType, options
|
|
15044
|
-
if (!templateType)
|
|
15045
|
-
if (!options.phase)
|
|
14945
|
+
function cmdTemplateFill(cwd, templateType, options) {
|
|
14946
|
+
if (!templateType) return cmdErr("template type required: summary, plan, or verification");
|
|
14947
|
+
if (!options.phase) return cmdErr("--phase required");
|
|
15046
14948
|
const phaseInfo = findPhaseInternal(cwd, options.phase);
|
|
15047
|
-
if (!phaseInfo) {
|
|
15048
|
-
|
|
15049
|
-
|
|
15050
|
-
|
|
15051
|
-
}, raw);
|
|
15052
|
-
return;
|
|
15053
|
-
}
|
|
14949
|
+
if (!phaseInfo) return cmdOk({
|
|
14950
|
+
error: "Phase not found",
|
|
14951
|
+
phase: options.phase
|
|
14952
|
+
});
|
|
15054
14953
|
const padded = normalizePhaseName(options.phase);
|
|
15055
14954
|
const today = todayISO();
|
|
15056
14955
|
const phaseName = options.name || phaseInfo.phase_name || "Unnamed";
|
|
@@ -15196,26 +15095,371 @@ function cmdTemplateFill(cwd, templateType, options, raw) {
|
|
|
15196
15095
|
].join("\n");
|
|
15197
15096
|
fileName = `${padded}-VERIFICATION.md`;
|
|
15198
15097
|
break;
|
|
15199
|
-
default:
|
|
15200
|
-
error(`Unknown template type: ${templateType}. Available: summary, plan, verification`);
|
|
15201
|
-
return;
|
|
15098
|
+
default: return cmdErr(`Unknown template type: ${templateType}. Available: summary, plan, verification`);
|
|
15202
15099
|
}
|
|
15203
15100
|
const fullContent = `---\n${reconstructFrontmatter(frontmatter)}\n---\n\n${body}\n`;
|
|
15204
15101
|
const outPath = node_path.default.join(cwd, phaseInfo.directory, fileName);
|
|
15205
|
-
if (node_fs.default.existsSync(outPath)) {
|
|
15206
|
-
|
|
15207
|
-
|
|
15208
|
-
|
|
15209
|
-
}, raw);
|
|
15210
|
-
return;
|
|
15211
|
-
}
|
|
15102
|
+
if (node_fs.default.existsSync(outPath)) return cmdOk({
|
|
15103
|
+
error: "File already exists",
|
|
15104
|
+
path: node_path.default.relative(cwd, outPath)
|
|
15105
|
+
});
|
|
15212
15106
|
node_fs.default.writeFileSync(outPath, fullContent, "utf-8");
|
|
15213
15107
|
const relPath = node_path.default.relative(cwd, outPath);
|
|
15214
|
-
|
|
15108
|
+
return cmdOk({
|
|
15215
15109
|
created: true,
|
|
15216
15110
|
path: relPath,
|
|
15217
15111
|
template: templateType
|
|
15218
|
-
},
|
|
15112
|
+
}, relPath);
|
|
15113
|
+
}
|
|
15114
|
+
|
|
15115
|
+
//#endregion
|
|
15116
|
+
//#region src/core/artefakte.ts
|
|
15117
|
+
/**
|
|
15118
|
+
* Artefakte — CRUD operations for project-level artefakte files
|
|
15119
|
+
*
|
|
15120
|
+
* Manages DECISIONS.md, ACCEPTANCE-CRITERIA.md, and NO-GOS.md
|
|
15121
|
+
* at both project level (.planning/) and phase level (.planning/phases/<phase>/).
|
|
15122
|
+
*/
|
|
15123
|
+
const ARTEFAKT_FILES = {
|
|
15124
|
+
"decisions": "DECISIONS.md",
|
|
15125
|
+
"acceptance-criteria": "ACCEPTANCE-CRITERIA.md",
|
|
15126
|
+
"no-gos": "NO-GOS.md"
|
|
15127
|
+
};
|
|
15128
|
+
function isValidType(type) {
|
|
15129
|
+
return !!type && type in ARTEFAKT_FILES;
|
|
15130
|
+
}
|
|
15131
|
+
function resolveArtefaktPath(cwd, type, phase) {
|
|
15132
|
+
const filename = ARTEFAKT_FILES[type];
|
|
15133
|
+
if (phase) {
|
|
15134
|
+
const phaseInfo = findPhaseInternal(cwd, phase);
|
|
15135
|
+
if (!phaseInfo?.directory) return null;
|
|
15136
|
+
return node_path.default.join(cwd, phaseInfo.directory, filename);
|
|
15137
|
+
}
|
|
15138
|
+
return planningPath(cwd, filename);
|
|
15139
|
+
}
|
|
15140
|
+
function getTemplate(type) {
|
|
15141
|
+
const today = todayISO();
|
|
15142
|
+
switch (type) {
|
|
15143
|
+
case "decisions": return `# Decisions\n\n> Architectural and design decisions for this project.\n\n**Created:** ${today}\n\n## Decision Log\n\n| # | Decision | Rationale | Date | Phase |\n|---|----------|-----------|------|-------|\n`;
|
|
15144
|
+
case "acceptance-criteria": return `# Acceptance Criteria\n\n> Conditions that must be met for deliverables to be accepted.\n\n**Created:** ${today}\n\n## Criteria\n\n| # | Criterion | Status | Verified |\n|---|-----------|--------|----------|\n`;
|
|
15145
|
+
case "no-gos": return `# No-Gos\n\n> Things explicitly out of scope or forbidden.\n\n**Created:** ${today}\n\n## Boundaries\n\n- _No entries yet._\n`;
|
|
15146
|
+
}
|
|
15147
|
+
}
|
|
15148
|
+
function cmdArtefakteRead(cwd, type, phase, raw) {
|
|
15149
|
+
if (!isValidType(type)) return cmdErr(`Invalid artefakt type: ${type}. Available: ${Object.keys(ARTEFAKT_FILES).join(", ")}`);
|
|
15150
|
+
const filePath = resolveArtefaktPath(cwd, type, phase);
|
|
15151
|
+
if (!filePath) return cmdErr(`Phase ${phase} not found`);
|
|
15152
|
+
const content = safeReadFile(filePath);
|
|
15153
|
+
if (content === null) return cmdOk({
|
|
15154
|
+
exists: false,
|
|
15155
|
+
type,
|
|
15156
|
+
phase: phase ?? null,
|
|
15157
|
+
content: null
|
|
15158
|
+
}, raw ? "" : void 0);
|
|
15159
|
+
return cmdOk({
|
|
15160
|
+
exists: true,
|
|
15161
|
+
type,
|
|
15162
|
+
phase: phase ?? null,
|
|
15163
|
+
content
|
|
15164
|
+
}, raw ? content : void 0);
|
|
15165
|
+
}
|
|
15166
|
+
function cmdArtefakteWrite(cwd, type, content, phase, raw) {
|
|
15167
|
+
if (!isValidType(type)) return cmdErr(`Invalid artefakt type: ${type}. Available: ${Object.keys(ARTEFAKT_FILES).join(", ")}`);
|
|
15168
|
+
const fileContent = content ?? getTemplate(type);
|
|
15169
|
+
const filePath = resolveArtefaktPath(cwd, type, phase);
|
|
15170
|
+
if (!filePath) return cmdErr(`Phase ${phase} not found`);
|
|
15171
|
+
node_fs.default.mkdirSync(node_path.default.dirname(filePath), { recursive: true });
|
|
15172
|
+
node_fs.default.writeFileSync(filePath, fileContent, "utf-8");
|
|
15173
|
+
const relPath = node_path.default.relative(cwd, filePath);
|
|
15174
|
+
return cmdOk({
|
|
15175
|
+
written: true,
|
|
15176
|
+
type,
|
|
15177
|
+
phase: phase ?? null,
|
|
15178
|
+
path: relPath
|
|
15179
|
+
}, raw ? relPath : void 0);
|
|
15180
|
+
}
|
|
15181
|
+
function cmdArtefakteAppend(cwd, type, entry, phase, raw) {
|
|
15182
|
+
if (!entry) return cmdErr("entry required for artefakte append");
|
|
15183
|
+
if (!isValidType(type)) return cmdErr(`Invalid artefakt type: ${type}. Available: ${Object.keys(ARTEFAKT_FILES).join(", ")}`);
|
|
15184
|
+
const filePath = resolveArtefaktPath(cwd, type, phase);
|
|
15185
|
+
if (!filePath) return cmdErr(`Phase ${phase} not found`);
|
|
15186
|
+
let fileContent = safeReadFile(filePath);
|
|
15187
|
+
if (fileContent === null) fileContent = getTemplate(type);
|
|
15188
|
+
fileContent = fileContent.replace(/^-\s*_No entries yet\._\s*$/m, "");
|
|
15189
|
+
const today = todayISO();
|
|
15190
|
+
let appendLine;
|
|
15191
|
+
if (type === "decisions") appendLine = `| ${(fileContent.match(/^\|\s*\d+/gm) || []).length + 1} | ${entry} | - | ${today} | - |`;
|
|
15192
|
+
else if (type === "acceptance-criteria") appendLine = `| ${(fileContent.match(/^\|\s*\d+/gm) || []).length + 1} | ${entry} | pending | - |`;
|
|
15193
|
+
else appendLine = `- ${entry}`;
|
|
15194
|
+
fileContent = fileContent.trimEnd() + "\n" + appendLine + "\n";
|
|
15195
|
+
node_fs.default.writeFileSync(filePath, fileContent, "utf-8");
|
|
15196
|
+
const relPath = node_path.default.relative(cwd, filePath);
|
|
15197
|
+
return cmdOk({
|
|
15198
|
+
appended: true,
|
|
15199
|
+
type,
|
|
15200
|
+
phase: phase ?? null,
|
|
15201
|
+
entry: appendLine,
|
|
15202
|
+
path: relPath
|
|
15203
|
+
}, raw ? "true" : void 0);
|
|
15204
|
+
}
|
|
15205
|
+
function cmdArtefakteList(cwd, phase, raw) {
|
|
15206
|
+
const results = [];
|
|
15207
|
+
for (const [type, filename] of Object.entries(ARTEFAKT_FILES)) {
|
|
15208
|
+
let filePath;
|
|
15209
|
+
if (phase) {
|
|
15210
|
+
const phaseInfo = findPhaseInternal(cwd, phase);
|
|
15211
|
+
if (!phaseInfo?.directory) return cmdOk({ error: `Phase ${phase} not found` });
|
|
15212
|
+
filePath = node_path.default.join(cwd, phaseInfo.directory, filename);
|
|
15213
|
+
} else filePath = planningPath(cwd, filename);
|
|
15214
|
+
results.push({
|
|
15215
|
+
type,
|
|
15216
|
+
exists: node_fs.default.existsSync(filePath),
|
|
15217
|
+
path: node_path.default.relative(cwd, filePath)
|
|
15218
|
+
});
|
|
15219
|
+
}
|
|
15220
|
+
return cmdOk({
|
|
15221
|
+
phase: phase ?? null,
|
|
15222
|
+
artefakte: results
|
|
15223
|
+
});
|
|
15224
|
+
}
|
|
15225
|
+
|
|
15226
|
+
//#endregion
|
|
15227
|
+
//#region src/core/context-loader.ts
|
|
15228
|
+
/**
|
|
15229
|
+
* Context Loader — Intelligent file selection for workflow context assembly
|
|
15230
|
+
*
|
|
15231
|
+
* Selects relevant planning files based on the current task/phase domain,
|
|
15232
|
+
* preventing context overload by loading only what matters.
|
|
15233
|
+
*/
|
|
15234
|
+
function fileEntry(cwd, relPath, role) {
|
|
15235
|
+
const fullPath = node_path.default.join(cwd, relPath);
|
|
15236
|
+
try {
|
|
15237
|
+
return {
|
|
15238
|
+
path: relPath,
|
|
15239
|
+
role,
|
|
15240
|
+
size: node_fs.default.statSync(fullPath).size
|
|
15241
|
+
};
|
|
15242
|
+
} catch {
|
|
15243
|
+
return null;
|
|
15244
|
+
}
|
|
15245
|
+
}
|
|
15246
|
+
function addIfExists(files, cwd, relPath, role) {
|
|
15247
|
+
const entry = fileEntry(cwd, relPath, role);
|
|
15248
|
+
if (entry) files.push(entry);
|
|
15249
|
+
}
|
|
15250
|
+
function loadProjectContext(cwd) {
|
|
15251
|
+
const files = [];
|
|
15252
|
+
addIfExists(files, cwd, ".planning/PROJECT.md", "project-vision");
|
|
15253
|
+
addIfExists(files, cwd, ".planning/REQUIREMENTS.md", "requirements");
|
|
15254
|
+
addIfExists(files, cwd, ".planning/STATE.md", "state");
|
|
15255
|
+
addIfExists(files, cwd, ".planning/config.json", "config");
|
|
15256
|
+
return files;
|
|
15257
|
+
}
|
|
15258
|
+
function loadRoadmapContext(cwd) {
|
|
15259
|
+
const files = [];
|
|
15260
|
+
addIfExists(files, cwd, ".planning/ROADMAP.md", "roadmap");
|
|
15261
|
+
return files;
|
|
15262
|
+
}
|
|
15263
|
+
function loadPhaseContext(cwd, phase) {
|
|
15264
|
+
const files = [];
|
|
15265
|
+
const phaseInfo = findPhaseInternal(cwd, phase);
|
|
15266
|
+
if (!phaseInfo?.directory) return files;
|
|
15267
|
+
const phaseDir = phaseInfo.directory;
|
|
15268
|
+
try {
|
|
15269
|
+
const phaseFiles = node_fs.default.readdirSync(node_path.default.join(cwd, phaseDir));
|
|
15270
|
+
for (const f of phaseFiles) {
|
|
15271
|
+
const relPath = node_path.default.join(phaseDir, f);
|
|
15272
|
+
if (f.endsWith("-CONTEXT.md") || f === "CONTEXT.md") addIfExists(files, cwd, relPath, "phase-context");
|
|
15273
|
+
else if (f.endsWith("-RESEARCH.md") || f === "RESEARCH.md") addIfExists(files, cwd, relPath, "phase-research");
|
|
15274
|
+
else if (f.endsWith("-PLAN.md")) addIfExists(files, cwd, relPath, "phase-plan");
|
|
15275
|
+
else if (f.endsWith("-SUMMARY.md")) addIfExists(files, cwd, relPath, "phase-summary");
|
|
15276
|
+
else if (f.endsWith("-VERIFICATION.md") || f === "VERIFICATION.md") addIfExists(files, cwd, relPath, "phase-verification");
|
|
15277
|
+
}
|
|
15278
|
+
} catch (e) {
|
|
15279
|
+
debugLog("context-loader-phase-files-failed", e);
|
|
15280
|
+
}
|
|
15281
|
+
return files;
|
|
15282
|
+
}
|
|
15283
|
+
function loadArtefakteContext(cwd, phase) {
|
|
15284
|
+
const files = [];
|
|
15285
|
+
for (const filename of [
|
|
15286
|
+
"DECISIONS.md",
|
|
15287
|
+
"ACCEPTANCE-CRITERIA.md",
|
|
15288
|
+
"NO-GOS.md"
|
|
15289
|
+
]) {
|
|
15290
|
+
if (phase) {
|
|
15291
|
+
const phaseInfo = findPhaseInternal(cwd, phase);
|
|
15292
|
+
if (phaseInfo?.directory) addIfExists(files, cwd, node_path.default.join(phaseInfo.directory, filename), `artefakt-${filename.toLowerCase()}`);
|
|
15293
|
+
}
|
|
15294
|
+
addIfExists(files, cwd, `.planning/${filename}`, `artefakt-${filename.toLowerCase()}`);
|
|
15295
|
+
}
|
|
15296
|
+
return files;
|
|
15297
|
+
}
|
|
15298
|
+
function loadHistoryContext(cwd, currentPhase) {
|
|
15299
|
+
const files = [];
|
|
15300
|
+
const pd = phasesPath(cwd);
|
|
15301
|
+
try {
|
|
15302
|
+
const dirs = listSubDirs(pd, true);
|
|
15303
|
+
for (const dir of dirs) {
|
|
15304
|
+
if (currentPhase) {
|
|
15305
|
+
if (dir.match(/^(\d+[A-Z]?(?:\.\d+)?)/i)?.[1] === currentPhase) continue;
|
|
15306
|
+
}
|
|
15307
|
+
const dirPath = node_path.default.join(pd, dir);
|
|
15308
|
+
const summaries = node_fs.default.readdirSync(dirPath).filter((f) => isSummaryFile(f));
|
|
15309
|
+
for (const s of summaries) addIfExists(files, cwd, node_path.default.join(".planning", "phases", dir, s), "history-summary");
|
|
15310
|
+
}
|
|
15311
|
+
} catch (e) {
|
|
15312
|
+
debugLog("context-loader-history-failed", e);
|
|
15313
|
+
}
|
|
15314
|
+
return files;
|
|
15315
|
+
}
|
|
15316
|
+
function cmdContextLoad(cwd, phase, topic, includeHistory) {
|
|
15317
|
+
const allFiles = [];
|
|
15318
|
+
allFiles.push(...loadProjectContext(cwd));
|
|
15319
|
+
allFiles.push(...loadRoadmapContext(cwd));
|
|
15320
|
+
allFiles.push(...loadArtefakteContext(cwd, phase));
|
|
15321
|
+
if (phase) allFiles.push(...loadPhaseContext(cwd, phase));
|
|
15322
|
+
if (includeHistory) allFiles.push(...loadHistoryContext(cwd, phase));
|
|
15323
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15324
|
+
const deduped = allFiles.filter((f) => {
|
|
15325
|
+
if (seen.has(f.path)) return false;
|
|
15326
|
+
seen.add(f.path);
|
|
15327
|
+
return true;
|
|
15328
|
+
});
|
|
15329
|
+
return cmdOk({
|
|
15330
|
+
files: deduped,
|
|
15331
|
+
total_size: deduped.reduce((sum, f) => sum + f.size, 0),
|
|
15332
|
+
phase: phase ?? null,
|
|
15333
|
+
topic: topic ?? null
|
|
15334
|
+
});
|
|
15335
|
+
}
|
|
15336
|
+
|
|
15337
|
+
//#endregion
|
|
15338
|
+
//#region src/core/skills.ts
|
|
15339
|
+
/**
|
|
15340
|
+
* Skills — List, install, and update skill templates
|
|
15341
|
+
*
|
|
15342
|
+
* Skills are installed to `.claude/skills/<name>/SKILL.md`.
|
|
15343
|
+
* Source templates live in `templates/skills/<name>/SKILL.md`.
|
|
15344
|
+
*/
|
|
15345
|
+
/**
|
|
15346
|
+
* Resolve the installed skills directory for the current project.
|
|
15347
|
+
* Skills live at `.claude/skills/` relative to cwd.
|
|
15348
|
+
*/
|
|
15349
|
+
function skillsDir(cwd) {
|
|
15350
|
+
return node_path.default.join(cwd, ".claude", "skills");
|
|
15351
|
+
}
|
|
15352
|
+
/**
|
|
15353
|
+
* Resolve the templates source directory for skills.
|
|
15354
|
+
* At runtime (from dist/cli.cjs), templates are bundled at dist/assets/templates/skills/.
|
|
15355
|
+
*/
|
|
15356
|
+
function skillsTemplateDir() {
|
|
15357
|
+
return node_path.default.resolve(__dirname, "assets", "templates", "skills");
|
|
15358
|
+
}
|
|
15359
|
+
/**
|
|
15360
|
+
* Read a single skill's metadata from its SKILL.md frontmatter.
|
|
15361
|
+
*/
|
|
15362
|
+
function readSkillInfo(skillDir, dirName) {
|
|
15363
|
+
const content = safeReadFile(node_path.default.join(skillDir, "SKILL.md"));
|
|
15364
|
+
if (!content) return null;
|
|
15365
|
+
const fm = extractFrontmatter(content);
|
|
15366
|
+
return {
|
|
15367
|
+
name: fm.name ?? dirName,
|
|
15368
|
+
description: fm.description ?? "",
|
|
15369
|
+
directory: dirName
|
|
15370
|
+
};
|
|
15371
|
+
}
|
|
15372
|
+
/**
|
|
15373
|
+
* List all installed skills from `.claude/skills/`.
|
|
15374
|
+
*/
|
|
15375
|
+
function cmdSkillList(cwd, raw) {
|
|
15376
|
+
const dir = skillsDir(cwd);
|
|
15377
|
+
if (!node_fs.default.existsSync(dir)) output({
|
|
15378
|
+
skills: [],
|
|
15379
|
+
count: 0
|
|
15380
|
+
}, raw, "No skills installed.");
|
|
15381
|
+
const entries = node_fs.default.readdirSync(dir, { withFileTypes: true });
|
|
15382
|
+
const skills = [];
|
|
15383
|
+
for (const entry of entries) {
|
|
15384
|
+
if (!entry.isDirectory()) continue;
|
|
15385
|
+
const info = readSkillInfo(node_path.default.join(dir, entry.name), entry.name);
|
|
15386
|
+
if (info) skills.push(info);
|
|
15387
|
+
}
|
|
15388
|
+
output({
|
|
15389
|
+
skills,
|
|
15390
|
+
count: skills.length
|
|
15391
|
+
}, raw, skills.map((s) => `${s.name}: ${s.description}`).join("\n"));
|
|
15392
|
+
}
|
|
15393
|
+
/**
|
|
15394
|
+
* Install a specific skill from the templates directory.
|
|
15395
|
+
*/
|
|
15396
|
+
function cmdSkillInstall(cwd, skillName, raw) {
|
|
15397
|
+
if (!skillName) error("skill name required. Usage: skill-install <name>");
|
|
15398
|
+
const srcFile = node_path.default.join(skillsTemplateDir(), skillName, "SKILL.md");
|
|
15399
|
+
if (!node_fs.default.existsSync(srcFile)) error(`Skill "${skillName}" not found in templates. Available: ${listAvailableTemplates().join(", ")}`);
|
|
15400
|
+
const destDir = node_path.default.join(skillsDir(cwd), skillName);
|
|
15401
|
+
const destFile = node_path.default.join(destDir, "SKILL.md");
|
|
15402
|
+
node_fs.default.mkdirSync(destDir, { recursive: true });
|
|
15403
|
+
node_fs.default.copyFileSync(srcFile, destFile);
|
|
15404
|
+
output({
|
|
15405
|
+
installed: true,
|
|
15406
|
+
skill: skillName,
|
|
15407
|
+
path: node_path.default.relative(cwd, destFile)
|
|
15408
|
+
}, raw, `Installed skill: ${skillName}`);
|
|
15409
|
+
}
|
|
15410
|
+
/**
|
|
15411
|
+
* Update one or all installed skills from the templates source.
|
|
15412
|
+
*/
|
|
15413
|
+
function cmdSkillUpdate(cwd, skillName, raw) {
|
|
15414
|
+
const dir = skillsDir(cwd);
|
|
15415
|
+
const templateDir = skillsTemplateDir();
|
|
15416
|
+
if (skillName) {
|
|
15417
|
+
const srcFile = node_path.default.join(templateDir, skillName, "SKILL.md");
|
|
15418
|
+
if (!node_fs.default.existsSync(srcFile)) error(`Skill template "${skillName}" not found.`);
|
|
15419
|
+
const destDir = node_path.default.join(dir, skillName);
|
|
15420
|
+
if (!node_fs.default.existsSync(destDir)) error(`Skill "${skillName}" is not installed. Use skill-install first.`);
|
|
15421
|
+
const destFile = node_path.default.join(destDir, "SKILL.md");
|
|
15422
|
+
node_fs.default.copyFileSync(srcFile, destFile);
|
|
15423
|
+
output({
|
|
15424
|
+
updated: [skillName],
|
|
15425
|
+
skipped: [],
|
|
15426
|
+
not_found: []
|
|
15427
|
+
}, raw, `Updated skill: ${skillName}`);
|
|
15428
|
+
return;
|
|
15429
|
+
}
|
|
15430
|
+
if (!node_fs.default.existsSync(dir)) {
|
|
15431
|
+
output({
|
|
15432
|
+
updated: [],
|
|
15433
|
+
skipped: [],
|
|
15434
|
+
not_found: []
|
|
15435
|
+
}, raw, "No skills installed.");
|
|
15436
|
+
return;
|
|
15437
|
+
}
|
|
15438
|
+
const entries = node_fs.default.readdirSync(dir, { withFileTypes: true });
|
|
15439
|
+
const updated = [];
|
|
15440
|
+
const skipped = [];
|
|
15441
|
+
for (const entry of entries) {
|
|
15442
|
+
if (!entry.isDirectory()) continue;
|
|
15443
|
+
const name = entry.name;
|
|
15444
|
+
const srcFile = node_path.default.join(templateDir, name, "SKILL.md");
|
|
15445
|
+
if (!node_fs.default.existsSync(srcFile)) {
|
|
15446
|
+
skipped.push(name);
|
|
15447
|
+
continue;
|
|
15448
|
+
}
|
|
15449
|
+
const destFile = node_path.default.join(dir, name, "SKILL.md");
|
|
15450
|
+
node_fs.default.copyFileSync(srcFile, destFile);
|
|
15451
|
+
updated.push(name);
|
|
15452
|
+
}
|
|
15453
|
+
const summary = updated.length > 0 ? `Updated ${updated.length} skill(s): ${updated.join(", ")}` : "No skills updated.";
|
|
15454
|
+
output({
|
|
15455
|
+
updated,
|
|
15456
|
+
skipped
|
|
15457
|
+
}, raw, summary);
|
|
15458
|
+
}
|
|
15459
|
+
function listAvailableTemplates() {
|
|
15460
|
+
const dir = skillsTemplateDir();
|
|
15461
|
+
if (!node_fs.default.existsSync(dir)) return [];
|
|
15462
|
+
return node_fs.default.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
15219
15463
|
}
|
|
15220
15464
|
|
|
15221
15465
|
//#endregion
|
|
@@ -15227,7 +15471,7 @@ function cmdTemplateFill(cwd, templateType, options, raw) {
|
|
|
15227
15471
|
*/
|
|
15228
15472
|
const DEFAULT_PORT = 3333;
|
|
15229
15473
|
const PORT_RANGE_END = 3343;
|
|
15230
|
-
const HEALTH_TIMEOUT_MS =
|
|
15474
|
+
const HEALTH_TIMEOUT_MS = 1e4;
|
|
15231
15475
|
/**
|
|
15232
15476
|
* Check if a dashboard health endpoint is responding on the given port.
|
|
15233
15477
|
*/
|
|
@@ -15239,7 +15483,11 @@ async function checkHealth(port, timeoutMs = HEALTH_TIMEOUT_MS) {
|
|
|
15239
15483
|
clearTimeout(timer);
|
|
15240
15484
|
if (res.ok) return (await res.json()).status === "ok";
|
|
15241
15485
|
return false;
|
|
15242
|
-
} catch {
|
|
15486
|
+
} catch (e) {
|
|
15487
|
+
debugLog("health-check-failed", {
|
|
15488
|
+
port,
|
|
15489
|
+
error: errorMsg(e)
|
|
15490
|
+
});
|
|
15243
15491
|
return false;
|
|
15244
15492
|
}
|
|
15245
15493
|
}
|
|
@@ -15265,11 +15513,29 @@ function killProcessOnPort(port) {
|
|
|
15265
15513
|
}
|
|
15266
15514
|
for (const pid of pids) try {
|
|
15267
15515
|
(0, node_child_process.execSync)(`taskkill /PID ${pid} /F`, { stdio: "ignore" });
|
|
15268
|
-
} catch {
|
|
15269
|
-
|
|
15516
|
+
} catch (e) {
|
|
15517
|
+
debugLog("kill-process-on-port-taskkill-failed", {
|
|
15518
|
+
port,
|
|
15519
|
+
pid,
|
|
15520
|
+
error: errorMsg(e)
|
|
15521
|
+
});
|
|
15522
|
+
}
|
|
15523
|
+
} catch (e) {
|
|
15524
|
+
debugLog("kill-process-on-port-netstat-failed", {
|
|
15525
|
+
port,
|
|
15526
|
+
platform: "win32",
|
|
15527
|
+
error: errorMsg(e)
|
|
15528
|
+
});
|
|
15529
|
+
}
|
|
15270
15530
|
else try {
|
|
15271
15531
|
(0, node_child_process.execSync)(`lsof -i :${port} -t | xargs kill -SIGTERM 2>/dev/null`, { stdio: "ignore" });
|
|
15272
|
-
} catch {
|
|
15532
|
+
} catch (e) {
|
|
15533
|
+
debugLog("kill-process-on-port-lsof-failed", {
|
|
15534
|
+
port,
|
|
15535
|
+
platform: process.platform,
|
|
15536
|
+
error: errorMsg(e)
|
|
15537
|
+
});
|
|
15538
|
+
}
|
|
15273
15539
|
}
|
|
15274
15540
|
/**
|
|
15275
15541
|
* Resolve the dashboard server entry point path.
|
|
@@ -15287,7 +15553,12 @@ function resolveDashboardServer() {
|
|
|
15287
15553
|
if (node_fs.default.existsSync(serverJs)) return serverJs;
|
|
15288
15554
|
const serverTs = node_path.default.join(pkgDir, "server.ts");
|
|
15289
15555
|
if (node_fs.default.existsSync(serverTs)) return serverTs;
|
|
15290
|
-
} catch {
|
|
15556
|
+
} catch (e) {
|
|
15557
|
+
debugLog("resolve-dashboard-strategy1-failed", {
|
|
15558
|
+
strategy: "@maxsim/dashboard package",
|
|
15559
|
+
error: errorMsg(e)
|
|
15560
|
+
});
|
|
15561
|
+
}
|
|
15291
15562
|
try {
|
|
15292
15563
|
let dir = node_path.default.dirname(new URL(require("url").pathToFileURL(__filename).href).pathname);
|
|
15293
15564
|
if (process.platform === "win32" && dir.startsWith("/")) dir = dir.slice(1);
|
|
@@ -15298,7 +15569,12 @@ function resolveDashboardServer() {
|
|
|
15298
15569
|
if (node_fs.default.existsSync(candidateJs)) return candidateJs;
|
|
15299
15570
|
dir = node_path.default.dirname(dir);
|
|
15300
15571
|
}
|
|
15301
|
-
} catch {
|
|
15572
|
+
} catch (e) {
|
|
15573
|
+
debugLog("resolve-dashboard-strategy2-failed", {
|
|
15574
|
+
strategy: "monorepo walk",
|
|
15575
|
+
error: errorMsg(e)
|
|
15576
|
+
});
|
|
15577
|
+
}
|
|
15302
15578
|
return null;
|
|
15303
15579
|
}
|
|
15304
15580
|
/**
|
|
@@ -15317,7 +15593,11 @@ function ensureNodePty(serverDir) {
|
|
|
15317
15593
|
timeout: 12e4
|
|
15318
15594
|
});
|
|
15319
15595
|
return true;
|
|
15320
|
-
} catch {
|
|
15596
|
+
} catch (e) {
|
|
15597
|
+
debugLog("ensure-node-pty-install-failed", {
|
|
15598
|
+
serverDir,
|
|
15599
|
+
error: errorMsg(e)
|
|
15600
|
+
});
|
|
15321
15601
|
return false;
|
|
15322
15602
|
}
|
|
15323
15603
|
}
|
|
@@ -15333,7 +15613,12 @@ function readDashboardConfig(serverPath) {
|
|
|
15333
15613
|
const config = JSON.parse(node_fs.default.readFileSync(dashboardConfigPath, "utf8"));
|
|
15334
15614
|
if (config.projectCwd) projectCwd = config.projectCwd;
|
|
15335
15615
|
networkMode = config.networkMode ?? false;
|
|
15336
|
-
} catch {
|
|
15616
|
+
} catch (e) {
|
|
15617
|
+
debugLog("read-dashboard-config-failed", {
|
|
15618
|
+
path: dashboardConfigPath,
|
|
15619
|
+
error: errorMsg(e)
|
|
15620
|
+
});
|
|
15621
|
+
}
|
|
15337
15622
|
return {
|
|
15338
15623
|
projectCwd,
|
|
15339
15624
|
networkMode
|
|
@@ -15366,6 +15651,78 @@ function spawnDashboard(options) {
|
|
|
15366
15651
|
child.unref();
|
|
15367
15652
|
return child.pid ?? null;
|
|
15368
15653
|
}
|
|
15654
|
+
/**
|
|
15655
|
+
* Poll the port range until a dashboard health endpoint responds.
|
|
15656
|
+
* Returns the URL if found within the timeout, null otherwise.
|
|
15657
|
+
*/
|
|
15658
|
+
async function waitForDashboard(pollIntervalMs = 500, pollTimeoutMs = 2e4, healthTimeoutMs = 1e3) {
|
|
15659
|
+
const deadline = Date.now() + pollTimeoutMs;
|
|
15660
|
+
while (Date.now() < deadline) {
|
|
15661
|
+
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
15662
|
+
for (let p = DEFAULT_PORT; p <= PORT_RANGE_END; p++) if (await checkHealth(p, healthTimeoutMs)) return `http://localhost:${p}`;
|
|
15663
|
+
}
|
|
15664
|
+
return null;
|
|
15665
|
+
}
|
|
15666
|
+
|
|
15667
|
+
//#endregion
|
|
15668
|
+
//#region src/core/start.ts
|
|
15669
|
+
/**
|
|
15670
|
+
* Start — Orchestrates Dashboard launch + browser open
|
|
15671
|
+
*
|
|
15672
|
+
* Provides a unified `maxsimcli start` entry point that:
|
|
15673
|
+
* 1. Checks for a running dashboard
|
|
15674
|
+
* 2. Starts the dashboard if needed
|
|
15675
|
+
* 3. Opens the browser
|
|
15676
|
+
* 4. Reports status
|
|
15677
|
+
*/
|
|
15678
|
+
function openBrowser(url) {
|
|
15679
|
+
(0, node_child_process.exec)(process.platform === "win32" ? `start "" "${url}"` : process.platform === "darwin" ? `open "${url}"` : `xdg-open "${url}"`, (err) => {
|
|
15680
|
+
if (err) debugLog("open-browser-failed", err);
|
|
15681
|
+
});
|
|
15682
|
+
}
|
|
15683
|
+
async function cmdStart(cwd, options) {
|
|
15684
|
+
const existingPort = await findRunningDashboard();
|
|
15685
|
+
if (existingPort) {
|
|
15686
|
+
const url = `http://localhost:${existingPort}`;
|
|
15687
|
+
if (!options.noBrowser) openBrowser(url);
|
|
15688
|
+
return cmdOk({
|
|
15689
|
+
started: true,
|
|
15690
|
+
url,
|
|
15691
|
+
already_running: true,
|
|
15692
|
+
port: existingPort
|
|
15693
|
+
}, url);
|
|
15694
|
+
}
|
|
15695
|
+
const serverPath = resolveDashboardServer();
|
|
15696
|
+
if (!serverPath) return cmdErr("Dashboard server not found. Run `npx maxsimcli` to install first.");
|
|
15697
|
+
const serverDir = node_path.default.dirname(serverPath);
|
|
15698
|
+
const dashConfig = readDashboardConfig(serverPath);
|
|
15699
|
+
ensureNodePty(serverDir);
|
|
15700
|
+
const pid = spawnDashboard({
|
|
15701
|
+
serverPath,
|
|
15702
|
+
projectCwd: dashConfig.projectCwd,
|
|
15703
|
+
networkMode: options.networkMode
|
|
15704
|
+
});
|
|
15705
|
+
if (!pid) return cmdErr("Failed to spawn dashboard process.");
|
|
15706
|
+
const url = await waitForDashboard();
|
|
15707
|
+
if (url) {
|
|
15708
|
+
if (!options.noBrowser) openBrowser(url);
|
|
15709
|
+
return cmdOk({
|
|
15710
|
+
started: true,
|
|
15711
|
+
url,
|
|
15712
|
+
already_running: false,
|
|
15713
|
+
pid
|
|
15714
|
+
}, url);
|
|
15715
|
+
} else {
|
|
15716
|
+
const fallbackUrl = `http://localhost:${DEFAULT_PORT}`;
|
|
15717
|
+
return cmdOk({
|
|
15718
|
+
started: true,
|
|
15719
|
+
url: fallbackUrl,
|
|
15720
|
+
already_running: false,
|
|
15721
|
+
pid,
|
|
15722
|
+
warning: "Dashboard spawned but health check timed out. It may still be starting."
|
|
15723
|
+
}, fallbackUrl);
|
|
15724
|
+
}
|
|
15725
|
+
}
|
|
15369
15726
|
|
|
15370
15727
|
//#endregion
|
|
15371
15728
|
//#region src/core/init.ts
|
|
@@ -15397,13 +15754,47 @@ function scanPhaseArtifacts(cwd, phaseDirectory) {
|
|
|
15397
15754
|
}
|
|
15398
15755
|
return result;
|
|
15399
15756
|
}
|
|
15400
|
-
|
|
15401
|
-
|
|
15757
|
+
const CODE_EXTENSIONS = new Set([
|
|
15758
|
+
".ts",
|
|
15759
|
+
".js",
|
|
15760
|
+
".py",
|
|
15761
|
+
".go",
|
|
15762
|
+
".rs",
|
|
15763
|
+
".swift",
|
|
15764
|
+
".java"
|
|
15765
|
+
]);
|
|
15766
|
+
const EXCLUDED_DIRS = new Set(["node_modules", ".git"]);
|
|
15767
|
+
function findCodeFiles(dir, maxDepth = 3, limit = 5) {
|
|
15768
|
+
const results = [];
|
|
15769
|
+
function walk(currentDir, depth) {
|
|
15770
|
+
if (depth > maxDepth || results.length >= limit) return;
|
|
15771
|
+
let entries;
|
|
15772
|
+
try {
|
|
15773
|
+
entries = node_fs.default.readdirSync(currentDir, { withFileTypes: true });
|
|
15774
|
+
} catch {
|
|
15775
|
+
return;
|
|
15776
|
+
}
|
|
15777
|
+
for (const entry of entries) {
|
|
15778
|
+
if (results.length >= limit) return;
|
|
15779
|
+
if (EXCLUDED_DIRS.has(entry.name)) continue;
|
|
15780
|
+
const fullPath = node_path.default.join(currentDir, entry.name);
|
|
15781
|
+
if (entry.isDirectory()) walk(fullPath, depth + 1);
|
|
15782
|
+
else if (entry.isFile()) {
|
|
15783
|
+
const ext = node_path.default.extname(entry.name).toLowerCase();
|
|
15784
|
+
if (CODE_EXTENSIONS.has(ext)) results.push(fullPath);
|
|
15785
|
+
}
|
|
15786
|
+
}
|
|
15787
|
+
}
|
|
15788
|
+
walk(dir, 1);
|
|
15789
|
+
return results;
|
|
15790
|
+
}
|
|
15791
|
+
function cmdInitExecutePhase(cwd, phase) {
|
|
15792
|
+
if (!phase) return cmdErr("phase required for init execute-phase");
|
|
15402
15793
|
const config = loadConfig(cwd);
|
|
15403
15794
|
const phaseInfo = findPhaseInternal(cwd, phase);
|
|
15404
15795
|
const milestone = getMilestoneInfo(cwd);
|
|
15405
15796
|
const phase_req_ids = extractReqIds(cwd, phase);
|
|
15406
|
-
|
|
15797
|
+
return cmdOk({
|
|
15407
15798
|
executor_model: resolveModelInternal(cwd, "maxsim-executor"),
|
|
15408
15799
|
verifier_model: resolveModelInternal(cwd, "maxsim-verifier"),
|
|
15409
15800
|
commit_docs: config.commit_docs,
|
|
@@ -15433,10 +15824,10 @@ function cmdInitExecutePhase(cwd, phase, raw) {
|
|
|
15433
15824
|
state_path: ".planning/STATE.md",
|
|
15434
15825
|
roadmap_path: ".planning/ROADMAP.md",
|
|
15435
15826
|
config_path: ".planning/config.json"
|
|
15436
|
-
}
|
|
15827
|
+
});
|
|
15437
15828
|
}
|
|
15438
|
-
function cmdInitPlanPhase(cwd, phase
|
|
15439
|
-
if (!phase)
|
|
15829
|
+
function cmdInitPlanPhase(cwd, phase) {
|
|
15830
|
+
if (!phase) return cmdErr("phase required for init plan-phase");
|
|
15440
15831
|
const config = loadConfig(cwd);
|
|
15441
15832
|
const phaseInfo = findPhaseInternal(cwd, phase);
|
|
15442
15833
|
const phase_req_ids = extractReqIds(cwd, phase);
|
|
@@ -15471,30 +15862,16 @@ function cmdInitPlanPhase(cwd, phase, raw) {
|
|
|
15471
15862
|
if (artifacts.verification_path) result.verification_path = artifacts.verification_path;
|
|
15472
15863
|
if (artifacts.uat_path) result.uat_path = artifacts.uat_path;
|
|
15473
15864
|
}
|
|
15474
|
-
|
|
15865
|
+
return cmdOk(result);
|
|
15475
15866
|
}
|
|
15476
|
-
function cmdInitNewProject(cwd
|
|
15867
|
+
function cmdInitNewProject(cwd) {
|
|
15477
15868
|
const config = loadConfig(cwd);
|
|
15478
15869
|
const homedir = node_os.default.homedir();
|
|
15479
15870
|
const braveKeyFile = node_path.default.join(homedir, ".maxsim", "brave_api_key");
|
|
15480
15871
|
const hasBraveSearch = !!(process.env.BRAVE_API_KEY || node_fs.default.existsSync(braveKeyFile));
|
|
15481
|
-
|
|
15482
|
-
|
|
15483
|
-
|
|
15484
|
-
hasCode = (0, node_child_process.execSync)("find . -maxdepth 3 \\( -name \"*.ts\" -o -name \"*.js\" -o -name \"*.py\" -o -name \"*.go\" -o -name \"*.rs\" -o -name \"*.swift\" -o -name \"*.java\" \\) 2>/dev/null | grep -v node_modules | grep -v .git | head -5", {
|
|
15485
|
-
cwd,
|
|
15486
|
-
encoding: "utf-8",
|
|
15487
|
-
stdio: [
|
|
15488
|
-
"pipe",
|
|
15489
|
-
"pipe",
|
|
15490
|
-
"pipe"
|
|
15491
|
-
]
|
|
15492
|
-
}).trim().length > 0;
|
|
15493
|
-
} catch (e) {
|
|
15494
|
-
debugLog(e);
|
|
15495
|
-
}
|
|
15496
|
-
hasPackageFile = pathExistsInternal(cwd, "package.json") || pathExistsInternal(cwd, "requirements.txt") || pathExistsInternal(cwd, "Cargo.toml") || pathExistsInternal(cwd, "go.mod") || pathExistsInternal(cwd, "Package.swift");
|
|
15497
|
-
output({
|
|
15872
|
+
const hasCode = findCodeFiles(cwd).length > 0;
|
|
15873
|
+
const hasPackageFile = pathExistsInternal(cwd, "package.json") || pathExistsInternal(cwd, "requirements.txt") || pathExistsInternal(cwd, "Cargo.toml") || pathExistsInternal(cwd, "go.mod") || pathExistsInternal(cwd, "Package.swift");
|
|
15874
|
+
return cmdOk({
|
|
15498
15875
|
researcher_model: resolveModelInternal(cwd, "maxsim-project-researcher"),
|
|
15499
15876
|
synthesizer_model: resolveModelInternal(cwd, "maxsim-research-synthesizer"),
|
|
15500
15877
|
roadmapper_model: resolveModelInternal(cwd, "maxsim-roadmapper"),
|
|
@@ -15509,12 +15886,12 @@ function cmdInitNewProject(cwd, raw) {
|
|
|
15509
15886
|
has_git: pathExistsInternal(cwd, ".git"),
|
|
15510
15887
|
brave_search_available: hasBraveSearch,
|
|
15511
15888
|
project_path: ".planning/PROJECT.md"
|
|
15512
|
-
}
|
|
15889
|
+
});
|
|
15513
15890
|
}
|
|
15514
|
-
function cmdInitNewMilestone(cwd
|
|
15891
|
+
function cmdInitNewMilestone(cwd) {
|
|
15515
15892
|
const config = loadConfig(cwd);
|
|
15516
15893
|
const milestone = getMilestoneInfo(cwd);
|
|
15517
|
-
|
|
15894
|
+
return cmdOk({
|
|
15518
15895
|
researcher_model: resolveModelInternal(cwd, "maxsim-project-researcher"),
|
|
15519
15896
|
synthesizer_model: resolveModelInternal(cwd, "maxsim-research-synthesizer"),
|
|
15520
15897
|
roadmapper_model: resolveModelInternal(cwd, "maxsim-roadmapper"),
|
|
@@ -15528,9 +15905,9 @@ function cmdInitNewMilestone(cwd, raw) {
|
|
|
15528
15905
|
project_path: ".planning/PROJECT.md",
|
|
15529
15906
|
roadmap_path: ".planning/ROADMAP.md",
|
|
15530
15907
|
state_path: ".planning/STATE.md"
|
|
15531
|
-
}
|
|
15908
|
+
});
|
|
15532
15909
|
}
|
|
15533
|
-
function cmdInitQuick(cwd, description
|
|
15910
|
+
function cmdInitQuick(cwd, description) {
|
|
15534
15911
|
const config = loadConfig(cwd);
|
|
15535
15912
|
const now = /* @__PURE__ */ new Date();
|
|
15536
15913
|
const slug = description ? generateSlugInternal(description)?.substring(0, 40) ?? null : null;
|
|
@@ -15542,7 +15919,7 @@ function cmdInitQuick(cwd, description, raw) {
|
|
|
15542
15919
|
} catch (e) {
|
|
15543
15920
|
debugLog(e);
|
|
15544
15921
|
}
|
|
15545
|
-
|
|
15922
|
+
return cmdOk({
|
|
15546
15923
|
planner_model: resolveModelInternal(cwd, "maxsim-planner"),
|
|
15547
15924
|
executor_model: resolveModelInternal(cwd, "maxsim-executor"),
|
|
15548
15925
|
checker_model: resolveModelInternal(cwd, "maxsim-plan-checker"),
|
|
@@ -15557,9 +15934,9 @@ function cmdInitQuick(cwd, description, raw) {
|
|
|
15557
15934
|
task_dir: slug ? `.planning/quick/${nextNum}-${slug}` : null,
|
|
15558
15935
|
roadmap_exists: pathExistsInternal(cwd, ".planning/ROADMAP.md"),
|
|
15559
15936
|
planning_exists: pathExistsInternal(cwd, ".planning")
|
|
15560
|
-
}
|
|
15937
|
+
});
|
|
15561
15938
|
}
|
|
15562
|
-
function cmdInitResume(cwd
|
|
15939
|
+
function cmdInitResume(cwd) {
|
|
15563
15940
|
const config = loadConfig(cwd);
|
|
15564
15941
|
let interruptedAgentId = null;
|
|
15565
15942
|
try {
|
|
@@ -15567,7 +15944,7 @@ function cmdInitResume(cwd, raw) {
|
|
|
15567
15944
|
} catch (e) {
|
|
15568
15945
|
debugLog(e);
|
|
15569
15946
|
}
|
|
15570
|
-
|
|
15947
|
+
return cmdOk({
|
|
15571
15948
|
state_exists: pathExistsInternal(cwd, ".planning/STATE.md"),
|
|
15572
15949
|
roadmap_exists: pathExistsInternal(cwd, ".planning/ROADMAP.md"),
|
|
15573
15950
|
project_exists: pathExistsInternal(cwd, ".planning/PROJECT.md"),
|
|
@@ -15578,13 +15955,13 @@ function cmdInitResume(cwd, raw) {
|
|
|
15578
15955
|
has_interrupted_agent: !!interruptedAgentId,
|
|
15579
15956
|
interrupted_agent_id: interruptedAgentId,
|
|
15580
15957
|
commit_docs: config.commit_docs
|
|
15581
|
-
}
|
|
15958
|
+
});
|
|
15582
15959
|
}
|
|
15583
|
-
function cmdInitVerifyWork(cwd, phase
|
|
15584
|
-
if (!phase)
|
|
15960
|
+
function cmdInitVerifyWork(cwd, phase) {
|
|
15961
|
+
if (!phase) return cmdErr("phase required for init verify-work");
|
|
15585
15962
|
const config = loadConfig(cwd);
|
|
15586
15963
|
const phaseInfo = findPhaseInternal(cwd, phase);
|
|
15587
|
-
|
|
15964
|
+
return cmdOk({
|
|
15588
15965
|
planner_model: resolveModelInternal(cwd, "maxsim-planner"),
|
|
15589
15966
|
checker_model: resolveModelInternal(cwd, "maxsim-plan-checker"),
|
|
15590
15967
|
commit_docs: config.commit_docs,
|
|
@@ -15593,9 +15970,9 @@ function cmdInitVerifyWork(cwd, phase, raw) {
|
|
|
15593
15970
|
phase_number: phaseInfo?.phase_number ?? null,
|
|
15594
15971
|
phase_name: phaseInfo?.phase_name ?? null,
|
|
15595
15972
|
has_verification: phaseInfo?.has_verification ?? false
|
|
15596
|
-
}
|
|
15973
|
+
});
|
|
15597
15974
|
}
|
|
15598
|
-
function cmdInitPhaseOp(cwd, phase
|
|
15975
|
+
function cmdInitPhaseOp(cwd, phase) {
|
|
15599
15976
|
const config = loadConfig(cwd);
|
|
15600
15977
|
let phaseInfo = findPhaseInternal(cwd, phase ?? "");
|
|
15601
15978
|
if (!phaseInfo) {
|
|
@@ -15644,9 +16021,9 @@ function cmdInitPhaseOp(cwd, phase, raw) {
|
|
|
15644
16021
|
if (artifacts.verification_path) result.verification_path = artifacts.verification_path;
|
|
15645
16022
|
if (artifacts.uat_path) result.uat_path = artifacts.uat_path;
|
|
15646
16023
|
}
|
|
15647
|
-
|
|
16024
|
+
return cmdOk(result);
|
|
15648
16025
|
}
|
|
15649
|
-
function cmdInitTodos(cwd, area
|
|
16026
|
+
function cmdInitTodos(cwd, area) {
|
|
15650
16027
|
const config = loadConfig(cwd);
|
|
15651
16028
|
const now = /* @__PURE__ */ new Date();
|
|
15652
16029
|
const pendingDir = planningPath(cwd, "todos", "pending");
|
|
@@ -15675,7 +16052,7 @@ function cmdInitTodos(cwd, area, raw) {
|
|
|
15675
16052
|
} catch (e) {
|
|
15676
16053
|
debugLog(e);
|
|
15677
16054
|
}
|
|
15678
|
-
|
|
16055
|
+
return cmdOk({
|
|
15679
16056
|
commit_docs: config.commit_docs,
|
|
15680
16057
|
date: todayISO(),
|
|
15681
16058
|
timestamp: now.toISOString(),
|
|
@@ -15687,9 +16064,9 @@ function cmdInitTodos(cwd, area, raw) {
|
|
|
15687
16064
|
planning_exists: pathExistsInternal(cwd, ".planning"),
|
|
15688
16065
|
todos_dir_exists: pathExistsInternal(cwd, ".planning/todos"),
|
|
15689
16066
|
pending_dir_exists: pathExistsInternal(cwd, ".planning/todos/pending")
|
|
15690
|
-
}
|
|
16067
|
+
});
|
|
15691
16068
|
}
|
|
15692
|
-
function cmdInitMilestoneOp(cwd
|
|
16069
|
+
function cmdInitMilestoneOp(cwd) {
|
|
15693
16070
|
const config = loadConfig(cwd);
|
|
15694
16071
|
const milestone = getMilestoneInfo(cwd);
|
|
15695
16072
|
let phaseCount = 0;
|
|
@@ -15713,7 +16090,7 @@ function cmdInitMilestoneOp(cwd, raw) {
|
|
|
15713
16090
|
} catch (e) {
|
|
15714
16091
|
debugLog(e);
|
|
15715
16092
|
}
|
|
15716
|
-
|
|
16093
|
+
return cmdOk({
|
|
15717
16094
|
commit_docs: config.commit_docs,
|
|
15718
16095
|
milestone_version: milestone.version,
|
|
15719
16096
|
milestone_name: milestone.name,
|
|
@@ -15728,9 +16105,9 @@ function cmdInitMilestoneOp(cwd, raw) {
|
|
|
15728
16105
|
state_exists: pathExistsInternal(cwd, ".planning/STATE.md"),
|
|
15729
16106
|
archive_exists: pathExistsInternal(cwd, ".planning/archive"),
|
|
15730
16107
|
phases_dir_exists: pathExistsInternal(cwd, ".planning/phases")
|
|
15731
|
-
}
|
|
16108
|
+
});
|
|
15732
16109
|
}
|
|
15733
|
-
function cmdInitMapCodebase(cwd
|
|
16110
|
+
function cmdInitMapCodebase(cwd) {
|
|
15734
16111
|
const config = loadConfig(cwd);
|
|
15735
16112
|
const codebaseDir = planningPath(cwd, "codebase");
|
|
15736
16113
|
let existingMaps = [];
|
|
@@ -15739,7 +16116,7 @@ function cmdInitMapCodebase(cwd, raw) {
|
|
|
15739
16116
|
} catch (e) {
|
|
15740
16117
|
debugLog(e);
|
|
15741
16118
|
}
|
|
15742
|
-
|
|
16119
|
+
return cmdOk({
|
|
15743
16120
|
mapper_model: resolveModelInternal(cwd, "maxsim-codebase-mapper"),
|
|
15744
16121
|
commit_docs: config.commit_docs,
|
|
15745
16122
|
search_gitignored: config.search_gitignored,
|
|
@@ -15749,29 +16126,15 @@ function cmdInitMapCodebase(cwd, raw) {
|
|
|
15749
16126
|
has_maps: existingMaps.length > 0,
|
|
15750
16127
|
planning_exists: pathExistsInternal(cwd, ".planning"),
|
|
15751
16128
|
codebase_dir_exists: pathExistsInternal(cwd, ".planning/codebase")
|
|
15752
|
-
}
|
|
16129
|
+
});
|
|
15753
16130
|
}
|
|
15754
|
-
function cmdInitExisting(cwd
|
|
16131
|
+
function cmdInitExisting(cwd) {
|
|
15755
16132
|
const config = loadConfig(cwd);
|
|
15756
16133
|
const homedir = node_os.default.homedir();
|
|
15757
16134
|
const braveKeyFile = node_path.default.join(homedir, ".maxsim", "brave_api_key");
|
|
15758
16135
|
const hasBraveSearch = !!(process.env.BRAVE_API_KEY || node_fs.default.existsSync(braveKeyFile));
|
|
15759
|
-
|
|
15760
|
-
|
|
15761
|
-
try {
|
|
15762
|
-
hasCode = (0, node_child_process.execSync)("find . -maxdepth 3 \\( -name \"*.ts\" -o -name \"*.js\" -o -name \"*.py\" -o -name \"*.go\" -o -name \"*.rs\" -o -name \"*.swift\" -o -name \"*.java\" \\) 2>/dev/null | grep -v node_modules | grep -v .git | head -5", {
|
|
15763
|
-
cwd,
|
|
15764
|
-
encoding: "utf-8",
|
|
15765
|
-
stdio: [
|
|
15766
|
-
"pipe",
|
|
15767
|
-
"pipe",
|
|
15768
|
-
"pipe"
|
|
15769
|
-
]
|
|
15770
|
-
}).trim().length > 0;
|
|
15771
|
-
} catch (e) {
|
|
15772
|
-
debugLog(e);
|
|
15773
|
-
}
|
|
15774
|
-
hasPackageFile = pathExistsInternal(cwd, "package.json") || pathExistsInternal(cwd, "requirements.txt") || pathExistsInternal(cwd, "Cargo.toml") || pathExistsInternal(cwd, "go.mod") || pathExistsInternal(cwd, "Package.swift");
|
|
16136
|
+
const hasCode = findCodeFiles(cwd).length > 0;
|
|
16137
|
+
const hasPackageFile = pathExistsInternal(cwd, "package.json") || pathExistsInternal(cwd, "requirements.txt") || pathExistsInternal(cwd, "Cargo.toml") || pathExistsInternal(cwd, "go.mod") || pathExistsInternal(cwd, "Package.swift");
|
|
15775
16138
|
let planningFiles = [];
|
|
15776
16139
|
try {
|
|
15777
16140
|
const planDir = planningPath(cwd);
|
|
@@ -15779,7 +16142,7 @@ function cmdInitExisting(cwd, raw) {
|
|
|
15779
16142
|
} catch (e) {
|
|
15780
16143
|
debugLog(e);
|
|
15781
16144
|
}
|
|
15782
|
-
|
|
16145
|
+
return cmdOk({
|
|
15783
16146
|
researcher_model: resolveModelInternal(cwd, "maxsim-project-researcher"),
|
|
15784
16147
|
synthesizer_model: resolveModelInternal(cwd, "maxsim-research-synthesizer"),
|
|
15785
16148
|
roadmapper_model: resolveModelInternal(cwd, "maxsim-roadmapper"),
|
|
@@ -15799,9 +16162,9 @@ function cmdInitExisting(cwd, raw) {
|
|
|
15799
16162
|
parallelization: config.parallelization,
|
|
15800
16163
|
project_path: ".planning/PROJECT.md",
|
|
15801
16164
|
codebase_dir: ".planning/codebase"
|
|
15802
|
-
}
|
|
16165
|
+
});
|
|
15803
16166
|
}
|
|
15804
|
-
function cmdInitProgress(cwd
|
|
16167
|
+
function cmdInitProgress(cwd) {
|
|
15805
16168
|
const config = loadConfig(cwd);
|
|
15806
16169
|
const milestone = getMilestoneInfo(cwd);
|
|
15807
16170
|
const progressPhasesDir = phasesPath(cwd);
|
|
@@ -15816,22 +16179,22 @@ function cmdInitProgress(cwd, raw) {
|
|
|
15816
16179
|
const phaseName = match && match[2] ? match[2] : null;
|
|
15817
16180
|
const phaseDirPath = node_path.default.join(progressPhasesDir, dir);
|
|
15818
16181
|
const phaseFiles = node_fs.default.readdirSync(phaseDirPath);
|
|
15819
|
-
const
|
|
16182
|
+
const plansList = phaseFiles.filter((f) => isPlanFile(f));
|
|
15820
16183
|
const summaries = phaseFiles.filter((f) => isSummaryFile(f));
|
|
15821
16184
|
const hasResearch = phaseFiles.some((f) => f.endsWith("-RESEARCH.md") || f === "RESEARCH.md");
|
|
15822
|
-
const status = summaries.length >=
|
|
15823
|
-
const
|
|
16185
|
+
const status = summaries.length >= plansList.length && plansList.length > 0 ? "complete" : plansList.length > 0 ? "in_progress" : hasResearch ? "researched" : "pending";
|
|
16186
|
+
const phaseInfoItem = {
|
|
15824
16187
|
number: phaseNumber,
|
|
15825
16188
|
name: phaseName,
|
|
15826
16189
|
directory: node_path.default.join(".planning", "phases", dir),
|
|
15827
16190
|
status,
|
|
15828
|
-
plan_count:
|
|
16191
|
+
plan_count: plansList.length,
|
|
15829
16192
|
summary_count: summaries.length,
|
|
15830
16193
|
has_research: hasResearch
|
|
15831
16194
|
};
|
|
15832
|
-
phases.push(
|
|
15833
|
-
if (!currentPhase && (status === "in_progress" || status === "researched")) currentPhase =
|
|
15834
|
-
if (!nextPhase && status === "pending") nextPhase =
|
|
16195
|
+
phases.push(phaseInfoItem);
|
|
16196
|
+
if (!currentPhase && (status === "in_progress" || status === "researched")) currentPhase = phaseInfoItem;
|
|
16197
|
+
if (!nextPhase && status === "pending") nextPhase = phaseInfoItem;
|
|
15835
16198
|
}
|
|
15836
16199
|
} catch (e) {
|
|
15837
16200
|
debugLog(e);
|
|
@@ -15843,7 +16206,7 @@ function cmdInitProgress(cwd, raw) {
|
|
|
15843
16206
|
} catch (e) {
|
|
15844
16207
|
debugLog(e);
|
|
15845
16208
|
}
|
|
15846
|
-
|
|
16209
|
+
return cmdOk({
|
|
15847
16210
|
executor_model: resolveModelInternal(cwd, "maxsim-executor"),
|
|
15848
16211
|
planner_model: resolveModelInternal(cwd, "maxsim-planner"),
|
|
15849
16212
|
commit_docs: config.commit_docs,
|
|
@@ -15864,7 +16227,7 @@ function cmdInitProgress(cwd, raw) {
|
|
|
15864
16227
|
roadmap_path: ".planning/ROADMAP.md",
|
|
15865
16228
|
project_path: ".planning/PROJECT.md",
|
|
15866
16229
|
config_path: ".planning/config.json"
|
|
15867
|
-
}
|
|
16230
|
+
});
|
|
15868
16231
|
}
|
|
15869
16232
|
|
|
15870
16233
|
//#endregion
|
|
@@ -15892,7 +16255,12 @@ function getFlags(args, ...flags) {
|
|
|
15892
16255
|
function hasFlag(args, flag) {
|
|
15893
16256
|
return args.includes(`--${flag}`);
|
|
15894
16257
|
}
|
|
15895
|
-
|
|
16258
|
+
/** Convert a CmdResult into the appropriate output()/error() call. */
|
|
16259
|
+
function handleResult(r, raw) {
|
|
16260
|
+
if (r.ok) output(r.result, raw, r.rawValue);
|
|
16261
|
+
else error(r.error);
|
|
16262
|
+
}
|
|
16263
|
+
const handleState = async (args, cwd, raw) => {
|
|
15896
16264
|
const sub = args[1];
|
|
15897
16265
|
const handler = sub ? {
|
|
15898
16266
|
"update": () => cmdStateUpdate(cwd, args[2], args[3]),
|
|
@@ -15904,12 +16272,12 @@ const handleState = (args, cwd, raw) => {
|
|
|
15904
16272
|
const value = args[i + 1];
|
|
15905
16273
|
if (key && value !== void 0) patches[key] = value;
|
|
15906
16274
|
}
|
|
15907
|
-
cmdStatePatch(cwd, patches, raw);
|
|
16275
|
+
return cmdStatePatch(cwd, patches, raw);
|
|
15908
16276
|
},
|
|
15909
16277
|
"advance-plan": () => cmdStateAdvancePlan(cwd, raw),
|
|
15910
16278
|
"record-metric": () => {
|
|
15911
16279
|
const f = getFlags(args, "phase", "plan", "duration", "tasks", "files");
|
|
15912
|
-
cmdStateRecordMetric(cwd, {
|
|
16280
|
+
return cmdStateRecordMetric(cwd, {
|
|
15913
16281
|
phase: f.phase ?? "",
|
|
15914
16282
|
plan: f.plan ?? "",
|
|
15915
16283
|
duration: f.duration ?? "",
|
|
@@ -15920,7 +16288,7 @@ const handleState = (args, cwd, raw) => {
|
|
|
15920
16288
|
"update-progress": () => cmdStateUpdateProgress(cwd, raw),
|
|
15921
16289
|
"add-decision": () => {
|
|
15922
16290
|
const f = getFlags(args, "phase", "summary", "summary-file", "rationale", "rationale-file");
|
|
15923
|
-
cmdStateAddDecision(cwd, {
|
|
16291
|
+
return cmdStateAddDecision(cwd, {
|
|
15924
16292
|
phase: f.phase ?? void 0,
|
|
15925
16293
|
summary: f.summary ?? void 0,
|
|
15926
16294
|
summary_file: f["summary-file"] ?? void 0,
|
|
@@ -15930,7 +16298,7 @@ const handleState = (args, cwd, raw) => {
|
|
|
15930
16298
|
},
|
|
15931
16299
|
"add-blocker": () => {
|
|
15932
16300
|
const f = getFlags(args, "text", "text-file");
|
|
15933
|
-
cmdStateAddBlocker(cwd, {
|
|
16301
|
+
return cmdStateAddBlocker(cwd, {
|
|
15934
16302
|
text: f.text ?? void 0,
|
|
15935
16303
|
text_file: f["text-file"] ?? void 0
|
|
15936
16304
|
}, raw);
|
|
@@ -15938,38 +16306,38 @@ const handleState = (args, cwd, raw) => {
|
|
|
15938
16306
|
"resolve-blocker": () => cmdStateResolveBlocker(cwd, getFlag(args, "--text"), raw),
|
|
15939
16307
|
"record-session": () => {
|
|
15940
16308
|
const f = getFlags(args, "stopped-at", "resume-file");
|
|
15941
|
-
cmdStateRecordSession(cwd, {
|
|
16309
|
+
return cmdStateRecordSession(cwd, {
|
|
15942
16310
|
stopped_at: f["stopped-at"] ?? void 0,
|
|
15943
16311
|
resume_file: f["resume-file"] ?? "None"
|
|
15944
16312
|
}, raw);
|
|
15945
16313
|
}
|
|
15946
16314
|
}[sub] : void 0;
|
|
15947
|
-
if (handler) return handler();
|
|
15948
|
-
cmdStateLoad(cwd, raw);
|
|
16315
|
+
if (handler) return handleResult(await handler(), raw);
|
|
16316
|
+
return handleResult(await cmdStateLoad(cwd, raw), raw);
|
|
15949
16317
|
};
|
|
15950
16318
|
const handleTemplate = (args, cwd, raw) => {
|
|
15951
16319
|
const sub = args[1];
|
|
15952
|
-
if (sub === "select") cmdTemplateSelect(cwd, args[2], raw);
|
|
16320
|
+
if (sub === "select") handleResult(cmdTemplateSelect(cwd, args[2]), raw);
|
|
15953
16321
|
else if (sub === "fill") {
|
|
15954
16322
|
const f = getFlags(args, "phase", "plan", "name", "type", "wave", "fields");
|
|
15955
|
-
cmdTemplateFill(cwd, args[2], {
|
|
16323
|
+
handleResult(cmdTemplateFill(cwd, args[2], {
|
|
15956
16324
|
phase: f.phase ?? "",
|
|
15957
16325
|
plan: f.plan ?? void 0,
|
|
15958
16326
|
name: f.name ?? void 0,
|
|
15959
16327
|
type: f.type ?? "execute",
|
|
15960
16328
|
wave: f.wave ?? "1",
|
|
15961
16329
|
fields: f.fields ? JSON.parse(f.fields) : {}
|
|
15962
|
-
}, raw);
|
|
16330
|
+
}), raw);
|
|
15963
16331
|
} else error("Unknown template subcommand. Available: select, fill");
|
|
15964
16332
|
};
|
|
15965
16333
|
const handleFrontmatter = (args, cwd, raw) => {
|
|
15966
16334
|
const sub = args[1];
|
|
15967
16335
|
const file = args[2];
|
|
15968
16336
|
const handler = sub ? {
|
|
15969
|
-
"get": () => cmdFrontmatterGet(cwd, file, getFlag(args, "--field"), raw),
|
|
15970
|
-
"set": () => cmdFrontmatterSet(cwd, file, getFlag(args, "--field"), getFlag(args, "--value") ?? void 0, raw),
|
|
15971
|
-
"merge": () => cmdFrontmatterMerge(cwd, file, getFlag(args, "--data"), raw),
|
|
15972
|
-
"validate": () => cmdFrontmatterValidate(cwd, file, getFlag(args, "--schema"), raw)
|
|
16337
|
+
"get": () => handleResult(cmdFrontmatterGet(cwd, file, getFlag(args, "--field")), raw),
|
|
16338
|
+
"set": () => handleResult(cmdFrontmatterSet(cwd, file, getFlag(args, "--field"), getFlag(args, "--value") ?? void 0), raw),
|
|
16339
|
+
"merge": () => handleResult(cmdFrontmatterMerge(cwd, file, getFlag(args, "--data")), raw),
|
|
16340
|
+
"validate": () => handleResult(cmdFrontmatterValidate(cwd, file, getFlag(args, "--schema")), raw)
|
|
15973
16341
|
}[sub] : void 0;
|
|
15974
16342
|
if (handler) return handler();
|
|
15975
16343
|
error("Unknown frontmatter subcommand. Available: get, set, merge, validate");
|
|
@@ -15977,46 +16345,48 @@ const handleFrontmatter = (args, cwd, raw) => {
|
|
|
15977
16345
|
const handleVerify = async (args, cwd, raw) => {
|
|
15978
16346
|
const sub = args[1];
|
|
15979
16347
|
const handler = sub ? {
|
|
15980
|
-
"plan-structure": () => cmdVerifyPlanStructure(cwd, args[2], raw),
|
|
15981
|
-
"phase-completeness": () => cmdVerifyPhaseCompleteness(cwd, args[2], raw),
|
|
15982
|
-
"references": () => cmdVerifyReferences(cwd, args[2], raw),
|
|
15983
|
-
"commits": () => cmdVerifyCommits(cwd, args.slice(2), raw),
|
|
15984
|
-
"artifacts": () => cmdVerifyArtifacts(cwd, args[2], raw),
|
|
15985
|
-
"key-links": () => cmdVerifyKeyLinks(cwd, args[2], raw)
|
|
16348
|
+
"plan-structure": () => handleResult(cmdVerifyPlanStructure(cwd, args[2]), raw),
|
|
16349
|
+
"phase-completeness": () => handleResult(cmdVerifyPhaseCompleteness(cwd, args[2]), raw),
|
|
16350
|
+
"references": () => handleResult(cmdVerifyReferences(cwd, args[2]), raw),
|
|
16351
|
+
"commits": async () => handleResult(await cmdVerifyCommits(cwd, args.slice(2)), raw),
|
|
16352
|
+
"artifacts": () => handleResult(cmdVerifyArtifacts(cwd, args[2]), raw),
|
|
16353
|
+
"key-links": () => handleResult(cmdVerifyKeyLinks(cwd, args[2]), raw)
|
|
15986
16354
|
}[sub] : void 0;
|
|
15987
16355
|
if (handler) return handler();
|
|
15988
16356
|
error("Unknown verify subcommand. Available: plan-structure, phase-completeness, references, commits, artifacts, key-links");
|
|
15989
16357
|
};
|
|
15990
|
-
const handlePhases = (args, cwd, raw) => {
|
|
16358
|
+
const handlePhases = async (args, cwd, raw) => {
|
|
15991
16359
|
if (args[1] === "list") {
|
|
15992
|
-
const f = getFlags(args, "type", "phase");
|
|
15993
|
-
cmdPhasesList(cwd, {
|
|
16360
|
+
const f = getFlags(args, "type", "phase", "offset", "limit");
|
|
16361
|
+
handleResult(await cmdPhasesList(cwd, {
|
|
15994
16362
|
type: f.type,
|
|
15995
16363
|
phase: f.phase,
|
|
15996
|
-
includeArchived: hasFlag(args, "include-archived")
|
|
15997
|
-
|
|
16364
|
+
includeArchived: hasFlag(args, "include-archived"),
|
|
16365
|
+
offset: f.offset !== null ? parseInt(f.offset, 10) : void 0,
|
|
16366
|
+
limit: f.limit !== null ? parseInt(f.limit, 10) : void 0
|
|
16367
|
+
}), raw);
|
|
15998
16368
|
} else error("Unknown phases subcommand. Available: list");
|
|
15999
16369
|
};
|
|
16000
|
-
const handleRoadmap = (args, cwd, raw) => {
|
|
16370
|
+
const handleRoadmap = async (args, cwd, raw) => {
|
|
16001
16371
|
const sub = args[1];
|
|
16002
16372
|
const handler = sub ? {
|
|
16003
|
-
"get-phase": () => cmdRoadmapGetPhase(cwd, args[2]
|
|
16004
|
-
"analyze": () => cmdRoadmapAnalyze(cwd
|
|
16005
|
-
"update-plan-progress": () => cmdRoadmapUpdatePlanProgress(cwd, args[2]
|
|
16373
|
+
"get-phase": () => cmdRoadmapGetPhase(cwd, args[2]),
|
|
16374
|
+
"analyze": () => cmdRoadmapAnalyze(cwd),
|
|
16375
|
+
"update-plan-progress": () => cmdRoadmapUpdatePlanProgress(cwd, args[2])
|
|
16006
16376
|
}[sub] : void 0;
|
|
16007
|
-
if (handler) return handler();
|
|
16377
|
+
if (handler) return handleResult(await handler(), raw);
|
|
16008
16378
|
error("Unknown roadmap subcommand. Available: get-phase, analyze, update-plan-progress");
|
|
16009
16379
|
};
|
|
16010
16380
|
const handlePhase = (args, cwd, raw) => {
|
|
16011
16381
|
const sub = args[1];
|
|
16012
16382
|
const handler = sub ? {
|
|
16013
|
-
"next-decimal": () => cmdPhaseNextDecimal(cwd, args[2]
|
|
16014
|
-
"add": () => cmdPhaseAdd(cwd, args.slice(2).join(" ")
|
|
16015
|
-
"insert": () => cmdPhaseInsert(cwd, args[2], args.slice(3).join(" ")
|
|
16016
|
-
"remove": () => cmdPhaseRemove(cwd, args[2], { force: hasFlag(args, "force") }
|
|
16017
|
-
"complete": () => cmdPhaseComplete(cwd, args[2]
|
|
16383
|
+
"next-decimal": () => cmdPhaseNextDecimal(cwd, args[2]),
|
|
16384
|
+
"add": () => cmdPhaseAdd(cwd, args.slice(2).join(" ")),
|
|
16385
|
+
"insert": () => cmdPhaseInsert(cwd, args[2], args.slice(3).join(" ")),
|
|
16386
|
+
"remove": () => cmdPhaseRemove(cwd, args[2], { force: hasFlag(args, "force") }),
|
|
16387
|
+
"complete": () => cmdPhaseComplete(cwd, args[2])
|
|
16018
16388
|
}[sub] : void 0;
|
|
16019
|
-
if (handler) return handler();
|
|
16389
|
+
if (handler) return handleResult(handler(), raw);
|
|
16020
16390
|
error("Unknown phase subcommand. Available: next-decimal, add, insert, remove, complete");
|
|
16021
16391
|
};
|
|
16022
16392
|
const handleMilestone = (args, cwd, raw) => {
|
|
@@ -16031,17 +16401,17 @@ const handleMilestone = (args, cwd, raw) => {
|
|
|
16031
16401
|
}
|
|
16032
16402
|
milestoneName = nameArgs.join(" ") || null;
|
|
16033
16403
|
}
|
|
16034
|
-
cmdMilestoneComplete(cwd, args[2], {
|
|
16404
|
+
handleResult(cmdMilestoneComplete(cwd, args[2], {
|
|
16035
16405
|
name: milestoneName ?? void 0,
|
|
16036
16406
|
archivePhases: hasFlag(args, "archive-phases")
|
|
16037
|
-
}, raw);
|
|
16407
|
+
}), raw);
|
|
16038
16408
|
} else error("Unknown milestone subcommand. Available: complete");
|
|
16039
16409
|
};
|
|
16040
16410
|
const handleValidate = (args, cwd, raw) => {
|
|
16041
16411
|
const sub = args[1];
|
|
16042
16412
|
const handler = sub ? {
|
|
16043
|
-
"consistency": () => cmdValidateConsistency(cwd, raw),
|
|
16044
|
-
"health": () => cmdValidateHealth(cwd, { repair: hasFlag(args, "repair") }, raw)
|
|
16413
|
+
"consistency": () => handleResult(cmdValidateConsistency(cwd), raw),
|
|
16414
|
+
"health": () => handleResult(cmdValidateHealth(cwd, { repair: hasFlag(args, "repair") }), raw)
|
|
16045
16415
|
}[sub] : void 0;
|
|
16046
16416
|
if (handler) return handler();
|
|
16047
16417
|
error("Unknown validate subcommand. Available: consistency, health");
|
|
@@ -16049,83 +16419,95 @@ const handleValidate = (args, cwd, raw) => {
|
|
|
16049
16419
|
const handleInit = (args, cwd, raw) => {
|
|
16050
16420
|
const workflow = args[1];
|
|
16051
16421
|
const handler = workflow ? {
|
|
16052
|
-
"execute-phase": () => cmdInitExecutePhase(cwd, args[2]
|
|
16053
|
-
"plan-phase": () => cmdInitPlanPhase(cwd, args[2]
|
|
16054
|
-
"new-project": () => cmdInitNewProject(cwd
|
|
16055
|
-
"new-milestone": () => cmdInitNewMilestone(cwd
|
|
16056
|
-
"quick": () => cmdInitQuick(cwd, args.slice(2).join(" ")
|
|
16057
|
-
"resume": () => cmdInitResume(cwd
|
|
16058
|
-
"verify-work": () => cmdInitVerifyWork(cwd, args[2]
|
|
16059
|
-
"phase-op": () => cmdInitPhaseOp(cwd, args[2]
|
|
16060
|
-
"todos": () => cmdInitTodos(cwd, args[2]
|
|
16061
|
-
"milestone-op": () => cmdInitMilestoneOp(cwd
|
|
16062
|
-
"map-codebase": () => cmdInitMapCodebase(cwd
|
|
16063
|
-
"init-existing": () => cmdInitExisting(cwd
|
|
16064
|
-
"progress": () => cmdInitProgress(cwd
|
|
16422
|
+
"execute-phase": () => cmdInitExecutePhase(cwd, args[2]),
|
|
16423
|
+
"plan-phase": () => cmdInitPlanPhase(cwd, args[2]),
|
|
16424
|
+
"new-project": () => cmdInitNewProject(cwd),
|
|
16425
|
+
"new-milestone": () => cmdInitNewMilestone(cwd),
|
|
16426
|
+
"quick": () => cmdInitQuick(cwd, args.slice(2).join(" ")),
|
|
16427
|
+
"resume": () => cmdInitResume(cwd),
|
|
16428
|
+
"verify-work": () => cmdInitVerifyWork(cwd, args[2]),
|
|
16429
|
+
"phase-op": () => cmdInitPhaseOp(cwd, args[2]),
|
|
16430
|
+
"todos": () => cmdInitTodos(cwd, args[2]),
|
|
16431
|
+
"milestone-op": () => cmdInitMilestoneOp(cwd),
|
|
16432
|
+
"map-codebase": () => cmdInitMapCodebase(cwd),
|
|
16433
|
+
"init-existing": () => cmdInitExisting(cwd),
|
|
16434
|
+
"progress": () => cmdInitProgress(cwd)
|
|
16065
16435
|
}[workflow] : void 0;
|
|
16066
|
-
if (handler) return handler();
|
|
16436
|
+
if (handler) return handleResult(handler(), raw);
|
|
16067
16437
|
error(`Unknown init workflow: ${workflow}\nAvailable: execute-phase, plan-phase, new-project, new-milestone, quick, resume, verify-work, phase-op, todos, milestone-op, map-codebase, init-existing, progress`);
|
|
16068
16438
|
};
|
|
16069
16439
|
const COMMANDS = {
|
|
16070
16440
|
"state": handleState,
|
|
16071
|
-
"resolve-model": (args, cwd, raw) => cmdResolveModel(cwd, args[1], raw),
|
|
16072
|
-
"find-phase": (args, cwd, raw) => cmdFindPhase(cwd, args[1], raw),
|
|
16441
|
+
"resolve-model": (args, cwd, raw) => handleResult(cmdResolveModel(cwd, args[1], raw), raw),
|
|
16442
|
+
"find-phase": (args, cwd, raw) => handleResult(cmdFindPhase(cwd, args[1]), raw),
|
|
16073
16443
|
"commit": async (args, cwd, raw) => {
|
|
16074
16444
|
const files = args.indexOf("--files") !== -1 ? args.slice(args.indexOf("--files") + 1).filter((a) => !a.startsWith("--")) : [];
|
|
16075
|
-
await cmdCommit(cwd, args[1], files, raw, hasFlag(args, "amend"));
|
|
16445
|
+
handleResult(await cmdCommit(cwd, args[1], files, raw, hasFlag(args, "amend")), raw);
|
|
16076
16446
|
},
|
|
16077
16447
|
"verify-summary": async (args, cwd, raw) => {
|
|
16078
16448
|
const countIndex = args.indexOf("--check-count");
|
|
16079
16449
|
const checkCount = countIndex !== -1 ? parseInt(args[countIndex + 1], 10) : 2;
|
|
16080
|
-
await cmdVerifySummary(cwd, args[1], checkCount, raw);
|
|
16450
|
+
handleResult(await cmdVerifySummary(cwd, args[1], checkCount), raw);
|
|
16081
16451
|
},
|
|
16082
16452
|
"template": handleTemplate,
|
|
16083
16453
|
"frontmatter": handleFrontmatter,
|
|
16084
16454
|
"verify": handleVerify,
|
|
16085
|
-
"generate-slug": (args, _cwd, raw) => cmdGenerateSlug(args[1], raw),
|
|
16086
|
-
"current-timestamp": (args, _cwd, raw) => cmdCurrentTimestamp(args[1] || "full", raw),
|
|
16087
|
-
"list-todos": (args, cwd, raw) => cmdListTodos(cwd, args[1], raw),
|
|
16088
|
-
"verify-path-exists": (args, cwd, raw) => cmdVerifyPathExists(cwd, args[1], raw),
|
|
16089
|
-
"config-ensure-section": (_args, cwd, raw) => cmdConfigEnsureSection(cwd, raw),
|
|
16090
|
-
"config-set": (args, cwd, raw) => cmdConfigSet(cwd, args[1], args[2], raw),
|
|
16091
|
-
"config-get": (args, cwd, raw) => cmdConfigGet(cwd, args[1], raw),
|
|
16092
|
-
"history-digest": (_args, cwd, raw) => cmdHistoryDigest(cwd, raw),
|
|
16455
|
+
"generate-slug": (args, _cwd, raw) => handleResult(cmdGenerateSlug(args[1], raw), raw),
|
|
16456
|
+
"current-timestamp": (args, _cwd, raw) => handleResult(cmdCurrentTimestamp(args[1] || "full", raw), raw),
|
|
16457
|
+
"list-todos": (args, cwd, raw) => handleResult(cmdListTodos(cwd, args[1], raw), raw),
|
|
16458
|
+
"verify-path-exists": (args, cwd, raw) => handleResult(cmdVerifyPathExists(cwd, args[1], raw), raw),
|
|
16459
|
+
"config-ensure-section": (_args, cwd, raw) => handleResult(cmdConfigEnsureSection(cwd, raw), raw),
|
|
16460
|
+
"config-set": (args, cwd, raw) => handleResult(cmdConfigSet(cwd, args[1], args[2], raw), raw),
|
|
16461
|
+
"config-get": (args, cwd, raw) => handleResult(cmdConfigGet(cwd, args[1], raw), raw),
|
|
16462
|
+
"history-digest": (_args, cwd, raw) => handleResult(cmdHistoryDigest(cwd, raw), raw),
|
|
16093
16463
|
"phases": handlePhases,
|
|
16094
16464
|
"roadmap": handleRoadmap,
|
|
16095
16465
|
"requirements": (args, cwd, raw) => {
|
|
16096
|
-
if (args[1] === "mark-complete") cmdRequirementsMarkComplete(cwd, args.slice(2), raw);
|
|
16466
|
+
if (args[1] === "mark-complete") handleResult(cmdRequirementsMarkComplete(cwd, args.slice(2)), raw);
|
|
16097
16467
|
else error("Unknown requirements subcommand. Available: mark-complete");
|
|
16098
16468
|
},
|
|
16099
16469
|
"phase": handlePhase,
|
|
16100
16470
|
"milestone": handleMilestone,
|
|
16101
16471
|
"validate": handleValidate,
|
|
16102
|
-
"progress": (args, cwd, raw) => cmdProgressRender(cwd, args[1] || "json", raw),
|
|
16472
|
+
"progress": (args, cwd, raw) => handleResult(cmdProgressRender(cwd, args[1] || "json", raw), raw),
|
|
16103
16473
|
"todo": (args, cwd, raw) => {
|
|
16104
|
-
if (args[1] === "complete") cmdTodoComplete(cwd, args[2], raw);
|
|
16474
|
+
if (args[1] === "complete") handleResult(cmdTodoComplete(cwd, args[2], raw), raw);
|
|
16105
16475
|
else error("Unknown todo subcommand. Available: complete");
|
|
16106
16476
|
},
|
|
16107
16477
|
"scaffold": (args, cwd, raw) => {
|
|
16108
16478
|
const f = getFlags(args, "phase", "name");
|
|
16109
|
-
cmdScaffold(cwd, args[1], {
|
|
16479
|
+
handleResult(cmdScaffold(cwd, args[1], {
|
|
16110
16480
|
phase: f.phase,
|
|
16111
16481
|
name: f.name ? args.slice(args.indexOf("--name") + 1).join(" ") : null
|
|
16112
|
-
}, raw);
|
|
16482
|
+
}, raw), raw);
|
|
16113
16483
|
},
|
|
16114
16484
|
"init": handleInit,
|
|
16115
|
-
"phase-plan-index": (args, cwd, raw) => cmdPhasePlanIndex(cwd, args[1], raw),
|
|
16116
|
-
"state-snapshot": (_args, cwd, raw) => cmdStateSnapshot(cwd, raw),
|
|
16485
|
+
"phase-plan-index": (args, cwd, raw) => handleResult(cmdPhasePlanIndex(cwd, args[1]), raw),
|
|
16486
|
+
"state-snapshot": (_args, cwd, raw) => handleResult(cmdStateSnapshot(cwd, raw), raw),
|
|
16117
16487
|
"summary-extract": (args, cwd, raw) => {
|
|
16118
16488
|
const fieldsIndex = args.indexOf("--fields");
|
|
16119
16489
|
const fields = fieldsIndex !== -1 ? args[fieldsIndex + 1].split(",") : null;
|
|
16120
|
-
cmdSummaryExtract(cwd, args[1], fields, raw);
|
|
16490
|
+
handleResult(cmdSummaryExtract(cwd, args[1], fields, raw), raw);
|
|
16121
16491
|
},
|
|
16122
16492
|
"websearch": async (args, _cwd, raw) => {
|
|
16123
16493
|
const f = getFlags(args, "limit", "freshness");
|
|
16124
|
-
await cmdWebsearch(args[1], {
|
|
16494
|
+
handleResult(await cmdWebsearch(args[1], {
|
|
16125
16495
|
limit: f.limit ? parseInt(f.limit, 10) : 10,
|
|
16126
16496
|
freshness: f.freshness ?? void 0
|
|
16127
|
-
}, raw);
|
|
16497
|
+
}, raw), raw);
|
|
16128
16498
|
},
|
|
16499
|
+
"artefakte-read": (args, cwd, raw) => handleResult(cmdArtefakteRead(cwd, args[1], getFlag(args, "--phase") ?? void 0, raw), raw),
|
|
16500
|
+
"artefakte-write": (args, cwd, raw) => handleResult(cmdArtefakteWrite(cwd, args[1], getFlag(args, "--content") ?? void 0, getFlag(args, "--phase") ?? void 0, raw), raw),
|
|
16501
|
+
"artefakte-append": (args, cwd, raw) => handleResult(cmdArtefakteAppend(cwd, args[1], getFlag(args, "--entry") ?? void 0, getFlag(args, "--phase") ?? void 0, raw), raw),
|
|
16502
|
+
"artefakte-list": (args, cwd, raw) => handleResult(cmdArtefakteList(cwd, getFlag(args, "--phase") ?? void 0, raw), raw),
|
|
16503
|
+
"context-load": (args, cwd, raw) => handleResult(cmdContextLoad(cwd, getFlag(args, "--phase") ?? void 0, getFlag(args, "--topic") ?? void 0, hasFlag(args, "include-history")), raw),
|
|
16504
|
+
"skill-list": (_args, cwd, raw) => cmdSkillList(cwd, raw),
|
|
16505
|
+
"skill-install": (args, cwd, raw) => cmdSkillInstall(cwd, args[1], raw),
|
|
16506
|
+
"skill-update": (args, cwd, raw) => cmdSkillUpdate(cwd, args[1], raw),
|
|
16507
|
+
"start": async (args, cwd, raw) => handleResult(await cmdStart(cwd, {
|
|
16508
|
+
noBrowser: hasFlag(args, "no-browser"),
|
|
16509
|
+
networkMode: hasFlag(args, "network")
|
|
16510
|
+
}), raw),
|
|
16129
16511
|
"dashboard": (args) => handleDashboard(args.slice(1)),
|
|
16130
16512
|
"start-server": async () => {
|
|
16131
16513
|
const serverPath = node_path.join(__dirname, "mcp-server.cjs");
|