@documentation-robotics/cli 0.1.13 → 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 (351) hide show
  1. package/README.md +74 -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/analyzers/verify-engine.js +22 -19
  34. package/dist/analyzers/verify-engine.js.map +2 -2
  35. package/dist/audit/relationships/ai/prompts.js +10 -10
  36. package/dist/audit/relationships/ai/prompts.js.map +2 -2
  37. package/dist/audit/relationships/analysis/layer-templates.js +52 -52
  38. package/dist/audit/relationships/analysis/layer-templates.js.map +1 -1
  39. package/dist/cli.js +112 -28
  40. package/dist/cli.js.map +3 -3
  41. package/dist/coding-agents/agents/claude-code.js +3 -1
  42. package/dist/coding-agents/agents/claude-code.js.map +2 -2
  43. package/dist/coding-agents/claude-code-client.js +2 -1
  44. package/dist/coding-agents/claude-code-client.js.map +2 -2
  45. package/dist/coding-agents/copilot-client.js +17 -12
  46. package/dist/coding-agents/copilot-client.js.map +2 -2
  47. package/dist/coding-agents/default-permissions.js +152 -0
  48. package/dist/coding-agents/default-permissions.js.map +7 -0
  49. package/dist/coding-agents/drbot-mcp.js +167 -0
  50. package/dist/coding-agents/drbot-mcp.js.map +7 -0
  51. package/dist/commands/add.js +12 -7
  52. package/dist/commands/add.js.map +2 -2
  53. package/dist/commands/analyzer.js +134 -23
  54. package/dist/commands/analyzer.js.map +2 -2
  55. package/dist/commands/audit.js +6 -6
  56. package/dist/commands/audit.js.map +2 -2
  57. package/dist/commands/changeset-verify.js +55 -0
  58. package/dist/commands/changeset-verify.js.map +7 -0
  59. package/dist/commands/changeset.js +261 -147
  60. package/dist/commands/changeset.js.map +2 -2
  61. package/dist/commands/delete.js +52 -19
  62. package/dist/commands/delete.js.map +3 -3
  63. package/dist/commands/element.js +3 -2
  64. package/dist/commands/element.js.map +2 -2
  65. package/dist/commands/farm.js +773 -0
  66. package/dist/commands/farm.js.map +7 -0
  67. package/dist/commands/init.js +11 -1
  68. package/dist/commands/init.js.map +2 -2
  69. package/dist/commands/mcp.js +122 -0
  70. package/dist/commands/mcp.js.map +7 -0
  71. package/dist/commands/relationship.js +251 -47
  72. package/dist/commands/relationship.js.map +2 -2
  73. package/dist/commands/reports.js +1 -1
  74. package/dist/commands/reports.js.map +2 -2
  75. package/dist/commands/update.js +56 -24
  76. package/dist/commands/update.js.map +2 -2
  77. package/dist/commands/upgrade.js +99 -11
  78. package/dist/commands/upgrade.js.map +3 -3
  79. package/dist/commands/validate.js +41 -3
  80. package/dist/commands/validate.js.map +2 -2
  81. package/dist/commands/version.js +2 -2
  82. package/dist/commands/version.js.map +2 -2
  83. package/dist/commands/visualize.js +4 -3
  84. package/dist/commands/visualize.js.map +2 -2
  85. package/dist/config.js +83 -0
  86. package/dist/config.js.map +7 -0
  87. package/dist/core/annotation-store.js +138 -0
  88. package/dist/core/annotation-store.js.map +7 -0
  89. package/dist/core/catalog-migration.js +33 -0
  90. package/dist/core/catalog-migration.js.map +7 -0
  91. package/dist/core/changeset-analysis.js +421 -0
  92. package/dist/core/changeset-analysis.js.map +7 -0
  93. package/dist/core/changeset-base-index.js +153 -0
  94. package/dist/core/changeset-base-index.js.map +7 -0
  95. package/dist/core/changeset-brief-delta.js +102 -0
  96. package/dist/core/changeset-brief-delta.js.map +7 -0
  97. package/dist/core/changeset-graph.js +281 -0
  98. package/dist/core/changeset-graph.js.map +7 -0
  99. package/dist/core/changeset-only-guard.js +32 -0
  100. package/dist/core/changeset-only-guard.js.map +7 -0
  101. package/dist/core/changeset-summary.js +68 -0
  102. package/dist/core/changeset-summary.js.map +7 -0
  103. package/dist/core/changeset-validator.js +66 -20
  104. package/dist/core/changeset-validator.js.map +2 -2
  105. package/dist/core/changeset-verify.js +518 -0
  106. package/dist/core/changeset-verify.js.map +7 -0
  107. package/dist/core/changeset.js +56 -9
  108. package/dist/core/changeset.js.map +2 -2
  109. package/dist/core/element-type-migration.js +145 -0
  110. package/dist/core/element-type-migration.js.map +7 -0
  111. package/dist/core/farm-manifest.js +140 -0
  112. package/dist/core/farm-manifest.js.map +7 -0
  113. package/dist/core/farm-sync-engine.js +346 -0
  114. package/dist/core/farm-sync-engine.js.map +7 -0
  115. package/dist/core/farm-sync-state.js +167 -0
  116. package/dist/core/farm-sync-state.js.map +7 -0
  117. package/dist/core/manifest.js +6 -3
  118. package/dist/core/manifest.js.map +2 -2
  119. package/dist/core/migration-registry.js +84 -1
  120. package/dist/core/migration-registry.js.map +2 -2
  121. package/dist/core/model-upgrade.js +102 -0
  122. package/dist/core/model-upgrade.js.map +7 -0
  123. package/dist/core/model.js +257 -44
  124. package/dist/core/model.js.map +3 -3
  125. package/dist/core/mutation-handler.js +71 -6
  126. package/dist/core/mutation-handler.js.map +2 -2
  127. package/dist/core/relationship-migration-table.js +6639 -0
  128. package/dist/core/relationship-migration-table.js.map +7 -0
  129. package/dist/core/relationship-migration.js +272 -0
  130. package/dist/core/relationship-migration.js.map +7 -0
  131. package/dist/core/relationship-prune.js +49 -0
  132. package/dist/core/relationship-prune.js.map +7 -0
  133. package/dist/core/relationship-staging.js +68 -0
  134. package/dist/core/relationship-staging.js.map +7 -0
  135. package/dist/core/relationships.js +26 -4
  136. package/dist/core/relationships.js.map +2 -2
  137. package/dist/core/report-data-model.js +7 -13
  138. package/dist/core/report-data-model.js.map +2 -2
  139. package/dist/core/staged-changeset-storage.js +64 -17
  140. package/dist/core/staged-changeset-storage.js.map +2 -2
  141. package/dist/core/staged-source-reference.js +28 -0
  142. package/dist/core/staged-source-reference.js.map +7 -0
  143. package/dist/core/staging-area.js +83 -18
  144. package/dist/core/staging-area.js.map +2 -2
  145. package/dist/core/virtual-projection.js +14 -5
  146. package/dist/core/virtual-projection.js.map +2 -2
  147. package/dist/export/archimate-exporter.js +1 -0
  148. package/dist/export/archimate-exporter.js.map +2 -2
  149. package/dist/generated/compiled-validators.js +2 -2
  150. package/dist/generated/compiled-validators.js.map +2 -2
  151. package/dist/generated/layer-registry.js +16 -16
  152. package/dist/generated/layer-registry.js.map +2 -2
  153. package/dist/generated/node-types.js +186 -168
  154. package/dist/generated/node-types.js.map +2 -2
  155. package/dist/generated/relationship-index.js +2209 -7727
  156. package/dist/generated/relationship-index.js.map +2 -2
  157. package/dist/integrations/claude_code/agents/dr-architect.md +16 -3
  158. package/dist/integrations/claude_code/agents/dr-extractor.md +1 -1
  159. package/dist/integrations/claude_code/commands/dr-analyzer.md +565 -0
  160. package/dist/integrations/claude_code/commands/dr-map.md +13 -0
  161. package/dist/integrations/claude_code/commands/dr-plan.md +40 -0
  162. package/dist/integrations/claude_code/commands/dr-relate.md +106 -96
  163. package/dist/integrations/claude_code/commands/dr-verify.md +45 -2
  164. package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +14 -0
  165. package/dist/integrations/claude_code/docs/MCP_SERVER.md +79 -0
  166. package/dist/integrations/claude_code/docs/QUICK_START.md +37 -8
  167. package/dist/integrations/claude_code/docs/README.md +52 -1
  168. package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +93 -72
  169. package/dist/integrations/claude_code/skills/dr_02_business_layer/SKILL.md +163 -126
  170. package/dist/integrations/claude_code/skills/dr_03_product_layer/SKILL.md +161 -126
  171. package/dist/integrations/claude_code/skills/dr_04_security_layer/SKILL.md +153 -133
  172. package/dist/integrations/claude_code/skills/dr_05_application_layer/SKILL.md +129 -100
  173. package/dist/integrations/claude_code/skills/dr_06_technology_layer/SKILL.md +111 -94
  174. package/dist/integrations/claude_code/skills/dr_07_api_layer/SKILL.md +143 -157
  175. package/dist/integrations/claude_code/skills/dr_08_data_model_layer/SKILL.md +117 -33
  176. package/dist/integrations/claude_code/skills/dr_09_dataset_layer/SKILL.md +74 -25
  177. package/dist/integrations/claude_code/skills/dr_10_ux_layer/SKILL.md +88 -12
  178. package/dist/integrations/claude_code/skills/dr_11_navigation_layer/SKILL.md +83 -24
  179. package/dist/integrations/claude_code/skills/dr_12_apm_layer/SKILL.md +57 -12
  180. package/dist/integrations/claude_code/skills/dr_13_testing_layer/SKILL.md +92 -15
  181. package/dist/integrations/claude_code/skills/dr_change_planning/SKILL.md +119 -0
  182. package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +3 -0
  183. package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +1 -1
  184. package/dist/integrations/github_copilot/agents/dr-architect.md +1 -1
  185. package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +182 -102
  186. package/dist/integrations/github_copilot/skills/dr_02_business_layer/SKILL.md +266 -148
  187. package/dist/integrations/github_copilot/skills/dr_03_product_layer/SKILL.md +547 -86
  188. package/dist/integrations/github_copilot/skills/dr_04_security_layer/SKILL.md +164 -169
  189. package/dist/integrations/github_copilot/skills/dr_05_application_layer/SKILL.md +420 -150
  190. package/dist/integrations/github_copilot/skills/dr_06_technology_layer/SKILL.md +331 -138
  191. package/dist/integrations/github_copilot/skills/dr_07_api_layer/SKILL.md +265 -201
  192. package/dist/integrations/github_copilot/skills/dr_08_data_model_layer/SKILL.md +181 -58
  193. package/dist/integrations/github_copilot/skills/dr_09_dataset_layer/SKILL.md +209 -57
  194. package/dist/integrations/github_copilot/skills/dr_10_ux_layer/SKILL.md +277 -112
  195. package/dist/integrations/github_copilot/skills/dr_11_navigation_layer/SKILL.md +219 -70
  196. package/dist/integrations/github_copilot/skills/dr_12_apm_layer/SKILL.md +189 -105
  197. package/dist/integrations/github_copilot/skills/dr_13_testing_layer/SKILL.md +222 -102
  198. package/dist/integrations/github_copilot/skills/dr_change_planning/SKILL.md +119 -0
  199. package/dist/integrations/github_copilot/skills/dr_changeset_reviewer/SKILL.md +3 -0
  200. package/dist/integrations/hash-utils.js.map +2 -2
  201. package/dist/mcp/api-key-manager.js +111 -0
  202. package/dist/mcp/api-key-manager.js.map +7 -0
  203. package/dist/mcp/changeset-only.js +34 -0
  204. package/dist/mcp/changeset-only.js.map +7 -0
  205. package/dist/mcp/http-transport.js +176 -0
  206. package/dist/mcp/http-transport.js.map +7 -0
  207. package/dist/mcp/resource-registry.js +97 -0
  208. package/dist/mcp/resource-registry.js.map +7 -0
  209. package/dist/mcp/tool-registry.js +60 -0
  210. package/dist/mcp/tool-registry.js.map +7 -0
  211. package/dist/mcp/tools/annotation-create.js +39 -0
  212. package/dist/mcp/tools/annotation-create.js.map +7 -0
  213. package/dist/mcp/tools/annotation-delete.js +29 -0
  214. package/dist/mcp/tools/annotation-delete.js.map +7 -0
  215. package/dist/mcp/tools/annotation-get.js +29 -0
  216. package/dist/mcp/tools/annotation-get.js.map +7 -0
  217. package/dist/mcp/tools/annotation-list.js +24 -0
  218. package/dist/mcp/tools/annotation-list.js.map +7 -0
  219. package/dist/mcp/tools/annotation-reply.js +34 -0
  220. package/dist/mcp/tools/annotation-reply.js.map +7 -0
  221. package/dist/mcp/tools/annotation-shared.js +10 -0
  222. package/dist/mcp/tools/annotation-shared.js.map +7 -0
  223. package/dist/mcp/tools/annotation-update.js +39 -0
  224. package/dist/mcp/tools/annotation-update.js.map +7 -0
  225. package/dist/mcp/tools/changeset-analysis-tools.js +78 -0
  226. package/dist/mcp/tools/changeset-analysis-tools.js.map +7 -0
  227. package/dist/mcp/tools/changeset-editing.js +184 -0
  228. package/dist/mcp/tools/changeset-editing.js.map +7 -0
  229. package/dist/mcp/tools/changeset-lifecycle.js +83 -0
  230. package/dist/mcp/tools/changeset-lifecycle.js.map +7 -0
  231. package/dist/mcp/tools/changeset-list.js +33 -0
  232. package/dist/mcp/tools/changeset-list.js.map +7 -0
  233. package/dist/mcp/tools/changeset-shared.js +31 -0
  234. package/dist/mcp/tools/changeset-shared.js.map +7 -0
  235. package/dist/mcp/tools/changeset-show.js +55 -0
  236. package/dist/mcp/tools/changeset-show.js.map +7 -0
  237. package/dist/mcp/tools/changeset-verify.js +43 -0
  238. package/dist/mcp/tools/changeset-verify.js.map +7 -0
  239. package/dist/mcp/tools/chat-launch.js +50 -0
  240. package/dist/mcp/tools/chat-launch.js.map +7 -0
  241. package/dist/mcp/tools/chat-status.js +26 -0
  242. package/dist/mcp/tools/chat-status.js.map +7 -0
  243. package/dist/mcp/tools/index.js +65 -0
  244. package/dist/mcp/tools/index.js.map +7 -0
  245. package/dist/mcp/tools/model-add.js +107 -0
  246. package/dist/mcp/tools/model-add.js.map +7 -0
  247. package/dist/mcp/tools/model-delete.js +99 -0
  248. package/dist/mcp/tools/model-delete.js.map +7 -0
  249. package/dist/mcp/tools/model-export.js +72 -0
  250. package/dist/mcp/tools/model-export.js.map +7 -0
  251. package/dist/mcp/tools/model-info.js +51 -0
  252. package/dist/mcp/tools/model-info.js.map +7 -0
  253. package/dist/mcp/tools/model-list.js +53 -0
  254. package/dist/mcp/tools/model-list.js.map +7 -0
  255. package/dist/mcp/tools/model-reload.js +34 -0
  256. package/dist/mcp/tools/model-reload.js.map +7 -0
  257. package/dist/mcp/tools/model-search.js +102 -0
  258. package/dist/mcp/tools/model-search.js.map +7 -0
  259. package/dist/mcp/tools/model-show.js +68 -0
  260. package/dist/mcp/tools/model-show.js.map +7 -0
  261. package/dist/mcp/tools/model-stats.js +22 -0
  262. package/dist/mcp/tools/model-stats.js.map +7 -0
  263. package/dist/mcp/tools/model-trace.js +118 -0
  264. package/dist/mcp/tools/model-trace.js.map +7 -0
  265. package/dist/mcp/tools/model-update.js +121 -0
  266. package/dist/mcp/tools/model-update.js.map +7 -0
  267. package/dist/mcp/tools/model-validate.js +78 -0
  268. package/dist/mcp/tools/model-validate.js.map +7 -0
  269. package/dist/mcp/tools/relationship-tools.js +98 -0
  270. package/dist/mcp/tools/relationship-tools.js.map +7 -0
  271. package/dist/mcp/tools/shared.js +126 -0
  272. package/dist/mcp/tools/shared.js.map +7 -0
  273. package/dist/mcp/tools/spec-type.js +90 -0
  274. package/dist/mcp/tools/spec-type.js.map +7 -0
  275. package/dist/reports/model-layer-report-generator.js +5 -3
  276. package/dist/reports/model-layer-report-generator.js.map +2 -2
  277. package/dist/reports/model-readme-generator.js +116 -0
  278. package/dist/reports/model-readme-generator.js.map +7 -0
  279. package/dist/reports/model-report-data.js +37 -1
  280. package/dist/reports/model-report-data.js.map +2 -2
  281. package/dist/reports/model-report-orchestrator.js +29 -6
  282. package/dist/reports/model-report-orchestrator.js.map +2 -2
  283. package/dist/reports/model-report-utils.js +9 -0
  284. package/dist/reports/model-report-utils.js.map +7 -0
  285. package/dist/schemas/bundled/analyzers/cbm.json +1186 -68
  286. package/dist/schemas/bundled/analyzers/manifest.json +2 -2
  287. package/dist/schemas/bundled/api.json +1029 -1658
  288. package/dist/schemas/bundled/apm.json +499 -734
  289. package/dist/schemas/bundled/application.json +745 -370
  290. package/dist/schemas/bundled/base.json +3 -220
  291. package/dist/schemas/bundled/business.json +452 -366
  292. package/dist/schemas/bundled/data-model.json +403 -383
  293. package/dist/schemas/bundled/data-store.json +334 -558
  294. package/dist/schemas/bundled/manifest.json +18 -18
  295. package/dist/schemas/bundled/motivation.json +349 -368
  296. package/dist/schemas/bundled/navigation.json +415 -731
  297. package/dist/schemas/bundled/product.json +325 -159
  298. package/dist/schemas/bundled/security.json +864 -1232
  299. package/dist/schemas/bundled/technology.json +574 -703
  300. package/dist/schemas/bundled/testing.json +718 -1643
  301. package/dist/schemas/bundled/ux.json +739 -1352
  302. package/dist/server/schemas.js +8 -0
  303. package/dist/server/schemas.js.map +2 -2
  304. package/dist/server/server.js +234 -31
  305. package/dist/server/server.js.map +2 -2
  306. package/dist/server-entry.js +2 -1
  307. package/dist/server-entry.js.map +2 -2
  308. package/dist/telemetry/config.js +2 -40
  309. package/dist/telemetry/config.js.map +2 -2
  310. package/dist/telemetry/index.js +1 -1
  311. package/dist/types/index.js.map +2 -2
  312. package/dist/utils/ansi-suppressor.js +23 -0
  313. package/dist/utils/ansi-suppressor.js.map +7 -0
  314. package/dist/utils/attribute-patch.js +41 -0
  315. package/dist/utils/attribute-patch.js.map +7 -0
  316. package/dist/utils/color-utils.js +34 -0
  317. package/dist/utils/color-utils.js.map +7 -0
  318. package/dist/utils/element-reference-shape.js +15 -1
  319. package/dist/utils/element-reference-shape.js.map +3 -3
  320. package/dist/utils/error-hints.js +29 -0
  321. package/dist/utils/error-hints.js.map +7 -0
  322. package/dist/utils/errors.js +75 -10
  323. package/dist/utils/errors.js.map +2 -2
  324. package/dist/utils/globals.js +17 -1
  325. package/dist/utils/globals.js.map +2 -2
  326. package/dist/utils/project-paths.js +50 -3
  327. package/dist/utils/project-paths.js.map +2 -2
  328. package/dist/utils/reference-path-parser.js +49 -0
  329. package/dist/utils/reference-path-parser.js.map +7 -0
  330. package/dist/utils/source-reference.js +17 -10
  331. package/dist/utils/source-reference.js.map +2 -2
  332. package/dist/utils/spec-version.js +10 -2
  333. package/dist/utils/spec-version.js.map +2 -2
  334. package/dist/validators/composed-reference-validator.js +240 -0
  335. package/dist/validators/composed-reference-validator.js.map +7 -0
  336. package/dist/validators/reference-validator.js +57 -13
  337. package/dist/validators/reference-validator.js.map +3 -3
  338. package/dist/validators/relationship-schema-validator.js +33 -5
  339. package/dist/validators/relationship-schema-validator.js.map +3 -3
  340. package/dist/validators/schema-validator.js +27 -9
  341. package/dist/validators/schema-validator.js.map +2 -2
  342. package/dist/validators/types.js.map +1 -1
  343. package/dist/viewer/README.md +6 -6
  344. package/dist/viewer/assets/main-CxzcuU_o.js +84 -0
  345. package/dist/viewer/assets/main-j-7ULYpm.css +1 -0
  346. package/dist/viewer/index.html +2 -2
  347. package/dist/viewer/manifest.json +10 -10
  348. package/dist/viewer/version.json +1 -1
  349. package/package.json +7 -3
  350. package/dist/viewer/assets/main-B7AOQXXX.js +0 -81
  351. package/dist/viewer/assets/main-vdrFrG7q.css +0 -1
