@documentation-robotics/cli 0.1.13 → 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.
Files changed (181) hide show
  1. package/README.md +38 -0
  2. package/dist/analyzers/verify-engine.js +22 -19
  3. package/dist/analyzers/verify-engine.js.map +2 -2
  4. package/dist/cli.js +101 -27
  5. package/dist/cli.js.map +3 -3
  6. package/dist/coding-agents/agents/claude-code.js +3 -1
  7. package/dist/coding-agents/agents/claude-code.js.map +2 -2
  8. package/dist/coding-agents/claude-code-client.js +2 -1
  9. package/dist/coding-agents/claude-code-client.js.map +2 -2
  10. package/dist/coding-agents/copilot-client.js +17 -12
  11. package/dist/coding-agents/copilot-client.js.map +2 -2
  12. package/dist/coding-agents/default-permissions.js +122 -0
  13. package/dist/coding-agents/default-permissions.js.map +7 -0
  14. package/dist/commands/add.js +10 -6
  15. package/dist/commands/add.js.map +2 -2
  16. package/dist/commands/analyzer.js +18 -10
  17. package/dist/commands/analyzer.js.map +2 -2
  18. package/dist/commands/audit.js +6 -6
  19. package/dist/commands/audit.js.map +2 -2
  20. package/dist/commands/changeset.js +238 -145
  21. package/dist/commands/changeset.js.map +2 -2
  22. package/dist/commands/delete.js +45 -18
  23. package/dist/commands/delete.js.map +3 -3
  24. package/dist/commands/element.js +3 -2
  25. package/dist/commands/element.js.map +2 -2
  26. package/dist/commands/farm.js +657 -0
  27. package/dist/commands/farm.js.map +7 -0
  28. package/dist/commands/init.js +11 -1
  29. package/dist/commands/init.js.map +2 -2
  30. package/dist/commands/mcp.js +107 -0
  31. package/dist/commands/mcp.js.map +7 -0
  32. package/dist/commands/relationship.js +53 -30
  33. package/dist/commands/relationship.js.map +2 -2
  34. package/dist/commands/reports.js +1 -1
  35. package/dist/commands/reports.js.map +2 -2
  36. package/dist/commands/update.js +19 -6
  37. package/dist/commands/update.js.map +2 -2
  38. package/dist/commands/validate.js +41 -3
  39. package/dist/commands/validate.js.map +2 -2
  40. package/dist/commands/version.js +2 -2
  41. package/dist/commands/version.js.map +2 -2
  42. package/dist/config.js +83 -0
  43. package/dist/config.js.map +7 -0
  44. package/dist/core/annotation-store.js +138 -0
  45. package/dist/core/annotation-store.js.map +7 -0
  46. package/dist/core/farm-manifest.js +134 -0
  47. package/dist/core/farm-manifest.js.map +7 -0
  48. package/dist/core/farm-sync-engine.js +346 -0
  49. package/dist/core/farm-sync-engine.js.map +7 -0
  50. package/dist/core/farm-sync-state.js +159 -0
  51. package/dist/core/farm-sync-state.js.map +7 -0
  52. package/dist/core/manifest.js +6 -3
  53. package/dist/core/manifest.js.map +2 -2
  54. package/dist/core/migration-registry.js +55 -1
  55. package/dist/core/migration-registry.js.map +2 -2
  56. package/dist/core/model.js +177 -16
  57. package/dist/core/model.js.map +3 -3
  58. package/dist/core/virtual-projection.js +1 -0
  59. package/dist/core/virtual-projection.js.map +2 -2
  60. package/dist/generated/node-types.js +1 -1
  61. package/dist/generated/node-types.js.map +2 -2
  62. package/dist/generated/relationship-index.js +45 -9
  63. package/dist/generated/relationship-index.js.map +2 -2
  64. package/dist/integrations/claude_code/docs/MCP_SERVER.md +55 -0
  65. package/dist/integrations/claude_code/docs/README.md +4 -0
  66. package/dist/mcp/api-key-manager.js +98 -0
  67. package/dist/mcp/api-key-manager.js.map +7 -0
  68. package/dist/mcp/http-transport.js +176 -0
  69. package/dist/mcp/http-transport.js.map +7 -0
  70. package/dist/mcp/resource-registry.js +97 -0
  71. package/dist/mcp/resource-registry.js.map +7 -0
  72. package/dist/mcp/tool-registry.js +44 -0
  73. package/dist/mcp/tool-registry.js.map +7 -0
  74. package/dist/mcp/tools/annotation-create.js +39 -0
  75. package/dist/mcp/tools/annotation-create.js.map +7 -0
  76. package/dist/mcp/tools/annotation-delete.js +29 -0
  77. package/dist/mcp/tools/annotation-delete.js.map +7 -0
  78. package/dist/mcp/tools/annotation-get.js +29 -0
  79. package/dist/mcp/tools/annotation-get.js.map +7 -0
  80. package/dist/mcp/tools/annotation-list.js +24 -0
  81. package/dist/mcp/tools/annotation-list.js.map +7 -0
  82. package/dist/mcp/tools/annotation-reply.js +34 -0
  83. package/dist/mcp/tools/annotation-reply.js.map +7 -0
  84. package/dist/mcp/tools/annotation-shared.js +10 -0
  85. package/dist/mcp/tools/annotation-shared.js.map +7 -0
  86. package/dist/mcp/tools/annotation-update.js +39 -0
  87. package/dist/mcp/tools/annotation-update.js.map +7 -0
  88. package/dist/mcp/tools/changeset-list.js +33 -0
  89. package/dist/mcp/tools/changeset-list.js.map +7 -0
  90. package/dist/mcp/tools/changeset-show.js +39 -0
  91. package/dist/mcp/tools/changeset-show.js.map +7 -0
  92. package/dist/mcp/tools/chat-launch.js +50 -0
  93. package/dist/mcp/tools/chat-launch.js.map +7 -0
  94. package/dist/mcp/tools/chat-status.js +26 -0
  95. package/dist/mcp/tools/chat-status.js.map +7 -0
  96. package/dist/mcp/tools/index.js +47 -0
  97. package/dist/mcp/tools/index.js.map +7 -0
  98. package/dist/mcp/tools/model-add.js +106 -0
  99. package/dist/mcp/tools/model-add.js.map +7 -0
  100. package/dist/mcp/tools/model-delete.js +99 -0
  101. package/dist/mcp/tools/model-delete.js.map +7 -0
  102. package/dist/mcp/tools/model-export.js +72 -0
  103. package/dist/mcp/tools/model-export.js.map +7 -0
  104. package/dist/mcp/tools/model-info.js +51 -0
  105. package/dist/mcp/tools/model-info.js.map +7 -0
  106. package/dist/mcp/tools/model-list.js +53 -0
  107. package/dist/mcp/tools/model-list.js.map +7 -0
  108. package/dist/mcp/tools/model-reload.js +34 -0
  109. package/dist/mcp/tools/model-reload.js.map +7 -0
  110. package/dist/mcp/tools/model-search.js +88 -0
  111. package/dist/mcp/tools/model-search.js.map +7 -0
  112. package/dist/mcp/tools/model-show.js +41 -0
  113. package/dist/mcp/tools/model-show.js.map +7 -0
  114. package/dist/mcp/tools/model-stats.js +22 -0
  115. package/dist/mcp/tools/model-stats.js.map +7 -0
  116. package/dist/mcp/tools/model-trace.js +55 -0
  117. package/dist/mcp/tools/model-trace.js.map +7 -0
  118. package/dist/mcp/tools/model-update.js +105 -0
  119. package/dist/mcp/tools/model-update.js.map +7 -0
  120. package/dist/mcp/tools/model-validate.js +78 -0
  121. package/dist/mcp/tools/model-validate.js.map +7 -0
  122. package/dist/mcp/tools/shared.js +103 -0
  123. package/dist/mcp/tools/shared.js.map +7 -0
  124. package/dist/reports/model-layer-report-generator.js +5 -3
  125. package/dist/reports/model-layer-report-generator.js.map +2 -2
  126. package/dist/reports/model-readme-generator.js +116 -0
  127. package/dist/reports/model-readme-generator.js.map +7 -0
  128. package/dist/reports/model-report-data.js +37 -1
  129. package/dist/reports/model-report-data.js.map +2 -2
  130. package/dist/reports/model-report-orchestrator.js +29 -6
  131. package/dist/reports/model-report-orchestrator.js.map +2 -2
  132. package/dist/reports/model-report-utils.js +9 -0
  133. package/dist/reports/model-report-utils.js.map +7 -0
  134. package/dist/schemas/bundled/analyzers/cbm.json +2 -2
  135. package/dist/schemas/bundled/analyzers/manifest.json +2 -2
  136. package/dist/schemas/bundled/api.json +1 -1
  137. package/dist/schemas/bundled/apm.json +1 -1
  138. package/dist/schemas/bundled/application.json +12 -1
  139. package/dist/schemas/bundled/base.json +1 -1
  140. package/dist/schemas/bundled/business.json +1 -1
  141. package/dist/schemas/bundled/data-model.json +38 -2
  142. package/dist/schemas/bundled/data-store.json +1 -1
  143. package/dist/schemas/bundled/manifest.json +4 -4
  144. package/dist/schemas/bundled/motivation.json +1 -1
  145. package/dist/schemas/bundled/navigation.json +1 -1
  146. package/dist/schemas/bundled/product.json +1 -1
  147. package/dist/schemas/bundled/security.json +1 -1
  148. package/dist/schemas/bundled/technology.json +1 -1
  149. package/dist/schemas/bundled/testing.json +1 -1
  150. package/dist/schemas/bundled/ux.json +1 -1
  151. package/dist/server/server.js +22 -3
  152. package/dist/server/server.js.map +2 -2
  153. package/dist/telemetry/config.js +2 -40
  154. package/dist/telemetry/config.js.map +2 -2
  155. package/dist/telemetry/index.js +1 -1
  156. package/dist/types/index.js.map +2 -2
  157. package/dist/utils/ansi-suppressor.js +25 -0
  158. package/dist/utils/ansi-suppressor.js.map +7 -0
  159. package/dist/utils/color-utils.js +34 -0
  160. package/dist/utils/color-utils.js.map +7 -0
  161. package/dist/utils/element-reference-shape.js +15 -1
  162. package/dist/utils/element-reference-shape.js.map +3 -3
  163. package/dist/utils/errors.js +69 -10
  164. package/dist/utils/errors.js.map +2 -2
  165. package/dist/utils/globals.js +17 -1
  166. package/dist/utils/globals.js.map +2 -2
  167. package/dist/utils/project-paths.js +24 -2
  168. package/dist/utils/project-paths.js.map +2 -2
  169. package/dist/utils/reference-path-parser.js +49 -0
  170. package/dist/utils/reference-path-parser.js.map +7 -0
  171. package/dist/utils/source-reference.js +5 -0
  172. package/dist/utils/source-reference.js.map +2 -2
  173. package/dist/utils/spec-version.js +10 -2
  174. package/dist/utils/spec-version.js.map +2 -2
  175. package/dist/validators/composed-reference-validator.js +262 -0
  176. package/dist/validators/composed-reference-validator.js.map +7 -0
  177. package/dist/validators/reference-validator.js +59 -8
  178. package/dist/validators/reference-validator.js.map +3 -3
  179. package/dist/validators/relationship-schema-validator.js +30 -2
  180. package/dist/validators/relationship-schema-validator.js.map +3 -3
  181. package/package.json +6 -3
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/commands/relationship.ts"],
4
- "sourcesContent": ["/**\n * Relationship subcommands for managing intra-layer and cross-layer relationships\n */\n\nimport { Command } from \"commander\";\nimport ansis from \"ansis\";\nimport { Model } from \"../core/model.js\";\nimport { StagingAreaManager } from \"../core/staging-area.js\";\nimport { findElementLayer } from \"../utils/element-utils.js\";\nimport { isValidLayerName } from \"../core/layers.js\";\nimport { CLIError, ErrorCategory, handleError, getErrorMessage } from \"../utils/errors.js\";\nimport {\n getValidRelationships,\n getValidPredicatesForSource,\n getValidDestinationsForSourceAndPredicate,\n} from \"../generated/relationship-index.js\";\nimport { normalizeNodeType } from \"../generated/node-types.js\";\nimport { ModelReportOrchestrator } from \"../reports/model-report-orchestrator.js\";\nimport { emitLog, SeverityNumber } from \"../telemetry/index.js\";\nimport { Element } from \"../core/element.js\";\nimport { Relationship } from \"../core/relationships.js\";\nimport { RelationshipCatalog } from \"../core/relationship-catalog.js\";\n\n/**\n * Pre-resolved element and layer information to avoid redundant lookups.\n * Ensures source and target pairs are consistent: if source is provided, both element and layerName must be present.\n * This prevents invalid states like {sourceElement: ..., sourceLayerName: undefined}.\n */\ninterface PreResolvedElements {\n source?: {\n element: Element;\n layerName: string;\n };\n target?: {\n element: Element;\n layerName: string;\n };\n}\n\n/**\n * Validate element properties and construct a SpecNodeId\n * @throws {CLIError} if element properties are invalid\n */\nfunction constructSpecNodeId(\n element: { layer_id?: unknown; type?: unknown },\n elementId: string\n): string {\n const layerId = element.layer_id;\n const type = element.type;\n\n if (typeof layerId !== \"string\" || !layerId) {\n throw new CLIError(\n `Invalid element ${elementId}: missing or invalid layer_id`,\n ErrorCategory.SYSTEM\n );\n }\n\n if (typeof type !== \"string\" || !type) {\n throw new CLIError(\n `Invalid element ${elementId}: missing or invalid type`,\n ErrorCategory.SYSTEM\n );\n }\n\n // Normalize type name for schema validation (e.g., \"process\" -> \"businessprocess\")\n const normalizedType = normalizeNodeType(layerId, type);\n\n return `${layerId}.${normalizedType}`;\n}\n\n/**\n * Validate a relationship combination using schema-driven registry\n * @returns {valid, suggestions} where suggestions are provided if invalid\n */\nexport function validateRelationshipCombination(\n sourceSpecNodeId: string,\n predicate: string,\n destSpecNodeId: string\n): { valid: boolean; suggestions?: string[] } {\n const valid =\n getValidRelationships(sourceSpecNodeId, predicate, destSpecNodeId)\n .length > 0;\n\n if (valid) {\n return { valid: true };\n }\n\n // Generate helpful suggestions\n const suggestions: string[] = [];\n const validPredicates = getValidPredicatesForSource(sourceSpecNodeId);\n\n if (validPredicates.length > 0) {\n suggestions.push(\n `Valid predicates for ${sourceSpecNodeId}: ${validPredicates.join(\", \")}`\n );\n } else {\n suggestions.push(`No valid relationships defined for ${sourceSpecNodeId}`);\n }\n\n // If predicate is provided, show valid destinations\n if (predicate) {\n const validDests = getValidDestinationsForSourceAndPredicate(\n sourceSpecNodeId,\n predicate\n );\n if (validDests.length > 0) {\n suggestions.push(\n `Valid destinations for --[${predicate}]-->: ${validDests.join(\", \")}`\n );\n } else {\n suggestions.push(\n `No valid destinations for ${sourceSpecNodeId} --[${predicate}]-->`\n );\n }\n }\n\n return { valid: false, suggestions };\n}\n\n/**\n * Get cardinality and strength info for a relationship\n */\nexport function getRelationshipConstraints(\n sourceSpecNodeId: string,\n predicate: string,\n destSpecNodeId: string\n): { cardinality: string; strength: string } | null {\n const rels = getValidRelationships(\n sourceSpecNodeId,\n predicate,\n destSpecNodeId\n );\n if (rels.length === 0) return null;\n\n const rel = rels[0];\n return {\n cardinality: rel.cardinality,\n strength: rel.strength,\n };\n}\n\n/**\n * Handler function for adding a relationship\n * Encapsulates the core logic of the add-relationship command\n * @internal Exported for testing purposes\n *\n * @param model - The architecture model\n * @param source - Source element ID\n * @param target - Target element ID\n * @param predicate - Relationship predicate\n * @param properties - Optional relationship properties\n * @param preResolved - Pre-resolved elements and layer names (optional, for performance)\n */\nexport async function addRelationshipHandler(\n model: Model,\n source: string,\n target: string,\n predicate: string,\n properties?: Record<string, unknown>,\n preResolved?: PreResolvedElements\n): Promise<boolean> {\n // Use pre-resolved elements if provided, otherwise resolve them\n let resolvedSourceLayerName = preResolved?.source?.layerName;\n let resolvedSourceElement = preResolved?.source?.element;\n let resolvedTargetLayerName = preResolved?.target?.layerName;\n let resolvedTargetElement = preResolved?.target?.element;\n\n if (!resolvedSourceLayerName) {\n resolvedSourceLayerName = await findElementLayer(model, source);\n if (!resolvedSourceLayerName) {\n throw new CLIError(`Source element ${source} not found`, ErrorCategory.USER);\n }\n }\n\n if (!resolvedSourceElement) {\n const sourceLayer = await model.getLayer(resolvedSourceLayerName);\n if (!sourceLayer) {\n throw new CLIError(`Source element ${source} not found`, ErrorCategory.USER);\n }\n resolvedSourceElement = sourceLayer.getElement(source);\n if (!resolvedSourceElement) {\n throw new CLIError(`Source element ${source} not found`, ErrorCategory.USER);\n }\n }\n\n if (!resolvedTargetLayerName) {\n resolvedTargetLayerName = await findElementLayer(model, target);\n if (!resolvedTargetLayerName) {\n throw new CLIError(`Target element ${target} not found`, ErrorCategory.USER);\n }\n }\n\n if (!resolvedTargetElement) {\n const targetLayer = await model.getLayer(resolvedTargetLayerName);\n if (!targetLayer) {\n throw new CLIError(`Target element ${target} not found`, ErrorCategory.USER);\n }\n resolvedTargetElement = targetLayer.getElement(target);\n if (!resolvedTargetElement) {\n throw new CLIError(`Target element ${target} not found`, ErrorCategory.USER);\n }\n }\n\n // Schema-driven validation\n const sourceSpecNodeId = constructSpecNodeId(resolvedSourceElement, source);\n const targetSpecNodeId = constructSpecNodeId(resolvedTargetElement, target);\n\n const validation = validateRelationshipCombination(\n sourceSpecNodeId,\n predicate,\n targetSpecNodeId\n );\n\n if (!validation.valid) {\n throw new CLIError(\n `Invalid relationship: ${source} --[${predicate}]--> ${target}`,\n ErrorCategory.USER,\n validation.suggestions\n );\n }\n\n {\n const catalog = new RelationshipCatalog();\n await catalog.load();\n const relType = catalog.getTypeByPredicate(predicate);\n if (relType && !relType.semantics.reflexivity && source === target) {\n throw new CLIError(\n `Invalid relationship: predicate \"${predicate}\" does not allow reflexive relationships (${source})`,\n ErrorCategory.USER\n );\n }\n }\n\n // Guard against duplicate relationships before staging or writing\n if (model.relationships.find(source, target, predicate).length > 0) {\n console.warn(\n ansis.yellow(`Warning: Relationship ${source} --[${predicate}]--> ${target} already exists. Skipping.`)\n );\n return false;\n }\n\n // Check for active changeset \u2014 stage relationship instead of writing directly\n const stagingManager = new StagingAreaManager(model.rootPath, model);\n const activeChangesetId = await stagingManager.getActiveId();\n\n const relData = {\n source,\n target,\n predicate,\n layer: resolvedSourceLayerName,\n ...(resolvedTargetLayerName !== resolvedSourceLayerName ? { targetLayer: resolvedTargetLayerName } : {}),\n category: \"structural\" as const,\n ...(properties ? { properties } : {}),\n };\n\n if (activeChangesetId) {\n // Stage the relationship change in the active changeset\n const compositeKey = `${source}::${predicate}::${target}`;\n await stagingManager.stage(activeChangesetId, {\n type: \"relationship-add\",\n elementId: compositeKey,\n layerName: resolvedSourceLayerName,\n after: relData,\n });\n } else {\n // No active changeset \u2014 write directly to relationships.yaml\n model.relationships.add(relData);\n await model.saveRelationships();\n await model.saveManifest();\n\n // Regenerate reports for all affected layers (including transitively related)\n try {\n const orchestrator = new ModelReportOrchestrator(model, model.rootPath);\n const affectedLayers = new Set<string>();\n\n // Compute affected layers for source layer\n if (isValidLayerName(resolvedSourceLayerName)) {\n for (const layer of orchestrator.computeAffectedLayers(resolvedSourceLayerName)) {\n affectedLayers.add(layer);\n }\n }\n\n // Compute affected layers for target layer\n if (isValidLayerName(resolvedTargetLayerName)) {\n for (const layer of orchestrator.computeAffectedLayers(resolvedTargetLayerName)) {\n affectedLayers.add(layer);\n }\n }\n\n await orchestrator.regenerate(affectedLayers);\n } catch (error) {\n emitLog(\n SeverityNumber.WARN,\n \"Failed to regenerate layer reports after relationship add\",\n {\n \"relationship.predicate\": predicate,\n \"relationship.sourceLayer\": resolvedSourceLayerName,\n \"relationship.targetLayer\": resolvedTargetLayerName,\n \"relationship.source\": source,\n \"relationship.target\": target,\n \"error.message\": getErrorMessage(error),\n }\n );\n }\n }\n\n return true;\n}\n\n/**\n * Handler function for deleting relationships\n * Encapsulates the core logic of the delete-relationship command\n * @internal Exported for testing purposes\n *\n * @param model - The architecture model\n * @param source - Source element ID\n * @param target - Target element ID\n * @param predicate - Optional relationship predicate\n * @param preResolved - Pre-resolved source layer name and relationships (optional, for performance)\n */\ninterface DeletePreResolved {\n sourceLayerName?: string;\n toDelete?: Relationship[];\n}\n\nexport async function deleteRelationshipHandler(\n model: Model,\n source: string,\n target: string,\n predicate?: string,\n preResolved?: DeletePreResolved\n): Promise<{ deletedCount: number }> {\n // Use pre-resolved source layer if provided, otherwise resolve it\n let resolvedSourceLayerName = preResolved?.sourceLayerName;\n if (!resolvedSourceLayerName) {\n resolvedSourceLayerName = await findElementLayer(model, source);\n if (!resolvedSourceLayerName) {\n throw new CLIError(`Source element ${source} not found`, ErrorCategory.USER);\n }\n }\n\n // Use pre-found relationships if provided, otherwise find them\n let resolvedToDelete = preResolved?.toDelete;\n if (!resolvedToDelete) {\n resolvedToDelete = model.relationships.find(source, target, predicate);\n }\n\n if (resolvedToDelete.length === 0) {\n throw new CLIError(\"No matching relationships found\", ErrorCategory.USER);\n }\n\n // Check for active changeset \u2014 stage deletion instead of writing directly\n const stagingManager = new StagingAreaManager(model.rootPath, model);\n const activeChangesetId = await stagingManager.getActiveId();\n\n if (activeChangesetId) {\n // Stage each relationship deletion in the active changeset\n for (const rel of resolvedToDelete) {\n const compositeKey = `${rel.source}::${rel.predicate}::${rel.target}`;\n await stagingManager.stage(activeChangesetId, {\n type: \"relationship-delete\",\n elementId: compositeKey,\n layerName: rel.layer,\n before: {\n source: rel.source,\n target: rel.target,\n predicate: rel.predicate,\n layer: rel.layer,\n ...(rel.targetLayer ? { targetLayer: rel.targetLayer } : {}),\n ...(rel.properties ? { properties: rel.properties } : {}),\n },\n });\n }\n } else {\n // No active changeset \u2014 write directly\n model.relationships.delete(source, target, predicate);\n await model.saveRelationships();\n await model.saveManifest();\n\n // Regenerate reports for all affected layers (including transitively related)\n try {\n const orchestrator = new ModelReportOrchestrator(model, model.rootPath);\n const affectedLayers = new Set<string>();\n\n // Compute affected layers for each deleted relationship's source and target\n for (const rel of resolvedToDelete) {\n // Compute affected layers for source layer\n if (rel.layer && isValidLayerName(rel.layer)) {\n for (const layer of orchestrator.computeAffectedLayers(rel.layer)) {\n affectedLayers.add(layer);\n }\n }\n\n // Compute affected layers for target layer\n if (rel.targetLayer && isValidLayerName(rel.targetLayer)) {\n for (const layer of orchestrator.computeAffectedLayers(rel.targetLayer)) {\n affectedLayers.add(layer);\n }\n }\n }\n\n await orchestrator.regenerate(affectedLayers);\n } catch (error) {\n emitLog(\n SeverityNumber.WARN,\n \"Failed to regenerate layer reports after relationship delete\",\n {\n \"relationship.predicate\": predicate,\n \"relationship.source\": source,\n \"relationship.target\": target,\n \"relationship.deleteCount\": resolvedToDelete.length,\n \"error.message\": getErrorMessage(error),\n }\n );\n }\n }\n\n return { deletedCount: resolvedToDelete.length };\n}\n\nexport function relationshipCommands(program: Command): void {\n program\n .command(\"add <source> <target>\")\n .description(\"Add a relationship between elements (intra-layer or cross-layer)\")\n .requiredOption(\n \"--predicate <predicate>\",\n \"Relationship predicate (e.g., depends-on, realizes, exposes)\"\n )\n .option(\"--properties <json>\", \"Relationship properties (JSON)\")\n .addHelpText(\n \"after\",\n `\nExamples:\n $ dr relationship add motivation.goal.goal-a motivation.goal.goal-b --predicate aggregates\n $ dr relationship add api.operation.create-order application.service.order-service --predicate exposes\n $ dr relationship add business.businessprocess.checkout application.applicationprocess.checkout --predicate aggregates\n $ dr relationship add element-1 element-2 --predicate realizes --properties '{\"strength\":\"high\"}'`\n )\n .action(async (source, target, options) => {\n try {\n // Load model\n const model = await Model.load();\n\n // Find source element for constraints info\n const sourceLayerName = await findElementLayer(model, source);\n if (!sourceLayerName) {\n throw new CLIError(`Source element ${source} not found`, ErrorCategory.USER);\n }\n const sourceLayer = await model.getLayer(sourceLayerName);\n if (!sourceLayer) {\n throw new CLIError(`Source element ${source} not found`, ErrorCategory.USER);\n }\n const sourceElement = sourceLayer.getElement(source);\n if (!sourceElement) {\n throw new CLIError(`Source element ${source} not found`, ErrorCategory.USER);\n }\n\n // Find target element for constraints info\n const targetLayerName = await findElementLayer(model, target);\n if (!targetLayerName) {\n throw new CLIError(`Target element ${target} not found`, ErrorCategory.USER);\n }\n const targetLayer = await model.getLayer(targetLayerName);\n if (!targetLayer) {\n throw new CLIError(`Target element ${target} not found`, ErrorCategory.USER);\n }\n const targetElement = targetLayer.getElement(target);\n if (!targetElement) {\n throw new CLIError(`Target element ${target} not found`, ErrorCategory.USER);\n }\n\n // Get cardinality and strength info (before calling handler for CLI output)\n const sourceSpecNodeId = constructSpecNodeId(sourceElement, source);\n const targetSpecNodeId = constructSpecNodeId(targetElement, target);\n const constraints = getRelationshipConstraints(\n sourceSpecNodeId,\n options.predicate,\n targetSpecNodeId\n );\n\n // Parse properties if provided\n let properties: Record<string, unknown> | undefined;\n if (options.properties) {\n try {\n properties = JSON.parse(options.properties);\n } catch (e) {\n throw new CLIError(\"Invalid JSON in --properties\", ErrorCategory.USER);\n }\n }\n\n // Check for active changeset\n const stagingManager = new StagingAreaManager(model.rootPath, model);\n const activeChangesetId = await stagingManager.getActiveId();\n\n // Call the handler function with pre-resolved elements to avoid duplicate lookups\n const added = await addRelationshipHandler(\n model,\n source,\n target,\n options.predicate,\n properties,\n {\n source: { element: sourceElement, layerName: sourceLayerName },\n target: { element: targetElement, layerName: targetLayerName },\n }\n );\n\n if (!added) return; // duplicate \u2014 warning already printed\n\n // Show cardinality and strength in output\n let message = `\u2713 Added relationship: ${ansis.bold(source)} ${options.predicate} ${ansis.bold(target)}`;\n if (constraints) {\n message += ansis.dim(\n ` (${constraints.cardinality}, ${constraints.strength} strength)`\n );\n }\n if (activeChangesetId) {\n message += ansis.dim(` [staged]`);\n }\n\n console.log(ansis.green(message));\n } catch (error) {\n handleError(error);\n }\n });\n\n program\n .command(\"delete <source> <target>\")\n .description(\"Delete a relationship\")\n .option(\n \"--predicate <predicate>\",\n \"Specific predicate to delete (optional, delete all if not specified)\"\n )\n .option(\"--force\", \"Skip confirmation prompt\")\n .addHelpText(\n \"after\",\n `\nExamples:\n $ dr relationship delete element-1 element-2 --predicate depends-on\n $ dr relationship delete element-1 element-2 --force`\n )\n .action(async (source, target, options) => {\n try {\n // Load model\n const model = await Model.load();\n\n // Find source element and relationships to delete (for confirmation)\n const sourceLayerName = await findElementLayer(model, source);\n if (!sourceLayerName) {\n throw new CLIError(`Source element ${source} not found`, ErrorCategory.USER);\n }\n\n // Find relationships to delete (to show confirmation count)\n const toDelete = model.relationships.find(source, target, options.predicate);\n\n if (toDelete.length === 0) {\n throw new CLIError(\"No matching relationships found\", ErrorCategory.USER);\n }\n\n // Confirm deletion unless --force or non-interactive environment\n // In non-interactive environments (CI, tests), skip confirmation\n const isInteractive = process.stdin.isTTY && process.stdout.isTTY;\n if (!options.force && isInteractive) {\n // Dynamic import of prompts is safe here: caught by outer try-catch\n // This allows lazy loading of the prompt library only when needed\n const { confirm } = await import(\"@clack/prompts\");\n const confirmed = await confirm({\n message: `Delete ${toDelete.length} relationship(s)? This cannot be undone.`,\n initialValue: false,\n });\n\n if (!confirmed) {\n console.log(ansis.dim(\"Cancelled\"));\n return;\n }\n }\n\n // Check for active changeset\n const stagingManager = new StagingAreaManager(model.rootPath, model);\n const activeChangesetId = await stagingManager.getActiveId();\n\n // Call the handler function with pre-resolved values to avoid duplicate lookups\n const result = await deleteRelationshipHandler(\n model,\n source,\n target,\n options.predicate,\n {\n sourceLayerName,\n toDelete,\n }\n );\n\n const stagedSuffix = activeChangesetId ? \" [staged]\" : \"\";\n console.log(\n ansis.green(\n `\u2713 Deleted ${result.deletedCount} relationship(s) from ${ansis.bold(source)} to ${ansis.bold(target)}${stagedSuffix}`\n )\n );\n } catch (error) {\n handleError(error);\n }\n });\n\n program\n .command(\"list <id>\")\n .description(\"List relationships for an element\")\n .option(\"--direction <dir>\", \"Filter by direction (incoming/outgoing/all)\", \"all\")\n .option(\"--json\", \"Output as JSON\")\n .addHelpText(\n \"after\",\n `\nExamples:\n $ dr relationship list api-endpoint-create-customer\n $ dr relationship list business-service-order --direction outgoing\n $ dr relationship list element-1 --json`\n )\n .action(async (id, options) => {\n try {\n // Load model\n const model = await Model.load();\n const layerName = await findElementLayer(model, id);\n\n if (!layerName) {\n throw new CLIError(`Element ${id} not found`, ErrorCategory.USER);\n }\n\n // Get relationships from centralized store\n const { outgoing, incoming } = model.relationships.getForElement(id);\n\n // Filter by direction\n let relationships: typeof outgoing = [];\n if (options.direction === \"outgoing\" || options.direction === \"all\") {\n relationships.push(...outgoing);\n }\n if (options.direction === \"incoming\" || options.direction === \"all\") {\n relationships.push(...incoming);\n }\n\n if (options.json) {\n console.log(JSON.stringify(relationships, null, 2));\n return;\n }\n\n if (relationships.length === 0) {\n console.log(ansis.yellow(`No ${options.direction} relationships for ${id}`));\n return;\n }\n\n console.log(\"\");\n console.log(\n ansis.bold(\n `${options.direction === \"all\" ? \"\" : options.direction + \" \"}relationships for ${ansis.cyan(id)}:`\n )\n );\n console.log(ansis.dim(\"\u2500\".repeat(80)));\n\n for (const rel of relationships) {\n const isOutgoing = rel.source === id;\n const direction = isOutgoing ? \"\u2192\" : \"\u2190\";\n const otherElement = isOutgoing ? rel.target : rel.source;\n const crossLayer = rel.targetLayer && rel.targetLayer !== rel.layer\n ? ansis.dim(` [${rel.layer} \u2192 ${rel.targetLayer}]`)\n : \"\";\n\n console.log(\n ` ${direction} ${ansis.magenta(rel.predicate)}: ${ansis.yellow(otherElement)}${crossLayer}`\n );\n\n if (rel.properties) {\n const propStr = Object.entries(rel.properties)\n .map(([k, v]) => `${k}=${v}`)\n .join(\", \");\n console.log(` ${ansis.dim(propStr)}`);\n }\n }\n\n console.log(ansis.dim(\"\u2500\".repeat(80)));\n console.log(ansis.dim(`Total: ${relationships.length} relationship(s)`));\n console.log(\"\");\n } catch (error) {\n handleError(error);\n }\n });\n\n program\n .command(\"show <source> <target>\")\n .description(\"Show relationship details\")\n .addHelpText(\n \"after\",\n `\nExamples:\n $ dr relationship show api-endpoint-create-customer business-service-customer-mgmt\n $ dr relationship show application-component-api motivation-goal-sales-efficiency`\n )\n .action(async (source, target) => {\n try {\n // Load model\n const model = await Model.load();\n const sourceLayerName = await findElementLayer(model, source);\n\n if (!sourceLayerName) {\n throw new CLIError(`Source element ${source} not found`, ErrorCategory.USER);\n }\n\n // Find relationships from centralized store\n const relationships = model.relationships.find(source, target);\n\n if (relationships.length === 0) {\n throw new CLIError(`No relationships from ${source} to ${target}`, ErrorCategory.USER);\n }\n\n console.log(\"\");\n console.log(\n ansis.bold(\n `Relationship${relationships.length > 1 ? \"s\" : \"\"} from ${ansis.cyan(source)} to ${ansis.yellow(target)}:`\n )\n );\n console.log(ansis.dim(\"\u2500\".repeat(60)));\n\n for (let i = 0; i < relationships.length; i++) {\n const rel = relationships[i];\n\n if (i > 0) {\n console.log(\"\");\n }\n\n console.log(`Predicate: ${ansis.magenta(rel.predicate)}`);\n console.log(`Layer: ${ansis.cyan(rel.layer)}`);\n if (rel.targetLayer && rel.targetLayer !== rel.layer) {\n console.log(`Target Layer: ${ansis.cyan(rel.targetLayer)}`);\n }\n\n if (rel.properties && Object.keys(rel.properties).length > 0) {\n console.log(\"Properties:\");\n for (const [key, value] of Object.entries(rel.properties)) {\n const displayValue =\n typeof value === \"string\" ? value : JSON.stringify(value, null, 2);\n console.log(` ${ansis.cyan(key)}: ${displayValue}`);\n }\n } else {\n console.log(\"Properties: none\");\n }\n }\n\n console.log(\"\");\n } catch (error) {\n handleError(error);\n }\n });\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Relationship subcommands for managing intra-layer and cross-layer relationships\n */\n\nimport { Command } from \"commander\";\nimport ansis from \"ansis\";\nimport { Model } from \"../core/model.js\";\nimport { StagingAreaManager } from \"../core/staging-area.js\";\nimport { findElementLayer } from \"../utils/element-utils.js\";\nimport { isValidLayerName } from \"../core/layers.js\";\nimport {\n CLIError,\n ErrorCategory,\n handleError,\n handleSuccess,\n handleInfo,\n getErrorMessage\n} from \"../utils/errors.js\";\nimport {\n getValidRelationships,\n getValidPredicatesForSource,\n getValidDestinationsForSourceAndPredicate\n} from \"../generated/relationship-index.js\";\nimport { normalizeNodeType } from \"../generated/node-types.js\";\nimport { ModelReportOrchestrator } from \"../reports/model-report-orchestrator.js\";\nimport { emitLog, SeverityNumber } from \"../telemetry/index.js\";\nimport { Element } from \"../core/element.js\";\nimport { Relationship } from \"../core/relationships.js\";\nimport { RelationshipCatalog } from \"../core/relationship-catalog.js\";\n\n/**\n * Pre-resolved element and layer information to avoid redundant lookups.\n * Ensures source and target pairs are consistent: if source is provided, both element and layerName must be present.\n * This prevents invalid states like {sourceElement: ..., sourceLayerName: undefined}.\n */\ninterface PreResolvedElements {\n source?: {\n element: Element;\n layerName: string;\n };\n target?: {\n element: Element;\n layerName: string;\n };\n}\n\n/**\n * Validate element properties and construct a SpecNodeId\n * @throws {CLIError} if element properties are invalid\n */\nfunction constructSpecNodeId(\n element: { layer_id?: unknown; type?: unknown },\n elementId: string\n): string {\n const layerId = element.layer_id;\n const type = element.type;\n\n if (typeof layerId !== \"string\" || !layerId) {\n throw new CLIError(\n `Invalid element ${elementId}: missing or invalid layer_id`,\n ErrorCategory.SYSTEM\n );\n }\n\n if (typeof type !== \"string\" || !type) {\n throw new CLIError(\n `Invalid element ${elementId}: missing or invalid type`,\n ErrorCategory.SYSTEM\n );\n }\n\n // Normalize type name for schema validation (e.g., \"process\" -> \"businessprocess\")\n const normalizedType = normalizeNodeType(layerId, type);\n\n return `${layerId}.${normalizedType}`;\n}\n\n/**\n * Validate a relationship combination using schema-driven registry\n * @returns {valid, suggestions} where suggestions are provided if invalid\n */\nexport function validateRelationshipCombination(\n sourceSpecNodeId: string,\n predicate: string,\n destSpecNodeId: string\n): { valid: boolean; suggestions?: string[] } {\n const valid =\n getValidRelationships(sourceSpecNodeId, predicate, destSpecNodeId).length >\n 0;\n\n if (valid) {\n return { valid: true };\n }\n\n // Generate helpful suggestions\n const suggestions: string[] = [];\n const validPredicates = getValidPredicatesForSource(sourceSpecNodeId);\n\n if (validPredicates.length > 0) {\n suggestions.push(\n `Valid predicates for ${sourceSpecNodeId}: ${validPredicates.join(\", \")}`\n );\n } else {\n suggestions.push(`No valid relationships defined for ${sourceSpecNodeId}`);\n }\n\n // If predicate is provided, show valid destinations\n if (predicate) {\n const validDests = getValidDestinationsForSourceAndPredicate(\n sourceSpecNodeId,\n predicate\n );\n if (validDests.length > 0) {\n suggestions.push(\n `Valid destinations for --[${predicate}]-->: ${validDests.join(\", \")}`\n );\n } else {\n suggestions.push(\n `No valid destinations for ${sourceSpecNodeId} --[${predicate}]-->`\n );\n }\n }\n\n return { valid: false, suggestions };\n}\n\n/**\n * Get cardinality and strength info for a relationship\n */\nexport function getRelationshipConstraints(\n sourceSpecNodeId: string,\n predicate: string,\n destSpecNodeId: string\n): { cardinality: string; strength: string } | null {\n const rels = getValidRelationships(\n sourceSpecNodeId,\n predicate,\n destSpecNodeId\n );\n if (rels.length === 0) return null;\n\n const rel = rels[0];\n return {\n cardinality: rel.cardinality,\n strength: rel.strength\n };\n}\n\n/**\n * Handler function for adding a relationship\n * Encapsulates the core logic of the add-relationship command\n * @internal Exported for testing purposes\n *\n * @param model - The architecture model\n * @param source - Source element ID\n * @param target - Target element ID\n * @param predicate - Relationship predicate\n * @param properties - Optional relationship properties\n * @param preResolved - Pre-resolved elements and layer names (optional, for performance)\n */\nexport async function addRelationshipHandler(\n model: Model,\n source: string,\n target: string,\n predicate: string,\n properties?: Record<string, unknown>,\n preResolved?: PreResolvedElements\n): Promise<boolean> {\n // Use pre-resolved elements if provided, otherwise resolve them\n let resolvedSourceLayerName = preResolved?.source?.layerName;\n let resolvedSourceElement = preResolved?.source?.element;\n let resolvedTargetLayerName = preResolved?.target?.layerName;\n let resolvedTargetElement = preResolved?.target?.element;\n\n if (!resolvedSourceLayerName) {\n resolvedSourceLayerName = await findElementLayer(model, source);\n if (!resolvedSourceLayerName) {\n throw new CLIError(\n `Source element ${source} not found`,\n ErrorCategory.USER\n );\n }\n }\n\n if (!resolvedSourceElement) {\n const sourceLayer = await model.getLayer(resolvedSourceLayerName);\n if (!sourceLayer) {\n throw new CLIError(\n `Source element ${source} not found`,\n ErrorCategory.USER\n );\n }\n resolvedSourceElement = sourceLayer.getElement(source);\n if (!resolvedSourceElement) {\n throw new CLIError(\n `Source element ${source} not found`,\n ErrorCategory.USER\n );\n }\n }\n\n if (!resolvedTargetLayerName) {\n resolvedTargetLayerName = await findElementLayer(model, target);\n if (!resolvedTargetLayerName) {\n throw new CLIError(\n `Target element ${target} not found`,\n ErrorCategory.USER\n );\n }\n }\n\n if (!resolvedTargetElement) {\n const targetLayer = await model.getLayer(resolvedTargetLayerName);\n if (!targetLayer) {\n throw new CLIError(\n `Target element ${target} not found`,\n ErrorCategory.USER\n );\n }\n resolvedTargetElement = targetLayer.getElement(target);\n if (!resolvedTargetElement) {\n throw new CLIError(\n `Target element ${target} not found`,\n ErrorCategory.USER\n );\n }\n }\n\n // Schema-driven validation\n const sourceSpecNodeId = constructSpecNodeId(resolvedSourceElement, source);\n const targetSpecNodeId = constructSpecNodeId(resolvedTargetElement, target);\n\n const validation = validateRelationshipCombination(\n sourceSpecNodeId,\n predicate,\n targetSpecNodeId\n );\n\n if (!validation.valid) {\n throw new CLIError(\n `Invalid relationship: ${source} --[${predicate}]--> ${target}`,\n ErrorCategory.USER,\n validation.suggestions\n );\n }\n\n {\n const catalog = new RelationshipCatalog();\n await catalog.load();\n const relType = catalog.getTypeByPredicate(predicate);\n if (relType && !relType.semantics.reflexivity && source === target) {\n throw new CLIError(\n `Invalid relationship: predicate \"${predicate}\" does not allow reflexive relationships (${source})`,\n ErrorCategory.USER\n );\n }\n }\n\n // Guard against duplicate relationships before staging or writing\n if (model.relationships.find(source, target, predicate).length > 0) {\n // Return false to signal duplicate; caller will emit appropriate output\n return false;\n }\n\n // Check for active changeset \u2014 stage relationship instead of writing directly\n const stagingManager = new StagingAreaManager(model.rootPath, model);\n const activeChangesetId = await stagingManager.getActiveId();\n\n const relData = {\n source,\n target,\n predicate,\n layer: resolvedSourceLayerName,\n ...(resolvedTargetLayerName !== resolvedSourceLayerName\n ? { targetLayer: resolvedTargetLayerName }\n : {}),\n category: \"structural\" as const,\n ...(properties ? { properties } : {})\n };\n\n if (activeChangesetId) {\n // Stage the relationship change in the active changeset\n const compositeKey = `${source}::${predicate}::${target}`;\n await stagingManager.stage(activeChangesetId, {\n type: \"relationship-add\",\n elementId: compositeKey,\n layerName: resolvedSourceLayerName,\n after: relData\n });\n } else {\n // No active changeset \u2014 write directly to relationships.yaml\n model.relationships.add(relData);\n await model.saveRelationships();\n await model.saveManifest();\n\n // Regenerate reports for all affected layers (including transitively related)\n try {\n const orchestrator = new ModelReportOrchestrator(model, model.rootPath);\n const affectedLayers = new Set<string>();\n\n // Compute affected layers for source layer\n if (isValidLayerName(resolvedSourceLayerName)) {\n for (const layer of orchestrator.computeAffectedLayers(\n resolvedSourceLayerName\n )) {\n affectedLayers.add(layer);\n }\n }\n\n // Compute affected layers for target layer\n if (isValidLayerName(resolvedTargetLayerName)) {\n for (const layer of orchestrator.computeAffectedLayers(\n resolvedTargetLayerName\n )) {\n affectedLayers.add(layer);\n }\n }\n\n await orchestrator.regenerate(affectedLayers);\n } catch (error) {\n emitLog(\n SeverityNumber.WARN,\n \"Failed to regenerate layer reports after relationship add\",\n {\n \"relationship.predicate\": predicate,\n \"relationship.sourceLayer\": resolvedSourceLayerName,\n \"relationship.targetLayer\": resolvedTargetLayerName,\n \"relationship.source\": source,\n \"relationship.target\": target,\n \"error.message\": getErrorMessage(error)\n }\n );\n }\n }\n\n return true;\n}\n\n/**\n * Handler function for deleting relationships\n * Encapsulates the core logic of the delete-relationship command\n * @internal Exported for testing purposes\n *\n * @param model - The architecture model\n * @param source - Source element ID\n * @param target - Target element ID\n * @param predicate - Optional relationship predicate\n * @param preResolved - Pre-resolved source layer name and relationships (optional, for performance)\n */\ninterface DeletePreResolved {\n sourceLayerName?: string;\n toDelete?: Relationship[];\n}\n\nexport async function deleteRelationshipHandler(\n model: Model,\n source: string,\n target: string,\n predicate?: string,\n preResolved?: DeletePreResolved\n): Promise<{ deletedCount: number }> {\n // Use pre-resolved source layer if provided, otherwise resolve it\n let resolvedSourceLayerName = preResolved?.sourceLayerName;\n if (!resolvedSourceLayerName) {\n resolvedSourceLayerName = await findElementLayer(model, source);\n if (!resolvedSourceLayerName) {\n throw new CLIError(\n `Source element ${source} not found`,\n ErrorCategory.USER\n );\n }\n }\n\n // Use pre-found relationships if provided, otherwise find them\n let resolvedToDelete = preResolved?.toDelete;\n if (!resolvedToDelete) {\n resolvedToDelete = model.relationships.find(source, target, predicate);\n }\n\n if (resolvedToDelete.length === 0) {\n throw new CLIError(\"No matching relationships found\", ErrorCategory.USER);\n }\n\n // Check for active changeset \u2014 stage deletion instead of writing directly\n const stagingManager = new StagingAreaManager(model.rootPath, model);\n const activeChangesetId = await stagingManager.getActiveId();\n\n if (activeChangesetId) {\n // Stage each relationship deletion in the active changeset\n for (const rel of resolvedToDelete) {\n const compositeKey = `${rel.source}::${rel.predicate}::${rel.target}`;\n await stagingManager.stage(activeChangesetId, {\n type: \"relationship-delete\",\n elementId: compositeKey,\n layerName: rel.layer,\n before: {\n source: rel.source,\n target: rel.target,\n predicate: rel.predicate,\n layer: rel.layer,\n ...(rel.targetLayer ? { targetLayer: rel.targetLayer } : {}),\n ...(rel.properties ? { properties: rel.properties } : {})\n }\n });\n }\n } else {\n // No active changeset \u2014 write directly\n model.relationships.delete(source, target, predicate);\n await model.saveRelationships();\n await model.saveManifest();\n\n // Regenerate reports for all affected layers (including transitively related)\n try {\n const orchestrator = new ModelReportOrchestrator(model, model.rootPath);\n const affectedLayers = new Set<string>();\n\n // Compute affected layers for each deleted relationship's source and target\n for (const rel of resolvedToDelete) {\n // Compute affected layers for source layer\n if (rel.layer && isValidLayerName(rel.layer)) {\n for (const layer of orchestrator.computeAffectedLayers(rel.layer)) {\n affectedLayers.add(layer);\n }\n }\n\n // Compute affected layers for target layer\n if (rel.targetLayer && isValidLayerName(rel.targetLayer)) {\n for (const layer of orchestrator.computeAffectedLayers(\n rel.targetLayer\n )) {\n affectedLayers.add(layer);\n }\n }\n }\n\n await orchestrator.regenerate(affectedLayers);\n } catch (error) {\n emitLog(\n SeverityNumber.WARN,\n \"Failed to regenerate layer reports after relationship delete\",\n {\n \"relationship.predicate\": predicate,\n \"relationship.source\": source,\n \"relationship.target\": target,\n \"relationship.deleteCount\": resolvedToDelete.length,\n \"error.message\": getErrorMessage(error)\n }\n );\n }\n }\n\n return { deletedCount: resolvedToDelete.length };\n}\n\nexport function relationshipCommands(program: Command, rootProgram?: Command): void {\n program\n .command(\"add <source> <target>\")\n .description(\n \"Add a relationship between elements (intra-layer or cross-layer)\"\n )\n .option(\n \"--model <path>\",\n \"Path to model root (contains model/manifest.yaml)\"\n )\n .requiredOption(\n \"--predicate <predicate>\",\n \"Relationship predicate (e.g., depends-on, realizes, exposes)\"\n )\n .option(\"--properties <json>\", \"Relationship properties (JSON)\")\n .addHelpText(\n \"after\",\n `\nExamples:\n $ dr relationship add motivation.goal.goal-a motivation.goal.goal-b --predicate aggregates\n $ dr relationship add api.operation.create-order application.service.order-service --predicate exposes\n $ dr relationship add business.businessprocess.checkout application.applicationprocess.checkout --predicate aggregates\n $ dr relationship add element-1 element-2 --predicate realizes --properties '{\"strength\":\"high\"}'`\n )\n .action(async (source, target, options) => {\n try {\n // Load model\n const model = await Model.load(options.model || process.cwd());\n\n // Find source element for constraints info\n const sourceLayerName = await findElementLayer(model, source);\n if (!sourceLayerName) {\n throw new CLIError(\n `Source element ${source} not found`,\n ErrorCategory.USER\n );\n }\n const sourceLayer = await model.getLayer(sourceLayerName);\n if (!sourceLayer) {\n throw new CLIError(\n `Source element ${source} not found`,\n ErrorCategory.USER\n );\n }\n const sourceElement = sourceLayer.getElement(source);\n if (!sourceElement) {\n throw new CLIError(\n `Source element ${source} not found`,\n ErrorCategory.USER\n );\n }\n\n // Find target element for constraints info\n const targetLayerName = await findElementLayer(model, target);\n if (!targetLayerName) {\n throw new CLIError(\n `Target element ${target} not found`,\n ErrorCategory.USER\n );\n }\n const targetLayer = await model.getLayer(targetLayerName);\n if (!targetLayer) {\n throw new CLIError(\n `Target element ${target} not found`,\n ErrorCategory.USER\n );\n }\n const targetElement = targetLayer.getElement(target);\n if (!targetElement) {\n throw new CLIError(\n `Target element ${target} not found`,\n ErrorCategory.USER\n );\n }\n\n // Get cardinality and strength info (before calling handler for CLI output)\n const sourceSpecNodeId = constructSpecNodeId(sourceElement, source);\n const targetSpecNodeId = constructSpecNodeId(targetElement, target);\n const constraints = getRelationshipConstraints(\n sourceSpecNodeId,\n options.predicate,\n targetSpecNodeId\n );\n\n // Parse properties if provided\n let properties: Record<string, unknown> | undefined;\n if (options.properties) {\n try {\n properties = JSON.parse(options.properties);\n } catch (e) {\n throw new CLIError(\n \"Invalid JSON in --properties\",\n ErrorCategory.USER\n );\n }\n }\n\n // Check for active changeset\n const stagingManager = new StagingAreaManager(model.rootPath, model);\n const activeChangesetId = await stagingManager.getActiveId();\n\n // Call the handler function with pre-resolved elements to avoid duplicate lookups\n const added = await addRelationshipHandler(\n model,\n source,\n target,\n options.predicate,\n properties,\n {\n source: { element: sourceElement, layerName: sourceLayerName },\n target: { element: targetElement, layerName: targetLayerName }\n }\n );\n\n if (!added) {\n // duplicate \u2014 emit JSON response indicating relationship already exists\n handleSuccess(\n `Relationship ${source} --[${options.predicate}]--> ${target} already exists`,\n {\n source,\n target,\n predicate: options.predicate,\n layer: sourceLayerName,\n duplicate: true,\n },\n { verbose: options.verbose }\n );\n return;\n }\n\n // Show cardinality and strength in output\n let message = `Added relationship: ${ansis.bold(source)} ${options.predicate} ${ansis.bold(target)}`;\n const details: Record<string, unknown> = {\n source,\n target,\n predicate: options.predicate,\n layer: sourceLayerName,\n };\n if (constraints) {\n message += ansis.dim(\n ` (${constraints.cardinality}, ${constraints.strength} strength)`\n );\n details.cardinality = constraints.cardinality;\n details.strength = constraints.strength;\n }\n if (activeChangesetId) {\n message += ansis.dim(` [staged]`);\n details.changesetStatus = \"staged\";\n }\n\n handleSuccess(message, details, { verbose: options.verbose });\n } catch (error) {\n handleError(error);\n }\n });\n\n program\n .command(\"delete <source> <target>\")\n .description(\"Delete a relationship\")\n .option(\n \"--model <path>\",\n \"Path to model root (contains model/manifest.yaml)\"\n )\n .option(\n \"--predicate <predicate>\",\n \"Specific predicate to delete (optional, delete all if not specified)\"\n )\n .option(\"--force\", \"Skip confirmation prompt\")\n .addHelpText(\n \"after\",\n `\nExamples:\n $ dr relationship delete element-1 element-2 --predicate depends-on\n $ dr relationship delete element-1 element-2 --force`\n )\n .action(async (source, target, options) => {\n try {\n // Load model\n const model = await Model.load(options.model || process.cwd());\n\n // Find source element and relationships to delete (for confirmation)\n const sourceLayerName = await findElementLayer(model, source);\n if (!sourceLayerName) {\n throw new CLIError(\n `Source element ${source} not found`,\n ErrorCategory.USER\n );\n }\n\n // Find relationships to delete (to show confirmation count)\n const toDelete = model.relationships.find(\n source,\n target,\n options.predicate\n );\n\n if (toDelete.length === 0) {\n throw new CLIError(\n \"No matching relationships found\",\n ErrorCategory.USER\n );\n }\n\n // Confirm deletion unless --force or non-interactive environment\n // In non-interactive environments (CI, tests), skip confirmation\n const isInteractive = process.stdin.isTTY && process.stdout.isTTY;\n if (!options.force && isInteractive) {\n // Dynamic import of prompts is safe here: caught by outer try-catch\n // This allows lazy loading of the prompt library only when needed\n const { confirm } = await import(\"@clack/prompts\");\n const confirmed = await confirm({\n message: `Delete ${toDelete.length} relationship(s)? This cannot be undone.`,\n initialValue: false\n });\n\n if (!confirmed) {\n handleSuccess(\"Deletion cancelled\", {\n source,\n target,\n status: \"cancelled\",\n });\n return;\n }\n }\n\n // Check for active changeset\n const stagingManager = new StagingAreaManager(model.rootPath, model);\n const activeChangesetId = await stagingManager.getActiveId();\n\n // Call the handler function with pre-resolved values to avoid duplicate lookups\n const result = await deleteRelationshipHandler(\n model,\n source,\n target,\n options.predicate,\n {\n sourceLayerName,\n toDelete\n }\n );\n\n const stagedSuffix = activeChangesetId ? \" [staged]\" : \"\";\n const deleteDetails: Record<string, unknown> = {\n source,\n target,\n layer: sourceLayerName,\n deletedCount: result.deletedCount,\n };\n if (options.predicate) {\n deleteDetails.predicate = options.predicate;\n }\n if (activeChangesetId) {\n deleteDetails.changesetStatus = \"staged\";\n }\n handleSuccess(\n `Deleted ${result.deletedCount} relationship(s) from ${ansis.bold(source)} to ${ansis.bold(target)}${stagedSuffix}`,\n deleteDetails,\n { verbose: options.verbose }\n );\n } catch (error) {\n handleError(error);\n }\n });\n\n program\n .command(\"list <id>\")\n .description(\"List relationships for an element\")\n .option(\n \"--model <path>\",\n \"Path to model root (contains model/manifest.yaml)\"\n )\n .option(\n \"--direction <dir>\",\n \"Filter by direction (incoming/outgoing/all)\",\n \"all\"\n )\n .option(\"--json\", \"Output as JSON\")\n .addHelpText(\n \"after\",\n `\nExamples:\n $ dr relationship list api-endpoint-create-customer\n $ dr relationship list business-service-order --direction outgoing\n $ dr relationship list element-1 --json`\n )\n .action(async (id, options) => {\n try {\n // Load model\n const model = await Model.load(options.model || process.cwd());\n const layerName = await findElementLayer(model, id);\n\n if (!layerName) {\n throw new CLIError(`Element ${id} not found`, ErrorCategory.USER);\n }\n\n // Get relationships from centralized store\n const { outgoing, incoming } = model.relationships.getForElement(id);\n\n // Filter by direction\n let relationships: typeof outgoing = [];\n if (options.direction === \"outgoing\" || options.direction === \"all\") {\n relationships.push(...outgoing);\n }\n if (options.direction === \"incoming\" || options.direction === \"all\") {\n relationships.push(...incoming);\n }\n\n const shouldOutputJson = options.json ?? (rootProgram?.opts().json);\n if (shouldOutputJson) {\n console.log(JSON.stringify(relationships, null, 2));\n return;\n }\n\n if (relationships.length === 0) {\n handleInfo(\n ansis.yellow(`No ${options.direction} relationships for ${id}`)\n );\n return;\n }\n\n handleInfo(\"\");\n handleInfo(\n ansis.bold(\n `${options.direction === \"all\" ? \"\" : options.direction + \" \"}relationships for ${ansis.cyan(id)}:`\n )\n );\n handleInfo(ansis.dim(\"\u2500\".repeat(80)));\n\n for (const rel of relationships) {\n const isOutgoing = rel.source === id;\n const direction = isOutgoing ? \"\u2192\" : \"\u2190\";\n const otherElement = isOutgoing ? rel.target : rel.source;\n const crossLayer =\n rel.targetLayer && rel.targetLayer !== rel.layer\n ? ansis.dim(` [${rel.layer} \u2192 ${rel.targetLayer}]`)\n : \"\";\n\n handleInfo(\n ` ${direction} ${ansis.magenta(rel.predicate)}: ${ansis.yellow(otherElement)}${crossLayer}`\n );\n\n if (rel.properties) {\n const propStr = Object.entries(rel.properties)\n .map(([k, v]) => `${k}=${v}`)\n .join(\", \");\n handleInfo(` ${ansis.dim(propStr)}`);\n }\n }\n\n handleInfo(ansis.dim(\"\u2500\".repeat(80)));\n handleInfo(ansis.dim(`Total: ${relationships.length} relationship(s)`));\n handleInfo(\"\");\n } catch (error) {\n handleError(error);\n }\n });\n\n program\n .command(\"show <source> <target>\")\n .description(\"Show relationship details\")\n .option(\n \"--model <path>\",\n \"Path to model root (contains model/manifest.yaml)\"\n )\n .addHelpText(\n \"after\",\n `\nExamples:\n $ dr relationship show api-endpoint-create-customer business-service-customer-mgmt\n $ dr relationship show application-component-api motivation-goal-sales-efficiency`\n )\n .action(async (source, target, options) => {\n try {\n // Load model\n const model = await Model.load(options.model || process.cwd());\n const sourceLayerName = await findElementLayer(model, source);\n\n if (!sourceLayerName) {\n throw new CLIError(\n `Source element ${source} not found`,\n ErrorCategory.USER\n );\n }\n\n // Find relationships from centralized store\n const relationships = model.relationships.find(source, target);\n\n if (relationships.length === 0) {\n throw new CLIError(\n `No relationships from ${source} to ${target}`,\n ErrorCategory.USER\n );\n }\n\n handleInfo(\"\");\n handleInfo(\n ansis.bold(\n `Relationship${relationships.length > 1 ? \"s\" : \"\"} from ${ansis.cyan(source)} to ${ansis.yellow(target)}:`\n )\n );\n handleInfo(ansis.dim(\"\u2500\".repeat(60)));\n\n for (let i = 0; i < relationships.length; i++) {\n const rel = relationships[i];\n\n if (i > 0) {\n handleInfo(\"\");\n }\n\n handleInfo(`Predicate: ${ansis.magenta(rel.predicate)}`);\n handleInfo(`Layer: ${ansis.cyan(rel.layer)}`);\n if (rel.targetLayer && rel.targetLayer !== rel.layer) {\n handleInfo(`Target Layer: ${ansis.cyan(rel.targetLayer)}`);\n }\n\n if (rel.properties && Object.keys(rel.properties).length > 0) {\n handleInfo(\"Properties:\");\n for (const [key, value] of Object.entries(rel.properties)) {\n const displayValue =\n typeof value === \"string\"\n ? value\n : JSON.stringify(value, null, 2);\n handleInfo(` ${ansis.cyan(key)}: ${displayValue}`);\n }\n } else {\n handleInfo(\"Properties: none\");\n }\n }\n\n handleInfo(\"\");\n } catch (error) {\n handleError(error);\n }\n });\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -24,7 +24,7 @@ async function regenerateLayerReports(projectRoot) {
24
24
  const reportDir = path.join(projectRoot, "documentation-robotics", "reports");
25
25
  console.log(ansis.dim("Removing existing reports...")), await fs.rm(reportDir, { recursive: !0, force: !0 }), console.log(ansis.dim("Loading model..."));
26
26
  const model = await Model.load(projectRoot), orchestrator = new ModelReportOrchestrator(model, projectRoot);
27
- console.log(ansis.dim("Generating 13 layer reports...")), await orchestrator.regenerateAll(), console.log(ansis.green("\u2713 All 13 layer reports regenerated")), console.log(ansis.dim(" Reports written to: documentation-robotics/reports/"));
27
+ console.log(ansis.dim("Generating 13 layer reports and README...")), await orchestrator.regenerateAll(), console.log(ansis.green("\u2713 All 13 layer reports and README regenerated")), console.log(ansis.dim(" Reports written to: documentation-robotics/reports/")), console.log(ansis.dim(" README written to: documentation-robotics/README.md"));
28
28
  }
