codecartographer-pi 0.1.4 → 0.6.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/config.yaml +13 -0
- package/README.md +9 -6
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.js +2 -1
- package/dist/core/orchestrator-config.d.ts +20 -0
- package/dist/core/orchestrator-config.js +45 -0
- package/dist/core/usage.d.ts +31 -0
- package/dist/core/usage.js +94 -0
- package/dist/extensions/codecarto/agent-rewriter.d.ts +20 -0
- package/dist/extensions/codecarto/agent-rewriter.js +149 -0
- package/dist/extensions/codecarto/agent-runner.d.ts +44 -0
- package/dist/extensions/codecarto/agent-runner.js +169 -0
- package/dist/extensions/codecarto/agent-state.d.ts +35 -0
- package/dist/extensions/codecarto/agent-state.js +46 -0
- package/dist/extensions/codecarto/agent-summary.d.ts +17 -0
- package/dist/extensions/codecarto/agent-summary.js +79 -0
- package/dist/extensions/codecarto/agent-widget.d.ts +26 -0
- package/dist/extensions/codecarto/agent-widget.js +260 -0
- package/dist/extensions/codecarto/index.js +191 -78
- package/dist/extensions/codecarto/next-flags.d.ts +6 -0
- package/dist/extensions/codecarto/next-flags.js +19 -0
- package/dist/mcp-server/server.js +1 -1
- package/package.json +2 -2
- package/dist/core/orchestrator.d.ts +0 -6
- package/dist/core/orchestrator.js +0 -36
|
@@ -1,9 +1,52 @@
|
|
|
1
1
|
import { cp, mkdir, rm, writeFile } from "node:fs/promises";
|
|
2
2
|
import { basename, join, resolve } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { runPhase } from "./agent-runner.js";
|
|
4
|
+
import { rewritePhasePrompt } from "./agent-rewriter.js";
|
|
5
|
+
import { clearPhase, finishPhase, getPhaseActivity, startPhase } from "./agent-state.js";
|
|
6
|
+
import { buildPhaseSummary } from "./agent-summary.js";
|
|
7
|
+
import { disposeAgentsWidget, getAgentsWidget } from "./agent-widget.js";
|
|
8
|
+
import { parseNextFlags } from "./next-flags.js";
|
|
9
|
+
import { appendUsageRun, buildPhasePrompt, buildSkillPrompt, buildThreadLogEntry, buildValidationSummary, canonicalPath, closeoutFileName, computePerPhaseTotals, computeTotals, createEmptyStatus, dateOnly, DEFAULT_PIPELINE_PATH, ensureCloseoutStub, getNextEligiblePhase, getPipelineLabel, getWorkspaceState, isWithinPath, listSkillNames, loadCodecartoConfig, loadUsage, loadYamlFile, normalizeForComparison, normalizeStatus, packagedWorkspaceDir, pathExists, PIPELINE_ALIASES, resolvePhase, resolvePipelineChoice, stringifySimpleYaml, uniqueStrings, updateStatusAtomically, validatePhaseOutput, } from "../../core/index.js";
|
|
4
10
|
const STATUS_WIDGET_ID = "codecarto-widget";
|
|
5
11
|
const STATUS_LINE_ID = "codecarto-status";
|
|
6
12
|
const SAFE_TOOL_NAMES = ["read", "grep", "find", "ls", "edit", "write"];
|
|
13
|
+
async function recordUsage(workspaceDir, phaseId, status, activity) {
|
|
14
|
+
try {
|
|
15
|
+
await appendUsageRun(workspaceDir, {
|
|
16
|
+
timestamp: new Date().toISOString(),
|
|
17
|
+
phase: phaseId,
|
|
18
|
+
status,
|
|
19
|
+
turn_count: activity.turnCount,
|
|
20
|
+
tool_uses: activity.toolUses,
|
|
21
|
+
duration_ms: (activity.completedAt ?? Date.now()) - activity.startedAt,
|
|
22
|
+
tokens: {
|
|
23
|
+
input: activity.lifetimeUsage.input,
|
|
24
|
+
output: activity.lifetimeUsage.output,
|
|
25
|
+
cache_write: activity.lifetimeUsage.cacheWrite,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Local usage logging is best-effort. A full disk or permission
|
|
31
|
+
// failure shouldn't surface as a phase error to the user.
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function formatUsageTokens(count) {
|
|
35
|
+
if (count >= 1_000_000)
|
|
36
|
+
return `${(count / 1_000_000).toFixed(2)}M`;
|
|
37
|
+
if (count >= 1_000)
|
|
38
|
+
return `${(count / 1_000).toFixed(1)}k`;
|
|
39
|
+
return `${count}`;
|
|
40
|
+
}
|
|
41
|
+
function formatUsageDuration(ms) {
|
|
42
|
+
if (ms < 1000)
|
|
43
|
+
return `${ms}ms`;
|
|
44
|
+
if (ms < 60_000)
|
|
45
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
46
|
+
const minutes = Math.floor(ms / 60_000);
|
|
47
|
+
const seconds = Math.floor((ms % 60_000) / 1000);
|
|
48
|
+
return `${minutes}m${seconds.toString().padStart(2, "0")}s`;
|
|
49
|
+
}
|
|
7
50
|
function buildStatusLines(state, extraLines = []) {
|
|
8
51
|
const nextPhase = getNextEligiblePhase(state);
|
|
9
52
|
const currentPhase = nextPhase?.id ?? state.status.current_phase ?? "complete";
|
|
@@ -79,6 +122,11 @@ export default function codeCartographerExtension(pi) {
|
|
|
79
122
|
return;
|
|
80
123
|
pi.setActiveTools(SAFE_TOOL_NAMES);
|
|
81
124
|
});
|
|
125
|
+
pi.on("session_shutdown", async () => {
|
|
126
|
+
// Tear down the persistent agents widget so we don't leak the timer
|
|
127
|
+
// or render against a torn-down UI context after a session swap.
|
|
128
|
+
disposeAgentsWidget();
|
|
129
|
+
});
|
|
82
130
|
pi.on("agent_end", async (_event, ctx) => {
|
|
83
131
|
await refreshWorkspaceUi(ctx);
|
|
84
132
|
});
|
|
@@ -153,21 +201,6 @@ export default function codeCartographerExtension(pi) {
|
|
|
153
201
|
normalizedStatus.last_updated = new Date().toISOString();
|
|
154
202
|
await writeFile(rawStatusPath, `${stringifySimpleYaml(normalizedStatus)}\n`, "utf8");
|
|
155
203
|
lastFeedbackLines = [`Initialized workspace with pipeline: ${getPipelineLabel(selectedPipelinePath)}`];
|
|
156
|
-
// Claim the current Pi session as the orchestrator for this workspace.
|
|
157
|
-
// /codecarto-next will then spawn each phase as a child session, keeping
|
|
158
|
-
// the orchestrator's context window clean. The pointer is gitignored
|
|
159
|
-
// (workflow/.orchestrator.local.yaml) so it's machine-local. If we have
|
|
160
|
-
// no session file (rare; Pi running headless), skip silently — handlers
|
|
161
|
-
// fall back to in-place phase prompts.
|
|
162
|
-
const orchestratorSessionFile = ctx.sessionManager.getSessionFile();
|
|
163
|
-
const orchestratorSessionId = ctx.sessionManager.getSessionId();
|
|
164
|
-
if (orchestratorSessionFile && orchestratorSessionId) {
|
|
165
|
-
await writeOrchestratorState(ctx.cwd, {
|
|
166
|
-
sessionFile: orchestratorSessionFile,
|
|
167
|
-
sessionId: orchestratorSessionId,
|
|
168
|
-
});
|
|
169
|
-
lastFeedbackLines.push(`Claimed this session as the orchestrator (${orchestratorSessionId.slice(0, 8)}…).`);
|
|
170
|
-
}
|
|
171
204
|
ctx.ui.notify(`Initialized CodeCartographer (${getPipelineLabel(selectedPipelinePath)})`, "info");
|
|
172
205
|
await ctx.reload();
|
|
173
206
|
return;
|
|
@@ -186,8 +219,19 @@ export default function codeCartographerExtension(pi) {
|
|
|
186
219
|
},
|
|
187
220
|
});
|
|
188
221
|
pi.registerCommand("codecarto-next", {
|
|
189
|
-
description: "
|
|
190
|
-
|
|
222
|
+
description: "Run the next eligible CodeCartographer phase as a sub-agent. Flags: --llm-steer / --no-llm-steer",
|
|
223
|
+
getArgumentCompletions: (prefix) => {
|
|
224
|
+
const items = ["--llm-steer", "--no-llm-steer"]
|
|
225
|
+
.filter((value) => value.startsWith(prefix))
|
|
226
|
+
.map((value) => ({ value, label: value }));
|
|
227
|
+
return items.length > 0 ? items : null;
|
|
228
|
+
},
|
|
229
|
+
handler: async (args, ctx) => {
|
|
230
|
+
const flags = parseNextFlags(args);
|
|
231
|
+
if (flags.unknown.length > 0) {
|
|
232
|
+
ctx.ui.notify(`Unknown /codecarto-next flag: ${flags.unknown.join(" ")}`, "error");
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
191
235
|
const state = await ensureWorkspaceState(ctx);
|
|
192
236
|
if (!state)
|
|
193
237
|
return;
|
|
@@ -198,71 +242,110 @@ export default function codeCartographerExtension(pi) {
|
|
|
198
242
|
ctx.ui.notify("All CodeCartographer phases are complete.", "info");
|
|
199
243
|
return;
|
|
200
244
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
// exactly as we did before sub-agent mode existed.
|
|
207
|
-
if (!orchestrator || !currentSessionFile) {
|
|
208
|
-
if (ctx.isIdle())
|
|
209
|
-
pi.sendUserMessage(prompt);
|
|
210
|
-
else
|
|
211
|
-
pi.sendUserMessage(prompt, { deliverAs: "followUp" });
|
|
212
|
-
lastFeedbackLines = [`Queued phase prompt for ${phase.id} (in-place; run /codecarto-init to enable sub-agent mode)`];
|
|
213
|
-
setUiState(ctx, state, lastFeedbackLines);
|
|
214
|
-
ctx.ui.notify(`Queued CodeCartographer phase: ${phase.id}`, "info");
|
|
245
|
+
// Reject re-entry: don't spawn a duplicate runner for a phase that's
|
|
246
|
+
// already in flight from a previous /codecarto-next invocation.
|
|
247
|
+
const existing = getPhaseActivity(phase.id);
|
|
248
|
+
if (existing && existing.status === "running") {
|
|
249
|
+
ctx.ui.notify(`Phase ${phase.id} is already running.`, "warning");
|
|
215
250
|
return;
|
|
216
251
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
await ctx.newSession({
|
|
231
|
-
parentSession: orchestrator.sessionFile,
|
|
232
|
-
withSession: async (childCtx) => {
|
|
233
|
-
childCtx.sendUserMessage(prompt);
|
|
234
|
-
},
|
|
235
|
-
});
|
|
236
|
-
return;
|
|
252
|
+
const config = await loadCodecartoConfig(state.workspaceDir);
|
|
253
|
+
const llmSteerEnabled = flags.llmSteerOverride ?? config.orchestrator.llm_steer_next_phase;
|
|
254
|
+
let prompt = await buildPhasePrompt(state, phase, false);
|
|
255
|
+
if (llmSteerEnabled) {
|
|
256
|
+
ctx.ui.notify(`Customizing ${phase.id} prompt via LLM rewriter…`, "info");
|
|
257
|
+
const rewrite = await rewritePhasePrompt({ ctx, state, originalPrompt: prompt, nextPhaseId: phase.id });
|
|
258
|
+
if (rewrite.used) {
|
|
259
|
+
prompt = rewrite.prompt;
|
|
260
|
+
ctx.ui.notify(`LLM rewriter customized ${phase.id} seed prompt.`, "info");
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
ctx.ui.notify(`LLM rewriter skipped (${rewrite.skipReason}); using stock prompt.`, "warning");
|
|
264
|
+
}
|
|
237
265
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
// `ctx`; post-switch work uses the fresh `orchestratorCtx` passed to
|
|
241
|
-
// `withSession`. Inside that callback the inner `newSession()` again
|
|
242
|
-
// invalidates `orchestratorCtx`, so the inner spawn must be the last
|
|
243
|
-
// thing the callback does.
|
|
244
|
-
lastFeedbackLines = ["Returning to orchestrator and queuing next phase as a sub-agent"];
|
|
266
|
+
const activity = startPhase(phase.id);
|
|
267
|
+
lastFeedbackLines = [`Running ${phase.id} phase as sub-agent`];
|
|
245
268
|
setUiState(ctx, state, lastFeedbackLines);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
269
|
+
ctx.ui.notify(`CodeCartographer phase: ${phase.id} (sub-agent running)`, "info");
|
|
270
|
+
// Attach the persistent "Agents" widget so the user can watch the
|
|
271
|
+
// phase's tool/turn/token counts live above the editor while the
|
|
272
|
+
// orchestrator's TUI stays responsive.
|
|
273
|
+
getAgentsWidget().attach(ctx.ui);
|
|
274
|
+
// Fire-and-forget: spawn the phase runner asynchronously so the
|
|
275
|
+
// orchestrator's TUI stays responsive. The runner mutates the shared
|
|
276
|
+
// agent-state map from event callbacks; M2 will read that map from a
|
|
277
|
+
// persistent widget. For M1 we just notify on completion.
|
|
278
|
+
void runPhase(ctx, prompt, {
|
|
279
|
+
onSessionCreated: (session) => { activity.session = session; },
|
|
280
|
+
onToolStart: (id, name) => { activity.activeTools.set(id, name); activity.toolUses++; },
|
|
281
|
+
onToolEnd: (id) => { activity.activeTools.delete(id); },
|
|
282
|
+
onTextDelta: (_delta, fullText) => { activity.responseText = fullText; },
|
|
283
|
+
onTurnEnd: (turnCount) => { activity.turnCount = turnCount; },
|
|
284
|
+
onMessageEnd: (usage) => {
|
|
285
|
+
activity.lifetimeUsage.input += usage.input;
|
|
286
|
+
activity.lifetimeUsage.output += usage.output;
|
|
287
|
+
activity.lifetimeUsage.cacheWrite += usage.cacheWrite;
|
|
265
288
|
},
|
|
289
|
+
}, { sessionName: `CodeCartographer phase: ${phase.id}` })
|
|
290
|
+
.then((result) => {
|
|
291
|
+
const status = result.aborted ? "aborted" : "completed";
|
|
292
|
+
finishPhase(phase.id, { status });
|
|
293
|
+
if (ctx.hasUI) {
|
|
294
|
+
ctx.ui.notify(result.aborted
|
|
295
|
+
? `Phase ${phase.id} aborted.`
|
|
296
|
+
: `Phase ${phase.id} sub-agent finished (${result.toolUses} tool uses, ${result.turnCount} turns).`, result.aborted ? "warning" : "info");
|
|
297
|
+
}
|
|
298
|
+
// Inject a CustomMessageEntry into the orchestrator's session so
|
|
299
|
+
// the user sees a closeout summary in the TUI scrollback and the
|
|
300
|
+
// orchestrator's LLM picks up the phase result as context on the
|
|
301
|
+
// next turn. display:true renders in the TUI; no triggerTurn so
|
|
302
|
+
// the LLM doesn't auto-respond — the user remains in control.
|
|
303
|
+
pi.sendMessage({
|
|
304
|
+
customType: "codecarto-phase-summary",
|
|
305
|
+
content: buildPhaseSummary({
|
|
306
|
+
phaseId: phase.id,
|
|
307
|
+
status: result.aborted ? "aborted" : "completed",
|
|
308
|
+
turnCount: activity.turnCount,
|
|
309
|
+
toolUses: activity.toolUses,
|
|
310
|
+
tokens: activity.lifetimeUsage,
|
|
311
|
+
durationMs: (activity.completedAt ?? Date.now()) - activity.startedAt,
|
|
312
|
+
responseText: result.responseText,
|
|
313
|
+
}),
|
|
314
|
+
display: true,
|
|
315
|
+
});
|
|
316
|
+
void recordUsage(state.workspaceDir, phase.id, status, activity);
|
|
317
|
+
})
|
|
318
|
+
.catch((err) => {
|
|
319
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
320
|
+
finishPhase(phase.id, { status: "error", error: message });
|
|
321
|
+
if (ctx.hasUI) {
|
|
322
|
+
ctx.ui.notify(`Phase ${phase.id} sub-agent failed: ${message}`, "error");
|
|
323
|
+
}
|
|
324
|
+
pi.sendMessage({
|
|
325
|
+
customType: "codecarto-phase-summary",
|
|
326
|
+
content: buildPhaseSummary({
|
|
327
|
+
phaseId: phase.id,
|
|
328
|
+
status: "error",
|
|
329
|
+
turnCount: activity.turnCount,
|
|
330
|
+
toolUses: activity.toolUses,
|
|
331
|
+
tokens: activity.lifetimeUsage,
|
|
332
|
+
durationMs: (activity.completedAt ?? Date.now()) - activity.startedAt,
|
|
333
|
+
responseText: "",
|
|
334
|
+
error: message,
|
|
335
|
+
}),
|
|
336
|
+
display: true,
|
|
337
|
+
});
|
|
338
|
+
void recordUsage(state.workspaceDir, phase.id, "error", activity);
|
|
339
|
+
})
|
|
340
|
+
.finally(() => {
|
|
341
|
+
// Sub-agent may have written findings, owner_notes, or carry-forward
|
|
342
|
+
// items into status.yaml. Refresh the main status widget so the
|
|
343
|
+
// "Open questions / Carry-forward / Next" lines reflect the new
|
|
344
|
+
// state without waiting for the user to run /codecarto-status.
|
|
345
|
+
void refreshWorkspaceUi(ctx);
|
|
346
|
+
// Linger 30s in M1 so /codecarto-status can show that the phase ran;
|
|
347
|
+
// M2's widget owns the proper "linger N turns" lifecycle.
|
|
348
|
+
setTimeout(() => clearPhase(phase.id), 30_000);
|
|
266
349
|
});
|
|
267
350
|
},
|
|
268
351
|
});
|
|
@@ -440,4 +523,34 @@ export default function codeCartographerExtension(pi) {
|
|
|
440
523
|
ctx.ui.notify(`Queued CodeCartographer skill: ${skillName}`, "info");
|
|
441
524
|
},
|
|
442
525
|
});
|
|
526
|
+
pi.registerCommand("codecarto-usage", {
|
|
527
|
+
description: "Show cumulative + per-phase token usage from local phase runs",
|
|
528
|
+
handler: async (_args, ctx) => {
|
|
529
|
+
const state = await ensureWorkspaceState(ctx);
|
|
530
|
+
if (!state)
|
|
531
|
+
return;
|
|
532
|
+
const usage = await loadUsage(state.workspaceDir);
|
|
533
|
+
if (usage.runs.length === 0) {
|
|
534
|
+
lastFeedbackLines = ["No phase runs recorded yet."];
|
|
535
|
+
setUiState(ctx, state, lastFeedbackLines);
|
|
536
|
+
ctx.ui.notify("No phase runs recorded yet.", "info");
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
const totals = computeTotals(usage);
|
|
540
|
+
const perPhase = computePerPhaseTotals(usage);
|
|
541
|
+
const lines = [];
|
|
542
|
+
lines.push(`Total runs: ${totals.runs}`);
|
|
543
|
+
lines.push(`Total tokens: ${formatUsageTokens(totals.tokens.input)} in · ${formatUsageTokens(totals.tokens.output)} out · ${formatUsageTokens(totals.tokens.cache_write)} cache-write`);
|
|
544
|
+
lines.push(`Total duration: ${formatUsageDuration(totals.duration_ms)} · ${totals.tool_uses} tool uses`);
|
|
545
|
+
lines.push("");
|
|
546
|
+
lines.push("Per-phase totals:");
|
|
547
|
+
for (const [phaseId, t] of perPhase) {
|
|
548
|
+
const tokensTotal = t.tokens.input + t.tokens.output;
|
|
549
|
+
lines.push(` ${phaseId}: ${t.runs} run${t.runs === 1 ? "" : "s"} · ${formatUsageTokens(tokensTotal)} tokens · ${t.tool_uses} tool uses · ${formatUsageDuration(t.duration_ms)}`);
|
|
550
|
+
}
|
|
551
|
+
lastFeedbackLines = lines;
|
|
552
|
+
setUiState(ctx, state, lastFeedbackLines);
|
|
553
|
+
ctx.ui.notify(`CodeCartographer usage: ${totals.runs} run${totals.runs === 1 ? "" : "s"}, ${formatUsageTokens(totals.tokens.input + totals.tokens.output)} tokens total`, "info");
|
|
554
|
+
},
|
|
555
|
+
});
|
|
443
556
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Flag parser for /codecarto-next. The user invokes the slash command with
|
|
2
|
+
// an optional "--llm-steer" or "--no-llm-steer" override. We parse and
|
|
3
|
+
// validate; index.ts decides how to merge the override with workspace
|
|
4
|
+
// config to compute the effective "should we run the LLM rewriter?" bit.
|
|
5
|
+
const KNOWN = new Set(["--llm-steer", "--no-llm-steer"]);
|
|
6
|
+
export function parseNextFlags(args) {
|
|
7
|
+
const tokens = args.trim().split(/\s+/).filter((t) => t.length > 0);
|
|
8
|
+
const result = { unknown: [] };
|
|
9
|
+
for (const t of tokens) {
|
|
10
|
+
if (t === "--llm-steer")
|
|
11
|
+
result.llmSteerOverride = true;
|
|
12
|
+
else if (t === "--no-llm-steer")
|
|
13
|
+
result.llmSteerOverride = false;
|
|
14
|
+
else
|
|
15
|
+
result.unknown.push(t);
|
|
16
|
+
}
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
export const KNOWN_NEXT_FLAGS = [...KNOWN];
|
|
@@ -367,7 +367,7 @@ const HANDLERS = {
|
|
|
367
367
|
};
|
|
368
368
|
// ---------- server bootstrap ----------
|
|
369
369
|
export function buildServer() {
|
|
370
|
-
const server = new Server({ name: "codecartographer", version: "0.
|
|
370
|
+
const server = new Server({ name: "codecartographer", version: "0.2.0" }, { capabilities: { tools: {} } });
|
|
371
371
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
372
372
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
373
373
|
const handler = HANDLERS[request.params.name];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codecartographer-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "CodeCartographer packaged for Pi as an extension-driven workflow wrapper.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@modelcontextprotocol/sdk": "^1.29.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@earendil-works/pi-coding-agent": "
|
|
45
|
+
"@earendil-works/pi-coding-agent": "~0.74.0",
|
|
46
46
|
"@sinclair/typebox": "*"
|
|
47
47
|
},
|
|
48
48
|
"pi": {
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export interface OrchestratorState {
|
|
2
|
-
sessionFile: string;
|
|
3
|
-
sessionId: string;
|
|
4
|
-
}
|
|
5
|
-
export declare function loadOrchestratorState(cwd: string): Promise<OrchestratorState | null>;
|
|
6
|
-
export declare function writeOrchestratorState(cwd: string, state: OrchestratorState): Promise<void>;
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// Per-machine pointer to the Pi session that runs as the CodeCartographer
|
|
2
|
-
// orchestrator. Stored in `.codecarto/workflow/.orchestrator.local.yaml`
|
|
3
|
-
// (gitignored) so committed `status.yaml` doesn't leak machine-specific
|
|
4
|
-
// session file paths to collaborators.
|
|
5
|
-
//
|
|
6
|
-
// Only the Pi extension writes/reads this — the MCP path has no session
|
|
7
|
-
// concept. When the file is missing or malformed, `/codecarto-next` falls
|
|
8
|
-
// back to in-place phase prompts (legacy 0.1.0–0.1.2 behavior).
|
|
9
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
10
|
-
import { dirname, join } from "node:path";
|
|
11
|
-
import { pathExists } from "./utils.js";
|
|
12
|
-
import { loadYamlFile, stringifySimpleYaml } from "./yaml.js";
|
|
13
|
-
const ORCHESTRATOR_STATE_RELATIVE = "workflow/.orchestrator.local.yaml";
|
|
14
|
-
function orchestratorStatePath(cwd) {
|
|
15
|
-
return join(cwd, ".codecarto", ORCHESTRATOR_STATE_RELATIVE);
|
|
16
|
-
}
|
|
17
|
-
export async function loadOrchestratorState(cwd) {
|
|
18
|
-
const path = orchestratorStatePath(cwd);
|
|
19
|
-
if (!(await pathExists(path)))
|
|
20
|
-
return null;
|
|
21
|
-
let data;
|
|
22
|
-
try {
|
|
23
|
-
data = await loadYamlFile(path);
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
if (!data?.sessionFile || !data?.sessionId)
|
|
29
|
-
return null;
|
|
30
|
-
return { sessionFile: data.sessionFile, sessionId: data.sessionId };
|
|
31
|
-
}
|
|
32
|
-
export async function writeOrchestratorState(cwd, state) {
|
|
33
|
-
const path = orchestratorStatePath(cwd);
|
|
34
|
-
await mkdir(dirname(path), { recursive: true });
|
|
35
|
-
await writeFile(path, `${stringifySimpleYaml(state)}\n`, "utf8");
|
|
36
|
-
}
|