codecartographer-pi 0.19.4 → 0.19.6
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/templates/gitignore +55 -0
- package/.codecarto/workflow/scaffold-version.yaml +1 -1
- package/dist/core/amendment.js +28 -23
- package/dist/core/broadside.js +3 -5
- package/dist/core/completion.js +14 -4
- package/dist/core/library.js +111 -106
- package/dist/core/status.d.ts +22 -2
- package/dist/core/status.js +46 -5
- package/dist/core/usage.d.ts +8 -0
- package/dist/core/usage.js +35 -7
- package/dist/core/utils.d.ts +32 -5
- package/dist/core/utils.js +81 -19
- package/dist/core/workspace.d.ts +44 -10
- package/dist/core/workspace.js +161 -30
- package/dist/extensions/codecarto/auto-runner.js +11 -17
- package/dist/extensions/codecarto/dashboard-narrator.js +3 -6
- package/dist/extensions/codecarto/dashboard-writer.js +3 -6
- package/dist/extensions/codecarto/index.js +101 -135
- package/dist/extensions/codecarto/notify.d.ts +16 -0
- package/dist/extensions/codecarto/notify.js +53 -0
- package/dist/extensions/codecarto/phase-compaction.js +7 -7
- package/dist/mcp-server/server.js +4 -1
- package/package.json +1 -1
|
@@ -9,8 +9,9 @@ import { writeDashboard } from "./dashboard-writer.js";
|
|
|
9
9
|
import { parseBroadsideFlags, KNOWN_BROADSIDE_TOKENS } from "./broadside-flags.js";
|
|
10
10
|
import { parseNextFlags } from "./next-flags.js";
|
|
11
11
|
import { buildPiGuideMessage } from "./guide-framing.js";
|
|
12
|
+
import { isCtxLive, notifyCtx } from "./notify.js";
|
|
12
13
|
import { phaseCompactionExtension } from "./phase-compaction.js";
|
|
13
|
-
import { applyAmendment, buildPhasePrompt, buildSkillPrompt, buildValidationSummary, canonicalPath, copyPackagedWorkspace, computePerPhaseTotals, computeTotals, ConfidentialityMismatchError, createEmptyStatus, DEFAULT_PIPELINE_PATH, describeScaffoldStaleness, deriveSlug, discoverLibrary, getNextEligiblePhase, getPipelineLabel, getWorkspaceState,
|
|
14
|
+
import { applyAmendment, buildPhasePrompt, buildSkillPrompt, buildValidationSummary, canonicalPath, copyPackagedWorkspace, computePerPhaseTotals, computeTotals, ConfidentialityMismatchError, createEmptyStatus, DEFAULT_PIPELINE_PATH, describeScaffoldStaleness, deriveSlug, discoverLibrary, getNextEligiblePhase, getPipelineLabel, getWorkspaceState, isWithinPath, resolveExistingPrefix, BROADSIDE_LENS_IDS, BROADSIDE_SKILL_NAME, BroadsideCancelledError, broadsideDirFor, collectResultText, estimateSubmitText, getLens, listAmendmentNames, listBatchModels, listGuideTopics, listScaffoldRefreshFiles, listSkillNames, loadAmendmentFile, loadBroadsideConfig, modelsText, runBroadsideCollect, runBroadsideStatus, runBroadsideSubmit, statusText, loadCodecartoConfig, loadUsage, loadYamlFile, normalizeForComparison, packagedWorkspaceDir, pathExists, PACKAGE_VERSION, readBroadsideSkill, readGuide, refreshScaffold, PhasePreflightError, PIPELINE_ALIASES, publishEntry, resolvePhase, resolvePipelineChoice, resolvePublishSourceRepo, SourceRepoMismatchError, runPhasePreflight, SCAFFOLD_REFRESH_PROTECTED, seedOrchestratorFiles, stringifySimpleYaml, switchPipeline, validatePhaseOutput, writeLibraryConfig, } from "../../core/index.js";
|
|
14
15
|
import { initLibrary } from "../../core/library.js";
|
|
15
16
|
import { resolveUserConfigPath } from "../../core/orchestrator-config.js";
|
|
16
17
|
const STATUS_WIDGET_ID = "codecarto-widget";
|
|
@@ -84,40 +85,6 @@ function buildStatusLines(state, extraLines = []) {
|
|
|
84
85
|
}
|
|
85
86
|
return lines;
|
|
86
87
|
}
|
|
87
|
-
/**
|
|
88
|
-
* Whether `ctx` still belongs to the live session.
|
|
89
|
-
*
|
|
90
|
-
* Pi invalidates an extension ctx when the session is replaced, and from then
|
|
91
|
-
* on *every* property access on it throws — `ctx.cwd` and `ctx.hasUI` included.
|
|
92
|
-
* A phase runs as a sub-agent, so by the time post-phase work fires, the ctx
|
|
93
|
-
* captured when the command started may already be dead. That is an ordinary
|
|
94
|
-
* outcome rather than an error: the UI it would have refreshed is gone with the
|
|
95
|
-
* session. Callers skip their UI work instead of throwing into a `void` call
|
|
96
|
-
* that nothing is waiting on.
|
|
97
|
-
*/
|
|
98
|
-
function isCtxLive(ctx) {
|
|
99
|
-
try {
|
|
100
|
-
return typeof ctx.cwd === "string";
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Notify through `ctx`, dropping the message if the session it belonged to is
|
|
108
|
-
* gone.
|
|
109
|
-
*
|
|
110
|
-
* `ctx.hasUI` throws on a stale ctx rather than returning false, so the usual
|
|
111
|
-
* `if (ctx.hasUI) ctx.ui.notify(...)` guard was itself a throw site. Inside a
|
|
112
|
-
* promise chain that was worse than a lost message: the `.catch` handler threw
|
|
113
|
-
* while reporting the original failure, and that second rejection had nothing
|
|
114
|
-
* left to catch it.
|
|
115
|
-
*/
|
|
116
|
-
function notifyCtx(ctx, message, level) {
|
|
117
|
-
if (!isCtxLive(ctx) || !ctx.hasUI)
|
|
118
|
-
return;
|
|
119
|
-
ctx.ui.notify(message, level);
|
|
120
|
-
}
|
|
121
88
|
function setUiState(ctx, state, extraLines = []) {
|
|
122
89
|
if (!isCtxLive(ctx) || !ctx.hasUI)
|
|
123
90
|
return;
|
|
@@ -310,8 +277,8 @@ export default function codeCartographerExtension(pi) {
|
|
|
310
277
|
setUiState(ctx, null);
|
|
311
278
|
// The ctx can die between the read above and here, so the error
|
|
312
279
|
// path must not assume it is still usable either.
|
|
313
|
-
if (notifyOnError
|
|
314
|
-
ctx
|
|
280
|
+
if (notifyOnError)
|
|
281
|
+
notifyCtx(ctx, message, "error");
|
|
315
282
|
return null;
|
|
316
283
|
}
|
|
317
284
|
};
|
|
@@ -370,24 +337,25 @@ export default function codeCartographerExtension(pi) {
|
|
|
370
337
|
if (!(await pathExists(workspaceDir)))
|
|
371
338
|
return undefined;
|
|
372
339
|
if (event.toolName === "bash") {
|
|
373
|
-
|
|
374
|
-
ctx.ui.notify("Blocked bash in CodeCartographer mode", "warning");
|
|
340
|
+
notifyCtx(ctx, "Blocked bash in CodeCartographer mode", "warning");
|
|
375
341
|
return { block: true, reason: "CodeCartographer mode disables bash to keep source analysis read-only." };
|
|
376
342
|
}
|
|
377
343
|
if (event.toolName === "edit" || event.toolName === "write") {
|
|
378
344
|
const inputPath = typeof event.input.path === "string" ? event.input.path : "";
|
|
379
345
|
const strippedPath = inputPath.startsWith("@") ? inputPath.slice(1) : inputPath;
|
|
380
|
-
|
|
346
|
+
// Resolve through whatever already exists on disk — symlinks included —
|
|
347
|
+
// before appending the unborn tail. `resolve()` would collapse
|
|
348
|
+
// `link/..` lexically and `realpath()` throws on a file that is not
|
|
349
|
+
// there yet, and either gap let a write escape the workspace (#223).
|
|
350
|
+
const targetPath = await resolveExistingPrefix(strippedPath, ctx.cwd);
|
|
381
351
|
const allowedRoots = [await canonicalPath(workspaceDir)];
|
|
382
352
|
const config = await loadCodecartoConfig(workspaceDir);
|
|
383
353
|
if (config.library.path && await discoverLibrary(config.library.path)) {
|
|
384
354
|
allowedRoots.push(await canonicalPath(config.library.path));
|
|
385
355
|
}
|
|
386
|
-
const withinAllowed =
|
|
356
|
+
const withinAllowed = allowedRoots.map((allowedRoot) => isWithinPath(targetPath, allowedRoot));
|
|
387
357
|
if (!withinAllowed.some((result) => result)) {
|
|
388
|
-
|
|
389
|
-
ctx.ui.notify(`Blocked ${event.toolName} outside .codecarto/ or configured library: ${inputPath}`, "warning");
|
|
390
|
-
}
|
|
358
|
+
notifyCtx(ctx, `Blocked ${event.toolName} outside .codecarto/ or configured library: ${inputPath}`, "warning");
|
|
391
359
|
return { block: true, reason: `CodeCartographer mode only allows ${event.toolName} within .codecarto/ or the configured CodeCartographer library.` };
|
|
392
360
|
}
|
|
393
361
|
}
|
|
@@ -398,7 +366,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
398
366
|
handler: async (_args, ctx) => {
|
|
399
367
|
const workspaceDir = join(ctx.cwd, ".codecarto");
|
|
400
368
|
if (!(await pathExists(join(workspaceDir, "workflow", "status.yaml")))) {
|
|
401
|
-
ctx
|
|
369
|
+
notifyCtx(ctx, "No existing CodeCartographer workspace found. Run /codecarto-init first.", "warning");
|
|
402
370
|
return;
|
|
403
371
|
}
|
|
404
372
|
try {
|
|
@@ -407,11 +375,11 @@ export default function codeCartographerExtension(pi) {
|
|
|
407
375
|
lastFeedbackLines = [`Opened existing workspace: ${getPipelineLabel(state.status.pipeline)}`];
|
|
408
376
|
pi.setActiveTools(SAFE_TOOL_NAMES);
|
|
409
377
|
await refreshWorkspaceUi(ctx, lastFeedbackLines);
|
|
410
|
-
ctx
|
|
378
|
+
notifyCtx(ctx, "Opened existing CodeCartographer workspace without resetting state.", "info");
|
|
411
379
|
}
|
|
412
380
|
catch (error) {
|
|
413
381
|
const message = error instanceof Error ? error.message : String(error);
|
|
414
|
-
ctx
|
|
382
|
+
notifyCtx(ctx, `Unable to open CodeCartographer workspace: ${message}`, "error");
|
|
415
383
|
}
|
|
416
384
|
},
|
|
417
385
|
});
|
|
@@ -420,7 +388,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
420
388
|
handler: async (_args, ctx) => {
|
|
421
389
|
const interviewPath = join(ctx.cwd, ".codecarto", "findings", "vision-capture", "INTERVIEW.md");
|
|
422
390
|
if (!(await pathExists(interviewPath))) {
|
|
423
|
-
ctx
|
|
391
|
+
notifyCtx(ctx, "Vision interview skill not found. Run /codecarto-init synthesis first.", "warning");
|
|
424
392
|
return;
|
|
425
393
|
}
|
|
426
394
|
const interviewSkill = await readFile(interviewPath, "utf8");
|
|
@@ -442,7 +410,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
442
410
|
pi.sendUserMessage(prompt, { deliverAs: "followUp" });
|
|
443
411
|
}
|
|
444
412
|
lastFeedbackLines = ["Vision interview started — answer the questions in chat."];
|
|
445
|
-
ctx
|
|
413
|
+
notifyCtx(ctx, "Vision interview queued — answer the questions in the chat.", "info");
|
|
446
414
|
},
|
|
447
415
|
});
|
|
448
416
|
pi.registerCommand("codecarto-init", {
|
|
@@ -457,13 +425,13 @@ export default function codeCartographerExtension(pi) {
|
|
|
457
425
|
const trimmedArgs = args.trim();
|
|
458
426
|
const pipelineChoice = resolvePipelineChoice(trimmedArgs);
|
|
459
427
|
if (trimmedArgs && !pipelineChoice) {
|
|
460
|
-
ctx
|
|
428
|
+
notifyCtx(ctx, `Unknown pipeline: ${trimmedArgs}`, "error");
|
|
461
429
|
return;
|
|
462
430
|
}
|
|
463
431
|
const targetWorkspaceDir = join(ctx.cwd, ".codecarto");
|
|
464
432
|
const sourceWorkspaceDir = packagedWorkspaceDir;
|
|
465
433
|
if (!(await pathExists(sourceWorkspaceDir))) {
|
|
466
|
-
ctx
|
|
434
|
+
notifyCtx(ctx, "Packaged .codecarto assets are missing.", "error");
|
|
467
435
|
return;
|
|
468
436
|
}
|
|
469
437
|
const targetExists = await pathExists(targetWorkspaceDir);
|
|
@@ -475,8 +443,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
475
443
|
return;
|
|
476
444
|
const backupDir = join(ctx.cwd, `.codecarto-backup-${new Date().toISOString().replace(/[:.]/g, "-")}`);
|
|
477
445
|
await rename(targetWorkspaceDir, backupDir);
|
|
478
|
-
|
|
479
|
-
ctx.ui.notify(`Backed up existing workspace to ${basename(backupDir)}/`, "info");
|
|
446
|
+
notifyCtx(ctx, `Backed up existing workspace to ${basename(backupDir)}/`, "info");
|
|
480
447
|
}
|
|
481
448
|
}
|
|
482
449
|
if (!(await pathExists(targetWorkspaceDir))) {
|
|
@@ -484,11 +451,12 @@ export default function codeCartographerExtension(pi) {
|
|
|
484
451
|
await copyPackagedWorkspace(targetWorkspaceDir);
|
|
485
452
|
}
|
|
486
453
|
const rawStatusPath = join(targetWorkspaceDir, "workflow", "status.yaml");
|
|
487
|
-
|
|
454
|
+
// Absent on a fresh copy: the template ships no status.yaml.
|
|
455
|
+
const rawStatus = (await pathExists(rawStatusPath)) ? ((await loadYamlFile(rawStatusPath)) ?? {}) : {};
|
|
488
456
|
const selectedPipelinePath = pipelineChoice ?? rawStatus.pipeline?.trim() ?? DEFAULT_PIPELINE_PATH;
|
|
489
457
|
const resolvedPipelinePath = join(targetWorkspaceDir, selectedPipelinePath);
|
|
490
458
|
if (!(await pathExists(resolvedPipelinePath))) {
|
|
491
|
-
ctx
|
|
459
|
+
notifyCtx(ctx, `Pipeline not found: ${selectedPipelinePath}`, "error");
|
|
492
460
|
return;
|
|
493
461
|
}
|
|
494
462
|
const pipeline = await loadYamlFile(resolvedPipelinePath);
|
|
@@ -506,7 +474,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
506
474
|
`Initialized workspace with pipeline: ${getPipelineLabel(selectedPipelinePath)}`,
|
|
507
475
|
"Full run: `/codecarto-next --auto --llm-steer` — or `/codecarto-next` to watch one phase first.",
|
|
508
476
|
];
|
|
509
|
-
ctx
|
|
477
|
+
notifyCtx(ctx, `Initialized CodeCartographer (${getPipelineLabel(selectedPipelinePath)}). Full run: /codecarto-next --auto --llm-steer`, "info");
|
|
510
478
|
// Render the initial dashboard (empty usage, all phases pending) so
|
|
511
479
|
// the user sees the file exist immediately after /codecarto-init.
|
|
512
480
|
void writeDashboard(ctx.cwd, PACKAGE_VERSION);
|
|
@@ -524,7 +492,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
524
492
|
const nextPhase = getNextEligiblePhase(state)?.id ?? "complete";
|
|
525
493
|
lastFeedbackLines = [`Current phase: ${nextPhase}`, `Pipeline: ${getPipelineLabel(state.status.pipeline)}`];
|
|
526
494
|
setUiState(ctx, state, lastFeedbackLines);
|
|
527
|
-
ctx
|
|
495
|
+
notifyCtx(ctx, `CodeCartographer phase: ${nextPhase}`, "info");
|
|
528
496
|
},
|
|
529
497
|
});
|
|
530
498
|
pi.registerCommand("codecarto-switch-pipeline", {
|
|
@@ -538,12 +506,12 @@ export default function codeCartographerExtension(pi) {
|
|
|
538
506
|
handler: async (args, ctx) => {
|
|
539
507
|
const trimmedArgs = args.trim();
|
|
540
508
|
if (!trimmedArgs) {
|
|
541
|
-
ctx
|
|
509
|
+
notifyCtx(ctx, "Usage: /codecarto-switch-pipeline <variant> (e.g. lite, full, synthesis)", "warning");
|
|
542
510
|
return;
|
|
543
511
|
}
|
|
544
512
|
const pipelineChoice = resolvePipelineChoice(trimmedArgs);
|
|
545
513
|
if (!pipelineChoice) {
|
|
546
|
-
ctx
|
|
514
|
+
notifyCtx(ctx, `Unknown pipeline: ${trimmedArgs}`, "error");
|
|
547
515
|
return;
|
|
548
516
|
}
|
|
549
517
|
const state = await ensureWorkspaceState(ctx);
|
|
@@ -551,7 +519,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
551
519
|
return;
|
|
552
520
|
const currentPipeline = state.status.pipeline;
|
|
553
521
|
if (currentPipeline === pipelineChoice) {
|
|
554
|
-
ctx
|
|
522
|
+
notifyCtx(ctx, `Already on pipeline: ${getPipelineLabel(pipelineChoice)}`, "info");
|
|
555
523
|
return;
|
|
556
524
|
}
|
|
557
525
|
try {
|
|
@@ -567,14 +535,14 @@ export default function codeCartographerExtension(pi) {
|
|
|
567
535
|
lines.push(`Phases not in new pipeline: ${result.dropped.join(", ")} (findings remain on disk)`);
|
|
568
536
|
lastFeedbackLines = lines;
|
|
569
537
|
await refreshWorkspaceUi(ctx, lastFeedbackLines);
|
|
570
|
-
ctx
|
|
538
|
+
notifyCtx(ctx, `Switched to pipeline: ${getPipelineLabel(pipelineChoice)}`, "info");
|
|
571
539
|
void writeDashboard(ctx.cwd, PACKAGE_VERSION);
|
|
572
540
|
}
|
|
573
541
|
catch (error) {
|
|
574
542
|
const message = error instanceof Error ? error.message : String(error);
|
|
575
543
|
lastFeedbackLines = [message];
|
|
576
544
|
setUiState(ctx, state, lastFeedbackLines);
|
|
577
|
-
ctx
|
|
545
|
+
notifyCtx(ctx, message, "error");
|
|
578
546
|
}
|
|
579
547
|
},
|
|
580
548
|
});
|
|
@@ -601,18 +569,18 @@ export default function codeCartographerExtension(pi) {
|
|
|
601
569
|
handler: async (args, ctx) => {
|
|
602
570
|
const flags = parseNextFlags(args);
|
|
603
571
|
if (flags.error) {
|
|
604
|
-
ctx
|
|
572
|
+
notifyCtx(ctx, flags.error, "error");
|
|
605
573
|
return;
|
|
606
574
|
}
|
|
607
575
|
if (flags.unknown.length > 0) {
|
|
608
|
-
ctx
|
|
576
|
+
notifyCtx(ctx, `Unknown /codecarto-next flag: ${flags.unknown.join(" ")}`, "error");
|
|
609
577
|
return;
|
|
610
578
|
}
|
|
611
579
|
const state = await ensureWorkspaceState(ctx);
|
|
612
580
|
if (!state)
|
|
613
581
|
return;
|
|
614
582
|
if (flags.auto) {
|
|
615
|
-
ctx
|
|
583
|
+
notifyCtx(ctx, `Auto pipeline${flags.strict ? " (strict)" : ""} running…`, "info");
|
|
616
584
|
const result = await runAuto(ctx, pi, state, {
|
|
617
585
|
strict: flags.strict,
|
|
618
586
|
llmSteerOverride: flags.llmSteerOverride,
|
|
@@ -635,14 +603,14 @@ export default function codeCartographerExtension(pi) {
|
|
|
635
603
|
});
|
|
636
604
|
lastFeedbackLines = [`Auto pipeline ${result.outcome}: ${result.reason}`];
|
|
637
605
|
await refreshWorkspaceUi(ctx, lastFeedbackLines);
|
|
638
|
-
ctx
|
|
606
|
+
notifyCtx(ctx, `Auto pipeline ${result.outcome}: ${result.phasesRun.length}/${result.totalPhases} phases.`, result.outcome === "complete" ? "info" : "warning");
|
|
639
607
|
return;
|
|
640
608
|
}
|
|
641
609
|
const phase = getNextEligiblePhase(state);
|
|
642
610
|
if (!phase) {
|
|
643
611
|
lastFeedbackLines = ["All phases complete."];
|
|
644
612
|
setUiState(ctx, state, lastFeedbackLines);
|
|
645
|
-
ctx
|
|
613
|
+
notifyCtx(ctx, "All CodeCartographer phases are complete.", "info");
|
|
646
614
|
return;
|
|
647
615
|
}
|
|
648
616
|
let preflight;
|
|
@@ -653,13 +621,13 @@ export default function codeCartographerExtension(pi) {
|
|
|
653
621
|
const message = error instanceof Error ? error.message : String(error);
|
|
654
622
|
lastFeedbackLines = [message];
|
|
655
623
|
setUiState(ctx, state, lastFeedbackLines);
|
|
656
|
-
ctx
|
|
624
|
+
notifyCtx(ctx, message, error instanceof PhasePreflightError ? "warning" : "error");
|
|
657
625
|
return;
|
|
658
626
|
}
|
|
659
627
|
// Reject re-entry: don't spawn a duplicate runner for a phase that's
|
|
660
628
|
// already in flight from a previous /codecarto-next invocation.
|
|
661
629
|
if (isPhaseRunning(phase.id)) {
|
|
662
|
-
ctx
|
|
630
|
+
notifyCtx(ctx, `Phase ${phase.id} is already running.`, "warning");
|
|
663
631
|
return;
|
|
664
632
|
}
|
|
665
633
|
const config = await loadCodecartoConfig(state.workspaceDir);
|
|
@@ -735,7 +703,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
735
703
|
handler: async (args, ctx) => {
|
|
736
704
|
const phaseId = args.trim();
|
|
737
705
|
if (!phaseId) {
|
|
738
|
-
ctx
|
|
706
|
+
notifyCtx(ctx, "Usage: /codecarto-phase <phase>", "warning");
|
|
739
707
|
return;
|
|
740
708
|
}
|
|
741
709
|
const state = await ensureWorkspaceState(ctx);
|
|
@@ -743,7 +711,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
743
711
|
return;
|
|
744
712
|
const phase = resolvePhase(state, phaseId);
|
|
745
713
|
if (!phase) {
|
|
746
|
-
ctx
|
|
714
|
+
notifyCtx(ctx, `Unknown phase: ${phaseId}`, "error");
|
|
747
715
|
return;
|
|
748
716
|
}
|
|
749
717
|
let prompt;
|
|
@@ -752,7 +720,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
752
720
|
}
|
|
753
721
|
catch (error) {
|
|
754
722
|
const message = error instanceof Error ? error.message : String(error);
|
|
755
|
-
ctx
|
|
723
|
+
notifyCtx(ctx, message, error instanceof PhasePreflightError ? "warning" : "error");
|
|
756
724
|
return;
|
|
757
725
|
}
|
|
758
726
|
if (ctx.isIdle()) {
|
|
@@ -763,7 +731,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
763
731
|
}
|
|
764
732
|
lastFeedbackLines = [`Queued explicit phase prompt for ${phase.id}`];
|
|
765
733
|
setUiState(ctx, state, lastFeedbackLines);
|
|
766
|
-
ctx
|
|
734
|
+
notifyCtx(ctx, `Queued CodeCartographer phase: ${phase.id}`, "info");
|
|
767
735
|
},
|
|
768
736
|
});
|
|
769
737
|
pi.registerCommand("codecarto-validate", {
|
|
@@ -776,13 +744,13 @@ export default function codeCartographerExtension(pi) {
|
|
|
776
744
|
if (validation instanceof Error) {
|
|
777
745
|
lastFeedbackLines = [validation.message];
|
|
778
746
|
setUiState(ctx, state, lastFeedbackLines);
|
|
779
|
-
ctx
|
|
747
|
+
notifyCtx(ctx, validation.message, "error");
|
|
780
748
|
return;
|
|
781
749
|
}
|
|
782
750
|
lastFeedbackLines = buildValidationSummary(validation);
|
|
783
751
|
setUiState(ctx, state, lastFeedbackLines);
|
|
784
752
|
const level = validation.overall === "FAIL" || validation.overall === "MISSING" ? "error" : validation.overall === "PASS WITH GAPS" ? "warning" : "info";
|
|
785
|
-
ctx
|
|
753
|
+
notifyCtx(ctx, `Validation ${validation.phaseId}: ${validation.overall}`, level);
|
|
786
754
|
},
|
|
787
755
|
});
|
|
788
756
|
pi.registerCommand("codecarto-complete", {
|
|
@@ -795,13 +763,13 @@ export default function codeCartographerExtension(pi) {
|
|
|
795
763
|
if (validation instanceof Error) {
|
|
796
764
|
lastFeedbackLines = [validation.message];
|
|
797
765
|
setUiState(ctx, currentState, lastFeedbackLines);
|
|
798
|
-
ctx
|
|
766
|
+
notifyCtx(ctx, validation.message, "error");
|
|
799
767
|
return;
|
|
800
768
|
}
|
|
801
769
|
if (validation.overall === "FAIL" || validation.overall === "MISSING") {
|
|
802
770
|
lastFeedbackLines = buildValidationSummary(validation);
|
|
803
771
|
setUiState(ctx, currentState, lastFeedbackLines);
|
|
804
|
-
ctx
|
|
772
|
+
notifyCtx(ctx, `Cannot complete ${validation.phaseId}: ${validation.overall}`, "error");
|
|
805
773
|
return;
|
|
806
774
|
}
|
|
807
775
|
// Completion refuses for reasons the framework words carefully — a
|
|
@@ -819,7 +787,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
819
787
|
const message = error instanceof Error ? error.message : String(error);
|
|
820
788
|
lastFeedbackLines = [`Completion refused: ${message}`];
|
|
821
789
|
setUiState(ctx, currentState, lastFeedbackLines);
|
|
822
|
-
ctx
|
|
790
|
+
notifyCtx(ctx, message, "error");
|
|
823
791
|
return;
|
|
824
792
|
}
|
|
825
793
|
const { updatedState, closeoutNotice, warnings } = completion;
|
|
@@ -834,11 +802,11 @@ export default function codeCartographerExtension(pi) {
|
|
|
834
802
|
for (const note of notes)
|
|
835
803
|
lastFeedbackLines.push(`NOTE: ${note} Non-gating.`);
|
|
836
804
|
setUiState(ctx, updatedState, lastFeedbackLines);
|
|
837
|
-
ctx
|
|
805
|
+
notifyCtx(ctx, `Marked ${validation.phaseId} complete`, validation.overall === "PASS WITH GAPS" || notes.length > 0 ? "warning" : "info");
|
|
838
806
|
if (closeoutNotice)
|
|
839
|
-
ctx
|
|
807
|
+
notifyCtx(ctx, closeoutNotice, "info");
|
|
840
808
|
for (const note of notes)
|
|
841
|
-
ctx
|
|
809
|
+
notifyCtx(ctx, note, "warning");
|
|
842
810
|
},
|
|
843
811
|
});
|
|
844
812
|
pi.registerCommand("codecarto-skill", {
|
|
@@ -848,7 +816,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
848
816
|
if (!skillName) {
|
|
849
817
|
const available = await listSkillNames(join(ctx.cwd, ".codecarto"));
|
|
850
818
|
const hint = available.length > 0 ? ` (available: ${available.join(", ")})` : "";
|
|
851
|
-
ctx
|
|
819
|
+
notifyCtx(ctx, `Usage: /codecarto-skill <name>${hint}`, "warning");
|
|
852
820
|
return;
|
|
853
821
|
}
|
|
854
822
|
// Broad-Side is a reading guide for batch reconnaissance output, not a
|
|
@@ -858,7 +826,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
858
826
|
if (skillName === BROADSIDE_SKILL_NAME) {
|
|
859
827
|
const skill = await readBroadsideSkill(ctx.cwd).catch(() => null);
|
|
860
828
|
if (!skill) {
|
|
861
|
-
ctx
|
|
829
|
+
notifyCtx(ctx, "Broad-Side reading guide not found. Reinstall codecartographer-pi.", "error");
|
|
862
830
|
return;
|
|
863
831
|
}
|
|
864
832
|
const message = [
|
|
@@ -872,7 +840,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
872
840
|
else {
|
|
873
841
|
pi.sendUserMessage(message, { deliverAs: "followUp" });
|
|
874
842
|
}
|
|
875
|
-
ctx
|
|
843
|
+
notifyCtx(ctx, "Queued the Broad-Side reading guide", "info");
|
|
876
844
|
return;
|
|
877
845
|
}
|
|
878
846
|
const state = await ensureWorkspaceState(ctx);
|
|
@@ -880,14 +848,14 @@ export default function codeCartographerExtension(pi) {
|
|
|
880
848
|
return;
|
|
881
849
|
const nextPhase = getNextEligiblePhase(state);
|
|
882
850
|
if (nextPhase) {
|
|
883
|
-
ctx
|
|
851
|
+
notifyCtx(ctx, `Cannot run skill: pipeline is not complete (next phase: ${nextPhase.id}). Finish the pipeline before running post-pipeline skills.`, "error");
|
|
884
852
|
return;
|
|
885
853
|
}
|
|
886
854
|
const skillFile = join(state.workspaceDir, "skills", skillName, "SKILL.md");
|
|
887
855
|
if (!(await pathExists(skillFile))) {
|
|
888
856
|
const available = await listSkillNames(state.workspaceDir);
|
|
889
857
|
const hint = available.length > 0 ? ` (available: ${available.join(", ")})` : " (no skills installed)";
|
|
890
|
-
ctx
|
|
858
|
+
notifyCtx(ctx, `Unknown skill: ${skillName}${hint}. The Broad-Side reading guide is served as \`${BROADSIDE_SKILL_NAME}\` and is not pipeline-gated.`, "error");
|
|
891
859
|
return;
|
|
892
860
|
}
|
|
893
861
|
const prompt = await buildSkillPrompt(state, skillName);
|
|
@@ -899,7 +867,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
899
867
|
}
|
|
900
868
|
lastFeedbackLines = [`Queued post-pipeline skill: ${skillName}`];
|
|
901
869
|
setUiState(ctx, state, lastFeedbackLines);
|
|
902
|
-
ctx
|
|
870
|
+
notifyCtx(ctx, `Queued CodeCartographer skill: ${skillName}`, "info");
|
|
903
871
|
},
|
|
904
872
|
});
|
|
905
873
|
pi.registerCommand("codecarto-list-skills", {
|
|
@@ -928,7 +896,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
928
896
|
}
|
|
929
897
|
lastFeedbackLines = lines;
|
|
930
898
|
setUiState(ctx, state, lastFeedbackLines);
|
|
931
|
-
ctx
|
|
899
|
+
notifyCtx(ctx, skills.length > 0
|
|
932
900
|
? `${skills.length} post-pipeline skill${skills.length === 1 ? "" : "s"}: ${skills.join(", ")}`
|
|
933
901
|
: "No post-pipeline skills installed.", "info");
|
|
934
902
|
},
|
|
@@ -952,7 +920,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
952
920
|
document = await readGuide(args.trim() || undefined);
|
|
953
921
|
}
|
|
954
922
|
catch (error) {
|
|
955
|
-
ctx
|
|
923
|
+
notifyCtx(ctx, error instanceof Error ? error.message : String(error), "error");
|
|
956
924
|
return;
|
|
957
925
|
}
|
|
958
926
|
const other = topics.filter((name) => name !== document.topic);
|
|
@@ -969,7 +937,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
969
937
|
lastFeedbackLines = [`Queued the CodeCartographer guide: ${document.topic}`];
|
|
970
938
|
if (codecartoModeActive)
|
|
971
939
|
void refreshWorkspaceUi(ctx, lastFeedbackLines).catch(() => undefined);
|
|
972
|
-
ctx
|
|
940
|
+
notifyCtx(ctx, `Queued the CodeCartographer guide (${document.topic})`, "info");
|
|
973
941
|
},
|
|
974
942
|
});
|
|
975
943
|
pi.registerCommand("codecarto-broadside", {
|
|
@@ -983,12 +951,12 @@ export default function codeCartographerExtension(pi) {
|
|
|
983
951
|
handler: async (args, ctx) => {
|
|
984
952
|
const flags = parseBroadsideFlags(args);
|
|
985
953
|
if (flags.unknown.length > 0) {
|
|
986
|
-
ctx
|
|
954
|
+
notifyCtx(ctx, `Unknown /codecarto-broadside argument: ${flags.unknown.join(" ")}. ` +
|
|
987
955
|
`Actions: submit, collect, status, models. Lenses: ${BROADSIDE_LENS_IDS.join(", ")}.`, "error");
|
|
988
956
|
return;
|
|
989
957
|
}
|
|
990
958
|
if (flags.error) {
|
|
991
|
-
ctx
|
|
959
|
+
notifyCtx(ctx, flags.error, "error");
|
|
992
960
|
return;
|
|
993
961
|
}
|
|
994
962
|
// Broad-Side runs on any git repository, with or without a workspace —
|
|
@@ -1008,7 +976,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
1008
976
|
// the result can live, so it holds it instead of being cleared.
|
|
1009
977
|
ctx.ui.setWidget(BROADSIDE_WIDGET_ID, ["Broad-Side", ...lines]);
|
|
1010
978
|
}
|
|
1011
|
-
ctx
|
|
979
|
+
notifyCtx(ctx, notice, level);
|
|
1012
980
|
};
|
|
1013
981
|
if (flags.action === "status") {
|
|
1014
982
|
const { state } = await runBroadsideStatus(ctx.cwd);
|
|
@@ -1018,12 +986,12 @@ export default function codeCartographerExtension(pi) {
|
|
|
1018
986
|
}
|
|
1019
987
|
const apiKey = resolveBroadsideKey(config.apiKey);
|
|
1020
988
|
if (!apiKey) {
|
|
1021
|
-
ctx
|
|
989
|
+
notifyCtx(ctx, "No OpenRouter API key. Set OPENROUTER_API_KEY in the environment, or add api_key to " +
|
|
1022
990
|
".codecarto/broadside/config.yaml. (A slash command takes no key: it would land in the transcript.)", "error");
|
|
1023
991
|
return;
|
|
1024
992
|
}
|
|
1025
993
|
if (flags.action === "models") {
|
|
1026
|
-
ctx
|
|
994
|
+
notifyCtx(ctx, "Fetching the OpenRouter batch-model catalog…", "info");
|
|
1027
995
|
try {
|
|
1028
996
|
const { entries, benchmarks } = await listBatchModels(broadsideDir, config, apiKey, {
|
|
1029
997
|
includeBenchmarks: flags.benchmarks,
|
|
@@ -1031,7 +999,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
1031
999
|
finish(modelsText(entries, { benchmarks, defaultModel: config.model }).split("\n"), `Broad-Side: ${entries.length} batch model${entries.length === 1 ? "" : "s"} listed`);
|
|
1032
1000
|
}
|
|
1033
1001
|
catch (error) {
|
|
1034
|
-
ctx
|
|
1002
|
+
notifyCtx(ctx, `Model catalog lookup failed: ${error instanceof Error ? error.message : String(error)}`, "error");
|
|
1035
1003
|
}
|
|
1036
1004
|
return;
|
|
1037
1005
|
}
|
|
@@ -1077,10 +1045,10 @@ export default function codeCartographerExtension(pi) {
|
|
|
1077
1045
|
if (ctx.hasUI)
|
|
1078
1046
|
ctx.ui.setWidget(BROADSIDE_WIDGET_ID, undefined);
|
|
1079
1047
|
if (error instanceof BroadsideCancelledError) {
|
|
1080
|
-
ctx
|
|
1048
|
+
notifyCtx(ctx, "Broad-Side cancelled. Nothing was submitted.", "info");
|
|
1081
1049
|
return;
|
|
1082
1050
|
}
|
|
1083
|
-
ctx
|
|
1051
|
+
notifyCtx(ctx, `Broad-Side submit failed: ${error instanceof Error ? error.message : String(error)}`, "error");
|
|
1084
1052
|
return;
|
|
1085
1053
|
}
|
|
1086
1054
|
const lines = estimateSubmitText(submit, lenses.map(getLens)).split("\n");
|
|
@@ -1089,7 +1057,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
1089
1057
|
finish(lines, `Broad-Side submitted: run ${submit.runId} (~$${submit.estimatedTotalCost.toFixed(4)})`);
|
|
1090
1058
|
return;
|
|
1091
1059
|
}
|
|
1092
|
-
ctx
|
|
1060
|
+
notifyCtx(ctx, `Broad-Side submitted run ${submit.runId}; polling for up to ${waitSeconds}s…`, "info");
|
|
1093
1061
|
try {
|
|
1094
1062
|
const collect = await runBroadsideCollect(ctx.cwd, apiKey, {
|
|
1095
1063
|
waitMs,
|
|
@@ -1105,7 +1073,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
1105
1073
|
ctx.ui.setWidget(BROADSIDE_WIDGET_ID, undefined);
|
|
1106
1074
|
// The batches are submitted and paid for either way — say so, so
|
|
1107
1075
|
// nobody re-submits a run that is already in flight.
|
|
1108
|
-
ctx
|
|
1076
|
+
notifyCtx(ctx, `Broad-Side submitted run ${submit.runId}, but collect failed: ` +
|
|
1109
1077
|
`${error instanceof Error ? error.message : String(error)}. Retry with /codecarto-broadside collect.`, "error");
|
|
1110
1078
|
}
|
|
1111
1079
|
return;
|
|
@@ -1129,7 +1097,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
1129
1097
|
catch (error) {
|
|
1130
1098
|
if (ctx.hasUI)
|
|
1131
1099
|
ctx.ui.setWidget(BROADSIDE_WIDGET_ID, undefined);
|
|
1132
|
-
ctx
|
|
1100
|
+
notifyCtx(ctx, `Broad-Side collect failed: ${error instanceof Error ? error.message : String(error)}`, "error");
|
|
1133
1101
|
}
|
|
1134
1102
|
},
|
|
1135
1103
|
});
|
|
@@ -1141,22 +1109,22 @@ export default function codeCartographerExtension(pi) {
|
|
|
1141
1109
|
return;
|
|
1142
1110
|
const config = await loadCodecartoConfig(state.workspaceDir);
|
|
1143
1111
|
if (!config.library.path) {
|
|
1144
|
-
ctx
|
|
1112
|
+
notifyCtx(ctx, "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");
|
|
1145
1113
|
return;
|
|
1146
1114
|
}
|
|
1147
1115
|
const marker = await discoverLibrary(config.library.path);
|
|
1148
1116
|
if (!marker) {
|
|
1149
|
-
ctx
|
|
1117
|
+
notifyCtx(ctx, `No CodeCartographer library at ${config.library.path} (missing .codecarto-library). Create a .codecarto-library marker file in that directory.`, "error");
|
|
1150
1118
|
return;
|
|
1151
1119
|
}
|
|
1152
1120
|
const phase = resolvePhase(state, "reimplementation-spec");
|
|
1153
1121
|
if (!phase?.primary_output) {
|
|
1154
|
-
ctx
|
|
1122
|
+
notifyCtx(ctx, "The active pipeline does not produce a reimplementation spec to publish.", "error");
|
|
1155
1123
|
return;
|
|
1156
1124
|
}
|
|
1157
1125
|
const specPath = join(state.workspaceDir, phase.primary_output);
|
|
1158
1126
|
if (!(await pathExists(specPath))) {
|
|
1159
|
-
ctx
|
|
1127
|
+
notifyCtx(ctx, `Reimplementation spec is missing: .codecarto/${phase.primary_output}`, "error");
|
|
1160
1128
|
return;
|
|
1161
1129
|
}
|
|
1162
1130
|
const spec = await readFile(specPath, "utf8");
|
|
@@ -1167,7 +1135,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
1167
1135
|
const headline = derivePublishHeadline(spec, ctx.cwd);
|
|
1168
1136
|
const namespace = marker.namespaced ? config.library.namespace ?? undefined : undefined;
|
|
1169
1137
|
if (marker.namespaced && !namespace) {
|
|
1170
|
-
ctx
|
|
1138
|
+
notifyCtx(ctx, "The configured library is namespaced; set library.namespace before publishing.", "error");
|
|
1171
1139
|
return;
|
|
1172
1140
|
}
|
|
1173
1141
|
const label = `${namespace ? `${namespace}/` : ""}${slug}`;
|
|
@@ -1212,7 +1180,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
1212
1180
|
// recorded the directory to one that records the git remote.
|
|
1213
1181
|
const moved = await ctx.ui.confirm("Source repository changed — did it move?", `Library entry ${label} records source_repo "${error.recorded}", but this publish carries "${error.incoming}". If the repository genuinely moved (rename, org transfer, host change — or this is the first publish since CodeCartographer began recording the git remote instead of the local directory), answer yes and this spec is appended as the entry's next version. If these are two different projects that share a directory name, answer no: nothing is written, and the second project needs a distinct slug (codecarto_publish on MCP accepts one). Did the repository move?`);
|
|
1214
1182
|
if (!moved) {
|
|
1215
|
-
ctx
|
|
1183
|
+
notifyCtx(ctx, "Publish cancelled. Nothing was written.", "info");
|
|
1216
1184
|
return;
|
|
1217
1185
|
}
|
|
1218
1186
|
options.allowSourceRepoChange = true;
|
|
@@ -1222,7 +1190,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
1222
1190
|
// default; whether it may go into a wider library is the user's call.
|
|
1223
1191
|
const publishAnyway = await ctx.ui.confirm("Confidentiality mismatch — publish anyway?", `This spec's confidentiality is "${error.entryConfidentiality}" (CodeCartographer's default; /codecarto-publish declares none), but the library "${marker.name}" has visibility "${error.libraryVisibility}". Publishing would expose it to everyone that library reaches. Publish anyway?`);
|
|
1224
1192
|
if (!publishAnyway) {
|
|
1225
|
-
ctx
|
|
1193
|
+
notifyCtx(ctx, "Publish cancelled. Nothing was written.", "info");
|
|
1226
1194
|
return;
|
|
1227
1195
|
}
|
|
1228
1196
|
options.allowConfidentialityMismatch = true;
|
|
@@ -1235,11 +1203,11 @@ export default function codeCartographerExtension(pi) {
|
|
|
1235
1203
|
lastFeedbackLines = [`Published ${result.namespace ? `${result.namespace}/` : ""}${result.slug} v${result.version}`, result.isNewVersion ? "New content version." : "Metadata-only update (content unchanged)."];
|
|
1236
1204
|
await writeDashboard(ctx.cwd, PACKAGE_VERSION);
|
|
1237
1205
|
await refreshWorkspaceUi(ctx, lastFeedbackLines);
|
|
1238
|
-
ctx
|
|
1206
|
+
notifyCtx(ctx, `Published ${result.namespace ? `${result.namespace}/` : ""}${result.slug} v${result.version}.`, "info");
|
|
1239
1207
|
}
|
|
1240
1208
|
catch (error) {
|
|
1241
1209
|
const message = error instanceof Error ? error.message : String(error);
|
|
1242
|
-
ctx
|
|
1210
|
+
notifyCtx(ctx, `Unable to publish: ${message}`, "error");
|
|
1243
1211
|
}
|
|
1244
1212
|
},
|
|
1245
1213
|
});
|
|
@@ -1251,7 +1219,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
1251
1219
|
const namespaceIdx = parts.indexOf("--namespace");
|
|
1252
1220
|
const namespace = namespaceIdx >= 0 ? parts[namespaceIdx + 1] : null;
|
|
1253
1221
|
if (!pathArg) {
|
|
1254
|
-
ctx
|
|
1222
|
+
notifyCtx(ctx, "Usage: /codecarto-library-init <path> [--namespace <name>]", "warning");
|
|
1255
1223
|
return;
|
|
1256
1224
|
}
|
|
1257
1225
|
const libraryPath = pathArg.startsWith("~") ? join(homedir(), pathArg.slice(1)) : resolve(pathArg);
|
|
@@ -1267,14 +1235,12 @@ export default function codeCartographerExtension(pi) {
|
|
|
1267
1235
|
? `Library already exists at ${libraryPath} (marker preserved). Config updated.`
|
|
1268
1236
|
: `Created library at ${libraryPath} with marker "${result.marker.name}".`;
|
|
1269
1237
|
lastFeedbackLines = [msg, `Config written to ${configPath}`];
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
ctx.ui.notify(`Config written to ${configPath}`, "info");
|
|
1273
|
-
}
|
|
1238
|
+
notifyCtx(ctx, msg, "info");
|
|
1239
|
+
notifyCtx(ctx, `Config written to ${configPath}`, "info");
|
|
1274
1240
|
}
|
|
1275
1241
|
catch (error) {
|
|
1276
1242
|
const message = error instanceof Error ? error.message : String(error);
|
|
1277
|
-
ctx
|
|
1243
|
+
notifyCtx(ctx, `Library init failed: ${message}`, "error");
|
|
1278
1244
|
}
|
|
1279
1245
|
},
|
|
1280
1246
|
});
|
|
@@ -1300,7 +1266,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
1300
1266
|
lastFeedbackLines = lines;
|
|
1301
1267
|
if (state)
|
|
1302
1268
|
setUiState(ctx, state, lastFeedbackLines);
|
|
1303
|
-
ctx
|
|
1269
|
+
notifyCtx(ctx, "Configuration shown in status widget.", "info");
|
|
1304
1270
|
},
|
|
1305
1271
|
});
|
|
1306
1272
|
pi.registerCommand("codecarto-usage", {
|
|
@@ -1313,7 +1279,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
1313
1279
|
if (usage.runs.length === 0) {
|
|
1314
1280
|
lastFeedbackLines = ["No phase runs recorded yet."];
|
|
1315
1281
|
setUiState(ctx, state, lastFeedbackLines);
|
|
1316
|
-
ctx
|
|
1282
|
+
notifyCtx(ctx, "No phase runs recorded yet.", "info");
|
|
1317
1283
|
return;
|
|
1318
1284
|
}
|
|
1319
1285
|
const totals = computeTotals(usage);
|
|
@@ -1333,7 +1299,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
1333
1299
|
}
|
|
1334
1300
|
lastFeedbackLines = lines;
|
|
1335
1301
|
setUiState(ctx, state, lastFeedbackLines);
|
|
1336
|
-
ctx
|
|
1302
|
+
notifyCtx(ctx, `CodeCartographer usage: ${totals.runs} run${totals.runs === 1 ? "" : "s"}, ${formatUsageTokens(totals.tokens.input + totals.tokens.output)} tokens total`, "info");
|
|
1337
1303
|
},
|
|
1338
1304
|
});
|
|
1339
1305
|
pi.registerCommand("codecarto-dashboard", {
|
|
@@ -1347,26 +1313,26 @@ export default function codeCartographerExtension(pi) {
|
|
|
1347
1313
|
handler: async (args, ctx) => {
|
|
1348
1314
|
const flags = parseDashboardFlags(args);
|
|
1349
1315
|
if (flags.unknown.length > 0) {
|
|
1350
|
-
ctx
|
|
1316
|
+
notifyCtx(ctx, `Unknown /codecarto-dashboard flag: ${flags.unknown.join(" ")}`, "error");
|
|
1351
1317
|
return;
|
|
1352
1318
|
}
|
|
1353
1319
|
const state = await ensureWorkspaceState(ctx);
|
|
1354
1320
|
if (!state)
|
|
1355
1321
|
return;
|
|
1356
1322
|
if (flags.narrate) {
|
|
1357
|
-
ctx
|
|
1323
|
+
notifyCtx(ctx, `Narrating dashboard via LLM…`, "info");
|
|
1358
1324
|
const result = await narrateDashboard(ctx, state);
|
|
1359
1325
|
if (result.used) {
|
|
1360
|
-
ctx
|
|
1326
|
+
notifyCtx(ctx, "Narration written to .codecarto/.dashboard-narration.local.md", "info");
|
|
1361
1327
|
}
|
|
1362
1328
|
else {
|
|
1363
|
-
ctx
|
|
1329
|
+
notifyCtx(ctx, `LLM narration skipped (${result.skipReason}); rendering deterministic dashboard.`, "warning");
|
|
1364
1330
|
}
|
|
1365
1331
|
}
|
|
1366
1332
|
await writeDashboard(ctx.cwd, PACKAGE_VERSION);
|
|
1367
1333
|
lastFeedbackLines = ["Dashboard regenerated: .codecarto/dashboard.html"];
|
|
1368
1334
|
setUiState(ctx, state, lastFeedbackLines);
|
|
1369
|
-
ctx
|
|
1335
|
+
notifyCtx(ctx, "Dashboard regenerated: .codecarto/dashboard.html", "info");
|
|
1370
1336
|
},
|
|
1371
1337
|
});
|
|
1372
1338
|
pi.registerCommand("codecarto-refresh-scaffold", {
|
|
@@ -1382,12 +1348,12 @@ export default function codeCartographerExtension(pi) {
|
|
|
1382
1348
|
files = await listScaffoldRefreshFiles();
|
|
1383
1349
|
}
|
|
1384
1350
|
catch (error) {
|
|
1385
|
-
ctx
|
|
1351
|
+
notifyCtx(ctx, error instanceof Error ? error.message : String(error), "error");
|
|
1386
1352
|
return;
|
|
1387
1353
|
}
|
|
1388
1354
|
const approved = await ctx.ui.confirm("Refresh the .codecarto/ scaffold from the packaged template?", describeScaffoldRefreshPreview(files, state.scaffoldVersion));
|
|
1389
1355
|
if (!approved) {
|
|
1390
|
-
ctx
|
|
1356
|
+
notifyCtx(ctx, "Scaffold refresh cancelled. Nothing was written.", "info");
|
|
1391
1357
|
return;
|
|
1392
1358
|
}
|
|
1393
1359
|
try {
|
|
@@ -1400,13 +1366,13 @@ export default function codeCartographerExtension(pi) {
|
|
|
1400
1366
|
];
|
|
1401
1367
|
// Re-read state so the widget's staleness line clears with the marker.
|
|
1402
1368
|
await refreshWorkspaceUi(ctx, lastFeedbackLines);
|
|
1403
|
-
ctx
|
|
1369
|
+
notifyCtx(ctx, `Refreshed ${result.written.length} framework-owned file(s) (${transition}).`, "info");
|
|
1404
1370
|
}
|
|
1405
1371
|
catch (error) {
|
|
1406
1372
|
const message = error instanceof Error ? error.message : String(error);
|
|
1407
1373
|
lastFeedbackLines = [message];
|
|
1408
1374
|
setUiState(ctx, state, lastFeedbackLines);
|
|
1409
|
-
ctx
|
|
1375
|
+
notifyCtx(ctx, `Scaffold refresh failed: ${message}`, "error");
|
|
1410
1376
|
}
|
|
1411
1377
|
},
|
|
1412
1378
|
});
|
|
@@ -1428,12 +1394,12 @@ export default function codeCartographerExtension(pi) {
|
|
|
1428
1394
|
const hint = staged.length > 0
|
|
1429
1395
|
? ` (staged: ${staged.join(", ")})`
|
|
1430
1396
|
: " — write .codecarto/scratch/amendments/<name>.yaml first (see templates/amendment.yaml)";
|
|
1431
|
-
ctx
|
|
1397
|
+
notifyCtx(ctx, `Usage: /codecarto-amend <name>${hint}`, "warning");
|
|
1432
1398
|
return;
|
|
1433
1399
|
}
|
|
1434
1400
|
const name = resolveAmendmentName(args, ctx.cwd);
|
|
1435
1401
|
if (!name) {
|
|
1436
|
-
ctx
|
|
1402
|
+
notifyCtx(ctx, `Amendments are read from .codecarto/scratch/amendments/ only; pass the amendment name or a path inside that directory, not ${args.trim()}.`, "error");
|
|
1437
1403
|
return;
|
|
1438
1404
|
}
|
|
1439
1405
|
// The same refusals codecarto_amend surfaces, raised before the
|
|
@@ -1443,12 +1409,12 @@ export default function codeCartographerExtension(pi) {
|
|
|
1443
1409
|
amendment = await loadAmendmentFile(name, state.workspaceDir);
|
|
1444
1410
|
}
|
|
1445
1411
|
catch (error) {
|
|
1446
|
-
ctx
|
|
1412
|
+
notifyCtx(ctx, error instanceof Error ? error.message : String(error), "error");
|
|
1447
1413
|
return;
|
|
1448
1414
|
}
|
|
1449
1415
|
const nextPhase = getNextEligiblePhase(state);
|
|
1450
1416
|
if (nextPhase) {
|
|
1451
|
-
ctx
|
|
1417
|
+
notifyCtx(ctx, `Cannot amend: the pipeline is not complete (next phase: ${nextPhase.id}). `
|
|
1452
1418
|
+ "Resolve open questions and routed items through that phase's handoff (open_question_closures / carry_forward_closures) instead.", "error");
|
|
1453
1419
|
return;
|
|
1454
1420
|
}
|
|
@@ -1456,7 +1422,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
1456
1422
|
// before anything is written — MCP's codecarto_amend applies on call.
|
|
1457
1423
|
const approved = await ctx.ui.confirm(`Apply amendment "${amendment.slug}"?`, describeAmendmentPreview(amendment, state));
|
|
1458
1424
|
if (!approved) {
|
|
1459
|
-
ctx
|
|
1425
|
+
notifyCtx(ctx, `Amendment ${amendment.slug} cancelled. Nothing was written.`, "info");
|
|
1460
1426
|
return;
|
|
1461
1427
|
}
|
|
1462
1428
|
try {
|
|
@@ -1477,14 +1443,14 @@ export default function codeCartographerExtension(pi) {
|
|
|
1477
1443
|
lastFeedbackLines = lines;
|
|
1478
1444
|
await refreshWorkspaceUi(ctx, lastFeedbackLines);
|
|
1479
1445
|
const closed = applied.openQuestionsClosed.length + applied.postPipelineClosed.length;
|
|
1480
|
-
ctx
|
|
1446
|
+
notifyCtx(ctx, `Amendment ${amendment.slug} applied: ${applied.openQuestionsClosed.length} open question(s) and ${applied.postPipelineClosed.length} post-pipeline item(s) closed`
|
|
1481
1447
|
+ `${applied.unknownIds.length > 0 ? `; ${applied.unknownIds.length} id(s) matched nothing` : ""}.`, closed === 0 || applied.unknownIds.length > 0 ? "warning" : "info");
|
|
1482
1448
|
}
|
|
1483
1449
|
catch (error) {
|
|
1484
1450
|
const message = error instanceof Error ? error.message : String(error);
|
|
1485
1451
|
lastFeedbackLines = [message];
|
|
1486
1452
|
setUiState(ctx, state, lastFeedbackLines);
|
|
1487
|
-
ctx
|
|
1453
|
+
notifyCtx(ctx, `Amendment failed: ${message}`, "error");
|
|
1488
1454
|
}
|
|
1489
1455
|
},
|
|
1490
1456
|
});
|