maxsimcli 4.0.2 → 4.2.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 (127) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/assets/CHANGELOG.md +16 -0
  3. package/dist/assets/dashboard/client/assets/{index-C_eAetZJ.js → index-BcRHShXD.js} +59 -59
  4. package/dist/assets/dashboard/client/assets/index-C199D4Eb.css +32 -0
  5. package/dist/assets/dashboard/client/index.html +2 -2
  6. package/dist/assets/dashboard/server.js +26 -11
  7. package/dist/assets/templates/agents/AGENTS.md +18 -69
  8. package/dist/assets/templates/agents/maxsim-code-reviewer.md +17 -92
  9. package/dist/assets/templates/agents/maxsim-codebase-mapper.md +57 -694
  10. package/dist/assets/templates/agents/maxsim-debugger.md +80 -925
  11. package/dist/assets/templates/agents/maxsim-executor.md +94 -431
  12. package/dist/assets/templates/agents/maxsim-integration-checker.md +51 -319
  13. package/dist/assets/templates/agents/maxsim-phase-researcher.md +63 -429
  14. package/dist/assets/templates/agents/maxsim-plan-checker.md +79 -568
  15. package/dist/assets/templates/agents/maxsim-planner.md +125 -855
  16. package/dist/assets/templates/agents/maxsim-project-researcher.md +32 -472
  17. package/dist/assets/templates/agents/maxsim-research-synthesizer.md +25 -134
  18. package/dist/assets/templates/agents/maxsim-roadmapper.md +66 -480
  19. package/dist/assets/templates/agents/maxsim-spec-reviewer.md +13 -55
  20. package/dist/assets/templates/agents/maxsim-verifier.md +95 -450
  21. package/dist/assets/templates/commands/maxsim/artefakte.md +122 -0
  22. package/dist/assets/templates/commands/maxsim/batch.md +42 -0
  23. package/dist/assets/templates/commands/maxsim/check-todos.md +1 -0
  24. package/dist/assets/templates/commands/maxsim/sdd.md +39 -0
  25. package/dist/assets/templates/references/thinking-partner.md +33 -0
  26. package/dist/assets/templates/workflows/batch.md +420 -0
  27. package/dist/assets/templates/workflows/check-todos.md +85 -1
  28. package/dist/assets/templates/workflows/discuss-phase.md +31 -0
  29. package/dist/assets/templates/workflows/execute-plan.md +96 -27
  30. package/dist/assets/templates/workflows/help.md +47 -0
  31. package/dist/assets/templates/workflows/sdd.md +426 -0
  32. package/dist/backend/index.d.ts +4 -0
  33. package/dist/backend/index.d.ts.map +1 -0
  34. package/dist/backend/index.js +12 -0
  35. package/dist/backend/index.js.map +1 -0
  36. package/dist/backend/lifecycle.d.ts +13 -0
  37. package/dist/backend/lifecycle.d.ts.map +1 -0
  38. package/dist/backend/lifecycle.js +168 -0
  39. package/dist/backend/lifecycle.js.map +1 -0
  40. package/dist/backend/server.d.ts +13 -0
  41. package/dist/backend/server.d.ts.map +1 -0
  42. package/dist/backend/server.js +1013 -0
  43. package/dist/backend/server.js.map +1 -0
  44. package/dist/backend/terminal.d.ts +49 -0
  45. package/dist/backend/terminal.d.ts.map +1 -0
  46. package/dist/backend/terminal.js +209 -0
  47. package/dist/backend/terminal.js.map +1 -0
  48. package/dist/backend/types.d.ts +77 -0
  49. package/dist/backend/types.d.ts.map +1 -0
  50. package/dist/backend/types.js +6 -0
  51. package/dist/backend/types.js.map +1 -0
  52. package/dist/backend-server.cjs +80636 -0
  53. package/dist/backend-server.cjs.map +1 -0
  54. package/dist/backend-server.d.cts +2 -0
  55. package/dist/backend-server.d.ts +11 -0
  56. package/dist/backend-server.d.ts.map +1 -0
  57. package/dist/backend-server.js +43 -0
  58. package/dist/backend-server.js.map +1 -0
  59. package/dist/cli.cjs +378 -172
  60. package/dist/cli.cjs.map +1 -1
  61. package/dist/cli.js +28 -8
  62. package/dist/cli.js.map +1 -1
  63. package/dist/core/artefakte.d.ts.map +1 -1
  64. package/dist/core/artefakte.js +16 -0
  65. package/dist/core/artefakte.js.map +1 -1
  66. package/dist/core/context-loader.d.ts +1 -0
  67. package/dist/core/context-loader.d.ts.map +1 -1
  68. package/dist/core/context-loader.js +58 -0
  69. package/dist/core/context-loader.js.map +1 -1
  70. package/dist/core/core.d.ts +6 -0
  71. package/dist/core/core.d.ts.map +1 -1
  72. package/dist/core/core.js +238 -0
  73. package/dist/core/core.js.map +1 -1
  74. package/dist/core/index.d.ts +1 -1
  75. package/dist/core/index.d.ts.map +1 -1
  76. package/dist/core/index.js +5 -3
  77. package/dist/core/index.js.map +1 -1
  78. package/dist/core/phase.d.ts +11 -11
  79. package/dist/core/phase.d.ts.map +1 -1
  80. package/dist/core/phase.js +88 -73
  81. package/dist/core/phase.js.map +1 -1
  82. package/dist/core/roadmap.d.ts +2 -2
  83. package/dist/core/roadmap.d.ts.map +1 -1
  84. package/dist/core/roadmap.js +11 -10
  85. package/dist/core/roadmap.js.map +1 -1
  86. package/dist/core/skills.d.ts +4 -3
  87. package/dist/core/skills.d.ts.map +1 -1
  88. package/dist/core/skills.js +14 -15
  89. package/dist/core/skills.js.map +1 -1
  90. package/dist/core/state.d.ts +11 -11
  91. package/dist/core/state.d.ts.map +1 -1
  92. package/dist/core/state.js +60 -54
  93. package/dist/core/state.js.map +1 -1
  94. package/dist/{core-TFSlUjV1.cjs → core-RRjCSt0G.cjs} +1 -9
  95. package/dist/{core-TFSlUjV1.cjs.map → core-RRjCSt0G.cjs.map} +1 -1
  96. package/dist/esm-iIOBzmdz.cjs +1561 -0
  97. package/dist/esm-iIOBzmdz.cjs.map +1 -0
  98. package/dist/install.cjs +2 -2
  99. package/dist/lifecycle-0M4VqOMm.cjs +136 -0
  100. package/dist/lifecycle-0M4VqOMm.cjs.map +1 -0
  101. package/dist/mcp/config-tools.d.ts +13 -0
  102. package/dist/mcp/config-tools.d.ts.map +1 -0
  103. package/dist/mcp/config-tools.js +66 -0
  104. package/dist/mcp/config-tools.js.map +1 -0
  105. package/dist/mcp/context-tools.d.ts +13 -0
  106. package/dist/mcp/context-tools.d.ts.map +1 -0
  107. package/dist/mcp/context-tools.js +176 -0
  108. package/dist/mcp/context-tools.js.map +1 -0
  109. package/dist/mcp/index.d.ts +0 -1
  110. package/dist/mcp/index.d.ts.map +1 -1
  111. package/dist/mcp/index.js +6 -1
  112. package/dist/mcp/index.js.map +1 -1
  113. package/dist/mcp/phase-tools.js +3 -3
  114. package/dist/mcp/phase-tools.js.map +1 -1
  115. package/dist/mcp/roadmap-tools.d.ts +13 -0
  116. package/dist/mcp/roadmap-tools.d.ts.map +1 -0
  117. package/dist/mcp/roadmap-tools.js +79 -0
  118. package/dist/mcp/roadmap-tools.js.map +1 -0
  119. package/dist/mcp-server.cjs +799 -38
  120. package/dist/mcp-server.cjs.map +1 -1
  121. package/dist/server-G1MIg_Oe.cjs +2980 -0
  122. package/dist/server-G1MIg_Oe.cjs.map +1 -0
  123. package/dist/{skills-BOSxYUzf.cjs → skills-MYlMkYNt.cjs} +41 -29
  124. package/dist/skills-MYlMkYNt.cjs.map +1 -0
  125. package/package.json +7 -1
  126. package/dist/assets/dashboard/client/assets/index-CmiJKqOU.css +0 -32
  127. package/dist/skills-BOSxYUzf.cjs.map +0 -1
package/dist/cli.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
3
  //#region \0rolldown/runtime.js
3
4
  var __create = Object.create;
4
5
  var __defProp$1 = Object.defineProperty;
