codecartographer-pi 0.19.6 → 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/workflow/VALIDATE.md +2 -1
- package/.codecarto/workflow/scaffold-version.yaml +1 -1
- package/README.md +10 -6
- package/dist/core/broadside.d.ts +72 -2
- package/dist/core/broadside.js +348 -63
- package/dist/core/completion.js +81 -22
- 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 +4 -1
- 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 +8 -0
- package/dist/core/status.js +8 -3
- package/dist/core/synthesis.js +5 -2
- package/dist/core/workspace.d.ts +55 -8
- package/dist/core/workspace.js +115 -7
- 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 +9 -2
- package/dist/extensions/codecarto/dashboard-writer.d.ts +1 -8
- package/dist/extensions/codecarto/dashboard-writer.js +5 -154
- package/dist/extensions/codecarto/index.js +65 -18
- package/dist/mcp-server/server.js +107 -49
- 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 });
|
|
@@ -169,6 +196,7 @@ export async function handleStatus(args) {
|
|
|
169
196
|
const terminalOpenQuestions = Object.values(state.status.phases).reduce((sum, phase) => sum + (phase.open_questions?.length ?? 0), 0);
|
|
170
197
|
const postPipelinePending = state.status.post_pipeline.filter((entry) => entry.status !== "resolved").length;
|
|
171
198
|
const scaffoldNotice = describeScaffoldStaleness(state);
|
|
199
|
+
const missingOutputs = await listMissingCompletedOutputs(state);
|
|
172
200
|
const summaryLines = [
|
|
173
201
|
`Phase: ${currentPhase}`,
|
|
174
202
|
`Pipeline state: ${currentPhase === "complete" ? "complete" : "in progress"}`,
|
|
@@ -186,9 +214,11 @@ export async function handleStatus(args) {
|
|
|
186
214
|
];
|
|
187
215
|
if (scaffoldNotice)
|
|
188
216
|
summaryLines.push(`Scaffold: ${scaffoldNotice}`);
|
|
217
|
+
summaryLines.push(...describeMissingCompletedOutputs(missingOutputs));
|
|
189
218
|
const summary = summaryLines.join("\n");
|
|
190
219
|
return textResult(summary, {
|
|
191
220
|
...(scaffoldNotice ? { scaffoldNotice } : {}),
|
|
221
|
+
...(missingOutputs.length > 0 ? { missingOutputs } : {}),
|
|
192
222
|
currentPhase,
|
|
193
223
|
pipeline: state.status.pipeline,
|
|
194
224
|
pipelineLabel: getPipelineLabel(state.status.pipeline),
|
|
@@ -212,6 +242,11 @@ export async function handleSwitchPipeline(args) {
|
|
|
212
242
|
return textResult(`Already on pipeline: ${getPipelineLabel(pipelineChoice)}`, { pipeline: getPipelineLabel(pipelineChoice) });
|
|
213
243
|
}
|
|
214
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;
|
|
215
250
|
const lines = [`Switched pipeline: ${getPipelineLabel(pipelineChoice)}`];
|
|
216
251
|
if (result.carried.length > 0)
|
|
217
252
|
lines.push(`Phases preserved (completed): ${result.carried.join(", ")}`);
|
|
@@ -219,11 +254,18 @@ export async function handleSwitchPipeline(args) {
|
|
|
219
254
|
lines.push(`New phases: ${result.newPhases.join(", ")}`);
|
|
220
255
|
if (result.dropped.length > 0)
|
|
221
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}`);
|
|
222
261
|
return textResult(lines.join("\n"), {
|
|
223
262
|
pipeline: getPipelineLabel(pipelineChoice),
|
|
263
|
+
currentPhase: result.state.status.current_phase,
|
|
224
264
|
carried: result.carried,
|
|
225
265
|
newPhases: result.newPhases,
|
|
226
266
|
dropped: result.dropped,
|
|
267
|
+
dangling: result.dangling,
|
|
268
|
+
dashboardPath,
|
|
227
269
|
});
|
|
228
270
|
}
|
|
229
271
|
export async function handleNext(args) {
|
|
@@ -353,17 +395,20 @@ export async function handleSkill(args) {
|
|
|
353
395
|
if (nextPhase) {
|
|
354
396
|
throw new McpError(ErrorCode.InvalidRequest, `Cannot run skill: pipeline is not complete (next phase: ${nextPhase.id}). Finish the pipeline first.`);
|
|
355
397
|
}
|
|
356
|
-
|
|
357
|
-
|
|
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) {
|
|
358
403
|
const available = await listSkillNames(state.workspaceDir);
|
|
359
404
|
const hint = available.length > 0 ? ` Available: ${available.join(", ")}.` : " No skills installed.";
|
|
360
|
-
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.`);
|
|
361
406
|
}
|
|
362
|
-
const prompt = await buildSkillPrompt(state,
|
|
363
|
-
return textResult(prompt, { skill:
|
|
407
|
+
const prompt = await buildSkillPrompt(state, skillName);
|
|
408
|
+
return textResult(prompt, { skill: skillName });
|
|
364
409
|
}
|
|
365
410
|
// ---------- library helpers ----------
|
|
366
|
-
|
|
411
|
+
function resolveLibraryPath(args, config) {
|
|
367
412
|
const explicit = typeof args.library_path === "string" && args.library_path.trim() !== ""
|
|
368
413
|
? args.library_path.trim()
|
|
369
414
|
: null;
|
|
@@ -373,18 +418,8 @@ async function resolveLibraryPath(args) {
|
|
|
373
418
|
}
|
|
374
419
|
return explicit;
|
|
375
420
|
}
|
|
376
|
-
if (
|
|
377
|
-
|
|
378
|
-
if (!isAbsolute(cwd)) {
|
|
379
|
-
throw new McpError(ErrorCode.InvalidParams, `cwd must be absolute, got: ${cwd}`);
|
|
380
|
-
}
|
|
381
|
-
// loadCodecartoConfig merges user-global under per-workspace and tolerates
|
|
382
|
-
// a missing workspace file, so a single call covers both cases.
|
|
383
|
-
const workspaceDir = join(cwd, ".codecarto");
|
|
384
|
-
const config = await loadCodecartoConfig(workspaceDir);
|
|
385
|
-
if (config.library.path)
|
|
386
|
-
return config.library.path;
|
|
387
|
-
}
|
|
421
|
+
if (config.library.path)
|
|
422
|
+
return config.library.path;
|
|
388
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).");
|
|
389
424
|
}
|
|
390
425
|
/**
|
|
@@ -394,17 +429,22 @@ async function resolveLibraryPath(args) {
|
|
|
394
429
|
* the publish tool enforces.
|
|
395
430
|
*/
|
|
396
431
|
async function loadEffectiveConfig(cwd) {
|
|
397
|
-
|
|
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)
|
|
398
435
|
return loadUserConfig();
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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"));
|
|
408
448
|
}
|
|
409
449
|
function asStringArray(value, fieldName) {
|
|
410
450
|
if (!Array.isArray(value)) {
|
|
@@ -491,12 +531,12 @@ async function resolveDefaultsFromWorkspace(cwd, overrides) {
|
|
|
491
531
|
let namespace = typeof overrides.namespace === "string" && overrides.namespace.trim() !== ""
|
|
492
532
|
? overrides.namespace.trim()
|
|
493
533
|
: null;
|
|
494
|
-
if (
|
|
495
|
-
const workspaceDir = join(cwd
|
|
534
|
+
if (cwd) {
|
|
535
|
+
const workspaceDir = join(cwd, ".codecarto");
|
|
496
536
|
if (await pathExists(workspaceDir)) {
|
|
497
537
|
if (pipeline === "unknown") {
|
|
498
538
|
try {
|
|
499
|
-
const state = await getWorkspaceState(cwd
|
|
539
|
+
const state = await getWorkspaceState(cwd);
|
|
500
540
|
if (state?.status.pipeline)
|
|
501
541
|
pipeline = state.status.pipeline;
|
|
502
542
|
}
|
|
@@ -542,16 +582,20 @@ function provenanceConflictLines(conflicts) {
|
|
|
542
582
|
}
|
|
543
583
|
// ---------- library handlers ----------
|
|
544
584
|
export async function handlePublish(args) {
|
|
545
|
-
|
|
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);
|
|
546
591
|
const marker = await discoverLibrary(libraryPath);
|
|
547
592
|
if (!marker) {
|
|
548
593
|
throw new McpError(ErrorCode.InvalidParams, `No CodeCartographer library at ${libraryPath} (missing .codecarto-library marker). Create one before publishing.`);
|
|
549
594
|
}
|
|
550
595
|
// Build allowed roots for spec_path containment: workspace .codecarto/ and library path
|
|
551
596
|
const allowedRoots = [libraryPath];
|
|
552
|
-
if (
|
|
553
|
-
allowedRoots.push(join(
|
|
554
|
-
}
|
|
597
|
+
if (cwd)
|
|
598
|
+
allowedRoots.push(join(cwd, ".codecarto"));
|
|
555
599
|
const spec = await readSpecArg(args, allowedRoots);
|
|
556
600
|
if (typeof args.source_repo !== "string" || args.source_repo.trim() === "") {
|
|
557
601
|
throw new McpError(ErrorCode.InvalidParams, "source_repo is required");
|
|
@@ -567,7 +611,7 @@ export async function handlePublish(args) {
|
|
|
567
611
|
if (!isValidSlug(slug)) {
|
|
568
612
|
throw new McpError(ErrorCode.InvalidParams, `Resolved slug "${slug}" is invalid. Provide an explicit slug (lowercase ASCII, starts with a letter, max 64 chars).`);
|
|
569
613
|
}
|
|
570
|
-
const defaults = await resolveDefaultsFromWorkspace(
|
|
614
|
+
const defaults = await resolveDefaultsFromWorkspace(cwd, {
|
|
571
615
|
pipeline: args.pipeline,
|
|
572
616
|
namespace: args.namespace,
|
|
573
617
|
});
|
|
@@ -596,7 +640,6 @@ export async function handlePublish(args) {
|
|
|
596
640
|
// opt-out, and a host that never configured the key keeps the behavior it
|
|
597
641
|
// had. Runs after every argument check so the preview names the resolved
|
|
598
642
|
// slug and namespace, and before publishEntry so nothing is written.
|
|
599
|
-
const config = await loadEffectiveConfig(args.cwd);
|
|
600
643
|
if (config.library.publish_confirm && config.library.publish_confirm_configured && args.confirm !== true) {
|
|
601
644
|
const preview = await previewPublishVersion(libraryPath, spec, { slug, namespace }, { forceNewVersion });
|
|
602
645
|
const label = `${namespace ? `${namespace}/` : ""}${slug}`;
|
|
@@ -665,7 +708,9 @@ export async function handlePublish(args) {
|
|
|
665
708
|
});
|
|
666
709
|
}
|
|
667
710
|
export async function handleLibraryList(args) {
|
|
668
|
-
const
|
|
711
|
+
const config = await loadEffectiveConfig(await optionalCwd(args.cwd));
|
|
712
|
+
refuseOnConfigProblems(config, "codecarto_library_list");
|
|
713
|
+
const libraryPath = resolveLibraryPath(args, config);
|
|
669
714
|
const marker = await discoverLibrary(libraryPath);
|
|
670
715
|
if (!marker) {
|
|
671
716
|
throw new McpError(ErrorCode.InvalidParams, `No CodeCartographer library at ${libraryPath} (missing .codecarto-library marker).`);
|
|
@@ -706,7 +751,9 @@ export async function handleLibraryList(args) {
|
|
|
706
751
|
});
|
|
707
752
|
}
|
|
708
753
|
export async function handleLibraryReindex(args) {
|
|
709
|
-
const
|
|
754
|
+
const config = await loadEffectiveConfig(await optionalCwd(args.cwd));
|
|
755
|
+
refuseOnConfigProblems(config, "codecarto_library_reindex");
|
|
756
|
+
const libraryPath = resolveLibraryPath(args, config);
|
|
710
757
|
const marker = await discoverLibrary(libraryPath);
|
|
711
758
|
if (!marker) {
|
|
712
759
|
throw new McpError(ErrorCode.InvalidParams, `No CodeCartographer library at ${libraryPath} (missing .codecarto-library marker).`);
|
|
@@ -742,10 +789,17 @@ export async function handleLibraryInit(args) {
|
|
|
742
789
|
});
|
|
743
790
|
// Write config to user-global location
|
|
744
791
|
const configPath = resolveUserConfigPath();
|
|
745
|
-
|
|
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";
|
|
746
800
|
const msg = result.alreadyExisted
|
|
747
|
-
? `Library already exists at ${libraryPath} (marker preserved).
|
|
748
|
-
: `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.`;
|
|
749
803
|
return textResult(msg, {
|
|
750
804
|
libraryPath,
|
|
751
805
|
markerName: result.marker.name,
|
|
@@ -808,6 +862,9 @@ export async function handleConfig(args) {
|
|
|
808
862
|
` Library marker: ${markerStatus}`,
|
|
809
863
|
` User-global config: ${userConfigPath}`,
|
|
810
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),
|
|
811
868
|
].join("\n"), {
|
|
812
869
|
libraryPath: config.library.path,
|
|
813
870
|
libraryNamespace: config.library.namespace,
|
|
@@ -815,6 +872,7 @@ export async function handleConfig(args) {
|
|
|
815
872
|
llmSteerNextPhase: config.orchestrator.llm_steer_next_phase,
|
|
816
873
|
userConfigPath,
|
|
817
874
|
workspaceConfigPath,
|
|
875
|
+
problems: config.problems,
|
|
818
876
|
});
|
|
819
877
|
}
|
|
820
878
|
// ---------- MCP parity handlers: open, usage, dashboard, list_skills ----------
|
|
@@ -1116,7 +1174,7 @@ const TOOLS = [
|
|
|
1116
1174
|
},
|
|
1117
1175
|
{
|
|
1118
1176
|
name: "codecarto_validate",
|
|
1119
|
-
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.",
|
|
1120
1178
|
inputSchema: {
|
|
1121
1179
|
type: "object",
|
|
1122
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
|
}
|