maxsimcli 4.15.3 → 4.16.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.
- package/dist/assets/CHANGELOG.md +14 -0
- package/dist/assets/hooks/maxsim-statusline.cjs +46 -7
- package/dist/assets/hooks/maxsim-statusline.cjs.map +1 -1
- package/dist/assets/templates/agents/AGENTS.md +1 -1
- package/dist/assets/templates/agents/executor.md +1 -1
- package/dist/assets/templates/agents/planner.md +4 -4
- package/dist/assets/templates/references/git-planning-commit.md +1 -1
- package/dist/assets/templates/references/questioning.md +1 -1
- package/dist/assets/templates/templates/codebase/structure.md +1 -1
- package/dist/assets/templates/templates/milestone-archive.md +3 -3
- package/dist/assets/templates/workflows/batch.md +2 -3
- package/dist/assets/templates/workflows/diagnose-issues.md +6 -6
- package/dist/assets/templates/workflows/discovery-phase.md +6 -7
- package/dist/assets/templates/workflows/discuss-phase.md +8 -11
- package/dist/assets/templates/workflows/execute-phase.md +11 -71
- package/dist/assets/templates/workflows/execute-plan.md +8 -37
- package/dist/assets/templates/workflows/execute.md +7 -9
- package/dist/assets/templates/workflows/go.md +4 -4
- package/dist/assets/templates/workflows/help.md +1 -1
- package/dist/assets/templates/workflows/init-existing.md +0 -5
- package/dist/assets/templates/workflows/new-milestone.md +2 -7
- package/dist/assets/templates/workflows/new-project.md +0 -5
- package/dist/assets/templates/workflows/progress.md +10 -11
- package/dist/assets/templates/workflows/quick.md +0 -1
- package/dist/assets/templates/workflows/sdd.md +29 -30
- package/dist/assets/templates/workflows/settings.md +2 -7
- package/dist/assets/templates/workflows/verify-work.md +2 -16
- package/dist/cli.cjs +6913 -6499
- package/dist/cli.cjs.map +1 -1
- package/dist/core-D5zUr9cb.cjs.map +1 -1
- package/dist/install.cjs +10 -26
- package/dist/install.cjs.map +1 -1
- package/dist/mcp-server.cjs +186 -91
- package/dist/mcp-server.cjs.map +1 -1
- package/dist/skills-CjFWZIGM.cjs.map +1 -1
- package/package.json +1 -1
- package/dist/assets/templates/references/dashboard-bridge.md +0 -59
- package/dist/assets/templates/workflows/plan-phase.md +0 -501
package/dist/mcp-server.cjs
CHANGED
|
@@ -7,6 +7,19 @@ var __getOwnPropNames$1 = Object.getOwnPropertyNames;
|
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
|
|
9
9
|
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
10
|
+
var __exportAll = (all, no_symbols) => {
|
|
11
|
+
let target = {};
|
|
12
|
+
for (var name in all) {
|
|
13
|
+
__defProp$1(target, name, {
|
|
14
|
+
get: all[name],
|
|
15
|
+
enumerable: true
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
if (!no_symbols) {
|
|
19
|
+
__defProp$1(target, Symbol.toStringTag, { value: "Module" });
|
|
20
|
+
}
|
|
21
|
+
return target;
|
|
22
|
+
};
|
|
10
23
|
var __copyProps$1 = (to, from, except, desc) => {
|
|
11
24
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
25
|
for (var keys = __getOwnPropNames$1(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
@@ -26765,8 +26778,9 @@ function configPath(cwd) {
|
|
|
26765
26778
|
function phasesPath(cwd) {
|
|
26766
26779
|
return planningPath(cwd, "phases");
|
|
26767
26780
|
}
|
|
26768
|
-
/** Phase-file predicates. */
|
|
26781
|
+
/** Phase-file predicates. @deprecated Use GitHub Issues for plan/summary tracking. Local fallback only. */
|
|
26769
26782
|
const isPlanFile = (f) => f.endsWith("-PLAN.md") || f === "PLAN.md";
|
|
26783
|
+
/** @deprecated Use GitHub Issues for plan/summary tracking. Local fallback only. */
|
|
26770
26784
|
const isSummaryFile = (f) => f.endsWith("-SUMMARY.md") || f === "SUMMARY.md";
|
|
26771
26785
|
/** Strip suffix to get plan/summary ID. */
|
|
26772
26786
|
const planId = (f) => f.replace("-PLAN.md", "").replace("PLAN.md", "");
|
|
@@ -27005,10 +27019,19 @@ async function searchPhaseInDir(baseDir, relBase, normalized) {
|
|
|
27005
27019
|
}
|
|
27006
27020
|
async function findPhaseInternal(cwd, phase) {
|
|
27007
27021
|
if (!phase) return null;
|
|
27008
|
-
const pd = phasesPath(cwd);
|
|
27009
27022
|
const normalized = normalizePhaseName(phase);
|
|
27010
|
-
|
|
27011
|
-
|
|
27023
|
+
try {
|
|
27024
|
+
const { findPhaseFromGitHub } = await Promise.resolve().then(() => sync_exports);
|
|
27025
|
+
const ghResult = await findPhaseFromGitHub(cwd, normalized);
|
|
27026
|
+
if (ghResult) return ghResult;
|
|
27027
|
+
} catch {
|
|
27028
|
+
debugLog("find-phase-github-fallback", "GitHub lookup failed, falling back to local");
|
|
27029
|
+
}
|
|
27030
|
+
const current = await searchPhaseInDir(phasesPath(cwd), node_path.default.join(".planning", "phases"), normalized);
|
|
27031
|
+
if (current) {
|
|
27032
|
+
current.source = "local";
|
|
27033
|
+
return current;
|
|
27034
|
+
}
|
|
27012
27035
|
const archiveDir = planningPath(cwd, "archive");
|
|
27013
27036
|
if (await pathExistsInternal(archiveDir)) try {
|
|
27014
27037
|
const versionDirs = (await node_fs.promises.readdir(archiveDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name).sort().reverse();
|
|
@@ -27016,29 +27039,13 @@ async function findPhaseInternal(cwd, phase) {
|
|
|
27016
27039
|
const result = await searchPhaseInDir(node_path.default.join(archiveDir, versionName), node_path.default.join(".planning", "archive", versionName), normalized);
|
|
27017
27040
|
if (result) {
|
|
27018
27041
|
result.archived = versionName;
|
|
27042
|
+
result.source = "local";
|
|
27019
27043
|
return result;
|
|
27020
27044
|
}
|
|
27021
27045
|
}
|
|
27022
27046
|
} catch (e) {
|
|
27023
27047
|
debugLog("find-phase-async-archive-search-failed", e);
|
|
27024
27048
|
}
|
|
27025
|
-
const milestonesDir = planningPath(cwd, "milestones");
|
|
27026
|
-
if (!await pathExistsInternal(milestonesDir)) return null;
|
|
27027
|
-
try {
|
|
27028
|
-
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();
|
|
27029
|
-
for (const archiveName of archiveDirs) {
|
|
27030
|
-
const versionMatch = archiveName.match(/^(v[\d.]+)-phases$/);
|
|
27031
|
-
if (!versionMatch) continue;
|
|
27032
|
-
const version = versionMatch[1];
|
|
27033
|
-
const result = await searchPhaseInDir(node_path.default.join(milestonesDir, archiveName), node_path.default.join(".planning", "milestones", archiveName), normalized);
|
|
27034
|
-
if (result) {
|
|
27035
|
-
result.archived = version;
|
|
27036
|
-
return result;
|
|
27037
|
-
}
|
|
27038
|
-
}
|
|
27039
|
-
} catch (e) {
|
|
27040
|
-
debugLog("find-phase-async-milestone-search-failed", e);
|
|
27041
|
-
}
|
|
27042
27049
|
return null;
|
|
27043
27050
|
}
|
|
27044
27051
|
async function getArchivedPhaseDirs(cwd) {
|
|
@@ -27059,25 +27066,6 @@ async function getArchivedPhaseDirs(cwd) {
|
|
|
27059
27066
|
} catch (e) {
|
|
27060
27067
|
debugLog("get-archived-phase-dirs-async-archive-failed", e);
|
|
27061
27068
|
}
|
|
27062
|
-
const milestonesDir = planningPath(cwd, "milestones");
|
|
27063
|
-
try {
|
|
27064
|
-
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();
|
|
27065
|
-
for (const archiveName of phaseDirs) {
|
|
27066
|
-
const versionMatch = archiveName.match(/^(v[\d.]+)-phases$/);
|
|
27067
|
-
if (!versionMatch) continue;
|
|
27068
|
-
const version = versionMatch[1];
|
|
27069
|
-
const archiveMilestonePath = node_path.default.join(milestonesDir, archiveName);
|
|
27070
|
-
const dirs = await listSubDirs(archiveMilestonePath, true);
|
|
27071
|
-
for (const dir of dirs) results.push({
|
|
27072
|
-
name: dir,
|
|
27073
|
-
milestone: version,
|
|
27074
|
-
basePath: node_path.default.join(".planning", "milestones", archiveName),
|
|
27075
|
-
fullPath: node_path.default.join(archiveMilestonePath, dir)
|
|
27076
|
-
});
|
|
27077
|
-
}
|
|
27078
|
-
} catch (e) {
|
|
27079
|
-
debugLog("get-archived-phase-dirs-async-failed", e);
|
|
27080
|
-
}
|
|
27081
27069
|
return results;
|
|
27082
27070
|
}
|
|
27083
27071
|
|
|
@@ -33763,10 +33751,7 @@ function cmdErr(error) {
|
|
|
33763
33751
|
*
|
|
33764
33752
|
* Ported from maxsim/bin/lib/phase.cjs
|
|
33765
33753
|
*/
|
|
33766
|
-
async function scaffoldPhaseStubs(
|
|
33767
|
-
const today = todayISO();
|
|
33768
|
-
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`)]);
|
|
33769
|
-
}
|
|
33754
|
+
async function scaffoldPhaseStubs(_dirPath, _phaseId, _name) {}
|
|
33770
33755
|
async function phaseAddCore(cwd, description, options) {
|
|
33771
33756
|
const rmPath = roadmapPath(cwd);
|
|
33772
33757
|
let content;
|
|
@@ -33789,8 +33774,8 @@ async function phaseAddCore(cwd, description, options) {
|
|
|
33789
33774
|
const dirPath = planningPath(cwd, "phases", dirName);
|
|
33790
33775
|
await node_fs.promises.mkdir(dirPath, { recursive: true });
|
|
33791
33776
|
await node_fs.promises.writeFile(node_path.default.join(dirPath, ".gitkeep"), "");
|
|
33792
|
-
if (options?.includeStubs) await scaffoldPhaseStubs(dirPath, paddedNum, description);
|
|
33793
|
-
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
|
|
33777
|
+
if (options?.includeStubs) await /* @__PURE__ */ scaffoldPhaseStubs(dirPath, paddedNum, description);
|
|
33778
|
+
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 ${newPhaseNum} to break down)\n`;
|
|
33794
33779
|
let updatedContent;
|
|
33795
33780
|
const lastSeparator = content.lastIndexOf("\n---");
|
|
33796
33781
|
if (lastSeparator > 0) updatedContent = content.slice(0, lastSeparator) + phaseEntry + content.slice(lastSeparator);
|
|
@@ -33833,8 +33818,8 @@ async function phaseInsertCore(cwd, afterPhase, description, options) {
|
|
|
33833
33818
|
const dirPath = planningPath(cwd, "phases", dirName);
|
|
33834
33819
|
await node_fs.promises.mkdir(dirPath, { recursive: true });
|
|
33835
33820
|
await node_fs.promises.writeFile(node_path.default.join(dirPath, ".gitkeep"), "");
|
|
33836
|
-
if (options?.includeStubs) await scaffoldPhaseStubs(dirPath, decimalPhase, description);
|
|
33837
|
-
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
|
|
33821
|
+
if (options?.includeStubs) await /* @__PURE__ */ scaffoldPhaseStubs(dirPath, decimalPhase, description);
|
|
33822
|
+
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 ${decimalPhase} to break down)\n`;
|
|
33838
33823
|
const headerPattern = new RegExp(`(#{2,4}\\s*Phase\\s+0*${afterPhaseEscaped}:[^\\n]*\\n)`, "i");
|
|
33839
33824
|
const headerMatch = content.match(headerPattern);
|
|
33840
33825
|
if (!headerMatch) throw new Error(`Could not find Phase ${afterPhase} header`);
|
|
@@ -38842,48 +38827,6 @@ function escapeStringRegexp(string) {
|
|
|
38842
38827
|
return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
|
|
38843
38828
|
}
|
|
38844
38829
|
|
|
38845
|
-
//#endregion
|
|
38846
|
-
//#region src/core/state.ts
|
|
38847
|
-
/**
|
|
38848
|
-
* State — STATE.md operations and progression engine
|
|
38849
|
-
*
|
|
38850
|
-
* Ported from maxsim/bin/lib/state.cjs
|
|
38851
|
-
*/
|
|
38852
|
-
function stateExtractField(content, fieldName) {
|
|
38853
|
-
const escaped = escapeStringRegexp(fieldName);
|
|
38854
|
-
const boldPattern = new RegExp(`\\*\\*\\s*${escaped}\\s*:\\s*\\*\\*\\s*(.+)`, "i");
|
|
38855
|
-
const boldMatch = content.match(boldPattern);
|
|
38856
|
-
if (boldMatch) return boldMatch[1].trim();
|
|
38857
|
-
const plainPattern = new RegExp(`^\\s*${escaped}\\s*:\\s*(.+)`, "im");
|
|
38858
|
-
const plainMatch = content.match(plainPattern);
|
|
38859
|
-
return plainMatch ? plainMatch[1].trim() : null;
|
|
38860
|
-
}
|
|
38861
|
-
function stateReplaceField(content, fieldName, newValue) {
|
|
38862
|
-
const escaped = escapeStringRegexp(fieldName);
|
|
38863
|
-
const boldPattern = new RegExp(`(\\*\\*\\s*${escaped}\\s*:\\s*\\*\\*\\s*)(.*)`, "i");
|
|
38864
|
-
let replaced = content.replace(boldPattern, (_match, prefix) => `${prefix}${newValue}`);
|
|
38865
|
-
if (replaced !== content) return replaced;
|
|
38866
|
-
const plainPattern = new RegExp(`(^[ \\t]*${escaped}\\s*:\\s*)(.*)`, "im");
|
|
38867
|
-
replaced = content.replace(plainPattern, (_match, prefix) => `${prefix}${newValue}`);
|
|
38868
|
-
return replaced !== content ? replaced : null;
|
|
38869
|
-
}
|
|
38870
|
-
/**
|
|
38871
|
-
* Append an entry to a section in STATE.md content, removing placeholder text.
|
|
38872
|
-
* Returns updated content or null if section not found.
|
|
38873
|
-
*/
|
|
38874
|
-
function appendToStateSection(content, sectionPattern, entry, placeholderPatterns) {
|
|
38875
|
-
const match = content.match(sectionPattern);
|
|
38876
|
-
if (!match) return null;
|
|
38877
|
-
let sectionBody = match[2];
|
|
38878
|
-
for (const pat of placeholderPatterns || [
|
|
38879
|
-
/None yet\.?\s*\n?/gi,
|
|
38880
|
-
/No decisions yet\.?\s*\n?/gi,
|
|
38881
|
-
/None\.?\s*\n?/gi
|
|
38882
|
-
]) sectionBody = sectionBody.replace(pat, "");
|
|
38883
|
-
sectionBody = sectionBody.trimEnd() + "\n" + entry + "\n";
|
|
38884
|
-
return content.replace(sectionPattern, (_m, header) => `${header}${sectionBody}`);
|
|
38885
|
-
}
|
|
38886
|
-
|
|
38887
38830
|
//#endregion
|
|
38888
38831
|
//#region src/github/sync.ts
|
|
38889
38832
|
/**
|
|
@@ -38902,6 +38845,12 @@ function appendToStateSection(content, sectionPattern, entry, placeholderPattern
|
|
|
38902
38845
|
* CRITICAL: All operations use client.ts (Octokit adapter) exclusively.
|
|
38903
38846
|
* CRITICAL: Never call process.exit() — return GhResult instead.
|
|
38904
38847
|
*/
|
|
38848
|
+
var sync_exports = /* @__PURE__ */ __exportAll({
|
|
38849
|
+
checkPhaseProgress: () => checkPhaseProgress,
|
|
38850
|
+
detectInterruptedPhase: () => detectInterruptedPhase,
|
|
38851
|
+
findPhaseFromGitHub: () => findPhaseFromGitHub,
|
|
38852
|
+
getAllPhasesProgress: () => getAllPhasesProgress
|
|
38853
|
+
});
|
|
38905
38854
|
/**
|
|
38906
38855
|
* Get the progress of a phase by counting open/closed sub-issues.
|
|
38907
38856
|
*
|
|
@@ -39019,6 +38968,130 @@ async function getAllPhasesProgress() {
|
|
|
39019
38968
|
return results;
|
|
39020
38969
|
});
|
|
39021
38970
|
}
|
|
38971
|
+
/**
|
|
38972
|
+
* Populate a PhaseSearchResult from GitHub Issues data.
|
|
38973
|
+
*
|
|
38974
|
+
* Looks up the phase in the local mapping cache, then queries GitHub for
|
|
38975
|
+
* sub-issue counts and comments to derive plans/summaries/research status.
|
|
38976
|
+
*
|
|
38977
|
+
* Returns null if:
|
|
38978
|
+
* - No mapping file exists
|
|
38979
|
+
* - Phase not found in mapping
|
|
38980
|
+
* - GitHub API call fails
|
|
38981
|
+
*
|
|
38982
|
+
* @param cwd - Project root directory
|
|
38983
|
+
* @param phaseNum - Normalized phase number (e.g., "01", "02A")
|
|
38984
|
+
*/
|
|
38985
|
+
async function findPhaseFromGitHub(cwd, phaseNum) {
|
|
38986
|
+
let mapping;
|
|
38987
|
+
try {
|
|
38988
|
+
mapping = loadMapping(cwd);
|
|
38989
|
+
} catch {
|
|
38990
|
+
return null;
|
|
38991
|
+
}
|
|
38992
|
+
if (!mapping) return null;
|
|
38993
|
+
const phaseMapping = mapping.phases[phaseNum];
|
|
38994
|
+
if (!phaseMapping) return null;
|
|
38995
|
+
const issueNumber = phaseMapping.tracking_issue.number;
|
|
38996
|
+
if (!issueNumber) return null;
|
|
38997
|
+
const progressResult = await checkPhaseProgress(issueNumber);
|
|
38998
|
+
if (!progressResult.ok) return null;
|
|
38999
|
+
const { tasks, completed, total } = progressResult.data;
|
|
39000
|
+
let phaseName = null;
|
|
39001
|
+
let phaseSlug = null;
|
|
39002
|
+
try {
|
|
39003
|
+
const octokit = getOctokit();
|
|
39004
|
+
const { owner, repo } = await getRepoInfo();
|
|
39005
|
+
const titleMatch = (await octokit.rest.issues.get({
|
|
39006
|
+
owner,
|
|
39007
|
+
repo,
|
|
39008
|
+
issue_number: issueNumber
|
|
39009
|
+
})).data.title.match(/\[Phase\s+\S+\]\s*(.*)/);
|
|
39010
|
+
if (titleMatch) {
|
|
39011
|
+
phaseName = titleMatch[1].trim();
|
|
39012
|
+
phaseSlug = phaseName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
39013
|
+
}
|
|
39014
|
+
} catch {}
|
|
39015
|
+
const planNames = tasks.map((t) => `task-${t.number}`);
|
|
39016
|
+
const summaryNames = tasks.filter((t) => t.state === "closed").map((t) => `task-${t.number}`);
|
|
39017
|
+
const completedSet = new Set(summaryNames);
|
|
39018
|
+
const incompletePlans = planNames.filter((p) => !completedSet.has(p));
|
|
39019
|
+
let hasResearch = false;
|
|
39020
|
+
let hasContext = false;
|
|
39021
|
+
let hasVerification = false;
|
|
39022
|
+
try {
|
|
39023
|
+
const octokit = getOctokit();
|
|
39024
|
+
const { owner, repo } = await getRepoInfo();
|
|
39025
|
+
const comments = await octokit.rest.issues.listComments({
|
|
39026
|
+
owner,
|
|
39027
|
+
repo,
|
|
39028
|
+
issue_number: issueNumber,
|
|
39029
|
+
per_page: 100
|
|
39030
|
+
});
|
|
39031
|
+
for (const comment of comments.data) {
|
|
39032
|
+
const body = comment.body ?? "";
|
|
39033
|
+
if (body.includes("<!-- maxsim:type=research -->") || body.includes("## Research") || body.startsWith("## Phase") && body.includes("Research")) hasResearch = true;
|
|
39034
|
+
if (body.includes("<!-- maxsim:type=context -->") || body.includes("## Context")) hasContext = true;
|
|
39035
|
+
if (body.includes("<!-- maxsim:type=verification -->") || body.includes("## Verification")) hasVerification = true;
|
|
39036
|
+
}
|
|
39037
|
+
} catch {}
|
|
39038
|
+
return {
|
|
39039
|
+
found: true,
|
|
39040
|
+
directory: `.planning/phases/${phaseNum}-${phaseSlug || "unknown"}`,
|
|
39041
|
+
phase_number: phaseNum,
|
|
39042
|
+
phase_name: phaseName,
|
|
39043
|
+
phase_slug: phaseSlug,
|
|
39044
|
+
plans: planNames,
|
|
39045
|
+
summaries: summaryNames,
|
|
39046
|
+
incomplete_plans: incompletePlans,
|
|
39047
|
+
has_research: hasResearch,
|
|
39048
|
+
has_context: hasContext,
|
|
39049
|
+
has_verification: hasVerification,
|
|
39050
|
+
source: "github"
|
|
39051
|
+
};
|
|
39052
|
+
}
|
|
39053
|
+
|
|
39054
|
+
//#endregion
|
|
39055
|
+
//#region src/core/state.ts
|
|
39056
|
+
/**
|
|
39057
|
+
* State — STATE.md operations and progression engine
|
|
39058
|
+
*
|
|
39059
|
+
* Ported from maxsim/bin/lib/state.cjs
|
|
39060
|
+
*/
|
|
39061
|
+
function stateExtractField(content, fieldName) {
|
|
39062
|
+
const escaped = escapeStringRegexp(fieldName);
|
|
39063
|
+
const boldPattern = new RegExp(`\\*\\*\\s*${escaped}\\s*:\\s*\\*\\*\\s*(.+)`, "i");
|
|
39064
|
+
const boldMatch = content.match(boldPattern);
|
|
39065
|
+
if (boldMatch) return boldMatch[1].trim();
|
|
39066
|
+
const plainPattern = new RegExp(`^\\s*${escaped}\\s*:\\s*(.+)`, "im");
|
|
39067
|
+
const plainMatch = content.match(plainPattern);
|
|
39068
|
+
return plainMatch ? plainMatch[1].trim() : null;
|
|
39069
|
+
}
|
|
39070
|
+
function stateReplaceField(content, fieldName, newValue) {
|
|
39071
|
+
const escaped = escapeStringRegexp(fieldName);
|
|
39072
|
+
const boldPattern = new RegExp(`(\\*\\*\\s*${escaped}\\s*:\\s*\\*\\*\\s*)(.*)`, "i");
|
|
39073
|
+
let replaced = content.replace(boldPattern, (_match, prefix) => `${prefix}${newValue}`);
|
|
39074
|
+
if (replaced !== content) return replaced;
|
|
39075
|
+
const plainPattern = new RegExp(`(^[ \\t]*${escaped}\\s*:\\s*)(.*)`, "im");
|
|
39076
|
+
replaced = content.replace(plainPattern, (_match, prefix) => `${prefix}${newValue}`);
|
|
39077
|
+
return replaced !== content ? replaced : null;
|
|
39078
|
+
}
|
|
39079
|
+
/**
|
|
39080
|
+
* Append an entry to a section in STATE.md content, removing placeholder text.
|
|
39081
|
+
* Returns updated content or null if section not found.
|
|
39082
|
+
*/
|
|
39083
|
+
function appendToStateSection(content, sectionPattern, entry, placeholderPatterns) {
|
|
39084
|
+
const match = content.match(sectionPattern);
|
|
39085
|
+
if (!match) return null;
|
|
39086
|
+
let sectionBody = match[2];
|
|
39087
|
+
for (const pat of placeholderPatterns || [
|
|
39088
|
+
/None yet\.?\s*\n?/gi,
|
|
39089
|
+
/No decisions yet\.?\s*\n?/gi,
|
|
39090
|
+
/None\.?\s*\n?/gi
|
|
39091
|
+
]) sectionBody = sectionBody.replace(pat, "");
|
|
39092
|
+
sectionBody = sectionBody.trimEnd() + "\n" + entry + "\n";
|
|
39093
|
+
return content.replace(sectionPattern, (_m, header) => `${header}${sectionBody}`);
|
|
39094
|
+
}
|
|
39022
39095
|
|
|
39023
39096
|
//#endregion
|
|
39024
39097
|
//#region src/mcp/state-tools.ts
|
|
@@ -39315,6 +39388,20 @@ async function cmdRoadmapAnalyze(cwd) {
|
|
|
39315
39388
|
checkboxPattern: new RegExp(`-\\s*\\[(x| )\\]\\s*.*Phase\\s+${phaseNum.replace(".", "\\.")}`, "i")
|
|
39316
39389
|
});
|
|
39317
39390
|
}
|
|
39391
|
+
let ghPhaseProgress = null;
|
|
39392
|
+
try {
|
|
39393
|
+
const mapping = loadMapping(cwd);
|
|
39394
|
+
if (mapping && Object.keys(mapping.phases).length > 0) {
|
|
39395
|
+
const ghResult = await getAllPhasesProgress();
|
|
39396
|
+
if (ghResult.ok) {
|
|
39397
|
+
ghPhaseProgress = /* @__PURE__ */ new Map();
|
|
39398
|
+
for (const entry of ghResult.data) ghPhaseProgress.set(entry.phaseNumber, {
|
|
39399
|
+
total: entry.progress.total,
|
|
39400
|
+
completed: entry.progress.completed
|
|
39401
|
+
});
|
|
39402
|
+
}
|
|
39403
|
+
}
|
|
39404
|
+
} catch {}
|
|
39318
39405
|
let allDirs = [];
|
|
39319
39406
|
try {
|
|
39320
39407
|
allDirs = await listSubDirs(phasesDir);
|
|
@@ -39325,7 +39412,15 @@ async function cmdRoadmapAnalyze(cwd) {
|
|
|
39325
39412
|
let summaryCount = 0;
|
|
39326
39413
|
let hasContext = false;
|
|
39327
39414
|
let hasResearch = false;
|
|
39328
|
-
|
|
39415
|
+
const ghData = ghPhaseProgress?.get(p.phaseNum);
|
|
39416
|
+
if (ghData) {
|
|
39417
|
+
planCount = ghData.total;
|
|
39418
|
+
summaryCount = ghData.completed;
|
|
39419
|
+
if (summaryCount >= planCount && planCount > 0) diskStatus = "complete";
|
|
39420
|
+
else if (summaryCount > 0) diskStatus = "partial";
|
|
39421
|
+
else if (planCount > 0) diskStatus = "planned";
|
|
39422
|
+
else diskStatus = "empty";
|
|
39423
|
+
} else try {
|
|
39329
39424
|
const dirMatch = allDirs.find((d) => d.startsWith(p.normalized + "-") || d === p.normalized);
|
|
39330
39425
|
if (dirMatch) {
|
|
39331
39426
|
const phaseFiles = await node_fs.promises.readdir(node_path.default.join(phasesDir, dirMatch));
|