@documentation-robotics/cli 0.3.0 → 0.4.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/README.md +10 -14
- package/dist/analyzers/active-analyzer.js +62 -0
- package/dist/analyzers/active-analyzer.js.map +7 -0
- package/dist/analyzers/cards/build.js +21 -1
- package/dist/analyzers/cards/build.js.map +2 -2
- package/dist/analyzers/cards/extract.js +138 -0
- package/dist/analyzers/cards/extract.js.map +7 -0
- package/dist/analyzers/cards/routes.js +96 -0
- package/dist/analyzers/cards/routes.js.map +7 -0
- package/dist/analyzers/cbm-analyzer.js +76 -4
- package/dist/analyzers/cbm-analyzer.js.map +2 -2
- package/dist/analyzers/cbm-responses.js +12 -6
- package/dist/analyzers/cbm-responses.js.map +2 -2
- package/dist/analyzers/code-changes.js +159 -0
- package/dist/analyzers/code-changes.js.map +7 -0
- package/dist/analyzers/edge-relationships.js +6 -2
- package/dist/analyzers/edge-relationships.js.map +2 -2
- package/dist/analyzers/extraction-plan.js +541 -0
- package/dist/analyzers/extraction-plan.js.map +7 -0
- package/dist/analyzers/index-job.js +229 -0
- package/dist/analyzers/index-job.js.map +7 -0
- package/dist/cli.js +12 -10
- package/dist/cli.js.map +2 -2
- package/dist/coding-agents/drbot-mcp.js +120 -18
- package/dist/coding-agents/drbot-mcp.js.map +2 -2
- package/dist/commands/add.js +4 -2
- package/dist/commands/add.js.map +2 -2
- package/dist/commands/analyzer.js +35 -35
- package/dist/commands/analyzer.js.map +2 -2
- package/dist/commands/changeset-verify.js +16 -8
- package/dist/commands/changeset-verify.js.map +2 -2
- package/dist/commands/changeset.js +41 -11
- package/dist/commands/changeset.js.map +2 -2
- package/dist/commands/init.js +9 -26
- package/dist/commands/init.js.map +2 -2
- package/dist/commands/mcp.js +35 -31
- package/dist/commands/mcp.js.map +3 -3
- package/dist/commands/relationship.js +2 -12
- package/dist/commands/relationship.js.map +2 -2
- package/dist/commands/reports.js +3 -3
- package/dist/commands/reports.js.map +2 -2
- package/dist/commands/update.js +14 -13
- package/dist/commands/update.js.map +2 -2
- package/dist/commands/upgrade.js +28 -120
- package/dist/commands/upgrade.js.map +2 -2
- package/dist/commands/validate.js +19 -3
- package/dist/commands/validate.js.map +2 -2
- package/dist/commands/version.js +1 -1
- package/dist/commands/visualize.js +30 -12
- package/dist/commands/visualize.js.map +2 -2
- package/dist/core/annotation-store.js +15 -5
- package/dist/core/annotation-store.js.map +2 -2
- package/dist/core/catalog-migration.js +2 -1
- package/dist/core/catalog-migration.js.map +2 -2
- package/dist/core/changeset-base-index.js +7 -0
- package/dist/core/changeset-base-index.js.map +2 -2
- package/dist/core/changeset-brief-compact.js +256 -0
- package/dist/core/changeset-brief-compact.js.map +7 -0
- package/dist/core/changeset-verify.js +356 -26
- package/dist/core/changeset-verify.js.map +3 -3
- package/dist/core/extraction-observability.js +161 -0
- package/dist/core/extraction-observability.js.map +7 -0
- package/dist/core/migration-registry.js +13 -1
- package/dist/core/migration-registry.js.map +2 -2
- package/dist/core/project-init.js +53 -0
- package/dist/core/project-init.js.map +7 -0
- package/dist/core/relationship-staging.js +94 -1
- package/dist/core/relationship-staging.js.map +2 -2
- package/dist/core/staged-changeset-storage.js +83 -3
- package/dist/core/staged-changeset-storage.js.map +2 -2
- package/dist/core/staging-area.js +85 -8
- package/dist/core/staging-area.js.map +2 -2
- package/dist/core/upgrade-plan.js +117 -0
- package/dist/core/upgrade-plan.js.map +7 -0
- package/dist/generated/relationship-index.js +124 -15
- package/dist/generated/relationship-index.js.map +2 -2
- package/dist/integrations/claude_code/.claude-plugin/plugin.json +24 -0
- package/dist/integrations/claude_code/agents/dr-architect.md +19 -5
- package/dist/integrations/claude_code/agents/dr-extractor.md +7 -1
- package/dist/integrations/claude_code/commands/dr-changeset.md +2 -1
- package/dist/integrations/claude_code/commands/dr-map.md +7 -1
- package/dist/integrations/claude_code/commands/dr-plan.md +42 -16
- package/dist/integrations/claude_code/docs/MCP_SERVER.md +28 -31
- package/dist/integrations/claude_code/docs/README.md +20 -1
- package/dist/integrations/claude_code/plugin-agents/dr-advisor.md +255 -0
- package/dist/integrations/claude_code/plugin-agents/dr-architect.md +306 -0
- package/dist/integrations/claude_code/skills/dr_13_testing_layer/SKILL.md +3 -0
- package/dist/integrations/claude_code/skills/dr_change_planning/SKILL.md +90 -31
- package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +5 -0
- package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +5 -0
- package/dist/integrations/github_copilot/agents/dr-architect.md +2 -0
- package/dist/integrations/github_copilot/skills/dr_13_testing_layer/SKILL.md +3 -0
- package/dist/integrations/github_copilot/skills/dr_change_planning/SKILL.md +88 -30
- package/dist/mcp/changeset-only.js +14 -3
- package/dist/mcp/changeset-only.js.map +2 -2
- package/dist/mcp/project-root.js +85 -0
- package/dist/mcp/project-root.js.map +7 -0
- package/dist/mcp/server-instructions.js +27 -0
- package/dist/mcp/server-instructions.js.map +7 -0
- package/dist/mcp/strict-schema.js +52 -0
- package/dist/mcp/strict-schema.js.map +7 -0
- package/dist/mcp/tool-registry.js +39 -5
- package/dist/mcp/tool-registry.js.map +2 -2
- package/dist/mcp/tools/changeset-analysis-tools.js +25 -5
- package/dist/mcp/tools/changeset-analysis-tools.js.map +3 -3
- package/dist/mcp/tools/changeset-commit.js +224 -0
- package/dist/mcp/tools/changeset-commit.js.map +7 -0
- package/dist/mcp/tools/changeset-editing.js +133 -57
- package/dist/mcp/tools/changeset-editing.js.map +3 -3
- package/dist/mcp/tools/changeset-lifecycle.js +1 -1
- package/dist/mcp/tools/changeset-lifecycle.js.map +2 -2
- package/dist/mcp/tools/changeset-verify.js +12 -6
- package/dist/mcp/tools/changeset-verify.js.map +2 -2
- package/dist/mcp/tools/codebase-extract.js +836 -0
- package/dist/mcp/tools/codebase-extract.js.map +7 -0
- package/dist/mcp/tools/codebase-index.js +125 -0
- package/dist/mcp/tools/codebase-index.js.map +7 -0
- package/dist/mcp/tools/codebase-shared.js +265 -0
- package/dist/mcp/tools/codebase-shared.js.map +7 -0
- package/dist/mcp/tools/codebase-verify.js +48 -0
- package/dist/mcp/tools/codebase-verify.js.map +7 -0
- package/dist/mcp/tools/index.js +15 -0
- package/dist/mcp/tools/index.js.map +2 -2
- package/dist/mcp/tools/model-add.js +189 -73
- package/dist/mcp/tools/model-add.js.map +2 -2
- package/dist/mcp/tools/model-update.js +16 -12
- package/dist/mcp/tools/model-update.js.map +2 -2
- package/dist/mcp/tools/model-validate.js +46 -4
- package/dist/mcp/tools/model-validate.js.map +2 -2
- package/dist/mcp/tools/project-status-collectors.js +381 -0
- package/dist/mcp/tools/project-status-collectors.js.map +7 -0
- package/dist/mcp/tools/project-status.js +237 -0
- package/dist/mcp/tools/project-status.js.map +7 -0
- package/dist/mcp/tools/project-tools.js +505 -0
- package/dist/mcp/tools/project-tools.js.map +7 -0
- package/dist/mcp/tools/relationship-tools.js +89 -20
- package/dist/mcp/tools/relationship-tools.js.map +2 -2
- package/dist/mcp/tools/review-open.js +210 -0
- package/dist/mcp/tools/review-open.js.map +7 -0
- package/dist/mcp/tools/shared.js +10 -2
- package/dist/mcp/tools/shared.js.map +2 -2
- package/dist/reports/model-report-orchestrator.js +31 -2
- package/dist/reports/model-report-orchestrator.js.map +2 -2
- package/dist/schemas/bundled/analyzers/cbm.json +22 -10
- package/dist/schemas/bundled/analyzers/manifest.json +2 -2
- package/dist/schemas/bundled/api.json +1 -1
- package/dist/schemas/bundled/apm.json +1 -1
- package/dist/schemas/bundled/application.json +1 -1
- package/dist/schemas/bundled/base.json +1 -1
- package/dist/schemas/bundled/business.json +1 -1
- package/dist/schemas/bundled/data-model.json +1 -1
- package/dist/schemas/bundled/data-store.json +1 -1
- package/dist/schemas/bundled/manifest.json +3 -3
- package/dist/schemas/bundled/motivation.json +1 -1
- package/dist/schemas/bundled/navigation.json +1 -1
- package/dist/schemas/bundled/product.json +1 -1
- package/dist/schemas/bundled/security.json +1 -1
- package/dist/schemas/bundled/technology.json +1 -1
- package/dist/schemas/bundled/testing.json +145 -1
- package/dist/schemas/bundled/ux.json +1 -1
- package/dist/server/server.js +508 -237
- package/dist/server/server.js.map +3 -3
- package/dist/server/viewer-registry.js +264 -0
- package/dist/server/viewer-registry.js.map +7 -0
- package/dist/telemetry/index.js +1 -1
- package/dist/utils/id-generator.js +5 -2
- package/dist/utils/id-generator.js.map +2 -2
- package/dist/utils/source-reference.js +58 -27
- package/dist/utils/source-reference.js.map +2 -2
- package/dist/utils/spec-version.js +2 -2
- package/dist/utils/spec-version.js.map +1 -1
- package/dist/validators/model-warnings.js +28 -0
- package/dist/validators/model-warnings.js.map +7 -0
- package/dist/viewer/README.md +6 -6
- package/dist/viewer/assets/main-CUOQGGRA.css +1 -0
- package/dist/viewer/assets/main-DvH3gYrI.js +84 -0
- package/dist/viewer/index.html +2 -2
- package/dist/viewer/manifest.json +10 -10
- package/dist/viewer/version.json +1 -1
- package/package.json +1 -1
- package/dist/viewer/assets/main-CxzcuU_o.js +0 -84
- package/dist/viewer/assets/main-j-7ULYpm.css +0 -1
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
4
|
+
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
|
+
import { initProject, hasModel } from "../../core/project-init.js";
|
|
6
|
+
import { previewCatalogMigration, scanUpgrades } from "../../core/upgrade-plan.js";
|
|
7
|
+
import { summarizeRelationshipChanges } from "../../core/relationship-migration.js";
|
|
8
|
+
import { upgradeModel } from "../../core/model-upgrade.js";
|
|
9
|
+
import { Relationships } from "../../core/relationships.js";
|
|
10
|
+
import { installSpecReference } from "../../utils/spec-installer.js";
|
|
11
|
+
import { findProjectRoot } from "../../utils/project-paths.js";
|
|
12
|
+
import { getCliVersion } from "../../utils/spec-version.js";
|
|
13
|
+
import { CLIError, ErrorCategory, getErrorMessage } from "../../utils/errors.js";
|
|
14
|
+
import { isDebug, isVerbose } from "../../utils/globals.js";
|
|
15
|
+
import { Validator } from "../../validators/validator.js";
|
|
16
|
+
import { addModelWarnings } from "../../validators/model-warnings.js";
|
|
17
|
+
import { fileExists } from "../../utils/file-io.js";
|
|
18
|
+
import { hasLayerReports, regenerateLayerReports } from "../../commands/reports.js";
|
|
19
|
+
import { currentProjectRoot, whenProjectRootSettled } from "../project-root.js";
|
|
20
|
+
import { clearModelCache, jsonResult, reloadModel, runTool } from "./shared.js";
|
|
21
|
+
const logToStderr = (message) => {
|
|
22
|
+
process.stderr.write(`[mcp] ${message}
|
|
23
|
+
`);
|
|
24
|
+
}, verboseToStderr = (message) => {
|
|
25
|
+
isVerbose() && logToStderr(message);
|
|
26
|
+
}, debugToStderr = (message) => {
|
|
27
|
+
isDebug() && logToStderr(`[DEBUG] ${message}`);
|
|
28
|
+
};
|
|
29
|
+
let consoleGuards = 0, savedConsole;
|
|
30
|
+
async function withConsoleOnStderr(fn) {
|
|
31
|
+
if (consoleGuards++ === 0) {
|
|
32
|
+
savedConsole = { log: console.log, info: console.info, debug: console.debug };
|
|
33
|
+
const toStderr = (...args) => console.error(...args);
|
|
34
|
+
console.log = toStderr, console.info = toStderr, console.debug = toStderr;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
return await fn();
|
|
38
|
+
} finally {
|
|
39
|
+
--consoleGuards === 0 && savedConsole && (console.log = savedConsole.log, console.info = savedConsole.info, console.debug = savedConsole.debug, savedConsole = void 0);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function absoluteRootPath(rootPath) {
|
|
43
|
+
if (!isAbsolute(rootPath))
|
|
44
|
+
throw new CLIError(`rootPath must be an absolute path: ${rootPath}`, ErrorCategory.USER, [
|
|
45
|
+
"Pass the absolute path of the project folder"
|
|
46
|
+
]);
|
|
47
|
+
return resolve(rootPath);
|
|
48
|
+
}
|
|
49
|
+
const MAX_LISTED = 200, MAX_VALIDATION_ERRORS = 20, initInputSchema = {
|
|
50
|
+
name: z.string().trim().min(1).describe("The model's name, usually the project's."),
|
|
51
|
+
description: z.string().optional().describe("What the model describes."),
|
|
52
|
+
author: z.string().optional().describe("The model's author or owning team."),
|
|
53
|
+
rootPath: z.string().optional().describe("Absolute path of the folder to create the model in (documentation-robotics/ goes inside it). Defaults to the project the client has open (its MCP roots).")
|
|
54
|
+
};
|
|
55
|
+
async function initRoot(rootPath) {
|
|
56
|
+
let root;
|
|
57
|
+
if (rootPath !== void 0)
|
|
58
|
+
root = absoluteRootPath(rootPath);
|
|
59
|
+
else {
|
|
60
|
+
await whenProjectRootSettled();
|
|
61
|
+
const current = currentProjectRoot();
|
|
62
|
+
if (current.source === "cwd")
|
|
63
|
+
throw new CLIError("The client has not said which folder is open (no MCP roots), so there is no folder to create the model in.", ErrorCategory.USER, ["Pass rootPath: the absolute path of the project folder"]);
|
|
64
|
+
root = current.root;
|
|
65
|
+
}
|
|
66
|
+
let isDirectory = !1;
|
|
67
|
+
try {
|
|
68
|
+
isDirectory = (await stat(root)).isDirectory();
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
if (!isDirectory)
|
|
72
|
+
throw new CLIError(`Not a folder: ${root}`, ErrorCategory.NOT_FOUND, [
|
|
73
|
+
"Pass rootPath: the absolute path of an existing project folder"
|
|
74
|
+
]);
|
|
75
|
+
return root;
|
|
76
|
+
}
|
|
77
|
+
async function projectInitHandler(args) {
|
|
78
|
+
return runTool(() => withConsoleOnStderr(async () => {
|
|
79
|
+
const root = await initRoot(args.rootPath);
|
|
80
|
+
if (await hasModel(root))
|
|
81
|
+
throw new CLIError(`A DR model already exists in ${root}`, ErrorCategory.USER, [
|
|
82
|
+
'Use the model tools (model_info, model_search) to work with it, or "project_upgrade" to move it to the current spec version'
|
|
83
|
+
]);
|
|
84
|
+
const result = await initProject(root, {
|
|
85
|
+
name: args.name,
|
|
86
|
+
description: args.description,
|
|
87
|
+
author: args.author,
|
|
88
|
+
log: logToStderr,
|
|
89
|
+
logVerbose: verboseToStderr,
|
|
90
|
+
logDebug: debugToStderr
|
|
91
|
+
});
|
|
92
|
+
clearModelCache();
|
|
93
|
+
try {
|
|
94
|
+
await reloadModel(args.rootPath);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
logToStderr(`after project_init, loading the new model failed: ${getErrorMessage(error)}`);
|
|
97
|
+
}
|
|
98
|
+
return jsonResult({
|
|
99
|
+
status: "created",
|
|
100
|
+
rootPath: result.rootPath,
|
|
101
|
+
model: {
|
|
102
|
+
name: result.name,
|
|
103
|
+
version: result.version,
|
|
104
|
+
specVersion: result.specVersion,
|
|
105
|
+
path: result.modelPath
|
|
106
|
+
},
|
|
107
|
+
...result.reportError !== void 0 ? { warnings: [`Layer reports and README were not generated: ${result.reportError}`] } : {},
|
|
108
|
+
next: "The model is empty. Ask the user whether to bootstrap it from the code now (codebase_index, then codebase_extract) or let it grow with each change (changeset_create), and wait for their answer."
|
|
109
|
+
});
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
const projectInitTool = {
|
|
113
|
+
name: "project_init",
|
|
114
|
+
description: "Create an empty DR model at the current spec version in the project folder (documentation-robotics/), with its spec reference (.dr/) and layer reports. Before calling it, propose a name (usually the project's) and confirm it with the user. Fails if the folder already has a model. Installs no integration files.",
|
|
115
|
+
inputSchema: initInputSchema,
|
|
116
|
+
handler: projectInitHandler
|
|
117
|
+
}, upgradeInputSchema = {
|
|
118
|
+
apply: z.boolean().optional().describe("Apply the migration. Without it the tool only previews: show the preview to the user and apply only when they approve."),
|
|
119
|
+
force: z.boolean().optional().describe("Only with apply: true. As `dr upgrade --force`: stamp the new spec version when no migration leads to it (additive spec changes), reinstall the spec reference, and skip migration validation. Use it only when the user approves."),
|
|
120
|
+
rootPath: z.string().optional().describe("Absolute path to the project root containing documentation-robotics/. Defaults to the project the client has open (its MCP roots).")
|
|
121
|
+
};
|
|
122
|
+
async function integrationsNote(projectRoot) {
|
|
123
|
+
const notes = [];
|
|
124
|
+
return await fileExists(join(projectRoot, ".claude", ".dr-version")) && notes.push("This project still has per-project integration files from `dr claude install` (.claude/). The dr Claude Code plugin replaces them and project_upgrade leaves them alone; the user can remove them."), await fileExists(join(projectRoot, ".github", ".dr-copilot-version")) && notes.push("This project has per-project GitHub Copilot integration files from `dr copilot install` (.github/), which project_upgrade leaves alone. To bring them up to this dr's version, the user runs `dr copilot upgrade` in a terminal (there is no tool for it)."), notes.length > 0 ? { integrations: notes.join(" ") } : {};
|
|
125
|
+
}
|
|
126
|
+
async function hashFiles(root, dir) {
|
|
127
|
+
const hashes = /* @__PURE__ */ new Map();
|
|
128
|
+
let entries;
|
|
129
|
+
try {
|
|
130
|
+
entries = await readdir(join(root, dir), { recursive: !0, withFileTypes: !0 });
|
|
131
|
+
} catch {
|
|
132
|
+
return hashes;
|
|
133
|
+
}
|
|
134
|
+
for (const entry of entries) {
|
|
135
|
+
if (!entry.isFile())
|
|
136
|
+
continue;
|
|
137
|
+
const file = join(entry.parentPath, entry.name);
|
|
138
|
+
try {
|
|
139
|
+
hashes.set(relative(root, file).split(sep).join("/"), createHash("sha1").update(await readFile(file)).digest("hex"));
|
|
140
|
+
} catch {
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return hashes;
|
|
144
|
+
}
|
|
145
|
+
function filesChanged(before, after) {
|
|
146
|
+
const modified = [], added = [];
|
|
147
|
+
for (const [file, hash] of after) {
|
|
148
|
+
const old = before.get(file);
|
|
149
|
+
old === void 0 ? added.push(file) : old !== hash && modified.push(file);
|
|
150
|
+
}
|
|
151
|
+
const removed = [...before.keys()].filter((file) => !after.has(file));
|
|
152
|
+
return {
|
|
153
|
+
filesModified: modified.length + added.length + removed.length,
|
|
154
|
+
files: { modified: modified.sort(), added: added.sort(), removed: removed.sort() }
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
async function upgradeRoot(rootPath) {
|
|
158
|
+
rootPath === void 0 && await whenProjectRootSettled();
|
|
159
|
+
const start = rootPath !== void 0 ? absoluteRootPath(rootPath) : process.cwd(), root = await findProjectRoot(start);
|
|
160
|
+
if (!root || !await hasModel(root))
|
|
161
|
+
throw new CLIError(`No DR model found at ${start}`, ErrorCategory.NOT_FOUND, [
|
|
162
|
+
'Call "project_init" to create one'
|
|
163
|
+
]);
|
|
164
|
+
return root;
|
|
165
|
+
}
|
|
166
|
+
function compareVersions(a, b) {
|
|
167
|
+
const as = a.split(".").map(Number), bs = b.split(".").map(Number);
|
|
168
|
+
for (let i = 0; i < Math.max(as.length, bs.length); i++) {
|
|
169
|
+
const d = (as[i] || 0) - (bs[i] || 0);
|
|
170
|
+
if (d !== 0)
|
|
171
|
+
return d < 0 ? -1 : 1;
|
|
172
|
+
}
|
|
173
|
+
return 0;
|
|
174
|
+
}
|
|
175
|
+
function formatRelationship(rel) {
|
|
176
|
+
return `${rel.source} --[${rel.predicate}]--> ${rel.target}`;
|
|
177
|
+
}
|
|
178
|
+
function capped(items) {
|
|
179
|
+
return items.length > MAX_LISTED ? { items: items.slice(0, MAX_LISTED), omitted: items.length - MAX_LISTED } : { items };
|
|
180
|
+
}
|
|
181
|
+
function elementTypeRenames(changes) {
|
|
182
|
+
const byType = /* @__PURE__ */ new Map();
|
|
183
|
+
for (const change of changes) {
|
|
184
|
+
const key = `${change.fromType}->${change.toType}`, group = byType.get(key) ?? { fromType: change.fromType, toType: change.toType, elements: [] };
|
|
185
|
+
group.elements.push({ from: change.from, to: change.to }), byType.set(key, group);
|
|
186
|
+
}
|
|
187
|
+
return [...byType.values()].map((group) => {
|
|
188
|
+
const { items, omitted } = capped(group.elements);
|
|
189
|
+
return {
|
|
190
|
+
fromType: group.fromType,
|
|
191
|
+
toType: group.toType,
|
|
192
|
+
count: group.elements.length,
|
|
193
|
+
elements: items,
|
|
194
|
+
...omitted ? { omitted } : {}
|
|
195
|
+
};
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
function relationshipChange(change) {
|
|
199
|
+
const staged = "changeset" in change ? change : void 0;
|
|
200
|
+
return {
|
|
201
|
+
action: change.action,
|
|
202
|
+
before: formatRelationship(change.before),
|
|
203
|
+
...change.after ? { after: formatRelationship(change.after) } : {},
|
|
204
|
+
rules: change.rules,
|
|
205
|
+
...staged ? { changeset: staged.changeset, stagedAs: staged.stagedAs } : {},
|
|
206
|
+
...staged?.duplicateOf ? { duplicateOf: staged.duplicateOf } : {}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function relationshipRewrites(changes, label) {
|
|
210
|
+
const { items, omitted } = capped(changes);
|
|
211
|
+
return {
|
|
212
|
+
summary: summarizeRelationshipChanges(changes, label),
|
|
213
|
+
changes: items.map(relationshipChange),
|
|
214
|
+
...omitted ? { omitted } : {}
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function catalogReport(catalog, errors) {
|
|
218
|
+
const dropped = [...catalog.relationshipChanges, ...catalog.stagedRelationshipChanges].filter((change) => change.action === "dropped");
|
|
219
|
+
return {
|
|
220
|
+
elementTypeRenames: elementTypeRenames(catalog.elementTypeChanges),
|
|
221
|
+
relationshipRewrites: relationshipRewrites(catalog.relationshipChanges),
|
|
222
|
+
...catalog.stagedRelationshipChanges.length > 0 ? { stagedRelationshipRewrites: relationshipRewrites(catalog.stagedRelationshipChanges, "staged relationship changes") } : {},
|
|
223
|
+
cannotMigrate: [
|
|
224
|
+
...dropped.map((change) => ({
|
|
225
|
+
kind: "relationship-dropped",
|
|
226
|
+
relationship: formatRelationship(change.before),
|
|
227
|
+
reason: "The catalog has no replacement for this relationship type; it is removed.",
|
|
228
|
+
..."changeset" in change ? { changeset: change.changeset } : {}
|
|
229
|
+
})),
|
|
230
|
+
...errors.map((message) => ({ kind: "error", message }))
|
|
231
|
+
]
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function predictOrphans(model, catalog) {
|
|
235
|
+
const after = catalog.committedRelationshipsAfter;
|
|
236
|
+
if (!after)
|
|
237
|
+
return null;
|
|
238
|
+
const renamed = new Map(catalog.elementTypeChanges.map((change) => [change.from, change.to])), id = (elementId) => renamed.get(elementId) ?? elementId, elements = /* @__PURE__ */ new Set(), referenced = /* @__PURE__ */ new Set();
|
|
239
|
+
for (const layer of model.layers.values())
|
|
240
|
+
for (const element of layer.listElements()) {
|
|
241
|
+
const elementId = id(element.path || element.id);
|
|
242
|
+
if (elements.add(elementId), (element.references ?? []).length !== 0) {
|
|
243
|
+
referenced.add(elementId);
|
|
244
|
+
for (const ref of element.references ?? [])
|
|
245
|
+
referenced.add(id(ref.target));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const connectedAfter = new Set(referenced);
|
|
249
|
+
for (const rel of after)
|
|
250
|
+
connectedAfter.add(id(rel.source)), connectedAfter.add(id(rel.target));
|
|
251
|
+
const loses = /* @__PURE__ */ new Map();
|
|
252
|
+
for (const rel of model.getBaseRelationships())
|
|
253
|
+
for (const end of [id(rel.source), id(rel.target)])
|
|
254
|
+
!elements.has(end) || connectedAfter.has(end) || loses.set(end, [...loses.get(end) ?? [], formatRelationship(rel)]);
|
|
255
|
+
return [...loses.entries()].map(([elementId, lost]) => ({ elementId, loses: lost })).sort((a, b) => a.elementId.localeCompare(b.elementId));
|
|
256
|
+
}
|
|
257
|
+
async function wouldOrphan(projectRoot, catalog) {
|
|
258
|
+
if (!catalog.committedRelationshipsAfter && catalog.relationshipChanges.length === 0)
|
|
259
|
+
return {};
|
|
260
|
+
try {
|
|
261
|
+
const { Model } = await import("../../core/model.js"), orphans = predictOrphans(await Model.load(projectRoot, { lazyLoad: !1 }), catalog);
|
|
262
|
+
if (orphans === null)
|
|
263
|
+
return { wouldOrphan: { error: "Could not predict orphaned elements: the dry run didn't produce the migrated relationships." } };
|
|
264
|
+
if (orphans.length === 0)
|
|
265
|
+
return {};
|
|
266
|
+
const { items, omitted } = capped(orphans);
|
|
267
|
+
return {
|
|
268
|
+
wouldOrphan: {
|
|
269
|
+
count: orphans.length,
|
|
270
|
+
note: "These elements lose their last relationship with the dropped ones; validation reports them as orphaned after apply. Relink them, or accept them as they are.",
|
|
271
|
+
elements: items,
|
|
272
|
+
...omitted ? { omitted } : {}
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
} catch (error) {
|
|
276
|
+
return { wouldOrphan: { error: `Could not predict orphaned elements: ${getErrorMessage(error)}` } };
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function emptyCatalogDetails() {
|
|
280
|
+
return { elementTypeChanges: [], relationshipChanges: [], stagedRelationshipChanges: [] };
|
|
281
|
+
}
|
|
282
|
+
function actionSummary(action) {
|
|
283
|
+
return {
|
|
284
|
+
type: action.type,
|
|
285
|
+
description: action.description,
|
|
286
|
+
...action.fromVersion ? { from: action.fromVersion } : {},
|
|
287
|
+
to: action.toVersion,
|
|
288
|
+
...action.details?.length ? { details: action.details } : {}
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function versions(scan) {
|
|
292
|
+
const model = scan.model.state === "found" ? scan.model : void 0;
|
|
293
|
+
return {
|
|
294
|
+
cliVersion: getCliVersion(),
|
|
295
|
+
cliSpecVersion: scan.bundledSpecVersion,
|
|
296
|
+
model: { specVersion: model?.specVersion ?? null, targetSpecVersion: model?.targetSpecVersion ?? null },
|
|
297
|
+
specReference: { installed: scan.installedSpecVersion }
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
async function previewCatalog(projectRoot, scan) {
|
|
301
|
+
if (scan.catalogPreview)
|
|
302
|
+
return { details: scan.catalogPreview.details, errors: scan.catalogPreview.errors };
|
|
303
|
+
if (!scan.steps.some((step) => step.rewritesCatalog))
|
|
304
|
+
return { details: emptyCatalogDetails(), errors: [] };
|
|
305
|
+
try {
|
|
306
|
+
const preview = await previewCatalogMigration(projectRoot);
|
|
307
|
+
return { details: preview.details, errors: preview.errors };
|
|
308
|
+
} catch (error) {
|
|
309
|
+
return {
|
|
310
|
+
details: emptyCatalogDetails(),
|
|
311
|
+
errors: [`Could not preview the catalog changes: ${getErrorMessage(error)}`]
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
async function validationSummary(model) {
|
|
316
|
+
const base = { ...model, relationships: new Relationships(model.getBaseRelationships()) }, result = await new Validator().validateModel(base);
|
|
317
|
+
addModelWarnings(result, base);
|
|
318
|
+
const issue = (item) => ({
|
|
319
|
+
layer: item.layer,
|
|
320
|
+
...item.elementId ? { elementId: item.elementId } : {},
|
|
321
|
+
message: item.message
|
|
322
|
+
}), errors = result.errors.slice(0, MAX_VALIDATION_ERRORS).map(issue), warnings = result.warnings.slice(0, MAX_VALIDATION_ERRORS).map(issue);
|
|
323
|
+
return {
|
|
324
|
+
scope: "base model (staged changesets not included)",
|
|
325
|
+
valid: result.isValid(),
|
|
326
|
+
errorCount: result.errors.length,
|
|
327
|
+
warningCount: result.warnings.length,
|
|
328
|
+
...errors.length > 0 ? { errors } : {},
|
|
329
|
+
...result.errors.length > errors.length ? { moreErrors: result.errors.length - errors.length } : {},
|
|
330
|
+
...warnings.length > 0 ? { warnings } : {},
|
|
331
|
+
...result.warnings.length > warnings.length ? { moreWarnings: result.warnings.length - warnings.length } : {}
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
function changesetSummary(result) {
|
|
335
|
+
return {
|
|
336
|
+
rebaselined: result.rebaselined.map((ref) => ref.name),
|
|
337
|
+
...result.drifted.length > 0 ? {
|
|
338
|
+
drifted: result.drifted.map((ref) => ref.name),
|
|
339
|
+
driftedNote: "These had drifted from the model before the upgrade; they keep their old base and need --force to commit."
|
|
340
|
+
} : {},
|
|
341
|
+
...result.errors.length > 0 ? { errors: result.errors } : {}
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
function catalogCheckErrors(scan) {
|
|
345
|
+
return [
|
|
346
|
+
...scan.catalogCheckError !== void 0 ? [`Could not check the model against the catalog: ${scan.catalogCheckError}`] : [],
|
|
347
|
+
...scan.catalogPreview?.errors ?? []
|
|
348
|
+
];
|
|
349
|
+
}
|
|
350
|
+
async function projectUpgradeHandler(args) {
|
|
351
|
+
return runTool(() => withConsoleOnStderr(async () => {
|
|
352
|
+
if (args.force && !args.apply)
|
|
353
|
+
throw new CLIError("force is only used together with apply: true", ErrorCategory.USER, [
|
|
354
|
+
"Preview without force first, then call project_upgrade with apply: true and force: true when the user approves"
|
|
355
|
+
]);
|
|
356
|
+
const projectRoot = await upgradeRoot(args.rootPath), force = args.force === !0, scan = await scanUpgrades(projectRoot, { targetBundledSpec: !0, force });
|
|
357
|
+
if (scan.model.state !== "found")
|
|
358
|
+
throw new CLIError("The model's manifest has no spec version, so the upgrade can't tell which migrations it needs.", ErrorCategory.USER, ["Check spec_version in documentation-robotics/model/manifest.yaml"]);
|
|
359
|
+
const { specVersion, targetSpecVersion } = scan.model;
|
|
360
|
+
if (compareVersions(specVersion, targetSpecVersion) > 0) {
|
|
361
|
+
const message = `The model is at spec v${specVersion}, newer than the v${targetSpecVersion} this dr carries. Update dr to work with it.`;
|
|
362
|
+
if (args.apply)
|
|
363
|
+
throw new CLIError(message, ErrorCategory.USER, [
|
|
364
|
+
"Install a newer dr (npm install -g @documentation-robotics/cli)"
|
|
365
|
+
]);
|
|
366
|
+
return jsonResult({ status: "model-newer", rootPath: projectRoot, ...versions(scan), message });
|
|
367
|
+
}
|
|
368
|
+
if (scan.noMigrationPath) {
|
|
369
|
+
const { from, to, available } = scan.noMigrationPath, message = `No migration leads from spec v${from} to v${to}. If the spec change is additive (new types and relationships only), project_upgrade with apply: true and force: true stamps the model with v${to} without changing its data.`;
|
|
370
|
+
if (args.apply)
|
|
371
|
+
throw new CLIError(message, ErrorCategory.USER, [
|
|
372
|
+
"Call project_upgrade with apply: true and force: true once the user approves stamping the new version"
|
|
373
|
+
]);
|
|
374
|
+
return jsonResult({
|
|
375
|
+
status: "no-migration-path",
|
|
376
|
+
rootPath: projectRoot,
|
|
377
|
+
...versions(scan),
|
|
378
|
+
message,
|
|
379
|
+
availableMigrations: available
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
const checkErrors = catalogCheckErrors(scan);
|
|
383
|
+
if (scan.actions.length === 0)
|
|
384
|
+
return checkErrors.length > 0 ? jsonResult({
|
|
385
|
+
status: "check-failed",
|
|
386
|
+
rootPath: projectRoot,
|
|
387
|
+
...versions(scan),
|
|
388
|
+
message: `The model is at spec v${specVersion}, but it could not be checked against the catalog, so it may still need catalog changes. Nothing was changed.`,
|
|
389
|
+
errors: checkErrors
|
|
390
|
+
}) : jsonResult({
|
|
391
|
+
status: "up-to-date",
|
|
392
|
+
rootPath: projectRoot,
|
|
393
|
+
...versions(scan),
|
|
394
|
+
message: `The model is at spec v${specVersion}, the version this dr carries. Nothing to upgrade.`
|
|
395
|
+
});
|
|
396
|
+
if (!args.apply) {
|
|
397
|
+
const catalog = await previewCatalog(projectRoot, scan), errors = [.../* @__PURE__ */ new Set([...checkErrors, ...catalog.errors])];
|
|
398
|
+
return jsonResult({
|
|
399
|
+
status: "preview",
|
|
400
|
+
rootPath: projectRoot,
|
|
401
|
+
...versions(scan),
|
|
402
|
+
steps: scan.steps,
|
|
403
|
+
actions: scan.actions.map(actionSummary),
|
|
404
|
+
...catalogReport(catalog.details, errors),
|
|
405
|
+
...await wouldOrphan(projectRoot, catalog.details),
|
|
406
|
+
...await hasLayerReports(projectRoot) ? {} : { reports: "No layer reports yet; applying generates them." },
|
|
407
|
+
...await integrationsNote(projectRoot),
|
|
408
|
+
next: "Nothing has changed. Show this to the user; call project_upgrade with apply: true when they approve."
|
|
409
|
+
}, { compact: !0 });
|
|
410
|
+
}
|
|
411
|
+
const modelFilesBefore = await hashFiles(projectRoot, "documentation-robotics"), applied = [], details = emptyCatalogDetails();
|
|
412
|
+
let changesets;
|
|
413
|
+
try {
|
|
414
|
+
for (const action of scan.actions)
|
|
415
|
+
if (action.type === "spec")
|
|
416
|
+
await installSpecReference(projectRoot, !0), applied.push(actionSummary(action));
|
|
417
|
+
else if (action.type === "model") {
|
|
418
|
+
const result = await upgradeModel(projectRoot, {
|
|
419
|
+
fromVersion: action.fromVersion,
|
|
420
|
+
toVersion: action.toVersion,
|
|
421
|
+
// As `dr upgrade --force`: skip migration validation
|
|
422
|
+
validate: !force,
|
|
423
|
+
catalogOnly: action.catalogOnly,
|
|
424
|
+
versionBumpOnly: action.versionBumpOnly
|
|
425
|
+
});
|
|
426
|
+
changesets = result.changesets, action.versionBumpOnly && applied.push(actionSummary(action));
|
|
427
|
+
for (const step of result.migration?.applied ?? []) {
|
|
428
|
+
applied.push({
|
|
429
|
+
type: "model",
|
|
430
|
+
from: step.from,
|
|
431
|
+
to: step.to,
|
|
432
|
+
description: step.changes?.description ?? step.description
|
|
433
|
+
});
|
|
434
|
+
const stepDetails = step.changes?.details;
|
|
435
|
+
details.elementTypeChanges.push(...stepDetails?.elementTypeChanges ?? []), details.relationshipChanges.push(...stepDetails?.relationshipChanges ?? []), details.stagedRelationshipChanges.push(...stepDetails?.stagedRelationshipChanges ?? []);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
} catch (error) {
|
|
439
|
+
if (applied.length === 0)
|
|
440
|
+
throw error;
|
|
441
|
+
return jsonResult({
|
|
442
|
+
status: "partially-applied",
|
|
443
|
+
rootPath: projectRoot,
|
|
444
|
+
applied,
|
|
445
|
+
...filesChanged(modelFilesBefore, await hashFiles(projectRoot, "documentation-robotics")),
|
|
446
|
+
error: getErrorMessage(error),
|
|
447
|
+
next: "Some changes were written before the failure. Check the model (model_validate) and tell the user."
|
|
448
|
+
}, { isError: !0, compact: !0 });
|
|
449
|
+
} finally {
|
|
450
|
+
clearModelCache();
|
|
451
|
+
}
|
|
452
|
+
const { cannotMigrate, ...rewrites } = catalogReport(details, []), appliedResult = {
|
|
453
|
+
rootPath: projectRoot,
|
|
454
|
+
from: specVersion,
|
|
455
|
+
to: targetSpecVersion,
|
|
456
|
+
applied,
|
|
457
|
+
...filesChanged(modelFilesBefore, await hashFiles(projectRoot, "documentation-robotics")),
|
|
458
|
+
...rewrites,
|
|
459
|
+
...cannotMigrate.length > 0 ? { dropped: cannotMigrate } : {},
|
|
460
|
+
...changesets ? { changesets: changesetSummary(changesets) } : {}
|
|
461
|
+
}, warnings = [...checkErrors];
|
|
462
|
+
try {
|
|
463
|
+
if (!await hasLayerReports(projectRoot))
|
|
464
|
+
try {
|
|
465
|
+
await regenerateLayerReports(projectRoot, logToStderr);
|
|
466
|
+
} catch (error) {
|
|
467
|
+
warnings.push(`Layer reports were not generated: ${getErrorMessage(error)}`);
|
|
468
|
+
}
|
|
469
|
+
const model = await reloadModel(args.rootPath), validation = await validationSummary(model);
|
|
470
|
+
return jsonResult({
|
|
471
|
+
status: "applied",
|
|
472
|
+
...appliedResult,
|
|
473
|
+
to: model.manifest.specVersion,
|
|
474
|
+
validation,
|
|
475
|
+
...warnings.length > 0 ? { warnings } : {},
|
|
476
|
+
...await integrationsNote(projectRoot)
|
|
477
|
+
}, { compact: !0 });
|
|
478
|
+
} catch (error) {
|
|
479
|
+
return jsonResult({
|
|
480
|
+
status: "applied-with-errors",
|
|
481
|
+
...appliedResult,
|
|
482
|
+
followUpError: `The migration was applied, but reloading or validating the model failed: ${getErrorMessage(error)}`,
|
|
483
|
+
...warnings.length > 0 ? { warnings } : {},
|
|
484
|
+
...await integrationsNote(projectRoot),
|
|
485
|
+
next: "Tell the user the model was migrated, then check it with model_reload and model_validate."
|
|
486
|
+
}, { compact: !0 });
|
|
487
|
+
}
|
|
488
|
+
}));
|
|
489
|
+
}
|
|
490
|
+
const projectUpgradeTool = {
|
|
491
|
+
name: "project_upgrade",
|
|
492
|
+
description: "Move the model to the spec version this dr carries. Previews by default: the version path, element type renames, relationship rewrites, anything it can't migrate, and the elements the dropped relationships would leave orphaned (wouldOrphan). apply: true runs the migration, then reloads and validates the base model; force: true (with apply) stamps the version when no migration leads to it. The applied result counts the model files written and the warnings dr validate reports.",
|
|
493
|
+
inputSchema: upgradeInputSchema,
|
|
494
|
+
handler: projectUpgradeHandler
|
|
495
|
+
};
|
|
496
|
+
export {
|
|
497
|
+
predictOrphans,
|
|
498
|
+
projectInitHandler,
|
|
499
|
+
projectInitTool,
|
|
500
|
+
projectUpgradeHandler,
|
|
501
|
+
projectUpgradeTool,
|
|
502
|
+
withConsoleOnStderr,
|
|
503
|
+
wouldOrphan
|
|
504
|
+
};
|
|
505
|
+
//# sourceMappingURL=project-tools.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/mcp/tools/project-tools.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * project_init, project_upgrade \u2014 onboard a project and move its model to a new spec version\n * from the conversation.\n *\n * project_init mirrors `dr init` without the prompts: the name and the rest are arguments. It\n * installs no per-project integration files (the Claude Code plugin replaces\n * `dr claude install`).\n *\n * project_upgrade mirrors `dr upgrade` for the spec reference and the model. It previews by\n * default: the version path, the element type renames and relationship rewrites the generated\n * migration table makes, and anything it can't migrate. `apply: true` runs the migration, then\n * reloads and validates the model with the warnings `dr validate` reports, and lists every model\n * file it wrote. Per-project integration files are left alone (the dr plugin replaces them).\n *\n * Both write outside a changeset, so changeset-only mode refuses them (mcp/changeset-only.ts),\n * and DrBot is not given them (coding-agents/drbot-mcp.ts).\n */\n\nimport { z } from \"zod\";\nimport { createHash } from \"node:crypto\";\nimport { readdir, readFile, stat } from \"node:fs/promises\";\nimport { isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { initProject, hasModel } from \"../../core/project-init.js\";\nimport {\n previewCatalogMigration,\n scanUpgrades,\n type UpgradeAction,\n type UpgradeScan,\n type UpgradeStep,\n} from \"../../core/upgrade-plan.js\";\nimport type { CatalogMigrationResult } from \"../../core/catalog-migration.js\";\nimport type { ElementTypeRenameChange } from \"../../core/element-type-migration.js\";\nimport {\n summarizeRelationshipChanges,\n type RelationshipRewriteChange,\n type StagedRelationshipRewriteChange,\n} from \"../../core/relationship-migration.js\";\nimport { upgradeModel, type ChangesetRebaselineResult } from \"../../core/model-upgrade.js\";\nimport type { Model } from \"../../core/model.js\";\nimport { Relationships } from \"../../core/relationships.js\";\nimport { installSpecReference } from \"../../utils/spec-installer.js\";\nimport { findProjectRoot } from \"../../utils/project-paths.js\";\nimport { getCliVersion } from \"../../utils/spec-version.js\";\nimport { CLIError, ErrorCategory, getErrorMessage } from \"../../utils/errors.js\";\nimport { isDebug, isVerbose } from \"../../utils/globals.js\";\nimport { Validator } from \"../../validators/validator.js\";\nimport { addModelWarnings } from \"../../validators/model-warnings.js\";\nimport { fileExists } from \"../../utils/file-io.js\";\nimport { hasLayerReports, regenerateLayerReports } from \"../../commands/reports.js\";\nimport { currentProjectRoot, whenProjectRootSettled } from \"../project-root.js\";\nimport { clearModelCache, jsonResult, reloadModel, runTool, type McpToolDefinition } from \"./shared.js\";\n\n/** The MCP server's stdout carries the protocol: progress lines go to stderr. */\nconst logToStderr = (message: string): void => {\n process.stderr.write(`[mcp] ${message}\\n`);\n};\n\n/** --verbose and --debug lines from the shared cores, on stderr. */\nconst verboseToStderr = (message: string): void => {\n if (isVerbose()) logToStderr(message);\n};\nconst debugToStderr = (message: string): void => {\n if (isDebug()) logToStderr(`[DEBUG] ${message}`);\n};\n\nlet consoleGuards = 0;\nlet savedConsole: Pick<Console, \"log\" | \"info\" | \"debug\"> | undefined;\n\n/**\n * Runs fn with console.log, console.info and console.debug sent to stderr. The init and upgrade\n * cores are CLI code: any line one of them prints while this server runs over stdio would land in\n * the JSON-RPC stream. Counted, so overlapping calls restore the console only when the last ends.\n */\nexport async function withConsoleOnStderr<T>(fn: () => Promise<T>): Promise<T> {\n if (consoleGuards++ === 0) {\n savedConsole = { log: console.log, info: console.info, debug: console.debug };\n const toStderr = (...args: unknown[]) => console.error(...args);\n console.log = toStderr;\n console.info = toStderr;\n console.debug = toStderr;\n }\n try {\n return await fn();\n } finally {\n if (--consoleGuards === 0 && savedConsole) {\n console.log = savedConsole.log;\n console.info = savedConsole.info;\n console.debug = savedConsole.debug;\n savedConsole = undefined;\n }\n }\n}\n\n/** A rootPath argument must be absolute: a relative one would resolve against the server's cwd. */\nfunction absoluteRootPath(rootPath: string): string {\n if (!isAbsolute(rootPath)) {\n throw new CLIError(`rootPath must be an absolute path: ${rootPath}`, ErrorCategory.USER, [\n \"Pass the absolute path of the project folder\",\n ]);\n }\n return resolve(rootPath);\n}\n\n/** At most this many relationship rewrites or element renames are listed; the rest are counted. */\nconst MAX_LISTED = 200;\n/** At most this many validation errors are listed after an upgrade. */\nconst MAX_VALIDATION_ERRORS = 20;\n\n// ---------------------------------------------------------------------------\n// project_init\n// ---------------------------------------------------------------------------\n\nconst initInputSchema = {\n name: z.string().trim().min(1).describe(\"The model's name, usually the project's.\"),\n description: z.string().optional().describe(\"What the model describes.\"),\n author: z.string().optional().describe(\"The model's author or owning team.\"),\n rootPath: z\n .string()\n .optional()\n .describe(\n \"Absolute path of the folder to create the model in (documentation-robotics/ goes inside it). Defaults to the project the client has open (its MCP roots).\"\n ),\n};\n\nexport type ProjectInitArgs = z.infer<z.ZodObject<typeof initInputSchema>>;\n\n/**\n * The folder to create the model in: rootPath, else the folder the client has open (or the one\n * DR_MCP_ROOT_PATH names). Never the server's own working directory, which for a server\n * registered once per user is wherever the client started it.\n */\nasync function initRoot(rootPath: string | undefined): Promise<string> {\n let root: string;\n if (rootPath !== undefined) {\n root = absoluteRootPath(rootPath);\n } else {\n await whenProjectRootSettled();\n const current = currentProjectRoot();\n if (current.source === \"cwd\") {\n throw new CLIError(\n \"The client has not said which folder is open (no MCP roots), so there is no folder to create the model in.\",\n ErrorCategory.USER,\n [\"Pass rootPath: the absolute path of the project folder\"]\n );\n }\n root = current.root;\n }\n\n let isDirectory = false;\n try {\n isDirectory = (await stat(root)).isDirectory();\n } catch {\n // Reported below.\n }\n if (!isDirectory) {\n throw new CLIError(`Not a folder: ${root}`, ErrorCategory.NOT_FOUND, [\n \"Pass rootPath: the absolute path of an existing project folder\",\n ]);\n }\n return root;\n}\n\nexport async function projectInitHandler(args: ProjectInitArgs): Promise<CallToolResult> {\n return runTool(() => withConsoleOnStderr(async () => {\n const root = await initRoot(args.rootPath);\n if (await hasModel(root)) {\n throw new CLIError(`A DR model already exists in ${root}`, ErrorCategory.USER, [\n 'Use the model tools (model_info, model_search) to work with it, or \"project_upgrade\" to move it to the current spec version',\n ]);\n }\n\n const result = await initProject(root, {\n name: args.name,\n description: args.description,\n author: args.author,\n log: logToStderr,\n logVerbose: verboseToStderr,\n logDebug: debugToStderr,\n });\n\n // Drop every cached model (and any failed \"no model\" lookup) so the next call reads the\n // new one, then warm the cache for the folder the model was created in.\n clearModelCache();\n try {\n await reloadModel(args.rootPath);\n } catch (error) {\n // The model was created; the next tool call loads it.\n logToStderr(`after project_init, loading the new model failed: ${getErrorMessage(error)}`);\n }\n\n return jsonResult({\n status: \"created\",\n rootPath: result.rootPath,\n model: {\n name: result.name,\n version: result.version,\n specVersion: result.specVersion,\n path: result.modelPath,\n },\n ...(result.reportError !== undefined\n ? { warnings: [`Layer reports and README were not generated: ${result.reportError}`] }\n : {}),\n next: \"The model is empty. Ask the user whether to bootstrap it from the code now (codebase_index, then codebase_extract) or let it grow with each change (changeset_create), and wait for their answer.\",\n });\n }));\n}\n\nexport const projectInitTool: McpToolDefinition<typeof initInputSchema> = {\n name: \"project_init\",\n description:\n \"Create an empty DR model at the current spec version in the project folder (documentation-robotics/), with its spec reference (.dr/) and layer reports. Before calling it, propose a name (usually the project's) and confirm it with the user. Fails if the folder already has a model. Installs no integration files.\",\n inputSchema: initInputSchema,\n handler: projectInitHandler,\n};\n\n// ---------------------------------------------------------------------------\n// project_upgrade\n// ---------------------------------------------------------------------------\n\nconst upgradeInputSchema = {\n apply: z\n .boolean()\n .optional()\n .describe(\n \"Apply the migration. Without it the tool only previews: show the preview to the user and apply only when they approve.\"\n ),\n force: z\n .boolean()\n .optional()\n .describe(\n \"Only with apply: true. As `dr upgrade --force`: stamp the new spec version when no migration leads to it (additive spec changes), reinstall the spec reference, and skip migration validation. Use it only when the user approves.\"\n ),\n rootPath: z\n .string()\n .optional()\n .describe(\n \"Absolute path to the project root containing documentation-robotics/. Defaults to the project the client has open (its MCP roots).\"\n ),\n};\n\nexport type ProjectUpgradeArgs = z.infer<z.ZodObject<typeof upgradeInputSchema>>;\n\n/**\n * What to say about per-project integration files, which the dr plugin replaces: nothing when\n * there are none, which is the usual case.\n */\nasync function integrationsNote(projectRoot: string): Promise<{ integrations?: string }> {\n const notes: string[] = [];\n if (await fileExists(join(projectRoot, \".claude\", \".dr-version\"))) {\n notes.push(\n \"This project still has per-project integration files from `dr claude install` (.claude/). The dr Claude Code plugin replaces them and project_upgrade leaves them alone; the user can remove them.\"\n );\n }\n if (await fileExists(join(projectRoot, \".github\", \".dr-copilot-version\"))) {\n notes.push(\n \"This project has per-project GitHub Copilot integration files from `dr copilot install` (.github/), which project_upgrade leaves alone. To bring them up to this dr's version, the user runs `dr copilot upgrade` in a terminal (there is no tool for it).\"\n );\n }\n return notes.length > 0 ? { integrations: notes.join(\" \") } : {};\n}\n\n/** Every file under `dir` with a hash of its content, by path relative to `root`. */\nasync function hashFiles(root: string, dir: string): Promise<Map<string, string>> {\n const hashes = new Map<string, string>();\n let entries;\n try {\n entries = await readdir(join(root, dir), { recursive: true, withFileTypes: true });\n } catch {\n return hashes;\n }\n for (const entry of entries) {\n if (!entry.isFile()) continue;\n const file = join(entry.parentPath, entry.name);\n try {\n hashes.set(relative(root, file).split(sep).join(\"/\"), createHash(\"sha1\").update(await readFile(file)).digest(\"hex\"));\n } catch {\n // gone since the listing\n }\n }\n return hashes;\n}\n\n/** The files written, added and removed between two hashFiles snapshots. */\nfunction filesChanged(before: Map<string, string>, after: Map<string, string>) {\n const modified: string[] = [];\n const added: string[] = [];\n for (const [file, hash] of after) {\n const old = before.get(file);\n if (old === undefined) added.push(file);\n else if (old !== hash) modified.push(file);\n }\n const removed = [...before.keys()].filter((file) => !after.has(file));\n return {\n filesModified: modified.length + added.length + removed.length,\n files: { modified: modified.sort(), added: added.sort(), removed: removed.sort() },\n };\n}\n\nasync function upgradeRoot(rootPath: string | undefined): Promise<string> {\n if (rootPath === undefined) await whenProjectRootSettled();\n const start = rootPath !== undefined ? absoluteRootPath(rootPath) : process.cwd();\n const root = await findProjectRoot(start);\n if (!root || !(await hasModel(root))) {\n throw new CLIError(`No DR model found at ${start}`, ErrorCategory.NOT_FOUND, [\n 'Call \"project_init\" to create one',\n ]);\n }\n return root;\n}\n\nfunction compareVersions(a: string, b: string): number {\n const as = a.split(\".\").map(Number);\n const bs = b.split(\".\").map(Number);\n for (let i = 0; i < Math.max(as.length, bs.length); i++) {\n const d = (as[i] || 0) - (bs[i] || 0);\n if (d !== 0) return d < 0 ? -1 : 1;\n }\n return 0;\n}\n\nfunction formatRelationship(rel: { source: string; predicate: string; target: string }): string {\n return `${rel.source} --[${rel.predicate}]--> ${rel.target}`;\n}\n\nfunction capped<T>(items: T[]): { items: T[]; omitted?: number } {\n return items.length > MAX_LISTED ? { items: items.slice(0, MAX_LISTED), omitted: items.length - MAX_LISTED } : { items };\n}\n\nfunction elementTypeRenames(changes: ElementTypeRenameChange[]) {\n const byType = new Map<string, { fromType: string; toType: string; elements: Array<{ from: string; to: string }> }>();\n for (const change of changes) {\n const key = `${change.fromType}->${change.toType}`;\n const group = byType.get(key) ?? { fromType: change.fromType, toType: change.toType, elements: [] };\n group.elements.push({ from: change.from, to: change.to });\n byType.set(key, group);\n }\n return [...byType.values()].map((group) => {\n const { items, omitted } = capped(group.elements);\n return {\n fromType: group.fromType,\n toType: group.toType,\n count: group.elements.length,\n elements: items,\n ...(omitted ? { omitted } : {}),\n };\n });\n}\n\nfunction relationshipChange(change: RelationshipRewriteChange | StagedRelationshipRewriteChange) {\n const staged = \"changeset\" in change ? change : undefined;\n return {\n action: change.action,\n before: formatRelationship(change.before),\n ...(change.after ? { after: formatRelationship(change.after) } : {}),\n rules: change.rules,\n ...(staged ? { changeset: staged.changeset, stagedAs: staged.stagedAs } : {}),\n ...(staged?.duplicateOf ? { duplicateOf: staged.duplicateOf } : {}),\n };\n}\n\nfunction relationshipRewrites(changes: RelationshipRewriteChange[], label?: string) {\n const { items, omitted } = capped(changes);\n return {\n summary: summarizeRelationshipChanges(changes, label),\n changes: items.map(relationshipChange),\n ...(omitted ? { omitted } : {}),\n };\n}\n\n/** What a catalog migration renames and rewrites, and what it drops or fails on. */\nfunction catalogReport(catalog: CatalogMigrationResult[\"details\"], errors: string[]) {\n const dropped = [...catalog.relationshipChanges, ...catalog.stagedRelationshipChanges].filter(\n (change) => change.action === \"dropped\"\n );\n return {\n elementTypeRenames: elementTypeRenames(catalog.elementTypeChanges),\n relationshipRewrites: relationshipRewrites(catalog.relationshipChanges),\n ...(catalog.stagedRelationshipChanges.length > 0\n ? { stagedRelationshipRewrites: relationshipRewrites(catalog.stagedRelationshipChanges, \"staged relationship changes\") }\n : {}),\n cannotMigrate: [\n ...dropped.map((change) => ({\n kind: \"relationship-dropped\",\n relationship: formatRelationship(change.before),\n reason: \"The catalog has no replacement for this relationship type; it is removed.\",\n ...(\"changeset\" in change ? { changeset: (change as StagedRelationshipRewriteChange).changeset } : {}),\n })),\n ...errors.map((message) => ({ kind: \"error\", message })),\n ],\n };\n}\n\n/**\n * The elements the migration would leave with no relationship and no reference: connected now,\n * and not in the relationships the migration itself produces (`committedRelationshipsAfter`,\n * from its dry run). What `dr validate` reports as orphaned after apply, staged changes aside as\n * in the applied validation. Only elements are reported, under their post-migration ids; a\n * relationship end that names no element is never one. Null when the dry run didn't produce the\n * migrated relationships.\n */\nexport function predictOrphans(\n model: Pick<Model, \"layers\" | \"getBaseRelationships\">,\n catalog: CatalogMigrationResult[\"details\"]\n): Array<{ elementId: string; loses: string[] }> | null {\n const after = catalog.committedRelationshipsAfter;\n if (!after) return null;\n // In a dry run the element files aren't renamed yet, so ids may still be the old ones.\n const renamed = new Map(catalog.elementTypeChanges.map((change) => [change.from, change.to]));\n const id = (elementId: string) => renamed.get(elementId) ?? elementId;\n\n const elements = new Set<string>();\n const referenced = new Set<string>();\n for (const layer of model.layers.values()) {\n for (const element of layer.listElements()) {\n const elementId = id(element.path || element.id);\n elements.add(elementId);\n if ((element.references ?? []).length === 0) continue;\n referenced.add(elementId);\n for (const ref of element.references ?? []) referenced.add(id(ref.target));\n }\n }\n\n const connectedAfter = new Set(referenced);\n for (const rel of after) {\n connectedAfter.add(id(rel.source));\n connectedAfter.add(id(rel.target));\n }\n const loses = new Map<string, string[]>();\n for (const rel of model.getBaseRelationships()) {\n for (const end of [id(rel.source), id(rel.target)]) {\n if (!elements.has(end) || connectedAfter.has(end)) continue;\n loses.set(end, [...(loses.get(end) ?? []), formatRelationship(rel)]);\n }\n }\n return [...loses.entries()]\n .map(([elementId, lost]) => ({ elementId, loses: lost }))\n .sort((a, b) => a.elementId.localeCompare(b.elementId));\n}\n\n/** predictOrphans for the preview, loading the model; says so when it can't predict. */\nexport async function wouldOrphan(projectRoot: string, catalog: CatalogMigrationResult[\"details\"]) {\n // Any rewrite can orphan an element (one that moves a relationship to other elements, as much\n // as a drop), so it runs whenever the dry run gave the migrated relationships.\n if (!catalog.committedRelationshipsAfter && catalog.relationshipChanges.length === 0) return {};\n try {\n const { Model } = await import(\"../../core/model.js\");\n const orphans = predictOrphans(await Model.load(projectRoot, { lazyLoad: false }), catalog);\n if (orphans === null) {\n return { wouldOrphan: { error: \"Could not predict orphaned elements: the dry run didn't produce the migrated relationships.\" } };\n }\n if (orphans.length === 0) return {};\n const { items, omitted } = capped(orphans);\n return {\n wouldOrphan: {\n count: orphans.length,\n note: \"These elements lose their last relationship with the dropped ones; validation reports them as orphaned after apply. Relink them, or accept them as they are.\",\n elements: items,\n ...(omitted ? { omitted } : {}),\n },\n };\n } catch (error) {\n return { wouldOrphan: { error: `Could not predict orphaned elements: ${getErrorMessage(error)}` } };\n }\n}\n\nfunction emptyCatalogDetails(): CatalogMigrationResult[\"details\"] {\n return { elementTypeChanges: [], relationshipChanges: [], stagedRelationshipChanges: [] };\n}\n\nfunction actionSummary(action: UpgradeAction) {\n return {\n type: action.type,\n description: action.description,\n ...(action.fromVersion ? { from: action.fromVersion } : {}),\n to: action.toVersion,\n ...(action.details?.length ? { details: action.details } : {}),\n };\n}\n\nfunction versions(scan: UpgradeScan) {\n const model = scan.model.state === \"found\" ? scan.model : undefined;\n return {\n cliVersion: getCliVersion(),\n cliSpecVersion: scan.bundledSpecVersion,\n model: { specVersion: model?.specVersion ?? null, targetSpecVersion: model?.targetSpecVersion ?? null },\n specReference: { installed: scan.installedSpecVersion },\n };\n}\n\n/** The catalog changes the plan makes: from the scan, or a dry run when a step rewrites the catalog. */\nasync function previewCatalog(\n projectRoot: string,\n scan: UpgradeScan\n): Promise<{ details: CatalogMigrationResult[\"details\"]; errors: string[] }> {\n if (scan.catalogPreview) {\n return { details: scan.catalogPreview.details, errors: scan.catalogPreview.errors };\n }\n if (!scan.steps.some((step: UpgradeStep) => step.rewritesCatalog)) {\n return { details: emptyCatalogDetails(), errors: [] };\n }\n try {\n const preview = await previewCatalogMigration(projectRoot);\n return { details: preview.details, errors: preview.errors };\n } catch (error) {\n return {\n details: emptyCatalogDetails(),\n errors: [`Could not preview the catalog changes: ${getErrorMessage(error)}`],\n };\n }\n}\n\n/**\n * Validate the committed model only, with the warnings `dr validate` adds (orphaned elements, no\n * source reference), so the counts match it. Model.load merges an active changeset's staged\n * relationship adds and deletes into `relationships` (its layers stay the base), so the check\n * swaps the committed relationships back in rather than validate a mix of base and plan.\n */\nasync function validationSummary(model: Model) {\n const base = { ...model, relationships: new Relationships(model.getBaseRelationships()) } as unknown as Model;\n const result = await new Validator().validateModel(base);\n addModelWarnings(result, base);\n const issue = (item: { layer: string; elementId?: string; message: string }) => ({\n layer: item.layer,\n ...(item.elementId ? { elementId: item.elementId } : {}),\n message: item.message,\n });\n const errors = result.errors.slice(0, MAX_VALIDATION_ERRORS).map(issue);\n const warnings = result.warnings.slice(0, MAX_VALIDATION_ERRORS).map(issue);\n return {\n scope: \"base model (staged changesets not included)\",\n valid: result.isValid(),\n errorCount: result.errors.length,\n warningCount: result.warnings.length,\n ...(errors.length > 0 ? { errors } : {}),\n ...(result.errors.length > errors.length ? { moreErrors: result.errors.length - errors.length } : {}),\n ...(warnings.length > 0 ? { warnings } : {}),\n ...(result.warnings.length > warnings.length ? { moreWarnings: result.warnings.length - warnings.length } : {}),\n };\n}\n\nfunction changesetSummary(result: ChangesetRebaselineResult) {\n return {\n rebaselined: result.rebaselined.map((ref) => ref.name),\n ...(result.drifted.length > 0\n ? {\n drifted: result.drifted.map((ref) => ref.name),\n driftedNote:\n \"These had drifted from the model before the upgrade; they keep their old base and need --force to commit.\",\n }\n : {}),\n ...(result.errors.length > 0 ? { errors: result.errors } : {}),\n };\n}\n\n/** Why the scan couldn't check the model against the catalog, if it couldn't. */\nfunction catalogCheckErrors(scan: UpgradeScan): string[] {\n return [\n ...(scan.catalogCheckError !== undefined\n ? [`Could not check the model against the catalog: ${scan.catalogCheckError}`]\n : []),\n ...(scan.catalogPreview?.errors ?? []),\n ];\n}\n\nexport async function projectUpgradeHandler(args: ProjectUpgradeArgs): Promise<CallToolResult> {\n return runTool(() => withConsoleOnStderr(async () => {\n if (args.force && !args.apply) {\n throw new CLIError(\"force is only used together with apply: true\", ErrorCategory.USER, [\n \"Preview without force first, then call project_upgrade with apply: true and force: true when the user approves\",\n ]);\n }\n const projectRoot = await upgradeRoot(args.rootPath);\n const force = args.force === true;\n const scan = await scanUpgrades(projectRoot, { targetBundledSpec: true, force });\n\n if (scan.model.state !== \"found\") {\n throw new CLIError(\n \"The model's manifest has no spec version, so the upgrade can't tell which migrations it needs.\",\n ErrorCategory.USER,\n [\"Check spec_version in documentation-robotics/model/manifest.yaml\"]\n );\n }\n\n // A model ahead of this dr can't be migrated, with or without force.\n const { specVersion, targetSpecVersion } = scan.model;\n if (compareVersions(specVersion, targetSpecVersion) > 0) {\n const message = `The model is at spec v${specVersion}, newer than the v${targetSpecVersion} this dr carries. Update dr to work with it.`;\n if (args.apply) {\n throw new CLIError(message, ErrorCategory.USER, [\n \"Install a newer dr (npm install -g @documentation-robotics/cli)\",\n ]);\n }\n return jsonResult({ status: \"model-newer\", rootPath: projectRoot, ...versions(scan), message });\n }\n\n if (scan.noMigrationPath) {\n const { from, to, available } = scan.noMigrationPath;\n const message = `No migration leads from spec v${from} to v${to}. If the spec change is additive (new types and relationships only), project_upgrade with apply: true and force: true stamps the model with v${to} without changing its data.`;\n if (args.apply) {\n throw new CLIError(message, ErrorCategory.USER, [\n \"Call project_upgrade with apply: true and force: true once the user approves stamping the new version\",\n ]);\n }\n return jsonResult({\n status: \"no-migration-path\",\n rootPath: projectRoot,\n ...versions(scan),\n message,\n availableMigrations: available,\n });\n }\n\n const checkErrors = catalogCheckErrors(scan);\n if (scan.actions.length === 0) {\n if (checkErrors.length > 0) {\n return jsonResult({\n status: \"check-failed\",\n rootPath: projectRoot,\n ...versions(scan),\n message: `The model is at spec v${specVersion}, but it could not be checked against the catalog, so it may still need catalog changes. Nothing was changed.`,\n errors: checkErrors,\n });\n }\n return jsonResult({\n status: \"up-to-date\",\n rootPath: projectRoot,\n ...versions(scan),\n message: `The model is at spec v${specVersion}, the version this dr carries. Nothing to upgrade.`,\n });\n }\n\n if (!args.apply) {\n const catalog = await previewCatalog(projectRoot, scan);\n const errors = [...new Set([...checkErrors, ...catalog.errors])];\n return jsonResult(\n {\n status: \"preview\",\n rootPath: projectRoot,\n ...versions(scan),\n steps: scan.steps,\n actions: scan.actions.map(actionSummary),\n ...catalogReport(catalog.details, errors),\n ...(await wouldOrphan(projectRoot, catalog.details)),\n ...(!(await hasLayerReports(projectRoot)) ? { reports: \"No layer reports yet; applying generates them.\" } : {}),\n ...(await integrationsNote(projectRoot)),\n next: \"Nothing has changed. Show this to the user; call project_upgrade with apply: true when they approve.\",\n },\n { compact: true }\n );\n }\n\n // Apply: spec reference first, then the model, as `dr upgrade` does. The model folder is\n // hashed before and after, so the result names every file the migration wrote, added or removed.\n const modelFilesBefore = await hashFiles(projectRoot, \"documentation-robotics\");\n const applied: Array<Record<string, unknown>> = [];\n const details = emptyCatalogDetails();\n let changesets: ChangesetRebaselineResult | undefined;\n try {\n for (const action of scan.actions) {\n if (action.type === \"spec\") {\n await installSpecReference(projectRoot, true);\n applied.push(actionSummary(action));\n } else if (action.type === \"model\") {\n const result = await upgradeModel(projectRoot, {\n fromVersion: action.fromVersion!,\n toVersion: action.toVersion,\n // As `dr upgrade --force`: skip migration validation\n validate: !force,\n catalogOnly: action.catalogOnly,\n versionBumpOnly: action.versionBumpOnly,\n });\n changesets = result.changesets;\n if (action.versionBumpOnly) {\n applied.push(actionSummary(action));\n }\n for (const step of result.migration?.applied ?? []) {\n // No per-step file count: the migrations count only some of the files they write.\n applied.push({\n type: \"model\",\n from: step.from,\n to: step.to,\n description: step.changes?.description ?? step.description,\n });\n const stepDetails = step.changes?.details as Partial<CatalogMigrationResult[\"details\"]> | undefined;\n details.elementTypeChanges.push(...(stepDetails?.elementTypeChanges ?? []));\n details.relationshipChanges.push(...(stepDetails?.relationshipChanges ?? []));\n details.stagedRelationshipChanges.push(...(stepDetails?.stagedRelationshipChanges ?? []));\n }\n }\n }\n } catch (error) {\n if (applied.length === 0) throw error;\n // Part of the plan is already on disk: say what, then what failed.\n return jsonResult(\n {\n status: \"partially-applied\",\n rootPath: projectRoot,\n applied,\n ...filesChanged(modelFilesBefore, await hashFiles(projectRoot, \"documentation-robotics\")),\n error: getErrorMessage(error),\n next: \"Some changes were written before the failure. Check the model (model_validate) and tell the user.\",\n },\n { isError: true, compact: true }\n );\n } finally {\n // Files changed on disk under any cached model, even when a step failed part way.\n clearModelCache();\n }\n\n const { cannotMigrate, ...rewrites } = catalogReport(details, []);\n const appliedResult = {\n rootPath: projectRoot,\n from: specVersion,\n to: targetSpecVersion,\n applied,\n ...filesChanged(modelFilesBefore, await hashFiles(projectRoot, \"documentation-robotics\")),\n ...rewrites,\n ...(cannotMigrate.length > 0 ? { dropped: cannotMigrate } : {}),\n ...(changesets ? { changesets: changesetSummary(changesets) } : {}),\n };\n const warnings = [...checkErrors];\n\n // The migration is on disk from here: a failure in the follow-up steps is reported with it.\n try {\n if (!(await hasLayerReports(projectRoot))) {\n try {\n await regenerateLayerReports(projectRoot, logToStderr);\n } catch (error) {\n warnings.push(`Layer reports were not generated: ${getErrorMessage(error)}`);\n }\n }\n const model = await reloadModel(args.rootPath);\n const validation = await validationSummary(model);\n return jsonResult(\n {\n status: \"applied\",\n ...appliedResult,\n to: model.manifest.specVersion,\n validation,\n ...(warnings.length > 0 ? { warnings } : {}),\n ...(await integrationsNote(projectRoot)),\n },\n { compact: true }\n );\n } catch (error) {\n return jsonResult(\n {\n status: \"applied-with-errors\",\n ...appliedResult,\n followUpError: `The migration was applied, but reloading or validating the model failed: ${getErrorMessage(error)}`,\n ...(warnings.length > 0 ? { warnings } : {}),\n ...(await integrationsNote(projectRoot)),\n next: \"Tell the user the model was migrated, then check it with model_reload and model_validate.\",\n },\n { compact: true }\n );\n }\n }));\n}\n\nexport const projectUpgradeTool: McpToolDefinition<typeof upgradeInputSchema> = {\n name: \"project_upgrade\",\n description:\n \"Move the model to the spec version this dr carries. Previews by default: the version path, element type renames, relationship rewrites, anything it can't migrate, and the elements the dropped relationships would leave orphaned (wouldOrphan). apply: true runs the migration, then reloads and validates the base model; force: true (with apply) stamps the version when no migration leads to it. The applied result counts the model files written and the warnings dr validate reports.\",\n inputSchema: upgradeInputSchema,\n handler: projectUpgradeHandler,\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|