@documentation-robotics/cli 0.1.12 → 0.2.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 +39 -1
- package/dist/analyzers/verify-engine.js +22 -19
- package/dist/analyzers/verify-engine.js.map +2 -2
- package/dist/cli.js +101 -27
- package/dist/cli.js.map +3 -3
- package/dist/coding-agents/agents/claude-code.js +3 -1
- package/dist/coding-agents/agents/claude-code.js.map +2 -2
- package/dist/coding-agents/claude-code-client.js +2 -1
- package/dist/coding-agents/claude-code-client.js.map +2 -2
- package/dist/coding-agents/copilot-client.js +17 -12
- package/dist/coding-agents/copilot-client.js.map +2 -2
- package/dist/coding-agents/default-permissions.js +122 -0
- package/dist/coding-agents/default-permissions.js.map +7 -0
- package/dist/commands/add.js +10 -6
- package/dist/commands/add.js.map +2 -2
- package/dist/commands/analyzer.js +18 -10
- package/dist/commands/analyzer.js.map +2 -2
- package/dist/commands/audit.js +6 -6
- package/dist/commands/audit.js.map +2 -2
- package/dist/commands/changeset.js +238 -145
- package/dist/commands/changeset.js.map +2 -2
- package/dist/commands/delete.js +45 -18
- package/dist/commands/delete.js.map +3 -3
- package/dist/commands/element.js +3 -2
- package/dist/commands/element.js.map +2 -2
- package/dist/commands/farm.js +657 -0
- package/dist/commands/farm.js.map +7 -0
- package/dist/commands/init.js +11 -1
- package/dist/commands/init.js.map +2 -2
- package/dist/commands/mcp.js +107 -0
- package/dist/commands/mcp.js.map +7 -0
- package/dist/commands/relationship.js +53 -30
- package/dist/commands/relationship.js.map +2 -2
- package/dist/commands/reports.js +1 -1
- package/dist/commands/reports.js.map +2 -2
- package/dist/commands/update.js +19 -6
- package/dist/commands/update.js.map +2 -2
- package/dist/commands/validate.js +41 -3
- package/dist/commands/validate.js.map +2 -2
- package/dist/commands/version.js +2 -2
- package/dist/commands/version.js.map +2 -2
- package/dist/config.js +83 -0
- package/dist/config.js.map +7 -0
- package/dist/core/annotation-store.js +138 -0
- package/dist/core/annotation-store.js.map +7 -0
- package/dist/core/farm-manifest.js +134 -0
- package/dist/core/farm-manifest.js.map +7 -0
- package/dist/core/farm-sync-engine.js +346 -0
- package/dist/core/farm-sync-engine.js.map +7 -0
- package/dist/core/farm-sync-state.js +159 -0
- package/dist/core/farm-sync-state.js.map +7 -0
- package/dist/core/manifest.js +6 -3
- package/dist/core/manifest.js.map +2 -2
- package/dist/core/migration-registry.js +55 -1
- package/dist/core/migration-registry.js.map +2 -2
- package/dist/core/model.js +177 -16
- package/dist/core/model.js.map +3 -3
- package/dist/core/virtual-projection.js +1 -0
- package/dist/core/virtual-projection.js.map +2 -2
- package/dist/generated/node-types.js +1 -1
- package/dist/generated/node-types.js.map +2 -2
- package/dist/generated/relationship-index.js +45 -9
- package/dist/generated/relationship-index.js.map +2 -2
- package/dist/integrations/claude_code/docs/MCP_SERVER.md +55 -0
- package/dist/integrations/claude_code/docs/README.md +4 -0
- package/dist/mcp/api-key-manager.js +98 -0
- package/dist/mcp/api-key-manager.js.map +7 -0
- package/dist/mcp/http-transport.js +176 -0
- package/dist/mcp/http-transport.js.map +7 -0
- package/dist/mcp/resource-registry.js +97 -0
- package/dist/mcp/resource-registry.js.map +7 -0
- package/dist/mcp/tool-registry.js +44 -0
- package/dist/mcp/tool-registry.js.map +7 -0
- package/dist/mcp/tools/annotation-create.js +39 -0
- package/dist/mcp/tools/annotation-create.js.map +7 -0
- package/dist/mcp/tools/annotation-delete.js +29 -0
- package/dist/mcp/tools/annotation-delete.js.map +7 -0
- package/dist/mcp/tools/annotation-get.js +29 -0
- package/dist/mcp/tools/annotation-get.js.map +7 -0
- package/dist/mcp/tools/annotation-list.js +24 -0
- package/dist/mcp/tools/annotation-list.js.map +7 -0
- package/dist/mcp/tools/annotation-reply.js +34 -0
- package/dist/mcp/tools/annotation-reply.js.map +7 -0
- package/dist/mcp/tools/annotation-shared.js +10 -0
- package/dist/mcp/tools/annotation-shared.js.map +7 -0
- package/dist/mcp/tools/annotation-update.js +39 -0
- package/dist/mcp/tools/annotation-update.js.map +7 -0
- package/dist/mcp/tools/changeset-list.js +33 -0
- package/dist/mcp/tools/changeset-list.js.map +7 -0
- package/dist/mcp/tools/changeset-show.js +39 -0
- package/dist/mcp/tools/changeset-show.js.map +7 -0
- package/dist/mcp/tools/chat-launch.js +50 -0
- package/dist/mcp/tools/chat-launch.js.map +7 -0
- package/dist/mcp/tools/chat-status.js +26 -0
- package/dist/mcp/tools/chat-status.js.map +7 -0
- package/dist/mcp/tools/index.js +47 -0
- package/dist/mcp/tools/index.js.map +7 -0
- package/dist/mcp/tools/model-add.js +106 -0
- package/dist/mcp/tools/model-add.js.map +7 -0
- package/dist/mcp/tools/model-delete.js +99 -0
- package/dist/mcp/tools/model-delete.js.map +7 -0
- package/dist/mcp/tools/model-export.js +72 -0
- package/dist/mcp/tools/model-export.js.map +7 -0
- package/dist/mcp/tools/model-info.js +51 -0
- package/dist/mcp/tools/model-info.js.map +7 -0
- package/dist/mcp/tools/model-list.js +53 -0
- package/dist/mcp/tools/model-list.js.map +7 -0
- package/dist/mcp/tools/model-reload.js +34 -0
- package/dist/mcp/tools/model-reload.js.map +7 -0
- package/dist/mcp/tools/model-search.js +88 -0
- package/dist/mcp/tools/model-search.js.map +7 -0
- package/dist/mcp/tools/model-show.js +41 -0
- package/dist/mcp/tools/model-show.js.map +7 -0
- package/dist/mcp/tools/model-stats.js +22 -0
- package/dist/mcp/tools/model-stats.js.map +7 -0
- package/dist/mcp/tools/model-trace.js +55 -0
- package/dist/mcp/tools/model-trace.js.map +7 -0
- package/dist/mcp/tools/model-update.js +105 -0
- package/dist/mcp/tools/model-update.js.map +7 -0
- package/dist/mcp/tools/model-validate.js +78 -0
- package/dist/mcp/tools/model-validate.js.map +7 -0
- package/dist/mcp/tools/shared.js +103 -0
- package/dist/mcp/tools/shared.js.map +7 -0
- package/dist/reports/model-layer-report-generator.js +5 -3
- package/dist/reports/model-layer-report-generator.js.map +2 -2
- package/dist/reports/model-readme-generator.js +116 -0
- package/dist/reports/model-readme-generator.js.map +7 -0
- package/dist/reports/model-report-data.js +37 -1
- package/dist/reports/model-report-data.js.map +2 -2
- package/dist/reports/model-report-orchestrator.js +29 -6
- package/dist/reports/model-report-orchestrator.js.map +2 -2
- package/dist/reports/model-report-utils.js +9 -0
- package/dist/reports/model-report-utils.js.map +7 -0
- package/dist/schemas/bundled/analyzers/cbm.json +2 -2
- 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 +12 -1
- package/dist/schemas/bundled/base.json +1 -1
- package/dist/schemas/bundled/business.json +1 -1
- package/dist/schemas/bundled/data-model.json +38 -2
- package/dist/schemas/bundled/data-store.json +1 -1
- package/dist/schemas/bundled/manifest.json +4 -4
- 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 +1 -1
- package/dist/schemas/bundled/ux.json +1 -1
- package/dist/server/server.js +45 -7
- package/dist/server/server.js.map +2 -2
- package/dist/telemetry/config.js +2 -40
- package/dist/telemetry/config.js.map +2 -2
- package/dist/telemetry/index.js +1 -1
- package/dist/types/index.js.map +2 -2
- package/dist/utils/ansi-suppressor.js +25 -0
- package/dist/utils/ansi-suppressor.js.map +7 -0
- package/dist/utils/color-utils.js +34 -0
- package/dist/utils/color-utils.js.map +7 -0
- package/dist/utils/element-reference-shape.js +15 -1
- package/dist/utils/element-reference-shape.js.map +3 -3
- package/dist/utils/errors.js +69 -10
- package/dist/utils/errors.js.map +2 -2
- package/dist/utils/globals.js +17 -1
- package/dist/utils/globals.js.map +2 -2
- package/dist/utils/project-paths.js +24 -2
- package/dist/utils/project-paths.js.map +2 -2
- package/dist/utils/reference-path-parser.js +49 -0
- package/dist/utils/reference-path-parser.js.map +7 -0
- package/dist/utils/source-reference.js +5 -0
- package/dist/utils/source-reference.js.map +2 -2
- package/dist/utils/spec-version.js +10 -2
- package/dist/utils/spec-version.js.map +2 -2
- package/dist/validators/composed-reference-validator.js +262 -0
- package/dist/validators/composed-reference-validator.js.map +7 -0
- package/dist/validators/reference-validator.js +59 -8
- package/dist/validators/reference-validator.js.map +3 -3
- package/dist/validators/relationship-schema-validator.js +30 -2
- package/dist/validators/relationship-schema-validator.js.map +3 -3
- package/dist/viewer/README.md +6 -6
- package/dist/viewer/assets/main-B7AOQXXX.js +81 -0
- package/dist/viewer/assets/main-vdrFrG7q.css +1 -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 +6 -3
- package/dist/viewer/assets/main-BRPZyc36.css +0 -1
- package/dist/viewer/assets/main-BrpLargo.js +0 -81
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/core/manifest.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n ManifestData,\n ChangesetHistoryEntry,\n} from \"../types/index.js\";\n\n/**\n * Manifest representing model metadata and configuration\n *\n * The Manifest class manages all metadata about a Documentation Robotics model,\n * including project information, changeset history, and
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type {\n ManifestData,\n ChangesetHistoryEntry,\n ExternalModelDeclaration,\n} from \"../types/index.js\";\n\n/**\n * Manifest representing model metadata and configuration\n *\n * The Manifest class manages all metadata about a Documentation Robotics model,\n * including project information, changeset history, layer configuration, and\n * external model declarations.\n *\n * This class handles:\n * - Project metadata (name, version, description, author)\n * - Changeset application history\n * - External model declarations for cross-model references\n */\nexport class Manifest {\n name: string;\n version: string;\n description?: string;\n author?: string;\n created: string;\n modified: string;\n cliVersion?: string;\n specVersion?: string;\n changeset_history?: ChangesetHistoryEntry[];\n models?: Record<string, ExternalModelDeclaration>;\n codebase_path?: string;\n\n constructor(data: ManifestData) {\n this.name = data.name;\n this.version = data.version;\n this.description = data.description;\n this.author = data.author;\n this.created = data.created ?? new Date().toISOString();\n this.modified = data.modified ?? new Date().toISOString();\n this.cliVersion = data.cliVersion;\n this.specVersion = data.specVersion;\n this.models = data.models;\n this.codebase_path = data.codebase_path;\n\n // Migrate changeset history from legacy format (if present)\n this.changeset_history = this.migrateChangesetHistory(data.changeset_history);\n }\n\n /**\n * Migrate changeset history from legacy field names to new format\n * Legacy format: applied_at \u2192 committed_at, action: \"applied\" \u2192 action: \"committed\"\n *\n * @param history - Changeset history from manifest (may use legacy field names)\n * @returns Migrated changeset history using new field names\n * @throws Error if action value is unrecognized (not \"committed\", \"discarded\", \"applied\", or \"reverted\")\n */\n private migrateChangesetHistory(\n history: ChangesetHistoryEntry[] | undefined\n ): ChangesetHistoryEntry[] {\n if (!history || history.length === 0) {\n return [];\n }\n\n return history.map((entry: any) => {\n // Validate action value to prevent silent data corruption\n let action: \"committed\" | \"discarded\";\n if (entry.action === \"applied\" || entry.action === \"committed\") {\n action = \"committed\";\n } else if (entry.action === \"reverted\" || entry.action === \"discarded\") {\n action = \"discarded\";\n } else {\n throw new Error(\n `Invalid changeset history action '${entry.action}' for changeset '${entry.name}'. ` +\n `Expected: committed, discarded, applied (legacy), or reverted (legacy).`\n );\n }\n\n return {\n name: entry.name,\n // Handle legacy field name: applied_at \u2192 committed_at\n committed_at: entry.committed_at || entry.applied_at || new Date().toISOString(),\n action,\n };\n });\n }\n\n /**\n * Update the modified timestamp to current time\n */\n updateModified(): void {\n this.modified = new Date().toISOString();\n }\n\n /**\n * Serialize to JSON representation\n *\n * Converts the Manifest to a JSON-serializable object that includes project\n * metadata, changeset history, and external model declarations.\n *\n * @returns ManifestData object suitable for JSON serialization\n */\n toJSON(): Record<string, unknown> {\n const result: Record<string, unknown> = {\n name: this.name,\n version: this.version,\n created: this.created,\n modified: this.modified,\n };\n\n if (this.description) {\n result.description = this.description;\n }\n\n if (this.author) {\n result.author = this.author;\n }\n\n if (this.cliVersion) {\n result.cliVersion = this.cliVersion;\n }\n\n if (this.specVersion) {\n result.specVersion = this.specVersion;\n }\n\n if (this.codebase_path) {\n result.codebase_path = this.codebase_path;\n }\n\n if (this.changeset_history && this.changeset_history.length > 0) {\n result.changeset_history = this.changeset_history;\n }\n\n if (this.models && Object.keys(this.models).length > 0) {\n result.models = this.models;\n }\n\n return result;\n }\n\n /**\n * Create Manifest from JSON string\n */\n static fromJSON(json: string): Manifest {\n const data = JSON.parse(json) as ManifestData;\n return new Manifest(data);\n }\n\n /**\n * String representation\n */\n toString(): string {\n return `Manifest(${this.name} v${this.version})`;\n }\n}\n"],
|
|
5
|
+
"mappings": "AAkBM,MAAO,SAAQ;EACnB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA,YAAY,MAAkB;AAC5B,SAAK,OAAO,KAAK,MACjB,KAAK,UAAU,KAAK,SACpB,KAAK,cAAc,KAAK,aACxB,KAAK,SAAS,KAAK,QACnB,KAAK,UAAU,KAAK,YAAW,oBAAI,KAAI,GAAG,YAAW,GACrD,KAAK,WAAW,KAAK,aAAY,oBAAI,KAAI,GAAG,YAAW,GACvD,KAAK,aAAa,KAAK,YACvB,KAAK,cAAc,KAAK,aACxB,KAAK,SAAS,KAAK,QACnB,KAAK,gBAAgB,KAAK,eAG1B,KAAK,oBAAoB,KAAK,wBAAwB,KAAK,iBAAiB;EAC9E;;;;;;;;;EAUQ,wBACN,SAA4C;AAE5C,WAAI,CAAC,WAAW,QAAQ,WAAW,IAC1B,CAAA,IAGF,QAAQ,IAAI,CAAC,UAAc;AAEhC,UAAI;AACJ,UAAI,MAAM,WAAW,aAAa,MAAM,WAAW;AACjD,iBAAS;eACA,MAAM,WAAW,cAAc,MAAM,WAAW;AACzD,iBAAS;;AAET,cAAM,IAAI,MACR,qCAAqC,MAAM,MAAM,oBAAoB,MAAM,IAAI,4EACN;AAI7E,aAAO;QACL,MAAM,MAAM;;QAEZ,cAAc,MAAM,gBAAgB,MAAM,eAAc,oBAAI,KAAI,GAAG,YAAW;QAC9E;;IAEJ,CAAC;EACH;;;;EAKA,iBAAc;AACZ,SAAK,YAAW,oBAAI,KAAI,GAAG,YAAW;EACxC;;;;;;;;;EAUA,SAAM;AACJ,UAAM,SAAkC;MACtC,MAAM,KAAK;MACX,SAAS,KAAK;MACd,SAAS,KAAK;MACd,UAAU,KAAK;;AAGjB,WAAI,KAAK,gBACP,OAAO,cAAc,KAAK,cAGxB,KAAK,WACP,OAAO,SAAS,KAAK,SAGnB,KAAK,eACP,OAAO,aAAa,KAAK,aAGvB,KAAK,gBACP,OAAO,cAAc,KAAK,cAGxB,KAAK,kBACP,OAAO,gBAAgB,KAAK,gBAG1B,KAAK,qBAAqB,KAAK,kBAAkB,SAAS,MAC5D,OAAO,oBAAoB,KAAK,oBAG9B,KAAK,UAAU,OAAO,KAAK,KAAK,MAAM,EAAE,SAAS,MACnD,OAAO,SAAS,KAAK,SAGhB;EACT;;;;EAKA,OAAO,SAAS,MAAY;AAC1B,UAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,WAAO,IAAI,SAAS,IAAI;EAC1B;;;;EAKA,WAAQ;AACN,WAAO,YAAY,KAAK,IAAI,KAAK,KAAK,OAAO;EAC/C;;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -134,13 +134,67 @@ class MigrationRegistry {
|
|
|
134
134
|
description: "Spec version updated to 0.9.0 (Product layer introduced at layer 3; Security through Testing shifted from layers 3\u201312 to 4\u201313)"
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
|
+
}), this.migrations.push({
|
|
138
|
+
fromVersion: "0.9.0",
|
|
139
|
+
toVersion: "0.10.0",
|
|
140
|
+
description: "Data Model objectschema.required type change (Spec v0.10.0)",
|
|
141
|
+
apply: async (model) => {
|
|
142
|
+
const fs = await import("fs/promises"), pathModule = await import("path"), YAML = await import("yaml"), modelDir = `${model.rootPath}/documentation-robotics/model`, dataModelDir = pathModule.join(modelDir, "08_data-model");
|
|
143
|
+
let filesModified = 0;
|
|
144
|
+
try {
|
|
145
|
+
await fs.access(dataModelDir);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (error.code === "ENOENT")
|
|
148
|
+
return {
|
|
149
|
+
migrationsApplied: 1,
|
|
150
|
+
filesModified: 0,
|
|
151
|
+
description: "Spec version updated to 0.10.0 (objectschema.required type changed from string to array; no model data to migrate)"
|
|
152
|
+
};
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
const files = await fs.readdir(dataModelDir), migrationErrors = [];
|
|
156
|
+
for (const file of files) {
|
|
157
|
+
if (!file.endsWith(".yaml") && !file.endsWith(".yml"))
|
|
158
|
+
continue;
|
|
159
|
+
const filePath = pathModule.join(dataModelDir, file);
|
|
160
|
+
try {
|
|
161
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
162
|
+
let modified = !1;
|
|
163
|
+
const doc = YAML.parse(content);
|
|
164
|
+
if (!doc || typeof doc != "object")
|
|
165
|
+
continue;
|
|
166
|
+
for (const [, element] of Object.entries(doc)) {
|
|
167
|
+
if (!element || typeof element != "object" || element.spec_node_id !== "data-model.objectschema")
|
|
168
|
+
continue;
|
|
169
|
+
const attributes = element.attributes;
|
|
170
|
+
if (attributes && typeof attributes == "object") {
|
|
171
|
+
const required = attributes.required;
|
|
172
|
+
required && typeof required == "string" && (attributes.required = required.split(",").map((item) => item.trim()).filter((item) => item.length > 0), modified = !0);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
modified && (await fs.writeFile(filePath, YAML.stringify(doc), "utf-8"), filesModified++);
|
|
176
|
+
} catch (error) {
|
|
177
|
+
migrationErrors.push(`Failed to migrate ${file}: ${getErrorMessage(error)}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return migrationErrors.length > 0 ? {
|
|
181
|
+
migrationsApplied: 1,
|
|
182
|
+
filesModified,
|
|
183
|
+
description: "Spec version updated to 0.10.0 (objectschema.required converted from string to array)",
|
|
184
|
+
error: migrationErrors.join("; ")
|
|
185
|
+
} : {
|
|
186
|
+
migrationsApplied: 1,
|
|
187
|
+
filesModified,
|
|
188
|
+
description: "Spec version updated to 0.10.0 (objectschema.required converted from string to array)"
|
|
189
|
+
};
|
|
190
|
+
}
|
|
137
191
|
});
|
|
138
192
|
}
|
|
139
193
|
/**
|
|
140
194
|
* Get the latest available specification version
|
|
141
195
|
*/
|
|
142
196
|
getLatestVersion() {
|
|
143
|
-
return this.migrations.length === 0 ? "0.7.0" : this.migrations.map((m) => m.toVersion).
|
|
197
|
+
return this.migrations.length === 0 ? "0.7.0" : this.migrations.map((m) => m.toVersion).reduce((max, current) => this.compareVersions(current, max) > 0 ? current : max);
|
|
144
198
|
}
|
|
145
199
|
/**
|
|
146
200
|
* Get the sequence of migrations needed from one version to another
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/core/migration-registry.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Migration Registry - Manages version-based model migrations\n *\n * Handles sequential migrations from one spec version to another,\n * supporting the evolution of the architecture model specification.\n */\n\nimport type { Model } from \"./model.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\n\n/**\n * Represents a single version migration\n */\nexport interface Migration {\n fromVersion: string;\n toVersion: string;\n description: string;\n apply: (model: Model) => Promise<MigrationResult>;\n}\n\n/**\n * Result of applying a migration\n */\nexport interface MigrationResult {\n migrationsApplied: number;\n filesModified: number;\n description: string;\n error?: string;\n details?: Record<string, unknown>;\n}\n\n/**\n * Migration Registry managing all available migrations\n */\nexport class MigrationRegistry {\n private migrations: Migration[] = [];\n\n constructor() {\n this.registerMigrations();\n }\n\n /**\n * Register all available migrations\n */\n private registerMigrations(): void {\n // Migration from v0.5.0 to v0.6.0: Relationship Taxonomy\n this.migrations.push({\n fromVersion: \"0.5.0\",\n toVersion: \"0.6.0\",\n description: \"Relationship Taxonomy (Spec v0.6.0)\",\n apply: async () => {\n // This migration is opt-in - the new relationship taxonomy\n // is fully backward compatible with existing models\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.6.0 (Relationship Taxonomy now available)\",\n };\n },\n });\n\n // Migration from v0.6.0 to v0.7.0: Layer Schema Relationship Metadata\n this.migrations.push({\n fromVersion: \"0.6.0\",\n toVersion: \"0.7.0\",\n description: \"Layer Schema Relationship Metadata (Spec v0.7.0)\",\n apply: async () => {\n // This migration updates layer schemas with relationship metadata sections\n // - All 12 layer schemas now include layerMetadata, intraLayerRelationships, crossLayerRelationships\n // - Deprecated link-registry.json (removed in v0.8.0)\n // - Terminology change: \"cross-layer links\" \u2192 \"cross-layer relationships\"\n // Fully backward compatible - existing models continue to work\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description:\n \"Spec version updated to 0.7.0 (Layer schemas now include relationship metadata sections)\",\n };\n },\n });\n\n // Migration from v0.7.0 to v0.7.1: Source Code Reference Infrastructure\n this.migrations.push({\n fromVersion: \"0.7.0\",\n toVersion: \"0.7.1\",\n description: \"Source Code Reference Infrastructure (Spec v0.7.1)\",\n apply: async () => {\n // This migration adds source code reference infrastructure\n // - Common schema definitions in spec/schemas/common/\n // - Source references integrated across 10 layers\n // - Backward compatible - existing models continue to work\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.7.1 (Source Code Reference Infrastructure)\",\n };\n },\n });\n\n // Migration from v0.7.1 to v0.8.0: JSON Schema-Only Spec Architecture\n this.migrations.push({\n fromVersion: \"0.7.1\",\n toVersion: \"0.8.0\",\n description: \"JSON Schema-Only Spec Architecture (Spec v0.8.0)\",\n apply: async () => {\n // Spec architecture refactored to JSON Schema-only model:\n // - Node type definitions moved to spec/schemas/nodes/{layer}/*.node.schema.json\n // - Relationship definitions moved to spec/schemas/relationships/{layer}/*.relationship.schema.json\n // - Predicates relocated to spec/schemas/base/predicates.json\n // This is a spec architecture change only - model data files are fully backward compatible\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.0 (JSON Schema-Only Spec Architecture)\",\n };\n },\n });\n\n // Migration from v0.8.0 to v0.8.1: Expanded Relationship Coverage and Node Improvements\n this.migrations.push({\n fromVersion: \"0.8.0\",\n toVersion: \"0.8.1\",\n description: \"Expanded Relationship Coverage and Node Improvements (Spec v0.8.1)\",\n apply: async () => {\n // Spec enhancements only - no model data format changes:\n // - 733 new relationship schemas added across all 12 layers\n // - Node type descriptions improved for all 12 layers\n // - Enum-masquerader node types consolidated into parent attributes\n // - Existing models continue to work without modification\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.1 (Expanded Relationship Coverage and Node Improvements)\",\n };\n },\n });\n\n // Migration from v0.8.1 to v0.8.2: UUID/Path identifier separation\n this.migrations.push({\n fromVersion: \"0.8.1\",\n toVersion: \"0.8.2\",\n description: \"UUID/Path identifier separation (Spec v0.8.2)\",\n apply: async () => {\n // The path field is now required on all spec nodes (spec-node.schema.json).\n // Existing elements are migrated automatically on load in model.ts (loadLayer):\n // - Elements with slug-format id: slug moved to path, deterministic UUID assigned to id\n // - Elements with UUID id but no path: path derived from layer_id.type.kebab(name)\n // No manual data file changes needed; migration runs transparently on next load/save.\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.2 (UUID/Path identifier separation)\",\n };\n },\n });\n\n // Migration from v0.8.2 to v0.8.3: Inter-layer Relationship Schemas and APM Node Types\n this.migrations.push({\n fromVersion: \"0.8.2\",\n toVersion: \"0.8.3\",\n description: \"Inter-layer Relationship Schemas and APM Node Types (Spec v0.8.3)\",\n apply: async () => {\n // Major expansion of inter-layer relationship coverage and APM layer:\n // - 495 new inter-layer relationship schemas across all 12 layers\n // - Total relationship count increased from 969 to 1,447\n // - 2 new APM node types: apm.alert and apm.dashboard\n // - Backward compatible - existing models continue to work without modification\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.3 (Inter-layer Relationship Schemas and APM Node Types)\",\n };\n },\n });\n\n // Migration from v0.8.3 to v0.8.4: Analyzer Infrastructure (CBM Mapping)\n this.migrations.push({\n fromVersion: \"0.8.3\",\n toVersion: \"0.8.4\",\n description: \"Analyzer Infrastructure (Spec v0.8.4)\",\n apply: async () => {\n // Introduces spec/analyzers/ with codebase-memory (CBM) mapping:\n // - New analyzer adapter foundation and full analyzer subcommands\n // - dr analyzer verify with changeset awareness\n // - Claude Code integration commands (/dr-verify, /dr-map, dr-extractor)\n // - No model data format changes; existing models are fully compatible\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.4 (Analyzer Infrastructure)\",\n };\n },\n });\n\n // Migration from v0.8.4 to v0.9.0: Product Layer Introduction\n this.migrations.push({\n fromVersion: \"0.8.4\",\n toVersion: \"0.9.0\",\n description: \"Product Layer Introduction (Spec v0.9.0)\",\n apply: async (model: Model) => {\n const fs = await import(\"fs/promises\");\n const pathModule = await import(\"path\");\n\n const modelDir = `${model.rootPath}/documentation-robotics/model`;\n let filesModified = 0;\n\n try {\n // Verify model directory exists\n await fs.access(modelDir);\n } catch (error) {\n const err = error as { code?: string };\n // ENOENT means the directory doesn't exist - nothing to migrate\n if (err.code === \"ENOENT\") {\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description:\n \"Spec version updated to 0.9.0 (Product layer introduced; no model directories to migrate)\",\n };\n }\n // Re-throw other errors (e.g., EACCES for permission denied)\n throw error;\n }\n\n // Rename existing layer directories: shift from 03-12 to 04-13\n // Must do this in reverse order (12\u219213, 11\u219212, ..., 03\u219204) to avoid conflicts\n const renameMap = [\n [\"12_testing\", \"13_testing\"],\n [\"11_apm\", \"12_apm\"],\n [\"10_navigation\", \"11_navigation\"],\n [\"09_ux\", \"10_ux\"],\n [\"08_data-store\", \"09_data-store\"],\n [\"07_data-model\", \"08_data-model\"],\n [\"06_api\", \"07_api\"],\n [\"05_technology\", \"06_technology\"],\n [\"04_application\", \"05_application\"],\n [\"03_security\", \"04_security\"],\n ];\n\n const renameErrors: string[] = [];\n\n for (const [oldName, newName] of renameMap) {\n const oldPath = pathModule.join(modelDir, oldName);\n const newPath = pathModule.join(modelDir, newName);\n\n try {\n await fs.rename(oldPath, newPath);\n filesModified++;\n } catch (error) {\n const err = error as { code?: string };\n // ENOENT is expected when a directory hasn't been created yet\n if (err.code === \"ENOENT\") {\n continue;\n }\n // Track non-ENOENT errors to report them in the result\n renameErrors.push(\n `Failed to rename ${oldName} to ${newName}: ${getErrorMessage(error)}`\n );\n }\n }\n\n // If there were any rename failures, include them in the error field\n if (renameErrors.length > 0) {\n return {\n migrationsApplied: 1,\n filesModified,\n description:\n \"Spec version updated to 0.9.0 (Product layer introduced at layer 3; Security through Testing shifted from layers 3\u201312 to 4\u201313)\",\n error: renameErrors.join(\"; \"),\n };\n }\n\n // Create new product layer directory (it will be empty for now)\n // recursive: true handles EEXIST, so no catch needed\n const productPath = pathModule.join(modelDir, \"03_product\");\n await fs.mkdir(productPath, { recursive: true });\n\n return {\n migrationsApplied: 1,\n filesModified,\n description:\n \"Spec version updated to 0.9.0 (Product layer introduced at layer 3; Security through Testing shifted from layers 3\u201312 to 4\u201313)\",\n };\n },\n });\n }\n\n /**\n * Get the latest available specification version\n */\n getLatestVersion(): string {\n if (this.migrations.length === 0) {\n return \"0.7.0\";\n }\n\n // Return the highest toVersion from all migrations\n const versions = this.migrations.map((m) => m.toVersion);\n return versions.sort().reverse()[0];\n }\n\n /**\n * Get the sequence of migrations needed from one version to another\n */\n getMigrationPath(fromVersion: string, toVersion?: string): Migration[] {\n const targetVersion = toVersion || this.getLatestVersion();\n\n if (this.compareVersions(fromVersion, targetVersion) >= 0) {\n return [];\n }\n\n const path: Migration[] = [];\n let current = fromVersion;\n\n // Sort migrations by fromVersion\n const sortedMigrations = [...this.migrations].sort((a, b) =>\n this.compareVersions(a.fromVersion, b.fromVersion)\n );\n\n for (const migration of sortedMigrations) {\n // Check if this migration is in our path\n if (\n this.compareVersions(current, migration.fromVersion) >= 0 &&\n this.compareVersions(current, migration.toVersion) < 0 &&\n this.compareVersions(migration.toVersion, targetVersion) <= 0\n ) {\n path.push(migration);\n current = migration.toVersion;\n }\n }\n\n return path;\n }\n\n /**\n * Check if a model requires migration\n */\n requiresMigration(currentVersion: string): boolean {\n const path = this.getMigrationPath(currentVersion);\n return path.length > 0;\n }\n\n /**\n * Apply all migrations in sequence\n */\n async applyMigrations(\n model: Model,\n options: {\n fromVersion: string;\n toVersion?: string;\n dryRun?: boolean;\n validate?: boolean;\n }\n ): Promise<ApplyMigrationsResult> {\n const path = this.getMigrationPath(options.fromVersion, options.toVersion);\n const targetVersion = options.toVersion || this.getLatestVersion();\n\n if (path.length === 0) {\n return {\n applied: [],\n currentVersion: options.fromVersion,\n targetVersion,\n totalChanges: 0,\n };\n }\n\n const results: ApplyMigrationsResult = {\n applied: [],\n currentVersion: options.fromVersion,\n targetVersion,\n totalChanges: 0,\n };\n\n for (const migration of path) {\n if (!options.dryRun) {\n try {\n const result = await migration.apply(model);\n\n results.applied.push({\n from: migration.fromVersion,\n to: migration.toVersion,\n description: migration.description,\n changes: result,\n });\n\n results.totalChanges += result.migrationsApplied;\n\n // Only update model's spec version if migration succeeded (no error field)\n if (!result.error) {\n model.manifest.specVersion = migration.toVersion;\n } else {\n // If migration has errors, throw to prevent partial success state\n throw new Error(result.error);\n }\n } catch (error) {\n throw new Error(\n `Migration ${migration.fromVersion} \u2192 ${migration.toVersion} failed: ${\n getErrorMessage(error)\n }`\n );\n }\n } else {\n // Dry run - just record what would happen\n results.applied.push({\n from: migration.fromVersion,\n to: migration.toVersion,\n description: migration.description,\n dryRun: true,\n });\n }\n }\n\n return results;\n }\n\n /**\n * Get a summary of migrations that would be applied\n */\n getMigrationSummary(\n fromVersion: string,\n toVersion?: string\n ): {\n currentVersion: string;\n targetVersion: string;\n migrationsNeeded: number;\n migrations: Array<{\n from: string;\n to: string;\n description: string;\n }>;\n } {\n const path = this.getMigrationPath(fromVersion, toVersion);\n const targetVersion = toVersion || this.getLatestVersion();\n\n return {\n currentVersion: fromVersion,\n targetVersion,\n migrationsNeeded: path.length,\n migrations: path.map((m) => ({\n from: m.fromVersion,\n to: m.toVersion,\n description: m.description,\n })),\n };\n }\n\n /**\n * Compare two version strings\n * Returns: -1 if a < b, 0 if a == b, 1 if a > b\n */\n private compareVersions(a: string, b: string): number {\n const aParts = a.split(\".\").map(Number);\n const bParts = b.split(\".\").map(Number);\n\n const maxLength = Math.max(aParts.length, bParts.length);\n\n for (let i = 0; i < maxLength; i++) {\n const aPart = aParts[i] || 0;\n const bPart = bParts[i] || 0;\n\n if (aPart < bPart) return -1;\n if (aPart > bPart) return 1;\n }\n\n return 0;\n }\n}\n\n/**\n * Result of applying multiple migrations\n */\nexport interface ApplyMigrationsResult {\n applied: Array<{\n from: string;\n to: string;\n description: string;\n changes?: MigrationResult;\n dryRun?: boolean;\n }>;\n currentVersion: string;\n targetVersion: string;\n totalChanges: number;\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["/**\n * Migration Registry - Manages version-based model migrations\n *\n * Handles sequential migrations from one spec version to another,\n * supporting the evolution of the architecture model specification.\n */\n\nimport type { Model } from \"./model.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\n\n/**\n * Represents a single version migration\n */\nexport interface Migration {\n fromVersion: string;\n toVersion: string;\n description: string;\n apply: (model: Model) => Promise<MigrationResult>;\n}\n\n/**\n * Result of applying a migration\n */\nexport interface MigrationResult {\n migrationsApplied: number;\n filesModified: number;\n description: string;\n error?: string;\n details?: Record<string, unknown>;\n}\n\n/**\n * Migration Registry managing all available migrations\n */\nexport class MigrationRegistry {\n private migrations: Migration[] = [];\n\n constructor() {\n this.registerMigrations();\n }\n\n /**\n * Register all available migrations\n */\n private registerMigrations(): void {\n // Migration from v0.5.0 to v0.6.0: Relationship Taxonomy\n this.migrations.push({\n fromVersion: \"0.5.0\",\n toVersion: \"0.6.0\",\n description: \"Relationship Taxonomy (Spec v0.6.0)\",\n apply: async () => {\n // This migration is opt-in - the new relationship taxonomy\n // is fully backward compatible with existing models\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.6.0 (Relationship Taxonomy now available)\",\n };\n },\n });\n\n // Migration from v0.6.0 to v0.7.0: Layer Schema Relationship Metadata\n this.migrations.push({\n fromVersion: \"0.6.0\",\n toVersion: \"0.7.0\",\n description: \"Layer Schema Relationship Metadata (Spec v0.7.0)\",\n apply: async () => {\n // This migration updates layer schemas with relationship metadata sections\n // - All 12 layer schemas now include layerMetadata, intraLayerRelationships, crossLayerRelationships\n // - Deprecated link-registry.json (removed in v0.8.0)\n // - Terminology change: \"cross-layer links\" \u2192 \"cross-layer relationships\"\n // Fully backward compatible - existing models continue to work\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description:\n \"Spec version updated to 0.7.0 (Layer schemas now include relationship metadata sections)\",\n };\n },\n });\n\n // Migration from v0.7.0 to v0.7.1: Source Code Reference Infrastructure\n this.migrations.push({\n fromVersion: \"0.7.0\",\n toVersion: \"0.7.1\",\n description: \"Source Code Reference Infrastructure (Spec v0.7.1)\",\n apply: async () => {\n // This migration adds source code reference infrastructure\n // - Common schema definitions in spec/schemas/common/\n // - Source references integrated across 10 layers\n // - Backward compatible - existing models continue to work\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.7.1 (Source Code Reference Infrastructure)\",\n };\n },\n });\n\n // Migration from v0.7.1 to v0.8.0: JSON Schema-Only Spec Architecture\n this.migrations.push({\n fromVersion: \"0.7.1\",\n toVersion: \"0.8.0\",\n description: \"JSON Schema-Only Spec Architecture (Spec v0.8.0)\",\n apply: async () => {\n // Spec architecture refactored to JSON Schema-only model:\n // - Node type definitions moved to spec/schemas/nodes/{layer}/*.node.schema.json\n // - Relationship definitions moved to spec/schemas/relationships/{layer}/*.relationship.schema.json\n // - Predicates relocated to spec/schemas/base/predicates.json\n // This is a spec architecture change only - model data files are fully backward compatible\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.0 (JSON Schema-Only Spec Architecture)\",\n };\n },\n });\n\n // Migration from v0.8.0 to v0.8.1: Expanded Relationship Coverage and Node Improvements\n this.migrations.push({\n fromVersion: \"0.8.0\",\n toVersion: \"0.8.1\",\n description: \"Expanded Relationship Coverage and Node Improvements (Spec v0.8.1)\",\n apply: async () => {\n // Spec enhancements only - no model data format changes:\n // - 733 new relationship schemas added across all 12 layers\n // - Node type descriptions improved for all 12 layers\n // - Enum-masquerader node types consolidated into parent attributes\n // - Existing models continue to work without modification\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.1 (Expanded Relationship Coverage and Node Improvements)\",\n };\n },\n });\n\n // Migration from v0.8.1 to v0.8.2: UUID/Path identifier separation\n this.migrations.push({\n fromVersion: \"0.8.1\",\n toVersion: \"0.8.2\",\n description: \"UUID/Path identifier separation (Spec v0.8.2)\",\n apply: async () => {\n // The path field is now required on all spec nodes (spec-node.schema.json).\n // Existing elements are migrated automatically on load in model.ts (loadLayer):\n // - Elements with slug-format id: slug moved to path, deterministic UUID assigned to id\n // - Elements with UUID id but no path: path derived from layer_id.type.kebab(name)\n // No manual data file changes needed; migration runs transparently on next load/save.\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.2 (UUID/Path identifier separation)\",\n };\n },\n });\n\n // Migration from v0.8.2 to v0.8.3: Inter-layer Relationship Schemas and APM Node Types\n this.migrations.push({\n fromVersion: \"0.8.2\",\n toVersion: \"0.8.3\",\n description: \"Inter-layer Relationship Schemas and APM Node Types (Spec v0.8.3)\",\n apply: async () => {\n // Major expansion of inter-layer relationship coverage and APM layer:\n // - 495 new inter-layer relationship schemas across all 12 layers\n // - Total relationship count increased from 969 to 1,447\n // - 2 new APM node types: apm.alert and apm.dashboard\n // - Backward compatible - existing models continue to work without modification\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.3 (Inter-layer Relationship Schemas and APM Node Types)\",\n };\n },\n });\n\n // Migration from v0.8.3 to v0.8.4: Analyzer Infrastructure (CBM Mapping)\n this.migrations.push({\n fromVersion: \"0.8.3\",\n toVersion: \"0.8.4\",\n description: \"Analyzer Infrastructure (Spec v0.8.4)\",\n apply: async () => {\n // Introduces spec/analyzers/ with codebase-memory (CBM) mapping:\n // - New analyzer adapter foundation and full analyzer subcommands\n // - dr analyzer verify with changeset awareness\n // - Claude Code integration commands (/dr-verify, /dr-map, dr-extractor)\n // - No model data format changes; existing models are fully compatible\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description: \"Spec version updated to 0.8.4 (Analyzer Infrastructure)\",\n };\n },\n });\n\n // Migration from v0.8.4 to v0.9.0: Product Layer Introduction\n this.migrations.push({\n fromVersion: \"0.8.4\",\n toVersion: \"0.9.0\",\n description: \"Product Layer Introduction (Spec v0.9.0)\",\n apply: async (model: Model) => {\n const fs = await import(\"fs/promises\");\n const pathModule = await import(\"path\");\n\n const modelDir = `${model.rootPath}/documentation-robotics/model`;\n let filesModified = 0;\n\n try {\n // Verify model directory exists\n await fs.access(modelDir);\n } catch (error) {\n const err = error as { code?: string };\n // ENOENT means the directory doesn't exist - nothing to migrate\n if (err.code === \"ENOENT\") {\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description:\n \"Spec version updated to 0.9.0 (Product layer introduced; no model directories to migrate)\",\n };\n }\n // Re-throw other errors (e.g., EACCES for permission denied)\n throw error;\n }\n\n // Rename existing layer directories: shift from 03-12 to 04-13\n // Must do this in reverse order (12\u219213, 11\u219212, ..., 03\u219204) to avoid conflicts\n const renameMap = [\n [\"12_testing\", \"13_testing\"],\n [\"11_apm\", \"12_apm\"],\n [\"10_navigation\", \"11_navigation\"],\n [\"09_ux\", \"10_ux\"],\n [\"08_data-store\", \"09_data-store\"],\n [\"07_data-model\", \"08_data-model\"],\n [\"06_api\", \"07_api\"],\n [\"05_technology\", \"06_technology\"],\n [\"04_application\", \"05_application\"],\n [\"03_security\", \"04_security\"],\n ];\n\n const renameErrors: string[] = [];\n\n for (const [oldName, newName] of renameMap) {\n const oldPath = pathModule.join(modelDir, oldName);\n const newPath = pathModule.join(modelDir, newName);\n\n try {\n await fs.rename(oldPath, newPath);\n filesModified++;\n } catch (error) {\n const err = error as { code?: string };\n // ENOENT is expected when a directory hasn't been created yet\n if (err.code === \"ENOENT\") {\n continue;\n }\n // Track non-ENOENT errors to report them in the result\n renameErrors.push(\n `Failed to rename ${oldName} to ${newName}: ${getErrorMessage(error)}`\n );\n }\n }\n\n // If there were any rename failures, include them in the error field\n if (renameErrors.length > 0) {\n return {\n migrationsApplied: 1,\n filesModified,\n description:\n \"Spec version updated to 0.9.0 (Product layer introduced at layer 3; Security through Testing shifted from layers 3\u201312 to 4\u201313)\",\n error: renameErrors.join(\"; \"),\n };\n }\n\n // Create new product layer directory (it will be empty for now)\n // recursive: true handles EEXIST, so no catch needed\n const productPath = pathModule.join(modelDir, \"03_product\");\n await fs.mkdir(productPath, { recursive: true });\n\n return {\n migrationsApplied: 1,\n filesModified,\n description:\n \"Spec version updated to 0.9.0 (Product layer introduced at layer 3; Security through Testing shifted from layers 3\u201312 to 4\u201313)\",\n };\n },\n });\n\n // Migration from v0.9.0 to v0.10.0: Data Model objectschema.required type change\n this.migrations.push({\n fromVersion: \"0.9.0\",\n toVersion: \"0.10.0\",\n description: \"Data Model objectschema.required type change (Spec v0.10.0)\",\n apply: async (model: Model) => {\n const fs = await import(\"fs/promises\");\n const pathModule = await import(\"path\");\n const YAML = await import(\"yaml\");\n\n const modelDir = `${model.rootPath}/documentation-robotics/model`;\n const dataModelDir = pathModule.join(modelDir, \"08_data-model\");\n let filesModified = 0;\n\n try {\n // Verify data model directory exists\n await fs.access(dataModelDir);\n } catch (error) {\n const err = error as { code?: string };\n // ENOENT means the directory doesn't exist - nothing to migrate\n if (err.code === \"ENOENT\") {\n return {\n migrationsApplied: 1,\n filesModified: 0,\n description:\n \"Spec version updated to 0.10.0 (objectschema.required type changed from string to array; no model data to migrate)\",\n };\n }\n // Re-throw other errors\n throw error;\n }\n\n // Scan all YAML files in the data model directory\n const files = await fs.readdir(dataModelDir);\n const migrationErrors: string[] = [];\n\n for (const file of files) {\n if (!file.endsWith(\".yaml\") && !file.endsWith(\".yml\")) {\n continue;\n }\n\n const filePath = pathModule.join(dataModelDir, file);\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n let modified = false;\n const doc = YAML.parse(content);\n\n if (!doc || typeof doc !== \"object\") {\n continue;\n }\n\n // Iterate through all elements in the file\n for (const [, element] of Object.entries(doc)) {\n if (\n !element ||\n typeof element !== \"object\" ||\n (element as Record<string, unknown>).spec_node_id !== \"data-model.objectschema\"\n ) {\n continue;\n }\n\n const elem = element as Record<string, unknown>;\n const attributes = elem.attributes as Record<string, unknown> | undefined;\n\n if (attributes && typeof attributes === \"object\") {\n const required = attributes.required;\n\n // Check if required field exists and is a string\n if (required && typeof required === \"string\") {\n // Convert comma-separated string to array\n attributes.required = required\n .split(\",\")\n .map((item: string) => item.trim())\n .filter((item: string) => item.length > 0);\n modified = true;\n }\n }\n }\n\n // Write back if modified\n if (modified) {\n await fs.writeFile(filePath, YAML.stringify(doc), \"utf-8\");\n filesModified++;\n }\n } catch (error) {\n migrationErrors.push(\n `Failed to migrate ${file}: ${getErrorMessage(error)}`\n );\n }\n }\n\n if (migrationErrors.length > 0) {\n return {\n migrationsApplied: 1,\n filesModified,\n description:\n \"Spec version updated to 0.10.0 (objectschema.required converted from string to array)\",\n error: migrationErrors.join(\"; \"),\n };\n }\n\n return {\n migrationsApplied: 1,\n filesModified,\n description:\n \"Spec version updated to 0.10.0 (objectschema.required converted from string to array)\",\n };\n },\n });\n }\n\n /**\n * Get the latest available specification version\n */\n getLatestVersion(): string {\n if (this.migrations.length === 0) {\n return \"0.7.0\";\n }\n\n // Return the highest toVersion from all migrations using proper version comparison\n const versions = this.migrations.map((m) => m.toVersion);\n return versions.reduce((max, current) => {\n return this.compareVersions(current, max) > 0 ? current : max;\n });\n }\n\n /**\n * Get the sequence of migrations needed from one version to another\n */\n getMigrationPath(fromVersion: string, toVersion?: string): Migration[] {\n const targetVersion = toVersion || this.getLatestVersion();\n\n if (this.compareVersions(fromVersion, targetVersion) >= 0) {\n return [];\n }\n\n const path: Migration[] = [];\n let current = fromVersion;\n\n // Sort migrations by fromVersion\n const sortedMigrations = [...this.migrations].sort((a, b) =>\n this.compareVersions(a.fromVersion, b.fromVersion)\n );\n\n for (const migration of sortedMigrations) {\n // Check if this migration is in our path\n if (\n this.compareVersions(current, migration.fromVersion) >= 0 &&\n this.compareVersions(current, migration.toVersion) < 0 &&\n this.compareVersions(migration.toVersion, targetVersion) <= 0\n ) {\n path.push(migration);\n current = migration.toVersion;\n }\n }\n\n return path;\n }\n\n /**\n * Check if a model requires migration\n */\n requiresMigration(currentVersion: string): boolean {\n const path = this.getMigrationPath(currentVersion);\n return path.length > 0;\n }\n\n /**\n * Apply all migrations in sequence\n */\n async applyMigrations(\n model: Model,\n options: {\n fromVersion: string;\n toVersion?: string;\n dryRun?: boolean;\n validate?: boolean;\n }\n ): Promise<ApplyMigrationsResult> {\n const path = this.getMigrationPath(options.fromVersion, options.toVersion);\n const targetVersion = options.toVersion || this.getLatestVersion();\n\n if (path.length === 0) {\n return {\n applied: [],\n currentVersion: options.fromVersion,\n targetVersion,\n totalChanges: 0,\n };\n }\n\n const results: ApplyMigrationsResult = {\n applied: [],\n currentVersion: options.fromVersion,\n targetVersion,\n totalChanges: 0,\n };\n\n for (const migration of path) {\n if (!options.dryRun) {\n try {\n const result = await migration.apply(model);\n\n results.applied.push({\n from: migration.fromVersion,\n to: migration.toVersion,\n description: migration.description,\n changes: result,\n });\n\n results.totalChanges += result.migrationsApplied;\n\n // Only update model's spec version if migration succeeded (no error field)\n if (!result.error) {\n model.manifest.specVersion = migration.toVersion;\n } else {\n // If migration has errors, throw to prevent partial success state\n throw new Error(result.error);\n }\n } catch (error) {\n throw new Error(\n `Migration ${migration.fromVersion} \u2192 ${migration.toVersion} failed: ${\n getErrorMessage(error)\n }`\n );\n }\n } else {\n // Dry run - just record what would happen\n results.applied.push({\n from: migration.fromVersion,\n to: migration.toVersion,\n description: migration.description,\n dryRun: true,\n });\n }\n }\n\n return results;\n }\n\n /**\n * Get a summary of migrations that would be applied\n */\n getMigrationSummary(\n fromVersion: string,\n toVersion?: string\n ): {\n currentVersion: string;\n targetVersion: string;\n migrationsNeeded: number;\n migrations: Array<{\n from: string;\n to: string;\n description: string;\n }>;\n } {\n const path = this.getMigrationPath(fromVersion, toVersion);\n const targetVersion = toVersion || this.getLatestVersion();\n\n return {\n currentVersion: fromVersion,\n targetVersion,\n migrationsNeeded: path.length,\n migrations: path.map((m) => ({\n from: m.fromVersion,\n to: m.toVersion,\n description: m.description,\n })),\n };\n }\n\n /**\n * Compare two version strings\n * Returns: -1 if a < b, 0 if a == b, 1 if a > b\n */\n private compareVersions(a: string, b: string): number {\n const aParts = a.split(\".\").map(Number);\n const bParts = b.split(\".\").map(Number);\n\n const maxLength = Math.max(aParts.length, bParts.length);\n\n for (let i = 0; i < maxLength; i++) {\n const aPart = aParts[i] || 0;\n const bPart = bParts[i] || 0;\n\n if (aPart < bPart) return -1;\n if (aPart > bPart) return 1;\n }\n\n return 0;\n }\n}\n\n/**\n * Result of applying multiple migrations\n */\nexport interface ApplyMigrationsResult {\n applied: Array<{\n from: string;\n to: string;\n description: string;\n changes?: MigrationResult;\n dryRun?: boolean;\n }>;\n currentVersion: string;\n targetVersion: string;\n totalChanges: number;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/core/model.js
CHANGED
|
@@ -10,6 +10,8 @@ import { ensureDir, writeFile } from "../utils/file-io.js";
|
|
|
10
10
|
import { getCliVersion } from "../utils/spec-version.js";
|
|
11
11
|
import { startSpan, endSpan, startActiveSpan } from "../telemetry/index.js";
|
|
12
12
|
import { findProjectRoot } from "../utils/project-paths.js";
|
|
13
|
+
import { getCodebasePath } from "../utils/globals.js";
|
|
14
|
+
import { getErrorMessage } from "../utils/errors.js";
|
|
13
15
|
import { getNodeType } from "../generated/node-types.js";
|
|
14
16
|
import path from "node:path";
|
|
15
17
|
import fs from "node:fs/promises";
|
|
@@ -49,11 +51,13 @@ function isPersistableAttribute(specNodeId, key) {
|
|
|
49
51
|
const isTelemetryEnabled = !1;
|
|
50
52
|
class Model {
|
|
51
53
|
rootPath;
|
|
54
|
+
codebaseRoot;
|
|
52
55
|
manifest;
|
|
53
56
|
graph;
|
|
54
57
|
layers;
|
|
55
58
|
relationships;
|
|
56
59
|
lazyLoad;
|
|
60
|
+
loadedLayerFilter;
|
|
57
61
|
loadedLayers;
|
|
58
62
|
virtualProjectionEngine;
|
|
59
63
|
/** ID of the currently active changeset, or null if none is active. Set during load(). */
|
|
@@ -61,7 +65,7 @@ class Model {
|
|
|
61
65
|
/** Layer names that exist only in staged changes (not yet committed to base model). */
|
|
62
66
|
_stagedLayerNames = /* @__PURE__ */ new Set();
|
|
63
67
|
constructor(rootPath, manifest, options = {}) {
|
|
64
|
-
this.rootPath = rootPath, this.manifest = manifest, this.graph = new GraphModel(), this.layers = /* @__PURE__ */ new Map(), this.relationships = new Relationships(), this.lazyLoad = options.lazyLoad ?? !1, this.loadedLayers = /* @__PURE__ */ new Set();
|
|
68
|
+
this.rootPath = rootPath, this.codebaseRoot = options.codebaseRoot ?? rootPath, this.manifest = manifest, this.graph = new GraphModel(), this.layers = /* @__PURE__ */ new Map(), this.relationships = new Relationships(), this.lazyLoad = options.lazyLoad ?? !1, this.loadedLayerFilter = options.layers, this.loadedLayers = /* @__PURE__ */ new Set();
|
|
65
69
|
}
|
|
66
70
|
/**
|
|
67
71
|
* Get the committed base layer (no staging projection).
|
|
@@ -114,12 +118,20 @@ class Model {
|
|
|
114
118
|
async loadLayer(name) {
|
|
115
119
|
await startActiveSpan("layer.load", async (layerSpan) => {
|
|
116
120
|
const fs2 = await import("fs/promises"), yaml = await import("yaml");
|
|
117
|
-
let layerPath = null
|
|
118
|
-
const modelDir = `${this.rootPath}/documentation-robotics/model`;
|
|
121
|
+
let layerPath = null, modelDir = `${this.rootPath}/documentation-robotics/model`;
|
|
119
122
|
try {
|
|
120
123
|
const layerDir = (await fs2.readdir(modelDir, { withFileTypes: !0 })).find((e) => e.isDirectory() && e.name.match(/^\d{2}_/) && e.name.replace(/^\d{2}_/, "") === name);
|
|
121
124
|
layerDir && (layerPath = `${modelDir}/${layerDir.name}`);
|
|
122
|
-
} catch {
|
|
125
|
+
} catch (err) {
|
|
126
|
+
if (!(err?.code === "ENOENT"))
|
|
127
|
+
throw err;
|
|
128
|
+
try {
|
|
129
|
+
const layerDir = (await fs2.readdir(this.rootPath, { withFileTypes: !0 })).find((e) => e.isDirectory() && e.name.match(/^\d{2}_/) && e.name.replace(/^\d{2}_/, "") === name);
|
|
130
|
+
layerDir && (layerPath = `${this.rootPath}/${layerDir.name}`);
|
|
131
|
+
} catch (err2) {
|
|
132
|
+
if (!(err2?.code === "ENOENT"))
|
|
133
|
+
throw err2;
|
|
134
|
+
}
|
|
123
135
|
}
|
|
124
136
|
if (!layerPath)
|
|
125
137
|
return;
|
|
@@ -224,7 +236,16 @@ class Model {
|
|
|
224
236
|
try {
|
|
225
237
|
const layerDir = (await fs2.readdir(modelDir, { withFileTypes: !0 })).find((e) => e.isDirectory() && e.name.match(/^\d{2}_/) && e.name.replace(/^\d{2}_/, "") === name);
|
|
226
238
|
layerDir && (layerPath = `${modelDir}/${layerDir.name}`);
|
|
227
|
-
} catch {
|
|
239
|
+
} catch (err) {
|
|
240
|
+
if (!(err?.code === "ENOENT"))
|
|
241
|
+
throw err;
|
|
242
|
+
try {
|
|
243
|
+
const layerDir = (await fs2.readdir(this.rootPath, { withFileTypes: !0 })).find((e) => e.isDirectory() && e.name.match(/^\d{2}_/) && e.name.replace(/^\d{2}_/, "") === name);
|
|
244
|
+
layerDir && (layerPath = `${this.rootPath}/${layerDir.name}`);
|
|
245
|
+
} catch (err2) {
|
|
246
|
+
if (!(err2?.code === "ENOENT"))
|
|
247
|
+
throw err2;
|
|
248
|
+
}
|
|
228
249
|
}
|
|
229
250
|
if (!layerPath) {
|
|
230
251
|
const layerNameWithoutPrefix = name.replace(/^\d{2}-/, ""), index = CANONICAL_LAYER_NAMES.indexOf(layerNameWithoutPrefix);
|
|
@@ -328,7 +349,7 @@ class Model {
|
|
|
328
349
|
...layer && { elements: this.getLayerElementCounts(layer) }
|
|
329
350
|
};
|
|
330
351
|
}
|
|
331
|
-
this.manifest.changeset_history && this.manifest.changeset_history.length > 0 && (yamlData.changeset_history = this.manifest.changeset_history), await ensureDir(`${this.rootPath}/documentation-robotics/model`), await writeFile(manifestPath, yaml.stringify(yamlData));
|
|
352
|
+
this.manifest.codebase_path && (yamlData.codebase_path = this.manifest.codebase_path), this.manifest.changeset_history && this.manifest.changeset_history.length > 0 && (yamlData.changeset_history = this.manifest.changeset_history), this.manifest.models && Object.keys(this.manifest.models).length > 0 && (yamlData.models = this.manifest.models), await ensureDir(`${this.rootPath}/documentation-robotics/model`), await writeFile(manifestPath, yaml.stringify(yamlData));
|
|
332
353
|
}
|
|
333
354
|
getLayerElementCounts(layer) {
|
|
334
355
|
const counts = {};
|
|
@@ -350,8 +371,8 @@ class Model {
|
|
|
350
371
|
throw new Error(`Failed to load relationships.yaml: ${err instanceof Error ? err.message : String(err)}`);
|
|
351
372
|
return;
|
|
352
373
|
}
|
|
353
|
-
if (!Array.isArray(data)) {
|
|
354
|
-
console.warn(`Warning: relationships.yaml contains non-array content (received ${
|
|
374
|
+
if (data === null && (data = []), !Array.isArray(data)) {
|
|
375
|
+
console.warn(`Warning: relationships.yaml contains non-array content (received ${typeof data}). Expected an array of relationships. Skipping relationship loading.`);
|
|
355
376
|
return;
|
|
356
377
|
}
|
|
357
378
|
this.relationships = Relationships.fromArray(data);
|
|
@@ -383,6 +404,58 @@ class Model {
|
|
|
383
404
|
` + yaml.stringify(data);
|
|
384
405
|
await fs2.writeFile(relationshipsPath, yamlContent, "utf-8"), this.relationships.markClean();
|
|
385
406
|
}
|
|
407
|
+
/**
|
|
408
|
+
* Auto-resolve codebaseRoot from farm manifest if inside a farm
|
|
409
|
+
*
|
|
410
|
+
* @param modelRoot - The model's project root (documentation_robotics parent)
|
|
411
|
+
* @returns Resolved codebase root path, or null if not in farm or resolution fails
|
|
412
|
+
*/
|
|
413
|
+
static async resolveFarmCodebaseRoot(modelRoot) {
|
|
414
|
+
const { findFarmRoot } = await import("../utils/project-paths.js"), { FarmManifest } = await import("./farm-manifest.js"), farmRoot = await findFarmRoot(modelRoot);
|
|
415
|
+
if (!farmRoot)
|
|
416
|
+
return null;
|
|
417
|
+
const farmYamlPath = path.join(farmRoot, "farm.yaml");
|
|
418
|
+
let manifest;
|
|
419
|
+
try {
|
|
420
|
+
manifest = await FarmManifest.load(farmYamlPath);
|
|
421
|
+
} catch (error) {
|
|
422
|
+
const errorMsg = getErrorMessage(error);
|
|
423
|
+
if (errorMsg.includes("ENOENT") || errorMsg.includes("not found"))
|
|
424
|
+
return null;
|
|
425
|
+
throw new Error(`Failed to load farm manifest at ${farmYamlPath}: ${errorMsg}`);
|
|
426
|
+
}
|
|
427
|
+
const modelName = path.basename(modelRoot);
|
|
428
|
+
for (const project of manifest.getAllProjects())
|
|
429
|
+
if (path.basename(project.model) === modelName)
|
|
430
|
+
return path.resolve(farmRoot, project.source);
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Walk upward from startPath looking for documentation-robotics/model/manifest.yaml,
|
|
435
|
+
* stopping at the nearest .git directory (repo boundary) so this can never cross into
|
|
436
|
+
* an unrelated parent project or a sibling farm/detached model directory.
|
|
437
|
+
*
|
|
438
|
+
* @param startPath - Directory to start the walk from
|
|
439
|
+
* @returns Manifest path if found within the repo boundary, or null
|
|
440
|
+
*/
|
|
441
|
+
static async findManifestUpwardBounded(startPath) {
|
|
442
|
+
let currentPath = startPath;
|
|
443
|
+
for (; ; ) {
|
|
444
|
+
const candidateManifestPath = path.join(currentPath, "documentation-robotics", "model", "manifest.yaml");
|
|
445
|
+
try {
|
|
446
|
+
return await fs.access(candidateManifestPath), candidateManifestPath;
|
|
447
|
+
} catch {
|
|
448
|
+
}
|
|
449
|
+
try {
|
|
450
|
+
return await fs.access(path.join(currentPath, ".git")), null;
|
|
451
|
+
} catch {
|
|
452
|
+
}
|
|
453
|
+
const parentPath = path.dirname(currentPath);
|
|
454
|
+
if (parentPath === currentPath)
|
|
455
|
+
return null;
|
|
456
|
+
currentPath = parentPath;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
386
459
|
/**
|
|
387
460
|
* Resolve model paths (private helper)
|
|
388
461
|
*
|
|
@@ -390,18 +463,52 @@ class Model {
|
|
|
390
463
|
* @returns Project root and manifest path
|
|
391
464
|
*/
|
|
392
465
|
static async resolveModelPaths(startPath) {
|
|
393
|
-
|
|
466
|
+
if (startPath) {
|
|
467
|
+
const resolvedPath = path.resolve(startPath);
|
|
468
|
+
let manifestPath2;
|
|
469
|
+
resolvedPath.endsWith("manifest.yaml") ? manifestPath2 = resolvedPath : resolvedPath.endsWith("model") ? manifestPath2 = path.join(resolvedPath, "manifest.yaml") : manifestPath2 = path.join(resolvedPath, "documentation-robotics", "model", "manifest.yaml");
|
|
470
|
+
try {
|
|
471
|
+
await fs.access(manifestPath2);
|
|
472
|
+
let projectRoot2;
|
|
473
|
+
const manifestParentDir = path.dirname(manifestPath2), standardProjectRoot = path.dirname(path.dirname(path.dirname(manifestPath2))), standardDocRobotsPath = path.join(standardProjectRoot, "documentation-robotics", "model");
|
|
474
|
+
try {
|
|
475
|
+
await fs.access(standardDocRobotsPath), projectRoot2 = standardProjectRoot;
|
|
476
|
+
} catch (err) {
|
|
477
|
+
if (!(err?.code === "ENOENT"))
|
|
478
|
+
throw err;
|
|
479
|
+
projectRoot2 = manifestParentDir;
|
|
480
|
+
}
|
|
481
|
+
return { projectRoot: projectRoot2, manifestPath: path.normalize(manifestPath2) };
|
|
482
|
+
} catch (err) {
|
|
483
|
+
if (!(err?.code === "ENOENT"))
|
|
484
|
+
throw err;
|
|
485
|
+
const fallbackManifestPath = await Model.findManifestUpwardBounded(resolvedPath);
|
|
486
|
+
if (fallbackManifestPath)
|
|
487
|
+
return { projectRoot: path.dirname(path.dirname(path.dirname(fallbackManifestPath))), manifestPath: path.normalize(fallbackManifestPath) };
|
|
488
|
+
throw new Error(`Model not found at ${startPath}`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
394
491
|
if (process.env.DR_MODEL_PATH) {
|
|
395
492
|
const envPath = path.resolve(process.env.DR_MODEL_PATH);
|
|
396
493
|
let manifestPath2;
|
|
397
494
|
envPath.endsWith("manifest.yaml") ? manifestPath2 = envPath : envPath.endsWith("model") ? manifestPath2 = path.join(envPath, "manifest.yaml") : manifestPath2 = path.join(envPath, "documentation-robotics", "model", "manifest.yaml");
|
|
398
495
|
try {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
496
|
+
await fs.access(manifestPath2);
|
|
497
|
+
let projectRoot2;
|
|
498
|
+
const manifestParentDir = path.dirname(manifestPath2), standardProjectRoot = path.dirname(path.dirname(path.dirname(manifestPath2))), standardDocRobotsPath = path.join(standardProjectRoot, "documentation-robotics", "model");
|
|
499
|
+
try {
|
|
500
|
+
await fs.access(standardDocRobotsPath), projectRoot2 = standardProjectRoot;
|
|
501
|
+
} catch (err) {
|
|
502
|
+
if (!(err?.code === "ENOENT"))
|
|
503
|
+
throw err;
|
|
504
|
+
projectRoot2 = manifestParentDir;
|
|
505
|
+
}
|
|
506
|
+
return { projectRoot: projectRoot2, manifestPath: path.normalize(manifestPath2) };
|
|
507
|
+
} catch (err) {
|
|
508
|
+
throw err?.code === "ENOENT" ? new Error(`Model not found at DR_MODEL_PATH: ${process.env.DR_MODEL_PATH}`) : err;
|
|
402
509
|
}
|
|
403
510
|
}
|
|
404
|
-
const projectRoot = await findProjectRoot(searchPath);
|
|
511
|
+
const searchPath = process.cwd(), projectRoot = await findProjectRoot(searchPath);
|
|
405
512
|
if (!projectRoot)
|
|
406
513
|
throw new Error(`No DR project found. Could not find documentation_robotics/ folder.
|
|
407
514
|
Run "dr init" to create a new project, or navigate to a directory containing a DR project.`);
|
|
@@ -413,6 +520,21 @@ Run "dr init" to create a new project, or navigate to a directory containing a D
|
|
|
413
520
|
Run "dr init" to create a model.`);
|
|
414
521
|
}
|
|
415
522
|
}
|
|
523
|
+
/**
|
|
524
|
+
* Validate that the manifest's models field is a plain object.
|
|
525
|
+
*
|
|
526
|
+
* @param models - The raw value from manifest YAML
|
|
527
|
+
* @returns Validation result with valid flag and error message if invalid
|
|
528
|
+
*/
|
|
529
|
+
static validateModelsField(models) {
|
|
530
|
+
return models == null ? { valid: !0 } : typeof models != "object" || Array.isArray(models) ? {
|
|
531
|
+
valid: !1,
|
|
532
|
+
error: `Invalid manifest 'models' field: expected an object with model names as keys (e.g., { "auth-service": { } }), but got ${Array.isArray(models) ? "an array" : `a ${typeof models}`}. This often happens when YAML syntax is incorrect, like writing 'models: auth-service' instead of 'models: { auth-service: {} }' or 'models:\\n auth-service: {}'.`
|
|
533
|
+
} : Object.getPrototypeOf(models) !== Object.prototype ? {
|
|
534
|
+
valid: !1,
|
|
535
|
+
error: `Invalid manifest 'models' field: expected a plain object, but got an instance of ${models.constructor?.name || "unknown"}. Please ensure 'models' is a simple YAML object mapping model names to declarations.`
|
|
536
|
+
} : { valid: !0 };
|
|
537
|
+
}
|
|
416
538
|
/**
|
|
417
539
|
* Load a model from disk
|
|
418
540
|
*
|
|
@@ -432,10 +554,34 @@ Run "dr init" to create a model.`);
|
|
|
432
554
|
name: manifestYaml.project?.name || "Unnamed Model",
|
|
433
555
|
description: manifestYaml.project?.description || "",
|
|
434
556
|
version: manifestYaml.project?.version || manifestYaml.version || "0.1.0",
|
|
557
|
+
cliVersion: manifestYaml.cli_version,
|
|
435
558
|
specVersion: manifestYaml.spec_version || "0.6.0",
|
|
436
559
|
created: manifestYaml.created || (/* @__PURE__ */ new Date()).toISOString(),
|
|
437
560
|
modified: manifestYaml.updated || (/* @__PURE__ */ new Date()).toISOString()
|
|
438
|
-
}
|
|
561
|
+
};
|
|
562
|
+
if (manifestYaml.models) {
|
|
563
|
+
const modelsValidation = Model.validateModelsField(manifestYaml.models);
|
|
564
|
+
if (!modelsValidation.valid)
|
|
565
|
+
throw new Error(modelsValidation.error);
|
|
566
|
+
manifestData.models = manifestYaml.models;
|
|
567
|
+
}
|
|
568
|
+
manifestYaml.codebase_path && (manifestData.codebase_path = manifestYaml.codebase_path);
|
|
569
|
+
const manifest = new Manifest(manifestData);
|
|
570
|
+
let codebaseRoot = projectRoot;
|
|
571
|
+
if (options.codebaseRoot)
|
|
572
|
+
codebaseRoot = options.codebaseRoot;
|
|
573
|
+
else {
|
|
574
|
+
const globalCodebasePath = getCodebasePath();
|
|
575
|
+
if (globalCodebasePath)
|
|
576
|
+
codebaseRoot = path.resolve(globalCodebasePath);
|
|
577
|
+
else if (manifest.codebase_path)
|
|
578
|
+
codebaseRoot = path.resolve(projectRoot, manifest.codebase_path);
|
|
579
|
+
else {
|
|
580
|
+
const farmCodebaseRoot = await Model.resolveFarmCodebaseRoot(projectRoot);
|
|
581
|
+
farmCodebaseRoot && (codebaseRoot = farmCodebaseRoot);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
const model = new Model(projectRoot, manifest, { ...options, codebaseRoot });
|
|
439
585
|
if (!options.lazyLoad)
|
|
440
586
|
try {
|
|
441
587
|
if (manifestYaml.layers)
|
|
@@ -496,13 +642,28 @@ Run "dr init" to create a model.`);
|
|
|
496
642
|
* Initialize a new model in a directory
|
|
497
643
|
*/
|
|
498
644
|
static async init(rootPath, manifestData, options = {}) {
|
|
645
|
+
const yaml = await import("yaml");
|
|
499
646
|
await ensureDir(`${rootPath}/documentation-robotics/model`);
|
|
500
647
|
for (let i = 0; i < CANONICAL_LAYER_NAMES.length; i++) {
|
|
501
648
|
const orderNum = String(i + 1).padStart(2, "0"), layerName = CANONICAL_LAYER_NAMES[i];
|
|
502
649
|
await ensureDir(`${rootPath}/documentation-robotics/model/${orderNum}_${layerName}`);
|
|
503
650
|
}
|
|
504
|
-
const manifest = new Manifest(manifestData)
|
|
505
|
-
|
|
651
|
+
const manifest = new Manifest(manifestData);
|
|
652
|
+
let codebaseRoot = rootPath;
|
|
653
|
+
if (options.codebaseRoot)
|
|
654
|
+
codebaseRoot = options.codebaseRoot;
|
|
655
|
+
else {
|
|
656
|
+
const globalCodebasePath = getCodebasePath();
|
|
657
|
+
globalCodebasePath ? codebaseRoot = path.resolve(globalCodebasePath) : manifest.codebase_path && (codebaseRoot = path.resolve(rootPath, manifest.codebase_path));
|
|
658
|
+
}
|
|
659
|
+
const model = new Model(rootPath, manifest, { ...options, codebaseRoot });
|
|
660
|
+
await model.saveManifest();
|
|
661
|
+
const relationshipsPath = `${rootPath}/documentation-robotics/model/relationships.yaml`, yamlContent = `# Intra-layer relationships
|
|
662
|
+
# Format: source_id -> predicate -> target_id
|
|
663
|
+
# Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
664
|
+
|
|
665
|
+
` + yaml.stringify([]);
|
|
666
|
+
return await writeFile(relationshipsPath, yamlContent), model;
|
|
506
667
|
}
|
|
507
668
|
/**
|
|
508
669
|
* Graph-based API methods for querying the model
|