maxsimcli 4.7.0 → 4.7.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/dist/assets/CHANGELOG.md +23 -0
- package/dist/cli.cjs +470 -961
- package/dist/cli.cjs.map +1 -1
- package/dist/{core-RRjCSt0G.cjs → core-D5zUr9cb.cjs} +4 -3
- package/dist/core-D5zUr9cb.cjs.map +1 -0
- package/dist/install.cjs +3 -195
- package/dist/install.cjs.map +1 -1
- package/dist/mcp-server.cjs +138 -201
- package/dist/mcp-server.cjs.map +1 -1
- package/dist/{skills-MYlMkYNt.cjs → skills-CjFWZIGM.cjs} +6 -6
- package/dist/{skills-MYlMkYNt.cjs.map → skills-CjFWZIGM.cjs.map} +1 -1
- package/package.json +1 -7
- package/dist/assets/dashboard/client/assets/index-C199D4Eb.css +0 -32
- package/dist/assets/dashboard/client/assets/index-nAXJLp0_.js +0 -233
- package/dist/assets/dashboard/client/index.html +0 -19
- package/dist/assets/dashboard/server.js +0 -78813
- package/dist/backend-server.cjs +0 -83370
- package/dist/backend-server.cjs.map +0 -1
- package/dist/core-RRjCSt0G.cjs.map +0 -1
- package/dist/esm-iIOBzmdz.cjs +0 -1561
- package/dist/esm-iIOBzmdz.cjs.map +0 -1
- package/dist/lifecycle-D8mcsEjy.cjs +0 -136
- package/dist/lifecycle-D8mcsEjy.cjs.map +0 -1
- package/dist/server-BAHfh_vw.cjs +0 -5694
- package/dist/server-BAHfh_vw.cjs.map +0 -1
package/dist/mcp-server.cjs
CHANGED
|
@@ -26771,17 +26771,12 @@ const isSummaryFile = (f) => f.endsWith("-SUMMARY.md") || f === "SUMMARY.md";
|
|
|
26771
26771
|
const planId = (f) => f.replace("-PLAN.md", "").replace("PLAN.md", "");
|
|
26772
26772
|
const summaryId = (f) => f.replace("-SUMMARY.md", "").replace("SUMMARY.md", "");
|
|
26773
26773
|
/** List subdirectory names, optionally sorted by phase number. */
|
|
26774
|
-
function listSubDirs(dir, sortByPhase = false) {
|
|
26775
|
-
const dirs = node_fs.default.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
26776
|
-
return sortByPhase ? dirs.sort((a, b) => comparePhaseNum(a, b)) : dirs;
|
|
26777
|
-
}
|
|
26778
|
-
/** Async version of listSubDirs using fs.promises. */
|
|
26779
|
-
async function listSubDirsAsync(dir, sortByPhase = false) {
|
|
26774
|
+
async function listSubDirs(dir, sortByPhase = false) {
|
|
26780
26775
|
const dirs = (await node_fs.promises.readdir(dir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
26781
26776
|
return sortByPhase ? dirs.sort((a, b) => comparePhaseNum(a, b)) : dirs;
|
|
26782
26777
|
}
|
|
26783
|
-
/**
|
|
26784
|
-
async function
|
|
26778
|
+
/** Read a file, returning null if it doesn't exist or fails. */
|
|
26779
|
+
async function safeReadFile(filePath) {
|
|
26785
26780
|
try {
|
|
26786
26781
|
return await node_fs.promises.readFile(filePath, "utf-8");
|
|
26787
26782
|
} catch {
|
|
@@ -26802,15 +26797,65 @@ function debugLog(contextOrError, error) {
|
|
|
26802
26797
|
function escapePhaseNum(phase) {
|
|
26803
26798
|
return String(phase).replace(/\./g, "\\.");
|
|
26804
26799
|
}
|
|
26805
|
-
|
|
26800
|
+
let _configCache = null;
|
|
26801
|
+
function normalizePhaseName(phase) {
|
|
26802
|
+
const match = phase.match(/^(\d+)([A-Z])?(\.\d+)?/i);
|
|
26803
|
+
if (!match) return phase;
|
|
26804
|
+
const padded = match[1].padStart(2, "0");
|
|
26805
|
+
const letter = match[2] ? match[2].toUpperCase() : "";
|
|
26806
|
+
const decimal = match[3] || "";
|
|
26807
|
+
return padded + letter + decimal;
|
|
26808
|
+
}
|
|
26809
|
+
function comparePhaseNum(a, b) {
|
|
26810
|
+
const pa = String(a).match(/^(\d+)([A-Z])?(\.\d+)?/i);
|
|
26811
|
+
const pb = String(b).match(/^(\d+)([A-Z])?(\.\d+)?/i);
|
|
26812
|
+
if (!pa || !pb) return String(a).localeCompare(String(b));
|
|
26813
|
+
const intDiff = parseInt(pa[1], 10) - parseInt(pb[1], 10);
|
|
26814
|
+
if (intDiff !== 0) return intDiff;
|
|
26815
|
+
const la = (pa[2] || "").toUpperCase();
|
|
26816
|
+
const lb = (pb[2] || "").toUpperCase();
|
|
26817
|
+
if (la !== lb) {
|
|
26818
|
+
if (!la) return -1;
|
|
26819
|
+
if (!lb) return 1;
|
|
26820
|
+
return la < lb ? -1 : 1;
|
|
26821
|
+
}
|
|
26822
|
+
return (pa[3] ? parseFloat(pa[3]) : -1) - (pb[3] ? parseFloat(pb[3]) : -1);
|
|
26823
|
+
}
|
|
26824
|
+
/**
|
|
26825
|
+
* Returns the canonical regex for matching Phase heading lines in ROADMAP.md.
|
|
26826
|
+
*
|
|
26827
|
+
* General form (no escapedPhaseNum):
|
|
26828
|
+
* Matches: ## Phase 03: Name Here
|
|
26829
|
+
* Group 1: phase number string (e.g. "03", "3A", "2.1")
|
|
26830
|
+
* Group 2: phase name string (e.g. "Name Here")
|
|
26831
|
+
*
|
|
26832
|
+
* Specific form (with escapedPhaseNum):
|
|
26833
|
+
* Matches: ## Phase 03: Name Here
|
|
26834
|
+
* Group 1: phase name string only
|
|
26835
|
+
*
|
|
26836
|
+
* @param escapedPhaseNum - regex-escaped phase number string to match a specific phase
|
|
26837
|
+
* @param flags - regex flags (default: 'gi')
|
|
26838
|
+
*/
|
|
26839
|
+
function getPhasePattern(escapedPhaseNum, flags = "gim") {
|
|
26840
|
+
if (escapedPhaseNum) return new RegExp(`^#{2,4}\\s*Phase\\s+${escapedPhaseNum}:\\s*([^\\n]+)`, flags);
|
|
26841
|
+
return new RegExp(`^#{2,4}\\s*Phase\\s+(\\d+[A-Z]?(?:\\.\\d+)?)\\s*:\\s*([^\\n]+)`, flags);
|
|
26842
|
+
}
|
|
26843
|
+
function generateSlugInternal(text) {
|
|
26844
|
+
if (!text) return null;
|
|
26845
|
+
return (0, import_slugify.default)(text, {
|
|
26846
|
+
lower: true,
|
|
26847
|
+
strict: true
|
|
26848
|
+
});
|
|
26849
|
+
}
|
|
26850
|
+
async function pathExistsInternal(p) {
|
|
26806
26851
|
try {
|
|
26807
|
-
|
|
26852
|
+
await node_fs.promises.access(p);
|
|
26853
|
+
return true;
|
|
26808
26854
|
} catch {
|
|
26809
|
-
return
|
|
26855
|
+
return false;
|
|
26810
26856
|
}
|
|
26811
26857
|
}
|
|
26812
|
-
|
|
26813
|
-
function loadConfig(cwd) {
|
|
26858
|
+
async function loadConfig(cwd) {
|
|
26814
26859
|
if (_configCache && _configCache.cwd === cwd) return _configCache.config;
|
|
26815
26860
|
const cfgPath = configPath(cwd);
|
|
26816
26861
|
const defaults = {
|
|
@@ -26827,7 +26872,7 @@ function loadConfig(cwd) {
|
|
|
26827
26872
|
brave_search: false
|
|
26828
26873
|
};
|
|
26829
26874
|
try {
|
|
26830
|
-
const raw = node_fs.
|
|
26875
|
+
const raw = await node_fs.promises.readFile(cfgPath, "utf-8");
|
|
26831
26876
|
const parsed = JSON.parse(raw);
|
|
26832
26877
|
const get = (key, nested) => {
|
|
26833
26878
|
if (parsed[key] !== void 0) return parsed[key];
|
|
@@ -26889,7 +26934,7 @@ function loadConfig(cwd) {
|
|
|
26889
26934
|
};
|
|
26890
26935
|
return result;
|
|
26891
26936
|
} catch (e) {
|
|
26892
|
-
if (
|
|
26937
|
+
if (await pathExistsInternal(cfgPath)) {
|
|
26893
26938
|
console.warn(`[maxsim] Warning: config.json exists but could not be parsed — using defaults.`);
|
|
26894
26939
|
debugLog("config-load-failed", e);
|
|
26895
26940
|
}
|
|
@@ -26900,57 +26945,15 @@ function loadConfig(cwd) {
|
|
|
26900
26945
|
return defaults;
|
|
26901
26946
|
}
|
|
26902
26947
|
}
|
|
26903
|
-
function
|
|
26904
|
-
const match = phase.match(/^(\d+)([A-Z])?(\.\d+)?/i);
|
|
26905
|
-
if (!match) return phase;
|
|
26906
|
-
const padded = match[1].padStart(2, "0");
|
|
26907
|
-
const letter = match[2] ? match[2].toUpperCase() : "";
|
|
26908
|
-
const decimal = match[3] || "";
|
|
26909
|
-
return padded + letter + decimal;
|
|
26910
|
-
}
|
|
26911
|
-
function comparePhaseNum(a, b) {
|
|
26912
|
-
const pa = String(a).match(/^(\d+)([A-Z])?(\.\d+)?/i);
|
|
26913
|
-
const pb = String(b).match(/^(\d+)([A-Z])?(\.\d+)?/i);
|
|
26914
|
-
if (!pa || !pb) return String(a).localeCompare(String(b));
|
|
26915
|
-
const intDiff = parseInt(pa[1], 10) - parseInt(pb[1], 10);
|
|
26916
|
-
if (intDiff !== 0) return intDiff;
|
|
26917
|
-
const la = (pa[2] || "").toUpperCase();
|
|
26918
|
-
const lb = (pb[2] || "").toUpperCase();
|
|
26919
|
-
if (la !== lb) {
|
|
26920
|
-
if (!la) return -1;
|
|
26921
|
-
if (!lb) return 1;
|
|
26922
|
-
return la < lb ? -1 : 1;
|
|
26923
|
-
}
|
|
26924
|
-
return (pa[3] ? parseFloat(pa[3]) : -1) - (pb[3] ? parseFloat(pb[3]) : -1);
|
|
26925
|
-
}
|
|
26926
|
-
/**
|
|
26927
|
-
* Returns the canonical regex for matching Phase heading lines in ROADMAP.md.
|
|
26928
|
-
*
|
|
26929
|
-
* General form (no escapedPhaseNum):
|
|
26930
|
-
* Matches: ## Phase 03: Name Here
|
|
26931
|
-
* Group 1: phase number string (e.g. "03", "3A", "2.1")
|
|
26932
|
-
* Group 2: phase name string (e.g. "Name Here")
|
|
26933
|
-
*
|
|
26934
|
-
* Specific form (with escapedPhaseNum):
|
|
26935
|
-
* Matches: ## Phase 03: Name Here
|
|
26936
|
-
* Group 1: phase name string only
|
|
26937
|
-
*
|
|
26938
|
-
* @param escapedPhaseNum - regex-escaped phase number string to match a specific phase
|
|
26939
|
-
* @param flags - regex flags (default: 'gi')
|
|
26940
|
-
*/
|
|
26941
|
-
function getPhasePattern(escapedPhaseNum, flags = "gim") {
|
|
26942
|
-
if (escapedPhaseNum) return new RegExp(`^#{2,4}\\s*Phase\\s+${escapedPhaseNum}:\\s*([^\\n]+)`, flags);
|
|
26943
|
-
return new RegExp(`^#{2,4}\\s*Phase\\s+(\\d+[A-Z]?(?:\\.\\d+)?)\\s*:\\s*([^\\n]+)`, flags);
|
|
26944
|
-
}
|
|
26945
|
-
function searchPhaseInDir(baseDir, relBase, normalized) {
|
|
26948
|
+
async function searchPhaseInDir(baseDir, relBase, normalized) {
|
|
26946
26949
|
try {
|
|
26947
|
-
const match = listSubDirs(baseDir, true).find((d) => d.startsWith(normalized));
|
|
26950
|
+
const match = (await listSubDirs(baseDir, true)).find((d) => d.startsWith(normalized));
|
|
26948
26951
|
if (!match) return null;
|
|
26949
26952
|
const dirMatch = match.match(/^(\d+[A-Z]?(?:\.\d+)?)-?(.*)/i);
|
|
26950
26953
|
const phaseNumber = dirMatch ? dirMatch[1] : normalized;
|
|
26951
26954
|
const phaseName = dirMatch && dirMatch[2] ? dirMatch[2] : null;
|
|
26952
26955
|
const phaseDir = node_path.default.join(baseDir, match);
|
|
26953
|
-
const phaseFiles = node_fs.
|
|
26956
|
+
const phaseFiles = await node_fs.promises.readdir(phaseDir);
|
|
26954
26957
|
const plans = phaseFiles.filter(isPlanFile).sort();
|
|
26955
26958
|
const summaries = phaseFiles.filter(isSummaryFile).sort();
|
|
26956
26959
|
const hasResearch = phaseFiles.some((f) => f.endsWith("-RESEARCH.md") || f === "RESEARCH.md");
|
|
@@ -26972,7 +26975,7 @@ function searchPhaseInDir(baseDir, relBase, normalized) {
|
|
|
26972
26975
|
has_verification: hasVerification
|
|
26973
26976
|
};
|
|
26974
26977
|
} catch (e) {
|
|
26975
|
-
debugLog("search-phase-in-dir-failed", {
|
|
26978
|
+
debugLog("search-phase-in-dir-async-failed", {
|
|
26976
26979
|
dir: baseDir,
|
|
26977
26980
|
phase: normalized,
|
|
26978
26981
|
error: errorMsg(e)
|
|
@@ -26980,148 +26983,82 @@ function searchPhaseInDir(baseDir, relBase, normalized) {
|
|
|
26980
26983
|
return null;
|
|
26981
26984
|
}
|
|
26982
26985
|
}
|
|
26983
|
-
function findPhaseInternal(cwd, phase) {
|
|
26986
|
+
async function findPhaseInternal(cwd, phase) {
|
|
26984
26987
|
if (!phase) return null;
|
|
26985
26988
|
const pd = phasesPath(cwd);
|
|
26986
26989
|
const normalized = normalizePhaseName(phase);
|
|
26987
|
-
const current = searchPhaseInDir(pd, node_path.default.join(".planning", "phases"), normalized);
|
|
26990
|
+
const current = await searchPhaseInDir(pd, node_path.default.join(".planning", "phases"), normalized);
|
|
26988
26991
|
if (current) return current;
|
|
26989
|
-
const
|
|
26990
|
-
try {
|
|
26991
|
-
node_fs.
|
|
26992
|
-
|
|
26993
|
-
|
|
26992
|
+
const archiveDir = planningPath(cwd, "archive");
|
|
26993
|
+
if (await pathExistsInternal(archiveDir)) try {
|
|
26994
|
+
const versionDirs = (await node_fs.promises.readdir(archiveDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name).sort().reverse();
|
|
26995
|
+
for (const versionName of versionDirs) {
|
|
26996
|
+
const result = await searchPhaseInDir(node_path.default.join(archiveDir, versionName), node_path.default.join(".planning", "archive", versionName), normalized);
|
|
26997
|
+
if (result) {
|
|
26998
|
+
result.archived = versionName;
|
|
26999
|
+
return result;
|
|
27000
|
+
}
|
|
27001
|
+
}
|
|
27002
|
+
} catch (e) {
|
|
27003
|
+
debugLog("find-phase-async-archive-search-failed", e);
|
|
26994
27004
|
}
|
|
27005
|
+
const milestonesDir = planningPath(cwd, "milestones");
|
|
27006
|
+
if (!await pathExistsInternal(milestonesDir)) return null;
|
|
26995
27007
|
try {
|
|
26996
|
-
const archiveDirs = node_fs.
|
|
27008
|
+
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();
|
|
26997
27009
|
for (const archiveName of archiveDirs) {
|
|
26998
27010
|
const versionMatch = archiveName.match(/^(v[\d.]+)-phases$/);
|
|
26999
27011
|
if (!versionMatch) continue;
|
|
27000
27012
|
const version = versionMatch[1];
|
|
27001
|
-
const result = searchPhaseInDir(node_path.default.join(milestonesDir, archiveName), node_path.default.join(".planning", "milestones", archiveName), normalized);
|
|
27013
|
+
const result = await searchPhaseInDir(node_path.default.join(milestonesDir, archiveName), node_path.default.join(".planning", "milestones", archiveName), normalized);
|
|
27002
27014
|
if (result) {
|
|
27003
27015
|
result.archived = version;
|
|
27004
27016
|
return result;
|
|
27005
27017
|
}
|
|
27006
27018
|
}
|
|
27007
27019
|
} catch (e) {
|
|
27008
|
-
debugLog("find-phase-milestone-search-failed", e);
|
|
27020
|
+
debugLog("find-phase-async-milestone-search-failed", e);
|
|
27009
27021
|
}
|
|
27010
27022
|
return null;
|
|
27011
27023
|
}
|
|
27012
|
-
function getArchivedPhaseDirs(cwd) {
|
|
27013
|
-
const milestonesDir = planningPath(cwd, "milestones");
|
|
27024
|
+
async function getArchivedPhaseDirs(cwd) {
|
|
27014
27025
|
const results = [];
|
|
27026
|
+
const archiveDir = planningPath(cwd, "archive");
|
|
27015
27027
|
try {
|
|
27016
|
-
const
|
|
27017
|
-
for (const
|
|
27018
|
-
const
|
|
27019
|
-
|
|
27020
|
-
const version = versionMatch[1];
|
|
27021
|
-
const archivePath = node_path.default.join(milestonesDir, archiveName);
|
|
27022
|
-
const dirs = listSubDirs(archivePath, true);
|
|
27028
|
+
const versionDirs = (await node_fs.promises.readdir(archiveDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name).sort().reverse();
|
|
27029
|
+
for (const versionName of versionDirs) {
|
|
27030
|
+
const versionPath = node_path.default.join(archiveDir, versionName);
|
|
27031
|
+
const dirs = await listSubDirs(versionPath, true);
|
|
27023
27032
|
for (const dir of dirs) results.push({
|
|
27024
27033
|
name: dir,
|
|
27025
|
-
milestone:
|
|
27026
|
-
basePath: node_path.default.join(".planning", "
|
|
27027
|
-
fullPath: node_path.default.join(
|
|
27034
|
+
milestone: versionName,
|
|
27035
|
+
basePath: node_path.default.join(".planning", "archive", versionName),
|
|
27036
|
+
fullPath: node_path.default.join(versionPath, dir)
|
|
27028
27037
|
});
|
|
27029
27038
|
}
|
|
27030
27039
|
} catch (e) {
|
|
27031
|
-
debugLog("get-archived-phase-dirs-failed", e);
|
|
27032
|
-
}
|
|
27033
|
-
return results;
|
|
27034
|
-
}
|
|
27035
|
-
function generateSlugInternal(text) {
|
|
27036
|
-
if (!text) return null;
|
|
27037
|
-
return (0, import_slugify.default)(text, {
|
|
27038
|
-
lower: true,
|
|
27039
|
-
strict: true
|
|
27040
|
-
});
|
|
27041
|
-
}
|
|
27042
|
-
async function pathExistsAsync(p) {
|
|
27043
|
-
try {
|
|
27044
|
-
await node_fs.promises.access(p);
|
|
27045
|
-
return true;
|
|
27046
|
-
} catch {
|
|
27047
|
-
return false;
|
|
27048
|
-
}
|
|
27049
|
-
}
|
|
27050
|
-
async function searchPhaseInDirAsync(baseDir, relBase, normalized) {
|
|
27051
|
-
try {
|
|
27052
|
-
const match = (await listSubDirsAsync(baseDir, true)).find((d) => d.startsWith(normalized));
|
|
27053
|
-
if (!match) return null;
|
|
27054
|
-
const dirMatch = match.match(/^(\d+[A-Z]?(?:\.\d+)?)-?(.*)/i);
|
|
27055
|
-
const phaseNumber = dirMatch ? dirMatch[1] : normalized;
|
|
27056
|
-
const phaseName = dirMatch && dirMatch[2] ? dirMatch[2] : null;
|
|
27057
|
-
const phaseDir = node_path.default.join(baseDir, match);
|
|
27058
|
-
const phaseFiles = await node_fs.promises.readdir(phaseDir);
|
|
27059
|
-
const plans = phaseFiles.filter(isPlanFile).sort();
|
|
27060
|
-
const summaries = phaseFiles.filter(isSummaryFile).sort();
|
|
27061
|
-
const hasResearch = phaseFiles.some((f) => f.endsWith("-RESEARCH.md") || f === "RESEARCH.md");
|
|
27062
|
-
const hasContext = phaseFiles.some((f) => f.endsWith("-CONTEXT.md") || f === "CONTEXT.md");
|
|
27063
|
-
const hasVerification = phaseFiles.some((f) => f.endsWith("-VERIFICATION.md") || f === "VERIFICATION.md");
|
|
27064
|
-
const completedPlanIds = new Set(summaries.map(summaryId));
|
|
27065
|
-
const incompletePlans = plans.filter((p) => !completedPlanIds.has(planId(p)));
|
|
27066
|
-
return {
|
|
27067
|
-
found: true,
|
|
27068
|
-
directory: node_path.default.join(relBase, match),
|
|
27069
|
-
phase_number: phaseNumber,
|
|
27070
|
-
phase_name: phaseName,
|
|
27071
|
-
phase_slug: phaseName ? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") : null,
|
|
27072
|
-
plans,
|
|
27073
|
-
summaries,
|
|
27074
|
-
incomplete_plans: incompletePlans,
|
|
27075
|
-
has_research: hasResearch,
|
|
27076
|
-
has_context: hasContext,
|
|
27077
|
-
has_verification: hasVerification
|
|
27078
|
-
};
|
|
27079
|
-
} catch (e) {
|
|
27080
|
-
debugLog("search-phase-in-dir-async-failed", {
|
|
27081
|
-
dir: baseDir,
|
|
27082
|
-
phase: normalized,
|
|
27083
|
-
error: errorMsg(e)
|
|
27084
|
-
});
|
|
27085
|
-
return null;
|
|
27086
|
-
}
|
|
27087
|
-
}
|
|
27088
|
-
async function findPhaseInternalAsync(cwd, phase) {
|
|
27089
|
-
if (!phase) return null;
|
|
27090
|
-
const pd = phasesPath(cwd);
|
|
27091
|
-
const normalized = normalizePhaseName(phase);
|
|
27092
|
-
const current = await searchPhaseInDirAsync(pd, node_path.default.join(".planning", "phases"), normalized);
|
|
27093
|
-
if (current) return current;
|
|
27094
|
-
const archiveDir = planningPath(cwd, "archive");
|
|
27095
|
-
if (await pathExistsAsync(archiveDir)) try {
|
|
27096
|
-
const versionDirs = (await node_fs.promises.readdir(archiveDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name).sort().reverse();
|
|
27097
|
-
for (const versionName of versionDirs) {
|
|
27098
|
-
const result = await searchPhaseInDirAsync(node_path.default.join(archiveDir, versionName), node_path.default.join(".planning", "archive", versionName), normalized);
|
|
27099
|
-
if (result) {
|
|
27100
|
-
result.archived = versionName;
|
|
27101
|
-
return result;
|
|
27102
|
-
}
|
|
27103
|
-
}
|
|
27104
|
-
} catch (e) {
|
|
27105
|
-
debugLog("find-phase-async-archive-search-failed", e);
|
|
27040
|
+
debugLog("get-archived-phase-dirs-async-archive-failed", e);
|
|
27106
27041
|
}
|
|
27107
27042
|
const milestonesDir = planningPath(cwd, "milestones");
|
|
27108
|
-
if (!await pathExistsAsync(milestonesDir)) return null;
|
|
27109
27043
|
try {
|
|
27110
|
-
const
|
|
27111
|
-
for (const archiveName of
|
|
27044
|
+
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();
|
|
27045
|
+
for (const archiveName of phaseDirs) {
|
|
27112
27046
|
const versionMatch = archiveName.match(/^(v[\d.]+)-phases$/);
|
|
27113
27047
|
if (!versionMatch) continue;
|
|
27114
27048
|
const version = versionMatch[1];
|
|
27115
|
-
const
|
|
27116
|
-
|
|
27117
|
-
|
|
27118
|
-
|
|
27119
|
-
|
|
27049
|
+
const archiveMilestonePath = node_path.default.join(milestonesDir, archiveName);
|
|
27050
|
+
const dirs = await listSubDirs(archiveMilestonePath, true);
|
|
27051
|
+
for (const dir of dirs) results.push({
|
|
27052
|
+
name: dir,
|
|
27053
|
+
milestone: version,
|
|
27054
|
+
basePath: node_path.default.join(".planning", "milestones", archiveName),
|
|
27055
|
+
fullPath: node_path.default.join(archiveMilestonePath, dir)
|
|
27056
|
+
});
|
|
27120
27057
|
}
|
|
27121
27058
|
} catch (e) {
|
|
27122
|
-
debugLog("
|
|
27059
|
+
debugLog("get-archived-phase-dirs-async-failed", e);
|
|
27123
27060
|
}
|
|
27124
|
-
return
|
|
27061
|
+
return results;
|
|
27125
27062
|
}
|
|
27126
27063
|
|
|
27127
27064
|
//#endregion
|
|
@@ -33862,7 +33799,7 @@ async function phaseInsertCore(cwd, afterPhase, description, options) {
|
|
|
33862
33799
|
const normalizedBase = normalizePhaseName(afterPhase);
|
|
33863
33800
|
const existingDecimals = [];
|
|
33864
33801
|
try {
|
|
33865
|
-
const dirs = await
|
|
33802
|
+
const dirs = await listSubDirs(phasesDirPath);
|
|
33866
33803
|
const decimalPattern = new RegExp(`^${normalizedBase}\\.(\\d+)`);
|
|
33867
33804
|
for (const dir of dirs) {
|
|
33868
33805
|
const dm = dir.match(decimalPattern);
|
|
@@ -33901,12 +33838,12 @@ async function phaseCompleteCore(cwd, phaseNum) {
|
|
|
33901
33838
|
const stPath = statePath(cwd);
|
|
33902
33839
|
const phasesDirPath = phasesPath(cwd);
|
|
33903
33840
|
const today = todayISO();
|
|
33904
|
-
const phaseInfo = await
|
|
33841
|
+
const phaseInfo = await findPhaseInternal(cwd, phaseNum);
|
|
33905
33842
|
if (!phaseInfo) throw new Error(`Phase ${phaseNum} not found`);
|
|
33906
33843
|
const planCount = phaseInfo.plans.length;
|
|
33907
33844
|
const summaryCount = phaseInfo.summaries.length;
|
|
33908
33845
|
let requirementsUpdated = false;
|
|
33909
|
-
const rmExists = await
|
|
33846
|
+
const rmExists = await pathExistsInternal(rmPath);
|
|
33910
33847
|
if (rmExists) {
|
|
33911
33848
|
let roadmapContent = await node_fs.promises.readFile(rmPath, "utf-8");
|
|
33912
33849
|
const checkboxPattern = new RegExp(`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${escapePhaseNum(phaseNum)}[:\\s][^\\n]*)`, "i");
|
|
@@ -33920,7 +33857,7 @@ async function phaseCompleteCore(cwd, phaseNum) {
|
|
|
33920
33857
|
await node_fs.promises.writeFile(rmPath, roadmapContent, "utf-8");
|
|
33921
33858
|
debugLog("phase-complete-write", `ROADMAP.md updated for phase ${phaseNum}`);
|
|
33922
33859
|
const reqPath = planningPath(cwd, "REQUIREMENTS.md");
|
|
33923
|
-
if (await
|
|
33860
|
+
if (await pathExistsInternal(reqPath)) {
|
|
33924
33861
|
const reqMatch = roadmapContent.match(new RegExp(`Phase\\s+${escapePhaseNum(phaseNum)}[\\s\\S]*?\\*\\*Requirements:\\*\\*\\s*([^\\n]+)`, "i"));
|
|
33925
33862
|
if (reqMatch) {
|
|
33926
33863
|
const reqIds = reqMatch[1].replace(/[\[\]]/g, "").split(/[,\s]+/).map((r) => r.trim()).filter(Boolean);
|
|
@@ -33940,7 +33877,7 @@ async function phaseCompleteCore(cwd, phaseNum) {
|
|
|
33940
33877
|
let nextPhaseName = null;
|
|
33941
33878
|
let isLastPhase = true;
|
|
33942
33879
|
try {
|
|
33943
|
-
const dirs = await
|
|
33880
|
+
const dirs = await listSubDirs(phasesDirPath, true);
|
|
33944
33881
|
for (const dir of dirs) {
|
|
33945
33882
|
const dm = dir.match(/^(\d+[A-Z]?(?:\.\d+)?)-?(.*)/i);
|
|
33946
33883
|
if (dm) {
|
|
@@ -33955,7 +33892,7 @@ async function phaseCompleteCore(cwd, phaseNum) {
|
|
|
33955
33892
|
} catch (e) {
|
|
33956
33893
|
debugLog("phase-complete-next-phase-scan-failed", e);
|
|
33957
33894
|
}
|
|
33958
|
-
const stExists = await
|
|
33895
|
+
const stExists = await pathExistsInternal(stPath);
|
|
33959
33896
|
if (stExists) {
|
|
33960
33897
|
let stateContent = await node_fs.promises.readFile(stPath, "utf-8");
|
|
33961
33898
|
stateContent = stateContent.replace(/(\*\*Current Phase:\*\*\s*).*/, `$1${nextPhaseNum || phaseNum}`);
|
|
@@ -35523,7 +35460,7 @@ function registerPhaseTools(server) {
|
|
|
35523
35460
|
try {
|
|
35524
35461
|
const cwd = detectProjectRoot();
|
|
35525
35462
|
if (!cwd) return mcpError("No .planning/ directory found", "Project not detected");
|
|
35526
|
-
const result = findPhaseInternal(cwd, phase);
|
|
35463
|
+
const result = await findPhaseInternal(cwd, phase);
|
|
35527
35464
|
if (!result) return mcpError(`Phase ${phase} not found`, "Phase not found");
|
|
35528
35465
|
let githubTracking = null;
|
|
35529
35466
|
let githubTaskIssues = null;
|
|
@@ -35588,9 +35525,9 @@ function registerPhaseTools(server) {
|
|
|
35588
35525
|
limit,
|
|
35589
35526
|
has_more: false
|
|
35590
35527
|
}, "No phases directory found");
|
|
35591
|
-
let dirs = listSubDirs(phasesDir);
|
|
35528
|
+
let dirs = await listSubDirs(phasesDir);
|
|
35592
35529
|
if (include_archived) {
|
|
35593
|
-
const archived = getArchivedPhaseDirs(cwd);
|
|
35530
|
+
const archived = await getArchivedPhaseDirs(cwd);
|
|
35594
35531
|
for (const a of archived) dirs.push(`${a.name} [${a.milestone}]`);
|
|
35595
35532
|
}
|
|
35596
35533
|
dirs.sort((a, b) => comparePhaseNum(a, b));
|
|
@@ -36297,7 +36234,7 @@ function registerStateTools(server) {
|
|
|
36297
36234
|
* Ported from maxsim/bin/lib/roadmap.cjs
|
|
36298
36235
|
*/
|
|
36299
36236
|
async function cmdRoadmapAnalyze(cwd) {
|
|
36300
|
-
const content = await
|
|
36237
|
+
const content = await safeReadFile(roadmapPath(cwd));
|
|
36301
36238
|
if (!content) return cmdOk({
|
|
36302
36239
|
error: "ROADMAP.md not found",
|
|
36303
36240
|
milestones: [],
|
|
@@ -36330,7 +36267,7 @@ async function cmdRoadmapAnalyze(cwd) {
|
|
|
36330
36267
|
}
|
|
36331
36268
|
let allDirs = [];
|
|
36332
36269
|
try {
|
|
36333
|
-
allDirs = await
|
|
36270
|
+
allDirs = await listSubDirs(phasesDir);
|
|
36334
36271
|
} catch {}
|
|
36335
36272
|
const phases = await Promise.all(parsedPhases.map(async (p) => {
|
|
36336
36273
|
let diskStatus = "no_directory";
|
|
@@ -36470,9 +36407,9 @@ function loadRoadmapContext(cwd) {
|
|
|
36470
36407
|
addIfExists(files, cwd, ".planning/ROADMAP.md", "roadmap");
|
|
36471
36408
|
return files;
|
|
36472
36409
|
}
|
|
36473
|
-
function loadPhaseContext(cwd, phase) {
|
|
36410
|
+
async function loadPhaseContext(cwd, phase) {
|
|
36474
36411
|
const files = [];
|
|
36475
|
-
const phaseInfo = findPhaseInternal(cwd, phase);
|
|
36412
|
+
const phaseInfo = await findPhaseInternal(cwd, phase);
|
|
36476
36413
|
if (!phaseInfo?.directory) return files;
|
|
36477
36414
|
const phaseDir = phaseInfo.directory;
|
|
36478
36415
|
try {
|
|
@@ -36490,7 +36427,7 @@ function loadPhaseContext(cwd, phase) {
|
|
|
36490
36427
|
}
|
|
36491
36428
|
return files;
|
|
36492
36429
|
}
|
|
36493
|
-
function loadArtefakteContext(cwd, phase) {
|
|
36430
|
+
async function loadArtefakteContext(cwd, phase) {
|
|
36494
36431
|
const files = [];
|
|
36495
36432
|
for (const filename of [
|
|
36496
36433
|
"DECISIONS.md",
|
|
@@ -36498,7 +36435,7 @@ function loadArtefakteContext(cwd, phase) {
|
|
|
36498
36435
|
"NO-GOS.md"
|
|
36499
36436
|
]) {
|
|
36500
36437
|
if (phase) {
|
|
36501
|
-
const phaseInfo = findPhaseInternal(cwd, phase);
|
|
36438
|
+
const phaseInfo = await findPhaseInternal(cwd, phase);
|
|
36502
36439
|
if (phaseInfo?.directory) addIfExists(files, cwd, node_path.default.join(phaseInfo.directory, filename), `artefakt-${filename.toLowerCase()}`);
|
|
36503
36440
|
}
|
|
36504
36441
|
addIfExists(files, cwd, `.planning/${filename}`, `artefakt-${filename.toLowerCase()}`);
|
|
@@ -36515,11 +36452,11 @@ function loadCodebaseContext(cwd, topic) {
|
|
|
36515
36452
|
} catch {}
|
|
36516
36453
|
return files;
|
|
36517
36454
|
}
|
|
36518
|
-
function loadHistoryContext(cwd, currentPhase) {
|
|
36455
|
+
async function loadHistoryContext(cwd, currentPhase) {
|
|
36519
36456
|
const files = [];
|
|
36520
36457
|
const pd = phasesPath(cwd);
|
|
36521
36458
|
try {
|
|
36522
|
-
const dirs = listSubDirs(pd, true);
|
|
36459
|
+
const dirs = await listSubDirs(pd, true);
|
|
36523
36460
|
for (const dir of dirs) {
|
|
36524
36461
|
if (currentPhase) {
|
|
36525
36462
|
if (dir.match(/^(\d+[A-Z]?(?:\.\d+)?)/i)?.[1] === currentPhase) continue;
|
|
@@ -36533,15 +36470,15 @@ function loadHistoryContext(cwd, currentPhase) {
|
|
|
36533
36470
|
}
|
|
36534
36471
|
return files;
|
|
36535
36472
|
}
|
|
36536
|
-
function cmdContextLoad(cwd, phase, topic, includeHistory) {
|
|
36473
|
+
async function cmdContextLoad(cwd, phase, topic, includeHistory) {
|
|
36537
36474
|
const allFiles = [];
|
|
36538
36475
|
allFiles.push(...loadProjectContext(cwd));
|
|
36539
36476
|
allFiles.push(...loadRoadmapContext(cwd));
|
|
36540
|
-
allFiles.push(...loadArtefakteContext(cwd, phase));
|
|
36477
|
+
allFiles.push(...await loadArtefakteContext(cwd, phase));
|
|
36541
36478
|
const selectedDocs = selectCodebaseDocs(topic);
|
|
36542
36479
|
allFiles.push(...loadCodebaseContext(cwd, topic));
|
|
36543
|
-
if (phase) allFiles.push(...loadPhaseContext(cwd, phase));
|
|
36544
|
-
if (includeHistory) allFiles.push(...loadHistoryContext(cwd, phase));
|
|
36480
|
+
if (phase) allFiles.push(...await loadPhaseContext(cwd, phase));
|
|
36481
|
+
if (includeHistory) allFiles.push(...await loadHistoryContext(cwd, phase));
|
|
36545
36482
|
const seen = /* @__PURE__ */ new Set();
|
|
36546
36483
|
const deduped = allFiles.filter((f) => {
|
|
36547
36484
|
if (seen.has(f.path)) return false;
|
|
@@ -36584,7 +36521,7 @@ function registerContextTools(server) {
|
|
|
36584
36521
|
current_phase = data.current_phase ?? null;
|
|
36585
36522
|
next_phase = data.next_phase ?? null;
|
|
36586
36523
|
}
|
|
36587
|
-
const stateContent = safeReadFile(planningPath(cwd, "STATE.md"));
|
|
36524
|
+
const stateContent = await safeReadFile(planningPath(cwd, "STATE.md"));
|
|
36588
36525
|
if (stateContent) {
|
|
36589
36526
|
const statePhase = stateExtractField(stateContent, "Current Phase");
|
|
36590
36527
|
if (statePhase) phase_name = statePhase;
|
|
@@ -36605,12 +36542,12 @@ function registerContextTools(server) {
|
|
|
36605
36542
|
try {
|
|
36606
36543
|
const cwd = detectProjectRoot();
|
|
36607
36544
|
if (!cwd) return mcpError("No .planning/ directory found", "Project not detected");
|
|
36608
|
-
const project_vision = safeReadFile(planningPath(cwd, "PROJECT.md"));
|
|
36609
|
-
const config = loadConfig(cwd);
|
|
36545
|
+
const project_vision = await safeReadFile(planningPath(cwd, "PROJECT.md"));
|
|
36546
|
+
const config = await loadConfig(cwd);
|
|
36610
36547
|
let phase_context = null;
|
|
36611
36548
|
if (phase) {
|
|
36612
|
-
const phaseInfo = findPhaseInternal(cwd, phase);
|
|
36613
|
-
if (phaseInfo) phase_context = safeReadFile(node_path.default.join(phaseInfo.directory, `${phaseInfo.phase_number}-CONTEXT.md`));
|
|
36549
|
+
const phaseInfo = await findPhaseInternal(cwd, phase);
|
|
36550
|
+
if (phaseInfo) phase_context = await safeReadFile(node_path.default.join(phaseInfo.directory, `${phaseInfo.phase_number}-CONTEXT.md`));
|
|
36614
36551
|
}
|
|
36615
36552
|
return mcpSuccess({
|
|
36616
36553
|
project_vision,
|
|
@@ -36628,7 +36565,7 @@ function registerContextTools(server) {
|
|
|
36628
36565
|
try {
|
|
36629
36566
|
const cwd = detectProjectRoot();
|
|
36630
36567
|
if (!cwd) return mcpError("No .planning/ directory found", "Project not detected");
|
|
36631
|
-
const result = cmdContextLoad(cwd, phase, topic, true);
|
|
36568
|
+
const result = await cmdContextLoad(cwd, phase, topic, true);
|
|
36632
36569
|
if (!result.ok) return mcpError(result.error, "Context load failed");
|
|
36633
36570
|
return mcpSuccess({ context: result.result }, `Context loaded${phase ? ` for phase ${phase}` : ""}${topic ? ` topic "${topic}"` : ""}`);
|
|
36634
36571
|
} catch (e) {
|
|
@@ -36640,9 +36577,9 @@ function registerContextTools(server) {
|
|
|
36640
36577
|
const cwd = detectProjectRoot();
|
|
36641
36578
|
if (!cwd) return mcpError("No .planning/ directory found", "Project not detected");
|
|
36642
36579
|
return mcpSuccess({
|
|
36643
|
-
project: safeReadFile(planningPath(cwd, "PROJECT.md")),
|
|
36644
|
-
requirements: safeReadFile(planningPath(cwd, "REQUIREMENTS.md")),
|
|
36645
|
-
state: safeReadFile(planningPath(cwd, "STATE.md"))
|
|
36580
|
+
project: await safeReadFile(planningPath(cwd, "PROJECT.md")),
|
|
36581
|
+
requirements: await safeReadFile(planningPath(cwd, "REQUIREMENTS.md")),
|
|
36582
|
+
state: await safeReadFile(planningPath(cwd, "STATE.md"))
|
|
36646
36583
|
}, "Project overview loaded");
|
|
36647
36584
|
} catch (e) {
|
|
36648
36585
|
return mcpError("Failed: " + e.message, "Error occurred");
|
|
@@ -36652,7 +36589,7 @@ function registerContextTools(server) {
|
|
|
36652
36589
|
try {
|
|
36653
36590
|
const cwd = detectProjectRoot();
|
|
36654
36591
|
if (!cwd) return mcpError("No .planning/ directory found", "Project not detected");
|
|
36655
|
-
const phaseInfo = findPhaseInternal(cwd, phase);
|
|
36592
|
+
const phaseInfo = await findPhaseInternal(cwd, phase);
|
|
36656
36593
|
if (!phaseInfo) return mcpError(`Phase ${phase} not found`, "Phase not found");
|
|
36657
36594
|
const files = [];
|
|
36658
36595
|
try {
|
|
@@ -36661,7 +36598,7 @@ function registerContextTools(server) {
|
|
|
36661
36598
|
const fullPath = node_path.default.join(phaseInfo.directory, entry);
|
|
36662
36599
|
if (node_fs.default.statSync(fullPath).isFile()) files.push({
|
|
36663
36600
|
name: entry,
|
|
36664
|
-
content: safeReadFile(fullPath)
|
|
36601
|
+
content: await safeReadFile(fullPath)
|
|
36665
36602
|
});
|
|
36666
36603
|
}
|
|
36667
36604
|
} catch {}
|
|
@@ -36812,7 +36749,7 @@ function registerConfigTools(server) {
|
|
|
36812
36749
|
value: result.rawValue ?? result.result
|
|
36813
36750
|
}, `Config value for "${key}"`);
|
|
36814
36751
|
}
|
|
36815
|
-
return mcpSuccess({ config: loadConfig(cwd) }, "Full configuration loaded");
|
|
36752
|
+
return mcpSuccess({ config: await loadConfig(cwd) }, "Full configuration loaded");
|
|
36816
36753
|
} catch (e) {
|
|
36817
36754
|
return mcpError("Failed: " + e.message, "Error occurred");
|
|
36818
36755
|
}
|