@documentation-robotics/cli 0.1.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.
Files changed (169) hide show
  1. package/PYTHON_CLI_PARITY.md +265 -0
  2. package/PYTHON_CLI_STRUCTURE_UPDATE.md +246 -0
  3. package/README.md +926 -0
  4. package/biome.json +42 -0
  5. package/bunfig.toml +3 -0
  6. package/dist/ai/claude-client.js +71 -0
  7. package/dist/ai/claude-client.js.map +7 -0
  8. package/dist/ai/context-provider.js +129 -0
  9. package/dist/ai/context-provider.js.map +7 -0
  10. package/dist/ai/tools.js +231 -0
  11. package/dist/ai/tools.js.map +7 -0
  12. package/dist/cli.js +224 -0
  13. package/dist/cli.js.map +7 -0
  14. package/dist/commands/add.js +51 -0
  15. package/dist/commands/add.js.map +7 -0
  16. package/dist/commands/changeset.js +174 -0
  17. package/dist/commands/changeset.js.map +7 -0
  18. package/dist/commands/chat.js +111 -0
  19. package/dist/commands/chat.js.map +7 -0
  20. package/dist/commands/conformance.js +132 -0
  21. package/dist/commands/conformance.js.map +7 -0
  22. package/dist/commands/delete.js +31 -0
  23. package/dist/commands/delete.js.map +7 -0
  24. package/dist/commands/element.js +28 -0
  25. package/dist/commands/element.js.map +7 -0
  26. package/dist/commands/export.js +65 -0
  27. package/dist/commands/export.js.map +7 -0
  28. package/dist/commands/info.js +40 -0
  29. package/dist/commands/info.js.map +7 -0
  30. package/dist/commands/init.js +45 -0
  31. package/dist/commands/init.js.map +7 -0
  32. package/dist/commands/list.js +32 -0
  33. package/dist/commands/list.js.map +7 -0
  34. package/dist/commands/migrate.js +36 -0
  35. package/dist/commands/migrate.js.map +7 -0
  36. package/dist/commands/project.js +74 -0
  37. package/dist/commands/project.js.map +7 -0
  38. package/dist/commands/relationship.js +118 -0
  39. package/dist/commands/relationship.js.map +7 -0
  40. package/dist/commands/search.js +48 -0
  41. package/dist/commands/search.js.map +7 -0
  42. package/dist/commands/show.js +38 -0
  43. package/dist/commands/show.js.map +7 -0
  44. package/dist/commands/trace.js +60 -0
  45. package/dist/commands/trace.js.map +7 -0
  46. package/dist/commands/update.js +37 -0
  47. package/dist/commands/update.js.map +7 -0
  48. package/dist/commands/upgrade.js +152 -0
  49. package/dist/commands/upgrade.js.map +7 -0
  50. package/dist/commands/validate.js +71 -0
  51. package/dist/commands/validate.js.map +7 -0
  52. package/dist/commands/version.js +20 -0
  53. package/dist/commands/version.js.map +7 -0
  54. package/dist/commands/visualize.js +51 -0
  55. package/dist/commands/visualize.js.map +7 -0
  56. package/dist/core/active-changeset.js +52 -0
  57. package/dist/core/active-changeset.js.map +7 -0
  58. package/dist/core/changeset.js +244 -0
  59. package/dist/core/changeset.js.map +7 -0
  60. package/dist/core/dependency-tracker.js +322 -0
  61. package/dist/core/dependency-tracker.js.map +7 -0
  62. package/dist/core/element.js +65 -0
  63. package/dist/core/element.js.map +7 -0
  64. package/dist/core/layer.js +75 -0
  65. package/dist/core/layer.js.map +7 -0
  66. package/dist/core/manifest.js +57 -0
  67. package/dist/core/manifest.js.map +7 -0
  68. package/dist/core/migration-registry.js +131 -0
  69. package/dist/core/migration-registry.js.map +7 -0
  70. package/dist/core/model.js +455 -0
  71. package/dist/core/model.js.map +7 -0
  72. package/dist/core/projection-engine.js +288 -0
  73. package/dist/core/projection-engine.js.map +7 -0
  74. package/dist/core/reference-registry.js +188 -0
  75. package/dist/core/reference-registry.js.map +7 -0
  76. package/dist/core/relationship-registry.js +114 -0
  77. package/dist/core/relationship-registry.js.map +7 -0
  78. package/dist/core/relationships.js +78 -0
  79. package/dist/core/relationships.js.map +7 -0
  80. package/dist/export/archimate-exporter.js +111 -0
  81. package/dist/export/archimate-exporter.js.map +7 -0
  82. package/dist/export/export-manager.js +63 -0
  83. package/dist/export/export-manager.js.map +7 -0
  84. package/dist/export/graphml-exporter.js +39 -0
  85. package/dist/export/graphml-exporter.js.map +7 -0
  86. package/dist/export/index.js +17 -0
  87. package/dist/export/index.js.map +7 -0
  88. package/dist/export/json-schema-exporter.js +60 -0
  89. package/dist/export/json-schema-exporter.js.map +7 -0
  90. package/dist/export/markdown-exporter.js +99 -0
  91. package/dist/export/markdown-exporter.js.map +7 -0
  92. package/dist/export/openapi-exporter.js +85 -0
  93. package/dist/export/openapi-exporter.js.map +7 -0
  94. package/dist/export/plantuml-exporter.js +60 -0
  95. package/dist/export/plantuml-exporter.js.map +7 -0
  96. package/dist/export/types.js +36 -0
  97. package/dist/export/types.js.map +7 -0
  98. package/dist/schemas/bundled/01-motivation-layer.schema.json +942 -0
  99. package/dist/schemas/bundled/02-business-layer.schema.json +1293 -0
  100. package/dist/schemas/bundled/03-security-layer.schema.json +2177 -0
  101. package/dist/schemas/bundled/04-application-layer.schema.json +1328 -0
  102. package/dist/schemas/bundled/05-technology-layer.schema.json +1465 -0
  103. package/dist/schemas/bundled/06-api-layer.schema.json +1743 -0
  104. package/dist/schemas/bundled/07-data-model-layer.schema.json +1253 -0
  105. package/dist/schemas/bundled/08-datastore-layer.schema.json +892 -0
  106. package/dist/schemas/bundled/09-ux-layer.schema.json +1506 -0
  107. package/dist/schemas/bundled/10-navigation-layer.schema.json +843 -0
  108. package/dist/schemas/bundled/11-apm-observability-layer.schema.json +1430 -0
  109. package/dist/schemas/bundled/12-testing-layer.schema.json +1091 -0
  110. package/dist/schemas/bundled/common/layer-extensions.schema.json +266 -0
  111. package/dist/schemas/bundled/common/predicates.schema.json +493 -0
  112. package/dist/schemas/bundled/common/relationships.schema.json +791 -0
  113. package/dist/schemas/bundled/common/source-references.schema.json +71 -0
  114. package/dist/schemas/bundled/link-registry.json +1839 -0
  115. package/dist/schemas/bundled/link-registry.schema.json +339 -0
  116. package/dist/schemas/bundled/relationship-catalog.json +1302 -0
  117. package/dist/schemas/bundled/relationship-type.schema.json +221 -0
  118. package/dist/server/server.js +1207 -0
  119. package/dist/server/server.js.map +7 -0
  120. package/dist/server/telemetry-middleware.js +24 -0
  121. package/dist/server/telemetry-middleware.js.map +7 -0
  122. package/dist/telemetry/console-interceptor.js +34 -0
  123. package/dist/telemetry/console-interceptor.js.map +7 -0
  124. package/dist/telemetry/index.js +54 -0
  125. package/dist/telemetry/index.js.map +7 -0
  126. package/dist/telemetry/resilient-exporter.js +41 -0
  127. package/dist/telemetry/resilient-exporter.js.map +7 -0
  128. package/dist/telemetry/resilient-log-exporter.js +41 -0
  129. package/dist/telemetry/resilient-log-exporter.js.map +7 -0
  130. package/dist/telemetry/test-instrumentation.js +32 -0
  131. package/dist/telemetry/test-instrumentation.js.map +7 -0
  132. package/dist/types/index.js +1 -0
  133. package/dist/types/index.js.map +7 -0
  134. package/dist/utils/element-utils.js +11 -0
  135. package/dist/utils/element-utils.js.map +7 -0
  136. package/dist/utils/errors.js +105 -0
  137. package/dist/utils/errors.js.map +7 -0
  138. package/dist/utils/file-io.js +69 -0
  139. package/dist/utils/file-io.js.map +7 -0
  140. package/dist/utils/globals.js +28 -0
  141. package/dist/utils/globals.js.map +7 -0
  142. package/dist/utils/model-path.js +22 -0
  143. package/dist/utils/model-path.js.map +7 -0
  144. package/dist/utils/project-paths.js +45 -0
  145. package/dist/utils/project-paths.js.map +7 -0
  146. package/dist/utils/spec-installer.js +85 -0
  147. package/dist/utils/spec-installer.js.map +7 -0
  148. package/dist/utils/spec-version.js +33 -0
  149. package/dist/utils/spec-version.js.map +7 -0
  150. package/dist/validators/index.js +15 -0
  151. package/dist/validators/index.js.map +7 -0
  152. package/dist/validators/naming-validator.js +82 -0
  153. package/dist/validators/naming-validator.js.map +7 -0
  154. package/dist/validators/reference-validator.js +106 -0
  155. package/dist/validators/reference-validator.js.map +7 -0
  156. package/dist/validators/schema-validator.js +148 -0
  157. package/dist/validators/schema-validator.js.map +7 -0
  158. package/dist/validators/semantic-validator.js +80 -0
  159. package/dist/validators/semantic-validator.js.map +7 -0
  160. package/dist/validators/types.js +48 -0
  161. package/dist/validators/types.js.map +7 -0
  162. package/dist/validators/validator.js +67 -0
  163. package/dist/validators/validator.js.map +7 -0
  164. package/docs/TEST-INSTRUMENTATION.md +405 -0
  165. package/docs/telemetry.md +427 -0
  166. package/package.json +70 -0
  167. package/test-debug.js +33 -0
  168. package/test-special.mjs +46 -0
  169. package/test_runner.ts +7 -0
