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.
Files changed (178) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/adapters/index.d.ts +0 -11
  3. package/dist/adapters/index.d.ts.map +1 -1
  4. package/dist/adapters/index.js +4 -40
  5. package/dist/adapters/index.js.map +1 -1
  6. package/dist/assets/CHANGELOG.md +17 -0
  7. package/dist/assets/dashboard/client/assets/{index-wtQDvXzr.js → index-C_eAetZJ.js} +60 -60
  8. package/dist/assets/dashboard/client/assets/index-CmiJKqOU.css +32 -0
  9. package/dist/assets/dashboard/client/index.html +2 -2
  10. package/dist/assets/dashboard/server.js +467 -271
  11. package/dist/assets/templates/agents/AGENTS.md +13 -1
  12. package/dist/assets/templates/agents/maxsim-debugger.md +2 -2
  13. package/dist/assets/templates/agents/maxsim-executor.md +5 -5
  14. package/dist/assets/templates/agents/maxsim-phase-researcher.md +2 -2
  15. package/dist/assets/templates/agents/maxsim-plan-checker.md +2 -2
  16. package/dist/assets/templates/agents/maxsim-planner.md +3 -3
  17. package/dist/assets/templates/commands/maxsim/add-todo.md +15 -5
  18. package/dist/assets/templates/commands/maxsim/discuss-phase.md +1 -0
  19. package/dist/assets/templates/commands/maxsim/init-existing.md +4 -0
  20. package/dist/assets/templates/commands/maxsim/new-project.md +4 -0
  21. package/dist/assets/templates/references/thinking-partner.md +41 -0
  22. package/dist/assets/templates/skills/batch-worktree/SKILL.md +137 -0
  23. package/dist/assets/templates/skills/brainstorming/SKILL.md +159 -0
  24. package/dist/assets/templates/skills/roadmap-writing/SKILL.md +198 -0
  25. package/dist/assets/templates/skills/sdd/SKILL.md +175 -0
  26. package/dist/assets/templates/skills/simplify/SKILL.md +48 -0
  27. package/dist/assets/templates/skills/using-maxsim/SKILL.md +6 -1
  28. package/dist/assets/templates/templates/acceptance-criteria.md +10 -0
  29. package/dist/assets/templates/templates/decisions.md +10 -0
  30. package/dist/assets/templates/templates/no-gos.md +9 -0
  31. package/dist/assets/templates/workflows/add-todo.md +89 -0
  32. package/dist/assets/templates/workflows/discuss-phase.md +85 -1
  33. package/dist/assets/templates/workflows/execute-phase.md +22 -2
  34. package/dist/assets/templates/workflows/execute-plan.md +166 -0
  35. package/dist/assets/templates/workflows/init-existing.md +116 -0
  36. package/dist/assets/templates/workflows/new-project.md +105 -1
  37. package/dist/assets/templates/workflows/plan-phase.md +3 -3
  38. package/dist/assets/templates/workflows/quick.md +2 -2
  39. package/dist/cli.cjs +1264 -882
  40. package/dist/cli.cjs.map +1 -1
  41. package/dist/cli.js +97 -74
  42. package/dist/cli.js.map +1 -1
  43. package/dist/core/artefakte.d.ts +12 -0
  44. package/dist/core/artefakte.d.ts.map +1 -0
  45. package/dist/core/artefakte.js +136 -0
  46. package/dist/core/artefakte.js.map +1 -0
  47. package/dist/core/commands.d.ts +13 -13
  48. package/dist/core/commands.d.ts.map +1 -1
  49. package/dist/core/commands.js +44 -57
  50. package/dist/core/commands.js.map +1 -1
  51. package/dist/core/config.d.ts +4 -3
  52. package/dist/core/config.d.ts.map +1 -1
  53. package/dist/core/config.js +14 -18
  54. package/dist/core/config.js.map +1 -1
  55. package/dist/core/context-loader.d.ts +20 -0
  56. package/dist/core/context-loader.d.ts.map +1 -0
  57. package/dist/core/context-loader.js +154 -0
  58. package/dist/core/context-loader.js.map +1 -0
  59. package/dist/core/core.d.ts +8 -2
  60. package/dist/core/core.d.ts.map +1 -1
  61. package/dist/core/core.js +47 -11
  62. package/dist/core/core.js.map +1 -1
  63. package/dist/core/dashboard-launcher.d.ts +1 -1
  64. package/dist/core/dashboard-launcher.d.ts.map +1 -1
  65. package/dist/core/dashboard-launcher.js +18 -15
  66. package/dist/core/dashboard-launcher.js.map +1 -1
  67. package/dist/core/frontmatter.d.ts +5 -5
  68. package/dist/core/frontmatter.d.ts.map +1 -1
  69. package/dist/core/frontmatter.js +21 -26
  70. package/dist/core/frontmatter.js.map +1 -1
  71. package/dist/core/index.d.ts +8 -3
  72. package/dist/core/index.d.ts.map +1 -1
  73. package/dist/core/index.js +23 -3
  74. package/dist/core/index.js.map +1 -1
  75. package/dist/core/init.d.ts +14 -14
  76. package/dist/core/init.d.ts.map +1 -1
  77. package/dist/core/init.js +93 -154
  78. package/dist/core/init.js.map +1 -1
  79. package/dist/core/milestone.d.ts +3 -3
  80. package/dist/core/milestone.d.ts.map +1 -1
  81. package/dist/core/milestone.js +9 -9
  82. package/dist/core/milestone.js.map +1 -1
  83. package/dist/core/phase.d.ts +9 -9
  84. package/dist/core/phase.d.ts.map +1 -1
  85. package/dist/core/phase.js +64 -68
  86. package/dist/core/phase.js.map +1 -1
  87. package/dist/core/roadmap.d.ts +4 -3
  88. package/dist/core/roadmap.d.ts.map +1 -1
  89. package/dist/core/roadmap.js +46 -109
  90. package/dist/core/roadmap.js.map +1 -1
  91. package/dist/core/skills.d.ts +19 -0
  92. package/dist/core/skills.d.ts.map +1 -0
  93. package/dist/core/skills.js +145 -0
  94. package/dist/core/skills.js.map +1 -0
  95. package/dist/core/start.d.ts +15 -0
  96. package/dist/core/start.d.ts.map +1 -0
  97. package/dist/core/start.js +80 -0
  98. package/dist/core/start.js.map +1 -0
  99. package/dist/core/state.d.ts +13 -13
  100. package/dist/core/state.d.ts.map +1 -1
  101. package/dist/core/state.js +119 -126
  102. package/dist/core/state.js.map +1 -1
  103. package/dist/core/template.d.ts +3 -3
  104. package/dist/core/template.d.ts.map +1 -1
  105. package/dist/core/template.js +12 -14
  106. package/dist/core/template.js.map +1 -1
  107. package/dist/core/types.d.ts +14 -2
  108. package/dist/core/types.d.ts.map +1 -1
  109. package/dist/core/types.js +8 -0
  110. package/dist/core/types.js.map +1 -1
  111. package/dist/core/verify.d.ts +10 -9
  112. package/dist/core/verify.d.ts.map +1 -1
  113. package/dist/core/verify.js +38 -48
  114. package/dist/core/verify.js.map +1 -1
  115. package/dist/core-TFSlUjV1.cjs +4312 -0
  116. package/dist/core-TFSlUjV1.cjs.map +1 -0
  117. package/dist/install/adapters.d.ts +2 -11
  118. package/dist/install/adapters.d.ts.map +1 -1
  119. package/dist/install/adapters.js +16 -154
  120. package/dist/install/adapters.js.map +1 -1
  121. package/dist/install/copy.d.ts +1 -10
  122. package/dist/install/copy.d.ts.map +1 -1
  123. package/dist/install/copy.js +5 -125
  124. package/dist/install/copy.js.map +1 -1
  125. package/dist/install/hooks.d.ts +4 -5
  126. package/dist/install/hooks.d.ts.map +1 -1
  127. package/dist/install/hooks.js +46 -71
  128. package/dist/install/hooks.js.map +1 -1
  129. package/dist/install/index.js +163 -226
  130. package/dist/install/index.js.map +1 -1
  131. package/dist/install/manifest.d.ts +5 -2
  132. package/dist/install/manifest.d.ts.map +1 -1
  133. package/dist/install/manifest.js +20 -26
  134. package/dist/install/manifest.js.map +1 -1
  135. package/dist/install/patches.d.ts +1 -2
  136. package/dist/install/patches.d.ts.map +1 -1
  137. package/dist/install/patches.js +4 -16
  138. package/dist/install/patches.js.map +1 -1
  139. package/dist/install/shared.d.ts +24 -18
  140. package/dist/install/shared.d.ts.map +1 -1
  141. package/dist/install/shared.js +65 -35
  142. package/dist/install/shared.js.map +1 -1
  143. package/dist/install/uninstall.d.ts +2 -3
  144. package/dist/install/uninstall.d.ts.map +1 -1
  145. package/dist/install/uninstall.js +24 -82
  146. package/dist/install/uninstall.js.map +1 -1
  147. package/dist/install.cjs +321 -1230
  148. package/dist/install.cjs.map +1 -1
  149. package/dist/mcp-server.cjs +38 -14
  150. package/dist/mcp-server.cjs.map +1 -1
  151. package/dist/skills-BOSxYUzf.cjs +6812 -0
  152. package/dist/skills-BOSxYUzf.cjs.map +1 -0
  153. package/package.json +1 -1
  154. package/dist/adapters/codex.d.ts +0 -19
  155. package/dist/adapters/codex.d.ts.map +0 -1
  156. package/dist/adapters/codex.js +0 -94
  157. package/dist/adapters/codex.js.map +0 -1
  158. package/dist/adapters/gemini.d.ts +0 -19
  159. package/dist/adapters/gemini.d.ts.map +0 -1
  160. package/dist/adapters/gemini.js +0 -96
  161. package/dist/adapters/gemini.js.map +0 -1
  162. package/dist/adapters/opencode.d.ts +0 -17
  163. package/dist/adapters/opencode.d.ts.map +0 -1
  164. package/dist/adapters/opencode.js +0 -111
  165. package/dist/adapters/opencode.js.map +0 -1
  166. package/dist/adapters/transforms/content.d.ts +0 -39
  167. package/dist/adapters/transforms/content.d.ts.map +0 -1
  168. package/dist/adapters/transforms/content.js +0 -125
  169. package/dist/adapters/transforms/content.js.map +0 -1
  170. package/dist/adapters/transforms/frontmatter.d.ts +0 -42
  171. package/dist/adapters/transforms/frontmatter.d.ts.map +0 -1
  172. package/dist/adapters/transforms/frontmatter.js +0 -204
  173. package/dist/adapters/transforms/frontmatter.js.map +0 -1
  174. package/dist/adapters/transforms/tool-maps.d.ts +0 -20
  175. package/dist/adapters/transforms/tool-maps.d.ts.map +0 -1
  176. package/dist/adapters/transforms/tool-maps.js +0 -64
  177. package/dist/adapters/transforms/tool-maps.js.map +0 -1
  178. 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
