maxsimcli 4.1.0 → 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 (79) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/assets/CHANGELOG.md +8 -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-server.cjs +174 -51
  33. package/dist/backend-server.cjs.map +1 -1
  34. package/dist/cli.cjs +310 -146
  35. package/dist/cli.cjs.map +1 -1
  36. package/dist/cli.js +5 -5
  37. package/dist/cli.js.map +1 -1
  38. package/dist/core/artefakte.d.ts.map +1 -1
  39. package/dist/core/artefakte.js +16 -0
  40. package/dist/core/artefakte.js.map +1 -1
  41. package/dist/core/context-loader.d.ts +1 -0
  42. package/dist/core/context-loader.d.ts.map +1 -1
  43. package/dist/core/context-loader.js +58 -0
  44. package/dist/core/context-loader.js.map +1 -1
  45. package/dist/core/core.d.ts +6 -0
  46. package/dist/core/core.d.ts.map +1 -1
  47. package/dist/core/core.js +238 -0
  48. package/dist/core/core.js.map +1 -1
  49. package/dist/core/index.d.ts +1 -1
  50. package/dist/core/index.d.ts.map +1 -1
  51. package/dist/core/index.js +5 -3
  52. package/dist/core/index.js.map +1 -1
  53. package/dist/core/phase.d.ts +11 -11
  54. package/dist/core/phase.d.ts.map +1 -1
  55. package/dist/core/phase.js +88 -73
  56. package/dist/core/phase.js.map +1 -1
  57. package/dist/core/roadmap.d.ts +2 -2
  58. package/dist/core/roadmap.d.ts.map +1 -1
  59. package/dist/core/roadmap.js +11 -10
  60. package/dist/core/roadmap.js.map +1 -1
  61. package/dist/core/state.d.ts +11 -11
  62. package/dist/core/state.d.ts.map +1 -1
  63. package/dist/core/state.js +60 -54
  64. package/dist/core/state.js.map +1 -1
  65. package/dist/core-RRjCSt0G.cjs.map +1 -1
  66. package/dist/{lifecycle-D4E9yP6E.cjs → lifecycle-0M4VqOMm.cjs} +2 -2
  67. package/dist/{lifecycle-D4E9yP6E.cjs.map → lifecycle-0M4VqOMm.cjs.map} +1 -1
  68. package/dist/mcp/context-tools.d.ts.map +1 -1
  69. package/dist/mcp/context-tools.js +7 -3
  70. package/dist/mcp/context-tools.js.map +1 -1
  71. package/dist/mcp/phase-tools.js +3 -3
  72. package/dist/mcp/phase-tools.js.map +1 -1
  73. package/dist/mcp-server.cjs +163 -40
  74. package/dist/mcp-server.cjs.map +1 -1
  75. package/dist/{server-pvY2WbKj.cjs → server-G1MIg_Oe.cjs} +7 -7
  76. package/dist/server-G1MIg_Oe.cjs.map +1 -0
  77. package/package.json +1 -1
  78. package/dist/assets/dashboard/client/assets/index-CmiJKqOU.css +0 -32
  79. package/dist/server-pvY2WbKj.cjs.map +0 -1
