maxsimcli 4.1.0 → 4.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -5
- package/dist/.tsbuildinfo +1 -1
- package/dist/assets/CHANGELOG.md +32 -0
- package/dist/assets/dashboard/client/assets/index-C199D4Eb.css +32 -0
- package/dist/assets/dashboard/client/assets/{index-C_eAetZJ.js → index-nAXJLp0_.js} +61 -59
- package/dist/assets/dashboard/client/index.html +2 -2
- package/dist/assets/dashboard/server.js +26 -11
- package/dist/assets/templates/agents/AGENTS.md +18 -69
- package/dist/assets/templates/agents/maxsim-code-reviewer.md +17 -92
- package/dist/assets/templates/agents/maxsim-codebase-mapper.md +57 -694
- package/dist/assets/templates/agents/maxsim-debugger.md +80 -925
- package/dist/assets/templates/agents/maxsim-executor.md +94 -431
- package/dist/assets/templates/agents/maxsim-integration-checker.md +51 -319
- package/dist/assets/templates/agents/maxsim-phase-researcher.md +63 -429
- package/dist/assets/templates/agents/maxsim-plan-checker.md +79 -568
- package/dist/assets/templates/agents/maxsim-planner.md +125 -855
- package/dist/assets/templates/agents/maxsim-project-researcher.md +32 -472
- package/dist/assets/templates/agents/maxsim-research-synthesizer.md +25 -134
- package/dist/assets/templates/agents/maxsim-roadmapper.md +66 -480
- package/dist/assets/templates/agents/maxsim-spec-reviewer.md +13 -55
- package/dist/assets/templates/agents/maxsim-verifier.md +95 -450
- package/dist/assets/templates/commands/maxsim/artefakte.md +122 -0
- package/dist/assets/templates/commands/maxsim/batch.md +42 -0
- package/dist/assets/templates/commands/maxsim/check-todos.md +1 -0
- package/dist/assets/templates/commands/maxsim/sdd.md +39 -0
- package/dist/assets/templates/references/thinking-partner.md +33 -0
- package/dist/assets/templates/workflows/batch.md +420 -0
- package/dist/assets/templates/workflows/check-todos.md +85 -1
- package/dist/assets/templates/workflows/discuss-phase.md +31 -0
- package/dist/assets/templates/workflows/execute-plan.md +96 -27
- package/dist/assets/templates/workflows/help.md +47 -0
- package/dist/assets/templates/workflows/sdd.md +426 -0
- package/dist/backend-server.cjs +174 -51
- package/dist/backend-server.cjs.map +1 -1
- package/dist/cli.cjs +310 -146
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +5 -5
- package/dist/cli.js.map +1 -1
- package/dist/core/artefakte.d.ts.map +1 -1
- package/dist/core/artefakte.js +16 -0
- package/dist/core/artefakte.js.map +1 -1
- package/dist/core/context-loader.d.ts +1 -0
- package/dist/core/context-loader.d.ts.map +1 -1
- package/dist/core/context-loader.js +58 -0
- package/dist/core/context-loader.js.map +1 -1
- package/dist/core/core.d.ts +6 -0
- package/dist/core/core.d.ts.map +1 -1
- package/dist/core/core.js +238 -0
- package/dist/core/core.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +5 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/phase.d.ts +11 -11
- package/dist/core/phase.d.ts.map +1 -1
- package/dist/core/phase.js +88 -73
- package/dist/core/phase.js.map +1 -1
- package/dist/core/roadmap.d.ts +2 -2
- package/dist/core/roadmap.d.ts.map +1 -1
- package/dist/core/roadmap.js +11 -10
- package/dist/core/roadmap.js.map +1 -1
- package/dist/core/state.d.ts +11 -11
- package/dist/core/state.d.ts.map +1 -1
- package/dist/core/state.js +60 -54
- package/dist/core/state.js.map +1 -1
- package/dist/core-RRjCSt0G.cjs.map +1 -1
- package/dist/{lifecycle-D4E9yP6E.cjs → lifecycle-0M4VqOMm.cjs} +2 -2
- package/dist/{lifecycle-D4E9yP6E.cjs.map → lifecycle-0M4VqOMm.cjs.map} +1 -1
- package/dist/mcp/context-tools.d.ts.map +1 -1
- package/dist/mcp/context-tools.js +7 -3
- package/dist/mcp/context-tools.js.map +1 -1
- package/dist/mcp/phase-tools.js +3 -3
- package/dist/mcp/phase-tools.js.map +1 -1
- package/dist/mcp-server.cjs +163 -40
- package/dist/mcp-server.cjs.map +1 -1
- package/dist/{server-pvY2WbKj.cjs → server-G1MIg_Oe.cjs} +7 -7
- package/dist/server-G1MIg_Oe.cjs.map +1 -0
- package/package.json +1 -1
- package/dist/assets/dashboard/client/assets/index-CmiJKqOU.css +0 -32
- package/dist/server-pvY2WbKj.cjs.map +0 -1
package/dist/backend-server.cjs
CHANGED
|
@@ -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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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.
|
|
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.
|
|
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
|
-
|
|
74900
|
-
|
|
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.
|
|
74914
|
-
node_fs.
|
|
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.
|
|
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
|
-
|
|
74933
|
-
|
|
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 =
|
|
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.
|
|
74954
|
-
node_fs.
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
74986
|
-
|
|
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.
|
|
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 (
|
|
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.
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
75034
|
-
|
|
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.
|
|
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:
|
|
75054
|
-
state_updated:
|
|
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
|
|
79254
|
-
phase: stringType().optional().describe("
|
|
79255
|
-
topic: stringType().optional().describe("
|
|
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();
|