codecartographer-pi 0.20.0 → 0.22.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 +1 -1
- package/.codecarto/broadside/SKILL.md +15 -4
- package/.codecarto/broadside/config.yaml +26 -10
- package/.codecarto/findings/contracts/SKILL.md +4 -1
- package/.codecarto/findings/defect-scan/SKILL.md +10 -0
- package/.codecarto/findings/defect-scan-mechanical/SKILL.md +6 -0
- package/.codecarto/findings/defect-scan-semantic/SKILL.md +8 -1
- package/.codecarto/findings/porting/SKILL.md +4 -0
- package/.codecarto/findings/protocols/SKILL.md +4 -0
- package/.codecarto/templates/mechanical-defects.md +15 -0
- package/.codecarto/templates/reimplementation-spec.md +5 -3
- package/.codecarto/templates/reverse-engineering-bundle.md +10 -1
- package/.codecarto/templates/semantic-defects.md +15 -0
- package/.codecarto/workflow/VALIDATE.md +1 -1
- package/.codecarto/workflow/scaffold-version.yaml +1 -1
- package/README.md +5 -5
- package/agent-skill/codecartographer/references/broadside.md +10 -0
- package/dist/core/amendment.js +9 -4
- package/dist/core/broadside.d.ts +98 -0
- package/dist/core/broadside.js +330 -63
- package/dist/core/completion.js +7 -1
- package/dist/core/library.js +5 -3
- package/dist/core/pipeline.d.ts +39 -3
- package/dist/core/pipeline.js +61 -7
- package/dist/core/prompts.js +10 -3
- package/dist/core/status.d.ts +8 -0
- package/dist/core/status.js +39 -17
- package/dist/core/utils.d.ts +7 -0
- package/dist/core/utils.js +7 -0
- package/dist/core/workspace.js +2 -2
- package/dist/core/yaml.js +8 -1
- package/dist/extensions/codecarto/auto-runner.d.ts +1 -1
- package/dist/extensions/codecarto/auto-runner.js +18 -3
- package/dist/extensions/codecarto/broadside-flags.d.ts +7 -1
- package/dist/extensions/codecarto/broadside-flags.js +51 -0
- package/dist/extensions/codecarto/index.js +103 -35
- package/dist/extensions/codecarto/phase-compaction.js +5 -1
- package/dist/mcp-server/server.d.ts +5 -1
- package/dist/mcp-server/server.js +140 -32
- package/package.json +1 -1
|
@@ -17,7 +17,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
17
17
|
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js";
|
|
18
18
|
import { mkdir, readFile, readdir, rename, writeFile } from "node:fs/promises";
|
|
19
19
|
import { basename, isAbsolute, join } from "node:path";
|
|
20
|
-
import { buildPhasePrompt, buildSkillPrompt, buildValidationSummary, BROADSIDE_DIR, broadsideDirFor, BROADSIDE_LENS_IDS, BROADSIDE_SKILL_NAME, backupWorkspaceState, canonicalPath, copyPackagedWorkspace, collectResultText, completeValidatedPhase, computePerPhaseTotals, computeTotals, createEmptyStatus, DEFAULT_PIPELINE_PATH, deriveSlug, discoverLibrary, describeScaffoldStaleness, detectProvenanceConflicts, estimateSubmitText, getLens, describeDanglingCarryForward, describeMissingCompletedOutputs,
|
|
20
|
+
import { buildPhasePrompt, buildSkillPrompt, buildValidationSummary, BROADSIDE_DIR, broadsideDirFor, BROADSIDE_LENS_IDS, BROADSIDE_SKILL_NAME, BroadsideConfigError, defaultBroadsideConfig, backupWorkspaceState, canonicalPath, copyPackagedWorkspace, collectResultText, completeValidatedPhase, computePerPhaseTotals, computeTotals, createEmptyStatus, DEFAULT_PIPELINE_PATH, deriveSlug, discoverLibrary, describeScaffoldStaleness, detectProvenanceConflicts, estimateSubmitText, getLens, describeDanglingCarryForward, describeMissingCompletedOutputs, describeStuckPipeline, getPipelineLabel, getWorkspaceState, isValidSlug, isWithinPathResolved, listBatchModels, listEntries, listGuideTopics, loadBroadsideConfig, modelsText, readGuide, listMissingCompletedOutputs, listSkillNames, resolvePipelineOutcome, resolveSkillName, loadCodecartoConfig, loadUsage, loadYamlFile, normalizeForComparison, PACKAGE_VERSION, packagedWorkspaceDir, pathExists, PhasePreflightError, previewPublishVersion, publishEntry, reindex as libraryReindex, refreshScaffold, resolvePhase, resolvePipelineChoice, readBroadsideSkill, runBroadsideCollect, runBroadsideStatus, runBroadsideSubmit, seedOrchestratorFiles, statusText, stringifySimpleYaml, switchPipeline, validatePhaseOutput, writeLibraryConfig, writeDashboard, } from "../core/index.js";
|
|
21
21
|
import { applyAmendment } from "../core/amendment.js";
|
|
22
22
|
import { appendUsageRun } from "../core/usage.js";
|
|
23
23
|
import { initLibrary } from "../core/library.js";
|
|
@@ -69,8 +69,12 @@ async function optionalCwd(cwd) {
|
|
|
69
69
|
return validateCwd(cwd.trim());
|
|
70
70
|
}
|
|
71
71
|
async function requireWorkspace(cwd) {
|
|
72
|
+
// What getWorkspaceState throws is the user's to fix — a status.yaml that
|
|
73
|
+
// does not parse, a missing `pipeline:`, a pipeline file that is not there
|
|
74
|
+
// — so it is InvalidRequest, not the InternalError a host would retry or
|
|
75
|
+
// report as a server bug (self-audit mech 2.8).
|
|
72
76
|
const state = await getWorkspaceState(cwd).catch((error) => {
|
|
73
|
-
throw new McpError(ErrorCode.
|
|
77
|
+
throw new McpError(ErrorCode.InvalidRequest, error instanceof Error ? error.message : String(error));
|
|
74
78
|
});
|
|
75
79
|
if (!state) {
|
|
76
80
|
throw new McpError(ErrorCode.InvalidRequest, `No CodeCartographer workspace at ${cwd}. Call codecarto_init first.`);
|
|
@@ -89,9 +93,9 @@ function textResult(text, structured) {
|
|
|
89
93
|
structuredContent: { ...structured, text },
|
|
90
94
|
};
|
|
91
95
|
}
|
|
92
|
-
async function buildMcpPhasePrompt(state, phase, forced) {
|
|
96
|
+
async function buildMcpPhasePrompt(state, phase, forced, auto = false) {
|
|
93
97
|
try {
|
|
94
|
-
return await buildPhasePrompt(state, phase, forced);
|
|
98
|
+
return await buildPhasePrompt(state, phase, forced, { auto });
|
|
95
99
|
}
|
|
96
100
|
catch (error) {
|
|
97
101
|
if (error instanceof PhasePreflightError) {
|
|
@@ -188,8 +192,11 @@ export async function handleInit(args) {
|
|
|
188
192
|
export async function handleStatus(args) {
|
|
189
193
|
const cwd = await validateCwd(args.cwd);
|
|
190
194
|
const state = await requireWorkspace(cwd);
|
|
191
|
-
const
|
|
192
|
-
const
|
|
195
|
+
const outcome = resolvePipelineOutcome(state);
|
|
196
|
+
const nextPhase = outcome.kind === "eligible" ? outcome.phase : null;
|
|
197
|
+
// "complete" is the terminal marker; a stuck pipeline sits on its first
|
|
198
|
+
// blocked phase and says so below (#228).
|
|
199
|
+
const currentPhase = outcome.kind === "eligible" ? outcome.phase.id : outcome.kind === "stuck" ? outcome.blocked[0].phaseId : "complete";
|
|
193
200
|
const completed = state.pipeline.phase_order.filter((id) => state.status.phases[id]?.status === "complete").length;
|
|
194
201
|
const totalCarryForward = Object.values(state.status.phases).reduce((sum, phase) => sum + (phase.carry_forward?.length ?? 0), 0);
|
|
195
202
|
const currentOpenQuestions = currentPhase === "complete" ? 0 : state.status.phases[currentPhase]?.open_questions?.length ?? 0;
|
|
@@ -199,7 +206,7 @@ export async function handleStatus(args) {
|
|
|
199
206
|
const missingOutputs = await listMissingCompletedOutputs(state);
|
|
200
207
|
const summaryLines = [
|
|
201
208
|
`Phase: ${currentPhase}`,
|
|
202
|
-
`Pipeline state: ${
|
|
209
|
+
`Pipeline state: ${outcome.kind === "eligible" ? "in progress" : outcome.kind}`,
|
|
203
210
|
`Pipeline: ${getPipelineLabel(state.status.pipeline)} (${state.status.pipeline})`,
|
|
204
211
|
`Progress: ${completed}/${state.pipeline.phase_order.length} complete`,
|
|
205
212
|
`Open questions (terminal unresolved): ${terminalOpenQuestions}`,
|
|
@@ -212,6 +219,8 @@ export async function handleStatus(args) {
|
|
|
212
219
|
? state.status.next_actions.map((action, index) => `${index === 0 ? "Next: " : " "}${action}`)
|
|
213
220
|
: [`Next: ${nextPhase ? `Begin ${nextPhase.id}` : "All phases complete."}`]),
|
|
214
221
|
];
|
|
222
|
+
if (outcome.kind === "stuck")
|
|
223
|
+
summaryLines.push(describeStuckPipeline(outcome.blocked));
|
|
215
224
|
if (scaffoldNotice)
|
|
216
225
|
summaryLines.push(`Scaffold: ${scaffoldNotice}`);
|
|
217
226
|
summaryLines.push(...describeMissingCompletedOutputs(missingOutputs));
|
|
@@ -219,6 +228,8 @@ export async function handleStatus(args) {
|
|
|
219
228
|
return textResult(summary, {
|
|
220
229
|
...(scaffoldNotice ? { scaffoldNotice } : {}),
|
|
221
230
|
...(missingOutputs.length > 0 ? { missingOutputs } : {}),
|
|
231
|
+
...(outcome.kind === "stuck" ? { stuck: outcome.blocked } : {}),
|
|
232
|
+
pipelineState: outcome.kind === "eligible" ? "in progress" : outcome.kind,
|
|
222
233
|
currentPhase,
|
|
223
234
|
pipeline: state.status.pipeline,
|
|
224
235
|
pipelineLabel: getPipelineLabel(state.status.pipeline),
|
|
@@ -271,14 +282,24 @@ export async function handleSwitchPipeline(args) {
|
|
|
271
282
|
export async function handleNext(args) {
|
|
272
283
|
const cwd = await validateCwd(args.cwd);
|
|
273
284
|
const state = await requireWorkspace(cwd);
|
|
274
|
-
const
|
|
275
|
-
if (
|
|
285
|
+
const outcome = resolvePipelineOutcome(state);
|
|
286
|
+
if (outcome.kind === "stuck") {
|
|
287
|
+
// Not a result: a host looping on codecarto_next would read a text
|
|
288
|
+
// answer as "done" (#228). There is no prompt to hand out until the
|
|
289
|
+
// pipeline file is fixed.
|
|
290
|
+
throw new McpError(ErrorCode.InvalidRequest, describeStuckPipeline(outcome.blocked));
|
|
291
|
+
}
|
|
292
|
+
if (outcome.kind === "complete") {
|
|
276
293
|
return textResult("All CodeCartographer phases are complete. Run codecarto_skill for post-pipeline work.", {
|
|
277
294
|
complete: true,
|
|
278
295
|
});
|
|
279
296
|
}
|
|
280
|
-
|
|
281
|
-
|
|
297
|
+
// `unattended` is the MCP spelling of Pi's --auto: nobody is there to
|
|
298
|
+
// answer the reimplementation-spec phase's Strategic Alignment Hook, so
|
|
299
|
+
// the prompt carries the auto-default instruction instead (#270).
|
|
300
|
+
const unattended = args.unattended === true;
|
|
301
|
+
const prompt = await buildMcpPhasePrompt(state, outcome.phase, false, unattended);
|
|
302
|
+
return textResult(prompt, { phase: outcome.phase.id, forced: false, unattended });
|
|
282
303
|
}
|
|
283
304
|
export async function handlePhase(args) {
|
|
284
305
|
if (typeof args.phase !== "string" || !args.phase.trim()) {
|
|
@@ -290,8 +311,9 @@ export async function handlePhase(args) {
|
|
|
290
311
|
if (!phase) {
|
|
291
312
|
throw new McpError(ErrorCode.InvalidParams, `Unknown phase: ${args.phase}`);
|
|
292
313
|
}
|
|
293
|
-
const
|
|
294
|
-
|
|
314
|
+
const unattended = args.unattended === true;
|
|
315
|
+
const prompt = await buildMcpPhasePrompt(state, phase, true, unattended);
|
|
316
|
+
return textResult(prompt, { phase: phase.id, forced: true, unattended });
|
|
295
317
|
}
|
|
296
318
|
export async function handleValidate(args) {
|
|
297
319
|
const cwd = await validateCwd(args.cwd);
|
|
@@ -391,9 +413,12 @@ export async function handleSkill(args) {
|
|
|
391
413
|
return textResult(skill.content, { skill: BROADSIDE_SKILL_NAME, path: skill.path, postPipeline: false });
|
|
392
414
|
}
|
|
393
415
|
const state = await requireWorkspace(cwd);
|
|
394
|
-
const
|
|
395
|
-
if (
|
|
396
|
-
throw new McpError(ErrorCode.InvalidRequest, `Cannot run skill: pipeline is not complete (next phase: ${
|
|
416
|
+
const outcome = resolvePipelineOutcome(state);
|
|
417
|
+
if (outcome.kind === "eligible") {
|
|
418
|
+
throw new McpError(ErrorCode.InvalidRequest, `Cannot run skill: pipeline is not complete (next phase: ${outcome.phase.id}). Finish the pipeline first.`);
|
|
419
|
+
}
|
|
420
|
+
if (outcome.kind === "stuck") {
|
|
421
|
+
throw new McpError(ErrorCode.InvalidRequest, `Cannot run skill: the pipeline is not complete. ${describeStuckPipeline(outcome.blocked)}`);
|
|
397
422
|
}
|
|
398
423
|
// Resolve against the installed list only: the name is never joined onto a
|
|
399
424
|
// path, so a traversal like `../findings/architecture` cannot splice a
|
|
@@ -883,8 +908,16 @@ export async function handleOpen(args) {
|
|
|
883
908
|
throw new McpError(ErrorCode.InvalidRequest, "No existing CodeCartographer workspace found. Run codecarto_init first.");
|
|
884
909
|
}
|
|
885
910
|
const state = await requireWorkspace(cwd);
|
|
886
|
-
const
|
|
887
|
-
|
|
911
|
+
const outcome = resolvePipelineOutcome(state);
|
|
912
|
+
const nextPhase = outcome.kind === "eligible" ? outcome.phase.id : outcome.kind === "stuck" ? `${outcome.blocked[0].phaseId} (stuck)` : "complete";
|
|
913
|
+
const lines = [`Opened existing CodeCartographer workspace: ${getPipelineLabel(state.status.pipeline)}. Current phase: ${nextPhase}.`];
|
|
914
|
+
if (outcome.kind === "stuck")
|
|
915
|
+
lines.push(describeStuckPipeline(outcome.blocked));
|
|
916
|
+
return textResult(lines.join("\n"), {
|
|
917
|
+
pipeline: getPipelineLabel(state.status.pipeline),
|
|
918
|
+
currentPhase: nextPhase,
|
|
919
|
+
...(outcome.kind === "stuck" ? { stuck: outcome.blocked } : {}),
|
|
920
|
+
});
|
|
888
921
|
}
|
|
889
922
|
export async function handleUsage(args) {
|
|
890
923
|
const cwd = await validateCwd(args.cwd);
|
|
@@ -1007,32 +1040,58 @@ export async function handleBroadside(args) {
|
|
|
1007
1040
|
if (!["submit", "collect", "status", "models"].includes(action)) {
|
|
1008
1041
|
throw new McpError(ErrorCode.InvalidParams, `Unknown action: ${action}. Valid actions: submit, collect, status, models.`);
|
|
1009
1042
|
}
|
|
1010
|
-
|
|
1043
|
+
// A config.yaml that exists but cannot be read refuses every action that
|
|
1044
|
+
// would act on it (#232); status only reads recorded runs, so it answers
|
|
1045
|
+
// and says the file is unreadable. A corrupt state.json refuses even that:
|
|
1046
|
+
// there is nothing trustworthy to report and nothing may write over it (#233).
|
|
1047
|
+
let config;
|
|
1048
|
+
let configWarning = null;
|
|
1049
|
+
try {
|
|
1050
|
+
config = await loadBroadsideConfig(broadsideDirFor(cwd));
|
|
1051
|
+
}
|
|
1052
|
+
catch (error) {
|
|
1053
|
+
if (!(error instanceof BroadsideConfigError) || action !== "status") {
|
|
1054
|
+
throw new McpError(ErrorCode.InvalidRequest, error instanceof Error ? error.message : String(error));
|
|
1055
|
+
}
|
|
1056
|
+
config = defaultBroadsideConfig();
|
|
1057
|
+
configWarning = error.message;
|
|
1058
|
+
}
|
|
1011
1059
|
if (action === "status") {
|
|
1012
|
-
const { state } = await runBroadsideStatus(cwd)
|
|
1013
|
-
|
|
1060
|
+
const { state } = await runBroadsideStatus(cwd).catch((error) => {
|
|
1061
|
+
throw new McpError(ErrorCode.InvalidRequest, error instanceof Error ? error.message : String(error));
|
|
1062
|
+
});
|
|
1063
|
+
const lines = [statusText(state)];
|
|
1064
|
+
if (configWarning)
|
|
1065
|
+
lines.push(`Warning: ${configWarning}`);
|
|
1066
|
+
return textResult(lines.join("\n"), { state, ...(configWarning ? { configWarning } : {}) });
|
|
1014
1067
|
}
|
|
1015
1068
|
const apiKey = resolveBroadsideApiKey(args.api_key, config);
|
|
1016
1069
|
// Every run knob resolves the same way: explicit parameter, else the repo's
|
|
1017
1070
|
// config.yaml default, else the shipped default baked into loadBroadsideConfig.
|
|
1018
|
-
|
|
1071
|
+
// An explicit 0 means "poll once and return": it used to fall through to
|
|
1072
|
+
// the config default and, as undefined, to the 25-minute budget (#230).
|
|
1073
|
+
const waitSeconds = typeof args.wait_seconds === "number" && args.wait_seconds >= 0
|
|
1019
1074
|
? args.wait_seconds
|
|
1020
1075
|
: config.waitSeconds;
|
|
1021
|
-
const waitMs = waitSeconds
|
|
1076
|
+
const waitMs = waitSeconds * 1000;
|
|
1022
1077
|
const includeSynthesis = args.include_synthesis ?? config.includeSynthesis;
|
|
1023
1078
|
const includeTriage = args.include_triage ?? config.includeTriage;
|
|
1024
1079
|
const retryTruncated = args.retry_truncated ?? config.retryTruncated;
|
|
1025
1080
|
const incremental = args.incremental ?? config.incremental;
|
|
1026
1081
|
if (action === "models") {
|
|
1027
|
-
const { entries, benchmarks } = await listBatchModels(broadsideDirFor(cwd), config, apiKey, {
|
|
1082
|
+
const { entries, benchmarks, endpoints } = await listBatchModels(broadsideDirFor(cwd), config, apiKey, {
|
|
1028
1083
|
includeBenchmarks: args.include_benchmarks === true,
|
|
1029
1084
|
}).catch((error) => {
|
|
1030
1085
|
throw new McpError(ErrorCode.InvalidRequest, error instanceof Error ? error.message : String(error));
|
|
1031
1086
|
});
|
|
1032
|
-
return textResult(modelsText(entries, { benchmarks, defaultModel: config.model }), {
|
|
1087
|
+
return textResult(modelsText(entries, { benchmarks, defaultModel: config.model, endpoints }), {
|
|
1033
1088
|
models: entries,
|
|
1034
1089
|
defaultModel: config.model,
|
|
1035
1090
|
benchmarkMeta: benchmarks?.meta ?? null,
|
|
1091
|
+
// The catalog is advisory (#141): what this repository's submits
|
|
1092
|
+
// learned about each id's batch endpoint rides alongside it.
|
|
1093
|
+
catalogAdvisory: true,
|
|
1094
|
+
endpoints,
|
|
1036
1095
|
});
|
|
1037
1096
|
}
|
|
1038
1097
|
if (action === "submit") {
|
|
@@ -1047,10 +1106,37 @@ export async function handleBroadside(args) {
|
|
|
1047
1106
|
else {
|
|
1048
1107
|
lenses = config.defaultLenses;
|
|
1049
1108
|
}
|
|
1050
|
-
|
|
1109
|
+
// An explicit 0 is "no limit" (#231); absent falls back to config.yaml,
|
|
1110
|
+
// whose own default is BROADSIDE_DEFAULT_MAX_COST.
|
|
1111
|
+
const maxCost = typeof args.max_cost === "number" && args.max_cost >= 0 ? args.max_cost : config.maxCost;
|
|
1112
|
+
// Model selection for one run (#141): `model` replaces the run default,
|
|
1113
|
+
// `lens_models` layers per-lens overrides over config.yaml's. Both are
|
|
1114
|
+
// pre-flighted by core exactly like the file's values — priced from the
|
|
1115
|
+
// catalog, refused without structured-output support, clamped to the
|
|
1116
|
+
// model's ceiling — so a wrong id fails before anything is submitted.
|
|
1117
|
+
const model = typeof args.model === "string" && args.model.trim() ? args.model.trim() : config.model;
|
|
1118
|
+
if (args.model !== undefined && !(typeof args.model === "string" && args.model.trim())) {
|
|
1119
|
+
throw new McpError(ErrorCode.InvalidParams, "model must be a non-empty OpenRouter batch model id (see action 'models').");
|
|
1120
|
+
}
|
|
1121
|
+
const lensModels = {};
|
|
1122
|
+
if (args.lens_models !== undefined) {
|
|
1123
|
+
if (!args.lens_models || typeof args.lens_models !== "object" || Array.isArray(args.lens_models)) {
|
|
1124
|
+
throw new McpError(ErrorCode.InvalidParams, "lens_models must be an object mapping lens ids to batch model ids.");
|
|
1125
|
+
}
|
|
1126
|
+
for (const [lensId, value] of Object.entries(args.lens_models)) {
|
|
1127
|
+
if (!BROADSIDE_LENS_IDS.includes(lensId)) {
|
|
1128
|
+
throw new McpError(ErrorCode.InvalidParams, `lens_models: unknown lens "${lensId}". Valid: ${BROADSIDE_LENS_IDS.join(", ")}`);
|
|
1129
|
+
}
|
|
1130
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
1131
|
+
throw new McpError(ErrorCode.InvalidParams, `lens_models.${lensId} must be a non-empty OpenRouter batch model id.`);
|
|
1132
|
+
}
|
|
1133
|
+
lensModels[lensId] = value.trim();
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1051
1136
|
const result = await runBroadsideSubmit(cwd, apiKey, {
|
|
1052
1137
|
lenses,
|
|
1053
|
-
model
|
|
1138
|
+
model,
|
|
1139
|
+
lensModels,
|
|
1054
1140
|
maxCost,
|
|
1055
1141
|
force: args.force === true,
|
|
1056
1142
|
incremental,
|
|
@@ -1084,11 +1170,13 @@ export async function handleBroadside(args) {
|
|
|
1084
1170
|
});
|
|
1085
1171
|
}
|
|
1086
1172
|
// action === "collect"
|
|
1173
|
+
const runId = typeof args.run_id === "string" && args.run_id.trim() ? args.run_id.trim() : undefined;
|
|
1087
1174
|
const collect = await runBroadsideCollect(cwd, apiKey, {
|
|
1088
1175
|
waitMs,
|
|
1089
1176
|
includeSynthesis,
|
|
1090
1177
|
includeTriage,
|
|
1091
1178
|
retryTruncated,
|
|
1179
|
+
...(runId && { runId }),
|
|
1092
1180
|
}).catch((error) => {
|
|
1093
1181
|
throw new McpError(ErrorCode.InvalidRequest, error instanceof Error ? error.message : String(error));
|
|
1094
1182
|
});
|
|
@@ -1156,7 +1244,13 @@ const TOOLS = [
|
|
|
1156
1244
|
description: "Return the prompt text for the next eligible CodeCartographer phase. The host should feed this prompt back to the agent or display it to the user.",
|
|
1157
1245
|
inputSchema: {
|
|
1158
1246
|
type: "object",
|
|
1159
|
-
properties: {
|
|
1247
|
+
properties: {
|
|
1248
|
+
cwd: { type: "string", description: "Absolute path to the target repository." },
|
|
1249
|
+
unattended: {
|
|
1250
|
+
type: "boolean",
|
|
1251
|
+
description: "Set when no user is in the loop to answer the prompt's questions (an autonomous run). The reimplementation-spec prompt then defaults to the language-agnostic variant and records selection: auto-default instead of asking which variant to build, and every interactive hook is suppressed. Same as Pi's /codecarto-next --auto.",
|
|
1252
|
+
},
|
|
1253
|
+
},
|
|
1160
1254
|
required: ["cwd"],
|
|
1161
1255
|
},
|
|
1162
1256
|
},
|
|
@@ -1168,6 +1262,7 @@ const TOOLS = [
|
|
|
1168
1262
|
properties: {
|
|
1169
1263
|
cwd: { type: "string", description: "Absolute path to the target repository." },
|
|
1170
1264
|
phase: { type: "string", description: "Phase id from the active pipeline." },
|
|
1265
|
+
unattended: { type: "boolean", description: "As for codecarto_next: no user is in the loop, so interactive hooks default and record their choice instead of asking." },
|
|
1171
1266
|
},
|
|
1172
1267
|
required: ["cwd", "phase"],
|
|
1173
1268
|
},
|
|
@@ -1186,7 +1281,7 @@ const TOOLS = [
|
|
|
1186
1281
|
},
|
|
1187
1282
|
{
|
|
1188
1283
|
name: "codecarto_complete",
|
|
1189
|
-
description: "Mark a phase complete. Requires the phase output's validation to be PASS or PASS WITH GAPS. Atomically updates status.yaml under a file lock, appends
|
|
1284
|
+
description: "Mark a phase complete. Requires the phase output's validation to be PASS or PASS WITH GAPS and applies the phase handoff at scratch/handoffs/<phase>.yaml. Atomically updates status.yaml under a file lock, appends one THREAD_LOG.md entry, and writes the phase's closeout: the handoff's closeout_content (plus its decisions) overwrites the canonical <date>-<phase>.md when supplied; otherwise a stub is copied from the template only if no closeout exists yet. If phase is omitted, completes the next eligible phase.",
|
|
1190
1285
|
inputSchema: {
|
|
1191
1286
|
type: "object",
|
|
1192
1287
|
properties: {
|
|
@@ -1412,11 +1507,15 @@ const TOOLS = [
|
|
|
1412
1507
|
},
|
|
1413
1508
|
api_key: {
|
|
1414
1509
|
type: "string",
|
|
1415
|
-
description: "OpenRouter API key.
|
|
1510
|
+
description: "OpenRouter API key. A value passed here is recorded wherever the host logs tool calls; prefer the OPENROUTER_API_KEY environment variable of the server process. (api_key in .codecarto/broadside/config.yaml also works, but that file is tracked, so a key there is committed with it.)",
|
|
1511
|
+
},
|
|
1512
|
+
run_id: {
|
|
1513
|
+
type: "string",
|
|
1514
|
+
description: "For collect: the run to collect, as listed by the status action. Defaults to the most recent run; pass this to collect an older run that is still in flight after a newer submit.",
|
|
1416
1515
|
},
|
|
1417
1516
|
wait_seconds: {
|
|
1418
1517
|
type: "number",
|
|
1419
|
-
description: "For submit: after submitting, poll up to this many seconds before returning. For collect: poll up to this many seconds before returning with partial state. Falls back to wait_seconds in .codecarto/broadside/config.yaml.",
|
|
1518
|
+
description: "For submit: after submitting, poll up to this many seconds before returning. For collect: poll up to this many seconds before returning with partial state. 0 polls each in-flight batch once and returns without waiting. Falls back to wait_seconds in .codecarto/broadside/config.yaml (default 0).",
|
|
1420
1519
|
},
|
|
1421
1520
|
include_synthesis: {
|
|
1422
1521
|
type: "boolean",
|
|
@@ -1432,7 +1531,7 @@ const TOOLS = [
|
|
|
1432
1531
|
},
|
|
1433
1532
|
max_cost: {
|
|
1434
1533
|
type: "number",
|
|
1435
|
-
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. Falls back to max_cost in .codecarto/broadside/config.yaml.",
|
|
1534
|
+
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. Falls back to max_cost in .codecarto/broadside/config.yaml, whose default is $1.00; pass 0 for no limit.",
|
|
1436
1535
|
},
|
|
1437
1536
|
force: {
|
|
1438
1537
|
type: "boolean",
|
|
@@ -1446,6 +1545,15 @@ const TOOLS = [
|
|
|
1446
1545
|
type: "boolean",
|
|
1447
1546
|
description: "For action 'models': annotate each model with its Artificial Analysis coding index (extra API call; default false).",
|
|
1448
1547
|
},
|
|
1548
|
+
model: {
|
|
1549
|
+
type: "string",
|
|
1550
|
+
description: "For submit: the OpenRouter batch model for this run (an id ending in :batch, as listed by action 'models'). Falls back to model in .codecarto/broadside/config.yaml, then the shipped default. Pre-flighted like the configured model: priced from the live catalog, refused without structured-output support, clamped to its completion ceiling. The models listing is advisory — some catalog ids have no batch endpoint and are refused at submit, at no cost; the listing tags ids this repository has already seen accepted or refused.",
|
|
1551
|
+
},
|
|
1552
|
+
lens_models: {
|
|
1553
|
+
type: "object",
|
|
1554
|
+
additionalProperties: { type: "string" },
|
|
1555
|
+
description: "For submit: per-lens model overrides for this run, e.g. {\"security\": \"deepseek/deepseek-v4-pro-0813:batch\"}. Keys are lens ids; a lens named here runs on that model, others on `model`. Layered over lens_models in .codecarto/broadside/config.yaml (a lens set in both takes the parameter's). Each override is priced, capability-checked, and clamped individually, and the estimate breaks cost out per lens.",
|
|
1556
|
+
},
|
|
1449
1557
|
},
|
|
1450
1558
|
required: ["cwd", "action"],
|
|
1451
1559
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codecartographer-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.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",
|