@documentation-robotics/cli 0.2.0 → 0.3.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 (269) hide show
  1. package/README.md +36 -2
  2. package/dist/analysis/relationship-classifier.js +2 -9
  3. package/dist/analysis/relationship-classifier.js.map +2 -2
  4. package/dist/analyzers/call-graph-utils.js +2 -2
  5. package/dist/analyzers/call-graph-utils.js.map +2 -2
  6. package/dist/analyzers/cards/build.js +171 -0
  7. package/dist/analyzers/cards/build.js.map +7 -0
  8. package/dist/analyzers/cards/classify.js +42 -0
  9. package/dist/analyzers/cards/classify.js.map +7 -0
  10. package/dist/analyzers/cards/edge-index.js +44 -0
  11. package/dist/analyzers/cards/edge-index.js.map +7 -0
  12. package/dist/analyzers/cards/facts.js +46 -0
  13. package/dist/analyzers/cards/facts.js.map +7 -0
  14. package/dist/analyzers/cards/graph.js +42 -0
  15. package/dist/analyzers/cards/graph.js.map +7 -0
  16. package/dist/analyzers/cards/index.js +8 -0
  17. package/dist/analyzers/cards/index.js.map +7 -0
  18. package/dist/analyzers/cards/tier0.js +18 -0
  19. package/dist/analyzers/cards/tier0.js.map +7 -0
  20. package/dist/analyzers/cards/types.js +1 -0
  21. package/dist/analyzers/cards/types.js.map +7 -0
  22. package/dist/analyzers/cbm-analyzer.js +195 -171
  23. package/dist/analyzers/cbm-analyzer.js.map +2 -2
  24. package/dist/analyzers/cbm-responses.js +336 -0
  25. package/dist/analyzers/cbm-responses.js.map +7 -0
  26. package/dist/analyzers/edge-relationships.js +45 -0
  27. package/dist/analyzers/edge-relationships.js.map +7 -0
  28. package/dist/analyzers/mapping-loader.js +9 -0
  29. package/dist/analyzers/mapping-loader.js.map +2 -2
  30. package/dist/analyzers/tier0-rules.js +153 -0
  31. package/dist/analyzers/tier0-rules.js.map +7 -0
  32. package/dist/analyzers/types.js.map +1 -1
  33. package/dist/audit/relationships/ai/prompts.js +10 -10
  34. package/dist/audit/relationships/ai/prompts.js.map +2 -2
  35. package/dist/audit/relationships/analysis/layer-templates.js +52 -52
  36. package/dist/audit/relationships/analysis/layer-templates.js.map +1 -1
  37. package/dist/cli.js +12 -2
  38. package/dist/cli.js.map +2 -2
  39. package/dist/coding-agents/default-permissions.js +31 -1
  40. package/dist/coding-agents/default-permissions.js.map +2 -2
  41. package/dist/coding-agents/drbot-mcp.js +167 -0
  42. package/dist/coding-agents/drbot-mcp.js.map +7 -0
  43. package/dist/commands/add.js +3 -2
  44. package/dist/commands/add.js.map +2 -2
  45. package/dist/commands/analyzer.js +117 -14
  46. package/dist/commands/analyzer.js.map +2 -2
  47. package/dist/commands/changeset-verify.js +55 -0
  48. package/dist/commands/changeset-verify.js.map +7 -0
  49. package/dist/commands/changeset.js +43 -22
  50. package/dist/commands/changeset.js.map +2 -2
  51. package/dist/commands/delete.js +10 -4
  52. package/dist/commands/delete.js.map +2 -2
  53. package/dist/commands/farm.js +176 -60
  54. package/dist/commands/farm.js.map +2 -2
  55. package/dist/commands/mcp.js +19 -4
  56. package/dist/commands/mcp.js.map +2 -2
  57. package/dist/commands/relationship.js +207 -26
  58. package/dist/commands/relationship.js.map +2 -2
  59. package/dist/commands/update.js +42 -23
  60. package/dist/commands/update.js.map +2 -2
  61. package/dist/commands/upgrade.js +99 -11
  62. package/dist/commands/upgrade.js.map +3 -3
  63. package/dist/commands/version.js +1 -1
  64. package/dist/commands/visualize.js +4 -3
  65. package/dist/commands/visualize.js.map +2 -2
  66. package/dist/core/catalog-migration.js +33 -0
  67. package/dist/core/catalog-migration.js.map +7 -0
  68. package/dist/core/changeset-analysis.js +421 -0
  69. package/dist/core/changeset-analysis.js.map +7 -0
  70. package/dist/core/changeset-base-index.js +153 -0
  71. package/dist/core/changeset-base-index.js.map +7 -0
  72. package/dist/core/changeset-brief-delta.js +102 -0
  73. package/dist/core/changeset-brief-delta.js.map +7 -0
  74. package/dist/core/changeset-graph.js +281 -0
  75. package/dist/core/changeset-graph.js.map +7 -0
  76. package/dist/core/changeset-only-guard.js +32 -0
  77. package/dist/core/changeset-only-guard.js.map +7 -0
  78. package/dist/core/changeset-summary.js +68 -0
  79. package/dist/core/changeset-summary.js.map +7 -0
  80. package/dist/core/changeset-validator.js +66 -20
  81. package/dist/core/changeset-validator.js.map +2 -2
  82. package/dist/core/changeset-verify.js +518 -0
  83. package/dist/core/changeset-verify.js.map +7 -0
  84. package/dist/core/changeset.js +56 -9
  85. package/dist/core/changeset.js.map +2 -2
  86. package/dist/core/element-type-migration.js +145 -0
  87. package/dist/core/element-type-migration.js.map +7 -0
  88. package/dist/core/farm-manifest.js +12 -6
  89. package/dist/core/farm-manifest.js.map +2 -2
  90. package/dist/core/farm-sync-engine.js +1 -1
  91. package/dist/core/farm-sync-engine.js.map +2 -2
  92. package/dist/core/farm-sync-state.js +10 -2
  93. package/dist/core/farm-sync-state.js.map +2 -2
  94. package/dist/core/migration-registry.js +75 -46
  95. package/dist/core/migration-registry.js.map +2 -2
  96. package/dist/core/model-upgrade.js +102 -0
  97. package/dist/core/model-upgrade.js.map +7 -0
  98. package/dist/core/model.js +82 -30
  99. package/dist/core/model.js.map +2 -2
  100. package/dist/core/mutation-handler.js +71 -6
  101. package/dist/core/mutation-handler.js.map +2 -2
  102. package/dist/core/relationship-migration-table.js +6639 -0
  103. package/dist/core/relationship-migration-table.js.map +7 -0
  104. package/dist/core/relationship-migration.js +272 -0
  105. package/dist/core/relationship-migration.js.map +7 -0
  106. package/dist/core/relationship-prune.js +49 -0
  107. package/dist/core/relationship-prune.js.map +7 -0
  108. package/dist/core/relationship-staging.js +68 -0
  109. package/dist/core/relationship-staging.js.map +7 -0
  110. package/dist/core/relationships.js +26 -4
  111. package/dist/core/relationships.js.map +2 -2
  112. package/dist/core/report-data-model.js +7 -13
  113. package/dist/core/report-data-model.js.map +2 -2
  114. package/dist/core/staged-changeset-storage.js +64 -17
  115. package/dist/core/staged-changeset-storage.js.map +2 -2
  116. package/dist/core/staged-source-reference.js +28 -0
  117. package/dist/core/staged-source-reference.js.map +7 -0
  118. package/dist/core/staging-area.js +83 -18
  119. package/dist/core/staging-area.js.map +2 -2
  120. package/dist/core/virtual-projection.js +13 -5
  121. package/dist/core/virtual-projection.js.map +2 -2
  122. package/dist/export/archimate-exporter.js +1 -0
  123. package/dist/export/archimate-exporter.js.map +2 -2
  124. package/dist/generated/compiled-validators.js +2 -2
  125. package/dist/generated/compiled-validators.js.map +2 -2
  126. package/dist/generated/layer-registry.js +16 -16
  127. package/dist/generated/layer-registry.js.map +2 -2
  128. package/dist/generated/node-types.js +186 -168
  129. package/dist/generated/node-types.js.map +2 -2
  130. package/dist/generated/relationship-index.js +2236 -7790
  131. package/dist/generated/relationship-index.js.map +2 -2
  132. package/dist/integrations/claude_code/agents/dr-architect.md +16 -3
  133. package/dist/integrations/claude_code/agents/dr-extractor.md +1 -1
  134. package/dist/integrations/claude_code/commands/dr-analyzer.md +565 -0
  135. package/dist/integrations/claude_code/commands/dr-map.md +13 -0
  136. package/dist/integrations/claude_code/commands/dr-plan.md +40 -0
  137. package/dist/integrations/claude_code/commands/dr-relate.md +106 -96
  138. package/dist/integrations/claude_code/commands/dr-verify.md +45 -2
  139. package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +14 -0
  140. package/dist/integrations/claude_code/docs/MCP_SERVER.md +26 -2
  141. package/dist/integrations/claude_code/docs/QUICK_START.md +37 -8
  142. package/dist/integrations/claude_code/docs/README.md +48 -1
  143. package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +93 -72
  144. package/dist/integrations/claude_code/skills/dr_02_business_layer/SKILL.md +163 -126
  145. package/dist/integrations/claude_code/skills/dr_03_product_layer/SKILL.md +161 -126
  146. package/dist/integrations/claude_code/skills/dr_04_security_layer/SKILL.md +153 -133
  147. package/dist/integrations/claude_code/skills/dr_05_application_layer/SKILL.md +129 -100
  148. package/dist/integrations/claude_code/skills/dr_06_technology_layer/SKILL.md +111 -94
  149. package/dist/integrations/claude_code/skills/dr_07_api_layer/SKILL.md +143 -157
  150. package/dist/integrations/claude_code/skills/dr_08_data_model_layer/SKILL.md +117 -33
  151. package/dist/integrations/claude_code/skills/dr_09_dataset_layer/SKILL.md +74 -25
  152. package/dist/integrations/claude_code/skills/dr_10_ux_layer/SKILL.md +88 -12
  153. package/dist/integrations/claude_code/skills/dr_11_navigation_layer/SKILL.md +83 -24
  154. package/dist/integrations/claude_code/skills/dr_12_apm_layer/SKILL.md +57 -12
  155. package/dist/integrations/claude_code/skills/dr_13_testing_layer/SKILL.md +92 -15
  156. package/dist/integrations/claude_code/skills/dr_change_planning/SKILL.md +119 -0
  157. package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +3 -0
  158. package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +1 -1
  159. package/dist/integrations/github_copilot/agents/dr-architect.md +1 -1
  160. package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +182 -102
  161. package/dist/integrations/github_copilot/skills/dr_02_business_layer/SKILL.md +266 -148
  162. package/dist/integrations/github_copilot/skills/dr_03_product_layer/SKILL.md +547 -86
  163. package/dist/integrations/github_copilot/skills/dr_04_security_layer/SKILL.md +164 -169
  164. package/dist/integrations/github_copilot/skills/dr_05_application_layer/SKILL.md +420 -150
  165. package/dist/integrations/github_copilot/skills/dr_06_technology_layer/SKILL.md +331 -138
  166. package/dist/integrations/github_copilot/skills/dr_07_api_layer/SKILL.md +265 -201
  167. package/dist/integrations/github_copilot/skills/dr_08_data_model_layer/SKILL.md +181 -58
  168. package/dist/integrations/github_copilot/skills/dr_09_dataset_layer/SKILL.md +209 -57
  169. package/dist/integrations/github_copilot/skills/dr_10_ux_layer/SKILL.md +277 -112
  170. package/dist/integrations/github_copilot/skills/dr_11_navigation_layer/SKILL.md +219 -70
  171. package/dist/integrations/github_copilot/skills/dr_12_apm_layer/SKILL.md +189 -105
  172. package/dist/integrations/github_copilot/skills/dr_13_testing_layer/SKILL.md +222 -102
  173. package/dist/integrations/github_copilot/skills/dr_change_planning/SKILL.md +119 -0
  174. package/dist/integrations/github_copilot/skills/dr_changeset_reviewer/SKILL.md +3 -0
  175. package/dist/integrations/hash-utils.js.map +2 -2
  176. package/dist/mcp/api-key-manager.js +14 -1
  177. package/dist/mcp/api-key-manager.js.map +2 -2
  178. package/dist/mcp/changeset-only.js +34 -0
  179. package/dist/mcp/changeset-only.js.map +7 -0
  180. package/dist/mcp/tool-registry.js +19 -3
  181. package/dist/mcp/tool-registry.js.map +2 -2
  182. package/dist/mcp/tools/changeset-analysis-tools.js +78 -0
  183. package/dist/mcp/tools/changeset-analysis-tools.js.map +7 -0
  184. package/dist/mcp/tools/changeset-editing.js +184 -0
  185. package/dist/mcp/tools/changeset-editing.js.map +7 -0
  186. package/dist/mcp/tools/changeset-lifecycle.js +83 -0
  187. package/dist/mcp/tools/changeset-lifecycle.js.map +7 -0
  188. package/dist/mcp/tools/changeset-shared.js +31 -0
  189. package/dist/mcp/tools/changeset-shared.js.map +7 -0
  190. package/dist/mcp/tools/changeset-show.js +17 -1
  191. package/dist/mcp/tools/changeset-show.js.map +2 -2
  192. package/dist/mcp/tools/changeset-verify.js +43 -0
  193. package/dist/mcp/tools/changeset-verify.js.map +7 -0
  194. package/dist/mcp/tools/index.js +19 -1
  195. package/dist/mcp/tools/index.js.map +2 -2
  196. package/dist/mcp/tools/model-add.js +6 -5
  197. package/dist/mcp/tools/model-add.js.map +2 -2
  198. package/dist/mcp/tools/model-delete.js +2 -2
  199. package/dist/mcp/tools/model-delete.js.map +2 -2
  200. package/dist/mcp/tools/model-search.js +14 -0
  201. package/dist/mcp/tools/model-search.js.map +2 -2
  202. package/dist/mcp/tools/model-show.js +29 -2
  203. package/dist/mcp/tools/model-show.js.map +2 -2
  204. package/dist/mcp/tools/model-trace.js +93 -30
  205. package/dist/mcp/tools/model-trace.js.map +2 -2
  206. package/dist/mcp/tools/model-update.js +35 -19
  207. package/dist/mcp/tools/model-update.js.map +2 -2
  208. package/dist/mcp/tools/relationship-tools.js +98 -0
  209. package/dist/mcp/tools/relationship-tools.js.map +7 -0
  210. package/dist/mcp/tools/shared.js +25 -2
  211. package/dist/mcp/tools/shared.js.map +2 -2
  212. package/dist/mcp/tools/spec-type.js +90 -0
  213. package/dist/mcp/tools/spec-type.js.map +7 -0
  214. package/dist/schemas/bundled/analyzers/cbm.json +1186 -68
  215. package/dist/schemas/bundled/analyzers/manifest.json +2 -2
  216. package/dist/schemas/bundled/api.json +1029 -1658
  217. package/dist/schemas/bundled/apm.json +499 -734
  218. package/dist/schemas/bundled/application.json +735 -371
  219. package/dist/schemas/bundled/base.json +3 -220
  220. package/dist/schemas/bundled/business.json +452 -366
  221. package/dist/schemas/bundled/data-model.json +395 -411
  222. package/dist/schemas/bundled/data-store.json +334 -558
  223. package/dist/schemas/bundled/manifest.json +18 -18
  224. package/dist/schemas/bundled/motivation.json +349 -368
  225. package/dist/schemas/bundled/navigation.json +415 -731
  226. package/dist/schemas/bundled/product.json +325 -159
  227. package/dist/schemas/bundled/security.json +864 -1232
  228. package/dist/schemas/bundled/technology.json +574 -703
  229. package/dist/schemas/bundled/testing.json +718 -1643
  230. package/dist/schemas/bundled/ux.json +739 -1352
  231. package/dist/server/schemas.js +8 -0
  232. package/dist/server/schemas.js.map +2 -2
  233. package/dist/server/server.js +218 -34
  234. package/dist/server/server.js.map +2 -2
  235. package/dist/server-entry.js +2 -1
  236. package/dist/server-entry.js.map +2 -2
  237. package/dist/telemetry/index.js +1 -1
  238. package/dist/utils/ansi-suppressor.js +6 -8
  239. package/dist/utils/ansi-suppressor.js.map +2 -2
  240. package/dist/utils/attribute-patch.js +41 -0
  241. package/dist/utils/attribute-patch.js.map +7 -0
  242. package/dist/utils/error-hints.js +29 -0
  243. package/dist/utils/error-hints.js.map +7 -0
  244. package/dist/utils/errors.js +9 -3
  245. package/dist/utils/errors.js.map +2 -2
  246. package/dist/utils/project-paths.js +26 -1
  247. package/dist/utils/project-paths.js.map +2 -2
  248. package/dist/utils/source-reference.js +14 -12
  249. package/dist/utils/source-reference.js.map +2 -2
  250. package/dist/utils/spec-version.js +2 -2
  251. package/dist/utils/spec-version.js.map +1 -1
  252. package/dist/validators/composed-reference-validator.js +5 -27
  253. package/dist/validators/composed-reference-validator.js.map +2 -2
  254. package/dist/validators/reference-validator.js +0 -7
  255. package/dist/validators/reference-validator.js.map +2 -2
  256. package/dist/validators/relationship-schema-validator.js +3 -3
  257. package/dist/validators/relationship-schema-validator.js.map +2 -2
  258. package/dist/validators/schema-validator.js +27 -9
  259. package/dist/validators/schema-validator.js.map +2 -2
  260. package/dist/validators/types.js.map +1 -1
  261. package/dist/viewer/README.md +6 -6
  262. package/dist/viewer/assets/main-CxzcuU_o.js +84 -0
  263. package/dist/viewer/assets/main-j-7ULYpm.css +1 -0
  264. package/dist/viewer/index.html +2 -2
  265. package/dist/viewer/manifest.json +10 -10
  266. package/dist/viewer/version.json +1 -1
  267. package/package.json +2 -1
  268. package/dist/viewer/assets/main-B7AOQXXX.js +0 -81
  269. package/dist/viewer/assets/main-vdrFrG7q.css +0 -1