@@ -32123,7 +32123,7 @@ var require_view = /* @__PURE__ */ __commonJSMin(((exports, module) => {
32123
32123
  */
32124
32124
  var debug = require_src$3()("express:view");
32125
32125
  var path$22 = require("path");
32126
- var fs$23 = require("fs");
32126
+ var fs$20 = require("fs");
32127
32127
  /**
32128
32128
  * Module variables.
32129
32129
  * @private
@@ -32229,7 +32229,7 @@ var require_view = /* @__PURE__ */ __commonJSMin(((exports, module) => {
32229
32229
  function tryStat(path) {
32230
32230
  debug("stat \"%s\"", path);
32231
32231
  try {
32232
- return fs$23.statSync(path);
32232
+ return fs$20.statSync(path);
32233
32233
  } catch (e) {
32234
32234
  return;
32235
32235
  }
@@ -34450,7 +34450,7 @@ var require_types$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
34450
34450
  //#region ../../node_modules/send/node_modules/mime/mime.js
34451
34451
  var require_mime = /* @__PURE__ */ __commonJSMin(((exports, module) => {
34452
34452
  require("path");
34453
- var fs$22 = require("fs");
34453
+ var fs$19 = require("fs");
34454
34454
  function Mime() {
34455
34455
  this.types = Object.create(null);
34456
34456
  this.extensions = Object.create(null);
@@ -34485,7 +34485,7 @@ var require_mime = /* @__PURE__ */ __commonJSMin(((exports, module) => {
34485
34485
  Mime.prototype.load = function(file) {
34486
34486
  this._loading = file;
34487
34487
  var map = {};
34488
- fs$22.readFileSync(file, "ascii").split(/[\r\n]+/).forEach(function(line) {
34488
+ fs$19.readFileSync(file, "ascii").split(/[\r\n]+/).forEach(function(line) {
34489
34489
  var fields = line.replace(/\s*#.*|^\s*|\s*$/g, "").split(/\s+/);
34490
34490
  map[fields.shift()] = fields;
34491
34491
  });
@@ -34764,7 +34764,7 @@ var require_send = /* @__PURE__ */ __commonJSMin(((exports, module) => {
34764
34764
  var escapeHtml = require_escape_html();
34765
34765
  var etag = require_etag();
34766
34766
  var fresh = require_fresh();
34767
- var fs$21 = require("fs");
34767
+ var fs$18 = require("fs");
34768
34768
  var mime = require_mime();
34769
34769
  var ms = require_ms();
34770
34770
  var onFinished = require_on_finished();
@@ -35229,7 +35229,7 @@ var require_send = /* @__PURE__ */ __commonJSMin(((exports, module) => {
35229
35229
  var i = 0;
35230
35230
  var self = this;
35231
35231
  debug("stat \"%s\"", path);
35232
- fs$21.stat(path, function onstat(err, stat) {
35232
+ fs$18.stat(path, function onstat(err, stat) {
35233
35233
  if (err && err.code === "ENOENT" && !extname(path) && path[path.length - 1] !== sep) return next(err);
35234
35234
  if (err) return self.onStatError(err);
35235
35235
  if (stat.isDirectory()) return self.redirect(path);
@@ -35240,7 +35240,7 @@ var require_send = /* @__PURE__ */ __commonJSMin(((exports, module) => {
35240
35240
  if (self._extensions.length <= i) return err ? self.onStatError(err) : self.error(404);
35241
35241
  var p = path + "." + self._extensions[i++];
35242
35242
  debug("stat \"%s\"", p);
35243
- fs$21.stat(p, function(err, stat) {
35243
+ fs$18.stat(p, function(err, stat) {
35244
35244
  if (err) return next(err);
35245
35245
  if (stat.isDirectory()) return next();
35246
35246
  self.emit("file", p, stat);
@@ -35264,7 +35264,7 @@ var require_send = /* @__PURE__ */ __commonJSMin(((exports, module) => {
35264
35264
  }
35265
35265
  var p = join(path, self._index[i]);
35266
35266
  debug("stat \"%s\"", p);
35267
- fs$21.stat(p, function(err, stat) {
35267
+ fs$18.stat(p, function(err, stat) {
35268
35268
  if (err) return next(err);
35269
35269
  if (stat.isDirectory()) return next();
35270
35270
  self.emit("file", p, stat);
@@ -35283,7 +35283,7 @@ var require_send = /* @__PURE__ */ __commonJSMin(((exports, module) => {
35283
35283
  SendStream.prototype.stream = function stream(path, options) {
35284
35284
  var self = this;
35285
35285
  var res = this.res;
35286
- var stream$5 = fs$21.createReadStream(path, options);
35286
+ var stream$5 = fs$18.createReadStream(path, options);
35287
35287
  this.emit("stream", stream$5);
35288
35288
  stream$5.pipe(res);
35289
35289
  function cleanup() {
@@ -63942,7 +63942,7 @@ var require_has_flag = /* @__PURE__ */ __commonJSMin(((exports, module) => {
63942
63942
  //#endregion
63943
63943
  //#region ../../node_modules/supports-color/index.js
63944
63944
  var require_supports_color = /* @__PURE__ */ __commonJSMin(((exports, module) => {
63945
- const os$4 = require("os");
63945
+ const os$5 = require("os");
63946
63946
  const tty$1 = require("tty");
63947
63947
  const hasFlag = require_has_flag();
63948
63948
  const { env } = process;
@@ -63969,7 +63969,7 @@ var require_supports_color = /* @__PURE__ */ __commonJSMin(((exports, module) =>
63969
63969
  const min = forceColor || 0;
63970
63970
  if (env.TERM === "dumb") return min;
63971
63971
  if (process.platform === "win32") {
63972
- const osRelease = os$4.release().split(".");
63972
+ const osRelease = os$5.release().split(".");
63973
63973
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) return Number(osRelease[2]) >= 14931 ? 3 : 2;
63974
63974
  return 1;
63975
63975
  }
@@ -67951,6 +67951,77 @@ function generateSlugInternal(text) {
67951
67951
  strict: true
67952
67952
  });
67953
67953
  }
67954
+ async function pathExistsAsync(p) {
67955
+ try {
67956
+ await node_fs.promises.access(p);
67957
+ return true;
67958
+ } catch {
67959
+ return false;
67960
+ }
67961
+ }
67962
+ async function searchPhaseInDirAsync(baseDir, relBase, normalized) {
67963
+ try {
67964
+ const match = (await listSubDirsAsync(baseDir, true)).find((d) => d.startsWith(normalized));
67965
+ if (!match) return null;
67966
+ const dirMatch = match.match(/^(\d+[A-Z]?(?:\.\d+)?)-?(.*)/i);
67967
+ const phaseNumber = dirMatch ? dirMatch[1] : normalized;
67968
+ const phaseName = dirMatch && dirMatch[2] ? dirMatch[2] : null;
67969
+ const phaseDir = node_path.default.join(baseDir, match);
67970
+ const phaseFiles = await node_fs.promises.readdir(phaseDir);
67971
+ const plans = phaseFiles.filter(isPlanFile).sort();
67972
+ const summaries = phaseFiles.filter(isSummaryFile).sort();
67973
+ const hasResearch = phaseFiles.some((f) => f.endsWith("-RESEARCH.md") || f === "RESEARCH.md");
67974
+ const hasContext = phaseFiles.some((f) => f.endsWith("-CONTEXT.md") || f === "CONTEXT.md");
67975
+ const hasVerification = phaseFiles.some((f) => f.endsWith("-VERIFICATION.md") || f === "VERIFICATION.md");
67976
+ const completedPlanIds = new Set(summaries.map(summaryId));
67977
+ const incompletePlans = plans.filter((p) => !completedPlanIds.has(planId(p)));
67978
+ return {
67979
+ found: true,
67980
+ directory: node_path.default.join(relBase, match),
67981
+ phase_number: phaseNumber,
67982
+ phase_name: phaseName,
67983
+ phase_slug: phaseName ? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") : null,
67984
+ plans,
67985
+ summaries,
67986
+ incomplete_plans: incompletePlans,
67987
+ has_research: hasResearch,
67988
+ has_context: hasContext,
67989
+ has_verification: hasVerification
67990
+ };
67991
+ } catch (e) {
67992
+ debugLog("search-phase-in-dir-async-failed", {
67993
+ dir: baseDir,
67994
+ phase: normalized,
67995
+ error: errorMsg(e)
67996
+ });
67997
+ return null;
67998
+ }
67999
+ }
68000
+ async function findPhaseInternalAsync(cwd, phase) {
68001
+ if (!phase) return null;
68002
+ const pd = phasesPath(cwd);
68003
+ const normalized = normalizePhaseName(phase);
68004
+ const current = await searchPhaseInDirAsync(pd, node_path.default.join(".planning", "phases"), normalized);
68005
+ if (current) return current;
68006
+ const milestonesDir = planningPath(cwd, "milestones");
68007
+ if (!await pathExistsAsync(milestonesDir)) return null;
68008
+ try {
68009
+ 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();
68010
+ for (const archiveName of archiveDirs) {
68011
+ const versionMatch = archiveName.match(/^(v[\d.]+)-phases$/);
68012
+ if (!versionMatch) continue;
68013
+ const version = versionMatch[1];
68014
+ const result = await searchPhaseInDirAsync(node_path.default.join(milestonesDir, archiveName), node_path.default.join(".planning", "milestones", archiveName), normalized);
68015
+ if (result) {
68016
+ result.archived = version;
68017
+ return result;
68018
+ }
68019
+ }
68020
+ } catch (e) {
68021
+ debugLog("find-phase-async-milestone-search-failed", e);
68022
+ }
68023
+ return null;
68024
+ }
67954
68025
 
67955
68026
  //#endregion
67956
68027
  //#region ../../node_modules/yaml/dist/nodes/identity.js
@@ -74784,7 +74855,7 @@ async function cmdRoadmapAnalyze(cwd) {
74784
74855
  try {
74785
74856
  const dirMatch = allDirs.find((d) => d.startsWith(p.normalized + "-") || d === p.normalized);
74786
74857
  if (dirMatch) {
74787
- const phaseFiles = await node_fs.default.promises.readdir(node_path.default.join(phasesDir, dirMatch));
74858
+ const phaseFiles = await node_fs.promises.readdir(node_path.default.join(phasesDir, dirMatch));
74788
74859
  planCount = phaseFiles.filter((f) => isPlanFile(f)).length;
74789
74860
  summaryCount = phaseFiles.filter((f) => isSummaryFile(f)).length;
74790
74861
  hasContext = phaseFiles.some((f) => f.endsWith("-CONTEXT.md") || f === "CONTEXT.md");
@@ -74889,15 +74960,18 @@ function parseTodoFrontmatter(content) {
74889
74960
  *
74890
74961
  * Ported from maxsim/bin/lib/phase.cjs
74891
74962
  */
74892
- function scaffoldPhaseStubs(dirPath, phaseId, name) {
74963
+ async function scaffoldPhaseStubs(dirPath, phaseId, name) {
74893
74964
  const today = todayISO();
74894
- 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`);
74895
- 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`);
74965
+ 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`)]);
74896
74966
  }
74897
- function phaseAddCore(cwd, description, options) {
74967
+ async function phaseAddCore(cwd, description, options) {
74898
74968
  const rmPath = roadmapPath(cwd);
74899
- if (!node_fs.default.existsSync(rmPath)) throw new Error("ROADMAP.md not found");
74900
- const content = node_fs.default.readFileSync(rmPath, "utf-8");
74969
+ let content;
74970
+ try {
74971
+ content = await node_fs.promises.readFile(rmPath, "utf-8");
74972
+ } catch {
74973
+ throw new Error("ROADMAP.md not found");
74974
+ }
74901
74975
  const slug = generateSlugInternal(description);
74902
74976
  const phasePattern = getPhasePattern();
74903
74977
  let maxPhase = 0;
@@ -74910,15 +74984,15 @@ function phaseAddCore(cwd, description, options) {
74910
74984
  const paddedNum = String(newPhaseNum).padStart(2, "0");
74911
74985
  const dirName = `${paddedNum}-${slug}`;
74912
74986
  const dirPath = planningPath(cwd, "phases", dirName);
74913
- node_fs.default.mkdirSync(dirPath, { recursive: true });
74914
- node_fs.default.writeFileSync(node_path.default.join(dirPath, ".gitkeep"), "");
74915
- if (options?.includeStubs) scaffoldPhaseStubs(dirPath, paddedNum, description);
74987
+ await node_fs.promises.mkdir(dirPath, { recursive: true });
74988
+ await node_fs.promises.writeFile(node_path.default.join(dirPath, ".gitkeep"), "");
74989
+ if (options?.includeStubs) await scaffoldPhaseStubs(dirPath, paddedNum, description);
74916
74990
  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`;
74917
74991
  let updatedContent;
74918
74992
  const lastSeparator = content.lastIndexOf("\n---");
74919
74993
  if (lastSeparator > 0) updatedContent = content.slice(0, lastSeparator) + phaseEntry + content.slice(lastSeparator);
74920
74994
  else updatedContent = content + phaseEntry;
74921
- node_fs.default.writeFileSync(rmPath, updatedContent, "utf-8");
74995
+ await node_fs.promises.writeFile(rmPath, updatedContent, "utf-8");
74922
74996
  return {
74923
74997
  phase_number: newPhaseNum,
74924
74998
  padded: paddedNum,
@@ -74927,10 +75001,14 @@ function phaseAddCore(cwd, description, options) {
74927
75001
  description
74928
75002
  };
74929
75003
  }
74930
- function phaseInsertCore(cwd, afterPhase, description, options) {
75004
+ async function phaseInsertCore(cwd, afterPhase, description, options) {
74931
75005
  const rmPath = roadmapPath(cwd);
74932
- if (!node_fs.default.existsSync(rmPath)) throw new Error("ROADMAP.md not found");
74933
- const content = node_fs.default.readFileSync(rmPath, "utf-8");
75006
+ let content;
75007
+ try {
75008
+ content = await node_fs.promises.readFile(rmPath, "utf-8");
75009
+ } catch {
75010
+ throw new Error("ROADMAP.md not found");
75011
+ }
74934
75012
  const slug = generateSlugInternal(description);
74935
75013
  const afterPhaseEscaped = "0*" + normalizePhaseName(afterPhase).replace(/^0+/, "").replace(/\./g, "\\.");
74936
75014
  if (!getPhasePattern(afterPhaseEscaped, "i").test(content)) throw new Error(`Phase ${afterPhase} not found in ROADMAP.md`);
@@ -74938,7 +75016,7 @@ function phaseInsertCore(cwd, afterPhase, description, options) {
74938
75016
  const normalizedBase = normalizePhaseName(afterPhase);
74939
75017
  const existingDecimals = [];
74940
75018
  try {
74941
- const dirs = listSubDirs(phasesDirPath);
75019
+ const dirs = await listSubDirsAsync(phasesDirPath);
74942
75020
  const decimalPattern = new RegExp(`^${normalizedBase}\\.(\\d+)`);
74943
75021
  for (const dir of dirs) {
74944
75022
  const dm = dir.match(decimalPattern);
@@ -74950,9 +75028,9 @@ function phaseInsertCore(cwd, afterPhase, description, options) {
74950
75028
  const decimalPhase = `${normalizedBase}.${existingDecimals.length === 0 ? 1 : Math.max(...existingDecimals) + 1}`;
74951
75029
  const dirName = `${decimalPhase}-${slug}`;
74952
75030
  const dirPath = planningPath(cwd, "phases", dirName);
74953
- node_fs.default.mkdirSync(dirPath, { recursive: true });
74954
- node_fs.default.writeFileSync(node_path.default.join(dirPath, ".gitkeep"), "");
74955
- if (options?.includeStubs) scaffoldPhaseStubs(dirPath, decimalPhase, description);
75031
+ await node_fs.promises.mkdir(dirPath, { recursive: true });
75032
+ await node_fs.promises.writeFile(node_path.default.join(dirPath, ".gitkeep"), "");
75033
+ if (options?.includeStubs) await scaffoldPhaseStubs(dirPath, decimalPhase, description);
74956
75034
  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`;
74957
75035
  const headerPattern = new RegExp(`(#{2,4}\\s*Phase\\s+0*${afterPhaseEscaped}:[^\\n]*\\n)`, "i");
74958
75036
  const headerMatch = content.match(headerPattern);
@@ -74963,7 +75041,7 @@ function phaseInsertCore(cwd, afterPhase, description, options) {
74963
75041
  if (nextPhaseMatch) insertIdx = headerIdx + headerMatch[0].length + nextPhaseMatch.index;
74964
75042
  else insertIdx = content.length;
74965
75043
  const updatedContent = content.slice(0, insertIdx) + phaseEntry + content.slice(insertIdx);
74966
- node_fs.default.writeFileSync(rmPath, updatedContent, "utf-8");
75044
+ await node_fs.promises.writeFile(rmPath, updatedContent, "utf-8");
74967
75045
  return {
74968
75046
  phase_number: decimalPhase,
74969
75047
  after_phase: afterPhase,
@@ -74972,18 +75050,19 @@ function phaseInsertCore(cwd, afterPhase, description, options) {
74972
75050
  description
74973
75051
  };
74974
75052
  }
74975
- function phaseCompleteCore(cwd, phaseNum) {
75053
+ async function phaseCompleteCore(cwd, phaseNum) {
74976
75054
  const rmPath = roadmapPath(cwd);
74977
75055
  const stPath = statePath(cwd);
74978
75056
  const phasesDirPath = phasesPath(cwd);
74979
75057
  const today = todayISO();
74980
- const phaseInfo = findPhaseInternal(cwd, phaseNum);
75058
+ const phaseInfo = await findPhaseInternalAsync(cwd, phaseNum);
74981
75059
  if (!phaseInfo) throw new Error(`Phase ${phaseNum} not found`);
74982
75060
  const planCount = phaseInfo.plans.length;
74983
75061
  const summaryCount = phaseInfo.summaries.length;
74984
75062
  let requirementsUpdated = false;
74985
- if (node_fs.default.existsSync(rmPath)) {
74986
- let roadmapContent = node_fs.default.readFileSync(rmPath, "utf-8");
75063
+ const rmExists = await pathExistsAsync(rmPath);
75064
+ if (rmExists) {
75065
+ let roadmapContent = await node_fs.promises.readFile(rmPath, "utf-8");
74987
75066
  const checkboxPattern = new RegExp(`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${escapePhaseNum(phaseNum)}[:\\s][^\\n]*)`, "i");
74988
75067
  roadmapContent = roadmapContent.replace(checkboxPattern, `$1x$2 (completed ${today})`);
74989
75068
  const phaseEscaped = escapePhaseNum(phaseNum);
@@ -74992,20 +75071,20 @@ function phaseCompleteCore(cwd, phaseNum) {
74992
75071
  const planCountPattern = new RegExp(`(#{2,4}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`, "i");
74993
75072
  roadmapContent = roadmapContent.replace(planCountPattern, `$1${summaryCount}/${planCount} plans complete`);
74994
75073
  debugLog("phase-complete-write", `writing ROADMAP.md for phase ${phaseNum}`);
74995
- node_fs.default.writeFileSync(rmPath, roadmapContent, "utf-8");
75074
+ await node_fs.promises.writeFile(rmPath, roadmapContent, "utf-8");
74996
75075
  debugLog("phase-complete-write", `ROADMAP.md updated for phase ${phaseNum}`);
74997
75076
  const reqPath = planningPath(cwd, "REQUIREMENTS.md");
74998
- if (node_fs.default.existsSync(reqPath)) {
75077
+ if (await pathExistsAsync(reqPath)) {
74999
75078
  const reqMatch = roadmapContent.match(new RegExp(`Phase\\s+${escapePhaseNum(phaseNum)}[\\s\\S]*?\\*\\*Requirements:\\*\\*\\s*([^\\n]+)`, "i"));
75000
75079
  if (reqMatch) {
75001
75080
  const reqIds = reqMatch[1].replace(/[\[\]]/g, "").split(/[,\s]+/).map((r) => r.trim()).filter(Boolean);
75002
- let reqContent = node_fs.default.readFileSync(reqPath, "utf-8");
75081
+ let reqContent = await node_fs.promises.readFile(reqPath, "utf-8");
75003
75082
  for (const reqId of reqIds) {
75004
75083
  reqContent = reqContent.replace(new RegExp(`(-\\s*\\[)[ ](\\]\\s*\\*\\*${reqId}\\*\\*)`, "gi"), "$1x$2");
75005
75084
  reqContent = reqContent.replace(new RegExp(`(\\|\\s*${reqId}\\s*\\|[^|]+\\|)\\s*Pending\\s*(\\|)`, "gi"), "$1 Complete $2");
75006
75085
  }
75007
75086
  debugLog("phase-complete-write", `writing REQUIREMENTS.md for phase ${phaseNum}`);
75008
- node_fs.default.writeFileSync(reqPath, reqContent, "utf-8");
75087
+ await node_fs.promises.writeFile(reqPath, reqContent, "utf-8");
75009
75088
  debugLog("phase-complete-write", `REQUIREMENTS.md updated for phase ${phaseNum}`);
75010
75089
  requirementsUpdated = true;
75011
75090
  }
@@ -75015,7 +75094,7 @@ function phaseCompleteCore(cwd, phaseNum) {
75015
75094
  let nextPhaseName = null;
75016
75095
  let isLastPhase = true;
75017
75096
  try {
75018
- const dirs = listSubDirs(phasesDirPath, true);
75097
+ const dirs = await listSubDirsAsync(phasesDirPath, true);
75019
75098
  for (const dir of dirs) {
75020
75099
  const dm = dir.match(/^(\d+[A-Z]?(?:\.\d+)?)-?(.*)/i);
75021
75100
  if (dm) {
@@ -75030,8 +75109,9 @@ function phaseCompleteCore(cwd, phaseNum) {
75030
75109
  } catch (e) {
75031
75110
  debugLog("phase-complete-next-phase-scan-failed", e);
75032
75111
  }
75033
- if (node_fs.default.existsSync(stPath)) {
75034
- let stateContent = node_fs.default.readFileSync(stPath, "utf-8");
75112
+ const stExists = await pathExistsAsync(stPath);
75113
+ if (stExists) {
75114
+ let stateContent = await node_fs.promises.readFile(stPath, "utf-8");
75035
75115
  stateContent = stateContent.replace(/(\*\*Current Phase:\*\*\s*).*/, `$1${nextPhaseNum || phaseNum}`);
75036
75116
  if (nextPhaseName) stateContent = stateContent.replace(/(\*\*Current Phase Name:\*\*\s*).*/, `$1${nextPhaseName.replace(/-/g, " ")}`);
75037
75117
  stateContent = stateContent.replace(/(\*\*Status:\*\*\s*).*/, `$1${isLastPhase ? "Milestone complete" : "Ready to plan"}`);
@@ -75039,7 +75119,7 @@ function phaseCompleteCore(cwd, phaseNum) {
75039
75119
  stateContent = stateContent.replace(/(\*\*Last Activity:\*\*\s*).*/, `$1${today}`);
75040
75120
  stateContent = stateContent.replace(/(\*\*Last Activity Description:\*\*\s*).*/, `$1Phase ${phaseNum} complete${nextPhaseNum ? `, transitioned to Phase ${nextPhaseNum}` : ""}`);
75041
75121
  debugLog("phase-complete-write", `writing STATE.md for phase ${phaseNum}`);
75042
- node_fs.default.writeFileSync(stPath, stateContent, "utf-8");
75122
+ await node_fs.promises.writeFile(stPath, stateContent, "utf-8");
75043
75123
  debugLog("phase-complete-write", `STATE.md updated for phase ${phaseNum}`);
75044
75124
  }
75045
75125
  return {
@@ -75050,8 +75130,8 @@ function phaseCompleteCore(cwd, phaseNum) {
75050
75130
  next_phase_name: nextPhaseName,
75051
75131
  is_last_phase: isLastPhase,
75052
75132
  date: today,
75053
- roadmap_updated: node_fs.default.existsSync(rmPath),
75054
- state_updated: node_fs.default.existsSync(stPath),
75133
+ roadmap_updated: rmExists,
75134
+ state_updated: stExists,
75055
75135
  requirements_updated: requirementsUpdated
75056
75136
  };
75057
75137
  }
@@ -75097,6 +75177,36 @@ function addIfExists(files, cwd, relPath, role) {
75097
75177
  const entry = fileEntry(cwd, relPath, role);
75098
75178
  if (entry) files.push(entry);
75099
75179
  }
75180
+ const TOPIC_TO_CODEBASE_DOCS = {
75181
+ ui: ["CONVENTIONS.md", "STRUCTURE.md"],
75182
+ frontend: ["CONVENTIONS.md", "STRUCTURE.md"],
75183
+ component: ["CONVENTIONS.md", "STRUCTURE.md"],
75184
+ api: ["ARCHITECTURE.md", "CONVENTIONS.md"],
75185
+ backend: ["ARCHITECTURE.md", "CONVENTIONS.md"],
75186
+ server: ["ARCHITECTURE.md", "CONVENTIONS.md"],
75187
+ database: ["ARCHITECTURE.md", "STACK.md"],
75188
+ schema: ["ARCHITECTURE.md", "STACK.md"],
75189
+ data: ["ARCHITECTURE.md", "STACK.md"],
75190
+ testing: ["TESTING.md", "CONVENTIONS.md"],
75191
+ test: ["TESTING.md", "CONVENTIONS.md"],
75192
+ integration: ["INTEGRATIONS.md", "STACK.md"],
75193
+ deploy: ["INTEGRATIONS.md", "STACK.md"],
75194
+ refactor: ["CONCERNS.md", "ARCHITECTURE.md"],
75195
+ cleanup: ["CONCERNS.md", "ARCHITECTURE.md"],
75196
+ setup: ["STACK.md", "STRUCTURE.md"],
75197
+ config: ["STACK.md", "STRUCTURE.md"],
75198
+ auth: ["ARCHITECTURE.md", "INTEGRATIONS.md"],
75199
+ performance: ["ARCHITECTURE.md", "STACK.md"],
75200
+ install: ["STACK.md", "STRUCTURE.md"]
75201
+ };
75202
+ const DEFAULT_CODEBASE_DOCS = ["STACK.md", "ARCHITECTURE.md"];
75203
+ function selectCodebaseDocs(topic) {
75204
+ if (!topic) return DEFAULT_CODEBASE_DOCS;
75205
+ const topicLower = topic.toLowerCase();
75206
+ const matched = /* @__PURE__ */ new Set();
75207
+ for (const [keyword, docs] of Object.entries(TOPIC_TO_CODEBASE_DOCS)) if (topicLower.includes(keyword)) for (const doc of docs) matched.add(doc);
75208
+ return matched.size > 0 ? Array.from(matched) : DEFAULT_CODEBASE_DOCS;
75209
+ }
75100
75210
  function loadProjectContext(cwd) {
75101
75211
  const files = [];
75102
75212
  addIfExists(files, cwd, ".planning/PROJECT.md", "project-vision");
@@ -75145,6 +75255,16 @@ function loadArtefakteContext(cwd, phase) {
75145
75255
  }
75146
75256
  return files;
75147
75257
  }
75258
+ function loadCodebaseContext(cwd, topic) {
75259
+ const files = [];
75260
+ const codebaseDir = planningPath(cwd, "codebase");
75261
+ try {
75262
+ const existing = node_fs.default.readdirSync(codebaseDir).filter((f) => f.endsWith(".md"));
75263
+ const selected = selectCodebaseDocs(topic);
75264
+ for (const filename of selected) if (existing.includes(filename)) addIfExists(files, cwd, `.planning/codebase/${filename}`, `codebase-${filename.replace(".md", "").toLowerCase()}`);
75265
+ } catch {}
75266
+ return files;
75267
+ }
75148
75268
  function loadHistoryContext(cwd, currentPhase) {
75149
75269
  const files = [];
75150
75270
  const pd = phasesPath(cwd);
@@ -75168,6 +75288,8 @@ function cmdContextLoad(cwd, phase, topic, includeHistory) {
75168
75288
  allFiles.push(...loadProjectContext(cwd));
75169
75289
  allFiles.push(...loadRoadmapContext(cwd));
75170
75290
  allFiles.push(...loadArtefakteContext(cwd, phase));
75291
+ const selectedDocs = selectCodebaseDocs(topic);
75292
+ allFiles.push(...loadCodebaseContext(cwd, topic));
75171
75293
  if (phase) allFiles.push(...loadPhaseContext(cwd, phase));
75172
75294
  if (includeHistory) allFiles.push(...loadHistoryContext(cwd, phase));
75173
75295
  const seen = /* @__PURE__ */ new Set();
@@ -75180,7 +75302,8 @@ function cmdContextLoad(cwd, phase, topic, includeHistory) {
75180
75302
  files: deduped,
75181
75303
  total_size: deduped.reduce((sum, f) => sum + f.size, 0),
75182
75304
  phase: phase ?? null,
75183
- topic: topic ?? null
75305
+ topic: topic ?? null,
75306
+ codebase_docs_selected: selectedDocs
75184
75307
  });
75185
75308
  }
75186
75309
 
@@ -78896,7 +79019,7 @@ function registerPhaseTools(server) {
78896
79019
  const cwd = detectProjectRoot();
78897
79020
  if (!cwd) return mcpError("No .planning/ directory found", "Project not detected");
78898
79021
  if (!name || !name.trim()) return mcpError("Phase name must not be empty", "Validation failed");
78899
- const result = phaseAddCore(cwd, name, { includeStubs: true });
79022
+ const result = await phaseAddCore(cwd, name, { includeStubs: true });
78900
79023
  return mcpSuccess({
78901
79024
  phase_number: result.phase_number,
78902
79025
  padded: result.padded,
@@ -78916,7 +79039,7 @@ function registerPhaseTools(server) {
78916
79039
  const cwd = detectProjectRoot();
78917
79040
  if (!cwd) return mcpError("No .planning/ directory found", "Project not detected");
78918
79041
  if (!name || !name.trim()) return mcpError("Phase name must not be empty", "Validation failed");
78919
- const result = phaseInsertCore(cwd, after, name, { includeStubs: true });
79042
+ const result = await phaseInsertCore(cwd, after, name, { includeStubs: true });
78920
79043
  return mcpSuccess({
78921
79044
  phase_number: result.phase_number,
78922
79045
  after_phase: result.after_phase,
@@ -78932,7 +79055,7 @@ function registerPhaseTools(server) {
78932
79055
  try {
78933
79056
  const cwd = detectProjectRoot();
78934
79057
  if (!cwd) return mcpError("No .planning/ directory found", "Project not detected");
78935
- const result = phaseCompleteCore(cwd, phase);
79058
+ const result = await phaseCompleteCore(cwd, phase);
78936
79059
  return mcpSuccess({
78937
79060
  completed_phase: result.completed_phase,
78938
79061
  phase_name: result.phase_name,
@@ -79250,9 +79373,9 @@ function registerContextTools(server) {
79250
79373
  return mcpError("Failed: " + e.message, "Error occurred");
79251
79374
  }
79252
79375
  });
79253
- server.tool("mcp_get_context_for_task", "Load context files for a task, including project context, roadmap, and optionally phase-specific and topic-specific files.", {
79254
- phase: stringType().optional().describe("Optional phase number to scope context to"),
79255
- topic: stringType().optional().describe("Optional topic to filter context by")
79376
+ server.tool("mcp_get_context_for_task", "Load context files for a task. Includes project context, roadmap, artefakte, and codebase docs filtered by topic. Topic keywords select relevant codebase docs: \"ui/frontend\" loads CONVENTIONS+STRUCTURE, \"api/backend\" loads ARCHITECTURE+CONVENTIONS, \"testing\" loads TESTING+CONVENTIONS, \"database\" loads ARCHITECTURE+STACK, \"refactor\" loads CONCERNS+ARCHITECTURE. Without topic, defaults to STACK+ARCHITECTURE.", {
79377
+ phase: stringType().optional().describe("Phase number to scope context to"),
79378
+ topic: stringType().optional().describe("Topic keywords to filter codebase docs (e.g. \"frontend\", \"api\", \"testing\", \"database\", \"refactor\")")
79256
79379
  }, async ({ phase, topic }) => {
79257
79380
  try {
79258
79381
  const cwd = detectProjectRoot();