codecartographer-pi 0.10.0 → 0.11.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/GUIDE.md +10 -3
- package/.codecarto/findings/porting/SKILL.md +7 -0
- package/.codecarto/findings/reimplementation-spec/SKILL.md +10 -0
- package/.codecarto/templates/architecture-map.md +9 -0
- package/.codecarto/templates/behavioral-contracts.md +9 -0
- package/.codecarto/templates/defect-report.md +9 -0
- package/.codecarto/templates/mechanical-defects.md +9 -0
- package/.codecarto/templates/phase-checkpoint.md +41 -0
- package/.codecarto/templates/protocols-and-state.md +9 -0
- package/.codecarto/templates/reimplementation-spec-opinionated.md +10 -0
- package/.codecarto/templates/reimplementation-spec.md +10 -0
- package/.codecarto/templates/reverse-engineering-bundle.md +29 -3
- package/.codecarto/templates/semantic-defects.md +9 -0
- package/.codecarto/workflow/pipeline-architecture-only.yaml +1 -0
- package/.codecarto/workflow/pipeline-defect-scan.yaml +2 -0
- package/.codecarto/workflow/pipeline-full-with-audit.yaml +8 -3
- package/.codecarto/workflow/pipeline-full-with-deep-audit.yaml +9 -5
- package/.codecarto/workflow/pipeline-lite.yaml +3 -0
- package/.codecarto/workflow/pipeline.yaml +7 -3
- package/README.md +38 -2
- package/dist/core/dashboard.js +18 -5
- package/dist/core/prompts.js +6 -0
- package/dist/core/usage.d.ts +11 -0
- package/dist/core/usage.js +64 -46
- package/dist/extensions/codecarto/agent-rewriter.js +0 -1
- package/dist/extensions/codecarto/agent-runner.d.ts +19 -0
- package/dist/extensions/codecarto/agent-runner.js +68 -6
- package/dist/extensions/codecarto/agent-state.d.ts +3 -0
- package/dist/extensions/codecarto/agent-state.js +2 -0
- package/dist/extensions/codecarto/agent-summary.d.ts +5 -0
- package/dist/extensions/codecarto/agent-summary.js +9 -0
- package/dist/extensions/codecarto/agent-widget.js +6 -0
- package/dist/extensions/codecarto/auto-runner.js +13 -1
- package/dist/extensions/codecarto/dashboard-narrator.js +0 -1
- package/dist/extensions/codecarto/index.d.ts +1 -1
- package/dist/extensions/codecarto/index.js +28 -1
- package/dist/extensions/codecarto/phase-compaction.d.ts +11 -0
- package/dist/extensions/codecarto/phase-compaction.js +115 -0
- package/package.json +2 -2
|
@@ -155,7 +155,6 @@ async function runNarratorOnce(ctx, prompt) {
|
|
|
155
155
|
agentDir,
|
|
156
156
|
sessionManager: SessionManager.inMemory(cwd),
|
|
157
157
|
settingsManager: SettingsManager.create(cwd, agentDir),
|
|
158
|
-
modelRegistry: ctx.modelRegistry,
|
|
159
158
|
model: ctx.model,
|
|
160
159
|
tools: [],
|
|
161
160
|
resourceLoader: loader,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
export default function codeCartographerExtension(pi: ExtensionAPI): void;
|
|
@@ -6,6 +6,7 @@ import { parseDashboardFlags } from "./dashboard-flags.js";
|
|
|
6
6
|
import { narrateDashboard } from "./dashboard-narrator.js";
|
|
7
7
|
import { writeDashboard } from "./dashboard-writer.js";
|
|
8
8
|
import { parseNextFlags } from "./next-flags.js";
|
|
9
|
+
import { phaseCompactionExtension } from "./phase-compaction.js";
|
|
9
10
|
import { buildPhasePrompt, buildSkillPrompt, buildValidationSummary, canonicalPath, computePerPhaseTotals, computeTotals, createEmptyStatus, DEFAULT_PIPELINE_PATH, getNextEligiblePhase, getPipelineLabel, getWorkspaceState, isWithinPath, listSkillNames, loadCodecartoConfig, loadUsage, loadYamlFile, normalizeForComparison, packagedWorkspaceDir, pathExists, PACKAGE_VERSION, PIPELINE_ALIASES, resolvePhase, resolvePipelineChoice, stringifySimpleYaml, validatePhaseOutput, } from "../../core/index.js";
|
|
10
11
|
const STATUS_WIDGET_ID = "codecarto-widget";
|
|
11
12
|
const STATUS_LINE_ID = "codecarto-status";
|
|
@@ -62,6 +63,7 @@ function setUiState(ctx, state, extraLines = []) {
|
|
|
62
63
|
ctx.ui.setWidget(STATUS_WIDGET_ID, buildStatusLines(state, extraLines));
|
|
63
64
|
}
|
|
64
65
|
export default function codeCartographerExtension(pi) {
|
|
66
|
+
phaseCompactionExtension(pi);
|
|
65
67
|
let lastFeedbackLines = [];
|
|
66
68
|
let codecartoModeActive = false;
|
|
67
69
|
const readWorkspaceState = async (ctx, notifyOnError = true) => {
|
|
@@ -143,6 +145,28 @@ export default function codeCartographerExtension(pi) {
|
|
|
143
145
|
}
|
|
144
146
|
return undefined;
|
|
145
147
|
});
|
|
148
|
+
pi.registerCommand("codecarto-open", {
|
|
149
|
+
description: "Activate an existing .codecarto workspace without resetting durable state",
|
|
150
|
+
handler: async (_args, ctx) => {
|
|
151
|
+
const workspaceDir = join(ctx.cwd, ".codecarto");
|
|
152
|
+
if (!(await pathExists(join(workspaceDir, "workflow", "status.yaml")))) {
|
|
153
|
+
ctx.ui.notify("No existing CodeCartographer workspace found. Run /codecarto-init first.", "warning");
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
const state = await getWorkspaceState(ctx.cwd);
|
|
158
|
+
codecartoModeActive = true;
|
|
159
|
+
lastFeedbackLines = [`Opened existing workspace: ${getPipelineLabel(state.status.pipeline)}`];
|
|
160
|
+
pi.setActiveTools(SAFE_TOOL_NAMES);
|
|
161
|
+
await refreshWorkspaceUi(ctx, lastFeedbackLines);
|
|
162
|
+
ctx.ui.notify("Opened existing CodeCartographer workspace without resetting state.", "info");
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
166
|
+
ctx.ui.notify(`Unable to open CodeCartographer workspace: ${message}`, "error");
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
});
|
|
146
170
|
pi.registerCommand("codecarto-init", {
|
|
147
171
|
description: "Initialize .codecarto/ in the current repository",
|
|
148
172
|
getArgumentCompletions: (prefix) => {
|
|
@@ -426,11 +450,14 @@ export default function codeCartographerExtension(pi) {
|
|
|
426
450
|
lines.push(`Total runs: ${totals.runs}`);
|
|
427
451
|
lines.push(`Total tokens: ${formatUsageTokens(totals.tokens.input)} in · ${formatUsageTokens(totals.tokens.output)} out · ${formatUsageTokens(totals.tokens.cache_write)} cache-write`);
|
|
428
452
|
lines.push(`Total duration: ${formatUsageDuration(totals.duration_ms)} · ${totals.tool_uses} tool uses`);
|
|
453
|
+
lines.push(totals.compaction_runs > 0
|
|
454
|
+
? `Compactions: ${totals.compactions.successful} successful · ${totals.compactions.failed} failed · ${totals.compactions.aborted} aborted`
|
|
455
|
+
: "Compactions: unavailable — historical or host usage records did not report compaction events");
|
|
429
456
|
lines.push("");
|
|
430
457
|
lines.push("Per-phase totals:");
|
|
431
458
|
for (const [phaseId, t] of perPhase) {
|
|
432
459
|
const tokensTotal = t.tokens.input + t.tokens.output;
|
|
433
|
-
lines.push(` ${phaseId}: ${t.runs} run${t.runs === 1 ? "" : "s"} · ${formatUsageTokens(tokensTotal)} tokens · ${t.tool_uses} tool uses · ${formatUsageDuration(t.duration_ms)}`);
|
|
460
|
+
lines.push(` ${phaseId}: ${t.runs} run${t.runs === 1 ? "" : "s"} · ${formatUsageTokens(tokensTotal)} tokens · ${t.tool_uses} tool uses · ${t.compaction_runs > 0 ? `${t.compactions.successful + t.compactions.failed + t.compactions.aborted} compactions` : "compactions unavailable"} · ${formatUsageDuration(t.duration_ms)}`);
|
|
434
461
|
}
|
|
435
462
|
lastFeedbackLines = lines;
|
|
436
463
|
setUiState(ctx, state, lastFeedbackLines);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
export declare function phaseIdFromSessionName(sessionName: string | undefined): string | null;
|
|
3
|
+
export declare function buildPhaseCompactionInstructions(phaseId: string, primaryOutput?: string): string;
|
|
4
|
+
export declare function writePhaseCheckpoint(cwd: string, phaseId: string, summary: string, tokensBefore: number): Promise<string>;
|
|
5
|
+
/**
|
|
6
|
+
* Phase-only compaction hooks shared by the parent extension and isolated
|
|
7
|
+
* child sessions. Keeping this as a standalone inline extension ensures that
|
|
8
|
+
* children spawned from an explicitly loaded (`pi -e ...`) CodeCartographer
|
|
9
|
+
* extension receive the same checkpoint behavior as globally installed runs.
|
|
10
|
+
*/
|
|
11
|
+
export declare function phaseCompactionExtension(pi: ExtensionAPI): void;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { mkdir, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { compact } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { canonicalPath, getWorkspaceState, isWithinPath } from "../../core/index.js";
|
|
5
|
+
const PHASE_SESSION_PREFIX = "CodeCartographer phase: ";
|
|
6
|
+
export function phaseIdFromSessionName(sessionName) {
|
|
7
|
+
if (!sessionName?.startsWith(PHASE_SESSION_PREFIX))
|
|
8
|
+
return null;
|
|
9
|
+
const phaseId = sessionName.slice(PHASE_SESSION_PREFIX.length).trim();
|
|
10
|
+
return /^[a-z0-9][a-z0-9-]*$/.test(phaseId) ? phaseId : null;
|
|
11
|
+
}
|
|
12
|
+
export function buildPhaseCompactionInstructions(phaseId, primaryOutput) {
|
|
13
|
+
const output = primaryOutput ? `.codecarto/${primaryOutput}` : "the phase's declared primary output";
|
|
14
|
+
return [
|
|
15
|
+
`This is a CodeCartographer phase session for ${phaseId}.`,
|
|
16
|
+
`Produce a phase-aware continuation summary for ${output}.`,
|
|
17
|
+
"Preserve these details explicitly:",
|
|
18
|
+
"- phase goal and constraints",
|
|
19
|
+
"- evidence-backed conclusions and their evidence levels",
|
|
20
|
+
"- files inspected and subsystems covered or skipped",
|
|
21
|
+
"- primary-output sections already written and sections still missing",
|
|
22
|
+
"- edits already made under .codecarto/",
|
|
23
|
+
"- open questions and carry-forward candidates",
|
|
24
|
+
"- validation criteria already satisfied and validation criteria still at risk",
|
|
25
|
+
"- exact next steps needed to finish and validate the phase.",
|
|
26
|
+
"Do not turn an inference into an observed fact. Retain file paths, finding IDs, and unresolved gaps verbatim where possible.",
|
|
27
|
+
].join("\n");
|
|
28
|
+
}
|
|
29
|
+
export async function writePhaseCheckpoint(cwd, phaseId, summary, tokensBefore) {
|
|
30
|
+
const dir = join(cwd, ".codecarto", "scratch", "checkpoints");
|
|
31
|
+
const target = join(dir, `${phaseId}.md`);
|
|
32
|
+
const temp = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
33
|
+
await mkdir(dir, { recursive: true });
|
|
34
|
+
const content = [
|
|
35
|
+
"---",
|
|
36
|
+
`phase: ${phaseId}`,
|
|
37
|
+
`updated_at: ${new Date().toISOString()}`,
|
|
38
|
+
`tokens_before: ${tokensBefore}`,
|
|
39
|
+
"source: pi-compaction",
|
|
40
|
+
"---",
|
|
41
|
+
"",
|
|
42
|
+
"# Phase checkpoint",
|
|
43
|
+
"",
|
|
44
|
+
summary.trim(),
|
|
45
|
+
"",
|
|
46
|
+
].join("\n");
|
|
47
|
+
await writeFile(temp, content, "utf8");
|
|
48
|
+
await rename(temp, target);
|
|
49
|
+
return target;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Phase-only compaction hooks shared by the parent extension and isolated
|
|
53
|
+
* child sessions. Keeping this as a standalone inline extension ensures that
|
|
54
|
+
* children spawned from an explicitly loaded (`pi -e ...`) CodeCartographer
|
|
55
|
+
* extension receive the same checkpoint behavior as globally installed runs.
|
|
56
|
+
*/
|
|
57
|
+
export function phaseCompactionExtension(pi) {
|
|
58
|
+
pi.on("tool_call", async (event, ctx) => {
|
|
59
|
+
if (!phaseIdFromSessionName(ctx.sessionManager.getSessionName()))
|
|
60
|
+
return undefined;
|
|
61
|
+
if (event.toolName === "bash") {
|
|
62
|
+
return { block: true, reason: "CodeCartographer phase sessions disable bash to keep source analysis read-only." };
|
|
63
|
+
}
|
|
64
|
+
if (event.toolName === "edit" || event.toolName === "write") {
|
|
65
|
+
const inputPath = typeof event.input.path === "string" ? event.input.path : "";
|
|
66
|
+
const strippedPath = inputPath.startsWith("@") ? inputPath.slice(1) : inputPath;
|
|
67
|
+
const targetPath = await canonicalPath(resolve(ctx.cwd, strippedPath));
|
|
68
|
+
const allowedRoot = await canonicalPath(join(ctx.cwd, ".codecarto"));
|
|
69
|
+
if (!isWithinPath(targetPath, allowedRoot)) {
|
|
70
|
+
return {
|
|
71
|
+
block: true,
|
|
72
|
+
reason: `CodeCartographer phase sessions only allow ${event.toolName} within .codecarto/`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
});
|
|
78
|
+
pi.on("session_before_compact", async (event, ctx) => {
|
|
79
|
+
const phaseId = phaseIdFromSessionName(ctx.sessionManager.getSessionName());
|
|
80
|
+
if (!phaseId || !ctx.model)
|
|
81
|
+
return undefined;
|
|
82
|
+
try {
|
|
83
|
+
const state = await getWorkspaceState(ctx.cwd);
|
|
84
|
+
const phase = state.pipeline.phases.find((candidate) => candidate.id === phaseId);
|
|
85
|
+
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(ctx.model);
|
|
86
|
+
if (!auth.ok || !auth.apiKey)
|
|
87
|
+
return undefined;
|
|
88
|
+
const instructions = buildPhaseCompactionInstructions(phaseId, phase?.primary_output);
|
|
89
|
+
const result = await compact(event.preparation, ctx.model, auth.apiKey, auth.headers, instructions, event.signal);
|
|
90
|
+
return { compaction: result };
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
if (ctx.hasUI) {
|
|
94
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
95
|
+
ctx.ui.notify(`Phase-aware compaction unavailable (${message}); using host default.`, "warning");
|
|
96
|
+
}
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
pi.on("session_compact", async (event, ctx) => {
|
|
101
|
+
const phaseId = phaseIdFromSessionName(ctx.sessionManager.getSessionName());
|
|
102
|
+
if (!phaseId)
|
|
103
|
+
return;
|
|
104
|
+
try {
|
|
105
|
+
await writePhaseCheckpoint(ctx.cwd, phaseId, event.compactionEntry.summary, event.compactionEntry.tokensBefore);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
109
|
+
if (ctx.hasUI)
|
|
110
|
+
ctx.ui.notify(`Phase checkpoint could not be written: ${message}`, "warning");
|
|
111
|
+
else
|
|
112
|
+
console.warn(`[codecarto] Phase checkpoint could not be written: ${message}`);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codecartographer-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "CodeCartographer packaged for Pi as an extension-driven workflow wrapper.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@modelcontextprotocol/sdk": "^1.29.0"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
|
-
"@earendil-works/pi-coding-agent": "
|
|
46
|
+
"@earendil-works/pi-coding-agent": "^0.80.10",
|
|
47
47
|
"@sinclair/typebox": "*"
|
|
48
48
|
},
|
|
49
49
|
"pi": {
|