@@ -0,0 +1,37 @@
1
+ import ansis from "ansis";
2
+ import { Model } from "../core/model.js";
3
+ import { findElementLayer } from "../utils/element-utils.js";
4
+ import { startSpan, endSpan } from "../telemetry/index.js";
5
+ const isTelemetryEnabled = !1;
6
+ async function updateCommand(id, options) {
7
+ const changedFields = [];
8
+ options.name && changedFields.push("name"), options.description && changedFields.push("description"), options.properties && changedFields.push("properties");
9
+ const span = isTelemetryEnabled ? startSpan("element.update", {
10
+ "element.id": id,
11
+ "element.changed_fields": changedFields.join(",")
12
+ }) : null;
13
+ try {
14
+ const model = await Model.load(), layerName = await findElementLayer(model, id);
15
+ layerName || (console.error(ansis.red(`Error: Element ${id} not found`)), process.exit(1));
16
+ const element = (await model.getLayer(layerName)).getElement(id), beforeState = element.toJSON();
17
+ let updated = !1;
18
+ if (options.name && (element.name = options.name, updated = !0), options.description !== void 0 && (element.description = options.description || void 0, updated = !0), options.properties)
19
+ try {
20
+ const newProperties = JSON.parse(options.properties);
21
+ element.properties = { ...element.properties, ...newProperties }, updated = !0;
22
+ } catch {
23
+ console.error(ansis.red("Error: Invalid JSON in --properties")), process.exit(1);
24
+ }
25
+ updated || (console.log(ansis.yellow("No fields specified for update")), process.exit(0)), await model.getActiveChangesetContext().trackChange("update", id, layerName, beforeState, element.toJSON()), await model.saveLayer(layerName), await model.saveManifest(), console.log(ansis.green(`\u2713 Updated element ${ansis.bold(id)}`)), options.verbose && (console.log(ansis.dim(` Layer: ${layerName}`)), options.name && console.log(ansis.dim(` Name: ${options.name}`)), options.description && console.log(ansis.dim(` Description: ${options.description}`)));
26
+ } catch (error) {
27
+ isTelemetryEnabled && span && (span.recordException(error), span.setStatus({ code: 2, message: error.message }));
28
+ const message = error instanceof Error ? error.message : String(error);
29
+ console.error(ansis.red(`Error: ${message}`)), process.exit(1);
30
+ } finally {
31
+ isTelemetryEnabled && endSpan(span);
32
+ }
33
+ }
34
+ export {
35
+ updateCommand
36
+ };
37
+ //# sourceMappingURL=update.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/commands/update.ts"],
4
+ "sourcesContent": ["/**\n * Update an element\n */\n\nimport ansis from 'ansis';\nimport { Model } from '../core/model.js';\nimport { findElementLayer } from '../utils/element-utils.js';\nimport { startSpan, endSpan } from '../telemetry/index.js';\n\ndeclare const TELEMETRY_ENABLED: boolean | undefined;\nconst isTelemetryEnabled = typeof TELEMETRY_ENABLED !== 'undefined' ? TELEMETRY_ENABLED : false;\n\nexport interface UpdateOptions {\n name?: string;\n description?: string;\n properties?: string;\n verbose?: boolean;\n debug?: boolean;\n}\n\nexport async function updateCommand(id: string, options: UpdateOptions): Promise<void> {\n const changedFields: string[] = [];\n if (options.name) changedFields.push('name');\n if (options.description) changedFields.push('description');\n if (options.properties) changedFields.push('properties');\n\n const span = isTelemetryEnabled ? startSpan('element.update', {\n 'element.id': id,\n 'element.changed_fields': changedFields.join(','),\n }) : null;\n\n try {\n // Load model\n const model = await Model.load();\n\n // Find element\n const layerName = await findElementLayer(model, id);\n if (!layerName) {\n console.error(ansis.red(`Error: Element ${id} not found`));\n process.exit(1);\n }\n\n const layer = (await model.getLayer(layerName))!;\n const element = layer.getElement(id)!;\n\n // Capture before state for changeset tracking\n const beforeState = element.toJSON();\n\n // Update fields\n let updated = false;\n\n if (options.name) {\n element.name = options.name;\n updated = true;\n }\n\n if (options.description !== undefined) {\n element.description = options.description || undefined;\n updated = true;\n }\n\n if (options.properties) {\n try {\n const newProperties = JSON.parse(options.properties);\n element.properties = { ...element.properties, ...newProperties };\n updated = true;\n } catch (e) {\n console.error(ansis.red('Error: Invalid JSON in --properties'));\n process.exit(1);\n }\n }\n\n if (!updated) {\n console.log(ansis.yellow('No fields specified for update'));\n process.exit(0);\n }\n\n // Track change in active changeset if present\n const activeChangeset = model.getActiveChangesetContext();\n await activeChangeset.trackChange(\n 'update',\n id,\n layerName,\n beforeState as unknown as Record<string, unknown>,\n element.toJSON() as unknown as Record<string, unknown>\n );\n\n // Save\n await model.saveLayer(layerName);\n await model.saveManifest();\n\n console.log(ansis.green(`\u2713 Updated element ${ansis.bold(id)}`));\n if (options.verbose) {\n console.log(ansis.dim(` Layer: ${layerName}`));\n if (options.name) {\n console.log(ansis.dim(` Name: ${options.name}`));\n }\n if (options.description) {\n console.log(ansis.dim(` Description: ${options.description}`));\n }\n }\n } catch (error) {\n if (isTelemetryEnabled && span) {\n (span as any).recordException(error as Error);\n (span as any).setStatus({ code: 2, message: (error as Error).message });\n }\n const message = error instanceof Error ? error.message : String(error);\n console.error(ansis.red(`Error: ${message}`));\n process.exit(1);\n } finally {\n if (isTelemetryEnabled) {\n endSpan(span);\n }\n }\n}\n"],
5
+ "mappings": "AAIA,OAAO,WAAW;AAClB,SAAS,aAAa;AACtB,SAAS,wBAAwB;AACjC,SAAS,WAAW,eAAe;AAGnC,MAAM,qBAAgE;AAUtE,eAAsB,cAAc,IAAY,SAAsB;AACpE,QAAM,gBAA0B,CAAA;AAChC,EAAI,QAAQ,QAAM,cAAc,KAAK,MAAM,GACvC,QAAQ,eAAa,cAAc,KAAK,aAAa,GACrD,QAAQ,cAAY,cAAc,KAAK,YAAY;AAEvD,QAAM,OAAO,qBAAqB,UAAU,kBAAkB;IAC5D,cAAc;IACd,0BAA0B,cAAc,KAAK,GAAG;GACjD,IAAI;AAEL,MAAI;AAEF,UAAM,QAAQ,MAAM,MAAM,KAAI,GAGxB,YAAY,MAAM,iBAAiB,OAAO,EAAE;AAClD,IAAK,cACH,QAAQ,MAAM,MAAM,IAAI,kBAAkB,EAAE,YAAY,CAAC,GACzD,QAAQ,KAAK,CAAC;AAIhB,UAAM,WADS,MAAM,MAAM,SAAS,SAAS,GACvB,WAAW,EAAE,GAG7B,cAAc,QAAQ,OAAM;AAGlC,QAAI,UAAU;AAYd,QAVI,QAAQ,SACV,QAAQ,OAAO,QAAQ,MACvB,UAAU,KAGR,QAAQ,gBAAgB,WAC1B,QAAQ,cAAc,QAAQ,eAAe,QAC7C,UAAU,KAGR,QAAQ;AACV,UAAI;AACF,cAAM,gBAAgB,KAAK,MAAM,QAAQ,UAAU;AACnD,gBAAQ,aAAa,EAAE,GAAG,QAAQ,YAAY,GAAG,cAAa,GAC9D,UAAU;MACZ,QAAY;AACV,gBAAQ,MAAM,MAAM,IAAI,qCAAqC,CAAC,GAC9D,QAAQ,KAAK,CAAC;MAChB;AAGF,IAAK,YACH,QAAQ,IAAI,MAAM,OAAO,gCAAgC,CAAC,GAC1D,QAAQ,KAAK,CAAC,IAKhB,MADwB,MAAM,0BAAyB,EACjC,YACpB,UACA,IACA,WACA,aACA,QAAQ,OAAM,CAAwC,GAIxD,MAAM,MAAM,UAAU,SAAS,GAC/B,MAAM,MAAM,aAAY,GAExB,QAAQ,IAAI,MAAM,MAAM,0BAAqB,MAAM,KAAK,EAAE,CAAC,EAAE,CAAC,GAC1D,QAAQ,YACV,QAAQ,IAAI,MAAM,IAAI,YAAY,SAAS,EAAE,CAAC,GAC1C,QAAQ,QACV,QAAQ,IAAI,MAAM,IAAI,WAAW,QAAQ,IAAI,EAAE,CAAC,GAE9C,QAAQ,eACV,QAAQ,IAAI,MAAM,IAAI,kBAAkB,QAAQ,WAAW,EAAE,CAAC;EAGpE,SAAS,OAAO;AACd,IAAI,sBAAsB,SACvB,KAAa,gBAAgB,KAAc,GAC3C,KAAa,UAAU,EAAE,MAAM,GAAG,SAAU,MAAgB,QAAO,CAAE;AAExE,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,YAAQ,MAAM,MAAM,IAAI,UAAU,OAAO,EAAE,CAAC,GAC5C,QAAQ,KAAK,CAAC;EAChB;AACE,IAAI,sBACF,QAAQ,IAAI;EAEhB;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,152 @@
1
+ import ansis from "ansis";
2
+ import { confirm } from "@clack/prompts";
3
+ import { findProjectRoot, getSpecReferencePath, getModelPath } from "../utils/project-paths.js";
4
+ import { getCliBundledSpecVersion, getInstalledSpecVersion, getModelSpecVersion } from "../utils/spec-version.js";
5
+ import { MigrationRegistry } from "../core/migration-registry.js";
6
+ import { installSpecReference } from "../utils/spec-installer.js";
7
+ import { Model } from "../core/model.js";
8
+ async function upgradeCommand(options = {}) {
9
+ try {
10
+ console.log(ansis.bold(`
11
+ Scanning for available upgrades...
12
+ `));
13
+ const projectRoot = await findProjectRoot();
14
+ projectRoot || (console.error(ansis.red("Error: No DR project found")), console.error(ansis.dim('Run "dr init" to create a new project')), process.exit(1));
15
+ const bundledSpecVersion = getCliBundledSpecVersion(), actions = [], drPath = await getSpecReferencePath();
16
+ let currentSpecVersion = null;
17
+ if (!drPath)
18
+ actions.push({
19
+ type: "spec",
20
+ description: "Install spec reference",
21
+ toVersion: bundledSpecVersion,
22
+ details: [
23
+ "Create .dr/ folder",
24
+ "Install schema files",
25
+ `Set spec version to ${bundledSpecVersion}`
26
+ ]
27
+ });
28
+ else {
29
+ const installedSpecVersion = await getInstalledSpecVersion(drPath);
30
+ currentSpecVersion = installedSpecVersion, installedSpecVersion ? installedSpecVersion !== bundledSpecVersion && actions.push({
31
+ type: "spec",
32
+ description: "Upgrade spec reference",
33
+ fromVersion: installedSpecVersion,
34
+ toVersion: bundledSpecVersion,
35
+ details: [
36
+ "Update schema files",
37
+ "Update .dr/manifest.json"
38
+ ]
39
+ }) : actions.push({
40
+ type: "spec",
41
+ description: "Reinstall spec reference (manifest missing)",
42
+ toVersion: bundledSpecVersion,
43
+ details: [
44
+ "Recreate .dr/manifest.json",
45
+ "Update schema files",
46
+ `Set spec version to ${bundledSpecVersion}`
47
+ ]
48
+ });
49
+ }
50
+ const modelPath = await getModelPath();
51
+ if (!modelPath) {
52
+ console.log(ansis.yellow("\u26A0 No model found")), console.log(ansis.dim(` Run "dr init" to create a model
53
+ `)), actions.length > 0 && await handleUpgrade(projectRoot, actions, options);
54
+ return;
55
+ }
56
+ const modelSpecVersion = await getModelSpecVersion(modelPath);
57
+ if (!modelSpecVersion) {
58
+ console.log(ansis.yellow("\u26A0 Model manifest.yaml not found or missing specVersion")), console.log(ansis.dim(` Check documentation_robotics/model/manifest.yaml
59
+ `)), actions.length > 0 && await handleUpgrade(projectRoot, actions, options);
60
+ return;
61
+ }
62
+ const targetSpecVersion = currentSpecVersion || bundledSpecVersion;
63
+ if (modelSpecVersion !== targetSpecVersion) {
64
+ const registry = new MigrationRegistry(), summary = registry.getMigrationSummary(modelSpecVersion, targetSpecVersion);
65
+ if (summary.migrationsNeeded === 0) {
66
+ console.log(ansis.yellow(`\u26A0 No migration path found from model v${modelSpecVersion} to v${targetSpecVersion}`)), console.log(ansis.dim(`
67
+ Available migrations:`));
68
+ const allMigrations = registry.getMigrationSummary("0.5.0").migrations;
69
+ for (const migration of allMigrations)
70
+ console.log(ansis.dim(` \u2022 ${migration.from} \u2192 ${migration.to}: ${migration.description}`));
71
+ console.log(), process.exit(1);
72
+ }
73
+ const migrationDetails = summary.migrations.map((m) => `${m.from} \u2192 ${m.to}: ${m.description}`);
74
+ actions.push({
75
+ type: "model",
76
+ description: "Migrate model data",
77
+ fromVersion: modelSpecVersion,
78
+ toVersion: targetSpecVersion,
79
+ details: migrationDetails
80
+ });
81
+ }
82
+ if (actions.length === 0) {
83
+ console.log(ansis.green(`\u2713 Everything is up to date!
84
+ `)), console.log(ansis.dim(` Spec reference: v${currentSpecVersion || bundledSpecVersion}`)), console.log(ansis.dim(` Model: v${modelSpecVersion}
85
+ `));
86
+ return;
87
+ }
88
+ await handleUpgrade(projectRoot, actions, options);
89
+ } catch (error) {
90
+ console.error(ansis.red(`Error: ${error instanceof Error ? error.message : String(error)}`)), process.exit(1);
91
+ }
92
+ }
93
+ async function handleUpgrade(projectRoot, actions, options) {
94
+ console.log(ansis.bold(`Upgrade Plan:
95
+ `));
96
+ for (const action of actions) {
97
+ const versionChange = action.fromVersion ? `${action.fromVersion} \u2192 ${action.toVersion}` : `v${action.toVersion}`;
98
+ if (console.log(ansis.yellow(`${action.type === "spec" ? "\u{1F4E6}" : "\u{1F504}"} ${action.description}`)), console.log(ansis.dim(` Version: ${versionChange}`)), action.details && action.details.length > 0)
99
+ for (const detail of action.details)
100
+ console.log(ansis.dim(` \u2022 ${detail}`));
101
+ console.log();
102
+ }
103
+ if (options.dryRun) {
104
+ console.log(ansis.yellow(`[DRY RUN] No changes will be made
105
+ `));
106
+ return;
107
+ }
108
+ let shouldProceed = options.yes;
109
+ if (shouldProceed || (shouldProceed = await confirm({
110
+ message: "Proceed with upgrade?"
111
+ }) === !0), !shouldProceed) {
112
+ console.log(ansis.dim(`
113
+ Upgrade cancelled
114
+ `));
115
+ return;
116
+ }
117
+ console.log(ansis.bold(`
118
+ Executing upgrades...
119
+ `));
120
+ for (const action of actions)
121
+ action.type === "spec" ? await executeSpecUpgrade(projectRoot, action) : action.type === "model" && await executeModelMigration(action, options);
122
+ console.log(ansis.green(`
123
+ \u2713 All upgrades completed successfully!
124
+ `));
125
+ }
126
+ async function executeSpecUpgrade(projectRoot, action) {
127
+ try {
128
+ console.log(ansis.dim(`Installing spec reference v${action.toVersion}...`)), await installSpecReference(projectRoot, !0), console.log(ansis.green(`\u2713 Spec reference upgraded to v${action.toVersion}`));
129
+ } catch (error) {
130
+ throw console.error(ansis.red(`Error upgrading spec reference: ${error instanceof Error ? error.message : String(error)}`)), error;
131
+ }
132
+ }
133
+ async function executeModelMigration(action, options) {
134
+ try {
135
+ console.log(ansis.dim(`Migrating model from v${action.fromVersion} to v${action.toVersion}...`));
136
+ const model = await Model.load(process.cwd(), { lazyLoad: !1 }), result = await new MigrationRegistry().applyMigrations(model, {
137
+ fromVersion: action.fromVersion,
138
+ toVersion: action.toVersion,
139
+ dryRun: !1,
140
+ validate: !options.force
141
+ });
142
+ for (const applied of result.applied)
143
+ console.log(ansis.green(` \u2713 ${applied.from} \u2192 ${applied.to}: ${applied.description}`)), applied.changes?.filesModified && console.log(ansis.dim(` Files modified: ${applied.changes.filesModified}`));
144
+ await model.saveManifest(), await model.saveDirtyLayers(), console.log(ansis.green(`\u2713 Model migrated to v${action.toVersion}`));
145
+ } catch (error) {
146
+ throw console.error(ansis.red(`Error migrating model: ${error instanceof Error ? error.message : String(error)}`)), error;
147
+ }
148
+ }
149
+ export {
150
+ upgradeCommand
151
+ };
152
+ //# sourceMappingURL=upgrade.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/commands/upgrade.ts"],
4
+ "sourcesContent": ["/**\n * Upgrade Command - Manages spec reference and model upgrades\n *\n * Scans the filesystem to determine what needs upgrading:\n * 1. .dr/ (spec reference) - if CLI has newer bundled version\n * 2. Model data - if model spec version doesn't match spec reference\n *\n * Shows upgrade plan and prompts for confirmation before proceeding.\n */\n\nimport ansis from 'ansis';\nimport { confirm } from '@clack/prompts';\nimport { findProjectRoot, getSpecReferencePath, getModelPath } from '../utils/project-paths.js';\nimport {\n getCliBundledSpecVersion,\n getInstalledSpecVersion,\n getModelSpecVersion,\n} from '../utils/spec-version.js';\nimport { MigrationRegistry } from '../core/migration-registry.js';\nimport { installSpecReference } from '../utils/spec-installer.js';\nimport { Model } from '../core/model.js';\n\nexport interface UpgradeOptions {\n yes?: boolean;\n dryRun?: boolean;\n force?: boolean;\n}\n\ninterface UpgradeAction {\n type: 'spec' | 'model';\n description: string;\n fromVersion?: string;\n toVersion: string;\n details?: string[];\n}\n\nexport async function upgradeCommand(options: UpgradeOptions = {}): Promise<void> {\n try {\n console.log(ansis.bold('\\nScanning for available upgrades...\\n'));\n\n // Find project root\n const projectRoot = await findProjectRoot();\n if (!projectRoot) {\n console.error(ansis.red('Error: No DR project found'));\n console.error(ansis.dim('Run \"dr init\" to create a new project'));\n process.exit(1);\n }\n\n const bundledSpecVersion = getCliBundledSpecVersion();\n const actions: UpgradeAction[] = [];\n\n // ============================================================================\n // STEP 1: Check spec reference (.dr/ folder)\n // ============================================================================\n\n const drPath = await getSpecReferencePath();\n let currentSpecVersion: string | null = null;\n\n if (!drPath) {\n actions.push({\n type: 'spec',\n description: 'Install spec reference',\n toVersion: bundledSpecVersion,\n details: [\n 'Create .dr/ folder',\n 'Install schema files',\n `Set spec version to ${bundledSpecVersion}`,\n ],\n });\n } else {\n const installedSpecVersion = await getInstalledSpecVersion(drPath);\n currentSpecVersion = installedSpecVersion;\n\n if (!installedSpecVersion) {\n actions.push({\n type: 'spec',\n description: 'Reinstall spec reference (manifest missing)',\n toVersion: bundledSpecVersion,\n details: [\n 'Recreate .dr/manifest.json',\n 'Update schema files',\n `Set spec version to ${bundledSpecVersion}`,\n ],\n });\n } else if (installedSpecVersion !== bundledSpecVersion) {\n actions.push({\n type: 'spec',\n description: 'Upgrade spec reference',\n fromVersion: installedSpecVersion,\n toVersion: bundledSpecVersion,\n details: [\n 'Update schema files',\n 'Update .dr/manifest.json',\n ],\n });\n }\n }\n\n // ============================================================================\n // STEP 2: Check model upgrade\n // ============================================================================\n\n const modelPath = await getModelPath();\n if (!modelPath) {\n console.log(ansis.yellow('\u26A0 No model found'));\n console.log(ansis.dim(' Run \"dr init\" to create a model\\n'));\n\n // If only spec needs upgrade, handle it\n if (actions.length > 0) {\n await handleUpgrade(projectRoot, actions, options);\n }\n return;\n }\n\n const modelSpecVersion = await getModelSpecVersion(modelPath);\n if (!modelSpecVersion) {\n console.log(ansis.yellow('\u26A0 Model manifest.yaml not found or missing specVersion'));\n console.log(ansis.dim(' Check documentation_robotics/model/manifest.yaml\\n'));\n\n // If only spec needs upgrade, handle it\n if (actions.length > 0) {\n await handleUpgrade(projectRoot, actions, options);\n }\n return;\n }\n\n // Determine target spec version for model\n const targetSpecVersion = currentSpecVersion || bundledSpecVersion;\n\n if (modelSpecVersion !== targetSpecVersion) {\n // Check if we have a migration path\n const registry = new MigrationRegistry();\n const summary = registry.getMigrationSummary(modelSpecVersion, targetSpecVersion);\n\n if (summary.migrationsNeeded === 0) {\n console.log(\n ansis.yellow(\n `\u26A0 No migration path found from model v${modelSpecVersion} to v${targetSpecVersion}`\n )\n );\n console.log(ansis.dim('\\nAvailable migrations:'));\n const allMigrations = registry.getMigrationSummary('0.5.0').migrations;\n for (const migration of allMigrations) {\n console.log(ansis.dim(` \u2022 ${migration.from} \u2192 ${migration.to}: ${migration.description}`));\n }\n console.log();\n process.exit(1);\n }\n\n const migrationDetails = summary.migrations.map(\n (m) => `${m.from} \u2192 ${m.to}: ${m.description}`\n );\n\n actions.push({\n type: 'model',\n description: 'Migrate model data',\n fromVersion: modelSpecVersion,\n toVersion: targetSpecVersion,\n details: migrationDetails,\n });\n }\n\n // ============================================================================\n // STEP 3: Display upgrade plan and execute\n // ============================================================================\n\n if (actions.length === 0) {\n console.log(ansis.green('\u2713 Everything is up to date!\\n'));\n console.log(ansis.dim(` Spec reference: v${currentSpecVersion || bundledSpecVersion}`));\n console.log(ansis.dim(` Model: v${modelSpecVersion}\\n`));\n return;\n }\n\n await handleUpgrade(projectRoot, actions, options);\n } catch (error) {\n console.error(\n ansis.red(`Error: ${error instanceof Error ? error.message : String(error)}`)\n );\n process.exit(1);\n }\n}\n\n/**\n * Handle the upgrade process\n */\nasync function handleUpgrade(\n projectRoot: string,\n actions: UpgradeAction[],\n options: UpgradeOptions\n): Promise<void> {\n // Display upgrade plan\n console.log(ansis.bold('Upgrade Plan:\\n'));\n\n for (const action of actions) {\n const versionChange = action.fromVersion\n ? `${action.fromVersion} \u2192 ${action.toVersion}`\n : `v${action.toVersion}`;\n\n console.log(ansis.yellow(`${action.type === 'spec' ? '\uD83D\uDCE6' : '\uD83D\uDD04'} ${action.description}`));\n console.log(ansis.dim(` Version: ${versionChange}`));\n\n if (action.details && action.details.length > 0) {\n for (const detail of action.details) {\n console.log(ansis.dim(` \u2022 ${detail}`));\n }\n }\n console.log();\n }\n\n // Handle dry-run mode\n if (options.dryRun) {\n console.log(ansis.yellow('[DRY RUN] No changes will be made\\n'));\n return;\n }\n\n // Prompt for confirmation unless --yes flag is set\n let shouldProceed = options.yes;\n if (!shouldProceed) {\n const response = await confirm({\n message: 'Proceed with upgrade?',\n });\n shouldProceed = response === true;\n }\n\n if (!shouldProceed) {\n console.log(ansis.dim('\\nUpgrade cancelled\\n'));\n return;\n }\n\n console.log(ansis.bold('\\nExecuting upgrades...\\n'));\n\n // Execute actions in order: spec first, then model\n for (const action of actions) {\n if (action.type === 'spec') {\n await executeSpecUpgrade(projectRoot, action);\n } else if (action.type === 'model') {\n await executeModelMigration(action, options);\n }\n }\n\n console.log(ansis.green('\\n\u2713 All upgrades completed successfully!\\n'));\n}\n\n/**\n * Execute spec reference upgrade\n */\nasync function executeSpecUpgrade(projectRoot: string, action: UpgradeAction): Promise<void> {\n try {\n console.log(ansis.dim(`Installing spec reference v${action.toVersion}...`));\n await installSpecReference(projectRoot, true);\n console.log(ansis.green(`\u2713 Spec reference upgraded to v${action.toVersion}`));\n } catch (error) {\n console.error(\n ansis.red(\n `Error upgrading spec reference: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n throw error;\n }\n}\n\n/**\n * Execute model migration\n */\nasync function executeModelMigration(action: UpgradeAction, options: UpgradeOptions): Promise<void> {\n try {\n console.log(ansis.dim(`Migrating model from v${action.fromVersion} to v${action.toVersion}...`));\n\n // Load the model\n const model = await Model.load(process.cwd(), { lazyLoad: false });\n\n // Apply migrations\n const registry = new MigrationRegistry();\n const result = await registry.applyMigrations(model, {\n fromVersion: action.fromVersion!,\n toVersion: action.toVersion,\n dryRun: false,\n validate: !options.force,\n });\n\n // Display migration results\n for (const applied of result.applied) {\n console.log(ansis.green(` \u2713 ${applied.from} \u2192 ${applied.to}: ${applied.description}`));\n if (applied.changes?.filesModified) {\n console.log(ansis.dim(` Files modified: ${applied.changes.filesModified}`));\n }\n }\n\n // Save changes\n await model.saveManifest();\n await model.saveDirtyLayers();\n\n console.log(ansis.green(`\u2713 Model migrated to v${action.toVersion}`));\n } catch (error) {\n console.error(\n ansis.red(`Error migrating model: ${error instanceof Error ? error.message : String(error)}`)\n );\n throw error;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,71 @@
1
+ import ansis from "ansis";
2
+ import { promises as fs } from "fs";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+ import { Model } from "../core/model.js";
6
+ import { Validator } from "../validators/validator.js";
7
+ async function validateSchemaSynchronization() {
8
+ console.log(""), console.log(ansis.bold("Validating schema synchronization...")), console.log("");
9
+ const __filename = fileURLToPath(import.meta.url), __dirname = path.dirname(__filename), projectRoot = path.resolve(__dirname, "../../.."), specSchemaDir = path.join(projectRoot, "spec", "schemas"), bundledSchemaDir = path.join(__dirname, "../schemas/bundled"), specSchemaFiles = await fs.readdir(specSchemaDir), bundledSchemaFiles = await fs.readdir(bundledSchemaDir), schemaFiles = specSchemaFiles.filter((f) => f.endsWith(".json"));
10
+ if (schemaFiles.length === 0)
11
+ throw new Error("No schema files found in spec/schemas/");
12
+ let mismatches = [];
13
+ for (const schemaFile of schemaFiles) {
14
+ const specPath = path.join(specSchemaDir, schemaFile), bundledPath = path.join(bundledSchemaDir, schemaFile);
15
+ try {
16
+ const specContent = await fs.readFile(specPath, "utf-8");
17
+ try {
18
+ const bundledContent = await fs.readFile(bundledPath, "utf-8"), specParsed = JSON.parse(specContent), bundledParsed = JSON.parse(bundledContent), specNormalized = JSON.stringify(specParsed, null, 2), bundledNormalized = JSON.stringify(bundledParsed, null, 2);
19
+ specNormalized !== bundledNormalized ? mismatches.push(` ${ansis.red("\u2717")} ${schemaFile} - Content differs between spec/ and cli/src/schemas/bundled/`) : console.log(` ${ansis.green("\u2713")} ${schemaFile}`);
20
+ } catch (error) {
21
+ if (error.code === "ENOENT")
22
+ mismatches.push(` ${ansis.red("\u2717")} ${schemaFile} - Missing in cli/src/schemas/bundled/`);
23
+ else
24
+ throw error;
25
+ }
26
+ } catch (error) {
27
+ const message = error instanceof Error ? error.message : String(error);
28
+ mismatches.push(` ${ansis.red("\u2717")} ${schemaFile} - Error reading spec schema: ${message}`);
29
+ }
30
+ }
31
+ for (const bundledFile of bundledSchemaFiles)
32
+ bundledFile.endsWith(".json") && !schemaFiles.includes(bundledFile) && mismatches.push(` ${ansis.red("\u2717")} ${bundledFile} - Extra file in cli/src/schemas/bundled/ (not in spec/)`);
33
+ if (mismatches.length > 0) {
34
+ console.log(""), console.log(ansis.bold(ansis.red(`\u2717 Schema synchronization failed with ${mismatches.length} issue(s):`))), console.log("");
35
+ for (const mismatch of mismatches)
36
+ console.log(mismatch);
37
+ throw console.log(""), console.log(ansis.dim("To fix: Ensure spec/schemas/*.json matches cli/src/schemas/bundled/*.json")), console.log(""), new Error(`Schema synchronization failed with ${mismatches.length} issue(s)`);
38
+ } else
39
+ console.log(""), console.log(ansis.green("\u2713 All schemas synchronized")), console.log("");
40
+ }
41
+ async function validateCommand(options) {
42
+ try {
43
+ if (options.schemas || options.schema) {
44
+ await validateSchemaSynchronization();
45
+ return;
46
+ }
47
+ const model = await Model.load(options.model), validator = new Validator();
48
+ console.log(""), console.log(ansis.bold("Validating model...")), console.log("");
49
+ const result = await validator.validateModel(model);
50
+ if (result.errors.length > 0) {
51
+ console.log(ansis.bold(ansis.red(`\u2717 ${result.errors.length} error(s):`))), console.log("");
52
+ for (const error of result.errors)
53
+ console.log(ansis.red(` [${error.layer}] ${error.message}`)), error.elementId && console.log(ansis.dim(` Element: ${error.elementId}`)), error.fixSuggestion && console.log(ansis.dim(` \u2192 Suggestion: ${error.fixSuggestion}`));
54
+ console.log("");
55
+ }
56
+ if (result.warnings.length > 0) {
57
+ console.log(ansis.bold(ansis.yellow(`\u26A0 ${result.warnings.length} warning(s):`))), console.log("");
58
+ for (const warning of result.warnings)
59
+ console.log(ansis.yellow(` [${warning.layer}] ${warning.message}`)), warning.elementId && console.log(ansis.dim(` Element: ${warning.elementId}`)), warning.fixSuggestion && console.log(ansis.dim(` \u2192 Suggestion: ${warning.fixSuggestion}`));
60
+ console.log("");
61
+ }
62
+ result.isValid() ? result.warnings.length === 0 ? (console.log(ansis.green("\u2713 Validation passed")), console.log(""), process.exit(0)) : (console.log(ansis.yellow(`\u2713 Validation passed with ${result.warnings.length} warning(s)`)), console.log(""), options.strict ? (console.log(ansis.red("Strict mode enabled: treating warnings as errors")), process.exit(1)) : process.exit(0)) : (console.log(ansis.red(`\u2717 Validation failed with ${result.errors.length} error(s)`)), console.log(""), process.exit(1));
63
+ } catch (error) {
64
+ const message = error instanceof Error ? error.message : String(error);
65
+ console.error(ansis.red(`Error: ${message}`)), process.env.DEBUG && console.error(error.stack), process.exit(1);
66
+ }
67
+ }
68
+ export {
69
+ validateCommand
70
+ };
71
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/commands/validate.ts"],
4
+ "sourcesContent": ["/**\n * Validate the architecture model\n */\n\nimport ansis from 'ansis';\nimport { promises as fs } from 'fs';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport { Model } from '../core/model.js';\nimport { Validator } from '../validators/validator.js';\n\nexport interface ValidateOptions {\n layers?: string[];\n strict?: boolean;\n verbose?: boolean;\n debug?: boolean;\n model?: string;\n // Python CLI compatibility options\n all?: boolean;\n markdown?: boolean;\n schemas?: boolean;\n schema?: boolean; // Alias for schemas\n relationships?: boolean;\n structure?: boolean;\n naming?: boolean;\n references?: boolean;\n}\n\n/**\n * Validate schema synchronization between spec/ and cli/src/schemas/bundled/\n * @throws {Error} if schema synchronization validation fails\n */\nasync function validateSchemaSynchronization(): Promise<void> {\n console.log('');\n console.log(ansis.bold('Validating schema synchronization...'));\n console.log('');\n\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n\n // Resolve paths relative to project root\n const projectRoot = path.resolve(__dirname, '../../..');\n const specSchemaDir = path.join(projectRoot, 'spec', 'schemas');\n const bundledSchemaDir = path.join(__dirname, '../schemas/bundled');\n\n const specSchemaFiles = await fs.readdir(specSchemaDir);\n const bundledSchemaFiles = await fs.readdir(bundledSchemaDir);\n\n const schemaFiles = specSchemaFiles.filter(f => f.endsWith('.json'));\n\n if (schemaFiles.length === 0) {\n throw new Error('No schema files found in spec/schemas/');\n }\n\n let mismatches: string[] = [];\n\n for (const schemaFile of schemaFiles) {\n const specPath = path.join(specSchemaDir, schemaFile);\n const bundledPath = path.join(bundledSchemaDir, schemaFile);\n\n try {\n const specContent = await fs.readFile(specPath, 'utf-8');\n\n try {\n const bundledContent = await fs.readFile(bundledPath, 'utf-8');\n\n // Normalize JSON to account for formatting differences\n const specParsed = JSON.parse(specContent);\n const bundledParsed = JSON.parse(bundledContent);\n const specNormalized = JSON.stringify(specParsed, null, 2);\n const bundledNormalized = JSON.stringify(bundledParsed, null, 2);\n\n if (specNormalized !== bundledNormalized) {\n mismatches.push(\n ` ${ansis.red('\u2717')} ${schemaFile} - Content differs between spec/ and cli/src/schemas/bundled/`\n );\n } else {\n console.log(` ${ansis.green('\u2713')} ${schemaFile}`);\n }\n } catch (error) {\n if ((error as any).code === 'ENOENT') {\n mismatches.push(\n ` ${ansis.red('\u2717')} ${schemaFile} - Missing in cli/src/schemas/bundled/`\n );\n } else {\n throw error;\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n mismatches.push(` ${ansis.red('\u2717')} ${schemaFile} - Error reading spec schema: ${message}`);\n }\n }\n\n // Check for extra files in bundled that aren't in spec\n for (const bundledFile of bundledSchemaFiles) {\n if (bundledFile.endsWith('.json') && !schemaFiles.includes(bundledFile)) {\n mismatches.push(\n ` ${ansis.red('\u2717')} ${bundledFile} - Extra file in cli/src/schemas/bundled/ (not in spec/)`\n );\n }\n }\n\n if (mismatches.length > 0) {\n console.log('');\n console.log(ansis.bold(ansis.red(`\u2717 Schema synchronization failed with ${mismatches.length} issue(s):`)));\n console.log('');\n for (const mismatch of mismatches) {\n console.log(mismatch);\n }\n console.log('');\n console.log(ansis.dim('To fix: Ensure spec/schemas/*.json matches cli/src/schemas/bundled/*.json'));\n console.log('');\n throw new Error(`Schema synchronization failed with ${mismatches.length} issue(s)`);\n } else {\n console.log('');\n console.log(ansis.green('\u2713 All schemas synchronized'));\n console.log('');\n }\n}\n\nexport async function validateCommand(options: ValidateOptions): Promise<void> {\n try {\n // Handle schema validation flag\n if (options.schemas || options.schema) {\n await validateSchemaSynchronization();\n return;\n }\n\n // Load model\n const model = await Model.load(options.model);\n\n // Validate\n const validator = new Validator();\n\n console.log('');\n console.log(ansis.bold('Validating model...'));\n console.log('');\n\n const result = await validator.validateModel(model);\n\n // Display errors\n if (result.errors.length > 0) {\n console.log(ansis.bold(ansis.red(`\u2717 ${result.errors.length} error(s):`)));\n console.log('');\n for (const error of result.errors) {\n console.log(ansis.red(` [${error.layer}] ${error.message}`));\n if (error.elementId) {\n console.log(ansis.dim(` Element: ${error.elementId}`));\n }\n if (error.fixSuggestion) {\n console.log(ansis.dim(` \u2192 Suggestion: ${error.fixSuggestion}`));\n }\n }\n console.log('');\n }\n\n // Display warnings\n if (result.warnings.length > 0) {\n console.log(ansis.bold(ansis.yellow(`\u26A0 ${result.warnings.length} warning(s):`)));\n console.log('');\n for (const warning of result.warnings) {\n console.log(ansis.yellow(` [${warning.layer}] ${warning.message}`));\n if (warning.elementId) {\n console.log(ansis.dim(` Element: ${warning.elementId}`));\n }\n if (warning.fixSuggestion) {\n console.log(ansis.dim(` \u2192 Suggestion: ${warning.fixSuggestion}`));\n }\n }\n console.log('');\n }\n\n // Display summary\n if (result.isValid()) {\n if (result.warnings.length === 0) {\n console.log(ansis.green('\u2713 Validation passed'));\n console.log('');\n process.exit(0);\n } else {\n console.log(\n ansis.yellow(\n `\u2713 Validation passed with ${result.warnings.length} warning(s)`\n )\n );\n console.log('');\n if (options.strict) {\n console.log(ansis.red('Strict mode enabled: treating warnings as errors'));\n process.exit(1);\n } else {\n process.exit(0);\n }\n }\n } else {\n console.log(\n ansis.red(`\u2717 Validation failed with ${result.errors.length} error(s)`)\n );\n console.log('');\n process.exit(1);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(ansis.red(`Error: ${message}`));\n if (process.env.DEBUG) {\n console.error((error as Error).stack);\n }\n process.exit(1);\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,20 @@
1
+ import ansis from "ansis";
2
+ import { readJSON, fileExists } from "../utils/file-io.js";
3
+ import { getCliBundledSpecVersion } from "../utils/spec-version.js";
4
+ async function getPackageVersion() {
5
+ const packagePath = `${import.meta.url.replace("file://", "").split("/src/")[0]}/package.json`;
6
+ if (await fileExists(packagePath))
7
+ try {
8
+ return (await readJSON(packagePath)).version;
9
+ } catch {
10
+ }
11
+ return "0.1.0";
12
+ }
13
+ async function versionCommand() {
14
+ const cliVersion = await getPackageVersion(), specVersion = getCliBundledSpecVersion();
15
+ console.log(ansis.bold("Documentation Robotics CLI")), console.log(`CLI Version: ${ansis.cyan(cliVersion)}`), console.log(`Spec Version: ${ansis.cyan(specVersion)}`);
16
+ }
17
+ export {
18
+ versionCommand
19
+ };
20
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/commands/version.ts"],
4
+ "sourcesContent": ["/**\n * Version Command - Display CLI and bundled spec versions\n */\n\nimport ansis from 'ansis';\nimport { readJSON, fileExists } from '../utils/file-io.js';\nimport { getCliBundledSpecVersion } from '../utils/spec-version.js';\n\n/**\n * Read CLI version from package.json\n */\nasync function getPackageVersion(): Promise<string> {\n const packagePath = `${import.meta.url.replace('file://', '').split('/src/')[0]}/package.json`;\n\n if (await fileExists(packagePath)) {\n try {\n const data = await readJSON<{ version: string }>(packagePath);\n return data.version;\n } catch {\n // Fall through to default\n }\n }\n\n return '0.1.0';\n}\n\nexport async function versionCommand(): Promise<void> {\n const cliVersion = await getPackageVersion();\n const specVersion = getCliBundledSpecVersion();\n\n console.log(ansis.bold('Documentation Robotics CLI'));\n console.log(`CLI Version: ${ansis.cyan(cliVersion)}`);\n console.log(`Spec Version: ${ansis.cyan(specVersion)}`);\n}\n"],
5
+ "mappings": "AAIA,OAAO,WAAW;AAClB,SAAS,UAAU,kBAAkB;AACrC,SAAS,gCAAgC;AAKzC,eAAe,oBAAiB;AAC9B,QAAM,cAAc,GAAG,YAAY,IAAI,QAAQ,WAAW,EAAE,EAAE,MAAM,OAAO,EAAE,CAAC,CAAC;AAE/E,MAAI,MAAM,WAAW,WAAW;AAC9B,QAAI;AAEF,cADa,MAAM,SAA8B,WAAW,GAChD;IACd,QAAQ;IAER;AAGF,SAAO;AACT;AAEA,eAAsB,iBAAc;AAClC,QAAM,aAAa,MAAM,kBAAiB,GACpC,cAAc,yBAAwB;AAE5C,UAAQ,IAAI,MAAM,KAAK,4BAA4B,CAAC,GACpD,QAAQ,IAAI,iBAAiB,MAAM,KAAK,UAAU,CAAC,EAAE,GACrD,QAAQ,IAAI,iBAAiB,MAAM,KAAK,WAAW,CAAC,EAAE;AACxD;",
6
+ "names": []
7
+ }
@@ -0,0 +1,51 @@
1
+ import ansis from "ansis";
2
+ import { Model } from "../core/model.js";
3
+ import { VisualizationServer } from "../server/server.js";
4
+ import { logVerbose, logDebug } from "../utils/globals.js";
5
+ async function visualizeCommand(options) {
6
+ try {
7
+ logDebug("Loading model for visualization...");
8
+ const model = await Model.load(process.cwd(), { lazyLoad: !1 });
9
+ logVerbose(`Model loaded: ${model.manifest.name}`);
10
+ let port = 8080;
11
+ if (options.port) {
12
+ const portNum = parseInt(String(options.port), 10);
13
+ if (isNaN(portNum) || portNum < 1 || portNum > 65535)
14
+ throw new Error(`Invalid port number: ${options.port}. Port must be between 1 and 65535.`);
15
+ port = portNum;
16
+ }
17
+ const authEnabled = !options.noAuth, authToken = options.token, server = new VisualizationServer(model, { authEnabled, authToken });
18
+ if (logDebug(`Starting visualization server on port ${port}`), logDebug(`Authentication: ${authEnabled ? "enabled" : "disabled"}`), authEnabled && authToken ? logDebug("Using provided token for authentication") : authEnabled && logDebug("Token will be auto-generated"), await server.start(port), console.log(ansis.green("\u2713 Visualization server started")), authEnabled) {
19
+ const token = server.getAuthToken();
20
+ console.log(ansis.cyan(` Access URL: http://localhost:${port}?token=${token}`)), console.log(ansis.dim(` Auth token: ${token}`));
21
+ } else
22
+ console.log(ansis.dim(` Open http://localhost:${port} in your browser`)), console.log(ansis.yellow(" \u26A0 Authentication disabled"));
23
+ logVerbose(` Model: ${model.manifest.name} (${model.getLayerNames().length} layers)`);
24
+ const shouldOpenBrowser = !options.noBrowser;
25
+ if (logDebug(`Browser auto-open: ${shouldOpenBrowser ? "enabled" : "disabled"} (noBrowser=${options.noBrowser})`), shouldOpenBrowser)
26
+ try {
27
+ const command = getOpenCommand(), url = authEnabled ? `http://localhost:${port}?token=${server.getAuthToken()}` : `http://localhost:${port}`;
28
+ logDebug(`Opening browser with command: ${command} ${url}`), Bun.spawn([command, url], {
29
+ stdout: "ignore",
30
+ stderr: "ignore"
31
+ });
32
+ } catch {
33
+ logVerbose("Could not auto-open browser (not critical)");
34
+ }
35
+ else
36
+ logDebug("Browser auto-open disabled");
37
+ await new Promise(() => {
38
+ });
39
+ } catch (error) {
40
+ const message = error instanceof Error ? error.message : String(error);
41
+ console.error(ansis.red(`Error: ${message}`)), logDebug(`Stack: ${error instanceof Error ? error.stack : "N/A"}`), process.exit(1);
42
+ }
43
+ }
44
+ function getOpenCommand() {
45
+ const platform = process.platform;
46
+ return platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
47
+ }
48
+ export {
49
+ visualizeCommand
50
+ };
51
+ //# sourceMappingURL=visualize.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/commands/visualize.ts"],
4
+ "sourcesContent": ["/**\n * Visualize command - Launch visualization server\n */\n\nimport ansis from 'ansis';\nimport { Model } from '../core/model.js';\nimport { VisualizationServer } from '../server/server.js';\nimport { logVerbose, logDebug } from '../utils/globals.js';\n\nexport interface VisualizeOptions {\n port?: number;\n noBrowser?: boolean;\n noAuth?: boolean;\n token?: string;\n verbose?: boolean;\n debug?: boolean;\n}\n\nexport async function visualizeCommand(\n options: VisualizeOptions & { port?: string }\n): Promise<void> {\n try {\n logDebug('Loading model for visualization...');\n\n // Load model with full content (no lazy loading)\n const model = await Model.load(process.cwd(), { lazyLoad: false });\n\n logVerbose(`Model loaded: ${model.manifest.name}`);\n\n // Parse and validate port\n let port = 8080;\n if (options.port) {\n const portNum = parseInt(String(options.port), 10);\n if (isNaN(portNum) || portNum < 1 || portNum > 65535) {\n throw new Error(\n `Invalid port number: ${options.port}. Port must be between 1 and 65535.`\n );\n }\n port = portNum;\n }\n\n // Determine auth settings\n const authEnabled = !options.noAuth; // Auth enabled by default unless --no-auth flag\n const authToken = options.token; // Optional token for testing\n\n // Create and start server\n const server = new VisualizationServer(model, { authEnabled, authToken });\n\n logDebug(`Starting visualization server on port ${port}`);\n logDebug(`Authentication: ${authEnabled ? 'enabled' : 'disabled'}`);\n if (authEnabled && authToken) {\n logDebug(`Using provided token for authentication`);\n } else if (authEnabled) {\n logDebug(`Token will be auto-generated`);\n }\n\n await server.start(port);\n\n console.log(ansis.green(`\u2713 Visualization server started`));\n\n // Display access information\n if (authEnabled) {\n const token = server.getAuthToken();\n console.log(ansis.cyan(` Access URL: http://localhost:${port}?token=${token}`));\n console.log(ansis.dim(` Auth token: ${token}`));\n } else {\n console.log(ansis.dim(` Open http://localhost:${port} in your browser`));\n console.log(ansis.yellow(` \u26A0 Authentication disabled`));\n }\n\n logVerbose(` Model: ${model.manifest.name} (${model.getLayerNames().length} layers)`);\n\n // Optionally open browser\n const shouldOpenBrowser = !options.noBrowser;\n logDebug(`Browser auto-open: ${shouldOpenBrowser ? 'enabled' : 'disabled'} (noBrowser=${options.noBrowser})`);\n if (shouldOpenBrowser) {\n // Only open browser if explicitly enabled (not in tests or CI)\n try {\n const command = getOpenCommand();\n // Include token in URL if auth is enabled\n const url = authEnabled\n ? `http://localhost:${port}?token=${server.getAuthToken()}`\n : `http://localhost:${port}`;\n logDebug(`Opening browser with command: ${command} ${url}`);\n Bun.spawn([command, url], {\n stdout: 'ignore',\n stderr: 'ignore'\n });\n } catch (error) {\n logVerbose('Could not auto-open browser (not critical)');\n }\n } else {\n logDebug('Browser auto-open disabled');\n }\n\n // Keep server running\n await new Promise(() => {\n // Never resolves - keeps process alive\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(ansis.red(`Error: ${message}`));\n logDebug(`Stack: ${error instanceof Error ? error.stack : 'N/A'}`);\n process.exit(1);\n }\n}\n\n/**\n * Get the appropriate command to open a URL\n */\nfunction getOpenCommand(): string {\n const platform = process.platform;\n\n if (platform === 'darwin') {\n return 'open';\n }\n if (platform === 'win32') {\n return 'start';\n }\n // Linux and others\n return 'xdg-open';\n}\n"],
5
+ "mappings": "AAIA,OAAO,WAAW;AAClB,SAAS,aAAa;AACtB,SAAS,2BAA2B;AACpC,SAAS,YAAY,gBAAgB;AAWrC,eAAsB,iBACpB,SAA6C;AAE7C,MAAI;AACF,aAAS,oCAAoC;AAG7C,UAAM,QAAQ,MAAM,MAAM,KAAK,QAAQ,IAAG,GAAI,EAAE,UAAU,GAAK,CAAE;AAEjE,eAAW,iBAAiB,MAAM,SAAS,IAAI,EAAE;AAGjD,QAAI,OAAO;AACX,QAAI,QAAQ,MAAM;AAChB,YAAM,UAAU,SAAS,OAAO,QAAQ,IAAI,GAAG,EAAE;AACjD,UAAI,MAAM,OAAO,KAAK,UAAU,KAAK,UAAU;AAC7C,cAAM,IAAI,MACR,wBAAwB,QAAQ,IAAI,qCAAqC;AAG7E,aAAO;IACT;AAGA,UAAM,cAAc,CAAC,QAAQ,QACvB,YAAY,QAAQ,OAGpB,SAAS,IAAI,oBAAoB,OAAO,EAAE,aAAa,UAAS,CAAE;AAexE,QAbA,SAAS,yCAAyC,IAAI,EAAE,GACxD,SAAS,mBAAmB,cAAc,YAAY,UAAU,EAAE,GAC9D,eAAe,YACjB,SAAS,yCAAyC,IACzC,eACT,SAAS,8BAA8B,GAGzC,MAAM,OAAO,MAAM,IAAI,GAEvB,QAAQ,IAAI,MAAM,MAAM,qCAAgC,CAAC,GAGrD,aAAa;AACf,YAAM,QAAQ,OAAO,aAAY;AACjC,cAAQ,IAAI,MAAM,KAAK,mCAAmC,IAAI,UAAU,KAAK,EAAE,CAAC,GAChF,QAAQ,IAAI,MAAM,IAAI,kBAAkB,KAAK,EAAE,CAAC;IAClD;AACE,cAAQ,IAAI,MAAM,IAAI,4BAA4B,IAAI,kBAAkB,CAAC,GACzE,QAAQ,IAAI,MAAM,OAAO,mCAA8B,CAAC;AAG1D,eAAW,aAAa,MAAM,SAAS,IAAI,KAAK,MAAM,cAAa,EAAG,MAAM,UAAU;AAGtF,UAAM,oBAAoB,CAAC,QAAQ;AAEnC,QADA,SAAS,sBAAsB,oBAAoB,YAAY,UAAU,eAAe,QAAQ,SAAS,GAAG,GACxG;AAEF,UAAI;AACF,cAAM,UAAU,eAAc,GAExB,MAAM,cACR,oBAAoB,IAAI,UAAU,OAAO,aAAY,CAAE,KACvD,oBAAoB,IAAI;AAC5B,iBAAS,iCAAiC,OAAO,IAAI,GAAG,EAAE,GAC1D,IAAI,MAAM,CAAC,SAAS,GAAG,GAAG;UACxB,QAAQ;UACR,QAAQ;SACT;MACH,QAAgB;AACd,mBAAW,4CAA4C;MACzD;;AAEA,eAAS,4BAA4B;AAIvC,UAAM,IAAI,QAAQ,MAAK;IAEvB,CAAC;EACH,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,YAAQ,MAAM,MAAM,IAAI,UAAU,OAAO,EAAE,CAAC,GAC5C,SAAS,UAAU,iBAAiB,QAAQ,MAAM,QAAQ,KAAK,EAAE,GACjE,QAAQ,KAAK,CAAC;EAChB;AACF;AAKA,SAAS,iBAAc;AACrB,QAAM,WAAW,QAAQ;AAEzB,SAAI,aAAa,WACR,SAEL,aAAa,UACR,UAGF;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,52 @@
1
+ import { readFile, writeFile, fileExists } from "../utils/file-io.js";
2
+ import { ChangesetManager } from "./changeset.js";
3
+ class ActiveChangesetContext {
4
+ activePath;
5
+ manager;
6
+ constructor(modelPath) {
7
+ this.activePath = `${modelPath}/.dr/changesets/active`, this.manager = new ChangesetManager(modelPath);
8
+ }
9
+ /**
10
+ * Get the currently active changeset name, if any
11
+ */
12
+ async getActive() {
13
+ return await fileExists(this.activePath) && (await readFile(this.activePath)).trim() || null;
14
+ }
15
+ /**
16
+ * Set a changeset as active for automatic tracking
17
+ */
18
+ async setActive(changesetName) {
19
+ if (!await this.manager.load(changesetName))
20
+ throw new Error(`Changeset '${changesetName}' not found`);
21
+ await writeFile(this.activePath, changesetName);
22
+ }
23
+ /**
24
+ * Clear the active changeset
25
+ */
26
+ async clearActive() {
27
+ await fileExists(this.activePath) && await writeFile(this.activePath, "");
28
+ }
29
+ /**
30
+ * Track a change in the active changeset
31
+ * If no changeset is active, this is a no-op
32
+ */
33
+ async trackChange(type, elementId, layerName, before, after) {
34
+ const activeChangesetName = await this.getActive();
35
+ if (!activeChangesetName)
36
+ return;
37
+ const changeset = await this.manager.load(activeChangesetName);
38
+ if (!changeset)
39
+ throw new Error(`Active changeset '${activeChangesetName}' not found`);
40
+ changeset.addChange(type, elementId, layerName, before, after), await this.manager.save(changeset);
41
+ }
42
+ /**
43
+ * Check if a changeset is currently active
44
+ */
45
+ async isActive() {
46
+ return await this.getActive() !== null;
47
+ }
48
+ }
49
+ export {
50
+ ActiveChangesetContext
51
+ };
52
+ //# sourceMappingURL=active-changeset.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/core/active-changeset.ts"],
4
+ "sourcesContent": ["/**\n * Active Changeset Context\n *\n * Manages the currently active changeset and provides utilities for\n * automatic change tracking during model operations.\n */\n\nimport { readFile, writeFile, fileExists } from '../utils/file-io.js';\nimport { ChangesetManager } from './changeset.js';\n\n/**\n * ActiveChangesetContext manages automatic change tracking\n * when a changeset is activated for transparent operation capture\n */\nexport class ActiveChangesetContext {\n private activePath: string;\n private manager: ChangesetManager;\n\n constructor(modelPath: string) {\n this.activePath = `${modelPath}/.dr/changesets/active`;\n this.manager = new ChangesetManager(modelPath);\n }\n\n /**\n * Get the currently active changeset name, if any\n */\n async getActive(): Promise<string | null> {\n if (!(await fileExists(this.activePath))) {\n return null;\n }\n const content = await readFile(this.activePath);\n return content.trim() || null;\n }\n\n /**\n * Set a changeset as active for automatic tracking\n */\n async setActive(changesetName: string): Promise<void> {\n // Verify changeset exists\n const changeset = await this.manager.load(changesetName);\n if (!changeset) {\n throw new Error(`Changeset '${changesetName}' not found`);\n }\n await writeFile(this.activePath, changesetName);\n }\n\n /**\n * Clear the active changeset\n */\n async clearActive(): Promise<void> {\n if (await fileExists(this.activePath)) {\n await writeFile(this.activePath, '');\n }\n }\n\n /**\n * Track a change in the active changeset\n * If no changeset is active, this is a no-op\n */\n async trackChange(\n type: 'add' | 'update' | 'delete',\n elementId: string,\n layerName: string,\n before?: Record<string, unknown>,\n after?: Record<string, unknown>\n ): Promise<void> {\n const activeChangesetName = await this.getActive();\n if (!activeChangesetName) {\n return; // No active changeset, nothing to track\n }\n\n const changeset = await this.manager.load(activeChangesetName);\n if (!changeset) {\n throw new Error(`Active changeset '${activeChangesetName}' not found`);\n }\n\n changeset.addChange(type, elementId, layerName, before, after);\n await this.manager.save(changeset);\n }\n\n /**\n * Check if a changeset is currently active\n */\n async isActive(): Promise<boolean> {\n const active = await this.getActive();\n return active !== null;\n }\n}\n"],
5
+ "mappings": "AAOA,SAAS,UAAU,WAAW,kBAAkB;AAChD,SAAS,wBAAwB;AAM3B,MAAO,uBAAsB;EACzB;EACA;EAER,YAAY,WAAiB;AAC3B,SAAK,aAAa,GAAG,SAAS,0BAC9B,KAAK,UAAU,IAAI,iBAAiB,SAAS;EAC/C;;;;EAKA,MAAM,YAAS;AACb,WAAM,MAAM,WAAW,KAAK,UAAU,MAGtB,MAAM,SAAS,KAAK,UAAU,GAC/B,KAAI,KAAM;EAC3B;;;;EAKA,MAAM,UAAU,eAAqB;AAGnC,QAAI,CADc,MAAM,KAAK,QAAQ,KAAK,aAAa;AAErD,YAAM,IAAI,MAAM,cAAc,aAAa,aAAa;AAE1D,UAAM,UAAU,KAAK,YAAY,aAAa;EAChD;;;;EAKA,MAAM,cAAW;AACf,IAAI,MAAM,WAAW,KAAK,UAAU,KAClC,MAAM,UAAU,KAAK,YAAY,EAAE;EAEvC;;;;;EAMA,MAAM,YACJ,MACA,WACA,WACA,QACA,OAA+B;AAE/B,UAAM,sBAAsB,MAAM,KAAK,UAAS;AAChD,QAAI,CAAC;AACH;AAGF,UAAM,YAAY,MAAM,KAAK,QAAQ,KAAK,mBAAmB;AAC7D,QAAI,CAAC;AACH,YAAM,IAAI,MAAM,qBAAqB,mBAAmB,aAAa;AAGvE,cAAU,UAAU,MAAM,WAAW,WAAW,QAAQ,KAAK,GAC7D,MAAM,KAAK,QAAQ,KAAK,SAAS;EACnC;;;;EAKA,MAAM,WAAQ;AAEZ,WADe,MAAM,KAAK,UAAS,MACjB;EACpB;;",
6
+ "names": []
7
+ }