codecartographer-pi 0.12.2 → 0.12.4

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 CHANGED
@@ -86,7 +86,7 @@ Add to your host config (`~/.config/claude-code/config.json`, `claude_desktop_co
86
86
  }
87
87
  ```
88
88
 
89
- Official MCP Registry server name: `io.github.HuginnIndustries/codecartographer`.
89
+ [Official MCP Registry listing](https://registry.modelcontextprotocol.io/?search=CodeCartographer): `io.github.HuginnIndustries/codecartographer`.
90
90
 
91
91
  ### Drop-in template (one-off / evaluation)
92
92
 
@@ -288,7 +288,7 @@ Beyond the slash commands, the Pi extension layers on:
288
288
  | `/codecarto-init [variant]` | Copy `.codecarto/` into the current repository, select pipeline variant |
289
289
  | `/codecarto-open` | Activate an existing `.codecarto/` workspace in a new Pi session without resetting durable state |
290
290
  | `/codecarto-status` | Current phase, progress, open questions |
291
- | `/codecarto-next [--auto [--strict]] [--llm-steer \| --no-llm-steer]` | Spawn the next eligible phase as a sub-agent. `--auto` walks the full pipeline end-to-end (auto-validate + auto-complete + advance); `--strict` flips the `PASS WITH GAPS` rule from "advance" to "pause". |
291
+ | `/codecarto-next [--auto [--strict]] [--llm-steer \| --no-llm-steer]` | Spawn the next eligible phase as a sub-agent. After the sub-agent finishes, auto-validates and auto-completes the phase so `status.yaml` advances without manual steps. `--auto` walks the full pipeline end-to-end (same validate + complete + advance loop, repeated); `--strict` flips the `PASS WITH GAPS` rule from "advance" to "pause". |
292
292
  | `/codecarto-phase <id>` | Force a specific phase, even out of pipeline order |
293
293
  | `/codecarto-validate [phase]` | Validate a phase output against completion criteria |
294
294
  | `/codecarto-complete [phase]` | Validate and atomically apply the phase handoff, canonical status, closeout, and log entry |
@@ -78,25 +78,25 @@ export async function runPhasePreflight(state, phase) {
78
78
  if (checks.has("requires-vision-input")) {
79
79
  const visionPath = join(state.workspaceDir, SYNTHESIS_VISION_INPUT_PATH);
80
80
  if (!(await pathExists(visionPath))) {
81
- throw new PhasePreflightError(phase.id, `the vision brief is missing at .codecarto/${SYNTHESIS_VISION_INPUT_PATH}. Create it before starting synthesis.`);
81
+ throw new PhasePreflightError(phase.id, `the vision brief is missing at .codecarto/${SYNTHESIS_VISION_INPUT_PATH}. Create that file and describe your product intent (audience, problem, desired outcome, constraints, non-goals). See .codecarto/templates/vision.md for the expected structure.`);
82
82
  }
83
83
  const rawVision = await readFile(visionPath, "utf8");
84
84
  if (!hasMeaningfulVisionContent(rawVision)) {
85
- throw new PhasePreflightError(phase.id, `the vision brief at .codecarto/${SYNTHESIS_VISION_INPUT_PATH} is still empty. Describe the audience, problem, and desired outcome, then retry.`);
85
+ throw new PhasePreflightError(phase.id, `the vision brief at .codecarto/${SYNTHESIS_VISION_INPUT_PATH} appears to be empty or only contains comments. Write your product intent into that file — at minimum: who the product is for, what problem it solves, and what outcome you want. See .codecarto/templates/vision.md for the full structure.`);
86
86
  }
87
87
  }
88
88
  if (checks.has("requires-library")) {
89
89
  const config = await loadCodecartoConfig(state.workspaceDir);
90
90
  if (!config.library.path) {
91
- throw new PhasePreflightError(phase.id, "no library.path is configured. Set it in ~/.codecarto/config.yaml or .codecarto/workflow/config.yaml.");
91
+ throw new PhasePreflightError(phase.id, "no library.path is configured. Create a library directory with a .codecarto-library marker file, then set library.path in ~/.codecarto/config.yaml or .codecarto/workflow/config.yaml. Example config:\n library:\n path: ~/codecarto-library\n publish_confirm: true");
92
92
  }
93
93
  const marker = await discoverLibrary(config.library.path);
94
94
  if (!marker) {
95
- throw new PhasePreflightError(phase.id, `no CodeCartographer library was found at ${config.library.path} (missing .codecarto-library).`);
95
+ throw new PhasePreflightError(phase.id, `no CodeCartographer library was found at ${config.library.path} (missing .codecarto-library). Create a .codecarto-library marker file in that directory with: {"schema_version": 1, "name": "personal-library", "visibility": "internal", "namespaced": false}`);
96
96
  }
97
97
  const entries = await listEntries(config.library.path);
98
98
  if (entries.length === 0) {
99
- throw new PhasePreflightError(phase.id, `the configured library at ${config.library.path} has no entries. Publish at least one reimplementation spec first.`);
99
+ throw new PhasePreflightError(phase.id, `the configured library at ${config.library.path} has no entries. Run a reverse-engineering pipeline (e.g. /codecarto-init full-with-deep-audit) on a source repository, then use /codecarto-publish to publish at least one reimplementation spec into the library before starting synthesis.`);
100
100
  }
101
101
  result.libraryPath = config.library.path;
102
102
  result.libraryName = marker.name;
@@ -110,13 +110,13 @@ export async function runPhasePreflight(state, phase) {
110
110
  result.confirmedSelections = parseConfirmedProposalSelections(await readFile(proposalPath, "utf8"));
111
111
  result.confirmedEntries = result.confirmedSelections.map((selection) => selection.ref);
112
112
  if (result.confirmedSelections.length === 0) {
113
- throw new PhasePreflightError(phase.id, `no library entries are confirmed in .codecarto/${SYNTHESIS_PROPOSAL_PATH}. Change at least one [ ] checkbox to [x], then retry.`);
113
+ throw new PhasePreflightError(phase.id, `no library entries are confirmed in .codecarto/${SYNTHESIS_PROPOSAL_PATH}. Open that file and change at least one [ ] checkbox to [x] for the entry you want to include, then retry.`);
114
114
  }
115
115
  const available = new Map(result.libraryEntries.map((entry) => [entry.ref, entry]));
116
116
  for (const selection of result.confirmedSelections) {
117
117
  const entry = available.get(selection.ref);
118
118
  if (!entry || !entry.versions.includes(selection.version)) {
119
- throw new PhasePreflightError(phase.id, `confirmed selection ${selection.ref}@v${selection.version} is not present in the configured library. Re-run the proposal phase or correct the checked row.`);
119
+ throw new PhasePreflightError(phase.id, `confirmed selection ${selection.ref}@v${selection.version} is not present in the configured library. Edit .codecarto/${SYNTHESIS_PROPOSAL_PATH} to correct the checked row (change [x] back to [ ] or update the version), or re-run the goal-synthesis-propose phase to regenerate the proposal.`);
120
120
  }
121
121
  selection.specPath = specPathForVersion(result.libraryPath, selection.ref, selection.version);
122
122
  }
@@ -66,7 +66,7 @@ function formatTrailer(input) {
66
66
  lines.push(`Phase transcript: \`${input.sessionFile}\` (open via \`/resume\`).`);
67
67
  }
68
68
  if (input.status === "completed") {
69
- lines.push("Run `/codecarto-validate` to check the output, then `/codecarto-complete` to advance.");
69
+ lines.push("Auto-validating and completing the phase — check the status widget for the result.");
70
70
  }
71
71
  return lines.join("\n");
72
72
  }
@@ -1,4 +1,4 @@
1
- import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
1
+ import { cp, mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
2
  import { basename, join, resolve } from "node:path";
3
3
  import { autoCompletePhase, buildAutoSummary, isPhaseRunning, runAuto, runSinglePhase } from "./auto-runner.js";
4
4
  import { disposeAgentsWidget } from "./agent-widget.js";
@@ -220,10 +220,13 @@ export default function codeCartographerExtension(pi) {
220
220
  if (targetExists) {
221
221
  const sameWorkspace = normalizeForComparison(await canonicalPath(targetWorkspaceDir)) === normalizeForComparison(await canonicalPath(sourceWorkspaceDir));
222
222
  if (!sameWorkspace) {
223
- const overwrite = await ctx.ui.confirm("CodeCartographer already exists", "A .codecarto/ directory already exists in this repository. Overwrite it?");
223
+ const overwrite = await ctx.ui.confirm("CodeCartographer already exists — data will be lost", "A .codecarto/ directory already exists in this repository. Re-initializing will back up the existing workspace to .codecarto-backup-TIMESTAMP/ and create a fresh one. All phase findings, handoffs, usage data, closeouts, and progress will be moved to the backup. Consider /codecarto-open to reattach without resetting. Continue?");
224
224
  if (!overwrite)
225
225
  return;
226
- await rm(targetWorkspaceDir, { recursive: true, force: true });
226
+ const backupDir = join(ctx.cwd, `.codecarto-backup-${new Date().toISOString().replace(/[:.]/g, "-")}`);
227
+ await rename(targetWorkspaceDir, backupDir);
228
+ if (ctx.hasUI)
229
+ ctx.ui.notify(`Backed up existing workspace to ${basename(backupDir)}/`, "info");
227
230
  }
228
231
  }
229
232
  if (!(await pathExists(targetWorkspaceDir))) {
@@ -344,7 +347,59 @@ export default function codeCartographerExtension(pi) {
344
347
  // Fire-and-forget: keep the TUI responsive while the sub-agent works.
345
348
  // runSinglePhase handles all side effects (steering message, notify,
346
349
  // phase summary, recordUsage, dashboard regen, clearPhase linger).
350
+ // After the sub-agent finishes, auto-validate and auto-complete the
351
+ // phase so status.yaml advances without requiring the user to manually
352
+ // run /codecarto-validate then /codecarto-complete. This mirrors what
353
+ // the auto loop (runAuto) does after each phase.
347
354
  void runSinglePhase(ctx, pi, state, phase, { llmSteerEnabled, signal: ctx.signal, preflight })
355
+ .then(async (result) => {
356
+ if (result.status !== "completed")
357
+ return;
358
+ // Refresh state from disk — the sub-agent may have written
359
+ // findings that the validator needs to read.
360
+ const stateForValidation = (await getWorkspaceState(ctx.cwd)) ?? state;
361
+ const validation = await validatePhaseOutput(stateForValidation, phase.id).catch((error) => (error instanceof Error ? error : new Error(String(error))));
362
+ if (validation instanceof Error) {
363
+ if (ctx.hasUI)
364
+ ctx.ui.notify(`Auto-validation error for ${phase.id}: ${validation.message}`, "warning");
365
+ lastFeedbackLines = [`Validation error: ${validation.message}`, "Run `/codecarto-validate` then `/codecarto-complete` manually."];
366
+ return;
367
+ }
368
+ if (validation.overall === "FAIL" || validation.overall === "MISSING") {
369
+ if (ctx.hasUI)
370
+ ctx.ui.notify(`Phase ${phase.id} validation: ${validation.overall}. Fix the output, then re-run /codecarto-next.`, "warning");
371
+ lastFeedbackLines = buildValidationSummary(validation);
372
+ return;
373
+ }
374
+ // PASS or PASS WITH GAPS — auto-complete the phase.
375
+ try {
376
+ const { updatedState, closeoutNotice } = await autoCompletePhase(ctx, validation);
377
+ if (ctx.hasUI) {
378
+ ctx.ui.notify(`Phase ${phase.id} auto-completed (validation: ${validation.overall}).`, validation.overall === "PASS WITH GAPS" ? "warning" : "info");
379
+ if (closeoutNotice)
380
+ ctx.ui.notify(closeoutNotice, "info");
381
+ }
382
+ lastFeedbackLines = [
383
+ `Completed phase: ${validation.phaseId}`,
384
+ `Validation: ${validation.overall}`,
385
+ `Next phase: ${updatedState.status.current_phase}`,
386
+ ];
387
+ if (closeoutNotice)
388
+ lastFeedbackLines.push(closeoutNotice);
389
+ }
390
+ catch (error) {
391
+ const message = error instanceof Error ? error.message : String(error);
392
+ if (ctx.hasUI)
393
+ ctx.ui.notify(`Auto-completion failed for ${phase.id}: ${message}. Run /codecarto-complete manually.`, "warning");
394
+ lastFeedbackLines = [`Auto-completion failed: ${message}`, "Run `/codecarto-complete` manually."];
395
+ }
396
+ })
397
+ .catch((error) => {
398
+ const message = error instanceof Error ? error.message : String(error);
399
+ if (ctx.hasUI)
400
+ ctx.ui.notify(`Post-phase processing error for ${phase.id}: ${message}`, "warning");
401
+ lastFeedbackLines = [`Post-phase error: ${message}`];
402
+ })
348
403
  .finally(() => {
349
404
  // Refresh the status widget after the phase resolves so the
350
405
  // "Open questions / Carry-forward / Next" lines reflect any
@@ -486,12 +541,12 @@ export default function codeCartographerExtension(pi) {
486
541
  return;
487
542
  const config = await loadCodecartoConfig(state.workspaceDir);
488
543
  if (!config.library.path) {
489
- ctx.ui.notify("No library.path is configured. Set it in ~/.codecarto/config.yaml or .codecarto/workflow/config.yaml.", "error");
544
+ ctx.ui.notify("No library.path is configured. Create a library directory with a .codecarto-library marker, then set library.path in ~/.codecarto/config.yaml or .codecarto/workflow/config.yaml.", "error");
490
545
  return;
491
546
  }
492
547
  const marker = await discoverLibrary(config.library.path);
493
548
  if (!marker) {
494
- ctx.ui.notify(`No CodeCartographer library at ${config.library.path} (missing .codecarto-library).`, "error");
549
+ ctx.ui.notify(`No CodeCartographer library at ${config.library.path} (missing .codecarto-library). Create a .codecarto-library marker file in that directory.`, "error");
495
550
  return;
496
551
  }
497
552
  const phase = resolvePhase(state, "reimplementation-spec");
@@ -13,7 +13,7 @@
13
13
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
14
14
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
15
15
  import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js";
16
- import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
16
+ import { cp, mkdir, readFile, rename, writeFile } from "node:fs/promises";
17
17
  import { basename, isAbsolute, join } from "node:path";
18
18
  import { buildPhasePrompt, buildSkillPrompt, buildValidationSummary, canonicalPath, completeValidatedPhase, createEmptyStatus, DEFAULT_PIPELINE_PATH, deriveSlug, discoverLibrary, getNextEligiblePhase, getPipelineLabel, getWorkspaceState, isValidSlug, listEntries, listSkillNames, loadCodecartoConfig, loadYamlFile, normalizeForComparison, PACKAGE_VERSION, packagedWorkspaceDir, pathExists, PhasePreflightError, publishEntry, reindex as libraryReindex, resolvePhase, resolvePipelineChoice, stringifySimpleYaml, validatePhaseOutput, } from "../core/index.js";
19
19
  // ---------- input helpers ----------
@@ -77,9 +77,10 @@ export async function handleInit(args) {
77
77
  }
78
78
  if (targetExists && !sameWorkspace) {
79
79
  if (!args.force) {
80
- throw new McpError(ErrorCode.InvalidRequest, `A .codecarto/ directory already exists at ${targetWorkspaceDir}. Pass force: true to overwrite it.`);
80
+ throw new McpError(ErrorCode.InvalidRequest, `A .codecarto/ directory already exists at ${targetWorkspaceDir}. Pass force: true to back it up and reinitialize. Warning: this moves all existing findings, handoffs, usage data, closeouts, and phase progress to a .codecarto-backup-TIMESTAMP/ directory.`);
81
81
  }
82
- await rm(targetWorkspaceDir, { recursive: true, force: true });
82
+ const backupDir = join(cwd, `.codecarto-backup-${new Date().toISOString().replace(/[:.]/g, "-")}`);
83
+ await rename(targetWorkspaceDir, backupDir);
83
84
  }
84
85
  if (!(await pathExists(targetWorkspaceDir))) {
85
86
  await mkdir(cwd, { recursive: true });
@@ -463,7 +464,7 @@ export async function handleLibraryReindex(args) {
463
464
  const TOOLS = [
464
465
  {
465
466
  name: "codecarto_init",
466
- description: "Initialize a CodeCartographer workspace (.codecarto/) in a target repository. Copies the packaged framework template and writes a fresh status.yaml for the chosen pipeline. Errors if .codecarto/ already exists unless force is true.",
467
+ description: "Initialize a CodeCartographer workspace (.codecarto/) in a target repository. Copies the packaged framework template and writes a fresh status.yaml for the chosen pipeline. If .codecarto/ already exists, pass force: true to back up the existing workspace to .codecarto-backup-TIMESTAMP/ and create a fresh one. Warning: backing up moves all existing findings, handoffs, usage data, closeouts, and phase progress to the backup directory.",
467
468
  inputSchema: {
468
469
  type: "object",
469
470
  properties: {
@@ -474,7 +475,7 @@ const TOOLS = [
474
475
  },
475
476
  force: {
476
477
  type: "boolean",
477
- description: "Overwrite an existing .codecarto/ directory if present (default false).",
478
+ description: "Back up and overwrite an existing .codecarto/ directory if present (default false).",
478
479
  },
479
480
  },
480
481
  required: ["cwd"],
@@ -625,7 +626,7 @@ const HANDLERS = {
625
626
  };
626
627
  // ---------- server bootstrap ----------
627
628
  export function buildServer() {
628
- const server = new Server({ name: "codecartographer", version: "0.2.0" }, { capabilities: { tools: {} } });
629
+ const server = new Server({ name: "codecartographer", version: PACKAGE_VERSION }, { capabilities: { tools: {} } });
629
630
  server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
630
631
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
631
632
  const handler = HANDLERS[request.params.name];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codecartographer-pi",
3
- "version": "0.12.2",
3
+ "version": "0.12.4",
4
4
  "mcpName": "io.github.HuginnIndustries/codecartographer",
5
5
  "description": "Evidence-backed reverse engineering and human-gated software planning for Pi and MCP coding agents.",
6
6
  "type": "module",