@@ -606,7 +607,7 @@ var require_has_flag = /* @__PURE__ */ __commonJSMin(((exports, module) => {
606
607
  //#endregion
607
608
  //#region ../../node_modules/supports-color/index.js
608
609
  var require_supports_color = /* @__PURE__ */ __commonJSMin(((exports, module) => {
609
- const os$5 = require("os");
610
+ const os$6 = require("os");
610
611
  const tty$2 = require("tty");
611
612
  const hasFlag = require_has_flag();
612
613
  const { env } = process;
@@ -633,7 +634,7 @@ var require_supports_color = /* @__PURE__ */ __commonJSMin(((exports, module) =>
633
634
  const min = forceColor || 0;
634
635
  if (env.TERM === "dumb") return min;
635
636
  if (process.platform === "win32") {
636
- const osRelease = os$5.release().split(".");
637
+ const osRelease = os$6.release().split(".");
637
638
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) return Number(osRelease[2]) >= 14931 ? 3 : 2;
638
639
  return 1;
639
640
  }
@@ -5152,6 +5153,100 @@ function getMilestoneInfo(cwd) {
5152
5153
  };
5153
5154
  }
5154
5155
  }
5156
+ async function pathExistsAsync(p) {
5157
+ try {
5158
+ await node_fs.promises.access(p);
5159
+ return true;
5160
+ } catch {
5161
+ return false;
5162
+ }
5163
+ }
5164
+ async function searchPhaseInDirAsync(baseDir, relBase, normalized) {
5165
+ try {
5166
+ const match = (await listSubDirsAsync(baseDir, true)).find((d) => d.startsWith(normalized));
5167
+ if (!match) return null;
5168
+ const dirMatch = match.match(/^(\d+[A-Z]?(?:\.\d+)?)-?(.*)/i);
5169
+ const phaseNumber = dirMatch ? dirMatch[1] : normalized;
5170
+ const phaseName = dirMatch && dirMatch[2] ? dirMatch[2] : null;
5171
+ const phaseDir = node_path.default.join(baseDir, match);
5172
+ const phaseFiles = await node_fs.promises.readdir(phaseDir);
5173
+ const plans = phaseFiles.filter(isPlanFile).sort();
5174
+ const summaries = phaseFiles.filter(isSummaryFile).sort();
5175
+ const hasResearch = phaseFiles.some((f) => f.endsWith("-RESEARCH.md") || f === "RESEARCH.md");
5176
+ const hasContext = phaseFiles.some((f) => f.endsWith("-CONTEXT.md") || f === "CONTEXT.md");
5177
+ const hasVerification = phaseFiles.some((f) => f.endsWith("-VERIFICATION.md") || f === "VERIFICATION.md");
5178
+ const completedPlanIds = new Set(summaries.map(summaryId));
5179
+ const incompletePlans = plans.filter((p) => !completedPlanIds.has(planId(p)));
5180
+ return {
5181
+ found: true,
5182
+ directory: node_path.default.join(relBase, match),
5183
+ phase_number: phaseNumber,
5184
+ phase_name: phaseName,
5185
+ phase_slug: phaseName ? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") : null,
5186
+ plans,
5187
+ summaries,
5188
+ incomplete_plans: incompletePlans,
5189
+ has_research: hasResearch,
5190
+ has_context: hasContext,
5191
+ has_verification: hasVerification
5192
+ };
5193
+ } catch (e) {
5194
+ debugLog("search-phase-in-dir-async-failed", {
5195
+ dir: baseDir,
5196
+ phase: normalized,
5197
+ error: errorMsg(e)
5198
+ });
5199
+ return null;
5200
+ }
5201
+ }
5202
+ async function findPhaseInternalAsync(cwd, phase) {
5203
+ if (!phase) return null;
5204
+ const pd = phasesPath(cwd);
5205
+ const normalized = normalizePhaseName(phase);
5206
+ const current = await searchPhaseInDirAsync(pd, node_path.default.join(".planning", "phases"), normalized);
5207
+ if (current) return current;
5208
+ const milestonesDir = planningPath(cwd, "milestones");
5209
+ if (!await pathExistsAsync(milestonesDir)) return null;
5210
+ try {
5211
+ const archiveDirs = (await node_fs.promises.readdir(milestonesDir, { withFileTypes: true })).filter((e) => e.isDirectory() && /^v[\d.]+-phases$/.test(e.name)).map((e) => e.name).sort().reverse();
5212
+ for (const archiveName of archiveDirs) {
5213
+ const versionMatch = archiveName.match(/^(v[\d.]+)-phases$/);
5214
+ if (!versionMatch) continue;
5215
+ const version = versionMatch[1];
5216
+ const result = await searchPhaseInDirAsync(node_path.default.join(milestonesDir, archiveName), node_path.default.join(".planning", "milestones", archiveName), normalized);
5217
+ if (result) {
5218
+ result.archived = version;
5219
+ return result;
5220
+ }
5221
+ }
5222
+ } catch (e) {
5223
+ debugLog("find-phase-async-milestone-search-failed", e);
5224
+ }
5225
+ return null;
5226
+ }
5227
+ async function getArchivedPhaseDirsAsync(cwd) {
5228
+ const milestonesDir = planningPath(cwd, "milestones");
5229
+ const results = [];
5230
+ try {
5231
+ const phaseDirs = (await node_fs.promises.readdir(milestonesDir, { withFileTypes: true })).filter((e) => e.isDirectory() && /^v[\d.]+-phases$/.test(e.name)).map((e) => e.name).sort().reverse();
5232
+ for (const archiveName of phaseDirs) {
5233
+ const versionMatch = archiveName.match(/^(v[\d.]+)-phases$/);
5234
+ if (!versionMatch) continue;
5235
+ const version = versionMatch[1];
5236
+ const archivePath = node_path.default.join(milestonesDir, archiveName);
5237
+ const dirs = await listSubDirsAsync(archivePath, true);
5238
+ for (const dir of dirs) results.push({
5239
+ name: dir,
5240
+ milestone: version,
5241
+ basePath: node_path.default.join(".planning", "milestones", archiveName),
5242
+ fullPath: node_path.default.join(archivePath, dir)
5243
+ });
5244
+ }
5245
+ } catch (e) {
5246
+ debugLog("get-archived-phase-dirs-async-failed", e);
5247
+ }
5248
+ return results;
5249
+ }
5155
5250
 
5156
5251
  //#endregion
5157
5252
  //#region ../../node_modules/yaml/dist/nodes/identity.js
@@ -12107,11 +12202,11 @@ function stateReplaceField(content, fieldName, newValue) {
12107
12202
  replaced = content.replace(plainPattern, (_match, prefix) => `${prefix}${newValue}`);
12108
12203
  return replaced !== content ? replaced : null;
12109
12204
  }
12110
- function readTextArgOrFile(cwd, value, filePath, label) {
12205
+ async function readTextArgOrFile(cwd, value, filePath, label) {
12111
12206
  if (!filePath) return value;
12112
12207
  const resolvedPath = node_path.default.isAbsolute(filePath) ? filePath : node_path.default.join(cwd, filePath);
12113
12208
  try {
12114
- return node_fs.default.readFileSync(resolvedPath, "utf-8").trimEnd();
12209
+ return (await node_fs.promises.readFile(resolvedPath, "utf-8")).trimEnd();
12115
12210
  } catch {
12116
12211
  throw new Error(`${label} file not found: ${filePath}`);
12117
12212
  }
@@ -12136,8 +12231,8 @@ async function cmdStateLoad(cwd, raw) {
12136
12231
  const config = loadConfig(cwd);
12137
12232
  const [stateContent, configExists, roadmapExists] = await Promise.all([
12138
12233
  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)
12234
+ pathExistsAsync(configPath(cwd)),
12235
+ pathExistsAsync(roadmapPath(cwd))
12141
12236
  ]);
12142
12237
  const stateRaw = stateContent ?? "";
12143
12238
  const stateExists = stateRaw.length > 0;
@@ -12167,10 +12262,10 @@ async function cmdStateLoad(cwd, raw) {
12167
12262
  }
12168
12263
  return cmdOk(result);
12169
12264
  }
12170
- function cmdStateGet(cwd, section, raw) {
12265
+ async function cmdStateGet(cwd, section, raw) {
12171
12266
  const statePath$2 = statePath(cwd);
12172
12267
  try {
12173
- const content = node_fs.default.readFileSync(statePath$2, "utf-8");
12268
+ const content = await node_fs.promises.readFile(statePath$2, "utf-8");
12174
12269
  if (!section) return cmdOk({ content }, raw ? content : void 0);
12175
12270
  const fieldValue = stateExtractField(content, section);
12176
12271
  if (fieldValue !== null) return cmdOk({ [section]: fieldValue }, raw ? fieldValue : void 0);
@@ -12184,10 +12279,10 @@ function cmdStateGet(cwd, section, raw) {
12184
12279
  return cmdErr("STATE.md not found");
12185
12280
  }
12186
12281
  }
12187
- function cmdStatePatch(cwd, patches, raw) {
12282
+ async function cmdStatePatch(cwd, patches, raw) {
12188
12283
  const statePath$3 = statePath(cwd);
12189
12284
  try {
12190
- let content = node_fs.default.readFileSync(statePath$3, "utf-8");
12285
+ let content = await node_fs.promises.readFile(statePath$3, "utf-8");
12191
12286
  const results = {
12192
12287
  updated: [],
12193
12288
  failed: []
@@ -12199,20 +12294,20 @@ function cmdStatePatch(cwd, patches, raw) {
12199
12294
  results.updated.push(field);
12200
12295
  } else results.failed.push(field);
12201
12296
  }
12202
- if (results.updated.length > 0) node_fs.default.writeFileSync(statePath$3, content, "utf-8");
12297
+ if (results.updated.length > 0) await node_fs.promises.writeFile(statePath$3, content, "utf-8");
12203
12298
  return cmdOk(results, raw ? results.updated.length > 0 ? "true" : "false" : void 0);
12204
12299
  } catch (e) {
12205
12300
  rethrowCliSignals(e);
12206
12301
  return cmdErr("STATE.md not found");
12207
12302
  }
12208
12303
  }
12209
- function cmdStateUpdate(cwd, field, value) {
12304
+ async function cmdStateUpdate(cwd, field, value) {
12210
12305
  if (!field || value === void 0) return cmdErr("field and value required for state update");
12211
12306
  const statePath$4 = statePath(cwd);
12212
12307
  try {
12213
- const result = stateReplaceField(node_fs.default.readFileSync(statePath$4, "utf-8"), field, value);
12308
+ const result = stateReplaceField(await node_fs.promises.readFile(statePath$4, "utf-8"), field, value);
12214
12309
  if (result) {
12215
- node_fs.default.writeFileSync(statePath$4, result, "utf-8");
12310
+ await node_fs.promises.writeFile(statePath$4, result, "utf-8");
12216
12311
  return cmdOk({ updated: true });
12217
12312
  } else return cmdOk({
12218
12313
  updated: false,
@@ -12226,10 +12321,10 @@ function cmdStateUpdate(cwd, field, value) {
12226
12321
  });
12227
12322
  }
12228
12323
  }
12229
- function cmdStateAdvancePlan(cwd, raw) {
12324
+ async function cmdStateAdvancePlan(cwd, raw) {
12230
12325
  const statePath$5 = statePath(cwd);
12231
- if (!node_fs.default.existsSync(statePath$5)) return cmdOk({ error: "STATE.md not found" });
12232
- let content = node_fs.default.readFileSync(statePath$5, "utf-8");
12326
+ if (!await pathExistsAsync(statePath$5)) return cmdOk({ error: "STATE.md not found" });
12327
+ let content = await node_fs.promises.readFile(statePath$5, "utf-8");
12233
12328
  const currentPlan = parseInt(stateExtractField(content, "Current Plan") ?? "", 10);
12234
12329
  const totalPlans = parseInt(stateExtractField(content, "Total Plans in Phase") ?? "", 10);
12235
12330
  const today = todayISO();
@@ -12237,7 +12332,7 @@ function cmdStateAdvancePlan(cwd, raw) {
12237
12332
  if (currentPlan >= totalPlans) {
12238
12333
  content = stateReplaceField(content, "Status", "Phase complete — ready for verification") || content;
12239
12334
  content = stateReplaceField(content, "Last Activity", today) || content;
12240
- node_fs.default.writeFileSync(statePath$5, content, "utf-8");
12335
+ await node_fs.promises.writeFile(statePath$5, content, "utf-8");
12241
12336
  return cmdOk({
12242
12337
  advanced: false,
12243
12338
  reason: "last_plan",
@@ -12250,7 +12345,7 @@ function cmdStateAdvancePlan(cwd, raw) {
12250
12345
  content = stateReplaceField(content, "Current Plan", String(newPlan)) || content;
12251
12346
  content = stateReplaceField(content, "Status", "Ready to execute") || content;
12252
12347
  content = stateReplaceField(content, "Last Activity", today) || content;
12253
- node_fs.default.writeFileSync(statePath$5, content, "utf-8");
12348
+ await node_fs.promises.writeFile(statePath$5, content, "utf-8");
12254
12349
  return cmdOk({
12255
12350
  advanced: true,
12256
12351
  previous_plan: currentPlan,
@@ -12259,10 +12354,10 @@ function cmdStateAdvancePlan(cwd, raw) {
12259
12354
  }, raw ? "true" : void 0);
12260
12355
  }
12261
12356
  }
12262
- function cmdStateRecordMetric(cwd, options, raw) {
12357
+ async function cmdStateRecordMetric(cwd, options, raw) {
12263
12358
  const statePath$6 = statePath(cwd);
12264
- if (!node_fs.default.existsSync(statePath$6)) return cmdOk({ error: "STATE.md not found" });
12265
- let content = node_fs.default.readFileSync(statePath$6, "utf-8");
12359
+ if (!await pathExistsAsync(statePath$6)) return cmdOk({ error: "STATE.md not found" });
12360
+ let content = await node_fs.promises.readFile(statePath$6, "utf-8");
12266
12361
  const { phase, plan, duration, tasks, files } = options;
12267
12362
  if (!phase || !plan || !duration) return cmdOk({ error: "phase, plan, and duration required" });
12268
12363
  const metricsPattern = /(#{2,3}\s*Performance Metrics[\s\S]*?\n\|[^\n]+\n\|[\s:|\-]+\n)([\s\S]*?)(?=\n#{2,3}\s|\n$|$)/i;
@@ -12273,7 +12368,7 @@ function cmdStateRecordMetric(cwd, options, raw) {
12273
12368
  if (tableBody.trim() === "" || tableBody.includes("None yet")) tableBody = newRow;
12274
12369
  else tableBody = tableBody + "\n" + newRow;
12275
12370
  content = content.replace(metricsPattern, (_match, header) => `${header}${tableBody}\n`);
12276
- node_fs.default.writeFileSync(statePath$6, content, "utf-8");
12371
+ await node_fs.promises.writeFile(statePath$6, content, "utf-8");
12277
12372
  return cmdOk({
12278
12373
  recorded: true,
12279
12374
  phase,
@@ -12285,19 +12380,25 @@ function cmdStateRecordMetric(cwd, options, raw) {
12285
12380
  reason: "Performance Metrics section not found in STATE.md"
12286
12381
  }, raw ? "false" : void 0);
12287
12382
  }
12288
- function cmdStateUpdateProgress(cwd, raw) {
12383
+ async function cmdStateUpdateProgress(cwd, raw) {
12289
12384
  const statePath$7 = statePath(cwd);
12290
- if (!node_fs.default.existsSync(statePath$7)) return cmdOk({ error: "STATE.md not found" });
12291
- let content = node_fs.default.readFileSync(statePath$7, "utf-8");
12385
+ if (!await pathExistsAsync(statePath$7)) return cmdOk({ error: "STATE.md not found" });
12386
+ let content = await node_fs.promises.readFile(statePath$7, "utf-8");
12292
12387
  const phasesDir = phasesPath(cwd);
12293
12388
  let totalPlans = 0;
12294
12389
  let totalSummaries = 0;
12295
- if (node_fs.default.existsSync(phasesDir)) {
12296
- const phaseDirs = node_fs.default.readdirSync(phasesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
12297
- for (const dir of phaseDirs) {
12298
- const files = node_fs.default.readdirSync(node_path.default.join(phasesDir, dir));
12299
- totalPlans += files.filter((f) => isPlanFile(f)).length;
12300
- totalSummaries += files.filter((f) => isSummaryFile(f)).length;
12390
+ if (await pathExistsAsync(phasesDir)) {
12391
+ const phaseDirs = (await node_fs.promises.readdir(phasesDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
12392
+ const counts = await Promise.all(phaseDirs.map(async (dir) => {
12393
+ const files = await node_fs.promises.readdir(node_path.default.join(phasesDir, dir));
12394
+ return {
12395
+ plans: files.filter((f) => isPlanFile(f)).length,
12396
+ summaries: files.filter((f) => isSummaryFile(f)).length
12397
+ };
12398
+ }));
12399
+ for (const c of counts) {
12400
+ totalPlans += c.plans;
12401
+ totalSummaries += c.summaries;
12301
12402
  }
12302
12403
  }
12303
12404
  const percent = totalPlans > 0 ? Math.min(100, Math.round(totalSummaries / totalPlans * 100)) : 0;
@@ -12306,7 +12407,7 @@ function cmdStateUpdateProgress(cwd, raw) {
12306
12407
  const progressStr = `[${"█".repeat(filled) + "░".repeat(barWidth - filled)}] ${percent}%`;
12307
12408
  const result = stateReplaceField(content, "Progress", progressStr);
12308
12409
  if (result) {
12309
- node_fs.default.writeFileSync(statePath$7, result, "utf-8");
12410
+ await node_fs.promises.writeFile(statePath$7, result, "utf-8");
12310
12411
  return cmdOk({
12311
12412
  updated: true,
12312
12413
  percent,
@@ -12319,15 +12420,15 @@ function cmdStateUpdateProgress(cwd, raw) {
12319
12420
  reason: "Progress field not found in STATE.md"
12320
12421
  }, raw ? "false" : void 0);
12321
12422
  }
12322
- function cmdStateAddDecision(cwd, options, raw) {
12423
+ async function cmdStateAddDecision(cwd, options, raw) {
12323
12424
  const statePath$8 = statePath(cwd);
12324
- if (!node_fs.default.existsSync(statePath$8)) return cmdOk({ error: "STATE.md not found" });
12425
+ if (!await pathExistsAsync(statePath$8)) return cmdOk({ error: "STATE.md not found" });
12325
12426
  const { phase, summary, summary_file, rationale, rationale_file } = options;
12326
12427
  let summaryText;
12327
12428
  let rationaleText = "";
12328
12429
  try {
12329
- summaryText = readTextArgOrFile(cwd, summary, summary_file, "summary");
12330
- rationaleText = readTextArgOrFile(cwd, rationale || "", rationale_file, "rationale") || "";
12430
+ summaryText = await readTextArgOrFile(cwd, summary, summary_file, "summary");
12431
+ rationaleText = await readTextArgOrFile(cwd, rationale || "", rationale_file, "rationale") || "";
12331
12432
  } catch (thrown) {
12332
12433
  return cmdOk({
12333
12434
  added: false,
@@ -12335,11 +12436,11 @@ function cmdStateAddDecision(cwd, options, raw) {
12335
12436
  }, raw ? "false" : void 0);
12336
12437
  }
12337
12438
  if (!summaryText) return cmdOk({ error: "summary required" });
12338
- const content = node_fs.default.readFileSync(statePath$8, "utf-8");
12439
+ const content = await node_fs.promises.readFile(statePath$8, "utf-8");
12339
12440
  const entry = `- [Phase ${phase || "?"}]: ${summaryText}${rationaleText ? ` — ${rationaleText}` : ""}`;
12340
12441
  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]);
12341
12442
  if (updated) {
12342
- node_fs.default.writeFileSync(statePath$8, updated, "utf-8");
12443
+ await node_fs.promises.writeFile(statePath$8, updated, "utf-8");
12343
12444
  return cmdOk({
12344
12445
  added: true,
12345
12446
  decision: entry
@@ -12349,13 +12450,13 @@ function cmdStateAddDecision(cwd, options, raw) {
12349
12450
  reason: "Decisions section not found in STATE.md"
12350
12451
  }, raw ? "false" : void 0);
12351
12452
  }
12352
- function cmdStateAddBlocker(cwd, text, raw) {
12453
+ async function cmdStateAddBlocker(cwd, text, raw) {
12353
12454
  const statePath$9 = statePath(cwd);
12354
- if (!node_fs.default.existsSync(statePath$9)) return cmdOk({ error: "STATE.md not found" });
12455
+ if (!await pathExistsAsync(statePath$9)) return cmdOk({ error: "STATE.md not found" });
12355
12456
  const blockerOptions = typeof text === "object" && text !== null ? text : { text };
12356
12457
  let blockerText;
12357
12458
  try {
12358
- blockerText = readTextArgOrFile(cwd, blockerOptions.text, blockerOptions.text_file, "blocker");
12459
+ blockerText = await readTextArgOrFile(cwd, blockerOptions.text, blockerOptions.text_file, "blocker");
12359
12460
  } catch (thrown) {
12360
12461
  return cmdOk({
12361
12462
  added: false,
@@ -12363,9 +12464,9 @@ function cmdStateAddBlocker(cwd, text, raw) {
12363
12464
  }, raw ? "false" : void 0);
12364
12465
  }
12365
12466
  if (!blockerText) return cmdOk({ error: "text required" });
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]);
12467
+ const updated = appendToStateSection(await node_fs.promises.readFile(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]);
12367
12468
  if (updated) {
12368
- node_fs.default.writeFileSync(statePath$9, updated, "utf-8");
12469
+ await node_fs.promises.writeFile(statePath$9, updated, "utf-8");
12369
12470
  return cmdOk({
12370
12471
  added: true,
12371
12472
  blocker: blockerText
@@ -12375,11 +12476,11 @@ function cmdStateAddBlocker(cwd, text, raw) {
12375
12476
  reason: "Blockers section not found in STATE.md"
12376
12477
  }, raw ? "false" : void 0);
12377
12478
  }
12378
- function cmdStateResolveBlocker(cwd, text, raw) {
12479
+ async function cmdStateResolveBlocker(cwd, text, raw) {
12379
12480
  const statePath$10 = statePath(cwd);
12380
- if (!node_fs.default.existsSync(statePath$10)) return cmdOk({ error: "STATE.md not found" });
12481
+ if (!await pathExistsAsync(statePath$10)) return cmdOk({ error: "STATE.md not found" });
12381
12482
  if (!text) return cmdOk({ error: "text required" });
12382
- let content = node_fs.default.readFileSync(statePath$10, "utf-8");
12483
+ let content = await node_fs.promises.readFile(statePath$10, "utf-8");
12383
12484
  const sectionPattern = /(#{2,3}\s*(?:Blockers|Blockers\/Concerns|Concerns)\s*\n\s*\n?)([\s\S]*?)(?=\n#{2,3}\s|$)/i;
12384
12485
  const match = content.match(sectionPattern);
12385
12486
  if (match) {
@@ -12389,7 +12490,7 @@ function cmdStateResolveBlocker(cwd, text, raw) {
12389
12490
  }).join("\n");
12390
12491
  if (!newBody.trim() || !/^\s*[-*]\s+/m.test(newBody)) newBody = "None\n";
12391
12492
  content = content.replace(sectionPattern, (_match, header) => `${header}${newBody}`);
12392
- node_fs.default.writeFileSync(statePath$10, content, "utf-8");
12493
+ await node_fs.promises.writeFile(statePath$10, content, "utf-8");
12393
12494
  return cmdOk({
12394
12495
  resolved: true,
12395
12496
  blocker: text
@@ -12399,10 +12500,10 @@ function cmdStateResolveBlocker(cwd, text, raw) {
12399
12500
  reason: "Blockers section not found in STATE.md"
12400
12501
  }, raw ? "false" : void 0);
12401
12502
  }
12402
- function cmdStateRecordSession(cwd, options, raw) {
12503
+ async function cmdStateRecordSession(cwd, options, raw) {
12403
12504
  const statePath$11 = statePath(cwd);
12404
- if (!node_fs.default.existsSync(statePath$11)) return cmdOk({ error: "STATE.md not found" });
12405
- let content = node_fs.default.readFileSync(statePath$11, "utf-8");
12505
+ if (!await pathExistsAsync(statePath$11)) return cmdOk({ error: "STATE.md not found" });
12506
+ let content = await node_fs.promises.readFile(statePath$11, "utf-8");
12406
12507
  const now = (/* @__PURE__ */ new Date()).toISOString();
12407
12508
  const updated = [];
12408
12509
  let result = stateReplaceField(content, "Last session", now);
@@ -12431,7 +12532,7 @@ function cmdStateRecordSession(cwd, options, raw) {
12431
12532
  updated.push("Resume File");
12432
12533
  }
12433
12534
  if (updated.length > 0) {
12434
- node_fs.default.writeFileSync(statePath$11, content, "utf-8");
12535
+ await node_fs.promises.writeFile(statePath$11, content, "utf-8");
12435
12536
  return cmdOk({
12436
12537
  recorded: true,
12437
12538
  updated
@@ -12441,10 +12542,10 @@ function cmdStateRecordSession(cwd, options, raw) {
12441
12542
  reason: "No session fields found in STATE.md"
12442
12543
  }, raw ? "false" : void 0);
12443
12544
  }
12444
- function cmdStateSnapshot(cwd, raw) {
12545
+ async function cmdStateSnapshot(cwd, raw) {
12445
12546
  const statePath$12 = statePath(cwd);
12446
- if (!node_fs.default.existsSync(statePath$12)) return cmdOk({ error: "STATE.md not found" });
12447
- const content = node_fs.default.readFileSync(statePath$12, "utf-8");
12547
+ if (!await pathExistsAsync(statePath$12)) return cmdOk({ error: "STATE.md not found" });
12548
+ const content = await node_fs.promises.readFile(statePath$12, "utf-8");
12448
12549
  const extractField = (fieldName) => stateExtractField(content, fieldName);
12449
12550
  const currentPhase = extractField("Current Phase");
12450
12551
  const currentPhaseName = extractField("Current Phase Name");
@@ -12515,14 +12616,13 @@ function cmdStateSnapshot(cwd, raw) {
12515
12616
  *
12516
12617
  * Ported from maxsim/bin/lib/roadmap.cjs
12517
12618
  */
12518
- function cmdRoadmapGetPhase(cwd, phaseNum) {
12519
- const rmPath = roadmapPath(cwd);
12520
- if (!node_fs.default.existsSync(rmPath)) return cmdOk({
12619
+ async function cmdRoadmapGetPhase(cwd, phaseNum) {
12620
+ const content = await safeReadFileAsync(roadmapPath(cwd));
12621
+ if (!content) return cmdOk({
12521
12622
  found: false,
12522
12623
  error: "ROADMAP.md not found"
12523
12624
  }, "");
12524
12625
  try {
12525
- const content = node_fs.default.readFileSync(rmPath, "utf-8");
12526
12626
  const escapedPhase = phaseNum.replace(/\./g, "\\.");
12527
12627
  const phasePattern = getPhasePattern(escapedPhase, "i");
12528
12628
  const headerMatch = content.match(phasePattern);
@@ -12606,7 +12706,7 @@ async function cmdRoadmapAnalyze(cwd) {
12606
12706
  try {
12607
12707
  const dirMatch = allDirs.find((d) => d.startsWith(p.normalized + "-") || d === p.normalized);
12608
12708
  if (dirMatch) {
12609
- const phaseFiles = await node_fs.default.promises.readdir(node_path.default.join(phasesDir, dirMatch));
12709
+ const phaseFiles = await node_fs.promises.readdir(node_path.default.join(phasesDir, dirMatch));
12610
12710
  planCount = phaseFiles.filter((f) => isPlanFile(f)).length;
12611
12711
  summaryCount = phaseFiles.filter((f) => isSummaryFile(f)).length;
12612
12712
  hasContext = phaseFiles.some((f) => f.endsWith("-CONTEXT.md") || f === "CONTEXT.md");
@@ -12667,10 +12767,10 @@ async function cmdRoadmapAnalyze(cwd) {
12667
12767
  missing_phase_details: missingDetails.length > 0 ? missingDetails : null
12668
12768
  });
12669
12769
  }
12670
- function cmdRoadmapUpdatePlanProgress(cwd, phaseNum) {
12770
+ async function cmdRoadmapUpdatePlanProgress(cwd, phaseNum) {
12671
12771
  if (!phaseNum) return cmdErr("phase number required for roadmap update-plan-progress");
12672
12772
  const rmPath = roadmapPath(cwd);
12673
- const phaseInfo = findPhaseInternal(cwd, phaseNum);
12773
+ const phaseInfo = await findPhaseInternalAsync(cwd, phaseNum);
12674
12774
  if (!phaseInfo) return cmdErr(`Phase ${phaseNum} not found`);
12675
12775
  const planCount = phaseInfo.plans.length;
12676
12776
  const summaryCount = phaseInfo.summaries.length;
@@ -12683,20 +12783,21 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum) {
12683
12783
  const isComplete = summaryCount >= planCount;
12684
12784
  const status = isComplete ? "Complete" : summaryCount > 0 ? "In Progress" : "Planned";
12685
12785
  const today = todayISO();
12686
- if (!node_fs.default.existsSync(rmPath)) return cmdOk({
12786
+ const rawContent = await safeReadFileAsync(rmPath);
12787
+ if (!rawContent) return cmdOk({
12687
12788
  updated: false,
12688
12789
  reason: "ROADMAP.md not found",
12689
12790
  plan_count: planCount,
12690
12791
  summary_count: summaryCount
12691
12792
  }, "no roadmap");
12692
- let roadmapContent = node_fs.default.readFileSync(rmPath, "utf-8");
12793
+ let roadmapContent = rawContent;
12693
12794
  const phaseEscaped = phaseNum.replace(".", "\\.");
12694
12795
  const dateField = isComplete ? ` ${today} ` : " ";
12695
12796
  roadmapContent = roadmapContent.replace(new RegExp(`(\\|\\s*${phaseEscaped}\\.?\\s[^|]*\\|)[^|]*(\\|)\\s*[^|]*(\\|)\\s*[^|]*(\\|)`, "i"), `$1 ${summaryCount}/${planCount} $2 ${status.padEnd(11)}$3${dateField}$4`);
12696
12797
  const planCountText = isComplete ? `${summaryCount}/${planCount} plans complete` : `${summaryCount}/${planCount} plans executed`;
12697
12798
  roadmapContent = roadmapContent.replace(new RegExp(`(#{2,4}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`, "i"), `$1${planCountText}`);
12698
12799
  if (isComplete) roadmapContent = roadmapContent.replace(new RegExp(`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${phaseEscaped}[:\\s][^\\n]*)`, "i"), `$1x$2 (completed ${today})`);
12699
- node_fs.default.writeFileSync(rmPath, roadmapContent, "utf-8");
12800
+ await node_fs.promises.writeFile(rmPath, roadmapContent, "utf-8");
12700
12801
  return cmdOk({
12701
12802
  updated: true,
12702
12803
  phase: phaseNum,
@@ -14317,15 +14418,18 @@ function cmdValidateHealth(cwd, options) {
14317
14418
  *
14318
14419
  * Ported from maxsim/bin/lib/phase.cjs
14319
14420
  */
14320
- function scaffoldPhaseStubs(dirPath, phaseId, name) {
14421
+ async function scaffoldPhaseStubs(dirPath, phaseId, name) {
14321
14422
  const today = todayISO();
14322
- node_fs.default.writeFileSync(node_path.default.join(dirPath, `${phaseId}-CONTEXT.md`), `# Phase ${phaseId} Context: ${name}\n\n**Created:** ${today}\n**Phase goal:** [To be defined during /maxsim:discuss-phase]\n\n---\n\n_Context will be populated by /maxsim:discuss-phase_\n`);
14323
- node_fs.default.writeFileSync(node_path.default.join(dirPath, `${phaseId}-RESEARCH.md`), `# Phase ${phaseId}: ${name} - Research\n\n**Researched:** Not yet\n**Domain:** TBD\n**Confidence:** TBD\n\n---\n\n_Research will be populated by /maxsim:research-phase_\n`);
14423
+ await Promise.all([node_fs.promises.writeFile(node_path.default.join(dirPath, `${phaseId}-CONTEXT.md`), `# Phase ${phaseId} Context: ${name}\n\n**Created:** ${today}\n**Phase goal:** [To be defined during /maxsim:discuss-phase]\n\n---\n\n_Context will be populated by /maxsim:discuss-phase_\n`), node_fs.promises.writeFile(node_path.default.join(dirPath, `${phaseId}-RESEARCH.md`), `# Phase ${phaseId}: ${name} - Research\n\n**Researched:** Not yet\n**Domain:** TBD\n**Confidence:** TBD\n\n---\n\n_Research will be populated by /maxsim:research-phase_\n`)]);
14324
14424
  }
14325
- function phaseAddCore(cwd, description, options) {
14425
+ async function phaseAddCore(cwd, description, options) {
14326
14426
  const rmPath = roadmapPath(cwd);
14327
- if (!node_fs.default.existsSync(rmPath)) throw new Error("ROADMAP.md not found");
14328
- const content = node_fs.default.readFileSync(rmPath, "utf-8");
14427
+ let content;
14428
+ try {
14429
+ content = await node_fs.promises.readFile(rmPath, "utf-8");
14430
+ } catch {
14431
+ throw new Error("ROADMAP.md not found");
14432
+ }
14329
14433
  const slug = generateSlugInternal(description);
14330
14434
  const phasePattern = getPhasePattern();
14331
14435
  let maxPhase = 0;
@@ -14338,15 +14442,15 @@ function phaseAddCore(cwd, description, options) {
14338
14442
  const paddedNum = String(newPhaseNum).padStart(2, "0");
14339
14443
  const dirName = `${paddedNum}-${slug}`;
14340
14444
  const dirPath = planningPath(cwd, "phases", dirName);
14341
- node_fs.default.mkdirSync(dirPath, { recursive: true });
14342
- node_fs.default.writeFileSync(node_path.default.join(dirPath, ".gitkeep"), "");
14343
- if (options?.includeStubs) scaffoldPhaseStubs(dirPath, paddedNum, description);
14445
+ await node_fs.promises.mkdir(dirPath, { recursive: true });
14446
+ await node_fs.promises.writeFile(node_path.default.join(dirPath, ".gitkeep"), "");
14447
+ if (options?.includeStubs) await scaffoldPhaseStubs(dirPath, paddedNum, description);
14344
14448
  const phaseEntry = `\n### Phase ${newPhaseNum}: ${description}\n\n**Goal:** [To be planned]\n**Requirements**: TBD\n**Depends on:** Phase ${maxPhase}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /maxsim:plan-phase ${newPhaseNum} to break down)\n`;
14345
14449
  let updatedContent;
14346
14450
  const lastSeparator = content.lastIndexOf("\n---");
14347
14451
  if (lastSeparator > 0) updatedContent = content.slice(0, lastSeparator) + phaseEntry + content.slice(lastSeparator);
14348
14452
  else updatedContent = content + phaseEntry;
14349
- node_fs.default.writeFileSync(rmPath, updatedContent, "utf-8");
14453
+ await node_fs.promises.writeFile(rmPath, updatedContent, "utf-8");
14350
14454
  return {
14351
14455
  phase_number: newPhaseNum,
14352
14456
  padded: paddedNum,
@@ -14355,10 +14459,14 @@ function phaseAddCore(cwd, description, options) {
14355
14459
  description
14356
14460
  };
14357
14461
  }
14358
- function phaseInsertCore(cwd, afterPhase, description, options) {
14462
+ async function phaseInsertCore(cwd, afterPhase, description, options) {
14359
14463
  const rmPath = roadmapPath(cwd);
14360
- if (!node_fs.default.existsSync(rmPath)) throw new Error("ROADMAP.md not found");
14361
- const content = node_fs.default.readFileSync(rmPath, "utf-8");
14464
+ let content;
14465
+ try {
14466
+ content = await node_fs.promises.readFile(rmPath, "utf-8");
14467
+ } catch {
14468
+ throw new Error("ROADMAP.md not found");
14469
+ }
14362
14470
  const slug = generateSlugInternal(description);
14363
14471
  const afterPhaseEscaped = "0*" + normalizePhaseName(afterPhase).replace(/^0+/, "").replace(/\./g, "\\.");
14364
14472
  if (!getPhasePattern(afterPhaseEscaped, "i").test(content)) throw new Error(`Phase ${afterPhase} not found in ROADMAP.md`);
@@ -14366,7 +14474,7 @@ function phaseInsertCore(cwd, afterPhase, description, options) {
14366
14474
  const normalizedBase = normalizePhaseName(afterPhase);
14367
14475
  const existingDecimals = [];
14368
14476
  try {
14369
- const dirs = listSubDirs(phasesDirPath);
14477
+ const dirs = await listSubDirsAsync(phasesDirPath);
14370
14478
  const decimalPattern = new RegExp(`^${normalizedBase}\\.(\\d+)`);
14371
14479
  for (const dir of dirs) {
14372
14480
  const dm = dir.match(decimalPattern);
@@ -14378,9 +14486,9 @@ function phaseInsertCore(cwd, afterPhase, description, options) {
14378
14486
  const decimalPhase = `${normalizedBase}.${existingDecimals.length === 0 ? 1 : Math.max(...existingDecimals) + 1}`;
14379
14487
  const dirName = `${decimalPhase}-${slug}`;
14380
14488
  const dirPath = planningPath(cwd, "phases", dirName);
14381
- node_fs.default.mkdirSync(dirPath, { recursive: true });
14382
- node_fs.default.writeFileSync(node_path.default.join(dirPath, ".gitkeep"), "");
14383
- if (options?.includeStubs) scaffoldPhaseStubs(dirPath, decimalPhase, description);
14489
+ await node_fs.promises.mkdir(dirPath, { recursive: true });
14490
+ await node_fs.promises.writeFile(node_path.default.join(dirPath, ".gitkeep"), "");
14491
+ if (options?.includeStubs) await scaffoldPhaseStubs(dirPath, decimalPhase, description);
14384
14492
  const phaseEntry = `\n### Phase ${decimalPhase}: ${description} (INSERTED)\n\n**Goal:** [Urgent work - to be planned]\n**Requirements**: TBD\n**Depends on:** Phase ${afterPhase}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /maxsim:plan-phase ${decimalPhase} to break down)\n`;
14385
14493
  const headerPattern = new RegExp(`(#{2,4}\\s*Phase\\s+0*${afterPhaseEscaped}:[^\\n]*\\n)`, "i");
14386
14494
  const headerMatch = content.match(headerPattern);
@@ -14391,7 +14499,7 @@ function phaseInsertCore(cwd, afterPhase, description, options) {
14391
14499
  if (nextPhaseMatch) insertIdx = headerIdx + headerMatch[0].length + nextPhaseMatch.index;
14392
14500
  else insertIdx = content.length;
14393
14501
  const updatedContent = content.slice(0, insertIdx) + phaseEntry + content.slice(insertIdx);
14394
- node_fs.default.writeFileSync(rmPath, updatedContent, "utf-8");
14502
+ await node_fs.promises.writeFile(rmPath, updatedContent, "utf-8");
14395
14503
  return {
14396
14504
  phase_number: decimalPhase,
14397
14505
  after_phase: afterPhase,
@@ -14400,18 +14508,19 @@ function phaseInsertCore(cwd, afterPhase, description, options) {
14400
14508
  description
14401
14509
  };
14402
14510
  }
14403
- function phaseCompleteCore(cwd, phaseNum) {
14511
+ async function phaseCompleteCore(cwd, phaseNum) {
14404
14512
  const rmPath = roadmapPath(cwd);
14405
14513
  const stPath = statePath(cwd);
14406
14514
  const phasesDirPath = phasesPath(cwd);
14407
14515
  const today = todayISO();
14408
- const phaseInfo = findPhaseInternal(cwd, phaseNum);
14516
+ const phaseInfo = await findPhaseInternalAsync(cwd, phaseNum);
14409
14517
  if (!phaseInfo) throw new Error(`Phase ${phaseNum} not found`);
14410
14518
  const planCount = phaseInfo.plans.length;
14411
14519
  const summaryCount = phaseInfo.summaries.length;
14412
14520
  let requirementsUpdated = false;
14413
- if (node_fs.default.existsSync(rmPath)) {
14414
- let roadmapContent = node_fs.default.readFileSync(rmPath, "utf-8");
14521
+ const rmExists = await pathExistsAsync(rmPath);
14522
+ if (rmExists) {
14523
+ let roadmapContent = await node_fs.promises.readFile(rmPath, "utf-8");
14415
14524
  const checkboxPattern = new RegExp(`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${escapePhaseNum(phaseNum)}[:\\s][^\\n]*)`, "i");
14416
14525
  roadmapContent = roadmapContent.replace(checkboxPattern, `$1x$2 (completed ${today})`);
14417
14526
  const phaseEscaped = escapePhaseNum(phaseNum);
@@ -14420,20 +14529,20 @@ function phaseCompleteCore(cwd, phaseNum) {
14420
14529
  const planCountPattern = new RegExp(`(#{2,4}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`, "i");
14421
14530
  roadmapContent = roadmapContent.replace(planCountPattern, `$1${summaryCount}/${planCount} plans complete`);
14422
14531
  debugLog("phase-complete-write", `writing ROADMAP.md for phase ${phaseNum}`);
14423
- node_fs.default.writeFileSync(rmPath, roadmapContent, "utf-8");
14532
+ await node_fs.promises.writeFile(rmPath, roadmapContent, "utf-8");
14424
14533
  debugLog("phase-complete-write", `ROADMAP.md updated for phase ${phaseNum}`);
14425
14534
  const reqPath = planningPath(cwd, "REQUIREMENTS.md");
14426
- if (node_fs.default.existsSync(reqPath)) {
14535
+ if (await pathExistsAsync(reqPath)) {
14427
14536
  const reqMatch = roadmapContent.match(new RegExp(`Phase\\s+${escapePhaseNum(phaseNum)}[\\s\\S]*?\\*\\*Requirements:\\*\\*\\s*([^\\n]+)`, "i"));
14428
14537
  if (reqMatch) {
14429
14538
  const reqIds = reqMatch[1].replace(/[\[\]]/g, "").split(/[,\s]+/).map((r) => r.trim()).filter(Boolean);
14430
- let reqContent = node_fs.default.readFileSync(reqPath, "utf-8");
14539
+ let reqContent = await node_fs.promises.readFile(reqPath, "utf-8");
14431
14540
  for (const reqId of reqIds) {
14432
14541
  reqContent = reqContent.replace(new RegExp(`(-\\s*\\[)[ ](\\]\\s*\\*\\*${reqId}\\*\\*)`, "gi"), "$1x$2");
14433
14542
  reqContent = reqContent.replace(new RegExp(`(\\|\\s*${reqId}\\s*\\|[^|]+\\|)\\s*Pending\\s*(\\|)`, "gi"), "$1 Complete $2");
14434
14543
  }
14435
14544
  debugLog("phase-complete-write", `writing REQUIREMENTS.md for phase ${phaseNum}`);
14436
- node_fs.default.writeFileSync(reqPath, reqContent, "utf-8");
14545
+ await node_fs.promises.writeFile(reqPath, reqContent, "utf-8");
14437
14546
  debugLog("phase-complete-write", `REQUIREMENTS.md updated for phase ${phaseNum}`);
14438
14547
  requirementsUpdated = true;
14439
14548
  }
@@ -14443,7 +14552,7 @@ function phaseCompleteCore(cwd, phaseNum) {
14443
14552
  let nextPhaseName = null;
14444
14553
  let isLastPhase = true;
14445
14554
  try {
14446
- const dirs = listSubDirs(phasesDirPath, true);
14555
+ const dirs = await listSubDirsAsync(phasesDirPath, true);
14447
14556
  for (const dir of dirs) {
14448
14557
  const dm = dir.match(/^(\d+[A-Z]?(?:\.\d+)?)-?(.*)/i);
14449
14558
  if (dm) {
@@ -14458,8 +14567,9 @@ function phaseCompleteCore(cwd, phaseNum) {
14458
14567
  } catch (e) {
14459
14568
  debugLog("phase-complete-next-phase-scan-failed", e);
14460
14569
  }
14461
- if (node_fs.default.existsSync(stPath)) {
14462
- let stateContent = node_fs.default.readFileSync(stPath, "utf-8");
14570
+ const stExists = await pathExistsAsync(stPath);
14571
+ if (stExists) {
14572
+ let stateContent = await node_fs.promises.readFile(stPath, "utf-8");
14463
14573
  stateContent = stateContent.replace(/(\*\*Current Phase:\*\*\s*).*/, `$1${nextPhaseNum || phaseNum}`);
14464
14574
  if (nextPhaseName) stateContent = stateContent.replace(/(\*\*Current Phase Name:\*\*\s*).*/, `$1${nextPhaseName.replace(/-/g, " ")}`);
14465
14575
  stateContent = stateContent.replace(/(\*\*Status:\*\*\s*).*/, `$1${isLastPhase ? "Milestone complete" : "Ready to plan"}`);
@@ -14467,7 +14577,7 @@ function phaseCompleteCore(cwd, phaseNum) {
14467
14577
  stateContent = stateContent.replace(/(\*\*Last Activity:\*\*\s*).*/, `$1${today}`);
14468
14578
  stateContent = stateContent.replace(/(\*\*Last Activity Description:\*\*\s*).*/, `$1Phase ${phaseNum} complete${nextPhaseNum ? `, transitioned to Phase ${nextPhaseNum}` : ""}`);
14469
14579
  debugLog("phase-complete-write", `writing STATE.md for phase ${phaseNum}`);
14470
- node_fs.default.writeFileSync(stPath, stateContent, "utf-8");
14580
+ await node_fs.promises.writeFile(stPath, stateContent, "utf-8");
14471
14581
  debugLog("phase-complete-write", `STATE.md updated for phase ${phaseNum}`);
14472
14582
  }
14473
14583
  return {
@@ -14478,15 +14588,15 @@ function phaseCompleteCore(cwd, phaseNum) {
14478
14588
  next_phase_name: nextPhaseName,
14479
14589
  is_last_phase: isLastPhase,
14480
14590
  date: today,
14481
- roadmap_updated: node_fs.default.existsSync(rmPath),
14482
- state_updated: node_fs.default.existsSync(stPath),
14591
+ roadmap_updated: rmExists,
14592
+ state_updated: stExists,
14483
14593
  requirements_updated: requirementsUpdated
14484
14594
  };
14485
14595
  }
14486
14596
  async function cmdPhasesList(cwd, options) {
14487
14597
  const phasesDirPath = phasesPath(cwd);
14488
14598
  const { type, phase, includeArchived, offset, limit } = options;
14489
- if (!node_fs.default.existsSync(phasesDirPath)) if (type) return cmdOk({
14599
+ if (!await pathExistsAsync(phasesDirPath)) if (type) return cmdOk({
14490
14600
  files: [],
14491
14601
  count: 0,
14492
14602
  total: 0
@@ -14499,7 +14609,7 @@ async function cmdPhasesList(cwd, options) {
14499
14609
  try {
14500
14610
  let dirs = await listSubDirsAsync(phasesDirPath);
14501
14611
  if (includeArchived) {
14502
- const archived = getArchivedPhaseDirs(cwd);
14612
+ const archived = await getArchivedPhaseDirsAsync(cwd);
14503
14613
  for (const a of archived) dirs.push(`${a.name} [${a.milestone}]`);
14504
14614
  }
14505
14615
  dirs.sort((a, b) => comparePhaseNum(a, b));
@@ -14518,7 +14628,7 @@ async function cmdPhasesList(cwd, options) {
14518
14628
  if (type) {
14519
14629
  const files = (await Promise.all(dirs.map(async (dir) => {
14520
14630
  const dirPath = node_path.default.join(phasesDirPath, dir);
14521
- const dirFiles = await node_fs.default.promises.readdir(dirPath);
14631
+ const dirFiles = await node_fs.promises.readdir(dirPath);
14522
14632
  let filtered;
14523
14633
  if (type === "plans") filtered = dirFiles.filter(isPlanFile);
14524
14634
  else if (type === "summaries") filtered = dirFiles.filter(isSummaryFile);
@@ -14544,17 +14654,17 @@ async function cmdPhasesList(cwd, options) {
14544
14654
  return cmdErr("Failed to list phases: " + e.message);
14545
14655
  }
14546
14656
  }
14547
- function cmdPhaseNextDecimal(cwd, basePhase) {
14657
+ async function cmdPhaseNextDecimal(cwd, basePhase) {
14548
14658
  const phasesDirPath = phasesPath(cwd);
14549
14659
  const normalized = normalizePhaseName(basePhase);
14550
- if (!node_fs.default.existsSync(phasesDirPath)) return cmdOk({
14660
+ if (!await pathExistsAsync(phasesDirPath)) return cmdOk({
14551
14661
  found: false,
14552
14662
  base_phase: normalized,
14553
14663
  next: `${normalized}.1`,
14554
14664
  existing: []
14555
14665
  }, `${normalized}.1`);
14556
14666
  try {
14557
- const dirs = listSubDirs(phasesDirPath);
14667
+ const dirs = await listSubDirsAsync(phasesDirPath);
14558
14668
  const baseExists = dirs.some((d) => d.startsWith(normalized + "-") || d === normalized);
14559
14669
  const decimalPattern = new RegExp(`^${normalized}\\.(\\d+)`);
14560
14670
  const existingDecimals = [];
@@ -14581,7 +14691,7 @@ function cmdPhaseNextDecimal(cwd, basePhase) {
14581
14691
  return cmdErr("Failed to calculate next decimal phase: " + e.message);
14582
14692
  }
14583
14693
  }
14584
- function cmdFindPhase(cwd, phase) {
14694
+ async function cmdFindPhase(cwd, phase) {
14585
14695
  if (!phase) return cmdErr("phase identifier required");
14586
14696
  const phasesDirPath = phasesPath(cwd);
14587
14697
  const normalized = normalizePhaseName(phase);
@@ -14594,13 +14704,13 @@ function cmdFindPhase(cwd, phase) {
14594
14704
  summaries: []
14595
14705
  };
14596
14706
  try {
14597
- const match = listSubDirs(phasesDirPath, true).find((d) => d.startsWith(normalized));
14707
+ const match = (await listSubDirsAsync(phasesDirPath, true)).find((d) => d.startsWith(normalized));
14598
14708
  if (!match) return cmdOk(notFound, "");
14599
14709
  const dirMatch = match.match(/^(\d+[A-Z]?(?:\.\d+)?)-?(.*)/i);
14600
14710
  const phaseNumber = dirMatch ? dirMatch[1] : normalized;
14601
14711
  const phaseName = dirMatch && dirMatch[2] ? dirMatch[2] : null;
14602
14712
  const phaseDir = node_path.default.join(phasesDirPath, match);
14603
- const phaseFiles = node_fs.default.readdirSync(phaseDir);
14713
+ const phaseFiles = await node_fs.promises.readdir(phaseDir);
14604
14714
  const plans = phaseFiles.filter(isPlanFile).sort();
14605
14715
  const summaries = phaseFiles.filter(isSummaryFile).sort();
14606
14716
  const result = {
@@ -14616,13 +14726,13 @@ function cmdFindPhase(cwd, phase) {
14616
14726
  return cmdOk(notFound, "");
14617
14727
  }
14618
14728
  }
14619
- function cmdPhasePlanIndex(cwd, phase) {
14729
+ async function cmdPhasePlanIndex(cwd, phase) {
14620
14730
  if (!phase) return cmdErr("phase required for phase-plan-index");
14621
14731
  const phasesDirPath = phasesPath(cwd);
14622
14732
  const normalized = normalizePhaseName(phase);
14623
14733
  let phaseDir = null;
14624
14734
  try {
14625
- const match = listSubDirs(phasesDirPath, true).find((d) => d.startsWith(normalized));
14735
+ const match = (await listSubDirsAsync(phasesDirPath, true)).find((d) => d.startsWith(normalized));
14626
14736
  if (match) phaseDir = node_path.default.join(phasesDirPath, match);
14627
14737
  } catch (e) {
14628
14738
  debugLog("phase-plan-index-failed", e);
@@ -14635,7 +14745,7 @@ function cmdPhasePlanIndex(cwd, phase) {
14635
14745
  incomplete: [],
14636
14746
  has_checkpoints: false
14637
14747
  });
14638
- const phaseFiles = node_fs.default.readdirSync(phaseDir);
14748
+ const phaseFiles = await node_fs.promises.readdir(phaseDir);
14639
14749
  const planFiles = phaseFiles.filter(isPlanFile).sort();
14640
14750
  const summaryFiles = phaseFiles.filter(isSummaryFile);
14641
14751
  const completedPlanIds = new Set(summaryFiles.map(summaryId));
@@ -14643,10 +14753,11 @@ function cmdPhasePlanIndex(cwd, phase) {
14643
14753
  const waves = {};
14644
14754
  const incomplete = [];
14645
14755
  let hasCheckpoints = false;
14646
- for (const planFile of planFiles) {
14756
+ const planContents = await Promise.all(planFiles.map((planFile) => node_fs.promises.readFile(node_path.default.join(phaseDir, planFile), "utf-8")));
14757
+ for (let i = 0; i < planFiles.length; i++) {
14758
+ const planFile = planFiles[i];
14647
14759
  const id = planId(planFile);
14648
- const planPath = node_path.default.join(phaseDir, planFile);
14649
- const content = node_fs.default.readFileSync(planPath, "utf-8");
14760
+ const content = planContents[i];
14650
14761
  const fm = extractFrontmatter(content);
14651
14762
  const taskCount = (content.match(/##\s*Task\s*\d+/gi) || []).length;
14652
14763
  const wave = parseInt(fm.wave, 10) || 1;
@@ -14679,10 +14790,10 @@ function cmdPhasePlanIndex(cwd, phase) {
14679
14790
  has_checkpoints: hasCheckpoints
14680
14791
  });
14681
14792
  }
14682
- function cmdPhaseAdd(cwd, description) {
14793
+ async function cmdPhaseAdd(cwd, description) {
14683
14794
  if (!description) return cmdErr("description required for phase add");
14684
14795
  try {
14685
- const result = phaseAddCore(cwd, description, { includeStubs: false });
14796
+ const result = await phaseAddCore(cwd, description, { includeStubs: false });
14686
14797
  return cmdOk({
14687
14798
  phase_number: result.phase_number,
14688
14799
  padded: result.padded,
@@ -14694,10 +14805,10 @@ function cmdPhaseAdd(cwd, description) {
14694
14805
  return cmdErr(e.message);
14695
14806
  }
14696
14807
  }
14697
- function cmdPhaseInsert(cwd, afterPhase, description) {
14808
+ async function cmdPhaseInsert(cwd, afterPhase, description) {
14698
14809
  if (!afterPhase || !description) return cmdErr("after-phase and description required for phase insert");
14699
14810
  try {
14700
- const result = phaseInsertCore(cwd, afterPhase, description, { includeStubs: false });
14811
+ const result = await phaseInsertCore(cwd, afterPhase, description, { includeStubs: false });
14701
14812
  return cmdOk({
14702
14813
  phase_number: result.phase_number,
14703
14814
  after_phase: result.after_phase,
@@ -14709,26 +14820,26 @@ function cmdPhaseInsert(cwd, afterPhase, description) {
14709
14820
  return cmdErr(e.message);
14710
14821
  }
14711
14822
  }
14712
- function cmdPhaseRemove(cwd, targetPhase, options) {
14823
+ async function cmdPhaseRemove(cwd, targetPhase, options) {
14713
14824
  if (!targetPhase) return cmdErr("phase number required for phase remove");
14714
14825
  const rmPath = roadmapPath(cwd);
14715
14826
  const phasesDirPath = phasesPath(cwd);
14716
14827
  const force = options.force || false;
14717
- if (!node_fs.default.existsSync(rmPath)) return cmdErr("ROADMAP.md not found");
14828
+ if (!await pathExistsAsync(rmPath)) return cmdErr("ROADMAP.md not found");
14718
14829
  const normalized = normalizePhaseName(targetPhase);
14719
14830
  const isDecimal = targetPhase.includes(".");
14720
14831
  let targetDir = null;
14721
14832
  try {
14722
- targetDir = listSubDirs(phasesDirPath, true).find((d) => d.startsWith(normalized + "-") || d === normalized) || null;
14833
+ targetDir = (await listSubDirsAsync(phasesDirPath, true)).find((d) => d.startsWith(normalized + "-") || d === normalized) || null;
14723
14834
  } catch (e) {
14724
14835
  debugLog("phase-remove-find-target-failed", e);
14725
14836
  }
14726
14837
  if (targetDir && !force) {
14727
14838
  const targetPath = node_path.default.join(phasesDirPath, targetDir);
14728
- const summaries = node_fs.default.readdirSync(targetPath).filter(isSummaryFile);
14839
+ const summaries = (await node_fs.promises.readdir(targetPath)).filter(isSummaryFile);
14729
14840
  if (summaries.length > 0) return cmdErr(`Phase ${targetPhase} has ${summaries.length} executed plan(s). Use --force to remove anyway.`);
14730
14841
  }
14731
- if (targetDir) node_fs.default.rmSync(node_path.default.join(phasesDirPath, targetDir), {
14842
+ if (targetDir) await node_fs.promises.rm(node_path.default.join(phasesDirPath, targetDir), {
14732
14843
  recursive: true,
14733
14844
  force: true
14734
14845
  });
@@ -14739,7 +14850,7 @@ function cmdPhaseRemove(cwd, targetPhase, options) {
14739
14850
  const baseInt = baseParts[0];
14740
14851
  const removedDecimal = parseInt(baseParts[1], 10);
14741
14852
  try {
14742
- const dirs = listSubDirs(phasesDirPath, true);
14853
+ const dirs = await listSubDirsAsync(phasesDirPath, true);
14743
14854
  const decPattern = new RegExp(`^${baseInt}\\.(\\d+)-(.+)$`);
14744
14855
  const toRename = [];
14745
14856
  for (const dir of dirs) {
@@ -14756,15 +14867,15 @@ function cmdPhaseRemove(cwd, targetPhase, options) {
14756
14867
  const oldPhaseId = `${baseInt}.${item.oldDecimal}`;
14757
14868
  const newPhaseId = `${baseInt}.${newDecimal}`;
14758
14869
  const newDirName = `${baseInt}.${newDecimal}-${item.slug}`;
14759
- node_fs.default.renameSync(node_path.default.join(phasesDirPath, item.dir), node_path.default.join(phasesDirPath, newDirName));
14870
+ await node_fs.promises.rename(node_path.default.join(phasesDirPath, item.dir), node_path.default.join(phasesDirPath, newDirName));
14760
14871
  renamedDirs.push({
14761
14872
  from: item.dir,
14762
14873
  to: newDirName
14763
14874
  });
14764
- const dirFiles = node_fs.default.readdirSync(node_path.default.join(phasesDirPath, newDirName));
14875
+ const dirFiles = await node_fs.promises.readdir(node_path.default.join(phasesDirPath, newDirName));
14765
14876
  for (const f of dirFiles) if (f.includes(oldPhaseId)) {
14766
14877
  const newFileName = f.replace(oldPhaseId, newPhaseId);
14767
- node_fs.default.renameSync(node_path.default.join(phasesDirPath, newDirName, f), node_path.default.join(phasesDirPath, newDirName, newFileName));
14878
+ await node_fs.promises.rename(node_path.default.join(phasesDirPath, newDirName, f), node_path.default.join(phasesDirPath, newDirName, newFileName));
14768
14879
  renamedFiles.push({
14769
14880
  from: f,
14770
14881
  to: newFileName
@@ -14780,7 +14891,7 @@ function cmdPhaseRemove(cwd, targetPhase, options) {
14780
14891
  } else {
14781
14892
  const removedInt = parseInt(normalized, 10);
14782
14893
  try {
14783
- const dirs = listSubDirs(phasesDirPath, true);
14894
+ const dirs = await listSubDirsAsync(phasesDirPath, true);
14784
14895
  const toRename = [];
14785
14896
  for (const dir of dirs) {
14786
14897
  const dm = dir.match(/^(\d+)([A-Z])?(?:\.(\d+))?-(.+)$/i);
@@ -14807,15 +14918,15 @@ function cmdPhaseRemove(cwd, targetPhase, options) {
14807
14918
  const oldPrefix = `${oldPadded}${letterSuffix}${decimalSuffix}`;
14808
14919
  const newPrefix = `${newPadded}${letterSuffix}${decimalSuffix}`;
14809
14920
  const newDirName = `${newPrefix}-${item.slug}`;
14810
- node_fs.default.renameSync(node_path.default.join(phasesDirPath, item.dir), node_path.default.join(phasesDirPath, newDirName));
14921
+ await node_fs.promises.rename(node_path.default.join(phasesDirPath, item.dir), node_path.default.join(phasesDirPath, newDirName));
14811
14922
  renamedDirs.push({
14812
14923
  from: item.dir,
14813
14924
  to: newDirName
14814
14925
  });
14815
- const dirFiles = node_fs.default.readdirSync(node_path.default.join(phasesDirPath, newDirName));
14926
+ const dirFiles = await node_fs.promises.readdir(node_path.default.join(phasesDirPath, newDirName));
14816
14927
  for (const f of dirFiles) if (f.startsWith(oldPrefix)) {
14817
14928
  const newFileName = newPrefix + f.slice(oldPrefix.length);
14818
- node_fs.default.renameSync(node_path.default.join(phasesDirPath, newDirName, f), node_path.default.join(phasesDirPath, newDirName, newFileName));
14929
+ await node_fs.promises.rename(node_path.default.join(phasesDirPath, newDirName, f), node_path.default.join(phasesDirPath, newDirName, newFileName));
14819
14930
  renamedFiles.push({
14820
14931
  from: f,
14821
14932
  to: newFileName
@@ -14829,7 +14940,7 @@ function cmdPhaseRemove(cwd, targetPhase, options) {
14829
14940
  });
14830
14941
  }
14831
14942
  }
14832
- let roadmapContent = node_fs.default.readFileSync(rmPath, "utf-8");
14943
+ let roadmapContent = await node_fs.promises.readFile(rmPath, "utf-8");
14833
14944
  const targetEscaped = escapePhaseNum(targetPhase);
14834
14945
  const sectionPattern = new RegExp(`\\n?#{2,4}\\s*Phase\\s+${targetEscaped}\\s*:[\\s\\S]*?(?=\\n#{2,4}\\s+Phase\\s+\\d|$)`, "i");
14835
14946
  roadmapContent = roadmapContent.replace(sectionPattern, "");
@@ -14852,10 +14963,11 @@ function cmdPhaseRemove(cwd, targetPhase, options) {
14852
14963
  roadmapContent = roadmapContent.replace(new RegExp(`(Depends on:\\*\\*\\s*Phase\\s+)${oldStr}\\b`, "gi"), `$1${newStr}`);
14853
14964
  }
14854
14965
  }
14855
- node_fs.default.writeFileSync(rmPath, roadmapContent, "utf-8");
14966
+ await node_fs.promises.writeFile(rmPath, roadmapContent, "utf-8");
14856
14967
  const stPath = statePath(cwd);
14857
- if (node_fs.default.existsSync(stPath)) {
14858
- let stateContent = node_fs.default.readFileSync(stPath, "utf-8");
14968
+ const stExists = await pathExistsAsync(stPath);
14969
+ if (stExists) {
14970
+ let stateContent = await node_fs.promises.readFile(stPath, "utf-8");
14859
14971
  const totalPattern = /(\*\*Total Phases:\*\*\s*)(\d+)/;
14860
14972
  const totalMatch = stateContent.match(totalPattern);
14861
14973
  if (totalMatch) {
@@ -14868,7 +14980,7 @@ function cmdPhaseRemove(cwd, targetPhase, options) {
14868
14980
  const oldTotal = parseInt(ofMatch[2], 10);
14869
14981
  stateContent = stateContent.replace(ofPattern, `$1${oldTotal - 1}$3`);
14870
14982
  }
14871
- node_fs.default.writeFileSync(stPath, stateContent, "utf-8");
14983
+ await node_fs.promises.writeFile(stPath, stateContent, "utf-8");
14872
14984
  }
14873
14985
  return cmdOk({
14874
14986
  removed: targetPhase,
@@ -14876,13 +14988,13 @@ function cmdPhaseRemove(cwd, targetPhase, options) {
14876
14988
  renamed_directories: renamedDirs,
14877
14989
  renamed_files: renamedFiles,
14878
14990
  roadmap_updated: true,
14879
- state_updated: node_fs.default.existsSync(stPath)
14991
+ state_updated: stExists
14880
14992
  });
14881
14993
  }
14882
- function cmdPhaseComplete(cwd, phaseNum) {
14994
+ async function cmdPhaseComplete(cwd, phaseNum) {
14883
14995
  if (!phaseNum) return cmdErr("phase number required for phase complete");
14884
14996
  try {
14885
- const result = phaseCompleteCore(cwd, phaseNum);
14997
+ const result = await phaseCompleteCore(cwd, phaseNum);
14886
14998
  return cmdOk({
14887
14999
  completed_phase: result.completed_phase,
14888
15000
  phase_name: result.phase_name,
@@ -15137,7 +15249,17 @@ function resolveArtefaktPath(cwd, type, phase) {
15137
15249
  }
15138
15250
  return planningPath(cwd, filename);
15139
15251
  }
15252
+ const TEMPLATE_FILES = {
15253
+ "decisions": "decisions.md",
15254
+ "acceptance-criteria": "acceptance-criteria.md",
15255
+ "no-gos": "no-gos.md"
15256
+ };
15140
15257
  function getTemplate(type) {
15258
+ const content = safeReadFile(node_path.default.join(node_os.default.homedir(), ".claude", "maxsim", "templates", TEMPLATE_FILES[type]));
15259
+ if (content) return content.replace(/\{\{date\}\}/g, todayISO());
15260
+ return getHardcodedTemplate(type);
15261
+ }
15262
+ function getHardcodedTemplate(type) {
15141
15263
  const today = todayISO();
15142
15264
  switch (type) {
15143
15265
  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`;
@@ -15247,6 +15369,36 @@ function addIfExists(files, cwd, relPath, role) {
15247
15369
  const entry = fileEntry(cwd, relPath, role);
15248
15370
  if (entry) files.push(entry);
15249
15371
  }
15372
+ const TOPIC_TO_CODEBASE_DOCS = {
15373
+ ui: ["CONVENTIONS.md", "STRUCTURE.md"],
15374
+ frontend: ["CONVENTIONS.md", "STRUCTURE.md"],
15375
+ component: ["CONVENTIONS.md", "STRUCTURE.md"],
15376
+ api: ["ARCHITECTURE.md", "CONVENTIONS.md"],
15377
+ backend: ["ARCHITECTURE.md", "CONVENTIONS.md"],
15378
+ server: ["ARCHITECTURE.md", "CONVENTIONS.md"],
15379
+ database: ["ARCHITECTURE.md", "STACK.md"],
15380
+ schema: ["ARCHITECTURE.md", "STACK.md"],
15381
+ data: ["ARCHITECTURE.md", "STACK.md"],
15382
+ testing: ["TESTING.md", "CONVENTIONS.md"],
15383
+ test: ["TESTING.md", "CONVENTIONS.md"],
15384
+ integration: ["INTEGRATIONS.md", "STACK.md"],
15385
+ deploy: ["INTEGRATIONS.md", "STACK.md"],
15386
+ refactor: ["CONCERNS.md", "ARCHITECTURE.md"],
15387
+ cleanup: ["CONCERNS.md", "ARCHITECTURE.md"],
15388
+ setup: ["STACK.md", "STRUCTURE.md"],
15389
+ config: ["STACK.md", "STRUCTURE.md"],
15390
+ auth: ["ARCHITECTURE.md", "INTEGRATIONS.md"],
15391
+ performance: ["ARCHITECTURE.md", "STACK.md"],
15392
+ install: ["STACK.md", "STRUCTURE.md"]
15393
+ };
15394
+ const DEFAULT_CODEBASE_DOCS = ["STACK.md", "ARCHITECTURE.md"];
15395
+ function selectCodebaseDocs(topic) {
15396
+ if (!topic) return DEFAULT_CODEBASE_DOCS;
15397
+ const topicLower = topic.toLowerCase();
15398
+ const matched = /* @__PURE__ */ new Set();
15399
+ for (const [keyword, docs] of Object.entries(TOPIC_TO_CODEBASE_DOCS)) if (topicLower.includes(keyword)) for (const doc of docs) matched.add(doc);
15400
+ return matched.size > 0 ? Array.from(matched) : DEFAULT_CODEBASE_DOCS;
15401
+ }
15250
15402
  function loadProjectContext(cwd) {
15251
15403
  const files = [];
15252
15404
  addIfExists(files, cwd, ".planning/PROJECT.md", "project-vision");
@@ -15295,6 +15447,16 @@ function loadArtefakteContext(cwd, phase) {
15295
15447
  }
15296
15448
  return files;
15297
15449
  }
15450
+ function loadCodebaseContext(cwd, topic) {
15451
+ const files = [];
15452
+ const codebaseDir = planningPath(cwd, "codebase");
15453
+ try {
15454
+ const existing = node_fs.default.readdirSync(codebaseDir).filter((f) => f.endsWith(".md"));
15455
+ const selected = selectCodebaseDocs(topic);
15456
+ for (const filename of selected) if (existing.includes(filename)) addIfExists(files, cwd, `.planning/codebase/${filename}`, `codebase-${filename.replace(".md", "").toLowerCase()}`);
15457
+ } catch {}
15458
+ return files;
15459
+ }
15298
15460
  function loadHistoryContext(cwd, currentPhase) {
15299
15461
  const files = [];
15300
15462
  const pd = phasesPath(cwd);
@@ -15318,6 +15480,8 @@ function cmdContextLoad(cwd, phase, topic, includeHistory) {
15318
15480
  allFiles.push(...loadProjectContext(cwd));
15319
15481
  allFiles.push(...loadRoadmapContext(cwd));
15320
15482
  allFiles.push(...loadArtefakteContext(cwd, phase));
15483
+ const selectedDocs = selectCodebaseDocs(topic);
15484
+ allFiles.push(...loadCodebaseContext(cwd, topic));
15321
15485
  if (phase) allFiles.push(...loadPhaseContext(cwd, phase));
15322
15486
  if (includeHistory) allFiles.push(...loadHistoryContext(cwd, phase));
15323
15487
  const seen = /* @__PURE__ */ new Set();
@@ -15330,7 +15494,8 @@ function cmdContextLoad(cwd, phase, topic, includeHistory) {
15330
15494
  files: deduped,
15331
15495
  total_size: deduped.reduce((sum, f) => sum + f.size, 0),
15332
15496
  phase: phase ?? null,
15333
- topic: topic ?? null
15497
+ topic: topic ?? null,
15498
+ codebase_docs_selected: selectedDocs
15334
15499
  });
15335
15500
  }
15336
15501
 
@@ -15372,12 +15537,12 @@ function readSkillInfo(skillDir, dirName) {
15372
15537
  /**
15373
15538
  * List all installed skills from `.claude/skills/`.
15374
15539
  */
15375
- function cmdSkillList(cwd, raw) {
15540
+ function cmdSkillList(cwd) {
15376
15541
  const dir = skillsDir(cwd);
15377
- if (!node_fs.default.existsSync(dir)) output({
15542
+ if (!node_fs.default.existsSync(dir)) return cmdOk({
15378
15543
  skills: [],
15379
15544
  count: 0
15380
- }, raw, "No skills installed.");
15545
+ }, "No skills installed.");
15381
15546
  const entries = node_fs.default.readdirSync(dir, { withFileTypes: true });
15382
15547
  const skills = [];
15383
15548
  for (const entry of entries) {
@@ -15385,56 +15550,52 @@ function cmdSkillList(cwd, raw) {
15385
15550
  const info = readSkillInfo(node_path.default.join(dir, entry.name), entry.name);
15386
15551
  if (info) skills.push(info);
15387
15552
  }
15388
- output({
15553
+ return cmdOk({
15389
15554
  skills,
15390
15555
  count: skills.length
15391
- }, raw, skills.map((s) => `${s.name}: ${s.description}`).join("\n"));
15556
+ }, skills.map((s) => `${s.name}: ${s.description}`).join("\n"));
15392
15557
  }
15393
15558
  /**
15394
15559
  * Install a specific skill from the templates directory.
15395
15560
  */
15396
- function cmdSkillInstall(cwd, skillName, raw) {
15397
- if (!skillName) error("skill name required. Usage: skill-install <name>");
15561
+ function cmdSkillInstall(cwd, skillName) {
15562
+ if (!skillName) return cmdErr("skill name required. Usage: skill-install <name>");
15398
15563
  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(", ")}`);
15564
+ if (!node_fs.default.existsSync(srcFile)) return cmdErr(`Skill "${skillName}" not found in templates. Available: ${listAvailableTemplates().join(", ")}`);
15400
15565
  const destDir = node_path.default.join(skillsDir(cwd), skillName);
15401
15566
  const destFile = node_path.default.join(destDir, "SKILL.md");
15402
15567
  node_fs.default.mkdirSync(destDir, { recursive: true });
15403
15568
  node_fs.default.copyFileSync(srcFile, destFile);
15404
- output({
15569
+ return cmdOk({
15405
15570
  installed: true,
15406
15571
  skill: skillName,
15407
15572
  path: node_path.default.relative(cwd, destFile)
15408
- }, raw, `Installed skill: ${skillName}`);
15573
+ }, `Installed skill: ${skillName}`);
15409
15574
  }
15410
15575
  /**
15411
15576
  * Update one or all installed skills from the templates source.
15412
15577
  */
15413
- function cmdSkillUpdate(cwd, skillName, raw) {
15578
+ function cmdSkillUpdate(cwd, skillName) {
15414
15579
  const dir = skillsDir(cwd);
15415
15580
  const templateDir = skillsTemplateDir();
15416
15581
  if (skillName) {
15417
15582
  const srcFile = node_path.default.join(templateDir, skillName, "SKILL.md");
15418
- if (!node_fs.default.existsSync(srcFile)) error(`Skill template "${skillName}" not found.`);
15583
+ if (!node_fs.default.existsSync(srcFile)) return cmdErr(`Skill template "${skillName}" not found.`);
15419
15584
  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.`);
15585
+ if (!node_fs.default.existsSync(destDir)) return cmdErr(`Skill "${skillName}" is not installed. Use skill-install first.`);
15421
15586
  const destFile = node_path.default.join(destDir, "SKILL.md");
15422
15587
  node_fs.default.copyFileSync(srcFile, destFile);
15423
- output({
15588
+ return cmdOk({
15424
15589
  updated: [skillName],
15425
15590
  skipped: [],
15426
15591
  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;
15592
+ }, `Updated skill: ${skillName}`);
15437
15593
  }
15594
+ if (!node_fs.default.existsSync(dir)) return cmdOk({
15595
+ updated: [],
15596
+ skipped: [],
15597
+ not_found: []
15598
+ }, "No skills installed.");
15438
15599
  const entries = node_fs.default.readdirSync(dir, { withFileTypes: true });
15439
15600
  const updated = [];
15440
15601
  const skipped = [];
@@ -15451,10 +15612,10 @@ function cmdSkillUpdate(cwd, skillName, raw) {
15451
15612
  updated.push(name);
15452
15613
  }
15453
15614
  const summary = updated.length > 0 ? `Updated ${updated.length} skill(s): ${updated.join(", ")}` : "No skills updated.";
15454
- output({
15615
+ return cmdOk({
15455
15616
  updated,
15456
15617
  skipped
15457
- }, raw, summary);
15618
+ }, summary);
15458
15619
  }
15459
15620
  function listAvailableTemplates() {
15460
15621
  const dir = skillsTemplateDir();
@@ -16377,7 +16538,7 @@ const handleRoadmap = async (args, cwd, raw) => {
16377
16538
  if (handler) return handleResult(await handler(), raw);
16378
16539
  error("Unknown roadmap subcommand. Available: get-phase, analyze, update-plan-progress");
16379
16540
  };
16380
- const handlePhase = (args, cwd, raw) => {
16541
+ const handlePhase = async (args, cwd, raw) => {
16381
16542
  const sub = args[1];
16382
16543
  const handler = sub ? {
16383
16544
  "next-decimal": () => cmdPhaseNextDecimal(cwd, args[2]),
@@ -16386,7 +16547,7 @@ const handlePhase = (args, cwd, raw) => {
16386
16547
  "remove": () => cmdPhaseRemove(cwd, args[2], { force: hasFlag(args, "force") }),
16387
16548
  "complete": () => cmdPhaseComplete(cwd, args[2])
16388
16549
  }[sub] : void 0;
16389
- if (handler) return handleResult(handler(), raw);
16550
+ if (handler) return handleResult(await handler(), raw);
16390
16551
  error("Unknown phase subcommand. Available: next-decimal, add, insert, remove, complete");
16391
16552
  };
16392
16553
  const handleMilestone = (args, cwd, raw) => {
@@ -16439,7 +16600,7 @@ const handleInit = (args, cwd, raw) => {
16439
16600
  const COMMANDS = {
16440
16601
  "state": handleState,
16441
16602
  "resolve-model": (args, cwd, raw) => handleResult(cmdResolveModel(cwd, args[1], raw), raw),
16442
- "find-phase": (args, cwd, raw) => handleResult(cmdFindPhase(cwd, args[1]), raw),
16603
+ "find-phase": async (args, cwd, raw) => handleResult(await cmdFindPhase(cwd, args[1]), raw),
16443
16604
  "commit": async (args, cwd, raw) => {
16444
16605
  const files = args.indexOf("--files") !== -1 ? args.slice(args.indexOf("--files") + 1).filter((a) => !a.startsWith("--")) : [];
16445
16606
  handleResult(await cmdCommit(cwd, args[1], files, raw, hasFlag(args, "amend")), raw);
@@ -16482,8 +16643,8 @@ const COMMANDS = {
16482
16643
  }, raw), raw);
16483
16644
  },
16484
16645
  "init": handleInit,
16485
- "phase-plan-index": (args, cwd, raw) => handleResult(cmdPhasePlanIndex(cwd, args[1]), raw),
16486
- "state-snapshot": (_args, cwd, raw) => handleResult(cmdStateSnapshot(cwd, raw), raw),
16646
+ "phase-plan-index": async (args, cwd, raw) => handleResult(await cmdPhasePlanIndex(cwd, args[1]), raw),
16647
+ "state-snapshot": async (_args, cwd, raw) => handleResult(await cmdStateSnapshot(cwd, raw), raw),
16487
16648
  "summary-extract": (args, cwd, raw) => {
16488
16649
  const fieldsIndex = args.indexOf("--fields");
16489
16650
  const fields = fieldsIndex !== -1 ? args[fieldsIndex + 1].split(",") : null;
@@ -16501,9 +16662,9 @@ const COMMANDS = {
16501
16662
  "artefakte-append": (args, cwd, raw) => handleResult(cmdArtefakteAppend(cwd, args[1], getFlag(args, "--entry") ?? void 0, getFlag(args, "--phase") ?? void 0, raw), raw),
16502
16663
  "artefakte-list": (args, cwd, raw) => handleResult(cmdArtefakteList(cwd, getFlag(args, "--phase") ?? void 0, raw), raw),
16503
16664
  "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),
16665
+ "skill-list": (_args, cwd, raw) => handleResult(cmdSkillList(cwd), raw),
16666
+ "skill-install": (args, cwd, raw) => handleResult(cmdSkillInstall(cwd, args[1]), raw),
16667
+ "skill-update": (args, cwd, raw) => handleResult(cmdSkillUpdate(cwd, args[1]), raw),
16507
16668
  "start": async (args, cwd, raw) => handleResult(await cmdStart(cwd, {
16508
16669
  noBrowser: hasFlag(args, "no-browser"),
16509
16670
  networkMode: hasFlag(args, "network")
@@ -16512,6 +16673,23 @@ const COMMANDS = {
16512
16673
  "start-server": async () => {
16513
16674
  const serverPath = node_path.join(__dirname, "mcp-server.cjs");
16514
16675
  (0, node_child_process.spawn)(process.execPath, [serverPath], { stdio: "inherit" }).on("exit", (code) => process.exit(code ?? 0));
16676
+ },
16677
+ "backend-start": async (args, cwd, raw) => {
16678
+ const { startBackend } = await Promise.resolve().then(() => require("./lifecycle-0M4VqOMm.cjs"));
16679
+ const portFlag = args.find((a) => a.startsWith("--port="))?.split("=")[1];
16680
+ const background = !args.includes("--foreground");
16681
+ output(await startBackend(cwd, {
16682
+ port: portFlag ? parseInt(portFlag, 10) : void 0,
16683
+ background
16684
+ }), raw);
16685
+ },
16686
+ "backend-stop": async (_args, cwd, raw) => {
16687
+ const { stopBackend } = await Promise.resolve().then(() => require("./lifecycle-0M4VqOMm.cjs"));
16688
+ output({ stopped: await stopBackend(cwd) }, raw);
16689
+ },
16690
+ "backend-status": async (_args, cwd, raw) => {
16691
+ const { getBackendStatus } = await Promise.resolve().then(() => require("./lifecycle-0M4VqOMm.cjs"));
16692
+ output(await getBackendStatus(cwd) || { running: false }, raw);
16515
16693
  }
16516
16694
  };
16517
16695
  async function main() {
@@ -16604,4 +16782,32 @@ async function handleDashboard(args) {
16604
16782
  main();
16605
16783
 
16606
16784
  //#endregion
16785
+ exports.__commonJSMin = __commonJSMin;
16786
+ exports.__toESM = __toESM;
16787
+ exports.appendToStateSection = appendToStateSection;
16788
+ exports.cmdConfigGet = cmdConfigGet;
16789
+ exports.cmdConfigSet = cmdConfigSet;
16790
+ exports.cmdContextLoad = cmdContextLoad;
16791
+ exports.cmdRoadmapAnalyze = cmdRoadmapAnalyze;
16792
+ exports.comparePhaseNum = comparePhaseNum;
16793
+ exports.escapeStringRegexp = escapeStringRegexp;
16794
+ exports.extractFrontmatter = extractFrontmatter;
16795
+ exports.findPhaseInternal = findPhaseInternal;
16796
+ exports.generateSlugInternal = generateSlugInternal;
16797
+ exports.getArchivedPhaseDirs = getArchivedPhaseDirs;
16798
+ exports.getPhasePattern = getPhasePattern;
16799
+ exports.listSubDirs = listSubDirs;
16800
+ exports.loadConfig = loadConfig;
16801
+ exports.normalizePhaseName = normalizePhaseName;
16802
+ exports.parseTodoFrontmatter = parseTodoFrontmatter;
16803
+ exports.phaseAddCore = phaseAddCore;
16804
+ exports.phaseCompleteCore = phaseCompleteCore;
16805
+ exports.phaseInsertCore = phaseInsertCore;
16806
+ exports.phasesPath = phasesPath;
16807
+ exports.planningPath = planningPath;
16808
+ exports.safeReadFile = safeReadFile;
16809
+ exports.stateExtractField = stateExtractField;
16810
+ exports.statePath = statePath;
16811
+ exports.stateReplaceField = stateReplaceField;
16812
+ exports.todayISO = todayISO;
16607
16813
  //# sourceMappingURL=cli.cjs.map