@@ -0,0 +1,773 @@
1
+ import ansis from "ansis";
2
+ import path from "path";
3
+ import { rm } from "fs/promises";
4
+ import { FarmManifest } from "../core/farm-manifest.js";
5
+ import { findFarmRoot, resolveModelDirectory } from "../utils/project-paths.js";
6
+ import { fileExists } from "../utils/file-io.js";
7
+ import { execSync, execFileSync } from "child_process";
8
+ import * as prompts from "@clack/prompts";
9
+ import { getErrorMessage, handleSuccess, handleInfo, handleWarning } from "../utils/errors.js";
10
+ import { isJson } from "../utils/globals.js";
11
+ import { isTelemetryEnabled, startSpan, endSpan } from "../telemetry/index.js";
12
+ import { Model } from "../core/model.js";
13
+ import { ComposedReferenceValidator } from "../validators/composed-reference-validator.js";
14
+ import { ValidationFormatter } from "../validators/validation-formatter.js";
15
+ import { FarmSyncEngine } from "../core/farm-sync-engine.js";
16
+ import { FarmSyncState, FarmSyncStateNotFoundError } from "../core/farm-sync-state.js";
17
+ function checkGitRepository(cwd) {
18
+ try {
19
+ return execSync("git rev-parse --git-dir", { cwd, stdio: "pipe" }), { isRepo: !0 };
20
+ } catch (error) {
21
+ const errorMsg = getErrorMessage(error);
22
+ return errorMsg.toLowerCase().includes("not a git repository") ? { isRepo: !1 } : { isRepo: !1, error: errorMsg.trim() };
23
+ }
24
+ }
25
+ function uncommittedChangeWarning(what, farmRoot, gitError) {
26
+ return `${what} was saved but not committed: could not check git repository at ${farmRoot}: ${gitError}`;
27
+ }
28
+ function gitTopLevel(cwd) {
29
+ try {
30
+ return execSync("git rev-parse --show-toplevel", { cwd, stdio: "pipe", encoding: "utf-8" }).trim() || null;
31
+ } catch {
32
+ return null;
33
+ }
34
+ }
35
+ function isInSameGitRepository(farmRoot, target) {
36
+ const farmTop = gitTopLevel(farmRoot);
37
+ return farmTop !== null && farmTop === gitTopLevel(target);
38
+ }
39
+ function farmRelativePath(farmRoot, target) {
40
+ const rel = path.relative(farmRoot, target);
41
+ return rel === "" ? "." : rel.split(path.sep).join("/");
42
+ }
43
+ function isWithinPath(target, parent) {
44
+ return target === parent || target.startsWith(parent + path.sep);
45
+ }
46
+ async function resolveProjectModel(name, input) {
47
+ const { farmRoot, codebaseFullPath, explicitModel, forceNewModel } = input;
48
+ if (explicitModel) {
49
+ const explicitFullPath = path.resolve(farmRoot, explicitModel), resolved = await resolveModelDirectory(explicitFullPath, { requireManifest: !0 });
50
+ if (!resolved)
51
+ throw new Error(`No model found at '${explicitModel}': expected manifest.yaml in ${explicitFullPath}, ${path.join(explicitFullPath, "model")} or ${path.join(explicitFullPath, "documentation-robotics", "model")}. Omit --model to scaffold a new model at ${name}-model.`);
52
+ return {
53
+ modelFolder: farmRelativePath(farmRoot, resolved.modelDir),
54
+ modelFullPath: resolved.modelDir,
55
+ adopted: !0
56
+ };
57
+ }
58
+ const detected = codebaseFullPath === farmRoot ? null : await resolveModelDirectory(codebaseFullPath, { requireManifest: !0 });
59
+ if (detected && !forceNewModel)
60
+ return {
61
+ modelFolder: farmRelativePath(farmRoot, detected.modelDir),
62
+ modelFullPath: detected.modelDir,
63
+ adopted: !0
64
+ };
65
+ const modelFolder = `${name}-model`, modelFullPath = path.resolve(path.join(farmRoot, modelFolder));
66
+ if (!modelFullPath.startsWith(farmRoot + path.sep))
67
+ throw new Error(`Invalid model path: would escape farm root. Path: ${modelFolder}`);
68
+ return {
69
+ modelFolder,
70
+ modelFullPath,
71
+ adopted: !1,
72
+ ...detected ? { ignoredModelPath: farmRelativePath(farmRoot, detected.modelDir) } : {}
73
+ };
74
+ }
75
+ async function farmInitCommand(options) {
76
+ const span = isTelemetryEnabled ? startSpan("farm.init") : null;
77
+ try {
78
+ const farmPath = process.cwd(), farmYamlPath = path.join(farmPath, "farm.yaml"), useJson = options.format === "json" || isJson();
79
+ if (await fileExists(farmYamlPath)) {
80
+ const message = "Farm already initialized in this directory";
81
+ useJson ? console.log(JSON.stringify({ status: "error", code: 1, message })) : console.error(ansis.red(`Error: ${message}`)), span && endSpan(span), process.exit(1);
82
+ }
83
+ let farmName = options.name;
84
+ if (!farmName && process.stdin.isTTY) {
85
+ const result = await prompts.text({
86
+ message: "Farm name",
87
+ placeholder: "My Architecture Farm"
88
+ });
89
+ typeof result == "string" && (farmName = result);
90
+ }
91
+ farmName || (farmName = "Architecture Farm"), await FarmManifest.create(farmName, {
92
+ platform_view: options.platformView || !1
93
+ }).save(farmYamlPath);
94
+ try {
95
+ execSync("git init", { cwd: farmPath, stdio: useJson ? "pipe" : "inherit" }), execSync("git config user.email 'dr-farm@localhost'", {
96
+ cwd: farmPath,
97
+ stdio: "pipe"
98
+ }), execSync("git config user.name 'DR Farm'", {
99
+ cwd: farmPath,
100
+ stdio: "pipe"
101
+ }), execSync("git add farm.yaml", { cwd: farmPath, stdio: "pipe" }), execSync("git commit -m 'Initialize farm manifest'", {
102
+ cwd: farmPath,
103
+ stdio: useJson ? "pipe" : "inherit"
104
+ });
105
+ } catch (gitError) {
106
+ throw new Error(`Failed to initialize farm git repository: ${getErrorMessage(gitError)}`);
107
+ }
108
+ if (useJson)
109
+ console.log(JSON.stringify({ status: "ok", farmPath, farmName, platform_view: options.platformView || !1 }));
110
+ else {
111
+ const platformViewNote = options.platformView ? " (platform-view enabled)" : "";
112
+ handleSuccess(`Farm initialized: ${ansis.bold(farmName)}${platformViewNote}
113
+ Location: ${farmPath}`);
114
+ }
115
+ span && endSpan(span);
116
+ } catch (error) {
117
+ options.format === "json" || isJson() ? console.log(JSON.stringify({
118
+ status: "error",
119
+ code: 1,
120
+ message: getErrorMessage(error)
121
+ })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), span && endSpan(span), process.exit(1);
122
+ }
123
+ }
124
+ async function farmAddCommand(name, options) {
125
+ const span = isTelemetryEnabled ? startSpan("farm.add", { "project.name": name }) : null, useJson = options.format === "json" || isJson();
126
+ try {
127
+ if (options.model && options.newModel)
128
+ throw new Error("--model and --new-model cannot be combined: --model registers an existing model, --new-model scaffolds a fresh one");
129
+ const farmRoot = await findFarmRoot();
130
+ if (!farmRoot)
131
+ throw new Error("Not in a farm directory. Run 'dr farm init' first.");
132
+ const farmYamlPath = path.join(farmRoot, "farm.yaml"), manifest = await FarmManifest.load(farmYamlPath);
133
+ if (manifest.getProject(name))
134
+ throw new Error(`Project '${name}' already exists in this farm`);
135
+ let codebasePath = options.codebase || name;
136
+ const codebaseFullPath = path.resolve(path.join(farmRoot, codebasePath)), farmRootResolved = path.resolve(farmRoot);
137
+ if (options.remote) {
138
+ if (!codebaseFullPath.startsWith(farmRootResolved + path.sep))
139
+ throw new Error(`Invalid codebase path: would escape farm root. Path: ${codebasePath}`);
140
+ if (await fileExists(codebaseFullPath))
141
+ throw new Error(`Codebase directory '${codebasePath}' already exists`);
142
+ useJson || handleInfo(`Cloning ${options.remote} to ${codebasePath}...`);
143
+ try {
144
+ execFileSync("git", ["clone", options.remote, codebaseFullPath], {
145
+ stdio: useJson ? "pipe" : "inherit"
146
+ });
147
+ } catch (error) {
148
+ throw new Error(`Failed to clone repository: ${getErrorMessage(error)}`);
149
+ }
150
+ }
151
+ const registration = await resolveProjectModel(name, {
152
+ farmRoot: farmRootResolved,
153
+ codebaseFullPath,
154
+ explicitModel: options.model,
155
+ forceNewModel: options.newModel ?? !1
156
+ }), modelFolder = registration.modelFolder;
157
+ if (registration.ignoredModelPath) {
158
+ const ignoredWarning = `Codebase '${codebasePath}' already has a model at ${registration.ignoredModelPath}; ignoring it and scaffolding a new model at ${modelFolder} (--new-model)`;
159
+ registration.warning = ignoredWarning, useJson || handleWarning(ignoredWarning);
160
+ }
161
+ registration.adopted ? useJson || handleInfo(`Using existing model at ${registration.modelFullPath}; nothing was created`) : await fileExists(registration.modelFullPath) || (await Model.init(registration.modelFullPath, {
162
+ name,
163
+ version: "0.1.0",
164
+ description: void 0,
165
+ author: void 0,
166
+ specVersion: (await import("../utils/spec-version.js")).getCliBundledSpecVersion(),
167
+ created: (/* @__PURE__ */ new Date()).toISOString()
168
+ }, { lazyLoad: !1 }), useJson || handleInfo(`Created and initialized model folder: ${modelFolder}`)), manifest.addProject(name, {
169
+ name,
170
+ source: codebasePath,
171
+ model: modelFolder,
172
+ remote: options.remote
173
+ }), await manifest.save(farmYamlPath);
174
+ const gitCheck = checkGitRepository(farmRoot);
175
+ let gitCommitted = !1, gitWarning;
176
+ if (gitCheck.error)
177
+ gitWarning = uncommittedChangeWarning("farm.yaml", farmRoot, gitCheck.error), useJson || handleWarning(gitWarning);
178
+ else if (gitCheck.isRepo)
179
+ try {
180
+ execSync("git add farm.yaml", { cwd: farmRoot, stdio: "pipe" }), execFileSync("git", ["commit", "-m", `Add project: ${name}`], {
181
+ cwd: farmRoot,
182
+ stdio: useJson ? "pipe" : "inherit"
183
+ }), gitCommitted = !0;
184
+ } catch (commitError) {
185
+ const errorMsg = getErrorMessage(commitError);
186
+ if (!errorMsg.includes("nothing to commit"))
187
+ throw new Error(`Failed to commit farm changes: ${errorMsg}`);
188
+ }
189
+ if (useJson)
190
+ console.log(JSON.stringify({
191
+ status: "ok",
192
+ project: name,
193
+ source: codebasePath,
194
+ model: modelFolder,
195
+ model_path: registration.modelFullPath,
196
+ model_adopted: registration.adopted,
197
+ ...registration.ignoredModelPath ? { ignored_model: registration.ignoredModelPath } : {},
198
+ ...registration.warning ? { warning: registration.warning } : {},
199
+ git_committed: gitCommitted,
200
+ ...gitWarning ? { git_warning: gitWarning } : {}
201
+ }));
202
+ else {
203
+ const modelNote = registration.adopted ? " (existing model, adopted in place)" : "";
204
+ handleSuccess(`Project added to farm: ${ansis.bold(name)}
205
+ Codebase: ${codebasePath}
206
+ Model: ${modelFolder}${modelNote}`);
207
+ }
208
+ span && endSpan(span);
209
+ } catch (error) {
210
+ useJson ? console.log(JSON.stringify({
211
+ status: "error",
212
+ code: 1,
213
+ message: getErrorMessage(error)
214
+ })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), span && endSpan(span), process.exit(1);
215
+ }
216
+ }
217
+ async function farmRemoveCommand(name, options) {
218
+ const span = isTelemetryEnabled ? startSpan("farm.remove", { "project.name": name }) : null, useJson = options.format === "json" || isJson();
219
+ try {
220
+ const farmRoot = await findFarmRoot();
221
+ if (!farmRoot)
222
+ throw new Error("Not in a farm directory.");
223
+ const farmYamlPath = path.join(farmRoot, "farm.yaml"), manifest = await FarmManifest.load(farmYamlPath), project = manifest.getProject(name);
224
+ if (!project)
225
+ throw new Error(`Project '${name}' not found in farm`);
226
+ let modelDeleted = !1, modelKeptReason;
227
+ if (options.deleteModel) {
228
+ const modelFullPath = path.resolve(path.join(farmRoot, project.model)), farmRootResolved = path.resolve(farmRoot), codebaseFullPath = path.resolve(path.join(farmRoot, project.source));
229
+ if (modelFullPath.startsWith(farmRootResolved + path.sep) ? isWithinPath(modelFullPath, codebaseFullPath) && (modelKeptReason = `model '${project.model}' is the codebase's own model (inside '${project.source}')`) : modelKeptReason = `model '${project.model}' lies outside the farm root and is not managed by the farm`, modelKeptReason)
230
+ useJson || handleWarning(`Model folder not deleted: ${modelKeptReason}. Remove it by hand if that is what you want.`);
231
+ else if (await fileExists(modelFullPath))
232
+ try {
233
+ await rm(modelFullPath, { recursive: !0, force: !1 }), modelDeleted = !0, useJson || handleInfo(`Deleted model folder: ${project.model}`);
234
+ } catch (error) {
235
+ throw new Error(`Failed to delete model folder: ${getErrorMessage(error)}`);
236
+ }
237
+ }
238
+ manifest.removeProject(name), await manifest.save(farmYamlPath);
239
+ const gitCheck = checkGitRepository(farmRoot);
240
+ let gitCommitted = !1, gitWarning;
241
+ if (gitCheck.error)
242
+ gitWarning = uncommittedChangeWarning("farm.yaml", farmRoot, gitCheck.error), useJson || handleWarning(gitWarning);
243
+ else if (gitCheck.isRepo)
244
+ try {
245
+ execSync("git add farm.yaml", { cwd: farmRoot, stdio: "pipe" }), execFileSync("git", ["commit", "-m", `Remove project: ${name}`], {
246
+ cwd: farmRoot,
247
+ stdio: useJson ? "pipe" : "inherit"
248
+ }), gitCommitted = !0;
249
+ } catch (commitError) {
250
+ const errorMsg = getErrorMessage(commitError);
251
+ if (!errorMsg.includes("nothing to commit"))
252
+ throw new Error(`Failed to commit farm changes: ${errorMsg}`);
253
+ }
254
+ useJson ? console.log(JSON.stringify({
255
+ status: "ok",
256
+ project: name,
257
+ model: project.model,
258
+ modelDeleted,
259
+ ...modelKeptReason ? { model_kept_reason: modelKeptReason } : {},
260
+ git_committed: gitCommitted,
261
+ ...gitWarning ? { git_warning: gitWarning } : {}
262
+ })) : handleSuccess(`Project removed from farm: ${ansis.bold(name)}`), span && endSpan(span);
263
+ } catch (error) {
264
+ useJson ? console.log(JSON.stringify({
265
+ status: "error",
266
+ code: 1,
267
+ message: getErrorMessage(error)
268
+ })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), span && endSpan(span), process.exit(1);
269
+ }
270
+ }
271
+ async function farmStatusCommand(options) {
272
+ const span = isTelemetryEnabled ? startSpan("farm.status") : null;
273
+ try {
274
+ const farmRoot = await findFarmRoot();
275
+ if (!farmRoot)
276
+ throw new Error("Not in a farm directory.");
277
+ const farmYamlPath = path.join(farmRoot, "farm.yaml"), manifest = await FarmManifest.load(farmYamlPath), projects = manifest.getAllProjects(), engine = new FarmSyncEngine(farmRoot), useJson = options.format === "json" || isJson(), projectsWithStatus = await Promise.all(projects.map(async (p) => {
278
+ const result = {
279
+ name: p.name,
280
+ source: p.source,
281
+ model: p.model,
282
+ remote: p.remote,
283
+ last_sync_commit: void 0,
284
+ current_commit: void 0,
285
+ has_pending_changes: void 0
286
+ };
287
+ let lastSyncCommit;
288
+ try {
289
+ const syncStatePath = path.join(farmRoot, p.model, ".farm-sync.yaml");
290
+ lastSyncCommit = (await FarmSyncState.loadOrCreate(syncStatePath, p.name)).lastSyncCommit, result.last_sync_commit = lastSyncCommit;
291
+ } catch (error) {
292
+ return error instanceof FarmSyncStateNotFoundError ? (result.has_pending_changes = !1, result) : (result.sync_state_error = getErrorMessage(error), useJson || handleWarning(`${p.name}: could not read sync state: ${result.sync_state_error}`), result);
293
+ }
294
+ try {
295
+ const currentCommit = await engine.getCurrentCommit(p.source);
296
+ result.current_commit = currentCommit, result.has_pending_changes = !!(lastSyncCommit && lastSyncCommit !== currentCommit);
297
+ } catch (error) {
298
+ result.codebase_error = getErrorMessage(error), useJson || handleWarning(`${p.name}: ${result.codebase_error}`);
299
+ }
300
+ return result;
301
+ })), projectsWithErrors = projectsWithStatus.filter((p) => p.sync_state_error !== void 0 || p.codebase_error !== void 0), allHealthy = projectsWithErrors.length === 0;
302
+ useJson ? console.log(JSON.stringify({
303
+ status: allHealthy ? "ok" : "error",
304
+ code: allHealthy ? 0 : 1,
305
+ farm: {
306
+ name: manifest.name,
307
+ path: farmRoot,
308
+ created: manifest.created,
309
+ modified: manifest.modified,
310
+ platform_view: manifest.platform_view
311
+ },
312
+ projects: projectsWithStatus,
313
+ project_count: projects.length
314
+ })) : (console.log(ansis.bold(`Farm: ${manifest.name}`)), console.log(`Location: ${farmRoot}`), manifest.platform_view && console.log(`Platform-View: ${ansis.green("enabled")}`), console.log(`Projects: ${projects.length}
315
+ `), projects.length === 0 ? console.log(ansis.dim("No projects registered yet.")) : projectsWithStatus.forEach((project) => {
316
+ console.log(` ${ansis.cyan(project.name)}`), console.log(` Codebase: ${project.source}`), console.log(` Model: ${project.model}`), project.remote && console.log(` Remote: ${project.remote}`), project.sync_state_error ? (console.log(` Status: ${ansis.red("\u2717 Sync state corrupted")}`), console.log(` Error: ${project.sync_state_error}`)) : project.codebase_error ? (console.log(` Status: ${ansis.red("\u2717 Codebase unavailable")}`), console.log(` Error: ${project.codebase_error}`)) : project.has_pending_changes ? console.log(` Status: ${ansis.yellow("\u26A0 Pending changes")}`) : project.last_sync_commit ? console.log(` Status: ${ansis.green("\u2713 Up to date")}`) : console.log(` Status: ${ansis.dim("Not synced yet")}`);
317
+ }), allHealthy || console.error(ansis.red(`
318
+ ${projectsWithErrors.length} project(s) could not be checked (see errors above)`))), span && endSpan(span), allHealthy || process.exit(1);
319
+ } catch (error) {
320
+ options.format === "json" || isJson() ? console.log(JSON.stringify({
321
+ status: "error",
322
+ code: 2,
323
+ message: getErrorMessage(error)
324
+ })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), span && endSpan(span), process.exit(2);
325
+ }
326
+ }
327
+ async function farmValidateCommand(options) {
328
+ const span = isTelemetryEnabled ? startSpan("farm.validate") : null;
329
+ try {
330
+ const farmRoot = await findFarmRoot();
331
+ if (!farmRoot)
332
+ throw new Error("Not in a farm directory. Run 'dr farm init' first.");
333
+ const farmYamlPath = path.join(farmRoot, "farm.yaml"), farmManifest = await FarmManifest.load(farmYamlPath), useJson = options.format === "json" || isJson();
334
+ let projectsToValidate = farmManifest.getAllProjects(), isPlatformViewValidation = !1;
335
+ if (options.project) {
336
+ const project = farmManifest.getProject(options.project);
337
+ if (!project)
338
+ throw new Error(`Project '${options.project}' not found in farm`);
339
+ projectsToValidate = [project], farmManifest.isPlatformViewEnabled() && options.project === "platform-view" ? isPlatformViewValidation = !0 : options.project === "platform-view" && !farmManifest.isPlatformViewEnabled() && !useJson && !options.quiet && handleInfo("Note: Validating platform-view project, but platform-view is not enabled on this farm");
340
+ }
341
+ if (projectsToValidate.length === 0)
342
+ throw new Error("No projects found in farm");
343
+ const composedValidator = await ComposedReferenceValidator.fromFarm(farmRoot);
344
+ if (useJson)
345
+ console.log(JSON.stringify({ status: "validating", projects: projectsToValidate.length, platform_view: isPlatformViewValidation }));
346
+ else if (!options.quiet)
347
+ if (options.project) {
348
+ const platformViewNote = isPlatformViewValidation ? " (platform-view aggregation)" : "";
349
+ handleInfo(`Validating project '${options.project}'${platformViewNote} with farm-aware references...`);
350
+ } else
351
+ handleInfo(`Validating ${projectsToValidate.length} farm project(s) with farm-aware references...`);
352
+ const allResults = [], reportData = [];
353
+ for (const project of projectsToValidate) {
354
+ const modelPath = path.join(farmRoot, project.model);
355
+ if (!await fileExists(modelPath)) {
356
+ const error = `Model folder not found for project '${project.name}' at ${modelPath}`;
357
+ useJson ? console.log(JSON.stringify({
358
+ status: "error",
359
+ code: 2,
360
+ project: project.name,
361
+ message: error
362
+ })) : console.error(ansis.red(`Error: ${error}`)), span && endSpan(span), process.exit(2);
363
+ }
364
+ let drModelPath = modelPath;
365
+ const manifestPath = path.join(modelPath, "manifest.yaml");
366
+ await fileExists(manifestPath) && (drModelPath = manifestPath);
367
+ const model = await Model.load(drModelPath);
368
+ let platformViewInfo;
369
+ if (isPlatformViewValidation) {
370
+ const declaredModels = model.manifest.models || {}, declaredModelNames = Object.keys(declaredModels);
371
+ if (platformViewInfo = {
372
+ declared_models: declaredModelNames,
373
+ missing_models: []
374
+ }, declaredModelNames.length === 0)
375
+ !useJson && !options.quiet && handleInfo(" Platform-view project has no declared external models");
376
+ else {
377
+ !useJson && !options.quiet && handleInfo(` Platform-view is declaring external models: ${declaredModelNames.join(", ")}`);
378
+ const missingModels = [];
379
+ for (const declaredModelName of declaredModelNames)
380
+ farmManifest.getProject(declaredModelName) || missingModels.push(declaredModelName);
381
+ platformViewInfo.missing_models = missingModels, missingModels.length > 0 && (useJson || console.error(ansis.yellow(` \u26A0 Warning: The following declared external models are not registered in the farm: ${missingModels.join(", ")}`)));
382
+ }
383
+ }
384
+ const result = await composedValidator.validateModel(model);
385
+ allResults.push({ project: project.name, result });
386
+ const formatted = ValidationFormatter.format(result, model, {
387
+ verbose: options.verbose,
388
+ quiet: options.quiet
389
+ }), reportItem = {
390
+ project: project.name,
391
+ valid: result.isValid(),
392
+ errors: result.errors.map((e) => ({
393
+ message: e.message,
394
+ layer: e.layer,
395
+ elementId: e.elementId,
396
+ location: e.location
397
+ })),
398
+ warnings: result.warnings.map((w) => ({
399
+ message: w.message,
400
+ layer: w.layer,
401
+ elementId: w.elementId,
402
+ location: w.location
403
+ })),
404
+ formatted
405
+ };
406
+ if (platformViewInfo && (reportItem.platform_view_info = platformViewInfo), reportData.push(reportItem), options.quiet || (console.log(ansis.bold(`
407
+ Project: ${project.name}`)), console.log(formatted)), !result.isValid() && options.project)
408
+ throw new Error(`Validation failed for project '${project.name}'`);
409
+ }
410
+ const allValid = allResults.every((r) => r.result.isValid());
411
+ if (options.output) {
412
+ const outputExt = path.extname(options.output).toLowerCase();
413
+ if (outputExt === ".json") {
414
+ const jsonReport = {
415
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
416
+ farm_root: farmRoot,
417
+ all_valid: allValid,
418
+ projects: reportData.map((r) => {
419
+ const projectData = {
420
+ project: r.project,
421
+ valid: r.valid,
422
+ errors: r.errors,
423
+ warnings: r.warnings
424
+ };
425
+ return r.platform_view_info && (projectData.platform_view_info = r.platform_view_info), projectData;
426
+ })
427
+ };
428
+ await (await import("fs/promises")).writeFile(options.output, JSON.stringify(jsonReport, null, 2), "utf-8");
429
+ } else if (outputExt === ".md") {
430
+ let mdContent = `# Farm Validation Report
431
+
432
+ `;
433
+ mdContent += `**Date**: ${(/* @__PURE__ */ new Date()).toISOString()}
434
+ `, mdContent += `**Farm Root**: ${farmRoot}
435
+ `, mdContent += `**Overall Status**: ${allValid ? "\u2705 Valid" : "\u274C Invalid"}
436
+
437
+ `, mdContent += `## Projects
438
+
439
+ `;
440
+ for (const r of reportData) {
441
+ if (mdContent += `### ${r.project}
442
+
443
+ `, mdContent += `**Status**: ${r.valid ? "\u2705 Valid" : "\u274C Invalid"}
444
+
445
+ `, r.errors.length > 0) {
446
+ mdContent += `#### Errors (${r.errors.length})
447
+
448
+ `;
449
+ for (const err of r.errors)
450
+ mdContent += `- ${err.message}
451
+ `;
452
+ mdContent += `
453
+ `;
454
+ }
455
+ if (r.warnings.length > 0) {
456
+ mdContent += `#### Warnings (${r.warnings.length})
457
+
458
+ `;
459
+ for (const warn of r.warnings)
460
+ mdContent += `- ${warn.message}
461
+ `;
462
+ mdContent += `
463
+ `;
464
+ }
465
+ }
466
+ await (await import("fs/promises")).writeFile(options.output, mdContent, "utf-8");
467
+ } else
468
+ throw new Error(`Unsupported output format: ${outputExt}. Use .json or .md`);
469
+ options.quiet || handleInfo(`Report written to ${options.output}`);
470
+ }
471
+ useJson && console.log(JSON.stringify({
472
+ status: "ok",
473
+ projects: allResults.map((r) => ({
474
+ name: r.project,
475
+ valid: r.result.isValid(),
476
+ errors: r.result.errors.length,
477
+ warnings: r.result.warnings.length
478
+ })),
479
+ all_valid: allValid,
480
+ output: options.output ? path.resolve(options.output) : void 0
481
+ })), allValid || (span && endSpan(span), process.exit(1)), options.quiet || handleSuccess(options.project ? `Project '${options.project}' validated successfully` : "All farm projects validated successfully"), span && endSpan(span);
482
+ } catch (error) {
483
+ options.format === "json" || isJson() ? console.log(JSON.stringify({
484
+ status: "error",
485
+ code: 2,
486
+ message: getErrorMessage(error)
487
+ })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), span && endSpan(span), process.exit(2);
488
+ }
489
+ }
490
+ async function farmPullCommand(options) {
491
+ const span = isTelemetryEnabled ? startSpan("farm.pull") : null;
492
+ try {
493
+ const farmRoot = await findFarmRoot();
494
+ if (!farmRoot)
495
+ throw new Error("Not in a farm directory. Run 'dr farm init' first.");
496
+ const farmYamlPath = path.join(farmRoot, "farm.yaml"), manifest = await FarmManifest.load(farmYamlPath);
497
+ let projectsToPull = manifest.getAllProjects();
498
+ if (options.project) {
499
+ const project = manifest.getProject(options.project);
500
+ if (!project)
501
+ throw new Error(`Project '${options.project}' not found in farm`);
502
+ projectsToPull = [project];
503
+ }
504
+ if (projectsToPull.length === 0)
505
+ throw new Error("No projects found in farm");
506
+ const useJson = options.format === "json" || isJson();
507
+ !useJson && !options.verbose && handleInfo(`Pulling ${projectsToPull.length} project(s)...`);
508
+ const engine = new FarmSyncEngine(farmRoot), results = [];
509
+ for (const project of projectsToPull)
510
+ try {
511
+ const commit = await engine.pullCodebase(project.source);
512
+ results.push({
513
+ project: project.name,
514
+ status: "success",
515
+ commit
516
+ }), options.verbose && !useJson && handleInfo(` \u2713 ${project.name}: ${commit.substring(0, 8)}`);
517
+ } catch (error) {
518
+ results.push({
519
+ project: project.name,
520
+ status: "error",
521
+ message: getErrorMessage(error)
522
+ }), useJson || console.error(ansis.red(` \u2717 ${project.name}: ${getErrorMessage(error)}`));
523
+ }
524
+ const allSucceeded = results.every((r) => r.status === "success");
525
+ useJson ? console.log(JSON.stringify({
526
+ status: allSucceeded ? "ok" : "partial",
527
+ code: allSucceeded ? 0 : 1,
528
+ projects: results,
529
+ pulled: results.filter((r) => r.status === "success").length,
530
+ failed: results.filter((r) => r.status === "error").length
531
+ })) : allSucceeded ? handleSuccess(`Pulled ${projectsToPull.length} project(s) successfully`) : console.error(ansis.red("Pull failed for some projects")), span && endSpan(span), allSucceeded || process.exit(1);
532
+ } catch (error) {
533
+ options.format === "json" || isJson() ? console.log(JSON.stringify({
534
+ status: "error",
535
+ code: 2,
536
+ message: getErrorMessage(error)
537
+ })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), span && endSpan(span), process.exit(2);
538
+ }
539
+ }
540
+ async function farmSyncCommand(options) {
541
+ const span = isTelemetryEnabled ? startSpan("farm.sync") : null;
542
+ try {
543
+ const farmRoot = await findFarmRoot();
544
+ if (!farmRoot)
545
+ throw new Error("Not in a farm directory. Run 'dr farm init' first.");
546
+ const farmYamlPath = path.join(farmRoot, "farm.yaml"), manifest = await FarmManifest.load(farmYamlPath);
547
+ let projectsToSync = manifest.getAllProjects();
548
+ if (options.project) {
549
+ const project = manifest.getProject(options.project);
550
+ if (!project)
551
+ throw new Error(`Project '${options.project}' not found in farm`);
552
+ projectsToSync = [project];
553
+ }
554
+ if (projectsToSync.length === 0)
555
+ throw new Error("No projects found in farm");
556
+ const concurrency = options.concurrency ? Math.max(1, parseInt(options.concurrency, 10)) : 1;
557
+ if (isNaN(concurrency))
558
+ throw new Error("Concurrency must be a positive number");
559
+ if (options.autoCommit && concurrency > 1)
560
+ throw new Error("Cannot use --auto-commit with --concurrency > 1: concurrent git operations would cause race conditions. Use --concurrency 1 for auto-commit, or remove --auto-commit for parallel processing.");
561
+ if (options.output) {
562
+ const outputExt = path.extname(options.output).toLowerCase();
563
+ if (outputExt !== ".json")
564
+ throw new Error(`Unsupported output format: ${outputExt || "(no extension)"}. Use .json`);
565
+ }
566
+ const useJson = options.format === "json" || isJson();
567
+ !useJson && !options.verbose && handleInfo(`Syncing ${projectsToSync.length} project(s) (concurrency: ${concurrency})...`);
568
+ const allResults = [], processSyncProject = async (project) => {
569
+ try {
570
+ const modelPath = path.join(farmRoot, project.model);
571
+ if (!await fileExists(modelPath))
572
+ throw new Error(`Model folder not found for project '${project.name}' at ${modelPath}`);
573
+ let drModelPath = modelPath;
574
+ const manifestPath = path.join(modelPath, "manifest.yaml");
575
+ await fileExists(manifestPath) && (drModelPath = manifestPath);
576
+ try {
577
+ const model = await Model.load(drModelPath), result = await new FarmSyncEngine(farmRoot, model).syncProject(project, {
578
+ verbose: options.verbose,
579
+ dryRun: options.dryRun,
580
+ force: options.force
581
+ }), resultEntry = {
582
+ project: project.name,
583
+ status: result.status === "failed" ? "error" : result.status,
584
+ change_count: result.changeCount,
585
+ changeset_id: result.changesetId,
586
+ files_changed: result.filesChanged,
587
+ ambiguities: result.ambiguities.length,
588
+ commits_before: result.commitsBefore,
589
+ commits_after: result.commitsAfter
590
+ };
591
+ if (options.autoCommit && result.changesetId && result.changeCount > 0 && !options.dryRun)
592
+ try {
593
+ const commitResult = await new (await import("../core/staging-area.js")).StagingAreaManager(model.rootPath, model).commit(model, result.changesetId);
594
+ resultEntry.auto_committed = !0, resultEntry.committed_changes = commitResult.committed;
595
+ const gitCheck = checkGitRepository(farmRoot);
596
+ if (gitCheck.error)
597
+ resultEntry.git_committed = !1, resultEntry.git_warning = uncommittedChangeWarning(`Model '${project.model}'`, farmRoot, gitCheck.error), useJson || handleWarning(`${project.name}: ${resultEntry.git_warning}`);
598
+ else if (gitCheck.isRepo && !isInSameGitRepository(farmRoot, modelPath))
599
+ resultEntry.git_committed = !1, resultEntry.git_warning = `Model '${project.model}' is outside the farm git repository at ${farmRoot}: the synced changes were saved but not committed. Commit them in the model's own repository.`, useJson || handleWarning(`${project.name}: ${resultEntry.git_warning}`);
600
+ else if (gitCheck.isRepo)
601
+ try {
602
+ execFileSync("git", ["add", project.model], { cwd: farmRoot, stdio: "pipe" }), execFileSync("git", ["commit", "-m", `Sync: ${result.changesetId} - ${commitResult.committed} change(s)`], { cwd: farmRoot, stdio: "pipe" }), resultEntry.git_committed = !0;
603
+ } catch (gitCommitError) {
604
+ if (!getErrorMessage(gitCommitError).includes("nothing to commit"))
605
+ throw gitCommitError;
606
+ resultEntry.git_committed = !0;
607
+ }
608
+ else
609
+ resultEntry.git_committed = !1;
610
+ options.verbose && !useJson && handleInfo(` Auto-committed: ${commitResult.committed} change(s)`);
611
+ } catch (commitError) {
612
+ resultEntry.auto_committed = !1, resultEntry.commit_error = getErrorMessage(commitError), resultEntry.status = "error", useJson || handleWarning(`${project.name}: auto-commit failed: ${getErrorMessage(commitError)}`);
613
+ }
614
+ if (allResults.push(resultEntry), options.verbose && !useJson) {
615
+ handleInfo(`
616
+ Project: ${project.name}`);
617
+ const statusEmoji = result.status === "success" ? "\u2713" : result.status === "partial" ? "\u26A0" : "\u2717", statusText = result.status === "success" ? "Success" : result.status === "partial" ? "Partial" : "Failed";
618
+ handleInfo(` Status: ${statusEmoji} ${statusText}`), handleInfo(` Commits: ${result.commitsBefore}...${result.commitsAfter}`), handleInfo(` Files changed: +${result.filesChanged.added.length} ~${result.filesChanged.modified.length} -${result.filesChanged.deleted.length}`), handleInfo(` Staged changes: ${result.changeCount}`), result.changesetId && handleInfo(` Changeset: ${result.changesetId}`), result.ambiguities.length > 0 && handleInfo(` Ambiguities: ${result.ambiguities.length} (flagged for review)`);
619
+ for (const note of result.notes)
620
+ console.log(` ${note}`);
621
+ }
622
+ } catch (error) {
623
+ allResults.push({
624
+ project: project.name,
625
+ status: "error",
626
+ message: getErrorMessage(error)
627
+ }), useJson || console.error(ansis.red(`\u2717 ${project.name}: ${getErrorMessage(error)}`));
628
+ }
629
+ } catch (error) {
630
+ allResults.push({
631
+ project: project.name,
632
+ status: "error",
633
+ message: getErrorMessage(error)
634
+ }), useJson || console.error(ansis.red(`\u2717 ${project.name}: ${getErrorMessage(error)}`));
635
+ }
636
+ };
637
+ if (concurrency === 1)
638
+ for (const project of projectsToSync)
639
+ await processSyncProject(project);
640
+ else {
641
+ const queue = [...projectsToSync], active = [];
642
+ for (; queue.length > 0 || active.length > 0; ) {
643
+ for (; active.length < concurrency && queue.length > 0; ) {
644
+ const project = queue.shift(), wrapped = processSyncProject(project).then(() => {
645
+ const idx = active.indexOf(wrapped);
646
+ idx !== -1 && active.splice(idx, 1);
647
+ });
648
+ active.push(wrapped);
649
+ }
650
+ active.length > 0 && await Promise.race(active);
651
+ }
652
+ }
653
+ const allSucceeded = allResults.every((r) => r.status === "success" || r.status === "partial");
654
+ if (useJson)
655
+ console.log(JSON.stringify({
656
+ status: allSucceeded ? "ok" : "error",
657
+ code: allSucceeded ? 0 : 1,
658
+ projects: allResults,
659
+ synced: allResults.filter((r) => r.status === "success").length,
660
+ failed: allResults.filter((r) => r.status === "error").length,
661
+ auto_committed: options.autoCommit
662
+ }));
663
+ else {
664
+ const synced = allResults.filter((r) => r.status === "success").length;
665
+ allSucceeded && handleSuccess(`Synced ${synced}/${projectsToSync.length} project(s)`);
666
+ }
667
+ if (options.output) {
668
+ const { writeFile } = await import("../utils/file-io.js");
669
+ await writeFile(options.output, JSON.stringify({
670
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
671
+ farm_root: farmRoot,
672
+ projects: allResults
673
+ }, null, 2)), useJson || handleInfo(`Report written to ${options.output}`);
674
+ }
675
+ span && endSpan(span), allSucceeded || process.exit(1);
676
+ } catch (error) {
677
+ options.format === "json" || isJson() ? console.log(JSON.stringify({
678
+ status: "error",
679
+ code: 2,
680
+ message: getErrorMessage(error)
681
+ })) : console.error(ansis.red(`Error: ${getErrorMessage(error)}`)), span && endSpan(span), process.exit(2);
682
+ }
683
+ }
684
+ function farmCommands(program) {
685
+ const farmGroup = program.command("farm").description("Manage model farms");
686
+ farmGroup.command("init").description("Initialize a new farm").option("--name <name>", "Farm name").option("--platform-view", "Enable platform-view support for cross-model aggregation").option("--format <format>", "Output format: text, json", "text").addHelpText("after", `
687
+ Examples:
688
+ $ dr farm init
689
+ $ dr farm init --name "My Architecture Farm"
690
+ $ dr farm init --platform-view
691
+ $ dr farm init --format json
692
+
693
+ Notes:
694
+ When using --platform-view, create a project named exactly "platform-view" to serve as
695
+ the aggregation hub. When validating this project with 'dr farm validate --project platform-view',
696
+ the CLI will automatically resolve cross-model references from all farm projects.`).action(async (options) => {
697
+ await farmInitCommand(options);
698
+ }), farmGroup.command("add <name>").description("Add a project to the farm").option("--remote <url>", "Git remote URL to clone").option("--codebase <path>", "Path to codebase folder, relative to the farm root (defaults to project name; may point outside the farm root unless --remote is used)").option("--model <path>", "Register an existing model at this path (relative to the farm root) instead of detecting or scaffolding one").option("--new-model", "Always scaffold a fresh <name>-model, even when the codebase already carries a model").option("--format <format>", "Output format: text, json", "text").addHelpText("after", `
699
+ Examples:
700
+ $ dr farm add my-service
701
+ $ dr farm add my-service --remote https://github.com/org/my-service.git
702
+ $ dr farm add my-service --codebase ./services/my-service
703
+ $ dr farm add my-service --codebase ../my-service # sibling checkout outside the farm root
704
+ $ dr farm add my-service --codebase ../my-service --new-model
705
+ $ dr farm add my-service --model ../shared-models/my-service
706
+ $ dr farm add my-service --format json
707
+
708
+ Model detection:
709
+ If the codebase already holds a model (documentation-robotics/model/manifest.yaml, model/manifest.yaml
710
+ or manifest.yaml at the codebase path itself), that model is adopted in place and nothing is
711
+ created; farm.yaml records its path (which may lie outside the farm root). Use --new-model to
712
+ scaffold a separate <name>-model regardless, or --model to point at a model explicitly.`).action(async (name, options) => {
713
+ await farmAddCommand(name, options);
714
+ }), farmGroup.command("remove <name>").description("Remove a project from the farm").option("--delete-model", "Also delete the model folder").option("--format <format>", "Output format: text, json", "text").addHelpText("after", `
715
+ Examples:
716
+ $ dr farm remove my-service
717
+ $ dr farm remove my-service --delete-model
718
+ $ dr farm remove my-service --format json`).action(async (name, options) => {
719
+ await farmRemoveCommand(name, options);
720
+ }), farmGroup.command("status").description("Show farm status and registered projects").option("--format <format>", "Output format: text, json", "text").addHelpText("after", `
721
+ Examples:
722
+ $ dr farm status
723
+ $ dr farm status --format json`).action(async (options) => {
724
+ await farmStatusCommand(options);
725
+ }), farmGroup.command("validate").description("Validate farm projects using composed scope (cross-model references)").option("--project <name>", "Validate only a specific project (default: all)").option("--verbose", "Show verbose validation output").option("--quiet", "Suppress success messages").option("--output <path>", "Export validation report to file (JSON/Markdown)").option("--format <format>", "Output format: text, json", "text").addHelpText("after", `
726
+ Examples:
727
+ $ dr farm validate
728
+ $ dr farm validate --project my-service
729
+ $ dr farm validate --verbose
730
+ $ dr farm validate --format json
731
+ $ dr farm validate --output report.json
732
+
733
+ Notes:
734
+ For platform-view aggregation: Use 'dr farm validate --project platform-view' to validate
735
+ a platform-view project. The project name must be exactly 'platform-view' and the farm
736
+ must have platform-view enabled (set during 'dr farm init --platform-view').`).action(async (options) => {
737
+ await farmValidateCommand(options);
738
+ }), farmGroup.command("pull").description("Pull latest changes from remote for farm projects").option("--project <name>", "Pull only a specific project (default: all)").option("--verbose", "Show verbose output").option("--format <format>", "Output format: text, json", "text").addHelpText("after", `
739
+ Examples:
740
+ $ dr farm pull
741
+ $ dr farm pull --project my-service
742
+ $ dr farm pull --verbose
743
+ $ dr farm pull --format json`).action(async (options) => {
744
+ await farmPullCommand(options);
745
+ }), farmGroup.command("sync").description("Sync farm projects with their models (generates staged changesets)").option("--project <name>", "Sync only a specific project (default: all)").option("--verbose", "Show verbose output").option("--dry-run", "Preview without creating changesets").option("--force", "Proceed despite ambiguous file-to-element mappings").option("--output <path>", "Export sync report to file (.json only)").option("--format <format>", "Output format: text, json", "text").option("--auto-commit", "Automatically commit changes (bypasses staged review)").option("--concurrency <n>", "Process multiple projects in parallel (default: 1)", "1").addHelpText("after", `
746
+ Examples:
747
+ $ dr farm sync
748
+ $ dr farm sync --project my-service
749
+ $ dr farm sync --verbose --output sync-report.json
750
+ $ dr farm sync --dry-run
751
+ $ dr farm sync --format json --auto-commit
752
+ $ dr farm sync --concurrency 4
753
+
754
+ Automation (cron/CI):
755
+ $ dr farm sync --format json --auto-commit --output sync-report.json
756
+ $ dr farm sync --format json --concurrency 4 --output sync-report.json
757
+
758
+ Note: --auto-commit and --concurrency > 1 cannot be used together (race condition risk).
759
+ Use --concurrency 1 (default) with --auto-commit for automated syncs.`).action(async (options) => {
760
+ await farmSyncCommand(options);
761
+ }), farmGroup.showSuggestionAfterError();
762
+ }
763
+ export {
764
+ farmAddCommand,
765
+ farmCommands,
766
+ farmInitCommand,
767
+ farmPullCommand,
768
+ farmRemoveCommand,
769
+ farmStatusCommand,
770
+ farmSyncCommand,
771
+ farmValidateCommand
772
+ };
773
+ //# sourceMappingURL=farm.js.map