29
29
  async function hasLayerReports(projectRoot) {
30
30
  const reportDir = path.join(projectRoot, "documentation-robotics", "reports");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/commands/reports.ts"],
4
- "sourcesContent": ["/**\n * Reports subcommands for managing the maintained set of markdown layer reports\n */\n\nimport { Command } from \"commander\";\nimport ansis from \"ansis\";\nimport * as fs from \"fs/promises\";\nimport * as path from \"path\";\nimport { Model } from \"../core/model.js\";\nimport { ModelReportOrchestrator } from \"../reports/model-report-orchestrator.js\";\nimport { findProjectRoot } from \"../utils/project-paths.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\n\nexport function reportsCommands(parent: Command): void {\n parent\n .command(\"regenerate\")\n .description(\"Delete existing layer reports and regenerate all 13 markdown reports\")\n .addHelpText(\n \"after\",\n `\nExamples:\n $ dr reports regenerate # Delete existing reports and regenerate all 13 layer reports`\n )\n .action(async () => {\n await reportsRegenerateCommand();\n });\n}\n\n/**\n * Delete existing layer reports and regenerate all 13 markdown reports.\n */\nexport async function reportsRegenerateCommand(): Promise<void> {\n try {\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 await regenerateLayerReports(projectRoot);\n } catch (error) {\n console.error(ansis.red(`Error: ${getErrorMessage(error)}`));\n process.exit(1);\n }\n}\n\n/**\n * Delete existing reports directory and regenerate all 13 layer reports.\n * Exported for use in the upgrade command.\n */\nexport async function regenerateLayerReports(projectRoot: string): Promise<void> {\n const reportDir = path.join(projectRoot, \"documentation-robotics\", \"reports\");\n\n // Remove the entire reports directory so stale reports from removed/renamed layers don't linger.\n // force: true means no error if the directory doesn't exist yet.\n // ModelReportOrchestrator.regenerateAll() recreates the directory before writing.\n console.log(ansis.dim(\"Removing existing reports...\"));\n await fs.rm(reportDir, { recursive: true, force: true });\n\n // Load model and regenerate all reports\n console.log(ansis.dim(\"Loading model...\"));\n const model = await Model.load(projectRoot);\n\n const orchestrator = new ModelReportOrchestrator(model, projectRoot);\n console.log(ansis.dim(\"Generating 13 layer reports...\"));\n await orchestrator.regenerateAll();\n\n console.log(ansis.green(\"\u2713 All 13 layer reports regenerated\"));\n console.log(ansis.dim(\" Reports written to: documentation-robotics/reports/\"));\n}\n\n/**\n * Check whether any layer reports exist in the reports directory.\n * Returns false if the directory doesn't exist or contains no .md files.\n */\nexport async function hasLayerReports(projectRoot: string): Promise<boolean> {\n const reportDir = path.join(projectRoot, \"documentation-robotics\", \"reports\");\n try {\n const entries = await fs.readdir(reportDir);\n return entries.some((f) => f.endsWith(\".md\"));\n } catch (error: unknown) {\n const err = error as NodeJS.ErrnoException;\n if (err.code === \"ENOENT\") return false;\n throw error;\n }\n}\n"],
5
- "mappings": "AAKA,OAAO,WAAW;AAClB,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,SAAS,aAAa;AACtB,SAAS,+BAA+B;AACxC,SAAS,uBAAuB;AAChC,SAAS,uBAAuB;AAE1B,SAAU,gBAAgB,QAAe;AAC7C,SACG,QAAQ,YAAY,EACpB,YAAY,sEAAsE,EAClF,YACC,SACA;;2FAEqF,EAEtF,OAAO,YAAW;AACjB,UAAM,yBAAwB;EAChC,CAAC;AACL;AAKA,eAAsB,2BAAwB;AAC5C,MAAI;AACF,UAAM,cAAc,MAAM,gBAAe;AACzC,IAAK,gBACH,QAAQ,MAAM,MAAM,IAAI,4BAA4B,CAAC,GACrD,QAAQ,MAAM,MAAM,IAAI,uCAAuC,CAAC,GAChE,QAAQ,KAAK,CAAC,IAGhB,MAAM,uBAAuB,WAAW;EAC1C,SAAS,OAAO;AACd,YAAQ,MAAM,MAAM,IAAI,UAAU,gBAAgB,KAAK,CAAC,EAAE,CAAC,GAC3D,QAAQ,KAAK,CAAC;EAChB;AACF;AAMA,eAAsB,uBAAuB,aAAmB;AAC9D,QAAM,YAAY,KAAK,KAAK,aAAa,0BAA0B,SAAS;AAK5E,UAAQ,IAAI,MAAM,IAAI,8BAA8B,CAAC,GACrD,MAAM,GAAG,GAAG,WAAW,EAAE,WAAW,IAAM,OAAO,GAAI,CAAE,GAGvD,QAAQ,IAAI,MAAM,IAAI,kBAAkB,CAAC;AACzC,QAAM,QAAQ,MAAM,MAAM,KAAK,WAAW,GAEpC,eAAe,IAAI,wBAAwB,OAAO,WAAW;AACnE,UAAQ,IAAI,MAAM,IAAI,gCAAgC,CAAC,GACvD,MAAM,aAAa,cAAa,GAEhC,QAAQ,IAAI,MAAM,MAAM,yCAAoC,CAAC,GAC7D,QAAQ,IAAI,MAAM,IAAI,uDAAuD,CAAC;AAChF;AAMA,eAAsB,gBAAgB,aAAmB;AACvD,QAAM,YAAY,KAAK,KAAK,aAAa,0BAA0B,SAAS;AAC5E,MAAI;AAEF,YADgB,MAAM,GAAG,QAAQ,SAAS,GAC3B,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC;EAC9C,SAAS,OAAgB;AAEvB,QADY,MACJ,SAAS;AAAU,aAAO;AAClC,UAAM;EACR;AACF;",
4
+ "sourcesContent": ["/**\n * Reports subcommands for managing the maintained set of markdown layer reports\n */\n\nimport { Command } from \"commander\";\nimport ansis from \"ansis\";\nimport * as fs from \"fs/promises\";\nimport * as path from \"path\";\nimport { Model } from \"../core/model.js\";\nimport { ModelReportOrchestrator } from \"../reports/model-report-orchestrator.js\";\nimport { findProjectRoot } from \"../utils/project-paths.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\n\nexport function reportsCommands(parent: Command): void {\n parent\n .command(\"regenerate\")\n .description(\"Delete existing layer reports and regenerate all 13 markdown reports\")\n .addHelpText(\n \"after\",\n `\nExamples:\n $ dr reports regenerate # Delete existing reports and regenerate all 13 layer reports`\n )\n .action(async () => {\n await reportsRegenerateCommand();\n });\n}\n\n/**\n * Delete existing layer reports and regenerate all 13 markdown reports.\n */\nexport async function reportsRegenerateCommand(): Promise<void> {\n try {\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 await regenerateLayerReports(projectRoot);\n } catch (error) {\n console.error(ansis.red(`Error: ${getErrorMessage(error)}`));\n process.exit(1);\n }\n}\n\n/**\n * Delete existing reports directory and regenerate all 13 layer reports and the README.\n * Exported for use in the upgrade command.\n */\nexport async function regenerateLayerReports(projectRoot: string): Promise<void> {\n const reportDir = path.join(projectRoot, \"documentation-robotics\", \"reports\");\n\n // Remove the entire reports directory so stale reports from removed/renamed layers don't linger.\n // force: true means no error if the directory doesn't exist yet.\n // ModelReportOrchestrator.regenerateAll() recreates the directory before writing.\n console.log(ansis.dim(\"Removing existing reports...\"));\n await fs.rm(reportDir, { recursive: true, force: true });\n\n // Load model and regenerate all reports\n console.log(ansis.dim(\"Loading model...\"));\n const model = await Model.load(projectRoot);\n\n const orchestrator = new ModelReportOrchestrator(model, projectRoot);\n console.log(ansis.dim(\"Generating 13 layer reports and README...\"));\n await orchestrator.regenerateAll();\n\n console.log(ansis.green(\"\u2713 All 13 layer reports and README regenerated\"));\n console.log(ansis.dim(\" Reports written to: documentation-robotics/reports/\"));\n console.log(ansis.dim(\" README written to: documentation-robotics/README.md\"));\n}\n\n/**\n * Check whether any layer reports exist in the reports directory.\n * Returns false if the directory doesn't exist or contains no .md files.\n */\nexport async function hasLayerReports(projectRoot: string): Promise<boolean> {\n const reportDir = path.join(projectRoot, \"documentation-robotics\", \"reports\");\n try {\n const entries = await fs.readdir(reportDir);\n return entries.some((f) => f.endsWith(\".md\"));\n } catch (error: unknown) {\n const err = error as NodeJS.ErrnoException;\n if (err.code === \"ENOENT\") return false;\n throw error;\n }\n}\n"],
5
+ "mappings": "AAKA,OAAO,WAAW;AAClB,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,SAAS,aAAa;AACtB,SAAS,+BAA+B;AACxC,SAAS,uBAAuB;AAChC,SAAS,uBAAuB;AAE1B,SAAU,gBAAgB,QAAe;AAC7C,SACG,QAAQ,YAAY,EACpB,YAAY,sEAAsE,EAClF,YACC,SACA;;2FAEqF,EAEtF,OAAO,YAAW;AACjB,UAAM,yBAAwB;EAChC,CAAC;AACL;AAKA,eAAsB,2BAAwB;AAC5C,MAAI;AACF,UAAM,cAAc,MAAM,gBAAe;AACzC,IAAK,gBACH,QAAQ,MAAM,MAAM,IAAI,4BAA4B,CAAC,GACrD,QAAQ,MAAM,MAAM,IAAI,uCAAuC,CAAC,GAChE,QAAQ,KAAK,CAAC,IAGhB,MAAM,uBAAuB,WAAW;EAC1C,SAAS,OAAO;AACd,YAAQ,MAAM,MAAM,IAAI,UAAU,gBAAgB,KAAK,CAAC,EAAE,CAAC,GAC3D,QAAQ,KAAK,CAAC;EAChB;AACF;AAMA,eAAsB,uBAAuB,aAAmB;AAC9D,QAAM,YAAY,KAAK,KAAK,aAAa,0BAA0B,SAAS;AAK5E,UAAQ,IAAI,MAAM,IAAI,8BAA8B,CAAC,GACrD,MAAM,GAAG,GAAG,WAAW,EAAE,WAAW,IAAM,OAAO,GAAI,CAAE,GAGvD,QAAQ,IAAI,MAAM,IAAI,kBAAkB,CAAC;AACzC,QAAM,QAAQ,MAAM,MAAM,KAAK,WAAW,GAEpC,eAAe,IAAI,wBAAwB,OAAO,WAAW;AACnE,UAAQ,IAAI,MAAM,IAAI,2CAA2C,CAAC,GAClE,MAAM,aAAa,cAAa,GAEhC,QAAQ,IAAI,MAAM,MAAM,oDAA+C,CAAC,GACxE,QAAQ,IAAI,MAAM,IAAI,uDAAuD,CAAC,GAC9E,QAAQ,IAAI,MAAM,IAAI,uDAAuD,CAAC;AAChF;AAMA,eAAsB,gBAAgB,aAAmB;AACvD,QAAM,YAAY,KAAK,KAAK,aAAa,0BAA0B,SAAS;AAC5E,MAAI;AAEF,YADgB,MAAM,GAAG,QAAQ,SAAS,GAC3B,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC;EAC9C,SAAS,OAAgB;AAEvB,QADY,MACJ,SAAS;AAAU,aAAO;AAClC,UAAM;EACR;AACF;",
6
6
  "names": []
7
7
  }
