codecartographer-pi 0.24.0 → 0.25.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/broadside/SKILL.md +31 -9
- package/.codecarto/workflow/scaffold-version.yaml +1 -1
- package/README.md +4 -3
- package/agent-skill/codecartographer/references/broadside.md +5 -1
- package/dist/core/amendment.d.ts +6 -3
- package/dist/core/amendment.js +21 -10
- package/dist/core/broadside.d.ts +87 -16
- package/dist/core/broadside.js +230 -30
- package/dist/core/status.d.ts +14 -0
- package/dist/core/status.js +104 -13
- package/dist/extensions/codecarto/agent-state.d.ts +7 -1
- package/dist/extensions/codecarto/agent-state.js +9 -1
- package/dist/extensions/codecarto/auto-runner.d.ts +7 -0
- package/dist/extensions/codecarto/auto-runner.js +16 -2
- package/dist/extensions/codecarto/broadside-flags.d.ts +3 -1
- package/dist/extensions/codecarto/broadside-flags.js +13 -0
- package/dist/extensions/codecarto/index.js +38 -8
- package/dist/mcp-server/server.d.ts +1 -0
- package/dist/mcp-server/server.js +18 -1
- package/package.json +1 -1
|
@@ -132,8 +132,9 @@ export async function runSinglePhase(ctx, pi, state, phase, options) {
|
|
|
132
132
|
return { status: "error", activity, error: message };
|
|
133
133
|
}
|
|
134
134
|
finally {
|
|
135
|
-
// Linger 30s so /codecarto-status can show that the phase ran.
|
|
136
|
-
|
|
135
|
+
// Linger 30s so /codecarto-status can show that the phase ran. Tied
|
|
136
|
+
// to this run's entry: a re-run inside the window keeps its own (#343).
|
|
137
|
+
setTimeout(() => clearPhase(phase.id, activity), 30_000).unref?.();
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
140
|
/**
|
|
@@ -304,6 +305,19 @@ export async function runAuto(ctx, pi, initialState, options) {
|
|
|
304
305
|
// ----------------------------------------------------------------------------
|
|
305
306
|
// buildAutoSummary — the codecarto-auto-summary message body
|
|
306
307
|
// ----------------------------------------------------------------------------
|
|
308
|
+
/**
|
|
309
|
+
* The one-line notification for an auto run's end. A run that stopped short
|
|
310
|
+
* carries its reason: the auto-summary message and the widget carry it too,
|
|
311
|
+
* but under `pi -p` neither is rendered, and "stopped: 0/1 phases" alone
|
|
312
|
+
* sent a reader back to the code to find out why (#347).
|
|
313
|
+
*/
|
|
314
|
+
export function describeAutoOutcome(result) {
|
|
315
|
+
const line = `Auto pipeline ${result.outcome}: ${result.phasesRun.length}/${result.totalPhases} phases.`;
|
|
316
|
+
if (result.outcome === "complete")
|
|
317
|
+
return line;
|
|
318
|
+
const reason = result.reason.trim();
|
|
319
|
+
return reason ? `${line.replace(/\.$/, "")} — ${reason}` : line;
|
|
320
|
+
}
|
|
307
321
|
export function buildAutoSummary(result, availableSkills = []) {
|
|
308
322
|
const totalTokens = result.totalTokens.input + result.totalTokens.output;
|
|
309
323
|
const wallTime = formatMillis(result.endedAt - result.startedAt);
|
|
@@ -24,11 +24,13 @@ export interface BroadsideFlags {
|
|
|
24
24
|
lensModels?: Partial<Record<BroadsideLensId, string>>;
|
|
25
25
|
/** For verify: how many findings to read (#143). */
|
|
26
26
|
top?: number;
|
|
27
|
+
/** For collect: reset the settled post-passes and run them again (#338). */
|
|
28
|
+
regeneratePostPasses?: boolean;
|
|
27
29
|
benchmarks: boolean;
|
|
28
30
|
unknown: string[];
|
|
29
31
|
/** Set on an invalid combination. The caller surfaces it as an error. */
|
|
30
32
|
error?: string;
|
|
31
33
|
}
|
|
32
34
|
/** Every token the completer offers, in the order it offers them. */
|
|
33
|
-
export declare const KNOWN_BROADSIDE_TOKENS: readonly ["submit", "collect", "status", "models", "verify", "architecture", "api", "security", "defect", "conventions", "porting", "--incremental", "--no-incremental", "--max-cost=", "--wait=", "--run=", "--model=", "--lens-model=", "--top=", "--no-synthesis", "--no-triage", "--no-retry-truncated", "--benchmarks"];
|
|
35
|
+
export declare const KNOWN_BROADSIDE_TOKENS: readonly ["submit", "collect", "status", "models", "verify", "architecture", "api", "security", "defect", "conventions", "porting", "--incremental", "--no-incremental", "--max-cost=", "--wait=", "--run=", "--model=", "--lens-model=", "--top=", "--regenerate", "--no-synthesis", "--no-triage", "--no-retry-truncated", "--benchmarks"];
|
|
34
36
|
export declare function parseBroadsideFlags(args: string): BroadsideFlags;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// /codecarto-broadside status
|
|
8
8
|
// /codecarto-broadside models --benchmarks
|
|
9
9
|
// /codecarto-broadside verify --top=10 → read the top findings against the source
|
|
10
|
+
// /codecarto-broadside collect --regenerate → rebuild synthesis and triage from the verdicts
|
|
10
11
|
//
|
|
11
12
|
// Flags mirror the codecarto_broadside tool parameters, with the negative
|
|
12
13
|
// forms spelled out because a slash command has no place to pass `false`:
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
// --model=ID (submit: the run's batch model, as listed by models; verify: the sync model to read with)
|
|
19
20
|
// --lens-model=LENS:ID (submit only, repeatable: one lens on its own model)
|
|
20
21
|
// --top=N (verify only: how many findings to read, most severe first)
|
|
22
|
+
// --regenerate (collect only: reset the settled post-passes and run them again, verdicts included)
|
|
21
23
|
//
|
|
22
24
|
// A model id itself contains a colon (`vendor/name:batch`), so --lens-model
|
|
23
25
|
// splits on the first colon only: `security:deepseek/deepseek-v4-pro:batch`.
|
|
@@ -47,6 +49,7 @@ export const KNOWN_BROADSIDE_TOKENS = [
|
|
|
47
49
|
"--model=",
|
|
48
50
|
"--lens-model=",
|
|
49
51
|
"--top=",
|
|
52
|
+
"--regenerate",
|
|
50
53
|
"--no-synthesis",
|
|
51
54
|
"--no-triage",
|
|
52
55
|
"--no-retry-truncated",
|
|
@@ -107,6 +110,10 @@ export function parseBroadsideFlags(args) {
|
|
|
107
110
|
result.retryTruncated = false;
|
|
108
111
|
continue;
|
|
109
112
|
}
|
|
113
|
+
if (token === "--regenerate") {
|
|
114
|
+
result.regeneratePostPasses = true;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
110
117
|
if (token === "--benchmarks") {
|
|
111
118
|
result.benchmarks = true;
|
|
112
119
|
continue;
|
|
@@ -188,6 +195,12 @@ export function parseBroadsideFlags(args) {
|
|
|
188
195
|
if (result.top !== undefined && result.action !== "verify") {
|
|
189
196
|
result.error ??= `--top is only meaningful for verify (got action "${result.action}").`;
|
|
190
197
|
}
|
|
198
|
+
if (result.regeneratePostPasses && result.action !== "collect") {
|
|
199
|
+
result.error ??= `--regenerate is only meaningful for collect (got action "${result.action}").`;
|
|
200
|
+
}
|
|
201
|
+
if (result.regeneratePostPasses && result.includeSynthesis === false && result.includeTriage === false) {
|
|
202
|
+
result.error ??= "--regenerate with both --no-synthesis and --no-triage leaves nothing to regenerate.";
|
|
203
|
+
}
|
|
191
204
|
if (result.action === "verify" && result.waitSeconds !== undefined) {
|
|
192
205
|
result.error ??= "--wait is only meaningful for submit and collect; verify runs to completion.";
|
|
193
206
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
2
|
import { basename, dirname, join, resolve } from "node:path";
|
|
3
|
-
import { autoCompletePhase, buildAutoSummary, isPhaseRunning, runAuto, runSinglePhase } from "./auto-runner.js";
|
|
3
|
+
import { autoCompletePhase, buildAutoSummary, describeAutoOutcome, isPhaseRunning, runAuto, runSinglePhase } from "./auto-runner.js";
|
|
4
4
|
import { disposeAgentsWidget } from "./agent-widget.js";
|
|
5
5
|
import { parseDashboardFlags } from "./dashboard-flags.js";
|
|
6
6
|
import { narrateDashboard } from "./dashboard-narrator.js";
|
|
@@ -11,7 +11,7 @@ import { buildPiGuideMessage } from "./guide-framing.js";
|
|
|
11
11
|
import { isCtxLive, notifyCtx } from "./notify.js";
|
|
12
12
|
import { phaseCompactionExtension } from "./phase-compaction.js";
|
|
13
13
|
import { applyAmendment, buildPhasePrompt, buildSkillPrompt, buildValidationSummary, backupWorkspaceState, canonicalPath, copyPackagedWorkspace, computePerPhaseTotals, computeTotals, ConfidentialityMismatchError, createEmptyStatus, DEFAULT_PIPELINE_PATH, describeScaffoldStaleness, deriveSlug, discoverLibrary, describeDanglingCarryForward, describeMissingCompletedOutputs, describeStuckPipeline, expandTilde, getPipelineLabel, getWorkspaceState, isWithinPath, resolveExistingPrefix, BROADSIDE_LENS_IDS, BROADSIDE_SKILL_NAME, BroadsideConfigError, defaultBroadsideConfig, BroadsideCancelledError, broadsideDirFor, collectResultText, estimateSubmitText, getLens, listAmendmentNames, listBatchModels, listGuideTopics, listScaffoldRefreshFiles, listMissingCompletedOutputs, listSkillNames, resolvePipelineOutcome, resolveSkillName, loadAmendmentFile, loadBroadsideConfig, modelsText, runBroadsideCollect, runBroadsideStatus, runBroadsideSubmit, runBroadsideVerify, verifyResultText, statusText, describeConfigProblems, describeIncrementalFallback, 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, writeDashboard, } from "../../core/index.js";
|
|
14
|
-
import { initLibrary } from "../../core/library.js";
|
|
14
|
+
import { initLibrary, isValidSlug } from "../../core/library.js";
|
|
15
15
|
import { resolveUserConfigPath } from "../../core/orchestrator-config.js";
|
|
16
16
|
const STATUS_WIDGET_ID = "codecarto-widget";
|
|
17
17
|
// Broad-Side gets its own widget id: a scout run is legal on a repository with
|
|
@@ -638,7 +638,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
638
638
|
});
|
|
639
639
|
lastFeedbackLines = [`Auto pipeline ${result.outcome}: ${result.reason}`];
|
|
640
640
|
await refreshWorkspaceUi(ctx, lastFeedbackLines);
|
|
641
|
-
notifyCtx(ctx,
|
|
641
|
+
notifyCtx(ctx, describeAutoOutcome(result), result.outcome === "complete" ? "info" : "warning");
|
|
642
642
|
return;
|
|
643
643
|
}
|
|
644
644
|
const outcome = resolvePipelineOutcome(state);
|
|
@@ -995,7 +995,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
995
995
|
},
|
|
996
996
|
});
|
|
997
997
|
pi.registerCommand("codecarto-broadside", {
|
|
998
|
-
description: "Batch reconnaissance (Broad-Side): /codecarto-broadside [submit|collect|status|models|verify] [lenses…] [--model=ID] [--lens-model=LENS:ID] [--top=N] [flags]",
|
|
998
|
+
description: "Batch reconnaissance (Broad-Side): /codecarto-broadside [submit|collect|status|models|verify] [lenses…] [--model=ID] [--lens-model=LENS:ID] [--top=N] [--regenerate] [flags]",
|
|
999
999
|
// Completes the token under the cursor, so lens names and flags are
|
|
1000
1000
|
// offered after the action too, and keeps everything typed before it.
|
|
1001
1001
|
getArgumentCompletions: (prefix) => completeLastToken(prefix, KNOWN_BROADSIDE_TOKENS.map((value) => ({ value }))),
|
|
@@ -1220,6 +1220,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
1220
1220
|
retryTruncated,
|
|
1221
1221
|
onStatus,
|
|
1222
1222
|
...(flags.runId && { runId: flags.runId }),
|
|
1223
|
+
...(flags.regeneratePostPasses && { regeneratePostPasses: true }),
|
|
1223
1224
|
});
|
|
1224
1225
|
const lines = collectResultText(collect).split("\n");
|
|
1225
1226
|
const done = collect.status === "completed";
|
|
@@ -1354,12 +1355,41 @@ export default function codeCartographerExtension(pi) {
|
|
|
1354
1355
|
pi.registerCommand("codecarto-library-init", {
|
|
1355
1356
|
description: "Initialize a CodeCartographer library and configure it: /codecarto-library-init <path> [--namespace <name>]",
|
|
1356
1357
|
handler: async (args, ctx) => {
|
|
1357
|
-
const
|
|
1358
|
-
const
|
|
1358
|
+
const usage = "Usage: /codecarto-library-init <path> [--namespace <name>]";
|
|
1359
|
+
const parts = args.trim() === "" ? [] : args.trim().split(/\s+/);
|
|
1360
|
+
// `--namespace` with nothing after it used to read as "no namespace"
|
|
1361
|
+
// and initialize an unnamespaced library without a word (Broad-Side
|
|
1362
|
+
// verify, 2026-09-13 run): the flag is either complete or refused.
|
|
1359
1363
|
const namespaceIdx = parts.indexOf("--namespace");
|
|
1360
|
-
|
|
1364
|
+
let namespace = null;
|
|
1365
|
+
if (namespaceIdx >= 0) {
|
|
1366
|
+
const value = parts[namespaceIdx + 1];
|
|
1367
|
+
if (value === undefined || value.startsWith("--")) {
|
|
1368
|
+
notifyCtx(ctx, `--namespace needs a name. ${usage}`, "warning");
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
// The rule publish applies to it later, applied before it is
|
|
1372
|
+
// written into the config: lowercase ASCII, starts with a
|
|
1373
|
+
// letter, at most 64 characters.
|
|
1374
|
+
if (!isValidSlug(value)) {
|
|
1375
|
+
notifyCtx(ctx, `Invalid namespace "${value}" (lowercase ASCII, starts with a letter, max 64 chars). ${usage}`, "warning");
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1378
|
+
namespace = value;
|
|
1379
|
+
parts.splice(namespaceIdx, 2);
|
|
1380
|
+
}
|
|
1381
|
+
const stray = parts.find((part) => part.startsWith("--"));
|
|
1382
|
+
if (stray) {
|
|
1383
|
+
notifyCtx(ctx, `Unknown flag ${stray}. ${usage}`, "warning");
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
const [pathArg, ...extra] = parts;
|
|
1361
1387
|
if (!pathArg) {
|
|
1362
|
-
notifyCtx(ctx,
|
|
1388
|
+
notifyCtx(ctx, usage, "warning");
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1391
|
+
if (extra.length > 0) {
|
|
1392
|
+
notifyCtx(ctx, `One path, please — got ${parts.length}. ${usage}`, "warning");
|
|
1363
1393
|
return;
|
|
1364
1394
|
}
|
|
1365
1395
|
// The same expansion the config loader applies, so `~user/x` and a
|
|
@@ -808,6 +808,11 @@ export async function handleLibraryInit(args) {
|
|
|
808
808
|
}
|
|
809
809
|
const libraryPath = args.library_path;
|
|
810
810
|
const namespaced = !!args.namespace;
|
|
811
|
+
// The rule codecarto_publish applies to the namespace later, applied
|
|
812
|
+
// before it is written into the config.
|
|
813
|
+
if (namespaced && !isValidSlug(args.namespace)) {
|
|
814
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid namespace "${args.namespace}" (lowercase ASCII, starts with a letter, max 64 chars).`);
|
|
815
|
+
}
|
|
811
816
|
const result = await initLibrary(libraryPath, {
|
|
812
817
|
name: args.name,
|
|
813
818
|
namespaced,
|
|
@@ -1207,6 +1212,12 @@ export async function handleBroadside(args) {
|
|
|
1207
1212
|
});
|
|
1208
1213
|
}
|
|
1209
1214
|
// action === "collect"
|
|
1215
|
+
if (args.regenerate_post_passes !== undefined && typeof args.regenerate_post_passes !== "boolean") {
|
|
1216
|
+
throw new McpError(ErrorCode.InvalidParams, "regenerate_post_passes must be a boolean.");
|
|
1217
|
+
}
|
|
1218
|
+
if (args.regenerate_post_passes && !includeSynthesis && !includeTriage) {
|
|
1219
|
+
throw new McpError(ErrorCode.InvalidParams, "regenerate_post_passes needs at least one of include_synthesis and include_triage.");
|
|
1220
|
+
}
|
|
1210
1221
|
const collect = await runBroadsideCollect(cwd, apiKey, {
|
|
1211
1222
|
waitMs,
|
|
1212
1223
|
includeSynthesis,
|
|
@@ -1214,6 +1225,7 @@ export async function handleBroadside(args) {
|
|
|
1214
1225
|
retryTruncated,
|
|
1215
1226
|
signal: serverLifetime?.signal,
|
|
1216
1227
|
...(runId && { runId }),
|
|
1228
|
+
...(args.regenerate_post_passes && { regeneratePostPasses: true }),
|
|
1217
1229
|
}).catch((error) => {
|
|
1218
1230
|
throw new McpError(ErrorCode.InvalidRequest, error instanceof Error ? error.message : String(error));
|
|
1219
1231
|
});
|
|
@@ -1227,6 +1239,7 @@ export async function handleBroadside(args) {
|
|
|
1227
1239
|
lensOutcomes: collect.lensOutcomes,
|
|
1228
1240
|
synthesis: collect.synthesis,
|
|
1229
1241
|
triage: collect.triage,
|
|
1242
|
+
...(collect.regenerated && { regenerated: collect.regenerated }),
|
|
1230
1243
|
topFindings: collect.topFindings,
|
|
1231
1244
|
topTriageItems: collect.topTriageItems,
|
|
1232
1245
|
});
|
|
@@ -1535,7 +1548,7 @@ const TOOLS = [
|
|
|
1535
1548
|
action: {
|
|
1536
1549
|
type: "string",
|
|
1537
1550
|
enum: ["submit", "collect", "status", "models", "verify"],
|
|
1538
|
-
description: "submit fires all lens batches and returns batch ids; collect polls submitted batches, saves results, and optionally runs the synthesis pass; status shows recorded runs; models lists batch-capable models with pricing and capabilities; verify reads a collected run's top defect and security findings against the repository with read-only tools (one sync-priced call each, about a cent on the default model) and writes verified.md/verified.json beside triage.md with a verdict per finding: confirmed (a reachable failure, with the trigger), not-a-defect, discarded, or unclear.",
|
|
1551
|
+
description: "submit fires all lens batches and returns batch ids; collect polls submitted batches, saves results, and optionally runs the synthesis and triage passes — built from verified.json when a verify pass has already written it, so run verify first or collect again with regenerate_post_passes afterwards; status shows recorded runs; models lists batch-capable models with pricing and capabilities; verify reads a collected run's top defect and security findings against the repository with read-only tools (one sync-priced call each, about a cent on the default model) and writes verified.md/verified.json beside triage.md with a verdict per finding: confirmed (a reachable failure, with the trigger), not-a-defect, discarded, or unclear.",
|
|
1539
1552
|
},
|
|
1540
1553
|
lenses: {
|
|
1541
1554
|
type: "array",
|
|
@@ -1570,6 +1583,10 @@ const TOOLS = [
|
|
|
1570
1583
|
type: "boolean",
|
|
1571
1584
|
description: "Re-submit lens results that came back truncated at the output token limit, once, with a doubled output cap. Falls back to retry_truncated in .codecarto/broadside/config.yaml (default true).",
|
|
1572
1585
|
},
|
|
1586
|
+
regenerate_post_passes: {
|
|
1587
|
+
type: "boolean",
|
|
1588
|
+
description: "collect only: reset the run's settled synthesis and triage passes and run them again, so a verify pass's verdicts (verified.json) are built into the executive report and the work order — confirmed findings first, discarded ones dropped, not-a-defect ones listed apart. Costs another post-pass pair (a few cents); a pass still in flight is left to finish; a run whose lens batches are still running is refused.",
|
|
1589
|
+
},
|
|
1573
1590
|
max_cost: {
|
|
1574
1591
|
type: "number",
|
|
1575
1592
|
description: "Approximate run expense limit in USD. The submit action estimates the run cost from slice sizes and the configured model's per-token pricing (live OpenRouter lookup, cached 24h) and refuses to submit when the estimate exceeds the limit unless force is true. For verify it is a running cap: the pass stops before the next finding once the calls so far have reached it. Falls back to max_cost in .codecarto/broadside/config.yaml, whose default is $1.00; pass 0 for no limit.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codecartographer-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"mcpName": "io.github.HuginnIndustries/codecartographer",
|
|
5
5
|
"description": "Turn an unfamiliar codebase into a validated reimplementation spec, then synthesize confirmed specs and a product vision into a traceable plan.",
|
|
6
6
|
"type": "module",
|