@@ -146,6 +146,13 @@ const SimpleWSMessageSchema = z.object({
146
146
  }), ChangesetsListSchema = z.object({
147
147
  version: z.string(),
148
148
  changesets: z.record(z.string(), ChangesetMetadataSchema)
149
+ }), ChangesetActionResultSchema = z.object({
150
+ id: z.string(),
151
+ name: z.string(),
152
+ status: z.enum(["staged", "committed", "discarded"]),
153
+ committed: z.number().optional().describe("Changes applied to the model (commit only)"),
154
+ skipped: z.number().optional().describe("Changes skipped as already present (commit only)"),
155
+ failed: z.number().optional().describe("Changes that failed to apply (commit only)")
149
156
  }), ChangesetDetailSchema = z.object({
150
157
  id: z.string(),
151
158
  name: z.string(),
@@ -177,6 +184,7 @@ export {
177
184
  AnnotationSchema,
178
185
  AnnotationUpdateSchema,
179
186
  AnnotationsListSchema,
187
+ ChangesetActionResultSchema,
180
188
  ChangesetDetailSchema,
181
189
  ChangesetMetadataSchema,
182
190
  ChangesetsListSchema,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/server/schemas.ts"],
4
- "sourcesContent": ["import { z } from '@hono/zod-openapi';\nimport { CANONICAL_LAYER_NAMES } from '../core/layers.js';\n\n/**\n * Branded type for ElementId\n * Provides compile-time safety by distinguishing ElementId from plain strings\n * Use with parseElementId() to validate and brand a string at runtime\n *\n * This type is exported as a public API contract for external consumers who want\n * to use strong typing in their code. However, internal server.ts implementation\n * uses plain string keys in Maps and other internal structures for simplicity.\n *\n * USAGE PATTERNS:\n * - External/public APIs: Type function parameters with ElementId for contract clarity\n * - Internal state: Use plain strings in Maps/Sets for performance and simplicity\n * - Validation: Use parseElementId() to validate untrusted input\n * - Type guards: Use isElementId() for optional runtime type checking\n *\n * Example:\n * const brandedId = parseElementId(\"motivation.goal.customer-satisfaction\");\n * function processElement(id: ElementId) { ... } // Type-safe public API\n *\n * // Internal implementation can use plain strings:\n * private annotations: Map<string, Annotation> = new Map();\n */\nexport type ElementId = string & { readonly __brand: 'ElementId' };\n\n/**\n * Parse and validate a string as an ElementId\n * Returns a branded type for compile-time type safety\n */\nexport function parseElementId(input: string): ElementId {\n const result = ElementIdSchema.safeParse(input);\n if (!result.success) {\n const errorMessages = result.error.issues.map(issue => issue.message).join('; ');\n throw new Error(`Invalid element ID: ${errorMessages}`);\n }\n return result.data as ElementId;\n}\n\n/**\n * Type guard for ElementId\n * Use with function parameters for optional compile-time checking\n */\nexport function isElementId(value: unknown): value is ElementId {\n return typeof value === 'string' && ElementIdSchema.safeParse(value).success;\n}\n\n// WebSocket message schemas with runtime validation\nexport const SimpleWSMessageSchema = z.object({\n type: z.enum(['subscribe', 'annotate', 'ping']),\n topics: z.array(z.string()).optional(),\n annotation: z.object({\n elementId: z.string(),\n author: z.string(),\n text: z.string(),\n timestamp: z.string(),\n }).optional(),\n});\n\nexport const JSONRPCRequestSchema = z.object({\n jsonrpc: z.literal('2.0'),\n method: z.string(),\n params: z.unknown().optional(),\n id: z.union([z.string(), z.number()]).optional(),\n});\n\nexport const JSONRPCResponseSchema = z.object({\n jsonrpc: z.literal('2.0'),\n result: z.unknown().optional(),\n error: z.object({\n code: z.number(),\n message: z.string(),\n data: z.unknown().optional(),\n }).optional(),\n id: z.union([z.string(), z.number(), z.null()]),\n}).refine(\n (data) => (data.result !== undefined) !== (data.error !== undefined),\n {\n message: \"JSON-RPC 2.0 response must contain either 'result' or 'error', not both\",\n path: [\"response\"],\n }\n);\n\nexport const WSMessageSchema = z.union([\n SimpleWSMessageSchema,\n JSONRPCRequestSchema,\n JSONRPCResponseSchema,\n]);\n\n// Base schemas for common types\n// Enforces three-part element ID format: layer.type.name\n// - layer: canonical layer name (e.g., motivation, api, data-store)\n// - type: lowercase element type (e.g., goal, endpoint, entity)\n// - name: kebab-case element name (e.g., customer-satisfaction)\n//\n// Example valid IDs:\n// - motivation.goal.customer-satisfaction\n// - api.endpoint.create-order\n// - data-store.table.user-profile\n// - a.b.c (minimal single-character segments, valid for testing purposes)\n// - ux.a.test (two-character segments are valid)\nexport const ElementIdSchema = z.string()\n .min(1, 'Element ID is required')\n .regex(\n /^[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*$/,\n 'Invalid element ID format. Must be: layer.type.name (e.g., motivation.goal.customer-satisfaction)'\n );\n\nexport const TimestampSchema = z.string().datetime();\n\n/**\n * Tag validation schema.\n *\n * Format: lowercase alphanumeric with optional internal hyphens\n * Pattern: /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/\n *\n * Constraints:\n * - Cannot start or end with hyphens: Prevents URL slugification issues and confusion with CLI flags (e.g., --tag-name)\n * - Single character tags allowed (e.g., \"a\", \"1\") via the second alternation\n * - Matches common tag conventions (GitHub labels, NPM tags, etc.)\n *\n * Examples:\n * - \u2705 \"api-critical\"\n * - \u2705 \"high-priority\"\n * - \u2705 \"p0\"\n * - \u274C \"-api-critical\" (starts with hyphen)\n * - \u274C \"api-critical-\" (ends with hyphen)\n * - \u274C \"API-CRITICAL\" (uppercase not allowed)\n */\nexport const TagSchema = z.string()\n .min(1, 'Tag cannot be empty')\n .max(50, 'Tag too long')\n .regex(/^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/, 'Tag must contain only lowercase letters, digits, and hyphens');\n\n// Annotation schemas - for creating annotations\nexport const AnnotationCreateSchema = z.object({\n elementId: ElementIdSchema,\n author: z.string()\n .min(1, 'Author is required')\n .max(100, 'Author name too long')\n .optional()\n .default('Anonymous'),\n content: z.string()\n .min(1, 'Content is required')\n .max(5000, 'Content too long'),\n tags: z.array(TagSchema).optional().default([]),\n}).strict();\n\n// Annotation schemas - for updating annotations\nexport const AnnotationUpdateSchema = z.object({\n content: z.string()\n .min(1, 'Content is required')\n .max(5000, 'Content too long')\n .optional(),\n tags: z.array(TagSchema).optional(),\n resolved: z.boolean().optional(),\n}).strict().refine(\n (obj) => Object.keys(obj).some(key => obj[key as keyof typeof obj] !== undefined),\n 'At least one field must be provided for update'\n);\n\n// Annotation schemas - for creating replies\nexport const AnnotationReplyCreateSchema = z.object({\n author: z.string()\n .min(1, 'Author is required')\n .max(100, 'Author name too long'),\n content: z.string()\n .min(1, 'Content is required')\n .max(5000, 'Content too long'),\n}).strict();\n\n// Layer name schema - validates against canonical layer names\nexport const LayerNameSchema = z.enum(CANONICAL_LAYER_NAMES, {\n message: `Invalid layer name. Must be one of: ${CANONICAL_LAYER_NAMES.join(', ')}`\n});\n\n// ID schema - validates generic IDs for annotations, changesets, and other non-element identifiers\n// Accepts lowercase alphanumeric characters, hyphens, and underscores\n// Note: Different from ElementIdSchema which requires dot-separated format (layer.type.name)\n//\n// Lowercase-only constraint rationale:\n// - Provides case-insensitive ID lookups by normalizing to lowercase at validation time\n// - Prevents collisions (MyId vs myid cannot coexist in same namespace)\n// - System-generated IDs comply; externally-generated uppercase IDs will be rejected\n// - If external systems need uppercase support, IDs should be normalized before API transmission\n// - Consider implementing ID normalization in API middleware if integration with external systems is required\nexport const IdSchema = z.string()\n .min(1, 'ID is required')\n .regex(/^[a-z0-9_-]+$/, 'Invalid ID format. Must contain only lowercase alphanumeric characters, hyphens, and underscores');\n\n// Annotation filter schema - validates query parameters for GET /api/annotations\n// Currently only supports filtering by elementId\n// UNIMPLEMENTED FILTERS: author, tags, and resolved fields are defined here but filtering is not yet implemented.\n// See /workspace/cli/src/server/server.ts:650-651 for implementation details.\n// These fields are reserved for future extensibility and currently ignored by the handler.\nexport const AnnotationFilterSchema = z.object({\n elementId: ElementIdSchema.optional(),\n author: z.string().optional(),\n tags: z.string().optional(),\n resolved: z.enum(['true', 'false']).optional(),\n}).strict(); // Validate only declared fields\n\n// Response schemas for OpenAPI documentation\nexport const ErrorResponseSchema = z.object({\n error: z.string().describe('Error message'),\n});\n\nexport const HealthResponseSchema = z.object({\n status: z.string().describe('Server health status'),\n version: z.string().describe('API version'),\n});\n\nexport const AnnotationSchema = z.object({\n id: z.string().describe('Annotation ID'),\n elementId: ElementIdSchema.describe('Target element ID'),\n author: z.string().describe('Author name'),\n content: z.string().describe('Annotation content'),\n createdAt: z.string().describe('Creation timestamp'),\n updatedAt: z.string().optional().describe('Last update timestamp'),\n tags: z.array(z.string()).optional().describe('Annotation tags'),\n resolved: z.boolean().optional().describe('Resolution status'),\n replies: z.array(z.object({\n id: z.string(),\n author: z.string(),\n content: z.string(),\n createdAt: z.string(),\n })).optional().describe('Annotation replies'),\n});\n\nexport const AnnotationReplySchema = z.object({\n id: z.string().describe('Reply ID'),\n author: z.string().describe('Author name'),\n content: z.string().describe('Reply content'),\n createdAt: z.string().describe('Creation timestamp'),\n});\n\nexport const AnnotationsListSchema = z.object({\n annotations: z.array(AnnotationSchema).describe('List of annotations'),\n});\n\nexport const AnnotationRepliesSchema = z.object({\n replies: z.array(AnnotationReplySchema).describe('List of replies'),\n});\n\nexport const LayerResponseSchema = z.object({\n name: z.string().describe('Layer name'),\n elements: z.array(z.object({}).passthrough()).describe('Layer elements (each element is an object with id, name, type, and properties)'),\n elementCount: z.number().describe('Total element count'),\n});\n\nexport const GraphNodeSchema = z.object({\n id: z.string().describe('Element ID (UUID)'),\n spec_node_id: z.string().describe('Spec node type ID (e.g. motivation.goal)'),\n type: z.string().describe('Element type (e.g. goal, endpoint)'),\n layer_id: z.string().describe('Layer this element belongs to'),\n name: z.string().describe('Element display name'),\n description: z.string().optional().describe('Element description'),\n attributes: z.record(z.string(), z.unknown()).optional()\n .describe('Typed attribute bag per spec-node schema'),\n source_reference: z.object({\n provenance: z.enum(['extracted', 'manual', 'inferred', 'generated']),\n locations: z.array(z.object({\n file: z.string(),\n symbol: z.string().optional(),\n })),\n repository: z.object({\n url: z.string().optional(),\n commit: z.string().optional(),\n }).optional(),\n }).optional().describe('Source code provenance linking this element to its implementation'),\n metadata: z.object({\n created_at: z.string().optional(),\n updated_at: z.string().optional(),\n created_by: z.string().optional(),\n version: z.number().optional(),\n }).optional().describe('Element lifecycle metadata (timestamps, version)'),\n properties: z.record(z.string(), z.unknown()).optional()\n .describe('Legacy property bag (deprecated \u2014 prefer attributes)'),\n});\n\nexport const GraphLinkSchema = z.object({\n id: z.string().describe('Stable link ID derived from source, target, and type'),\n source: z.string().describe('Source element ID'),\n target: z.string().describe('Target element ID'),\n type: z.string().describe('Relationship predicate or reference type'),\n // Intra-layer links carry layer_id; cross-layer links carry source/target layer IDs\n layer_id: z.string().optional().describe('Layer ID for intra-layer relationships'),\n source_layer_id: z.string().optional().describe('Source layer ID for cross-layer references'),\n target_layer_id: z.string().optional().describe('Target layer ID for cross-layer references'),\n});\n\nexport const ModelResponseSchema = z.object({\n nodes: z.array(GraphNodeSchema).describe('All architecture elements across all layers'),\n links: z.array(GraphLinkSchema).describe('Intra-layer relationships and cross-layer references'),\n});\n\nexport const SpecResponseSchema = z.object({\n version: z.string().describe('Spec version'),\n type: z.string().describe('Response type'),\n description: z.string().describe('Response description'),\n source: z.string().describe('Schema source'),\n schemas: z.record(z.string(), z.unknown()).describe('JSON schema definitions (each schema is a JSON Schema object)'),\n schemaCount: z.number().describe('Number of schemas'),\n});\n\nexport const ChangesetMetadataSchema = z.object({\n name: z.string(),\n status: z.enum(['staged', 'committed', 'discarded'] as const),\n created: z.string(),\n changes_count: z.number(),\n});\n\nexport const ChangesetsListSchema = z.object({\n version: z.string(),\n changesets: z.record(z.string(), ChangesetMetadataSchema),\n});\n\nexport const ChangesetDetailSchema = z.object({\n id: z.string(),\n name: z.string(),\n description: z.string().optional(),\n status: z.enum(['staged', 'committed', 'discarded'] as const),\n created: z.string(),\n modified: z.string(),\n baseSnapshot: z.string().optional(),\n stats: z.object({\n additions: z.number(),\n modifications: z.number(),\n deletions: z.number(),\n }),\n changes: z.array(z.object({\n type: z.enum(['add', 'update', 'delete'] as const),\n elementId: z.string(),\n layerName: z.string(),\n timestamp: z.string().optional(),\n before: z.unknown().optional().describe('Previous element state (for update/delete operations)'),\n after: z.unknown().optional().describe('New element state (for add/update operations)'),\n })),\n});\n\n// Type inference from input schemas\nexport type AnnotationCreate = z.infer<typeof AnnotationCreateSchema>;\nexport type AnnotationUpdate = z.infer<typeof AnnotationUpdateSchema>;\nexport type AnnotationReplyCreate = z.infer<typeof AnnotationReplyCreateSchema>;\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { z } from '@hono/zod-openapi';\nimport { CANONICAL_LAYER_NAMES } from '../core/layers.js';\n\n/**\n * Branded type for ElementId\n * Provides compile-time safety by distinguishing ElementId from plain strings\n * Use with parseElementId() to validate and brand a string at runtime\n *\n * This type is exported as a public API contract for external consumers who want\n * to use strong typing in their code. However, internal server.ts implementation\n * uses plain string keys in Maps and other internal structures for simplicity.\n *\n * USAGE PATTERNS:\n * - External/public APIs: Type function parameters with ElementId for contract clarity\n * - Internal state: Use plain strings in Maps/Sets for performance and simplicity\n * - Validation: Use parseElementId() to validate untrusted input\n * - Type guards: Use isElementId() for optional runtime type checking\n *\n * Example:\n * const brandedId = parseElementId(\"motivation.goal.customer-satisfaction\");\n * function processElement(id: ElementId) { ... } // Type-safe public API\n *\n * // Internal implementation can use plain strings:\n * private annotations: Map<string, Annotation> = new Map();\n */\nexport type ElementId = string & { readonly __brand: 'ElementId' };\n\n/**\n * Parse and validate a string as an ElementId\n * Returns a branded type for compile-time type safety\n */\nexport function parseElementId(input: string): ElementId {\n const result = ElementIdSchema.safeParse(input);\n if (!result.success) {\n const errorMessages = result.error.issues.map(issue => issue.message).join('; ');\n throw new Error(`Invalid element ID: ${errorMessages}`);\n }\n return result.data as ElementId;\n}\n\n/**\n * Type guard for ElementId\n * Use with function parameters for optional compile-time checking\n */\nexport function isElementId(value: unknown): value is ElementId {\n return typeof value === 'string' && ElementIdSchema.safeParse(value).success;\n}\n\n// WebSocket message schemas with runtime validation\nexport const SimpleWSMessageSchema = z.object({\n type: z.enum(['subscribe', 'annotate', 'ping']),\n topics: z.array(z.string()).optional(),\n annotation: z.object({\n elementId: z.string(),\n author: z.string(),\n text: z.string(),\n timestamp: z.string(),\n }).optional(),\n});\n\nexport const JSONRPCRequestSchema = z.object({\n jsonrpc: z.literal('2.0'),\n method: z.string(),\n params: z.unknown().optional(),\n id: z.union([z.string(), z.number()]).optional(),\n});\n\nexport const JSONRPCResponseSchema = z.object({\n jsonrpc: z.literal('2.0'),\n result: z.unknown().optional(),\n error: z.object({\n code: z.number(),\n message: z.string(),\n data: z.unknown().optional(),\n }).optional(),\n id: z.union([z.string(), z.number(), z.null()]),\n}).refine(\n (data) => (data.result !== undefined) !== (data.error !== undefined),\n {\n message: \"JSON-RPC 2.0 response must contain either 'result' or 'error', not both\",\n path: [\"response\"],\n }\n);\n\nexport const WSMessageSchema = z.union([\n SimpleWSMessageSchema,\n JSONRPCRequestSchema,\n JSONRPCResponseSchema,\n]);\n\n// Base schemas for common types\n// Enforces three-part element ID format: layer.type.name\n// - layer: canonical layer name (e.g., motivation, api, data-store)\n// - type: lowercase element type (e.g., goal, endpoint, entity)\n// - name: kebab-case element name (e.g., customer-satisfaction)\n//\n// Example valid IDs:\n// - motivation.goal.customer-satisfaction\n// - api.endpoint.create-order\n// - data-store.table.user-profile\n// - a.b.c (minimal single-character segments, valid for testing purposes)\n// - ux.a.test (two-character segments are valid)\nexport const ElementIdSchema = z.string()\n .min(1, 'Element ID is required')\n .regex(\n /^[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*$/,\n 'Invalid element ID format. Must be: layer.type.name (e.g., motivation.goal.customer-satisfaction)'\n );\n\nexport const TimestampSchema = z.string().datetime();\n\n/**\n * Tag validation schema.\n *\n * Format: lowercase alphanumeric with optional internal hyphens\n * Pattern: /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/\n *\n * Constraints:\n * - Cannot start or end with hyphens: Prevents URL slugification issues and confusion with CLI flags (e.g., --tag-name)\n * - Single character tags allowed (e.g., \"a\", \"1\") via the second alternation\n * - Matches common tag conventions (GitHub labels, NPM tags, etc.)\n *\n * Examples:\n * - \u2705 \"api-critical\"\n * - \u2705 \"high-priority\"\n * - \u2705 \"p0\"\n * - \u274C \"-api-critical\" (starts with hyphen)\n * - \u274C \"api-critical-\" (ends with hyphen)\n * - \u274C \"API-CRITICAL\" (uppercase not allowed)\n */\nexport const TagSchema = z.string()\n .min(1, 'Tag cannot be empty')\n .max(50, 'Tag too long')\n .regex(/^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/, 'Tag must contain only lowercase letters, digits, and hyphens');\n\n// Annotation schemas - for creating annotations\nexport const AnnotationCreateSchema = z.object({\n elementId: ElementIdSchema,\n author: z.string()\n .min(1, 'Author is required')\n .max(100, 'Author name too long')\n .optional()\n .default('Anonymous'),\n content: z.string()\n .min(1, 'Content is required')\n .max(5000, 'Content too long'),\n tags: z.array(TagSchema).optional().default([]),\n}).strict();\n\n// Annotation schemas - for updating annotations\nexport const AnnotationUpdateSchema = z.object({\n content: z.string()\n .min(1, 'Content is required')\n .max(5000, 'Content too long')\n .optional(),\n tags: z.array(TagSchema).optional(),\n resolved: z.boolean().optional(),\n}).strict().refine(\n (obj) => Object.keys(obj).some(key => obj[key as keyof typeof obj] !== undefined),\n 'At least one field must be provided for update'\n);\n\n// Annotation schemas - for creating replies\nexport const AnnotationReplyCreateSchema = z.object({\n author: z.string()\n .min(1, 'Author is required')\n .max(100, 'Author name too long'),\n content: z.string()\n .min(1, 'Content is required')\n .max(5000, 'Content too long'),\n}).strict();\n\n// Layer name schema - validates against canonical layer names\nexport const LayerNameSchema = z.enum(CANONICAL_LAYER_NAMES, {\n message: `Invalid layer name. Must be one of: ${CANONICAL_LAYER_NAMES.join(', ')}`\n});\n\n// ID schema - validates generic IDs for annotations, changesets, and other non-element identifiers\n// Accepts lowercase alphanumeric characters, hyphens, and underscores\n// Note: Different from ElementIdSchema which requires dot-separated format (layer.type.name)\n//\n// Lowercase-only constraint rationale:\n// - Provides case-insensitive ID lookups by normalizing to lowercase at validation time\n// - Prevents collisions (MyId vs myid cannot coexist in same namespace)\n// - System-generated IDs comply; externally-generated uppercase IDs will be rejected\n// - If external systems need uppercase support, IDs should be normalized before API transmission\n// - Consider implementing ID normalization in API middleware if integration with external systems is required\nexport const IdSchema = z.string()\n .min(1, 'ID is required')\n .regex(/^[a-z0-9_-]+$/, 'Invalid ID format. Must contain only lowercase alphanumeric characters, hyphens, and underscores');\n\n// Annotation filter schema - validates query parameters for GET /api/annotations\n// Currently only supports filtering by elementId\n// UNIMPLEMENTED FILTERS: author, tags, and resolved fields are defined here but filtering is not yet implemented.\n// See /workspace/cli/src/server/server.ts:650-651 for implementation details.\n// These fields are reserved for future extensibility and currently ignored by the handler.\nexport const AnnotationFilterSchema = z.object({\n elementId: ElementIdSchema.optional(),\n author: z.string().optional(),\n tags: z.string().optional(),\n resolved: z.enum(['true', 'false']).optional(),\n}).strict(); // Validate only declared fields\n\n// Response schemas for OpenAPI documentation\nexport const ErrorResponseSchema = z.object({\n error: z.string().describe('Error message'),\n});\n\nexport const HealthResponseSchema = z.object({\n status: z.string().describe('Server health status'),\n version: z.string().describe('API version'),\n});\n\nexport const AnnotationSchema = z.object({\n id: z.string().describe('Annotation ID'),\n elementId: ElementIdSchema.describe('Target element ID'),\n author: z.string().describe('Author name'),\n content: z.string().describe('Annotation content'),\n createdAt: z.string().describe('Creation timestamp'),\n updatedAt: z.string().optional().describe('Last update timestamp'),\n tags: z.array(z.string()).optional().describe('Annotation tags'),\n resolved: z.boolean().optional().describe('Resolution status'),\n replies: z.array(z.object({\n id: z.string(),\n author: z.string(),\n content: z.string(),\n createdAt: z.string(),\n })).optional().describe('Annotation replies'),\n});\n\nexport const AnnotationReplySchema = z.object({\n id: z.string().describe('Reply ID'),\n author: z.string().describe('Author name'),\n content: z.string().describe('Reply content'),\n createdAt: z.string().describe('Creation timestamp'),\n});\n\nexport const AnnotationsListSchema = z.object({\n annotations: z.array(AnnotationSchema).describe('List of annotations'),\n});\n\nexport const AnnotationRepliesSchema = z.object({\n replies: z.array(AnnotationReplySchema).describe('List of replies'),\n});\n\nexport const LayerResponseSchema = z.object({\n name: z.string().describe('Layer name'),\n elements: z.array(z.object({}).passthrough()).describe('Layer elements (each element is an object with id, name, type, and properties)'),\n elementCount: z.number().describe('Total element count'),\n});\n\nexport const GraphNodeSchema = z.object({\n id: z.string().describe('Element ID (UUID)'),\n spec_node_id: z.string().describe('Spec node type ID (e.g. motivation.goal)'),\n type: z.string().describe('Element type (e.g. goal, endpoint)'),\n layer_id: z.string().describe('Layer this element belongs to'),\n name: z.string().describe('Element display name'),\n description: z.string().optional().describe('Element description'),\n attributes: z.record(z.string(), z.unknown()).optional()\n .describe('Typed attribute bag per spec-node schema'),\n source_reference: z.object({\n provenance: z.enum(['extracted', 'manual', 'inferred', 'generated']),\n locations: z.array(z.object({\n file: z.string(),\n symbol: z.string().optional(),\n })),\n repository: z.object({\n url: z.string().optional(),\n commit: z.string().optional(),\n }).optional(),\n }).optional().describe('Source code provenance linking this element to its implementation'),\n metadata: z.object({\n created_at: z.string().optional(),\n updated_at: z.string().optional(),\n created_by: z.string().optional(),\n version: z.number().optional(),\n }).optional().describe('Element lifecycle metadata (timestamps, version)'),\n properties: z.record(z.string(), z.unknown()).optional()\n .describe('Legacy property bag (deprecated \u2014 prefer attributes)'),\n});\n\nexport const GraphLinkSchema = z.object({\n id: z.string().describe('Stable link ID derived from source, target, and type'),\n source: z.string().describe('Source element ID'),\n target: z.string().describe('Target element ID'),\n type: z.string().describe('Relationship predicate or reference type'),\n // Intra-layer links carry layer_id; cross-layer links carry source/target layer IDs\n layer_id: z.string().optional().describe('Layer ID for intra-layer relationships'),\n source_layer_id: z.string().optional().describe('Source layer ID for cross-layer references'),\n target_layer_id: z.string().optional().describe('Target layer ID for cross-layer references'),\n});\n\nexport const ModelResponseSchema = z.object({\n nodes: z.array(GraphNodeSchema).describe('All architecture elements across all layers'),\n links: z.array(GraphLinkSchema).describe('Intra-layer relationships and cross-layer references'),\n});\n\nexport const SpecResponseSchema = z.object({\n version: z.string().describe('Spec version'),\n type: z.string().describe('Response type'),\n description: z.string().describe('Response description'),\n source: z.string().describe('Schema source'),\n schemas: z.record(z.string(), z.unknown()).describe('JSON schema definitions (each schema is a JSON Schema object)'),\n schemaCount: z.number().describe('Number of schemas'),\n});\n\nexport const ChangesetMetadataSchema = z.object({\n name: z.string(),\n status: z.enum(['staged', 'committed', 'discarded'] as const),\n created: z.string(),\n changes_count: z.number(),\n});\n\nexport const ChangesetsListSchema = z.object({\n version: z.string(),\n changesets: z.record(z.string(), ChangesetMetadataSchema),\n});\n\nexport const ChangesetActionResultSchema = z.object({\n id: z.string(),\n name: z.string(),\n status: z.enum(['staged', 'committed', 'discarded'] as const),\n committed: z.number().optional().describe('Changes applied to the model (commit only)'),\n skipped: z.number().optional().describe('Changes skipped as already present (commit only)'),\n failed: z.number().optional().describe('Changes that failed to apply (commit only)'),\n});\n\nexport const ChangesetDetailSchema = z.object({\n id: z.string(),\n name: z.string(),\n description: z.string().optional(),\n status: z.enum(['staged', 'committed', 'discarded'] as const),\n created: z.string(),\n modified: z.string(),\n baseSnapshot: z.string().optional(),\n stats: z.object({\n additions: z.number(),\n modifications: z.number(),\n deletions: z.number(),\n }),\n changes: z.array(z.object({\n type: z.enum(['add', 'update', 'delete'] as const),\n elementId: z.string(),\n layerName: z.string(),\n timestamp: z.string().optional(),\n before: z.unknown().optional().describe('Previous element state (for update/delete operations)'),\n after: z.unknown().optional().describe('New element state (for add/update operations)'),\n })),\n});\n\n// Type inference from input schemas\nexport type AnnotationCreate = z.infer<typeof AnnotationCreateSchema>;\nexport type AnnotationUpdate = z.infer<typeof AnnotationUpdateSchema>;\nexport type AnnotationReplyCreate = z.infer<typeof AnnotationReplyCreateSchema>;\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -12,14 +12,17 @@ import path from "path";
12
12
  const require2 = createRequire(import.meta.url);
13
13
  import { Model } from "../core/model.js";
14
14
  import { StagedChangesetStorage } from "../core/staged-changeset-storage.js";
15
+ import { StagingAreaManager } from "../core/staging-area.js";
15
16
  import { CANONICAL_LAYER_NAMES } from "../core/layers.js";
16
17
  import { telemetryMiddleware } from "./telemetry-middleware.js";
17
18
  import { ClaudeCodeClient } from "../coding-agents/claude-code-client.js";
18
19
  import { CopilotClient } from "../coding-agents/copilot-client.js";
19
20
  import { detectAvailableClients, selectChatClient } from "../coding-agents/chat-utils.js";
20
- import { formatForClaudeCode, applyCopilotPermissions } from "../coding-agents/default-permissions.js";
21
+ import { formatForClaudeCode, formatForClaudeCodeWithChangesets, applyCopilotPermissions, CHANGESET_ONLY_ENV } from "../coding-agents/default-permissions.js";
22
+ import { DR_ARCHITECT_AGENT, checkInstalledAgentMcpTools, drBotMcpPrompt, drMcpAllowedTools, prepareDrMcpLaunch } from "../coding-agents/drbot-mcp.js";
23
+ import { ApiKeyManager } from "../mcp/api-key-manager.js";
21
24
  import { getErrorMessage } from "../utils/errors.js";
22
- import { AnnotationCreateSchema, AnnotationUpdateSchema, AnnotationReplyCreateSchema, LayerNameSchema, IdSchema, AnnotationFilterSchema, ErrorResponseSchema, HealthResponseSchema, AnnotationSchema, AnnotationReplySchema, AnnotationsListSchema, ChangesetsListSchema, ChangesetDetailSchema, ModelResponseSchema, LayerResponseSchema, SpecResponseSchema, AnnotationRepliesSchema, WSMessageSchema } from "./schemas.js";
25
+ import { AnnotationCreateSchema, AnnotationUpdateSchema, AnnotationReplyCreateSchema, LayerNameSchema, IdSchema, AnnotationFilterSchema, ErrorResponseSchema, HealthResponseSchema, AnnotationSchema, AnnotationReplySchema, AnnotationsListSchema, ChangesetsListSchema, ChangesetDetailSchema, ChangesetActionResultSchema, ModelResponseSchema, LayerResponseSchema, SpecResponseSchema, AnnotationRepliesSchema, WSMessageSchema } from "./schemas.js";
23
26
  const JSONRPC_ERRORS = {
24
27
  // Standard JSON-RPC errors
25
28
  INVALID_PARAMS: -32602,
@@ -54,6 +57,8 @@ class VisualizationServer {
54
57
  authEnabled = !0;
55
58
  withDanger = !1;
56
59
  // Danger mode disabled by default
60
+ withChangesets = !1;
61
+ // Changeset-only write mode disabled by default
57
62
  viewerPath;
58
63
  // Optional custom viewer path
59
64
  activeChatProcesses = /* @__PURE__ */ new Map();
@@ -65,6 +70,12 @@ class VisualizationServer {
65
70
  // Store initialization error for status endpoint
66
71
  _testClaudeCmdOverride;
67
72
  _testCopilotCmdOverride;
73
+ /** Test hook: prepare DrBot's MCP launch with these (fake key, scratch dirs) even under _testClaudeCmdOverride. */
74
+ _testDrMcpOptions;
75
+ /** Test hook: the claude arguments the last chat launch built, before any _testClaudeCmdOverride. */
76
+ _testLastClaudeChatArgs;
77
+ drMcpKey;
78
+ drBotWarnings = /* @__PURE__ */ new Set();
68
79
  /**
69
80
  * Valid spec node IDs per layer
70
81
  * Dynamically loaded from compiled spec (spec/dist/{layer}.json at module load time)
@@ -116,7 +127,7 @@ class VisualizationServer {
116
127
  }), ids;
117
128
  })();
118
129
  constructor(model, options) {
119
- this.app = new OpenAPIHono(), this.model = model, this.loadWebSocketAdapter(), this.authEnabled = options?.authEnabled ?? process.env.DR_AUTH_ENABLED !== "false", this.authToken = options?.authToken || process.env.DR_AUTH_TOKEN || this.generateAuthToken(), this.withDanger = options?.withDanger || !1, this.viewerPath = options?.viewerPath || new URL("../viewer", import.meta.url).pathname, !this.authEnabled && options?.authToken && console.warn("[VisualizationServer] Warning: authToken provided but authEnabled is false. The token will be generated but not used for authentication. Set authEnabled: true to enable authentication."), this.app.use("/*", cors()), this.app.use("/*", telemetryMiddleware), this.app.use("/api/*", async (c, next) => {
130
+ this.app = new OpenAPIHono(), this.model = model, this.loadWebSocketAdapter(), this.authEnabled = options?.authEnabled ?? process.env.DR_AUTH_ENABLED !== "false", this.authToken = options?.authToken || process.env.DR_AUTH_TOKEN || this.generateAuthToken(), this.withDanger = options?.withDanger || !1, this.withChangesets = options?.withChangesets || !1, this.viewerPath = options?.viewerPath || new URL("../viewer", import.meta.url).pathname, !this.authEnabled && options?.authToken && console.warn("[VisualizationServer] Warning: authToken provided but authEnabled is false. The token will be generated but not used for authentication. Set authEnabled: true to enable authentication."), this.app.use("/*", cors()), this.app.use("/*", telemetryMiddleware), this.app.use("/api/*", async (c, next) => {
120
131
  if (!this.authEnabled)
121
132
  return next();
122
133
  const authHeader = c.req.header("Authorization"), queryToken = c.req.query("token");
@@ -776,7 +787,8 @@ class VisualizationServer {
776
787
  }
777
788
  }
778
789
  });
779
- this.app.openapi(getChangesetsRoute, (c) => {
790
+ this.app.openapi(getChangesetsRoute, async (c) => {
791
+ await this.loadChangesetsFromDisk();
780
792
  const changesets = {};
781
793
  for (const [id, changeset] of this.changesets)
782
794
  changesets[id] = {
@@ -819,8 +831,10 @@ class VisualizationServer {
819
831
  }
820
832
  }
821
833
  });
822
- this.app.openapi(getChangesetRoute, (c) => {
823
- const { changesetId } = c.req.valid("param"), changeset = this.changesets.get(changesetId);
834
+ this.app.openapi(getChangesetRoute, async (c) => {
835
+ const { changesetId } = c.req.valid("param");
836
+ await this.loadChangesetsFromDisk();
837
+ const changeset = this.changesets.get(changesetId);
824
838
  return changeset ? c.json({
825
839
  id: changeset.id,
826
840
  name: changeset.name,
@@ -832,6 +846,51 @@ class VisualizationServer {
832
846
  stats: changeset.stats,
833
847
  changes: changeset.changes
834
848
  }) : c.json({ error: "Changeset not found" }, 404);
849
+ });
850
+ const changesetActionResponses = {
851
+ 200: {
852
+ description: "Action completed",
853
+ content: { "application/json": { schema: ChangesetActionResultSchema } }
854
+ },
855
+ 404: {
856
+ description: "Changeset not found",
857
+ content: { "application/json": { schema: ErrorResponseSchema } }
858
+ },
859
+ 409: {
860
+ description: "Changeset is not staged, or the model drifted since it was created",
861
+ content: { "application/json": { schema: ErrorResponseSchema } }
862
+ },
863
+ 422: {
864
+ description: "The changeset failed validation; nothing was applied",
865
+ content: { "application/json": { schema: ErrorResponseSchema } }
866
+ }
867
+ }, commitChangesetRoute = createRoute({
868
+ method: "post",
869
+ path: "/api/changesets/:changesetId/commit",
870
+ tags: ["Changesets"],
871
+ summary: "Commit changeset",
872
+ description: "Apply a staged changeset to the model (same as `dr changeset commit`): validates it, applies every change atomically, marks it committed, and deactivates it if it was active. Refused when the model changed since the changeset was created (use the CLI with --force).",
873
+ security: [{ bearerAuth: [] }],
874
+ request: { params: z.object({ changesetId: IdSchema }) },
875
+ responses: changesetActionResponses
876
+ });
877
+ this.app.openapi(commitChangesetRoute, async (c) => {
878
+ const { changesetId } = c.req.valid("param");
879
+ return this.runChangesetAction(c, changesetId, "commit");
880
+ });
881
+ const discardChangesetRoute = createRoute({
882
+ method: "post",
883
+ path: "/api/changesets/:changesetId/discard",
884
+ tags: ["Changesets"],
885
+ summary: "Discard changeset",
886
+ description: "Throw away a staged changeset's changes (same as `dr changeset discard`): clears them, marks it discarded, and deactivates it if it was active. The model is not touched.",
887
+ security: [{ bearerAuth: [] }],
888
+ request: { params: z.object({ changesetId: IdSchema }) },
889
+ responses: changesetActionResponses
890
+ });
891
+ this.app.openapi(discardChangesetRoute, async (c) => {
892
+ const { changesetId } = c.req.valid("param");
893
+ return this.runChangesetAction(c, changesetId, "discard");
835
894
  }), this.app.doc("/api-spec.yaml", {
836
895
  openapi: "3.1.0",
837
896
  info: {
@@ -1269,9 +1328,73 @@ class VisualizationServer {
1269
1328
  /**
1270
1329
  * Build command arguments for Claude Code launch
1271
1330
  */
1272
- buildClaudeChatArgs() {
1273
- const cmd = ["claude", "--agent", "dr-architect", "--print"];
1274
- return this.withDanger ? cmd.push("--dangerously-skip-permissions") : cmd.push("--allowedTools", formatForClaudeCode()), cmd.push("--verbose", "--output-format", "stream-json"), cmd;
1331
+ buildClaudeChatArgs(mcpConfigPath) {
1332
+ const cmd = ["claude", "--agent", "dr-architect", "--print"], mode = this.drBotMode(), mcpRules = mcpConfigPath ? drMcpAllowedTools(mode) : [];
1333
+ if (mode === "danger")
1334
+ cmd.push("--dangerously-skip-permissions");
1335
+ else {
1336
+ const bashRules = mode === "changesets" ? formatForClaudeCodeWithChangesets() : formatForClaudeCode();
1337
+ cmd.push("--allowedTools", [bashRules, ...mcpRules].join(","));
1338
+ }
1339
+ return mcpConfigPath && cmd.push("--mcp-config", mcpConfigPath, "--append-system-prompt", drBotMcpPrompt(mode)), cmd.push("--verbose", "--output-format", "stream-json"), cmd;
1340
+ }
1341
+ /** DrBot's permission mode; danger wins over changeset-only. */
1342
+ drBotMode() {
1343
+ return this.withDanger ? "danger" : this.withChangesets ? "changesets" : "read-safe";
1344
+ }
1345
+ /**
1346
+ * Writes the MCP config that connects DrBot to `dr mcp` for this model. Returns null when it
1347
+ * can't (logged): DrBot then launches without MCP and uses its Bash rules.
1348
+ */
1349
+ async prepareDrBotMcp() {
1350
+ const mode = this.drBotMode(), overrides = this._testDrMcpOptions ?? {};
1351
+ try {
1352
+ const check = await checkInstalledAgentMcpTools(this.model.rootPath, mode, overrides.homeDir);
1353
+ if (check && (this.warnDrBotOnce(check.noneVisible ? `[Claude Code] The installed ${DR_ARCHITECT_AGENT} agent (${check.path}) predates the dr MCP tools, so the chat can't use them and falls back to Bash dr commands. Run \`dr claude upgrade\` to update it.` : `[Claude Code] The installed ${DR_ARCHITECT_AGENT} agent (${check.path}) doesn't list ${check.missing.join(", ")}, so the chat can't use those dr MCP tools. Run \`dr claude upgrade\` to update it.`), check.noneVisible))
1354
+ return null;
1355
+ } catch (error) {
1356
+ this.warnDrBotOnce(`[Claude Code] Could not check the installed ${DR_ARCHITECT_AGENT} agent's tools (${getErrorMessage(error)}); connecting the dr MCP server anyway.`);
1357
+ }
1358
+ try {
1359
+ return await prepareDrMcpLaunch({
1360
+ rootPath: this.model.rootPath,
1361
+ changesetOnly: mode === "changesets",
1362
+ keyManager: { ensureKey: () => this.ensureDrMcpKey() },
1363
+ drCommand: overrides.drCommand,
1364
+ tempRoot: overrides.tempRoot
1365
+ });
1366
+ } catch (error) {
1367
+ return console.warn(`[Claude Code] dr MCP server unavailable for chat, falling back to Bash dr commands: ${getErrorMessage(error)}`), null;
1368
+ }
1369
+ }
1370
+ /**
1371
+ * The installation's MCP API key. Concurrent chat launches share one in-flight ensureKey call,
1372
+ * so a first-time key is created once rather than raced and overwritten (which would leave an
1373
+ * earlier launch's MCP server holding a key that no longer matches). Once it settles the next
1374
+ * launch reads the key afresh, so a key rotated with `dr mcp --regenerate-key` is picked up.
1375
+ */
1376
+ ensureDrMcpKey() {
1377
+ if (!this.drMcpKey) {
1378
+ const pending = (this._testDrMcpOptions?.keyManager ?? new ApiKeyManager()).ensureKey();
1379
+ this.drMcpKey = pending;
1380
+ const clear = () => {
1381
+ this.drMcpKey === pending && (this.drMcpKey = void 0);
1382
+ };
1383
+ pending.then(clear, clear);
1384
+ }
1385
+ return this.drMcpKey;
1386
+ }
1387
+ /** Logs a DrBot setup warning once per server rather than on every chat message. */
1388
+ warnDrBotOnce(message) {
1389
+ this.drBotWarnings.has(message) || (this.drBotWarnings.add(message), console.warn(message));
1390
+ }
1391
+ /**
1392
+ * Environment for chat agent processes. In changeset-only mode it sets
1393
+ * CHANGESET_ONLY_ENV, so any `dr` command the agent runs refuses to save the base
1394
+ * model and can only stage changes in an active changeset (core/changeset-only-guard.ts).
1395
+ */
1396
+ buildChatEnv() {
1397
+ return this.withChangesets && !this.withDanger ? { ...process.env, [CHANGESET_ONLY_ENV]: "true" } : process.env;
1275
1398
  }
1276
1399
  /**
1277
1400
  * Add permission flags to Copilot command based on withDanger setting
@@ -1288,13 +1411,20 @@ class VisualizationServer {
1288
1411
  * Launch Claude Code CLI with dr-architect agent and stream responses
1289
1412
  */
1290
1413
  async launchClaudeCodeChat(ws, conversationId, message, requestId) {
1414
+ let drMcp = null;
1291
1415
  try {
1292
- const cmd = this.buildClaudeChatArgs();
1293
- this._testClaudeCmdOverride && cmd.splice(0, cmd.length, ...this._testClaudeCmdOverride);
1416
+ drMcp = this._testClaudeCmdOverride !== void 0 && this._testDrMcpOptions === void 0 ? null : await this.prepareDrBotMcp();
1417
+ const cmd = this.buildClaudeChatArgs(drMcp?.configPath);
1418
+ this._testLastClaudeChatArgs = [...cmd], this._testClaudeCmdOverride && cmd.splice(0, cmd.length, ...this._testClaudeCmdOverride);
1294
1419
  const proc = spawn(cmd[0], cmd.slice(1), {
1295
1420
  cwd: this.model.rootPath,
1421
+ env: this.buildChatEnv(),
1296
1422
  stdio: ["pipe", "pipe", "pipe"]
1297
1423
  });
1424
+ if (drMcp) {
1425
+ const cleanup = drMcp.cleanup;
1426
+ proc.once("exit", () => void cleanup()), proc.once("error", () => void cleanup());
1427
+ }
1298
1428
  proc.on("error", (err) => {
1299
1429
  const errorMsg = getErrorMessage(err);
1300
1430
  console.error(`[Claude Code] Failed to spawn subprocess for conversation ${conversationId}: ${errorMsg}`), this.activeChatProcesses.delete(conversationId);
@@ -1428,6 +1558,7 @@ class VisualizationServer {
1428
1558
  }
1429
1559
  });
1430
1560
  } catch (error) {
1561
+ drMcp?.cleanup();
1431
1562
  const errorMsg = getErrorMessage(error);
1432
1563
  ws.send(JSON.stringify({
1433
1564
  jsonrpc: "2.0",
@@ -1561,19 +1692,7 @@ class VisualizationServer {
1561
1692
  }), this.watcher.on("all", async (_event, path2) => {
1562
1693
  process.env.VERBOSE && console.log(`[Watcher] File changed: ${path2}`);
1563
1694
  try {
1564
- this.model = await Model.load(this.model.rootPath, { lazyLoad: !1 }), await this.loadChangesetsFromDisk();
1565
- const modelData = await this.serializeModel(), message = JSON.stringify({
1566
- type: "model.updated",
1567
- data: modelData,
1568
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1569
- });
1570
- for (const client of this.clients)
1571
- try {
1572
- client.send(message);
1573
- } catch (error) {
1574
- const msg = getErrorMessage(error);
1575
- console.warn(`[Watcher] Failed to send update: ${msg}`);
1576
- }
1695
+ await this.reloadAndBroadcast();
1577
1696
  } catch (error) {
1578
1697
  const message = getErrorMessage(error);
1579
1698
  console.error(`[Watcher] Failed to reload model: ${message}`);
@@ -2263,20 +2382,85 @@ class VisualizationServer {
2263
2382
  }
2264
2383
  }
2265
2384
  /**
2266
- * Load all changesets from disk into the in-memory map
2385
+ * Reload the model and changesets from disk and send the new model to every client.
2267
2386
  */
2268
- async loadChangesetsFromDisk() {
2269
- if (this.changesetStorage)
2387
+ async reloadAndBroadcast() {
2388
+ this.model = await Model.load(this.model.rootPath, { lazyLoad: !1 }), await this.loadChangesetsFromDisk();
2389
+ const modelData = await this.serializeModel(), message = JSON.stringify({
2390
+ type: "model.updated",
2391
+ data: modelData,
2392
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2393
+ });
2394
+ for (const client of this.clients)
2270
2395
  try {
2271
- const changesets = await this.changesetStorage.list();
2272
- this.changesets.clear();
2273
- for (const cs of changesets)
2274
- this.changesets.set(cs.id, cs);
2275
- process.env.VERBOSE && console.log(`[Changesets] Loaded ${changesets.length} changeset(s) from disk`);
2276
- } catch (err) {
2277
- console.warn(`[Changesets] Warning: Failed to load changesets from disk: ${err}`);
2396
+ client.send(message);
2397
+ } catch (error) {
2398
+ const msg = getErrorMessage(error);
2399
+ console.warn(`[Watcher] Failed to send update: ${msg}`);
2278
2400
  }
2279
2401
  }
2402
+ /**
2403
+ * Commit or discard a staged changeset for POST /api/changesets/:id/{commit,discard}.
2404
+ *
2405
+ * Works on a freshly loaded model rather than the server's in-memory copy, so a CLI
2406
+ * change since the last reload can't be overwritten. Afterwards the server reloads
2407
+ * (the watcher only sees model files, and a discard touches none) and broadcasts.
2408
+ */
2409
+ async runChangesetAction(c, changesetId, action) {
2410
+ const model = await Model.load(this.model.rootPath, { lazyLoad: !1 }), manager = new StagingAreaManager(model.rootPath, model), changeset = await manager.load(changesetId);
2411
+ if (!changeset)
2412
+ return c.json({ error: "Changeset not found" }, 404);
2413
+ if (changeset.status !== "staged")
2414
+ return c.json({ error: `Changeset is already ${changeset.status}` }, 409);
2415
+ let result = {};
2416
+ try {
2417
+ if (action === "commit") {
2418
+ const commit = await manager.commit(model, changesetId, { validate: !0 });
2419
+ result = {
2420
+ committed: commit.committed,
2421
+ skipped: commit.skipped ?? 0,
2422
+ failed: commit.failed,
2423
+ preExistingErrors: commit.validation.preExistingErrors ?? 0
2424
+ };
2425
+ } else
2426
+ await manager.discard(changesetId);
2427
+ } catch (error) {
2428
+ const message = getErrorMessage(error);
2429
+ if (message.startsWith("Validation failed"))
2430
+ return c.json({ error: message }, 422);
2431
+ if (message.includes("drifted"))
2432
+ return c.json({ error: message }, 409);
2433
+ throw error;
2434
+ }
2435
+ await manager.getActiveId() === changesetId && await manager.clearActive();
2436
+ try {
2437
+ await this.reloadAndBroadcast();
2438
+ } catch (error) {
2439
+ console.error(`[Changesets] Reload after ${action} failed: ${getErrorMessage(error)}`);
2440
+ }
2441
+ const updated = this.changesets.get(changesetId) ?? await manager.load(changesetId);
2442
+ return c.json({
2443
+ id: changesetId,
2444
+ name: changeset.name,
2445
+ status: updated?.status ?? (action === "commit" ? "committed" : "discarded"),
2446
+ ...result
2447
+ });
2448
+ }
2449
+ /**
2450
+ * Load all changesets from disk into the in-memory map
2451
+ */
2452
+ async loadChangesetsFromDisk() {
2453
+ this.changesetStorage ??= new StagedChangesetStorage(this.model.rootPath);
2454
+ try {
2455
+ const changesets = await this.changesetStorage.list();
2456
+ this.changesets.clear();
2457
+ for (const cs of changesets)
2458
+ this.changesets.set(cs.id, cs);
2459
+ process.env.VERBOSE && console.log(`[Changesets] Loaded ${changesets.length} changeset(s) from disk`);
2460
+ } catch (err) {
2461
+ console.warn(`[Changesets] Warning: Failed to load changesets from disk: ${err}`);
2462
+ }
2463
+ }
2280
2464
  /**
2281
2465
  * Start the server
2282
2466
  */