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
|
@@ -1,154 +1,5 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
|
|
6
|
-
import { join } from "node:path";
|
|
7
|
-
import { atomicWriteFile, DASHBOARD_RELATIVE_PATH, getWorkspaceState, loadUsage, NARRATION_CACHE_RELATIVE_PATH, parseSimpleYaml, pathExists, renderDashboard, } from "../../core/index.js";
|
|
8
|
-
const CLOSEOUT_FILENAME_RE = /^(\d{4}-\d{2}-\d{2})-(.+)\.md$/;
|
|
9
|
-
/**
|
|
10
|
-
* Render and atomically replace `.codecarto/dashboard.html`.
|
|
11
|
-
* @returns true when a fresh dashboard landed on disk; false when the
|
|
12
|
-
* workspace is missing or any gather/render/write step failed (swallowed —
|
|
13
|
-
* lifecycle callers must never fail on a dashboard problem, but they may
|
|
14
|
-
* report truthfully whether a refresh happened).
|
|
15
|
-
*/
|
|
16
|
-
export async function writeDashboard(cwd, packageVersion) {
|
|
17
|
-
try {
|
|
18
|
-
const state = await getWorkspaceState(cwd);
|
|
19
|
-
if (!state)
|
|
20
|
-
return false;
|
|
21
|
-
const workspaceDir = state.workspaceDir;
|
|
22
|
-
const [usage, closeouts, outputsPresent, narration] = await Promise.all([
|
|
23
|
-
loadUsage(workspaceDir),
|
|
24
|
-
listCloseouts(workspaceDir),
|
|
25
|
-
buildOutputsPresent(state.workspaceDir, state.pipeline),
|
|
26
|
-
loadNarration(workspaceDir),
|
|
27
|
-
]);
|
|
28
|
-
const inputs = {
|
|
29
|
-
status: state.status,
|
|
30
|
-
pipeline: state.pipeline,
|
|
31
|
-
usage,
|
|
32
|
-
closeouts,
|
|
33
|
-
outputsPresent,
|
|
34
|
-
packageVersion,
|
|
35
|
-
generatedAt: new Date().toISOString(),
|
|
36
|
-
narration,
|
|
37
|
-
};
|
|
38
|
-
const html = renderDashboard(inputs);
|
|
39
|
-
await atomicWriteFile(join(workspaceDir, DASHBOARD_RELATIVE_PATH), html);
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
// Best-effort: a failed dashboard write must not surface as a phase
|
|
44
|
-
// error. The user's pipeline state is unaffected; the next state
|
|
45
|
-
// change will trigger another render attempt.
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
async function listCloseouts(workspaceDir) {
|
|
50
|
-
const dir = join(workspaceDir, "closeouts");
|
|
51
|
-
if (!(await pathExists(dir)))
|
|
52
|
-
return [];
|
|
53
|
-
let entries;
|
|
54
|
-
try {
|
|
55
|
-
entries = await readdir(dir);
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
return [];
|
|
59
|
-
}
|
|
60
|
-
const out = [];
|
|
61
|
-
for (const name of entries) {
|
|
62
|
-
const m = CLOSEOUT_FILENAME_RE.exec(name);
|
|
63
|
-
if (!m)
|
|
64
|
-
continue;
|
|
65
|
-
out.push({ date: m[1], phaseOrModule: m[2], fileName: name, summary: await readCloseoutSummary(join(dir, name)) });
|
|
66
|
-
}
|
|
67
|
-
return out;
|
|
68
|
-
}
|
|
69
|
-
async function readCloseoutSummary(path) {
|
|
70
|
-
try {
|
|
71
|
-
const raw = await readFile(path, "utf8");
|
|
72
|
-
const lines = raw.split(/\r?\n/);
|
|
73
|
-
const summaryStart = lines.findIndex((line) => /^##\s+Summary\s*$/i.test(line.trim()));
|
|
74
|
-
if (summaryStart === -1)
|
|
75
|
-
return undefined;
|
|
76
|
-
const body = [];
|
|
77
|
-
for (const line of lines.slice(summaryStart + 1)) {
|
|
78
|
-
if (/^##\s+/.test(line.trim()))
|
|
79
|
-
break;
|
|
80
|
-
const trimmed = line.trim();
|
|
81
|
-
if (!trimmed || trimmed === "-")
|
|
82
|
-
continue;
|
|
83
|
-
body.push(trimmed.replace(/^[-*]\s+/, ""));
|
|
84
|
-
if (body.join(" ").length > 280)
|
|
85
|
-
break;
|
|
86
|
-
}
|
|
87
|
-
const summary = body.join(" ").trim();
|
|
88
|
-
return summary ? `${summary.slice(0, 280)}${summary.length > 280 ? "…" : ""}` : undefined;
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
return undefined;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
async function buildOutputsPresent(workspaceDir, pipeline) {
|
|
95
|
-
const out = new Map();
|
|
96
|
-
for (const phaseId of pipeline.phase_order) {
|
|
97
|
-
const phaseDef = pipeline.phases.find((p) => p.id === phaseId);
|
|
98
|
-
if (!phaseDef)
|
|
99
|
-
continue;
|
|
100
|
-
const entry = { secondary: [] };
|
|
101
|
-
if (phaseDef.primary_output) {
|
|
102
|
-
entry.primary = {
|
|
103
|
-
path: phaseDef.primary_output,
|
|
104
|
-
exists: await pathExists(join(workspaceDir, phaseDef.primary_output)),
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
for (const sec of phaseDef.secondary_outputs ?? []) {
|
|
108
|
-
entry.secondary.push({
|
|
109
|
-
path: sec.path,
|
|
110
|
-
exists: await pathExists(join(workspaceDir, sec.path)),
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
out.set(phaseId, entry);
|
|
114
|
-
}
|
|
115
|
-
return out;
|
|
116
|
-
}
|
|
117
|
-
async function loadNarration(workspaceDir) {
|
|
118
|
-
const path = join(workspaceDir, NARRATION_CACHE_RELATIVE_PATH);
|
|
119
|
-
if (!(await pathExists(path)))
|
|
120
|
-
return undefined;
|
|
121
|
-
try {
|
|
122
|
-
const raw = await readFile(path, "utf8");
|
|
123
|
-
const { frontmatter, body } = splitFrontmatter(raw);
|
|
124
|
-
if (!frontmatter)
|
|
125
|
-
return undefined;
|
|
126
|
-
const generatedAt = typeof frontmatter.generatedAt === "string" ? frontmatter.generatedAt : "";
|
|
127
|
-
const phaseCountAtGeneration = typeof frontmatter.phaseCountAtGeneration === "number" ? frontmatter.phaseCountAtGeneration : 0;
|
|
128
|
-
if (!generatedAt)
|
|
129
|
-
return undefined;
|
|
130
|
-
return { content: body.trim(), generatedAt, phaseCountAtGeneration };
|
|
131
|
-
}
|
|
132
|
-
catch {
|
|
133
|
-
return undefined;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
function splitFrontmatter(raw) {
|
|
137
|
-
if (!raw.startsWith("---\n"))
|
|
138
|
-
return { frontmatter: null, body: raw };
|
|
139
|
-
const end = raw.indexOf("\n---\n", 4);
|
|
140
|
-
if (end === -1)
|
|
141
|
-
return { frontmatter: null, body: raw };
|
|
142
|
-
const yamlText = raw.slice(4, end);
|
|
143
|
-
const body = raw.slice(end + 5);
|
|
144
|
-
try {
|
|
145
|
-
const parsed = parseSimpleYaml(yamlText);
|
|
146
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
147
|
-
return { frontmatter: parsed, body };
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
// fall through
|
|
152
|
-
}
|
|
153
|
-
return { frontmatter: null, body };
|
|
154
|
-
}
|
|
1
|
+
// Moved to core/dashboard-writer.ts (#254): both surfaces render the dashboard
|
|
2
|
+
// at the same lifecycle points, so it is a shared primitive. This re-export
|
|
3
|
+
// keeps the old import path — scripts/build-demo-dashboard.mjs among them —
|
|
4
|
+
// working.
|
|
5
|
+
export { writeDashboard } from "../../core/dashboard-writer.js";
|
|
@@ -5,13 +5,12 @@ import { autoCompletePhase, buildAutoSummary, isPhaseRunning, runAuto, runSingle
|
|
|
5
5
|
import { disposeAgentsWidget } from "./agent-widget.js";
|
|
6
6
|
import { parseDashboardFlags } from "./dashboard-flags.js";
|
|
7
7
|
import { narrateDashboard } from "./dashboard-narrator.js";
|
|
8
|
-
import { writeDashboard } from "./dashboard-writer.js";
|
|
9
8
|
import { parseBroadsideFlags, KNOWN_BROADSIDE_TOKENS } from "./broadside-flags.js";
|
|
10
9
|
import { parseNextFlags } from "./next-flags.js";
|
|
11
10
|
import { buildPiGuideMessage } from "./guide-framing.js";
|
|
12
11
|
import { isCtxLive, notifyCtx } from "./notify.js";
|
|
13
12
|
import { phaseCompactionExtension } from "./phase-compaction.js";
|
|
14
|
-
import { applyAmendment, buildPhasePrompt, buildSkillPrompt, buildValidationSummary, canonicalPath, copyPackagedWorkspace, computePerPhaseTotals, computeTotals, ConfidentialityMismatchError, createEmptyStatus, DEFAULT_PIPELINE_PATH, describeScaffoldStaleness, deriveSlug, discoverLibrary, getNextEligiblePhase, getPipelineLabel, getWorkspaceState, isWithinPath, resolveExistingPrefix, BROADSIDE_LENS_IDS, BROADSIDE_SKILL_NAME, BroadsideCancelledError, broadsideDirFor, collectResultText, estimateSubmitText, getLens, listAmendmentNames, listBatchModels, listGuideTopics, listScaffoldRefreshFiles, listSkillNames, loadAmendmentFile, loadBroadsideConfig, modelsText, runBroadsideCollect, runBroadsideStatus, runBroadsideSubmit, statusText, loadCodecartoConfig, loadUsage, loadYamlFile, normalizeForComparison, packagedWorkspaceDir, pathExists, PACKAGE_VERSION, readBroadsideSkill, readGuide, refreshScaffold, PhasePreflightError, PIPELINE_ALIASES, publishEntry, resolvePhase, resolvePipelineChoice, resolvePublishSourceRepo, SourceRepoMismatchError, runPhasePreflight, SCAFFOLD_REFRESH_PROTECTED, seedOrchestratorFiles, stringifySimpleYaml, switchPipeline, validatePhaseOutput, writeLibraryConfig, } from "../../core/index.js";
|
|
13
|
+
import { applyAmendment, buildPhasePrompt, buildSkillPrompt, buildValidationSummary, backupWorkspaceState, canonicalPath, copyPackagedWorkspace, computePerPhaseTotals, computeTotals, ConfidentialityMismatchError, createEmptyStatus, DEFAULT_PIPELINE_PATH, describeScaffoldStaleness, deriveSlug, discoverLibrary, describeDanglingCarryForward, describeMissingCompletedOutputs, getNextEligiblePhase, getPipelineLabel, getWorkspaceState, isWithinPath, resolveExistingPrefix, BROADSIDE_LENS_IDS, BROADSIDE_SKILL_NAME, BroadsideCancelledError, broadsideDirFor, collectResultText, estimateSubmitText, getLens, listAmendmentNames, listBatchModels, listGuideTopics, listScaffoldRefreshFiles, listMissingCompletedOutputs, listSkillNames, resolveSkillName, loadAmendmentFile, loadBroadsideConfig, modelsText, runBroadsideCollect, runBroadsideStatus, runBroadsideSubmit, statusText, describeConfigProblems, 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";
|
|
15
14
|
import { initLibrary } from "../../core/library.js";
|
|
16
15
|
import { resolveUserConfigPath } from "../../core/orchestrator-config.js";
|
|
17
16
|
const STATUS_WIDGET_ID = "codecarto-widget";
|
|
@@ -437,11 +436,21 @@ export default function codeCartographerExtension(pi) {
|
|
|
437
436
|
const targetExists = await pathExists(targetWorkspaceDir);
|
|
438
437
|
if (targetExists) {
|
|
439
438
|
const sameWorkspace = normalizeForComparison(await canonicalPath(targetWorkspaceDir)) === normalizeForComparison(await canonicalPath(sourceWorkspaceDir));
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
439
|
+
// The packaged template itself (a checkout install) is an existing
|
|
440
|
+
// workspace like any other: ask, and back up. It cannot be renamed
|
|
441
|
+
// away — it is what init copies from — so its session state is
|
|
442
|
+
// moved out file by file instead (#245).
|
|
443
|
+
const overwrite = await ctx.ui.confirm("CodeCartographer already exists — data will be lost", sameWorkspace
|
|
444
|
+
? "This .codecarto/ is CodeCartographer's own packaged template (a checkout install), and it holds workspace state. Re-initializing will move that state — status, findings, handoffs, usage data, closeouts, dashboard — to .codecarto-backup-TIMESTAMP/ and reset; the framework files stay in place. Consider /codecarto-open to reattach without resetting. Continue?"
|
|
445
|
+
: "A .codecarto/ directory already exists in this repository. Re-initializing will back up the existing workspace to .codecarto-backup-TIMESTAMP/ and create a fresh one. All phase findings, handoffs, usage data, closeouts, and progress will be moved to the backup. Consider /codecarto-open to reattach without resetting. Continue?");
|
|
446
|
+
if (!overwrite)
|
|
447
|
+
return;
|
|
448
|
+
const backupDir = join(ctx.cwd, `.codecarto-backup-${new Date().toISOString().replace(/[:.]/g, "-")}`);
|
|
449
|
+
if (sameWorkspace) {
|
|
450
|
+
const moved = await backupWorkspaceState(targetWorkspaceDir, backupDir);
|
|
451
|
+
notifyCtx(ctx, `Moved ${moved.length} workspace state file${moved.length === 1 ? "" : "s"} to ${basename(backupDir)}/`, "info");
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
445
454
|
await rename(targetWorkspaceDir, backupDir);
|
|
446
455
|
notifyCtx(ctx, `Backed up existing workspace to ${basename(backupDir)}/`, "info");
|
|
447
456
|
}
|
|
@@ -490,9 +499,17 @@ export default function codeCartographerExtension(pi) {
|
|
|
490
499
|
if (!state)
|
|
491
500
|
return;
|
|
492
501
|
const nextPhase = getNextEligiblePhase(state)?.id ?? "complete";
|
|
493
|
-
|
|
502
|
+
// Same check codecarto_status makes: a phase complete in status.yaml
|
|
503
|
+
// whose report is not on disk (#259).
|
|
504
|
+
const missingOutputs = describeMissingCompletedOutputs(await listMissingCompletedOutputs(state));
|
|
505
|
+
lastFeedbackLines = [`Current phase: ${nextPhase}`, `Pipeline: ${getPipelineLabel(state.status.pipeline)}`, ...missingOutputs];
|
|
494
506
|
setUiState(ctx, state, lastFeedbackLines);
|
|
495
|
-
|
|
507
|
+
if (missingOutputs.length > 0) {
|
|
508
|
+
notifyCtx(ctx, `CodeCartographer phase: ${nextPhase}. ${missingOutputs[0]}`, "warning");
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
notifyCtx(ctx, `CodeCartographer phase: ${nextPhase}`, "info");
|
|
512
|
+
}
|
|
496
513
|
},
|
|
497
514
|
});
|
|
498
515
|
pi.registerCommand("codecarto-switch-pipeline", {
|
|
@@ -533,6 +550,7 @@ export default function codeCartographerExtension(pi) {
|
|
|
533
550
|
lines.push(`New phases: ${result.newPhases.join(", ")}`);
|
|
534
551
|
if (result.dropped.length > 0)
|
|
535
552
|
lines.push(`Phases not in new pipeline: ${result.dropped.join(", ")} (findings remain on disk)`);
|
|
553
|
+
lines.push(...describeDanglingCarryForward(result.dangling));
|
|
536
554
|
lastFeedbackLines = lines;
|
|
537
555
|
await refreshWorkspaceUi(ctx, lastFeedbackLines);
|
|
538
556
|
notifyCtx(ctx, `Switched to pipeline: ${getPipelineLabel(pipelineChoice)}`, "info");
|
|
@@ -631,6 +649,10 @@ export default function codeCartographerExtension(pi) {
|
|
|
631
649
|
return;
|
|
632
650
|
}
|
|
633
651
|
const config = await loadCodecartoConfig(state.workspaceDir);
|
|
652
|
+
// A bad config file must not block a phase run, but it must not be
|
|
653
|
+
// silent either: the toggle read here may be the one it dropped.
|
|
654
|
+
if (config.problems.length > 0)
|
|
655
|
+
notifyCtx(ctx, describeConfigProblems(config).join("\n"), "warning");
|
|
634
656
|
const llmSteerEnabled = flags.llmSteerOverride ?? config.orchestrator.llm_steer_next_phase;
|
|
635
657
|
lastFeedbackLines = [`Running ${phase.id} phase as sub-agent`];
|
|
636
658
|
setUiState(ctx, state, lastFeedbackLines);
|
|
@@ -851,23 +873,27 @@ export default function codeCartographerExtension(pi) {
|
|
|
851
873
|
notifyCtx(ctx, `Cannot run skill: pipeline is not complete (next phase: ${nextPhase.id}). Finish the pipeline before running post-pipeline skills.`, "error");
|
|
852
874
|
return;
|
|
853
875
|
}
|
|
854
|
-
|
|
855
|
-
|
|
876
|
+
// Resolve against the installed list only: the name is never joined
|
|
877
|
+
// onto a path, so a traversal like `../findings/architecture` cannot
|
|
878
|
+
// splice a phase SKILL.md into the post-pipeline prompt. Same rule as
|
|
879
|
+
// the MCP surface's handleSkill.
|
|
880
|
+
const resolved = await resolveSkillName(state.workspaceDir, skillName);
|
|
881
|
+
if (!resolved) {
|
|
856
882
|
const available = await listSkillNames(state.workspaceDir);
|
|
857
883
|
const hint = available.length > 0 ? ` (available: ${available.join(", ")})` : " (no skills installed)";
|
|
858
884
|
notifyCtx(ctx, `Unknown skill: ${skillName}${hint}. The Broad-Side reading guide is served as \`${BROADSIDE_SKILL_NAME}\` and is not pipeline-gated.`, "error");
|
|
859
885
|
return;
|
|
860
886
|
}
|
|
861
|
-
const prompt = await buildSkillPrompt(state,
|
|
887
|
+
const prompt = await buildSkillPrompt(state, resolved);
|
|
862
888
|
if (ctx.isIdle()) {
|
|
863
889
|
pi.sendUserMessage(prompt);
|
|
864
890
|
}
|
|
865
891
|
else {
|
|
866
892
|
pi.sendUserMessage(prompt, { deliverAs: "followUp" });
|
|
867
893
|
}
|
|
868
|
-
lastFeedbackLines = [`Queued post-pipeline skill: ${
|
|
894
|
+
lastFeedbackLines = [`Queued post-pipeline skill: ${resolved}`];
|
|
869
895
|
setUiState(ctx, state, lastFeedbackLines);
|
|
870
|
-
notifyCtx(ctx, `Queued CodeCartographer skill: ${
|
|
896
|
+
notifyCtx(ctx, `Queued CodeCartographer skill: ${resolved}`, "info");
|
|
871
897
|
},
|
|
872
898
|
});
|
|
873
899
|
pi.registerCommand("codecarto-list-skills", {
|
|
@@ -1108,6 +1134,13 @@ export default function codeCartographerExtension(pi) {
|
|
|
1108
1134
|
if (!state)
|
|
1109
1135
|
return;
|
|
1110
1136
|
const config = await loadCodecartoConfig(state.workspaceDir);
|
|
1137
|
+
// The library path, namespace, and confirm gate all come from the
|
|
1138
|
+
// config, so a file that could not be used is a refusal, not a silent
|
|
1139
|
+
// fallback to the other layer (#242). Same rule as codecarto_publish.
|
|
1140
|
+
if (config.problems.length > 0) {
|
|
1141
|
+
notifyCtx(ctx, ["Publish refused: the configuration has problems. Fix or remove the offending file, then retry.", ...describeConfigProblems(config)].join("\n"), "error");
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1111
1144
|
if (!config.library.path) {
|
|
1112
1145
|
notifyCtx(ctx, "No library.path is configured. Create a library directory with a .codecarto-library marker, then set library.path in ~/.codecarto/config.yaml or .codecarto/workflow/config.yaml.", "error");
|
|
1113
1146
|
return;
|
|
@@ -1228,15 +1261,21 @@ export default function codeCartographerExtension(pi) {
|
|
|
1228
1261
|
namespaced: !!namespace,
|
|
1229
1262
|
...(namespace ? {} : {}),
|
|
1230
1263
|
});
|
|
1231
|
-
// Write
|
|
1264
|
+
// Write library.path (and library.namespace when given) to the
|
|
1265
|
+
// user-global config and nothing else — not publish_confirm, so
|
|
1266
|
+
// initializing a library does not change how publish behaves on
|
|
1267
|
+
// MCP (#244). A config file that cannot be parsed is left alone;
|
|
1268
|
+
// the error lands in the catch below.
|
|
1232
1269
|
const configPath = resolveUserConfigPath();
|
|
1233
1270
|
await writeLibraryConfig(configPath, libraryPath, namespace);
|
|
1271
|
+
const written = namespace ? "library.path and library.namespace" : "library.path";
|
|
1234
1272
|
const msg = result.alreadyExisted
|
|
1235
1273
|
? `Library already exists at ${libraryPath} (marker preserved). Config updated.`
|
|
1236
1274
|
: `Created library at ${libraryPath} with marker "${result.marker.name}".`;
|
|
1237
|
-
|
|
1275
|
+
const configLine = `Wrote ${written} to ${configPath}; other keys untouched`;
|
|
1276
|
+
lastFeedbackLines = [msg, configLine];
|
|
1238
1277
|
notifyCtx(ctx, msg, "info");
|
|
1239
|
-
notifyCtx(ctx,
|
|
1278
|
+
notifyCtx(ctx, configLine, "info");
|
|
1240
1279
|
}
|
|
1241
1280
|
catch (error) {
|
|
1242
1281
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1263,10 +1302,18 @@ export default function codeCartographerExtension(pi) {
|
|
|
1263
1302
|
const marker = await discoverLibrary(config.library.path);
|
|
1264
1303
|
lines.push(` Library marker: ${marker ? `found ("${marker.name}", namespaced: ${marker.namespaced})` : "MISSING — run /codecarto-library-init"}`);
|
|
1265
1304
|
}
|
|
1305
|
+
// A file that could not be used is the one thing this command exists
|
|
1306
|
+
// to surface; /codecarto-publish refuses while any is listed (#242).
|
|
1307
|
+
lines.push(...describeConfigProblems(config));
|
|
1266
1308
|
lastFeedbackLines = lines;
|
|
1267
1309
|
if (state)
|
|
1268
1310
|
setUiState(ctx, state, lastFeedbackLines);
|
|
1269
|
-
|
|
1311
|
+
if (config.problems.length > 0) {
|
|
1312
|
+
notifyCtx(ctx, describeConfigProblems(config).join("\n"), "warning");
|
|
1313
|
+
}
|
|
1314
|
+
else {
|
|
1315
|
+
notifyCtx(ctx, "Configuration shown in status widget.", "info");
|
|
1316
|
+
}
|
|
1270
1317
|
},
|
|
1271
1318
|
});
|
|
1272
1319
|
pi.registerCommand("codecarto-usage", {
|