codecartographer-pi 0.15.0 → 0.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/.codecarto/workflow/scaffold-version.yaml +1 -1
- package/agent-skill/codecartographer/SKILL.md +3 -0
- package/agent-skill/codecartographer/references/library.md +32 -0
- package/dist/core/amendment.js +4 -1
- package/dist/core/completion.js +29 -10
- package/dist/core/status.d.ts +8 -0
- package/dist/core/status.js +22 -0
- package/dist/extensions/codecarto/dashboard-writer.d.ts +8 -1
- package/dist/extensions/codecarto/dashboard-writer.js +10 -1
- package/dist/mcp-server/server.js +23 -2
- package/package.json +1 -1
|
@@ -122,6 +122,8 @@ A PARTIAL row's evidence must name what is missing and which `open_questions` or
|
|
|
122
122
|
|
|
123
123
|
## When the run drives a rewrite
|
|
124
124
|
|
|
125
|
+
When the pipeline completes, the finished spec has a designed destination: publish it to a **library** (`codecarto_publish`; create one with `codecarto_library_init`) so synthesis runs and other projects can consume it — `references/library.md`. A spec that only ever lives in its workspace helps exactly one repository.
|
|
126
|
+
|
|
125
127
|
If the goal is to rebuild or refactor rather than to understand, two phases carry that weight and both have their own reference:
|
|
126
128
|
|
|
127
129
|
- the defect scans feed `porting` and `reimplementation-spec` as inputs, not as an appendix — `references/deep-audit-synthesis.md`;
|
|
@@ -142,5 +144,6 @@ Using what it produces:
|
|
|
142
144
|
- `references/deep-audit-synthesis.md` — defect dispositions, hazards as normative rules, reporting
|
|
143
145
|
- `references/kernel-first-rewrite.md` — rings, build order, acceptance harness, strategic assumptions
|
|
144
146
|
- `references/carrying-results-forward.md` — starting implementation, autonomy boundaries, publishing findings
|
|
147
|
+
- `references/library.md` — publishing finished specs to a library and consuming them from synthesis runs
|
|
145
148
|
|
|
146
149
|
This guide is also served by the `codecarto_guide` MCP tool, so an agent with the server configured can read it without installing anything.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# The library: where finished specs go
|
|
2
|
+
|
|
3
|
+
A CodeCartographer **library** is a directory of published reimplementation-specs with provenance — the bridge between analysis runs and everything downstream. An analysis pipeline ends with a spec in one workspace; publishing it makes it addressable from any other project, and the **synthesis pipeline** consumes exactly these entries ("convert a user vision and explicitly confirmed library specs into a provenance-backed project plan" — see `codecarto_guide` topic `pipeline-selection`). Without a publish step, every analysis is an island.
|
|
4
|
+
|
|
5
|
+
## Anatomy
|
|
6
|
+
|
|
7
|
+
- A directory holding a `.codecarto-library` marker, `entries/` (optionally namespaced), and a generated `index.yaml` + `INDEX.md`.
|
|
8
|
+
- Discovery: tools take `library_path` (absolute) directly, or resolve the library from a workspace `cwd`'s `config.yaml`; `codecarto_library_init` also writes `library.path` into the user-global config so later calls need no path at all.
|
|
9
|
+
- `codecarto_config` shows the effective merge (`library.path`, `library.namespace`, `publish_confirm`) and whether the marker was found.
|
|
10
|
+
|
|
11
|
+
## The four tools
|
|
12
|
+
|
|
13
|
+
| Tool | Does | Notes |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| `codecarto_library_init` | Create the directory, write the marker, record `library.path` in user-global config | Idempotent; pass `namespace` to create a namespaced library |
|
|
16
|
+
| `codecarto_publish` | Publish a spec as a library entry | Required: `source_repo`, `headline`, and `spec` (inline) or `spec_path` (absolute). Content-hash idempotent: identical bytes update metadata in place, no version bump. `slug` derives from `source_repo` if omitted; namespaced libraries require `namespace` (or inherit via `cwd`). Provenance (`source_commit`, `source_branch`, `source_dirty`, `analyzed_at`, `pipeline`, `model_metadata`) is recorded; omitted generation fields default to `unknown` |
|
|
17
|
+
| `codecarto_library_list` | List entries | Filter by `namespace`, `tag`, `slug`, or `source_repo` |
|
|
18
|
+
| `codecarto_library_reindex` | Regenerate `index.yaml` + `INDEX.md` from filesystem state | For manual edits and index merge conflicts |
|
|
19
|
+
|
|
20
|
+
## When to publish
|
|
21
|
+
|
|
22
|
+
The moment `reimplementation-spec` completes and validates is the publish moment — the spec is finished, the workspace still knows its provenance (`cwd` inherits `pipeline` from `status.yaml`), and the terminal `next_actions` point here. Publish with `cwd` set so provenance rides along:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
codecarto_publish cwd:<workspace repo> source_repo:<repo URL or path> headline:"<one line>" spec_path:<abs path to reimplementation-spec.md>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Set `publish_confirm` in config if you want an explicit confirmation gate before writes.
|
|
29
|
+
|
|
30
|
+
## What this is not
|
|
31
|
+
|
|
32
|
+
Publishing a spec into a library is different from copying findings into a product repository you are about to build — that curated-snapshot flow is `references/carrying-results-forward.md`. The library holds *specs as reusable inputs*; a product repo holds *your implementation of one*.
|
package/dist/core/amendment.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { getNextEligiblePhase } from "./pipeline.js";
|
|
11
|
-
import { ensureArray, normalizeStatus } from "./status.js";
|
|
11
|
+
import { buildTerminalNextActions, ensureArray, normalizeStatus } from "./status.js";
|
|
12
12
|
import { dateOnly, pathExists } from "./utils.js";
|
|
13
13
|
import { getWorkspaceState, updateStatusAtomically } from "./workspace.js";
|
|
14
14
|
import { loadYamlFile } from "./yaml.js";
|
|
@@ -111,6 +111,9 @@ export async function applyAmendment(cwd, name) {
|
|
|
111
111
|
nextStatus.post_pipeline = nextStatus.post_pipeline.filter((entry) => entry.id !== closureId);
|
|
112
112
|
(nextStatus.post_pipeline.length !== before ? applied.postPipelineClosed : applied.unknownIds).push(closureId);
|
|
113
113
|
}
|
|
114
|
+
// The amendment changed exactly the counts the terminal routing lines
|
|
115
|
+
// carry (issue #114); rebuild them so status never shows stale numbers.
|
|
116
|
+
nextStatus.next_actions = buildTerminalNextActions(nextStatus);
|
|
114
117
|
nextStatus.last_updated = timestamp;
|
|
115
118
|
// Amendment closeout + THREAD_LOG entry, same idempotence rule as
|
|
116
119
|
// completion: the closeout link appears in THREAD_LOG at most once.
|
package/dist/core/completion.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { appendFile, copyFile, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { getNextEligiblePhase, resolvePhase } from "./pipeline.js";
|
|
4
|
-
import { applyHandoff, autoAssignIds, loadHandoffFile, normalizeStatus } from "./status.js";
|
|
4
|
+
import { applyHandoff, autoAssignIds, buildTerminalNextActions, loadHandoffFile, normalizeStatus } from "./status.js";
|
|
5
5
|
import { dateOnly, pathExists, uniqueStrings } from "./utils.js";
|
|
6
6
|
import { getWorkspaceState, updateStatusAtomically } from "./workspace.js";
|
|
7
7
|
/**
|
|
@@ -46,6 +46,18 @@ function visibleMarkdown(content) {
|
|
|
46
46
|
export const DECISIONS_COMPLETION_LOG_HEADING = "## Completion log";
|
|
47
47
|
/** Section heading completion stages proposed conventions under. */
|
|
48
48
|
export const CONVENTIONS_PENDING_HEADING = "## Pending proposals";
|
|
49
|
+
/**
|
|
50
|
+
* True when `heading` exists as its own visible line. Both orchestrator-file
|
|
51
|
+
* templates mention their headings in running prose (issue #111: a raw
|
|
52
|
+
* substring check saw the decisions-template's "…rows under `## Completion
|
|
53
|
+
* log`…" sentence and never inserted the real heading), so presence checks
|
|
54
|
+
* must match a whole trimmed line of comment-stripped content.
|
|
55
|
+
*/
|
|
56
|
+
function hasVisibleHeadingLine(content, heading) {
|
|
57
|
+
return visibleMarkdown(content)
|
|
58
|
+
.split(/\r?\n/)
|
|
59
|
+
.some((line) => line.trim() === heading);
|
|
60
|
+
}
|
|
49
61
|
/**
|
|
50
62
|
* Ensure an orchestrator file exists: prefer the workspace's template, fall
|
|
51
63
|
* back to a minimal header for scaffolds that predate the template.
|
|
@@ -90,7 +102,7 @@ async function appendDecisionLog(workspaceDir, phaseId, closeoutFile, decisions)
|
|
|
90
102
|
if (Number.isFinite(parsed) && parsed >= nextNumber)
|
|
91
103
|
nextNumber = parsed + 1;
|
|
92
104
|
}
|
|
93
|
-
if (!content
|
|
105
|
+
if (!hasVisibleHeadingLine(content, DECISIONS_COMPLETION_LOG_HEADING)) {
|
|
94
106
|
content += `${content.endsWith("\n") ? "" : "\n"}\n${DECISIONS_COMPLETION_LOG_HEADING}\n\nAppended by completion from each phase handoff's \`decisions\` array. The orchestrator may re-file entries into the category sections above; numbering is shared with them.\n`;
|
|
95
107
|
}
|
|
96
108
|
const source = closeoutFile.replace(/\.md$/, "");
|
|
@@ -114,13 +126,20 @@ export async function countPendingProposals(workspaceDir, content) {
|
|
|
114
126
|
return 0;
|
|
115
127
|
text = await readFile(filePath, "utf8");
|
|
116
128
|
}
|
|
117
|
-
|
|
118
|
-
|
|
129
|
+
// Same line-anchored rule as the heading-insertion checks: a prose mention
|
|
130
|
+
// of the heading must not open the section early and miscount.
|
|
131
|
+
const lines = visibleMarkdown(text).split(/\r?\n/);
|
|
132
|
+
const headingIndex = lines.findIndex((line) => line.trim() === CONVENTIONS_PENDING_HEADING);
|
|
133
|
+
if (headingIndex === -1)
|
|
119
134
|
return 0;
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
135
|
+
let count = 0;
|
|
136
|
+
for (const line of lines.slice(headingIndex + 1)) {
|
|
137
|
+
if (line.startsWith("## "))
|
|
138
|
+
break;
|
|
139
|
+
if (line.startsWith("- **"))
|
|
140
|
+
count += 1;
|
|
141
|
+
}
|
|
142
|
+
return count;
|
|
124
143
|
}
|
|
125
144
|
/**
|
|
126
145
|
* Stage handoff `proposed_conventions` in CONVENTIONS.md under
|
|
@@ -135,7 +154,7 @@ async function stageProposedConventions(workspaceDir, phaseId, timestamp, propos
|
|
|
135
154
|
return { staged: 0, totalPending: await countPendingProposals(workspaceDir) };
|
|
136
155
|
}
|
|
137
156
|
let content = await ensureOrchestratorFile(workspaceDir, "CONVENTIONS.md", "conventions-template.md", "# Conventions\n\nCross-cutting patterns promoted to project-wide invariants. This scaffold predates templates/conventions-template.md; refresh the framework-owned files for the full format.\n");
|
|
138
|
-
if (!content
|
|
157
|
+
if (!hasVisibleHeadingLine(content, CONVENTIONS_PENDING_HEADING)) {
|
|
139
158
|
content += `${content.endsWith("\n") ? "" : "\n"}\n${CONVENTIONS_PENDING_HEADING}\n\nStaged by completion from each phase handoff's \`proposed_conventions\`. The orchestrator promotes an entry into a numbered convention above (or removes it with a note) at the phase boundary — see GUIDE.md §Roles.\n`;
|
|
140
159
|
}
|
|
141
160
|
// Same visible-content rule as the decision log: template comments must not
|
|
@@ -298,7 +317,7 @@ export async function completeValidatedPhase(cwd, validation, sourceLabel) {
|
|
|
298
317
|
nextStatus.current_phase = nextEligible?.id ?? "complete";
|
|
299
318
|
nextStatus.next_actions = nextEligible
|
|
300
319
|
? [`Begin ${nextEligible.id} phase by producing ${nextEligible.primary_output ?? `findings/${nextEligible.id}/`}`]
|
|
301
|
-
:
|
|
320
|
+
: buildTerminalNextActions(nextStatus);
|
|
302
321
|
const artifacts = await writeCompletionArtifacts(lockedState.workspaceDir, validation.phaseId, validation, completionTimestamp, handoff);
|
|
303
322
|
closeoutPath = artifacts.closeoutPath;
|
|
304
323
|
orchestratorCheckpoint = buildOrchestratorCheckpoint(artifacts.decisionsAppended, artifacts.totalPendingProposals, nextStatus);
|
package/dist/core/status.d.ts
CHANGED
|
@@ -9,6 +9,14 @@ export declare function autoAssignIds(entries: OpenQuestionEntry[], prefix: stri
|
|
|
9
9
|
export declare function ensurePostPipelineArray(value: unknown): PostPipelineEntry[];
|
|
10
10
|
export declare function ensurePhaseRecord(value: unknown): Record<string, StatusPhase>;
|
|
11
11
|
export declare function createEmptyStatus(projectName: string, pipelinePath: string, pipeline: PipelineFile): NormalizedStatus;
|
|
12
|
+
/**
|
|
13
|
+
* Route the terminal boundary to the post-pipeline surfaces (issue #114). The
|
|
14
|
+
* moment every phase completes is exactly when skills, amendments, publishing,
|
|
15
|
+
* and the dashboard apply; the prior static sentence left them undiscovered —
|
|
16
|
+
* the 0.15.0 field test finished two full runs with every one of them unused.
|
|
17
|
+
* Amendment recomputes this list so closure counts never go stale.
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildTerminalNextActions(status: NormalizedStatus): string[];
|
|
12
20
|
export declare function normalizeStatus(status: StatusFile, pipeline: PipelineFile, pipelinePath: string, cwd: string): NormalizedStatus;
|
|
13
21
|
export declare function parseHandoff(value: unknown): PhaseHandoff;
|
|
14
22
|
/**
|
package/dist/core/status.js
CHANGED
|
@@ -126,6 +126,28 @@ export function createEmptyStatus(projectName, pipelinePath, pipeline) {
|
|
|
126
126
|
post_pipeline: [],
|
|
127
127
|
};
|
|
128
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Route the terminal boundary to the post-pipeline surfaces (issue #114). The
|
|
131
|
+
* moment every phase completes is exactly when skills, amendments, publishing,
|
|
132
|
+
* and the dashboard apply; the prior static sentence left them undiscovered —
|
|
133
|
+
* the 0.15.0 field test finished two full runs with every one of them unused.
|
|
134
|
+
* Amendment recomputes this list so closure counts never go stale.
|
|
135
|
+
*/
|
|
136
|
+
export function buildTerminalNextActions(status) {
|
|
137
|
+
const openQuestions = Object.values(status.phases).reduce((sum, phase) => sum + (phase.open_questions?.length ?? 0), 0);
|
|
138
|
+
const postPipeline = status.post_pipeline.length;
|
|
139
|
+
const actions = [
|
|
140
|
+
"All phases complete. Review findings; post-pipeline skills: codecarto_list_skills / codecarto_skill.",
|
|
141
|
+
];
|
|
142
|
+
if (openQuestions > 0 || postPipeline > 0) {
|
|
143
|
+
actions.push(`${openQuestions} open question(s) and ${postPipeline} post-pipeline item(s) remain — apply resolutions with codecarto_amend (write scratch/amendments/<slug>.yaml from templates/amendment.yaml).`);
|
|
144
|
+
}
|
|
145
|
+
if ("reimplementation-spec" in status.phases) {
|
|
146
|
+
actions.push("Publish the finished spec to a library: codecarto_publish (create one with codecarto_library_init; see the library guide topic).");
|
|
147
|
+
}
|
|
148
|
+
actions.push("Dashboard: .codecarto/dashboard.html (refreshed on completion and amendment; codecarto_dashboard re-renders on demand). Usage totals: codecarto_usage.");
|
|
149
|
+
return actions;
|
|
150
|
+
}
|
|
129
151
|
export function normalizeStatus(status, pipeline, pipelinePath, cwd) {
|
|
130
152
|
if (typeof status.schema_version === "number" && status.schema_version > 1) {
|
|
131
153
|
throw new Error(`Unsupported status schema_version ${status.schema_version}. Supported: 1.`);
|
|
@@ -1 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Render and atomically replace `.codecarto/dashboard.html`.
|
|
3
|
+
* @returns true when a fresh dashboard landed on disk; false when the
|
|
4
|
+
* workspace is missing or any gather/render/write step failed (swallowed —
|
|
5
|
+
* lifecycle callers must never fail on a dashboard problem, but they may
|
|
6
|
+
* report truthfully whether a refresh happened).
|
|
7
|
+
*/
|
|
8
|
+
export declare function writeDashboard(cwd: string, packageVersion: string): Promise<boolean>;
|
|
@@ -6,11 +6,18 @@ import { readdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { DASHBOARD_RELATIVE_PATH, getWorkspaceState, loadUsage, NARRATION_CACHE_RELATIVE_PATH, parseSimpleYaml, pathExists, renderDashboard, } from "../../core/index.js";
|
|
8
8
|
const CLOSEOUT_FILENAME_RE = /^(\d{4}-\d{2}-\d{2})-(.+)\.md$/;
|
|
9
|
+
/**
|
|
10
|
+
* Render and atomically replace `.codecarto/dashboard.html`.
|
|
11
|
+
* @returns true when a fresh dashboard landed on disk; false when the
|
|
12
|
+
* workspace is missing or any gather/render/write step failed (swallowed —
|
|
13
|
+
* lifecycle callers must never fail on a dashboard problem, but they may
|
|
14
|
+
* report truthfully whether a refresh happened).
|
|
15
|
+
*/
|
|
9
16
|
export async function writeDashboard(cwd, packageVersion) {
|
|
10
17
|
try {
|
|
11
18
|
const state = await getWorkspaceState(cwd);
|
|
12
19
|
if (!state)
|
|
13
|
-
return;
|
|
20
|
+
return false;
|
|
14
21
|
const workspaceDir = state.workspaceDir;
|
|
15
22
|
const [usage, closeouts, outputsPresent, narration] = await Promise.all([
|
|
16
23
|
loadUsage(workspaceDir),
|
|
@@ -33,11 +40,13 @@ export async function writeDashboard(cwd, packageVersion) {
|
|
|
33
40
|
const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
34
41
|
await writeFile(tempPath, html, "utf8");
|
|
35
42
|
await rename(tempPath, path);
|
|
43
|
+
return true;
|
|
36
44
|
}
|
|
37
45
|
catch {
|
|
38
46
|
// Best-effort: a failed dashboard write must not surface as a phase
|
|
39
47
|
// error. The user's pipeline state is unaffected; the next state
|
|
40
48
|
// change will trigger another render attempt.
|
|
49
|
+
return false;
|
|
41
50
|
}
|
|
42
51
|
}
|
|
43
52
|
async function listCloseouts(workspaceDir) {
|
|
@@ -144,7 +144,12 @@ export async function handleStatus(args) {
|
|
|
144
144
|
`Open questions (terminal unresolved): ${terminalOpenQuestions}`,
|
|
145
145
|
`Carry-forward (pipeline phases): ${totalCarryForward}`,
|
|
146
146
|
`Post-pipeline work: ${postPipelinePending} pending`,
|
|
147
|
-
|
|
147
|
+
// Render every stored action: the terminal list routes to several
|
|
148
|
+
// post-pipeline surfaces (issue #114), and a text-reading client that
|
|
149
|
+
// only ever sees actions[0] loses exactly the routing it exists for.
|
|
150
|
+
...(state.status.next_actions.length > 0
|
|
151
|
+
? state.status.next_actions.map((action, index) => `${index === 0 ? "Next: " : " "}${action}`)
|
|
152
|
+
: [`Next: ${nextPhase ? `Begin ${nextPhase.id}` : "All phases complete."}`]),
|
|
148
153
|
];
|
|
149
154
|
if (scaffoldNotice)
|
|
150
155
|
summaryLines.push(`Scaffold: ${scaffoldNotice}`);
|
|
@@ -267,6 +272,11 @@ export async function handleComplete(args) {
|
|
|
267
272
|
// state is already written, so a usage-log write failure must not fail
|
|
268
273
|
// the completion result. Nothing else can act on the error here.
|
|
269
274
|
}
|
|
275
|
+
// Dashboard freshness is a completion side effect (issue #112): the counts
|
|
276
|
+
// it renders change exactly here, and a stale dashboard misreports them
|
|
277
|
+
// confidently. writeDashboard never throws; its boolean says whether a
|
|
278
|
+
// fresh render actually landed, so the result only claims what happened.
|
|
279
|
+
const dashboardPath = (await writeDashboard(cwd, PACKAGE_VERSION)) ? ".codecarto/dashboard.html" : undefined;
|
|
270
280
|
const lines = [
|
|
271
281
|
`Marked ${validation.phaseId} complete (validation: ${validation.overall}).`,
|
|
272
282
|
`Next phase: ${updatedState.status.current_phase}`,
|
|
@@ -275,12 +285,15 @@ export async function handleComplete(args) {
|
|
|
275
285
|
lines.push(closeoutNotice);
|
|
276
286
|
if (orchestratorCheckpoint)
|
|
277
287
|
lines.push(orchestratorCheckpoint);
|
|
288
|
+
if (dashboardPath)
|
|
289
|
+
lines.push(`Dashboard refreshed: ${dashboardPath}`);
|
|
278
290
|
return textResult(lines.join("\n"), {
|
|
279
291
|
completedPhase: validation.phaseId,
|
|
280
292
|
validation: validation.overall,
|
|
281
293
|
nextPhase: updatedState.status.current_phase,
|
|
282
294
|
closeoutNotice,
|
|
283
295
|
orchestratorCheckpoint,
|
|
296
|
+
dashboardPath,
|
|
284
297
|
});
|
|
285
298
|
}
|
|
286
299
|
export async function handleSkill(args) {
|
|
@@ -684,7 +697,9 @@ export async function handleUsage(args) {
|
|
|
684
697
|
export async function handleDashboard(args) {
|
|
685
698
|
const cwd = await validateCwd(args.cwd);
|
|
686
699
|
await requireWorkspace(cwd);
|
|
687
|
-
await writeDashboard(cwd, PACKAGE_VERSION)
|
|
700
|
+
if (!(await writeDashboard(cwd, PACKAGE_VERSION))) {
|
|
701
|
+
throw new McpError(ErrorCode.InvalidRequest, "Dashboard render failed: the workspace state could not be gathered or .codecarto/dashboard.html is not writable.");
|
|
702
|
+
}
|
|
688
703
|
return textResult("Dashboard regenerated: .codecarto/dashboard.html", { path: ".codecarto/dashboard.html" });
|
|
689
704
|
}
|
|
690
705
|
export async function handleListSkills(args) {
|
|
@@ -725,6 +740,9 @@ export async function handleAmend(args) {
|
|
|
725
740
|
const { applied, closeoutNotice } = await applyAmendment(cwd, args.name).catch((error) => {
|
|
726
741
|
throw new McpError(ErrorCode.InvalidRequest, error instanceof Error ? error.message : String(error));
|
|
727
742
|
});
|
|
743
|
+
// An amendment exists precisely to change the numbers the dashboard shows
|
|
744
|
+
// (issue #112); refresh it, reporting only a render that actually landed.
|
|
745
|
+
const dashboardPath = (await writeDashboard(cwd, PACKAGE_VERSION)) ? ".codecarto/dashboard.html" : undefined;
|
|
728
746
|
const lines = [
|
|
729
747
|
`Amendment applied.`,
|
|
730
748
|
`Open questions closed: ${applied.openQuestionsClosed.length > 0 ? applied.openQuestionsClosed.join(", ") : "none"}`,
|
|
@@ -733,11 +751,14 @@ export async function handleAmend(args) {
|
|
|
733
751
|
if (applied.unknownIds.length > 0)
|
|
734
752
|
lines.push(`Ids that matched nothing (already closed or unknown): ${applied.unknownIds.join(", ")}`);
|
|
735
753
|
lines.push(closeoutNotice);
|
|
754
|
+
if (dashboardPath)
|
|
755
|
+
lines.push(`Dashboard refreshed: ${dashboardPath}`);
|
|
736
756
|
return textResult(lines.join("\n"), {
|
|
737
757
|
openQuestionsClosed: applied.openQuestionsClosed,
|
|
738
758
|
postPipelineClosed: applied.postPipelineClosed,
|
|
739
759
|
unknownIds: applied.unknownIds,
|
|
740
760
|
closeoutNotice,
|
|
761
|
+
dashboardPath,
|
|
741
762
|
});
|
|
742
763
|
}
|
|
743
764
|
// ---------- tool registry ----------
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codecartographer-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"mcpName": "io.github.HuginnIndustries/codecartographer",
|
|
5
5
|
"description": "Turn an unfamiliar codebase into a validated reimplementation spec, then synthesize confirmed specs and a product vision into a traceable plan.",
|
|
6
6
|
"type": "module",
|