- /** Log only when MAXSIM_DEBUG is set. */
4787
- function debugLog(e) {
4788
- if (process.env.MAXSIM_DEBUG) console.error(e);
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: thrown.message ?? ""
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, raw) {
11836
- if (!filePath) error("file path required");
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
- output({
11840
- error: "File not found",
11841
- path: filePath
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
- output({
11850
- error: "Field not found",
11851
- field
11852
- }, raw);
11853
- return;
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, raw) {
11859
- if (!filePath || !field || value === void 0) error("file, field, and value required");
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
- output({
11863
- error: "File not found",
11864
- path: filePath
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
- output({
11920
+ return cmdOk({
11880
11921
  updated: true,
11881
11922
  field,
11882
11923
  value: parsedValue
11883
- }, raw, "true");
11924
+ }, "true");
11884
11925
  }
11885
- function cmdFrontmatterMerge(cwd, filePath, data, raw) {
11886
- if (!filePath || !data) error("file and data required");
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
- output({
11890
- error: "File not found",
11891
- path: filePath
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
- error("Invalid JSON for --data");
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
- output({
11944
+ return cmdOk({
11908
11945
  merged: true,
11909
11946
  fields: Object.keys(mergeData)
11910
- }, raw, "true");
11947
+ }, "true");
11911
11948
  }
11912
- function cmdFrontmatterValidate(cwd, filePath, schemaName, raw) {
11913
- if (!filePath || !schemaName) error("file and schema required");
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) error(`Unknown schema: ${schemaName}. Available: ${Object.keys(FRONTMATTER_SCHEMAS).join(", ")}`);
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
- output({
11919
- error: "File not found",
11920
- path: filePath
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
- output({
11961
+ return cmdOk({
11928
11962
  valid: missing.length === 0,
11929
11963
  missing,
11930
11964
  present,
11931
11965
  schema: schemaName
11932
- }, raw, missing.length === 0 ? "valid" : "invalid");
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
- error("Failed to create .planning directory: " + err.message);
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
- output({
12010
+ return cmdOk({
11980
12011
  created: true,
11981
12012
  path: ".planning/config.json"
11982
- }, raw, "created");
12013
+ }, raw ? "created" : void 0);
11983
12014
  } catch (err) {
11984
- error("Failed to create config.json: " + err.message);
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) error("Usage: config-set <key.path> <value>");
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
- error("Failed to read config.json: " + err.message);
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
- output({
12041
+ return cmdOk({
12011
12042
  updated: true,
12012
12043
  key: keyPath,
12013
12044
  value: parsedValue
12014
- }, raw, `${keyPath}=${parsedValue}`);
12045
+ }, raw ? `${keyPath}=${parsedValue}` : void 0);
12015
12046
  } catch (err) {
12016
- error("Failed to write config.json: " + err.message);
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) error("Usage: config-get <key.path>");
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 error("No config.json found at " + configPath);
12056
+ else return cmdErr("No config.json found at " + configPath);
12026
12057
  } catch (err) {
12027
- if (err.message.startsWith("No config.json")) throw err;
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") error(`Key not found: ${keyPath}`);
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) error(`Key not found: ${keyPath}`);
12037
- output(current, raw, String(current));
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 pattern = new RegExp(`\\*\\*${fieldName}:\\*\\*\\s*(.+)`, "i");
12056
- const match = content.match(pattern);
12057
- return match ? match[1].trim() : null;
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 pattern = new RegExp(`(\\*\\*${escaped}:\\*\\*\\s*)(.*)`, "i");
12062
- if (pattern.test(content)) return content.replace(pattern, (_match, prefix) => `${prefix}${newValue}`);
12063
- return null;
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
- let stateRaw = "";
12093
- try {
12094
- stateRaw = node_fs.default.readFileSync(statePath(cwd), "utf-8");
12095
- } catch (e) {
12096
- debugLog(e);
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
- output(result, true, [
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
- output(result);
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
- output({ content }, raw, content);
12133
- return;
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 fieldPattern = new RegExp(`\\*\\*${fieldEscaped}:\\*\\*\\s*(.*)`, "i");
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
- output({ [section]: sectionMatch[1].trim() }, raw, sectionMatch[1].trim());
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
- error("STATE.md not found");
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 fieldEscaped = escapeStringRegexp(field);
12164
- const pattern = new RegExp(`(\\*\\*${fieldEscaped}:\\*\\*\\s*)(.*)`, "i");
12165
- if (pattern.test(content)) {
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
- output(results, raw, results.updated.length > 0 ? "true" : "false");
12203
+ return cmdOk(results, raw ? results.updated.length > 0 ? "true" : "false" : void 0);
12172
12204
  } catch (e) {
12173
12205
  rethrowCliSignals(e);
12174
- error("STATE.md not found");
12206
+ return cmdErr("STATE.md not found");
12175
12207
  }
12176
12208
  }
12177
12209
  function cmdStateUpdate(cwd, field, value) {
12178
- if (!field || value === void 0) error("field and value required for state update");
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
- let content = node_fs.default.readFileSync(statePath$4, "utf-8");
12182
- const fieldEscaped = escapeStringRegexp(field);
12183
- const pattern = new RegExp(`(\\*\\*${fieldEscaped}:\\*\\*\\s*)(.*)`, "i");
12184
- if (pattern.test(content)) {
12185
- content = content.replace(pattern, (_match, prefix) => `${prefix}${value}`);
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
- output({
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
- output({
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, "false");
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
- output({
12254
+ return cmdOk({
12232
12255
  advanced: true,
12233
12256
  previous_plan: currentPlan,
12234
12257
  current_plan: newPlan,
12235
12258
  total_plans: totalPlans
12236
- }, raw, "true");
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
- output({ error: "phase, plan, and duration required" }, raw);
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
- output({
12277
+ return cmdOk({
12261
12278
  recorded: true,
12262
12279
  phase,
12263
12280
  plan,
12264
12281
  duration
12265
- }, raw, "true");
12266
- } else output({
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, "false");
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 progressPattern = /(\*\*Progress:\*\*\s*).*/i;
12294
- if (progressPattern.test(content)) {
12295
- content = content.replace(progressPattern, (_match, prefix) => `${prefix}${progressStr}`);
12296
- node_fs.default.writeFileSync(statePath$7, content, "utf-8");
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, progressStr);
12304
- } else output({
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, "false");
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
- output({
12332
+ return cmdOk({
12323
12333
  added: false,
12324
12334
  reason: thrown.message
12325
- }, raw, "false");
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
- output({
12343
+ return cmdOk({
12338
12344
  added: true,
12339
12345
  decision: entry
12340
- }, raw, "true");
12341
- } else output({
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, "false");
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
- output({
12360
+ return cmdOk({
12358
12361
  added: false,
12359
12362
  reason: thrown.message
12360
- }, raw, "false");
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
- output({
12369
+ return cmdOk({
12371
12370
  added: true,
12372
12371
  blocker: blockerText
12373
- }, raw, "true");
12374
- } else output({
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, "false");
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
- output({ error: "STATE.md not found" }, raw);
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 = /(###?\s*(?:Blockers|Blockers\/Concerns|Concerns)\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i;
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 (!line.startsWith("- ")) return true;
12387
+ if (!/^\s*[-*]\s+/.test(line)) return true;
12395
12388
  return !line.toLowerCase().includes(text.toLowerCase());
12396
12389
  }).join("\n");
12397
- if (!newBody.trim() || !newBody.includes("- ")) newBody = "None\n";
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
- output({
12393
+ return cmdOk({
12401
12394
  resolved: true,
12402
12395
  blocker: text
12403
- }, raw, "true");
12404
- } else output({
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, "false");
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
- output({
12435
+ return cmdOk({
12446
12436
  recorded: true,
12447
12437
  updated
12448
- }, raw, "true");
12449
- } else output({
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, "false");
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(/##\s*Decisions Made[\s\S]*?\n\|[^\n]+\n\|[-|\s]+\n([\s\S]*?)(?=\n##|\n$|$)/i);
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
- const cells = row.split("|").map((c) => c.trim()).filter(Boolean);
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(/##\s*Blockers\s*\n([\s\S]*?)(?=\n##|$)/i);
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(/^-\s+(.+)$/gm) || [];
12496
- for (const item of items) blockers.push(item.replace(/^-\s+/, "").trim());
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(/##\s*Session\s*\n([\s\S]*?)(?=\n##|$)/i);
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
- const lastDateMatch = sessionSection.match(/\*\*Last Date:\*\*\s*(.+)/i);
12507
- const stoppedAtMatch = sessionSection.match(/\*\*Stopped At:\*\*\s*(.+)/i);
12508
- const resumeFileMatch = sessionSection.match(/\*\*Resume File:\*\*\s*(.+)/i);
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
- output({
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
- }, raw);
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, raw) {
12518
+ function cmdRoadmapGetPhase(cwd, phaseNum) {
12538
12519
  const rmPath = roadmapPath(cwd);
12539
- if (!node_fs.default.existsSync(rmPath)) {
12540
- output({
12541
- found: false,
12542
- error: "ROADMAP.md not found"
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
- output({
12556
- found: false,
12557
- phase_number: phaseNum,
12558
- phase_name: checklistMatch[1].trim(),
12559
- error: "malformed_roadmap",
12560
- message: `Phase ${phaseNum} exists in summary list but missing "### Phase ${phaseNum}:" detail section. ROADMAP.md needs both formats.`
12561
- }, raw, "");
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
- }, raw, "");
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
- output({
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
- }, raw, section);
12559
+ }, section);
12586
12560
  } catch (e) {
12587
- rethrowCliSignals(e);
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, raw) {
12592
- const rmPath = roadmapPath(cwd);
12593
- if (!node_fs.default.existsSync(rmPath)) {
12594
- output({
12595
- error: "ROADMAP.md not found",
12596
- milestones: [],
12597
- phases: [],
12598
- current_phase: null
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 phases = [];
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
- const normalized = normalizePhaseName(phaseNum);
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 = listSubDirs(phasesDir).find((d) => d.startsWith(normalized + "-") || d === normalized);
12607
+ const dirMatch = allDirs.find((d) => d.startsWith(p.normalized + "-") || d === p.normalized);
12626
12608
  if (dirMatch) {
12627
- const phaseFiles = node_fs.default.readdirSync(node_path.default.join(phasesDir, dirMatch));
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 checkboxPattern = new RegExp(`-\\s*\\[(x| )\\]\\s*.*Phase\\s+${phaseNum.replace(".", "\\.")}`, "i");
12643
- const checkboxMatch = content.match(checkboxPattern);
12624
+ const checkboxMatch = content.match(p.checkboxPattern);
12644
12625
  const roadmapComplete = checkboxMatch ? checkboxMatch[1] === "x" : false;
12645
- phases.push({
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
- output({
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
- }, raw);
12668
+ });
12688
12669
  }
12689
- function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
12690
- if (!phaseNum) error("phase number required for roadmap update-plan-progress");
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) error(`Phase ${phaseNum} not found`);
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
- output({
12698
- updated: false,
12699
- reason: "No plans found",
12700
- plan_count: 0,
12701
- summary_count: 0
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
- output({
12710
- updated: false,
12711
- reason: "ROADMAP.md not found",
12712
- plan_count: planCount,
12713
- summary_count: summaryCount
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(tablePattern, `$1 ${summaryCount}/${planCount} $2 ${status.padEnd(11)}$3${dateField}$4`);
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(planCountPattern, `$1${planCountText}`);
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
- output({
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
- }, raw, `${summaryCount}/${planCount} ${status}`);
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, raw) {
12748
- if (!reqIdsRaw || reqIdsRaw.length === 0) error("requirement IDs required. Usage: requirements mark-complete REQ-01,REQ-02 or REQ-01 REQ-02");
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) error("no valid requirement IDs found");
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
- output({
12754
- updated: false,
12755
- reason: "REQUIREMENTS.md not found",
12756
- ids: reqIds
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
- output({
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
- }, raw, `${updated.length}/${reqIds.length} requirements marked complete`);
12750
+ }, `${updated.length}/${reqIds.length} requirements marked complete`);
12784
12751
  }
12785
- function cmdMilestoneComplete(cwd, version, options, raw) {
12786
- if (!version) error("version required for milestone complete (e.g., v1.0)");
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
- output({
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
- }, raw);
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) error("text required for slug generation");
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
- output({ slug }, raw, slug);
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
- output({ timestamp: result }, raw, result);
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
- output({
13313
+ return cmdOk({
13347
13314
  count,
13348
13315
  todos
13349
- }, raw, count.toString());
13316
+ }, raw ? count.toString() : void 0);
13350
13317
  }
13351
13318
  function cmdVerifyPathExists(cwd, targetPath, raw) {
13352
- if (!targetPath) error("path required for verification");
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
- output({
13323
+ return cmdOk({
13357
13324
  exists: true,
13358
13325
  type: stats.isDirectory() ? "directory" : stats.isFile() ? "file" : "other"
13359
- }, raw, "true");
13326
+ }, raw ? "true" : void 0);
13360
13327
  } catch (e) {
13361
13328
  rethrowCliSignals(e);
13362
- output({
13329
+ return cmdOk({
13363
13330
  exists: false,
13364
13331
  type: null
13365
- }, raw, "false");
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
- output({
13394
- phases: {},
13395
- decisions: [],
13396
- tech_stack: []
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
- output(outputDigest, raw);
13404
+ return cmdOk(outputDigest);
13441
13405
  } catch (e) {
13442
13406
  rethrowCliSignals(e);
13443
- error("Failed to generate history digest: " + e.message);
13407
+ return cmdErr("Failed to generate history digest: " + e.message);
13444
13408
  }
13445
13409
  }
13446
13410
  function cmdResolveModel(cwd, agentType, raw) {
13447
- if (!agentType) error("agent-type required");
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
- output({
13452
- model: "sonnet",
13453
- profile,
13454
- unknown_agent: true
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
- output({
13421
+ return cmdOk({
13461
13422
  model,
13462
13423
  profile
13463
- }, raw, model);
13424
+ }, raw ? model : void 0);
13464
13425
  }
13465
13426
  async function cmdCommit(cwd, message, files, raw, amend) {
13466
- if (!message && !amend) error("commit message required");
13467
- if (!loadConfig(cwd).commit_docs) {
13468
- output({
13469
- committed: false,
13470
- hash: null,
13471
- reason: "skipped_commit_docs_false"
13472
- }, raw, "skipped");
13473
- return;
13474
- }
13475
- if (await isGitIgnored(cwd, ".planning")) {
13476
- output({
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
- output({
13497
- committed: false,
13498
- hash: null,
13499
- reason: "nothing_to_commit"
13500
- }, raw, "nothing");
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, "nothing");
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
- output({
13468
+ return cmdOk({
13518
13469
  committed: true,
13519
13470
  hash,
13520
13471
  reason: "committed"
13521
- }, raw, hash || "committed");
13472
+ }, raw ? hash || "committed" : void 0);
13522
13473
  }
13523
13474
  function cmdSummaryExtract(cwd, summaryPath, fields, raw) {
13524
- if (!summaryPath) error("summary-path required for summary-extract");
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
- output({
13528
- error: "File not found",
13529
- path: summaryPath
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
- output(filtered, raw);
13562
- return;
13509
+ return cmdOk(filtered);
13563
13510
  }
13564
- output(fullResult, raw);
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
- output({
13570
- available: false,
13571
- reason: "BRAVE_API_KEY not set"
13572
- }, raw, "");
13573
- return;
13574
- }
13575
- if (!query) {
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
- output({
13597
- available: false,
13598
- error: `API error: ${response.status}`
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
- output({
13546
+ return cmdOk({
13609
13547
  available: true,
13610
13548
  query,
13611
13549
  count: results.length,
13612
13550
  results
13613
- }, raw, results.map((r) => `${r.title}\n${r.url}\n${r.description}`).join("\n\n"));
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
- output({
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
- output({ rendered: out }, raw, out);
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
- output({
13610
+ return cmdOk({
13673
13611
  bar: text,
13674
13612
  percent,
13675
13613
  completed: totalSummaries,
13676
13614
  total: totalPlans
13677
- }, raw, text);
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
- output({
13635
+ return cmdOk({
13698
13636
  rendered,
13699
13637
  done: doneCount,
13700
13638
  in_progress: inProgressCount,
13701
13639
  total: totalCount,
13702
13640
  percent
13703
- }, raw, rendered);
13704
- } else output({
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
- }, raw);
13649
+ });
13712
13650
  }
13713
13651
  function cmdTodoComplete(cwd, filename, raw) {
13714
- if (!filename) error("filename required for todo complete");
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)) error(`Todo not found: ${filename}`);
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
- output({
13663
+ return cmdOk({
13726
13664
  completed: true,
13727
13665
  file: filename,
13728
13666
  date: today
13729
- }, raw, "completed");
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") error(`Phase ${phase} directory not found`);
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) error("phase and name required for phase-dir scaffold");
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
- output({
13698
+ return cmdOk({
13761
13699
  created: true,
13762
13700
  directory: `.planning/phases/${dirName}`,
13763
13701
  path: dirPath
13764
- }, raw, dirPath);
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
- output({
13713
+ return cmdOk({
13782
13714
  created: true,
13783
13715
  path: relPath
13784
- }, raw, relPath);
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, raw) {
13795
- if (!summaryPath) error("summary-path required");
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
- output({
13800
- passed: false,
13801
- checks: {
13802
- summary_exists: false,
13803
- files_created: {
13804
- checked: 0,
13805
- found: 0,
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
- errors: ["SUMMARY.md not found"]
13812
- }, raw, "failed");
13813
- return;
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
- output({
13793
+ return cmdOk({
13865
13794
  passed,
13866
13795
  checks,
13867
13796
  errors
13868
- }, raw, passed ? "passed" : "failed");
13797
+ }, passed ? "passed" : "failed");
13869
13798
  }
13870
- function cmdVerifyPlanStructure(cwd, filePath, raw) {
13871
- if (!filePath) error("file path required");
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
- output({
13875
- error: "File not found",
13876
- path: filePath
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
- output({
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
- }, raw, errors.length === 0 ? "valid" : "invalid");
13853
+ }, errors.length === 0 ? "valid" : "invalid");
13928
13854
  }
13929
- function cmdVerifyPhaseCompleteness(cwd, phase, raw) {
13930
- if (!phase) error("phase required");
13855
+ function cmdVerifyPhaseCompleteness(cwd, phase) {
13856
+ if (!phase) return cmdErr("phase required");
13931
13857
  const phaseInfo = findPhaseInternal(cwd, phase);
13932
- if (!phaseInfo) {
13933
- output({
13934
- error: "Phase not found",
13935
- phase
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
- output({ error: "Cannot read phase directory" }, raw);
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
- output({
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
- }, raw, errors.length === 0 ? "complete" : "incomplete");
13888
+ }, errors.length === 0 ? "complete" : "incomplete");
13967
13889
  }
13968
- function cmdVerifyReferences(cwd, filePath, raw) {
13969
- if (!filePath) error("file path required");
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
- output({
13973
- error: "File not found",
13974
- path: filePath
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
- output({
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
- }, raw, missing.length === 0 ? "valid" : "invalid");
13920
+ }, missing.length === 0 ? "valid" : "invalid");
14002
13921
  }
14003
- async function cmdVerifyCommits(cwd, hashes, raw) {
14004
- if (!hashes || hashes.length === 0) error("At least one commit hash required");
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
- output({
13935
+ return cmdOk({
14017
13936
  all_valid: invalid.length === 0,
14018
13937
  valid,
14019
13938
  invalid,
14020
13939
  total: hashes.length
14021
- }, raw, invalid.length === 0 ? "valid" : "invalid");
13940
+ }, invalid.length === 0 ? "valid" : "invalid");
14022
13941
  }
14023
- function cmdVerifyArtifacts(cwd, planFilePath, raw) {
14024
- if (!planFilePath) error("plan file path required");
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
- output({
14028
- error: "File not found",
14029
- path: planFilePath
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
- output({
14036
- error: "No must_haves.artifacts found in frontmatter",
14037
- path: planFilePath
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
- output({
13982
+ return cmdOk({
14070
13983
  all_passed: passed === results.length,
14071
13984
  passed,
14072
13985
  total: results.length,
14073
13986
  artifacts: results
14074
- }, raw, passed === results.length ? "valid" : "invalid");
13987
+ }, passed === results.length ? "valid" : "invalid");
14075
13988
  }
14076
- function cmdVerifyKeyLinks(cwd, planFilePath, raw) {
14077
- if (!planFilePath) error("plan file path required");
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
- output({
14081
- error: "File not found",
14082
- path: planFilePath
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
- output({
14089
- error: "No must_haves.key_links found in frontmatter",
14090
- path: planFilePath
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
- output({
14036
+ return cmdOk({
14130
14037
  all_verified: verified === results.length,
14131
14038
  verified,
14132
14039
  total: results.length,
14133
14040
  links: results
14134
- }, raw, verified === results.length ? "valid" : "invalid");
14041
+ }, verified === results.length ? "valid" : "invalid");
14135
14042
  }
14136
- function cmdValidateConsistency(cwd, raw) {
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
- output({
14050
+ return cmdOk({
14144
14051
  passed: false,
14145
14052
  errors,
14146
14053
  warnings
14147
- }, raw, "failed");
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
- output({
14106
+ return cmdOk({
14201
14107
  passed,
14202
14108
  errors,
14203
14109
  warnings,
14204
14110
  warning_count: warnings.length
14205
- }, raw, passed ? "passed" : "failed");
14111
+ }, passed ? "passed" : "failed");
14206
14112
  }
14207
- function cmdValidateHealth(cwd, options, raw) {
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
- output({
14137
+ return cmdOk({
14232
14138
  status: "broken",
14233
14139
  errors,
14234
14140
  warnings,
14235
14141
  info,
14236
14142
  repairable_count: 0
14237
- }, raw);
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
- output({
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
- }, raw);
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, raw) {
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
- if (type) output({
14580
- files: [],
14581
- count: 0
14582
- }, raw, "");
14583
- else output({
14584
- directories: [],
14585
- count: 0
14586
- }, raw, "");
14587
- return;
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 = listSubDirs(phasesDirPath);
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
- output({
14601
- files: [],
14602
- count: 0,
14603
- phase_dir: null,
14604
- error: "Phase not found"
14605
- }, raw, "");
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.readdirSync(dirPath);
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
- files.push(...filtered.sort());
14620
- }
14621
- output({
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
- }, raw, files.join("\n"));
14626
- return;
14627
- }
14628
- output({
14629
- directories: dirs,
14630
- count: dirs.length
14631
- }, raw, dirs.join("\n"));
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
- rethrowCliSignals(e);
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, raw) {
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
- output({
14642
- found: false,
14643
- base_phase: normalized,
14644
- next: `${normalized}.1`,
14645
- existing: []
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
- output({
14574
+ return cmdOk({
14668
14575
  found: baseExists,
14669
14576
  base_phase: normalized,
14670
14577
  next: nextDecimal,
14671
14578
  existing: existingDecimals
14672
- }, raw, nextDecimal);
14579
+ }, nextDecimal);
14673
14580
  } catch (e) {
14674
- rethrowCliSignals(e);
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, raw) {
14679
- if (!phase) error("phase identifier required");
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
- output(result, raw, result.directory);
14614
+ return cmdOk(result, result.directory);
14712
14615
  } catch (e) {
14713
- rethrowCliSignals(e);
14714
- output(notFound, raw, "");
14616
+ return cmdOk(notFound, "");
14715
14617
  }
14716
14618
  }
14717
- function cmdPhasePlanIndex(cwd, phase, raw) {
14718
- if (!phase) error("phase required for phase-plan-index");
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
- output({
14674
+ return cmdOk({
14776
14675
  phase: normalized,
14777
14676
  plans,
14778
14677
  waves,
14779
14678
  incomplete,
14780
14679
  has_checkpoints: hasCheckpoints
14781
- }, raw);
14680
+ });
14782
14681
  }
14783
- function cmdPhaseAdd(cwd, description, raw) {
14784
- if (!description) error("description required for phase add");
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
- output({
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
- }, raw, result.padded);
14692
+ }, result.padded);
14794
14693
  } catch (e) {
14795
- rethrowCliSignals(e);
14796
- error(e.message);
14694
+ return cmdErr(e.message);
14797
14695
  }
14798
14696
  }
14799
- function cmdPhaseInsert(cwd, afterPhase, description, raw) {
14800
- if (!afterPhase || !description) error("after-phase and description required for phase insert");
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
- output({
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
- }, raw, result.phase_number);
14707
+ }, result.phase_number);
14810
14708
  } catch (e) {
14811
- rethrowCliSignals(e);
14812
- error(e.message);
14709
+ return cmdErr(e.message);
14813
14710
  }
14814
14711
  }
14815
- function cmdPhaseRemove(cwd, targetPhase, options, raw) {
14816
- if (!targetPhase) error("phase number required for phase remove");
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)) error("ROADMAP.md not found");
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) error(`Phase ${targetPhase} has ${summaries.length} executed plan(s). Use --force to remove anyway.`);
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(e);
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(e);
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
- output({
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
- }, raw);
14880
+ });
14978
14881
  }
14979
- function cmdPhaseComplete(cwd, phaseNum, raw) {
14980
- if (!phaseNum) error("phase number required for phase complete");
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
- output({
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
- }, raw);
14896
+ });
14994
14897
  } catch (e) {
14995
- rethrowCliSignals(e);
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, raw) {
15008
- if (!planPath) error("plan-path required");
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
- output({
14930
+ return cmdOk({
15029
14931
  template,
15030
14932
  type,
15031
14933
  taskCount,
15032
14934
  fileCount,
15033
14935
  hasDecisions
15034
- }, raw, template);
14936
+ }, template);
15035
14937
  } catch (thrown) {
15036
- output({
14938
+ return cmdOk({
15037
14939
  template: "templates/summary-standard.md",
15038
14940
  type: "standard",
15039
14941
  error: thrown.message
15040
- }, raw, "templates/summary-standard.md");
14942
+ }, "templates/summary-standard.md");
15041
14943
  }
15042
14944
  }
15043
- function cmdTemplateFill(cwd, templateType, options, raw) {
15044
- if (!templateType) error("template type required: summary, plan, or verification");
15045
- if (!options.phase) error("--phase required");
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
- output({
15049
- error: "Phase not found",
15050
- phase: options.phase
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
- output({
15207
- error: "File already exists",
15208
- path: node_path.default.relative(cwd, outPath)
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
- output({
15108
+ return cmdOk({
15215
15109
  created: true,
15216
15110
  path: relPath,
15217
15111
  template: templateType
15218
- }, raw, relPath);
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 = 1500;
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
- } catch {}
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
- function cmdInitExecutePhase(cwd, phase, raw) {
15401
- if (!phase) error("phase required for init execute-phase");
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
- output({
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
- }, raw);
15827
+ });
15437
15828
  }
15438
- function cmdInitPlanPhase(cwd, phase, raw) {
15439
- if (!phase) error("phase required for init plan-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
- output(result, raw);
15865
+ return cmdOk(result);
15475
15866
  }
15476
- function cmdInitNewProject(cwd, raw) {
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
- let hasCode = false;
15482
- let hasPackageFile = false;
15483
- try {
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
- }, raw);
15889
+ });
15513
15890
  }
15514
- function cmdInitNewMilestone(cwd, raw) {
15891
+ function cmdInitNewMilestone(cwd) {
15515
15892
  const config = loadConfig(cwd);
15516
15893
  const milestone = getMilestoneInfo(cwd);
15517
- output({
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
- }, raw);
15908
+ });
15532
15909
  }
15533
- function cmdInitQuick(cwd, description, raw) {
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
- output({
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
- }, raw);
15937
+ });
15561
15938
  }
15562
- function cmdInitResume(cwd, raw) {
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
- output({
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
- }, raw);
15958
+ });
15582
15959
  }
15583
- function cmdInitVerifyWork(cwd, phase, raw) {
15584
- if (!phase) error("phase required for init verify-work");
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
- output({
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
- }, raw);
15973
+ });
15597
15974
  }
15598
- function cmdInitPhaseOp(cwd, phase, raw) {
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
- output(result, raw);
16024
+ return cmdOk(result);
15648
16025
  }
15649
- function cmdInitTodos(cwd, area, raw) {
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
- output({
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
- }, raw);
16067
+ });
15691
16068
  }
15692
- function cmdInitMilestoneOp(cwd, raw) {
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
- output({
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
- }, raw);
16108
+ });
15732
16109
  }
15733
- function cmdInitMapCodebase(cwd, raw) {
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
- output({
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
- }, raw);
16129
+ });
15753
16130
  }
15754
- function cmdInitExisting(cwd, raw) {
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
- let hasCode = false;
15760
- let hasPackageFile = false;
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
- output({
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
- }, raw);
16165
+ });
15803
16166
  }
15804
- function cmdInitProgress(cwd, raw) {
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 plans = phaseFiles.filter((f) => isPlanFile(f));
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 >= plans.length && plans.length > 0 ? "complete" : plans.length > 0 ? "in_progress" : hasResearch ? "researched" : "pending";
15823
- const phaseInfo = {
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: plans.length,
16191
+ plan_count: plansList.length,
15829
16192
  summary_count: summaries.length,
15830
16193
  has_research: hasResearch
15831
16194
  };
15832
- phases.push(phaseInfo);
15833
- if (!currentPhase && (status === "in_progress" || status === "researched")) currentPhase = phaseInfo;
15834
- if (!nextPhase && status === "pending") nextPhase = phaseInfo;
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
- output({
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
- }, raw);
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
- const handleState = (args, cwd, raw) => {
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
- }, raw);
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], raw),
16004
- "analyze": () => cmdRoadmapAnalyze(cwd, raw),
16005
- "update-plan-progress": () => cmdRoadmapUpdatePlanProgress(cwd, args[2], raw)
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], raw),
16014
- "add": () => cmdPhaseAdd(cwd, args.slice(2).join(" "), raw),
16015
- "insert": () => cmdPhaseInsert(cwd, args[2], args.slice(3).join(" "), raw),
16016
- "remove": () => cmdPhaseRemove(cwd, args[2], { force: hasFlag(args, "force") }, raw),
16017
- "complete": () => cmdPhaseComplete(cwd, args[2], raw)
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], raw),
16053
- "plan-phase": () => cmdInitPlanPhase(cwd, args[2], raw),
16054
- "new-project": () => cmdInitNewProject(cwd, raw),
16055
- "new-milestone": () => cmdInitNewMilestone(cwd, raw),
16056
- "quick": () => cmdInitQuick(cwd, args.slice(2).join(" "), raw),
16057
- "resume": () => cmdInitResume(cwd, raw),
16058
- "verify-work": () => cmdInitVerifyWork(cwd, args[2], raw),
16059
- "phase-op": () => cmdInitPhaseOp(cwd, args[2], raw),
16060
- "todos": () => cmdInitTodos(cwd, args[2], raw),
16061
- "milestone-op": () => cmdInitMilestoneOp(cwd, raw),
16062
- "map-codebase": () => cmdInitMapCodebase(cwd, raw),
16063
- "init-existing": () => cmdInitExisting(cwd, raw),
16064
- "progress": () => cmdInitProgress(cwd, raw)
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");