@@ -4,7 +4,7 @@ import { MutationHandler } from "../core/mutation-handler.js";
4
4
  import { StagingAreaManager } from "../core/staging-area.js";
5
5
  import { StagedChangesetStorage } from "../core/staged-changeset-storage.js";
6
6
  import { findElementLayer } from "../utils/element-utils.js";
7
- import { CLIError, handleError, findSimilar, formatValidOptions } from "../utils/errors.js";
7
+ import { CLIError, handleError, handleSuccess, handleInfo, findSimilar, formatValidOptions } from "../utils/errors.js";
8
8
  import { isValidNodeType, getNodeTypesForLayer, normalizeNodeType } from "../generated/node-types.js";
9
9
  import { Element } from "../core/element.js";
10
10
  import { Layer } from "../core/layer.js";
@@ -27,7 +27,7 @@ async function updateCommand(id, options) {
27
27
  }) : null;
28
28
  try {
29
29
  validateUpdateSourceReferenceOptions(options);
30
- const model = await Model.load(), layerName = await findElementLayer(model, id);
30
+ const model = await Model.load(options.model || process.cwd()), layerName = await findElementLayer(model, id);
31
31
  if (!layerName) {
32
32
  const stagingManager = new StagingAreaManager(model.rootPath, model), storage = new StagedChangesetStorage(model.rootPath), activeId = await stagingManager.getActiveId();
33
33
  if (activeId) {
@@ -36,7 +36,7 @@ async function updateCommand(id, options) {
36
36
  const addChange = changeset.changes.find((c) => c.type === "add" && c.elementId === id);
37
37
  if (addChange && addChange.after) {
38
38
  if (!(options.name || options.description !== void 0 || options.attributes || options.sourceFile || options.clearSourceReference || options.type)) {
39
- console.log(ansis.yellow("No fields specified for update"));
39
+ handleInfo(ansis.yellow("No fields specified for update"));
40
40
  return;
41
41
  }
42
42
  const after = { ...addChange.after };
@@ -91,7 +91,11 @@ ${errorMessages}`, 1, [
91
91
  layerName: addChange.layerName,
92
92
  before: addChange.before,
93
93
  after
94
- }), console.log(ansis.green(`\u2713 Updated staged element ${ansis.bold(id)}`));
94
+ }), handleSuccess(`Updated staged element ${ansis.bold(id)}`, {
95
+ elementId: id,
96
+ layer: addChange.layerName,
97
+ changesetStatus: "staged"
98
+ }, { verbose: options.verbose });
95
99
  return;
96
100
  }
97
101
  }
@@ -105,7 +109,7 @@ ${errorMessages}`, 1, [
105
109
  if (!element)
106
110
  throw new CLIError(`Element ${id} not found`, 1);
107
111
  if (!(options.name || options.description !== void 0 || options.type || options.attributes || options.sourceFile || options.clearSourceReference)) {
108
- console.log(ansis.yellow("No fields specified for update"));
112
+ handleInfo(ansis.yellow("No fields specified for update"));
109
113
  return;
110
114
  }
111
115
  await new MutationHandler(model, id, layerName).executeUpdate(element, async (elem, after) => {
@@ -151,7 +155,16 @@ ${errorMessages}`, 1, [
151
155
  newRef && (elem.setSourceReference(newRef), after.sourceReference = newRef);
152
156
  }
153
157
  layer.updateElement(elem);
154
- }), console.log(ansis.green(`\u2713 Updated element ${ansis.bold(id)}`)), options.verbose && (console.log(ansis.dim(` Layer: ${layerName}`)), options.type && console.log(ansis.dim(` Type: ${options.type}`)), options.name && console.log(ansis.dim(` Name: ${options.name}`)), options.description && console.log(ansis.dim(` Description: ${options.description}`)), options.sourceFile && console.log(ansis.dim(` Source: ${options.sourceFile}`)), options.clearSourceReference && console.log(ansis.dim(" Source: cleared")));
158
+ });
159
+ const changes = {};
160
+ options.type && (changes.type = options.type), options.name && (changes.name = options.name), options.description !== void 0 && (changes.description = options.description), options.sourceFile && (changes.source = options.sourceFile), options.clearSourceReference && (changes.source = "cleared");
161
+ const details = {
162
+ elementId: id,
163
+ layer: layerName,
164
+ name: element.name,
165
+ changes
166
+ };
167
+ handleSuccess(`Updated element ${ansis.bold(id)}`, details, { verbose: options.verbose });
155
168
  } catch (error) {
156
169
  isTelemetryEnabled && span && (span.recordException(error), span.setStatus({ code: 2, message: error.message })), handleError(error);
157
170
  } finally {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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 { MutationHandler } from \"../core/mutation-handler.js\";\nimport { StagingAreaManager } from \"../core/staging-area.js\";\nimport { StagedChangesetStorage } from \"../core/staged-changeset-storage.js\";\nimport { findElementLayer } from \"../utils/element-utils.js\";\nimport { CLIError, handleError, findSimilar, formatValidOptions } from \"../utils/errors.js\";\nimport { isValidNodeType, getNodeTypesForLayer, normalizeNodeType } from \"../generated/node-types.js\";\nimport { Element } from \"../core/element.js\";\nimport { Layer } from \"../core/layer.js\";\nimport { SchemaValidator } from \"../validators/schema-validator.js\";\nimport { validateSourceReferenceOptions, buildSourceReference } from \"../utils/source-reference.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 type?: string;\n attributes?: string;\n sourceFile?: string;\n sourceSymbol?: string;\n sourceProvenance?: string;\n sourceRepoRemote?: string;\n sourceRepoCommit?: string;\n clearSourceReference?: boolean;\n verbose?: boolean;\n debug?: boolean;\n}\n\n/**\n * Validate update-specific source reference options\n */\nfunction validateUpdateSourceReferenceOptions(options: UpdateOptions): void {\n const hasSourceOptions =\n options.sourceFile ||\n options.sourceSymbol ||\n options.sourceProvenance ||\n options.sourceRepoRemote ||\n options.sourceRepoCommit;\n\n if (options.clearSourceReference && hasSourceOptions) {\n throw new CLIError(\n \"Cannot use --clear-source-reference with other source reference options\",\n 1,\n [\"Specify either --clear-source-reference or other source options, not both\"]\n );\n }\n\n // Use shared validation for source reference options if any are provided\n if (hasSourceOptions) {\n validateSourceReferenceOptions(options);\n }\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.type) changedFields.push(\"type\");\n if (options.attributes) changedFields.push(\"attributes\");\n if (options.sourceFile || options.clearSourceReference) changedFields.push(\"sourceReference\");\n\n const span = isTelemetryEnabled\n ? startSpan(\"element.update\", {\n \"element.id\": id,\n \"element.changed_fields\": changedFields.join(\",\"),\n })\n : null;\n\n try {\n // Validate source reference options\n validateUpdateSourceReferenceOptions(options);\n\n // Load model\n const model = await Model.load();\n\n // Find element\n const layerName = await findElementLayer(model, id);\n if (!layerName) {\n // Not in committed model \u2014 check active changeset for a staged ADD\n const stagingManager = new StagingAreaManager(model.rootPath, model);\n const storage = new StagedChangesetStorage(model.rootPath);\n const activeId = await stagingManager.getActiveId();\n\n if (activeId) {\n const changeset = await storage.load(activeId);\n if (changeset) {\n const addChange = changeset.changes.find(\n (c) => c.type === \"add\" && c.elementId === id\n );\n if (addChange && addChange.after) {\n const hasUpdates =\n options.name ||\n options.description !== undefined ||\n options.attributes ||\n options.sourceFile ||\n options.clearSourceReference ||\n options.type;\n\n if (!hasUpdates) {\n console.log(ansis.yellow(\"No fields specified for update\"));\n return;\n }\n\n const after: Record<string, unknown> = { ...addChange.after };\n\n if (options.type) {\n const stagedLayerName = addChange.layerName;\n if (!isValidNodeType(stagedLayerName, options.type)) {\n const validNodeTypes = getNodeTypesForLayer(stagedLayerName);\n const typeNames = validNodeTypes.map((t) => t.type).sort();\n const similar = findSimilar(options.type, typeNames, 3);\n const suggestions: string[] = [`Valid types for ${stagedLayerName}: ${formatValidOptions(typeNames)}`];\n if (similar.length > 0) suggestions.unshift(`Did you mean: ${similar.join(\" or \")}?`);\n throw new CLIError(`Invalid element type \"${options.type}\" for layer \"${stagedLayerName}\"`, 1, suggestions);\n }\n const resolvedNewType = normalizeNodeType(stagedLayerName, options.type);\n after.type = resolvedNewType;\n after.spec_node_id = `${stagedLayerName}.${resolvedNewType}`;\n }\n if (options.name) after.name = options.name;\n if (options.description !== undefined) {\n after.description = options.description || undefined;\n }\n if (options.attributes) {\n try {\n const parsed = JSON.parse(options.attributes);\n after.attributes = parsed;\n } catch {\n throw new CLIError(\"Invalid JSON in --attributes\", 1, [\n \"Ensure your JSON is valid and properly formatted\",\n ]);\n }\n }\n if (options.clearSourceReference) {\n delete after.source_reference;\n } else if (options.sourceFile) {\n const newRef = buildSourceReference(options);\n if (newRef) after.source_reference = newRef;\n }\n\n // Validate schema if type or attributes changed in staged element\n if (options.type || options.attributes) {\n const stagedLayerName = addChange.layerName;\n const tempElem = Element.fromSpecNode({\n id: String(after.id ?? addChange.elementId ?? \"staged\"),\n path: String(after.path ?? addChange.elementId ?? \"\"),\n spec_node_id: String(after.spec_node_id ?? \"\"),\n type: String(after.type ?? \"\"),\n layer_id: String(after.layer_id ?? stagedLayerName),\n name: String(after.name ?? \"\"),\n attributes: (after.attributes as Record<string, unknown>) || {},\n });\n const schemaValidator = new SchemaValidator();\n const tempLayer = new Layer(stagedLayerName);\n tempLayer.addElement(tempElem);\n const attrValidation = await schemaValidator.validateLayer(tempLayer);\n if (!attrValidation.isValid()) {\n const errorMessages = attrValidation.errors\n .map((e) => ` ${e.message}`)\n .join(\"\\n\");\n throw new CLIError(\n `Updated attributes fail schema validation:\\n${errorMessages}`,\n 1,\n [\n options.type\n ? `Incompatible attributes must be manually cleaned up before changing type. Run \"dr schema ${stagedLayerName} ${after.type}\" to see the required attributes`\n : `Run \"dr schema ${stagedLayerName} ${after.type}\" to see the required attributes`,\n ]\n );\n }\n }\n\n await storage.updateChange(activeId, id, {\n type: addChange.type,\n elementId: addChange.elementId,\n layerName: addChange.layerName,\n before: addChange.before,\n after,\n });\n\n console.log(ansis.green(`\u2713 Updated staged element ${ansis.bold(id)}`));\n return;\n }\n }\n }\n\n throw new CLIError(`Element ${id} not found`, 1);\n }\n\n const layer = await model.getLayer(layerName);\n if (!layer) {\n throw new CLIError(`Layer ${layerName} not found`, 1);\n }\n\n const element = layer.getElement(id);\n if (!element) {\n throw new CLIError(`Element ${id} not found`, 1);\n }\n\n // Validate that at least one field is specified\n const hasUpdates =\n options.name ||\n options.description !== undefined ||\n options.type ||\n options.attributes ||\n options.sourceFile ||\n options.clearSourceReference;\n\n if (!hasUpdates) {\n console.log(ansis.yellow(\"No fields specified for update\"));\n return;\n }\n\n // Single unified mutation handler for update\n const handler = new MutationHandler(model, id, layerName);\n\n // Execute update through unified path (handles staging and base model consistently)\n // The mutator function applies all updates in a single pass with validated JSON parsing\n await handler.executeUpdate(element, async (elem, after) => {\n // Resolve new type (validate name only \u2014 don't mutate live elem yet)\n let resolvedNewType: string | undefined;\n if (options.type) {\n if (!isValidNodeType(layerName, options.type)) {\n const validNodeTypes = getNodeTypesForLayer(layerName);\n const typeNames = validNodeTypes.map((t) => t.type).sort();\n const similar = findSimilar(options.type, typeNames, 3);\n const suggestions: string[] = [`Valid types for ${layerName}: ${formatValidOptions(typeNames)}`];\n if (similar.length > 0) suggestions.unshift(`Did you mean: ${similar.join(\" or \")}?`);\n throw new CLIError(`Invalid element type \"${options.type}\" for layer \"${layerName}\"`, 1, suggestions);\n }\n resolvedNewType = normalizeNodeType(layerName, options.type);\n }\n\n // Parse attributes (don't mutate yet)\n let parsedAttributes: Record<string, unknown> | undefined;\n if (options.attributes) {\n try {\n parsedAttributes = JSON.parse(options.attributes) as Record<string, unknown>;\n } catch (e) {\n throw new CLIError(\"Invalid JSON in --attributes\", 1, [\n \"Ensure your JSON is valid and properly formatted\",\n ]);\n }\n }\n\n // Validate against schema using a temp element BEFORE mutating the live elem\n if (resolvedNewType || parsedAttributes) {\n const candidateType = resolvedNewType ?? elem.type;\n const tempElem = new Element({\n ...elem.toJSON(),\n type: candidateType,\n spec_node_id: `${layerName}.${candidateType}`,\n attributes: parsedAttributes ?? elem.attributes,\n });\n const schemaValidator = new SchemaValidator();\n const tempLayer = new Layer(layerName);\n tempLayer.addElement(tempElem);\n const attrValidation = await schemaValidator.validateLayer(tempLayer);\n if (!attrValidation.isValid()) {\n const errorMessages = attrValidation.errors\n .map((e) => ` ${e.message}`)\n .join(\"\\n\");\n throw new CLIError(\n `Updated attributes fail schema validation:\\n${errorMessages}`,\n 1,\n [\n resolvedNewType\n ? `Incompatible attributes must be manually cleaned up before changing type. Run \"dr schema ${layerName} ${candidateType}\" to see the required attributes`\n : `Run \"dr schema ${layerName} ${elem.type}\" to see the required attributes`,\n ]\n );\n }\n }\n\n // Validation passed \u2014 apply all mutations to live element\n if (resolvedNewType) {\n elem.type = resolvedNewType;\n after.type = resolvedNewType;\n elem.spec_node_id = `${layerName}.${resolvedNewType}`;\n after.spec_node_id = `${layerName}.${resolvedNewType}`;\n }\n\n if (options.name) {\n elem.name = options.name;\n after.name = options.name;\n }\n\n if (options.description !== undefined) {\n elem.description = options.description || undefined;\n after.description = options.description || undefined;\n }\n\n if (parsedAttributes) {\n elem.attributes = parsedAttributes;\n after.attributes = parsedAttributes;\n }\n\n if (options.clearSourceReference) {\n elem.setSourceReference(undefined);\n after.sourceReference = undefined;\n } else if (options.sourceFile) {\n const newRef = buildSourceReference(options);\n if (newRef) {\n elem.setSourceReference(newRef);\n after.sourceReference = newRef;\n }\n }\n\n // Update element in layer graph to persist mutations\n layer.updateElement(elem);\n });\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.type) {\n console.log(ansis.dim(` Type: ${options.type}`));\n }\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 if (options.sourceFile) {\n console.log(ansis.dim(` Source: ${options.sourceFile}`));\n }\n if (options.clearSourceReference) {\n console.log(ansis.dim(` Source: cleared`));\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 handleError(error);\n } finally {\n if (isTelemetryEnabled) {\n endSpan(span);\n }\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Update an element\n */\n\nimport ansis from \"ansis\";\nimport { Model } from \"../core/model.js\";\nimport { MutationHandler } from \"../core/mutation-handler.js\";\nimport { StagingAreaManager } from \"../core/staging-area.js\";\nimport { StagedChangesetStorage } from \"../core/staged-changeset-storage.js\";\nimport { findElementLayer } from \"../utils/element-utils.js\";\nimport { CLIError, handleError, handleSuccess, handleInfo, findSimilar, formatValidOptions } from \"../utils/errors.js\";\nimport { isValidNodeType, getNodeTypesForLayer, normalizeNodeType } from \"../generated/node-types.js\";\nimport { Element } from \"../core/element.js\";\nimport { Layer } from \"../core/layer.js\";\nimport { SchemaValidator } from \"../validators/schema-validator.js\";\nimport { validateSourceReferenceOptions, buildSourceReference } from \"../utils/source-reference.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 model?: string;\n name?: string;\n description?: string;\n type?: string;\n attributes?: string;\n sourceFile?: string;\n sourceSymbol?: string;\n sourceProvenance?: string;\n sourceRepoRemote?: string;\n sourceRepoCommit?: string;\n clearSourceReference?: boolean;\n verbose?: boolean;\n debug?: boolean;\n}\n\n/**\n * Validate update-specific source reference options\n */\nfunction validateUpdateSourceReferenceOptions(options: UpdateOptions): void {\n const hasSourceOptions =\n options.sourceFile ||\n options.sourceSymbol ||\n options.sourceProvenance ||\n options.sourceRepoRemote ||\n options.sourceRepoCommit;\n\n if (options.clearSourceReference && hasSourceOptions) {\n throw new CLIError(\n \"Cannot use --clear-source-reference with other source reference options\",\n 1,\n [\"Specify either --clear-source-reference or other source options, not both\"]\n );\n }\n\n // Use shared validation for source reference options if any are provided\n if (hasSourceOptions) {\n validateSourceReferenceOptions(options);\n }\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.type) changedFields.push(\"type\");\n if (options.attributes) changedFields.push(\"attributes\");\n if (options.sourceFile || options.clearSourceReference) changedFields.push(\"sourceReference\");\n\n const span = isTelemetryEnabled\n ? startSpan(\"element.update\", {\n \"element.id\": id,\n \"element.changed_fields\": changedFields.join(\",\"),\n })\n : null;\n\n try {\n // Validate source reference options\n validateUpdateSourceReferenceOptions(options);\n\n // Load model\n const model = await Model.load(options.model || process.cwd());\n\n // Find element\n const layerName = await findElementLayer(model, id);\n if (!layerName) {\n // Not in committed model \u2014 check active changeset for a staged ADD\n const stagingManager = new StagingAreaManager(model.rootPath, model);\n const storage = new StagedChangesetStorage(model.rootPath);\n const activeId = await stagingManager.getActiveId();\n\n if (activeId) {\n const changeset = await storage.load(activeId);\n if (changeset) {\n const addChange = changeset.changes.find(\n (c) => c.type === \"add\" && c.elementId === id\n );\n if (addChange && addChange.after) {\n const hasUpdates =\n options.name ||\n options.description !== undefined ||\n options.attributes ||\n options.sourceFile ||\n options.clearSourceReference ||\n options.type;\n\n if (!hasUpdates) {\n handleInfo(ansis.yellow(\"No fields specified for update\"));\n return;\n }\n\n const after: Record<string, unknown> = { ...addChange.after };\n\n if (options.type) {\n const stagedLayerName = addChange.layerName;\n if (!isValidNodeType(stagedLayerName, options.type)) {\n const validNodeTypes = getNodeTypesForLayer(stagedLayerName);\n const typeNames = validNodeTypes.map((t) => t.type).sort();\n const similar = findSimilar(options.type, typeNames, 3);\n const suggestions: string[] = [`Valid types for ${stagedLayerName}: ${formatValidOptions(typeNames)}`];\n if (similar.length > 0) suggestions.unshift(`Did you mean: ${similar.join(\" or \")}?`);\n throw new CLIError(`Invalid element type \"${options.type}\" for layer \"${stagedLayerName}\"`, 1, suggestions);\n }\n const resolvedNewType = normalizeNodeType(stagedLayerName, options.type);\n after.type = resolvedNewType;\n after.spec_node_id = `${stagedLayerName}.${resolvedNewType}`;\n }\n if (options.name) after.name = options.name;\n if (options.description !== undefined) {\n after.description = options.description || undefined;\n }\n if (options.attributes) {\n try {\n const parsed = JSON.parse(options.attributes);\n after.attributes = parsed;\n } catch {\n throw new CLIError(\"Invalid JSON in --attributes\", 1, [\n \"Ensure your JSON is valid and properly formatted\",\n ]);\n }\n }\n if (options.clearSourceReference) {\n delete after.source_reference;\n } else if (options.sourceFile) {\n const newRef = buildSourceReference(options);\n if (newRef) after.source_reference = newRef;\n }\n\n // Validate schema if type or attributes changed in staged element\n if (options.type || options.attributes) {\n const stagedLayerName = addChange.layerName;\n const tempElem = Element.fromSpecNode({\n id: String(after.id ?? addChange.elementId ?? \"staged\"),\n path: String(after.path ?? addChange.elementId ?? \"\"),\n spec_node_id: String(after.spec_node_id ?? \"\"),\n type: String(after.type ?? \"\"),\n layer_id: String(after.layer_id ?? stagedLayerName),\n name: String(after.name ?? \"\"),\n attributes: (after.attributes as Record<string, unknown>) || {},\n });\n const schemaValidator = new SchemaValidator();\n const tempLayer = new Layer(stagedLayerName);\n tempLayer.addElement(tempElem);\n const attrValidation = await schemaValidator.validateLayer(tempLayer);\n if (!attrValidation.isValid()) {\n const errorMessages = attrValidation.errors\n .map((e) => ` ${e.message}`)\n .join(\"\\n\");\n throw new CLIError(\n `Updated attributes fail schema validation:\\n${errorMessages}`,\n 1,\n [\n options.type\n ? `Incompatible attributes must be manually cleaned up before changing type. Run \"dr schema ${stagedLayerName} ${after.type}\" to see the required attributes`\n : `Run \"dr schema ${stagedLayerName} ${after.type}\" to see the required attributes`,\n ]\n );\n }\n }\n\n await storage.updateChange(activeId, id, {\n type: addChange.type,\n elementId: addChange.elementId,\n layerName: addChange.layerName,\n before: addChange.before,\n after,\n });\n\n handleSuccess(`Updated staged element ${ansis.bold(id)}`, {\n elementId: id,\n layer: addChange.layerName,\n changesetStatus: \"staged\",\n }, { verbose: options.verbose });\n return;\n }\n }\n }\n\n throw new CLIError(`Element ${id} not found`, 1);\n }\n\n const layer = await model.getLayer(layerName);\n if (!layer) {\n throw new CLIError(`Layer ${layerName} not found`, 1);\n }\n\n const element = layer.getElement(id);\n if (!element) {\n throw new CLIError(`Element ${id} not found`, 1);\n }\n\n // Validate that at least one field is specified\n const hasUpdates =\n options.name ||\n options.description !== undefined ||\n options.type ||\n options.attributes ||\n options.sourceFile ||\n options.clearSourceReference;\n\n if (!hasUpdates) {\n handleInfo(ansis.yellow(\"No fields specified for update\"));\n return;\n }\n\n // Single unified mutation handler for update\n const handler = new MutationHandler(model, id, layerName);\n\n // Execute update through unified path (handles staging and base model consistently)\n // The mutator function applies all updates in a single pass with validated JSON parsing\n await handler.executeUpdate(element, async (elem, after) => {\n // Resolve new type (validate name only \u2014 don't mutate live elem yet)\n let resolvedNewType: string | undefined;\n if (options.type) {\n if (!isValidNodeType(layerName, options.type)) {\n const validNodeTypes = getNodeTypesForLayer(layerName);\n const typeNames = validNodeTypes.map((t) => t.type).sort();\n const similar = findSimilar(options.type, typeNames, 3);\n const suggestions: string[] = [`Valid types for ${layerName}: ${formatValidOptions(typeNames)}`];\n if (similar.length > 0) suggestions.unshift(`Did you mean: ${similar.join(\" or \")}?`);\n throw new CLIError(`Invalid element type \"${options.type}\" for layer \"${layerName}\"`, 1, suggestions);\n }\n resolvedNewType = normalizeNodeType(layerName, options.type);\n }\n\n // Parse attributes (don't mutate yet)\n let parsedAttributes: Record<string, unknown> | undefined;\n if (options.attributes) {\n try {\n parsedAttributes = JSON.parse(options.attributes) as Record<string, unknown>;\n } catch (e) {\n throw new CLIError(\"Invalid JSON in --attributes\", 1, [\n \"Ensure your JSON is valid and properly formatted\",\n ]);\n }\n }\n\n // Validate against schema using a temp element BEFORE mutating the live elem\n if (resolvedNewType || parsedAttributes) {\n const candidateType = resolvedNewType ?? elem.type;\n const tempElem = new Element({\n ...elem.toJSON(),\n type: candidateType,\n spec_node_id: `${layerName}.${candidateType}`,\n attributes: parsedAttributes ?? elem.attributes,\n });\n const schemaValidator = new SchemaValidator();\n const tempLayer = new Layer(layerName);\n tempLayer.addElement(tempElem);\n const attrValidation = await schemaValidator.validateLayer(tempLayer);\n if (!attrValidation.isValid()) {\n const errorMessages = attrValidation.errors\n .map((e) => ` ${e.message}`)\n .join(\"\\n\");\n throw new CLIError(\n `Updated attributes fail schema validation:\\n${errorMessages}`,\n 1,\n [\n resolvedNewType\n ? `Incompatible attributes must be manually cleaned up before changing type. Run \"dr schema ${layerName} ${candidateType}\" to see the required attributes`\n : `Run \"dr schema ${layerName} ${elem.type}\" to see the required attributes`,\n ]\n );\n }\n }\n\n // Validation passed \u2014 apply all mutations to live element\n if (resolvedNewType) {\n elem.type = resolvedNewType;\n after.type = resolvedNewType;\n elem.spec_node_id = `${layerName}.${resolvedNewType}`;\n after.spec_node_id = `${layerName}.${resolvedNewType}`;\n }\n\n if (options.name) {\n elem.name = options.name;\n after.name = options.name;\n }\n\n if (options.description !== undefined) {\n elem.description = options.description || undefined;\n after.description = options.description || undefined;\n }\n\n if (parsedAttributes) {\n elem.attributes = parsedAttributes;\n after.attributes = parsedAttributes;\n }\n\n if (options.clearSourceReference) {\n elem.setSourceReference(undefined);\n after.sourceReference = undefined;\n } else if (options.sourceFile) {\n const newRef = buildSourceReference(options);\n if (newRef) {\n elem.setSourceReference(newRef);\n after.sourceReference = newRef;\n }\n }\n\n // Update element in layer graph to persist mutations\n layer.updateElement(elem);\n });\n\n const changes: Record<string, unknown> = {};\n if (options.type) changes.type = options.type;\n if (options.name) changes.name = options.name;\n if (options.description !== undefined) changes.description = options.description;\n if (options.sourceFile) changes.source = options.sourceFile;\n if (options.clearSourceReference) changes.source = \"cleared\";\n\n const details: Record<string, unknown> = {\n elementId: id,\n layer: layerName,\n name: element.name,\n changes,\n };\n\n handleSuccess(`Updated element ${ansis.bold(id)}`, details, { verbose: options.verbose });\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 handleError(error);\n } finally {\n if (isTelemetryEnabled) {\n endSpan(span);\n }\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -8,6 +8,7 @@ import { ValidationFormatter } from "../validators/validation-formatter.js";
8
8
  import { getErrorMessage } from "../utils/errors.js";
9
9
  import { RELATIONSHIPS_BY_SOURCE, RELATIONSHIPS_BY_DESTINATION } from "../generated/relationship-index.js";
10
10
  import { getActiveSpan } from "../telemetry/index.js";
11
+ import { StagedChangesetStorage } from "../core/staged-changeset-storage.js";
11
12
  async function findJsonFiles(dir, baseDir = dir) {
12
13
  const entries = await fs.readdir(dir, { withFileTypes: !0 }), files = [];
13
14
  for (const entry of entries) {
@@ -112,9 +113,36 @@ Orphaned Elements (${stats.orphanedElements.length})`)), console.log(ansis.dim(`
112
113
  const noSchemaCount = stats.orphanedElements.length - connectableCount - deadEndCount, parts = [];
113
114
  connectableCount > 0 && parts.push(`${connectableCount} connectable`), deadEndCount > 0 && parts.push(`${deadEndCount} dead-end (valid target types missing from model)`), noSchemaCount > 0 && parts.push(`${noSchemaCount} no-schema`), console.log(ansis.dim(`Summary: ${parts.length > 0 ? parts.join(", ") : "0 actionable"}`)), connectableCount > 0 && console.log(ansis.dim("Run /dr-relate --orphans to wire connectable elements"));
114
115
  }
116
+ async function reportStagedChangesIncluded(rootPath, changesetId, validatedLayerNames) {
117
+ try {
118
+ const changeset = await new StagedChangesetStorage(rootPath).load(changesetId);
119
+ if (!changeset || changeset.changes.length === 0)
120
+ return;
121
+ const validatedLayers = new Set(validatedLayerNames), relevantChangeCount = changeset.changes.filter((change) => validatedLayers.has(change.layerName)).length;
122
+ if (relevantChangeCount === 0)
123
+ return;
124
+ console.log(ansis.cyan(`\u2139 Including ${relevantChangeCount} staged change${relevantChangeCount === 1 ? "" : "s"} from changeset '${changesetId}'`)), console.log();
125
+ } catch (error) {
126
+ const message = getErrorMessage(error);
127
+ console.warn(ansis.yellow(`\u26A0 Could not load changeset '${changesetId}': ${message}`)), console.log();
128
+ }
129
+ }
130
+ function parseModelPaths(modelPathArray) {
131
+ const result = {};
132
+ if (!modelPathArray || modelPathArray.length === 0)
133
+ return result;
134
+ for (const item of modelPathArray) {
135
+ const [modelName, ...rest] = item.split("="), modelPath = rest.join("=");
136
+ if (!modelName || !modelPath)
137
+ throw new Error(`Invalid --model-path format: '${item}'. Expected format: 'model-name=/path/to/model'`);
138
+ result[modelName.trim()] = modelPath.trim();
139
+ }
140
+ return result;
141
+ }
115
142
  async function validateCommand(options) {
143
+ options.modelPath && (options.modelPaths = parseModelPaths(options.modelPath));
116
144
  const activeSpan = getActiveSpan();
117
- activeSpan && (activeSpan.setAttribute("validate.layers", options.layers?.join(",") || "all"), activeSpan.setAttribute("validate.strict", options.strict || !1), activeSpan.setAttribute("validate.verbose", options.verbose || !1), activeSpan.setAttribute("validate.orphans", options.orphans || !1), activeSpan.setAttribute("validate.schemas", options.schemas || options.schema || !1), activeSpan.setAttribute("validate.output", options.output || "console"));
145
+ activeSpan && (activeSpan.setAttribute("validate.layers", options.layers?.join(",") || "all"), activeSpan.setAttribute("validate.strict", options.strict || !1), activeSpan.setAttribute("validate.verbose", options.verbose || !1), activeSpan.setAttribute("validate.orphans", options.orphans || !1), activeSpan.setAttribute("validate.schemas", options.schemas || options.schema || !1), activeSpan.setAttribute("validate.output", options.output || "console"), activeSpan.setAttribute("validate.scope", options.scope || "local"));
118
146
  try {
119
147
  if (options.schemas || options.schema) {
120
148
  await validateSchemaSynchronization(), activeSpan && (activeSpan.setAttribute("validate.mode", "schema-sync"), activeSpan.setAttribute("validate.result", "success"));
@@ -124,10 +152,20 @@ async function validateCommand(options) {
124
152
  if (options.orphans) {
125
153
  activeSpan && activeSpan.setAttribute("validate.mode", "orphans-only");
126
154
  const activeChangesetId2 = model.getActiveChangesetId(), modelForOrphans = activeChangesetId2 ? await model.getVirtualProjectionEngine().projectModel(model, activeChangesetId2) : model;
127
- await validateOrphansOnly(modelForOrphans, options.output), activeSpan && activeSpan.setAttribute("validate.result", "success");
155
+ activeChangesetId2 && await reportStagedChangesIncluded(model.rootPath, activeChangesetId2, options.layers && options.layers.length > 0 ? options.layers : modelForOrphans.layers.keys()), await validateOrphansOnly(modelForOrphans, options.output), activeSpan && activeSpan.setAttribute("validate.result", "success");
128
156
  return;
129
157
  }
130
- const validator = new Validator(), activeChangesetId = model.getActiveChangesetId(), modelToValidate = activeChangesetId ? await model.getVirtualProjectionEngine().projectModel(model, activeChangesetId) : model, result = await validator.validateModel(modelToValidate), stats = ValidationFormatter.calculateStats(modelToValidate);
158
+ const scope = options.scope || "local", activeChangesetId = model.getActiveChangesetId(), modelToValidate = activeChangesetId ? await model.getVirtualProjectionEngine().projectModel(model, activeChangesetId) : model;
159
+ activeChangesetId && await reportStagedChangesIncluded(model.rootPath, activeChangesetId, options.layers && options.layers.length > 0 ? options.layers : modelToValidate.layers.keys());
160
+ let result;
161
+ if (scope === "composed") {
162
+ const { ComposedReferenceValidator } = await import("../validators/composed-reference-validator.js");
163
+ result = await new ComposedReferenceValidator(options.modelPaths || {}).validateModel(modelToValidate);
164
+ } else if (scope === "local")
165
+ result = await new Validator().validateModel(modelToValidate);
166
+ else
167
+ throw new Error(`Invalid validation scope: '${scope}'. Expected 'local' or 'composed'.`);
168
+ const stats = ValidationFormatter.calculateStats(modelToValidate);
131
169
  for (const orphanId of stats.orphanedElements)
132
170
  result.addWarning({
133
171
  message: `Element '${orphanId}' is orphaned (no cross-layer references or intra-layer relationships)`,