codecartographer-pi 0.19.5 → 0.20.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 +14 -0
- package/.codecarto/broadside/config.yaml +18 -0
- package/.codecarto/templates/gitignore +55 -0
- package/.codecarto/workflow/VALIDATE.md +2 -1
- package/.codecarto/workflow/scaffold-version.yaml +1 -1
- package/README.md +10 -6
- package/dist/core/amendment.js +28 -23
- package/dist/core/broadside.d.ts +72 -2
- package/dist/core/broadside.js +351 -68
- package/dist/core/completion.js +95 -26
- package/dist/core/dashboard-writer.d.ts +8 -0
- package/dist/core/dashboard-writer.js +159 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +2 -0
- package/dist/core/library.js +115 -107
- package/dist/core/orchestrator-config.d.ts +32 -7
- package/dist/core/orchestrator-config.js +124 -44
- package/dist/core/pipeline.d.ts +37 -0
- package/dist/core/pipeline.js +80 -10
- package/dist/core/prompts.d.ts +20 -0
- package/dist/core/prompts.js +43 -10
- package/dist/core/secrets.d.ts +16 -0
- package/dist/core/secrets.js +98 -0
- package/dist/core/status.d.ts +30 -2
- package/dist/core/status.js +54 -8
- package/dist/core/synthesis.js +5 -2
- 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 +99 -18
- package/dist/core/workspace.js +275 -36
- package/dist/core/yaml.js +173 -14
- package/dist/extensions/codecarto/agent-rewriter.js +21 -14
- package/dist/extensions/codecarto/agent-runner.d.ts +6 -2
- package/dist/extensions/codecarto/agent-runner.js +27 -9
- package/dist/extensions/codecarto/agent-state.d.ts +0 -2
- package/dist/extensions/codecarto/auto-runner.js +10 -6
- package/dist/extensions/codecarto/dashboard-narrator.js +12 -8
- package/dist/extensions/codecarto/dashboard-writer.d.ts +1 -8
- package/dist/extensions/codecarto/dashboard-writer.js +5 -157
- package/dist/extensions/codecarto/index.js +73 -21
- package/dist/extensions/codecarto/phase-compaction.js +7 -7
- package/dist/mcp-server/server.js +111 -50
- package/package.json +3 -2
|
@@ -17,12 +17,11 @@ 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, canonicalPath, copyPackagedWorkspace, collectResultText, completeValidatedPhase, computePerPhaseTotals, computeTotals, createEmptyStatus, DEFAULT_PIPELINE_PATH, deriveSlug, discoverLibrary, describeScaffoldStaleness, detectProvenanceConflicts, estimateSubmitText, getLens, getNextEligiblePhase, getPipelineLabel, getWorkspaceState, isValidSlug, isWithinPathResolved, listBatchModels, listEntries, listGuideTopics, loadBroadsideConfig, modelsText, readGuide, listSkillNames, 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, } from "../core/index.js";
|
|
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, getNextEligiblePhase, getPipelineLabel, getWorkspaceState, isValidSlug, isWithinPathResolved, listBatchModels, listEntries, listGuideTopics, loadBroadsideConfig, modelsText, readGuide, listMissingCompletedOutputs, listSkillNames, 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";
|
|
24
|
-
import { loadUserConfig, resolveUserConfigPath } from "../core/orchestrator-config.js";
|
|
25
|
-
import { writeDashboard } from "../extensions/codecarto/dashboard-writer.js";
|
|
24
|
+
import { describeConfigProblems, loadUserConfig, resolveUserConfigPath } from "../core/orchestrator-config.js";
|
|
26
25
|
// ---------- input helpers ----------
|
|
27
26
|
/**
|
|
28
27
|
* Normalize an optional `phase` argument. A client can send any JSON, and
|
|
@@ -50,6 +49,25 @@ async function validateCwd(cwd) {
|
|
|
50
49
|
}
|
|
51
50
|
return cwd;
|
|
52
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* The library tools take `cwd` optionally: absent means "no workspace, use
|
|
54
|
+
* the user-global config alone". When it is given it becomes a containment
|
|
55
|
+
* root for spec_path and the source of the workspace config that gates the
|
|
56
|
+
* write, so it is validated exactly as a required cwd is — absolute and
|
|
57
|
+
* existing — before anything reads through it (#241). A relative path would
|
|
58
|
+
* resolve against the MCP server process's working directory, not the
|
|
59
|
+
* caller's, and widen the root to whatever `.codecarto/` sits there.
|
|
60
|
+
*/
|
|
61
|
+
async function optionalCwd(cwd) {
|
|
62
|
+
if (cwd === undefined || cwd === null)
|
|
63
|
+
return null;
|
|
64
|
+
if (typeof cwd !== "string") {
|
|
65
|
+
throw new McpError(ErrorCode.InvalidParams, "cwd must be a string when given");
|
|
66
|
+
}
|
|
67
|
+
if (cwd.trim() === "")
|
|
68
|
+
return null;
|
|
69
|
+
return validateCwd(cwd.trim());
|
|
70
|
+
}
|
|
53
71
|
async function requireWorkspace(cwd) {
|
|
54
72
|
const state = await getWorkspaceState(cwd).catch((error) => {
|
|
55
73
|
throw new McpError(ErrorCode.InternalError, error instanceof Error ? error.message : String(error));
|
|
@@ -109,12 +127,21 @@ export async function handleInit(args) {
|
|
|
109
127
|
const entries = (await readdir(targetWorkspaceDir)).filter((entry) => entry !== BROADSIDE_DIR);
|
|
110
128
|
broadsideOnly = entries.length === 0 && (await pathExists(join(targetWorkspaceDir, BROADSIDE_DIR)));
|
|
111
129
|
}
|
|
112
|
-
if (targetExists && !
|
|
130
|
+
if (targetExists && !broadsideOnly) {
|
|
113
131
|
if (!args.force) {
|
|
114
|
-
throw new McpError(ErrorCode.InvalidRequest,
|
|
132
|
+
throw new McpError(ErrorCode.InvalidRequest, sameWorkspace
|
|
133
|
+
? `The .codecarto/ at ${targetWorkspaceDir} is CodeCartographer's own packaged template (a checkout install), and it holds workspace state. Pass force: true to move that state — status, findings, handoffs, usage data, closeouts, dashboard — to a .codecarto-backup-TIMESTAMP/ directory and reinitialize; the framework files stay in place. Consider codecarto_open to reattach without resetting.`
|
|
134
|
+
: `A .codecarto/ directory already exists at ${targetWorkspaceDir}. Pass force: true to back it up and reinitialize. Warning: this moves all existing findings, handoffs, usage data, closeouts, and phase progress to a .codecarto-backup-TIMESTAMP/ directory.`);
|
|
115
135
|
}
|
|
116
136
|
const backupDir = join(cwd, `.codecarto-backup-${new Date().toISOString().replace(/[:.]/g, "-")}`);
|
|
117
|
-
|
|
137
|
+
if (sameWorkspace) {
|
|
138
|
+
// The template cannot be renamed away — it is what init copies from —
|
|
139
|
+
// so its session state is moved out file by file instead (#245).
|
|
140
|
+
await backupWorkspaceState(targetWorkspaceDir, backupDir);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
await rename(targetWorkspaceDir, backupDir);
|
|
144
|
+
}
|
|
118
145
|
}
|
|
119
146
|
if (!(await pathExists(targetWorkspaceDir))) {
|
|
120
147
|
await mkdir(cwd, { recursive: true });
|
|
@@ -125,8 +152,11 @@ export async function handleInit(args) {
|
|
|
125
152
|
// broadside state and results already on disk.
|
|
126
153
|
await copyPackagedWorkspace(targetWorkspaceDir);
|
|
127
154
|
}
|
|
155
|
+
// The template no longer carries a status.yaml (it is this repository's
|
|
156
|
+
// own project state); one is only present when init is re-run over an
|
|
157
|
+
// existing workspace, where its pipeline stays the default.
|
|
128
158
|
const statusPath = join(targetWorkspaceDir, "workflow", "status.yaml");
|
|
129
|
-
const rawStatus = (await loadYamlFile(statusPath)) ?? {};
|
|
159
|
+
const rawStatus = (await pathExists(statusPath)) ? ((await loadYamlFile(statusPath)) ?? {}) : {};
|
|
130
160
|
const selectedPipelinePath = pipelineChoice ?? rawStatus.pipeline?.trim() ?? DEFAULT_PIPELINE_PATH;
|
|
131
161
|
const resolvedPipelinePath = join(targetWorkspaceDir, selectedPipelinePath);
|
|
132
162
|
if (!(await pathExists(resolvedPipelinePath))) {
|
|
@@ -166,6 +196,7 @@ export async function handleStatus(args) {
|
|
|
166
196
|
const terminalOpenQuestions = Object.values(state.status.phases).reduce((sum, phase) => sum + (phase.open_questions?.length ?? 0), 0);
|
|
167
197
|
const postPipelinePending = state.status.post_pipeline.filter((entry) => entry.status !== "resolved").length;
|
|
168
198
|
const scaffoldNotice = describeScaffoldStaleness(state);
|
|
199
|
+
const missingOutputs = await listMissingCompletedOutputs(state);
|
|
169
200
|
const summaryLines = [
|
|
170
201
|
`Phase: ${currentPhase}`,
|
|
171
202
|
`Pipeline state: ${currentPhase === "complete" ? "complete" : "in progress"}`,
|
|
@@ -183,9 +214,11 @@ export async function handleStatus(args) {
|
|
|
183
214
|
];
|
|
184
215
|
if (scaffoldNotice)
|
|
185
216
|
summaryLines.push(`Scaffold: ${scaffoldNotice}`);
|
|
217
|
+
summaryLines.push(...describeMissingCompletedOutputs(missingOutputs));
|
|
186
218
|
const summary = summaryLines.join("\n");
|
|
187
219
|
return textResult(summary, {
|
|
188
220
|
...(scaffoldNotice ? { scaffoldNotice } : {}),
|
|
221
|
+
...(missingOutputs.length > 0 ? { missingOutputs } : {}),
|
|
189
222
|
currentPhase,
|
|
190
223
|
pipeline: state.status.pipeline,
|
|
191
224
|
pipelineLabel: getPipelineLabel(state.status.pipeline),
|
|
@@ -209,6 +242,11 @@ export async function handleSwitchPipeline(args) {
|
|
|
209
242
|
return textResult(`Already on pipeline: ${getPipelineLabel(pipelineChoice)}`, { pipeline: getPipelineLabel(pipelineChoice) });
|
|
210
243
|
}
|
|
211
244
|
const result = await switchPipeline(cwd, pipelineChoice);
|
|
245
|
+
// The dashboard is rendered from the pipeline the switch just replaced;
|
|
246
|
+
// Pi re-rendered here and MCP did not, so the file showed the old phase
|
|
247
|
+
// list until the next completion (#254). Same best-effort call as
|
|
248
|
+
// completion and amendment make.
|
|
249
|
+
const dashboardPath = (await writeDashboard(cwd, PACKAGE_VERSION)) ? ".codecarto/dashboard.html" : undefined;
|
|
212
250
|
const lines = [`Switched pipeline: ${getPipelineLabel(pipelineChoice)}`];
|
|
213
251
|
if (result.carried.length > 0)
|
|
214
252
|
lines.push(`Phases preserved (completed): ${result.carried.join(", ")}`);
|
|
@@ -216,11 +254,18 @@ export async function handleSwitchPipeline(args) {
|
|
|
216
254
|
lines.push(`New phases: ${result.newPhases.join(", ")}`);
|
|
217
255
|
if (result.dropped.length > 0)
|
|
218
256
|
lines.push(`Phases not in new pipeline: ${result.dropped.join(", ")} (findings remain on disk)`);
|
|
257
|
+
lines.push(...describeDanglingCarryForward(result.dangling));
|
|
258
|
+
lines.push(`Current phase: ${result.state.status.current_phase}`);
|
|
259
|
+
if (dashboardPath)
|
|
260
|
+
lines.push(`Dashboard refreshed: ${dashboardPath}`);
|
|
219
261
|
return textResult(lines.join("\n"), {
|
|
220
262
|
pipeline: getPipelineLabel(pipelineChoice),
|
|
263
|
+
currentPhase: result.state.status.current_phase,
|
|
221
264
|
carried: result.carried,
|
|
222
265
|
newPhases: result.newPhases,
|
|
223
266
|
dropped: result.dropped,
|
|
267
|
+
dangling: result.dangling,
|
|
268
|
+
dashboardPath,
|
|
224
269
|
});
|
|
225
270
|
}
|
|
226
271
|
export async function handleNext(args) {
|
|
@@ -350,17 +395,20 @@ export async function handleSkill(args) {
|
|
|
350
395
|
if (nextPhase) {
|
|
351
396
|
throw new McpError(ErrorCode.InvalidRequest, `Cannot run skill: pipeline is not complete (next phase: ${nextPhase.id}). Finish the pipeline first.`);
|
|
352
397
|
}
|
|
353
|
-
|
|
354
|
-
|
|
398
|
+
// Resolve against the installed list only: the name is never joined onto a
|
|
399
|
+
// path, so a traversal like `../findings/architecture` cannot splice a
|
|
400
|
+
// phase SKILL.md (or anything else) into the post-pipeline prompt.
|
|
401
|
+
const skillName = await resolveSkillName(state.workspaceDir, args.name);
|
|
402
|
+
if (!skillName) {
|
|
355
403
|
const available = await listSkillNames(state.workspaceDir);
|
|
356
404
|
const hint = available.length > 0 ? ` Available: ${available.join(", ")}.` : " No skills installed.";
|
|
357
|
-
throw new McpError(ErrorCode.InvalidParams, `Unknown skill: ${args.name}.${hint} The Broad-Side reading guide is served as \`${BROADSIDE_SKILL_NAME}\` and is not pipeline-gated.`);
|
|
405
|
+
throw new McpError(ErrorCode.InvalidParams, `Unknown skill: ${args.name.trim()}.${hint} The Broad-Side reading guide is served as \`${BROADSIDE_SKILL_NAME}\` and is not pipeline-gated.`);
|
|
358
406
|
}
|
|
359
|
-
const prompt = await buildSkillPrompt(state,
|
|
360
|
-
return textResult(prompt, { skill:
|
|
407
|
+
const prompt = await buildSkillPrompt(state, skillName);
|
|
408
|
+
return textResult(prompt, { skill: skillName });
|
|
361
409
|
}
|
|
362
410
|
// ---------- library helpers ----------
|
|
363
|
-
|
|
411
|
+
function resolveLibraryPath(args, config) {
|
|
364
412
|
const explicit = typeof args.library_path === "string" && args.library_path.trim() !== ""
|
|
365
413
|
? args.library_path.trim()
|
|
366
414
|
: null;
|
|
@@ -370,18 +418,8 @@ async function resolveLibraryPath(args) {
|
|
|
370
418
|
}
|
|
371
419
|
return explicit;
|
|
372
420
|
}
|
|
373
|
-
if (
|
|
374
|
-
|
|
375
|
-
if (!isAbsolute(cwd)) {
|
|
376
|
-
throw new McpError(ErrorCode.InvalidParams, `cwd must be absolute, got: ${cwd}`);
|
|
377
|
-
}
|
|
378
|
-
// loadCodecartoConfig merges user-global under per-workspace and tolerates
|
|
379
|
-
// a missing workspace file, so a single call covers both cases.
|
|
380
|
-
const workspaceDir = join(cwd, ".codecarto");
|
|
381
|
-
const config = await loadCodecartoConfig(workspaceDir);
|
|
382
|
-
if (config.library.path)
|
|
383
|
-
return config.library.path;
|
|
384
|
-
}
|
|
421
|
+
if (config.library.path)
|
|
422
|
+
return config.library.path;
|
|
385
423
|
throw new McpError(ErrorCode.InvalidParams, "library_path is required (pass it explicitly, or pass cwd and configure library.path in ~/.codecarto/config.yaml or .codecarto/workflow/config.yaml).");
|
|
386
424
|
}
|
|
387
425
|
/**
|
|
@@ -391,17 +429,22 @@ async function resolveLibraryPath(args) {
|
|
|
391
429
|
* the publish tool enforces.
|
|
392
430
|
*/
|
|
393
431
|
async function loadEffectiveConfig(cwd) {
|
|
394
|
-
|
|
432
|
+
// This config decides whether publish_confirm gates the write, so the cwd
|
|
433
|
+
// it is read through is the one optionalCwd validated, never a raw argument.
|
|
434
|
+
if (!cwd)
|
|
395
435
|
return loadUserConfig();
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
436
|
+
return loadCodecartoConfig(join(cwd, ".codecarto"));
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* The library tools answer from the config — its path, its namespace, its
|
|
440
|
+
* confirm gate — so a config file that could not be used in full is a
|
|
441
|
+
* refusal, not a silent fallback to whatever the other layer says (#242).
|
|
442
|
+
* The message names each file and key; codecarto_config shows the same list.
|
|
443
|
+
*/
|
|
444
|
+
function refuseOnConfigProblems(config, tool) {
|
|
445
|
+
if (config.problems.length === 0)
|
|
446
|
+
return;
|
|
447
|
+
throw new McpError(ErrorCode.InvalidRequest, [`${tool} refused: the configuration has problems. Fix or remove the offending file, then retry.`, ...describeConfigProblems(config)].join("\n"));
|
|
405
448
|
}
|
|
406
449
|
function asStringArray(value, fieldName) {
|
|
407
450
|
if (!Array.isArray(value)) {
|
|
@@ -488,12 +531,12 @@ async function resolveDefaultsFromWorkspace(cwd, overrides) {
|
|
|
488
531
|
let namespace = typeof overrides.namespace === "string" && overrides.namespace.trim() !== ""
|
|
489
532
|
? overrides.namespace.trim()
|
|
490
533
|
: null;
|
|
491
|
-
if (
|
|
492
|
-
const workspaceDir = join(cwd
|
|
534
|
+
if (cwd) {
|
|
535
|
+
const workspaceDir = join(cwd, ".codecarto");
|
|
493
536
|
if (await pathExists(workspaceDir)) {
|
|
494
537
|
if (pipeline === "unknown") {
|
|
495
538
|
try {
|
|
496
|
-
const state = await getWorkspaceState(cwd
|
|
539
|
+
const state = await getWorkspaceState(cwd);
|
|
497
540
|
if (state?.status.pipeline)
|
|
498
541
|
pipeline = state.status.pipeline;
|
|
499
542
|
}
|
|
@@ -539,16 +582,20 @@ function provenanceConflictLines(conflicts) {
|
|
|
539
582
|
}
|
|
540
583
|
// ---------- library handlers ----------
|
|
541
584
|
export async function handlePublish(args) {
|
|
542
|
-
|
|
585
|
+
// First, before anything is read through it: cwd is a containment root
|
|
586
|
+
// for spec_path below and the source of the config that gates the write.
|
|
587
|
+
const cwd = await optionalCwd(args.cwd);
|
|
588
|
+
const config = await loadEffectiveConfig(cwd);
|
|
589
|
+
refuseOnConfigProblems(config, "codecarto_publish");
|
|
590
|
+
const libraryPath = resolveLibraryPath(args, config);
|
|
543
591
|
const marker = await discoverLibrary(libraryPath);
|
|
544
592
|
if (!marker) {
|
|
545
593
|
throw new McpError(ErrorCode.InvalidParams, `No CodeCartographer library at ${libraryPath} (missing .codecarto-library marker). Create one before publishing.`);
|
|
546
594
|
}
|
|
547
595
|
// Build allowed roots for spec_path containment: workspace .codecarto/ and library path
|
|
548
596
|
const allowedRoots = [libraryPath];
|
|
549
|
-
if (
|
|
550
|
-
allowedRoots.push(join(
|
|
551
|
-
}
|
|
597
|
+
if (cwd)
|
|
598
|
+
allowedRoots.push(join(cwd, ".codecarto"));
|
|
552
599
|
const spec = await readSpecArg(args, allowedRoots);
|
|
553
600
|
if (typeof args.source_repo !== "string" || args.source_repo.trim() === "") {
|
|
554
601
|
throw new McpError(ErrorCode.InvalidParams, "source_repo is required");
|
|
@@ -564,7 +611,7 @@ export async function handlePublish(args) {
|
|
|
564
611
|
if (!isValidSlug(slug)) {
|
|
565
612
|
throw new McpError(ErrorCode.InvalidParams, `Resolved slug "${slug}" is invalid. Provide an explicit slug (lowercase ASCII, starts with a letter, max 64 chars).`);
|
|
566
613
|
}
|
|
567
|
-
const defaults = await resolveDefaultsFromWorkspace(
|
|
614
|
+
const defaults = await resolveDefaultsFromWorkspace(cwd, {
|
|
568
615
|
pipeline: args.pipeline,
|
|
569
616
|
namespace: args.namespace,
|
|
570
617
|
});
|
|
@@ -593,7 +640,6 @@ export async function handlePublish(args) {
|
|
|
593
640
|
// opt-out, and a host that never configured the key keeps the behavior it
|
|
594
641
|
// had. Runs after every argument check so the preview names the resolved
|
|
595
642
|
// slug and namespace, and before publishEntry so nothing is written.
|
|
596
|
-
const config = await loadEffectiveConfig(args.cwd);
|
|
597
643
|
if (config.library.publish_confirm && config.library.publish_confirm_configured && args.confirm !== true) {
|
|
598
644
|
const preview = await previewPublishVersion(libraryPath, spec, { slug, namespace }, { forceNewVersion });
|
|
599
645
|
const label = `${namespace ? `${namespace}/` : ""}${slug}`;
|
|
@@ -662,7 +708,9 @@ export async function handlePublish(args) {
|
|
|
662
708
|
});
|
|
663
709
|
}
|
|
664
710
|
export async function handleLibraryList(args) {
|
|
665
|
-
const
|
|
711
|
+
const config = await loadEffectiveConfig(await optionalCwd(args.cwd));
|
|
712
|
+
refuseOnConfigProblems(config, "codecarto_library_list");
|
|
713
|
+
const libraryPath = resolveLibraryPath(args, config);
|
|
666
714
|
const marker = await discoverLibrary(libraryPath);
|
|
667
715
|
if (!marker) {
|
|
668
716
|
throw new McpError(ErrorCode.InvalidParams, `No CodeCartographer library at ${libraryPath} (missing .codecarto-library marker).`);
|
|
@@ -703,7 +751,9 @@ export async function handleLibraryList(args) {
|
|
|
703
751
|
});
|
|
704
752
|
}
|
|
705
753
|
export async function handleLibraryReindex(args) {
|
|
706
|
-
const
|
|
754
|
+
const config = await loadEffectiveConfig(await optionalCwd(args.cwd));
|
|
755
|
+
refuseOnConfigProblems(config, "codecarto_library_reindex");
|
|
756
|
+
const libraryPath = resolveLibraryPath(args, config);
|
|
707
757
|
const marker = await discoverLibrary(libraryPath);
|
|
708
758
|
if (!marker) {
|
|
709
759
|
throw new McpError(ErrorCode.InvalidParams, `No CodeCartographer library at ${libraryPath} (missing .codecarto-library marker).`);
|
|
@@ -739,10 +789,17 @@ export async function handleLibraryInit(args) {
|
|
|
739
789
|
});
|
|
740
790
|
// Write config to user-global location
|
|
741
791
|
const configPath = resolveUserConfigPath();
|
|
742
|
-
|
|
792
|
+
// Writes library.path (and library.namespace when given) and nothing else:
|
|
793
|
+
// in particular it does not set publish_confirm, so initializing a library
|
|
794
|
+
// does not switch the codecarto_publish confirm gate on (#244). A config
|
|
795
|
+
// file that cannot be parsed is left alone and reported.
|
|
796
|
+
await writeLibraryConfig(configPath, libraryPath, args.namespace ?? null).catch((error) => {
|
|
797
|
+
throw new McpError(ErrorCode.InvalidRequest, error instanceof Error ? error.message : String(error));
|
|
798
|
+
});
|
|
799
|
+
const written = args.namespace ? "library.path and library.namespace" : "library.path";
|
|
743
800
|
const msg = result.alreadyExisted
|
|
744
|
-
? `Library already exists at ${libraryPath} (marker preserved).
|
|
745
|
-
: `Created library at ${libraryPath} with marker "${result.marker.name}".
|
|
801
|
+
? `Library already exists at ${libraryPath} (marker preserved). Wrote ${written} to ${configPath}; other keys untouched.`
|
|
802
|
+
: `Created library at ${libraryPath} with marker "${result.marker.name}". Wrote ${written} to ${configPath}; other keys untouched.`;
|
|
746
803
|
return textResult(msg, {
|
|
747
804
|
libraryPath,
|
|
748
805
|
markerName: result.marker.name,
|
|
@@ -805,6 +862,9 @@ export async function handleConfig(args) {
|
|
|
805
862
|
` Library marker: ${markerStatus}`,
|
|
806
863
|
` User-global config: ${userConfigPath}`,
|
|
807
864
|
` Workspace config: ${workspaceConfigPath ?? "(no cwd provided)"}`,
|
|
865
|
+
// A file that could not be used is the one thing this tool exists
|
|
866
|
+
// to surface; the library tools refuse while any is listed (#242).
|
|
867
|
+
...describeConfigProblems(config),
|
|
808
868
|
].join("\n"), {
|
|
809
869
|
libraryPath: config.library.path,
|
|
810
870
|
libraryNamespace: config.library.namespace,
|
|
@@ -812,6 +872,7 @@ export async function handleConfig(args) {
|
|
|
812
872
|
llmSteerNextPhase: config.orchestrator.llm_steer_next_phase,
|
|
813
873
|
userConfigPath,
|
|
814
874
|
workspaceConfigPath,
|
|
875
|
+
problems: config.problems,
|
|
815
876
|
});
|
|
816
877
|
}
|
|
817
878
|
// ---------- MCP parity handlers: open, usage, dashboard, list_skills ----------
|
|
@@ -1113,7 +1174,7 @@ const TOOLS = [
|
|
|
1113
1174
|
},
|
|
1114
1175
|
{
|
|
1115
1176
|
name: "codecarto_validate",
|
|
1116
|
-
description: "
|
|
1177
|
+
description: "Read the ## Validation table a phase wrote at the end of its primary output — the phase's own PASS/PARTIAL/FAIL per completion criterion — apply two cross-checks (findings evidence/action pairing; declared secondary outputs present), and return PASS/PASS WITH GAPS/FAIL/MISSING plus the parsed rows. It does not judge the criteria itself. If phase is omitted, validates the next eligible phase.",
|
|
1117
1178
|
inputSchema: {
|
|
1118
1179
|
type: "object",
|
|
1119
1180
|
properties: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codecartographer-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.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",
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"typescript": "^5.9.3"
|
|
77
77
|
},
|
|
78
78
|
"overrides": {
|
|
79
|
-
"undici": "^8.10.0"
|
|
79
|
+
"undici": "^8.10.0",
|
|
80
|
+
"hono": "^4.13.5"
|
|
80
81
|
}
|
|
81
82
|
}
|