orbital-command 0.2.0 → 1.0.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 (431) hide show
  1. package/README.md +67 -42
  2. package/bin/commands/config.js +19 -0
  3. package/bin/commands/events.js +40 -0
  4. package/bin/commands/launch.js +126 -0
  5. package/bin/commands/manifest.js +283 -0
  6. package/bin/commands/registry.js +104 -0
  7. package/bin/commands/update.js +24 -0
  8. package/bin/lib/helpers.js +229 -0
  9. package/bin/orbital.js +147 -319
  10. package/dist/assets/Landing-CfQdHR0N.js +11 -0
  11. package/dist/assets/PrimitivesConfig-DThSipFy.js +32 -0
  12. package/dist/assets/QualityGates-B4kxM5UU.js +26 -0
  13. package/dist/assets/SessionTimeline-Bz1iZnmg.js +1 -0
  14. package/dist/assets/Settings-DLcZwbCT.js +12 -0
  15. package/dist/assets/SourceControl-BMNIz7Lt.js +36 -0
  16. package/dist/assets/WorkflowVisualizer-CxuSBOYu.js +69 -0
  17. package/dist/assets/arrow-down-DVPp6_qp.js +6 -0
  18. package/dist/assets/bot-NFaJBDn_.js +6 -0
  19. package/dist/assets/charts-LGLb8hyU.js +68 -0
  20. package/dist/assets/circle-x-IsFCkBZu.js +6 -0
  21. package/dist/assets/file-text-J1cebZXF.js +6 -0
  22. package/dist/assets/globe-WzeyHsUc.js +6 -0
  23. package/dist/assets/index-BdJ57EhC.css +1 -0
  24. package/dist/assets/index-o4ScMAuR.js +349 -0
  25. package/dist/assets/key-CKR8JJSj.js +6 -0
  26. package/dist/assets/minus-CHBsJyjp.js +6 -0
  27. package/dist/assets/radio-xqZaR-Uk.js +6 -0
  28. package/dist/assets/rocket-D_xvvNG6.js +6 -0
  29. package/dist/assets/shield-TdB1yv_a.js +6 -0
  30. package/dist/assets/ui-BmsSg9jU.js +53 -0
  31. package/dist/assets/useSocketListener-0L5yiN5i.js +1 -0
  32. package/dist/assets/useWorkflowEditor-CqeRWVQX.js +11 -0
  33. package/dist/assets/{vendor-Dzv9lrRc.js → vendor-Bqt8AJn2.js} +1 -1
  34. package/dist/assets/workflow-constants-Rw-GmgHZ.js +6 -0
  35. package/dist/assets/zap-C9wqYMpl.js +6 -0
  36. package/dist/favicon.svg +1 -0
  37. package/dist/index.html +6 -5
  38. package/dist/server/server/__tests__/data-routes.test.js +126 -0
  39. package/dist/server/server/__tests__/helpers/db.js +17 -0
  40. package/dist/server/server/__tests__/helpers/mock-emitter.js +8 -0
  41. package/dist/server/server/__tests__/scope-routes.test.js +138 -0
  42. package/dist/server/server/__tests__/sprint-routes.test.js +102 -0
  43. package/dist/server/server/__tests__/workflow-routes.test.js +107 -0
  44. package/dist/server/server/config-migrator.js +135 -0
  45. package/dist/server/server/config.js +51 -7
  46. package/dist/server/server/database.js +21 -28
  47. package/dist/server/server/global-config.js +143 -0
  48. package/dist/server/server/index.js +118 -276
  49. package/dist/server/server/init.js +243 -225
  50. package/dist/server/server/launch.js +29 -0
  51. package/dist/server/server/manifest-types.js +8 -0
  52. package/dist/server/server/manifest.js +454 -0
  53. package/dist/server/server/migrate-legacy.js +229 -0
  54. package/dist/server/server/parsers/event-parser.js +4 -1
  55. package/dist/server/server/parsers/event-parser.test.js +117 -0
  56. package/dist/server/server/parsers/scope-parser.js +74 -28
  57. package/dist/server/server/parsers/scope-parser.test.js +230 -0
  58. package/dist/server/server/project-context.js +265 -0
  59. package/dist/server/server/project-emitter.js +41 -0
  60. package/dist/server/server/project-manager.js +297 -0
  61. package/dist/server/server/routes/aggregate-routes.js +871 -0
  62. package/dist/server/server/routes/config-routes.js +41 -90
  63. package/dist/server/server/routes/data-routes.js +25 -123
  64. package/dist/server/server/routes/dispatch-routes.js +37 -15
  65. package/dist/server/server/routes/git-routes.js +74 -0
  66. package/dist/server/server/routes/manifest-routes.js +319 -0
  67. package/dist/server/server/routes/scope-routes.js +45 -28
  68. package/dist/server/server/routes/sync-routes.js +134 -0
  69. package/dist/server/server/routes/version-routes.js +1 -15
  70. package/dist/server/server/routes/workflow-routes.js +9 -3
  71. package/dist/server/server/schema.js +3 -0
  72. package/dist/server/server/services/batch-orchestrator.js +41 -17
  73. package/dist/server/server/services/claude-session-service.js +17 -14
  74. package/dist/server/server/services/config-service.js +10 -1
  75. package/dist/server/server/services/deploy-service.test.js +119 -0
  76. package/dist/server/server/services/event-service.js +64 -1
  77. package/dist/server/server/services/event-service.test.js +191 -0
  78. package/dist/server/server/services/gate-service.test.js +105 -0
  79. package/dist/server/server/services/git-service.js +108 -4
  80. package/dist/server/server/services/github-service.js +110 -2
  81. package/dist/server/server/services/readiness-service.test.js +190 -0
  82. package/dist/server/server/services/scope-cache.js +5 -1
  83. package/dist/server/server/services/scope-cache.test.js +142 -0
  84. package/dist/server/server/services/scope-service.js +222 -131
  85. package/dist/server/server/services/scope-service.test.js +137 -0
  86. package/dist/server/server/services/sprint-orchestrator.js +29 -15
  87. package/dist/server/server/services/sprint-service.js +23 -3
  88. package/dist/server/server/services/sprint-service.test.js +238 -0
  89. package/dist/server/server/services/sync-service.js +434 -0
  90. package/dist/server/server/services/sync-types.js +2 -0
  91. package/dist/server/server/services/workflow-service.js +26 -5
  92. package/dist/server/server/services/workflow-service.test.js +159 -0
  93. package/dist/server/server/settings-sync.js +284 -0
  94. package/dist/server/server/uninstall.js +195 -0
  95. package/dist/server/server/update-planner.js +279 -0
  96. package/dist/server/server/update.js +212 -0
  97. package/dist/server/server/utils/cc-hooks-parser.js +3 -0
  98. package/dist/server/server/utils/cc-hooks-parser.test.js +86 -0
  99. package/dist/server/server/utils/dispatch-utils.js +83 -24
  100. package/dist/server/server/utils/dispatch-utils.test.js +182 -0
  101. package/dist/server/server/utils/flag-builder.js +54 -0
  102. package/dist/server/server/utils/json-fields.js +14 -0
  103. package/dist/server/server/utils/json-fields.test.js +73 -0
  104. package/dist/server/server/utils/logger.js +37 -3
  105. package/dist/server/server/utils/package-info.js +30 -0
  106. package/dist/server/server/utils/route-helpers.js +47 -0
  107. package/dist/server/server/utils/route-helpers.test.js +115 -0
  108. package/dist/server/server/utils/terminal-launcher.js +79 -25
  109. package/dist/server/server/utils/worktree-manager.js +13 -4
  110. package/dist/server/server/validator.js +230 -0
  111. package/dist/server/server/watchers/event-watcher.js +28 -13
  112. package/dist/server/server/watchers/global-watcher.js +63 -0
  113. package/dist/server/server/watchers/scope-watcher.js +27 -12
  114. package/dist/server/server/wizard/config-editor.js +237 -0
  115. package/dist/server/server/wizard/detect.js +96 -0
  116. package/dist/server/server/wizard/doctor.js +115 -0
  117. package/dist/server/server/wizard/index.js +340 -0
  118. package/dist/server/server/wizard/phases/confirm.js +39 -0
  119. package/dist/server/server/wizard/phases/project-setup.js +90 -0
  120. package/dist/server/server/wizard/phases/setup-wizard.js +66 -0
  121. package/dist/server/server/wizard/phases/welcome.js +32 -0
  122. package/dist/server/server/wizard/phases/workflow-setup.js +22 -0
  123. package/dist/server/server/wizard/types.js +29 -0
  124. package/dist/server/server/wizard/ui.js +73 -0
  125. package/dist/server/shared/__fixtures__/workflow-configs.js +75 -0
  126. package/dist/server/shared/api-types.js +80 -1
  127. package/dist/server/shared/default-workflow.json +65 -0
  128. package/dist/server/shared/onboarding-tour.test.js +81 -0
  129. package/dist/server/shared/project-colors.js +24 -0
  130. package/dist/server/shared/workflow-config.test.js +84 -0
  131. package/dist/server/shared/workflow-engine.js +1 -1
  132. package/dist/server/shared/workflow-engine.test.js +302 -0
  133. package/dist/server/shared/workflow-normalizer.js +101 -0
  134. package/dist/server/shared/workflow-normalizer.test.js +100 -0
  135. package/dist/server/src/components/onboarding/tour-steps.js +84 -0
  136. package/package.json +34 -29
  137. package/schemas/orbital.config.schema.json +2 -5
  138. package/scripts/postinstall.js +18 -6
  139. package/scripts/release.sh +53 -0
  140. package/server/__tests__/data-routes.test.ts +151 -0
  141. package/server/__tests__/helpers/db.ts +19 -0
  142. package/server/__tests__/helpers/mock-emitter.ts +10 -0
  143. package/server/__tests__/scope-routes.test.ts +158 -0
  144. package/server/__tests__/sprint-routes.test.ts +118 -0
  145. package/server/__tests__/workflow-routes.test.ts +120 -0
  146. package/server/config-migrator.ts +160 -0
  147. package/server/config.ts +64 -12
  148. package/server/database.ts +22 -31
  149. package/server/global-config.ts +204 -0
  150. package/server/index.ts +139 -316
  151. package/server/init.ts +266 -234
  152. package/server/launch.ts +32 -0
  153. package/server/manifest-types.ts +145 -0
  154. package/server/manifest.ts +494 -0
  155. package/server/migrate-legacy.ts +290 -0
  156. package/server/parsers/event-parser.test.ts +135 -0
  157. package/server/parsers/event-parser.ts +4 -1
  158. package/server/parsers/scope-parser.test.ts +270 -0
  159. package/server/parsers/scope-parser.ts +79 -31
  160. package/server/project-context.ts +325 -0
  161. package/server/project-emitter.ts +50 -0
  162. package/server/project-manager.ts +368 -0
  163. package/server/routes/aggregate-routes.ts +968 -0
  164. package/server/routes/config-routes.ts +43 -85
  165. package/server/routes/data-routes.ts +34 -156
  166. package/server/routes/dispatch-routes.ts +46 -17
  167. package/server/routes/git-routes.ts +77 -0
  168. package/server/routes/manifest-routes.ts +388 -0
  169. package/server/routes/scope-routes.ts +39 -30
  170. package/server/routes/sync-routes.ts +175 -0
  171. package/server/routes/version-routes.ts +1 -16
  172. package/server/routes/workflow-routes.ts +9 -3
  173. package/server/schema.ts +3 -0
  174. package/server/services/batch-orchestrator.ts +41 -17
  175. package/server/services/claude-session-service.ts +16 -14
  176. package/server/services/config-service.ts +10 -1
  177. package/server/services/deploy-service.test.ts +145 -0
  178. package/server/services/deploy-service.ts +2 -2
  179. package/server/services/event-service.test.ts +242 -0
  180. package/server/services/event-service.ts +92 -3
  181. package/server/services/gate-service.test.ts +131 -0
  182. package/server/services/gate-service.ts +2 -2
  183. package/server/services/git-service.ts +137 -4
  184. package/server/services/github-service.ts +120 -2
  185. package/server/services/readiness-service.test.ts +217 -0
  186. package/server/services/scope-cache.test.ts +167 -0
  187. package/server/services/scope-cache.ts +4 -1
  188. package/server/services/scope-service.test.ts +169 -0
  189. package/server/services/scope-service.ts +224 -130
  190. package/server/services/sprint-orchestrator.ts +30 -15
  191. package/server/services/sprint-service.test.ts +271 -0
  192. package/server/services/sprint-service.ts +29 -5
  193. package/server/services/sync-service.ts +482 -0
  194. package/server/services/sync-types.ts +77 -0
  195. package/server/services/workflow-service.test.ts +190 -0
  196. package/server/services/workflow-service.ts +29 -9
  197. package/server/settings-sync.ts +359 -0
  198. package/server/uninstall.ts +214 -0
  199. package/server/update-planner.ts +346 -0
  200. package/server/update.ts +263 -0
  201. package/server/utils/cc-hooks-parser.test.ts +96 -0
  202. package/server/utils/cc-hooks-parser.ts +4 -0
  203. package/server/utils/dispatch-utils.test.ts +245 -0
  204. package/server/utils/dispatch-utils.ts +102 -30
  205. package/server/utils/flag-builder.ts +56 -0
  206. package/server/utils/json-fields.test.ts +83 -0
  207. package/server/utils/json-fields.ts +14 -0
  208. package/server/utils/logger.ts +40 -3
  209. package/server/utils/package-info.ts +32 -0
  210. package/server/utils/route-helpers.test.ts +144 -0
  211. package/server/utils/route-helpers.ts +50 -0
  212. package/server/utils/terminal-launcher.ts +85 -25
  213. package/server/utils/worktree-manager.ts +9 -4
  214. package/server/validator.ts +270 -0
  215. package/server/watchers/event-watcher.ts +24 -12
  216. package/server/watchers/global-watcher.ts +77 -0
  217. package/server/watchers/scope-watcher.ts +21 -9
  218. package/server/wizard/config-editor.ts +248 -0
  219. package/server/wizard/detect.ts +104 -0
  220. package/server/wizard/doctor.ts +114 -0
  221. package/server/wizard/index.ts +438 -0
  222. package/server/wizard/phases/confirm.ts +45 -0
  223. package/server/wizard/phases/project-setup.ts +106 -0
  224. package/server/wizard/phases/setup-wizard.ts +78 -0
  225. package/server/wizard/phases/welcome.ts +39 -0
  226. package/server/wizard/phases/workflow-setup.ts +28 -0
  227. package/server/wizard/types.ts +56 -0
  228. package/server/wizard/ui.ts +92 -0
  229. package/shared/__fixtures__/workflow-configs.ts +80 -0
  230. package/shared/api-types.ts +106 -0
  231. package/shared/onboarding-tour.test.ts +94 -0
  232. package/shared/project-colors.ts +24 -0
  233. package/shared/workflow-config.test.ts +111 -0
  234. package/shared/workflow-config.ts +7 -0
  235. package/shared/workflow-engine.test.ts +388 -0
  236. package/shared/workflow-engine.ts +1 -1
  237. package/shared/workflow-normalizer.test.ts +119 -0
  238. package/shared/workflow-normalizer.ts +118 -0
  239. package/templates/agents/QUICK-REFERENCE.md +1 -0
  240. package/templates/agents/README.md +1 -0
  241. package/templates/agents/SKILL-TRIGGERS.md +11 -0
  242. package/templates/agents/green-team/deep-dive.md +361 -0
  243. package/templates/hooks/end-session.sh +4 -1
  244. package/templates/hooks/init-session.sh +1 -0
  245. package/templates/hooks/orbital-emit.sh +2 -2
  246. package/templates/hooks/orbital-report-deploy.sh +4 -4
  247. package/templates/hooks/orbital-report-gates.sh +4 -4
  248. package/templates/hooks/orbital-scope-update.sh +1 -1
  249. package/templates/hooks/scope-commit-logger.sh +2 -2
  250. package/templates/hooks/scope-create-cleanup.sh +2 -2
  251. package/templates/hooks/scope-create-gate.sh +2 -5
  252. package/templates/hooks/scope-gate.sh +4 -6
  253. package/templates/hooks/scope-helpers.sh +28 -1
  254. package/templates/hooks/scope-lifecycle-gate.sh +14 -5
  255. package/templates/hooks/scope-prepare.sh +67 -12
  256. package/templates/hooks/scope-transition.sh +14 -6
  257. package/templates/hooks/time-tracker.sh +2 -5
  258. package/templates/migrations/renames.json +1 -0
  259. package/templates/orbital.config.json +8 -6
  260. package/{shared/default-workflow.json → templates/presets/default.json} +65 -0
  261. package/templates/presets/development.json +4 -4
  262. package/templates/presets/gitflow.json +7 -0
  263. package/templates/prompts/README.md +23 -0
  264. package/templates/prompts/deep-dive-audit.md +94 -0
  265. package/templates/quick/rules.md +56 -5
  266. package/templates/settings-hooks.json +1 -1
  267. package/templates/skills/git-commit/SKILL.md +27 -7
  268. package/templates/skills/git-dev/SKILL.md +13 -4
  269. package/templates/skills/git-main/SKILL.md +13 -3
  270. package/templates/skills/git-production/SKILL.md +9 -2
  271. package/templates/skills/git-staging/SKILL.md +11 -3
  272. package/templates/skills/scope-create/SKILL.md +17 -3
  273. package/templates/skills/scope-fix-review/SKILL.md +14 -7
  274. package/templates/skills/scope-implement/SKILL.md +15 -4
  275. package/templates/skills/scope-post-review/SKILL.md +77 -7
  276. package/templates/skills/scope-pre-review/SKILL.md +11 -4
  277. package/templates/skills/scope-verify/SKILL.md +5 -3
  278. package/templates/skills/test-code-review/SKILL.md +41 -33
  279. package/templates/skills/test-scaffold/SKILL.md +222 -0
  280. package/dist/assets/WorkflowVisualizer-BZ21PIIF.js +0 -84
  281. package/dist/assets/charts-D__PA1zp.js +0 -72
  282. package/dist/assets/index-D1G6i0nS.css +0 -1
  283. package/dist/assets/index-DpItvKpf.js +0 -419
  284. package/dist/assets/ui-BvF022GT.js +0 -53
  285. package/index.html +0 -15
  286. package/postcss.config.js +0 -6
  287. package/src/App.tsx +0 -33
  288. package/src/components/AgentBadge.tsx +0 -40
  289. package/src/components/BatchPreflightModal.tsx +0 -115
  290. package/src/components/CardDisplayToggle.tsx +0 -74
  291. package/src/components/ColumnHeaderActions.tsx +0 -55
  292. package/src/components/ColumnMenu.tsx +0 -99
  293. package/src/components/DeployHistory.tsx +0 -141
  294. package/src/components/DispatchModal.tsx +0 -164
  295. package/src/components/DispatchPopover.tsx +0 -139
  296. package/src/components/DragOverlay.tsx +0 -25
  297. package/src/components/DriftSidebar.tsx +0 -140
  298. package/src/components/EnvironmentStrip.tsx +0 -88
  299. package/src/components/ErrorBoundary.tsx +0 -62
  300. package/src/components/FilterChip.tsx +0 -105
  301. package/src/components/GateIndicator.tsx +0 -33
  302. package/src/components/IdeaDetailModal.tsx +0 -190
  303. package/src/components/IdeaFormDialog.tsx +0 -113
  304. package/src/components/KanbanColumn.tsx +0 -201
  305. package/src/components/MarkdownRenderer.tsx +0 -114
  306. package/src/components/NeonGrid.tsx +0 -128
  307. package/src/components/PromotionQueue.tsx +0 -89
  308. package/src/components/ScopeCard.tsx +0 -234
  309. package/src/components/ScopeDetailModal.tsx +0 -255
  310. package/src/components/ScopeFilterBar.tsx +0 -152
  311. package/src/components/SearchInput.tsx +0 -102
  312. package/src/components/SessionPanel.tsx +0 -335
  313. package/src/components/SprintContainer.tsx +0 -303
  314. package/src/components/SprintDependencyDialog.tsx +0 -78
  315. package/src/components/SprintPreflightModal.tsx +0 -138
  316. package/src/components/StatusBar.tsx +0 -168
  317. package/src/components/SwimCell.tsx +0 -67
  318. package/src/components/SwimLaneRow.tsx +0 -94
  319. package/src/components/SwimlaneBoardView.tsx +0 -108
  320. package/src/components/VersionBadge.tsx +0 -139
  321. package/src/components/ViewModeSelector.tsx +0 -114
  322. package/src/components/config/AgentChip.tsx +0 -53
  323. package/src/components/config/AgentCreateDialog.tsx +0 -321
  324. package/src/components/config/AgentEditor.tsx +0 -175
  325. package/src/components/config/DirectoryTree.tsx +0 -582
  326. package/src/components/config/FileEditor.tsx +0 -550
  327. package/src/components/config/HookChip.tsx +0 -50
  328. package/src/components/config/StageCard.tsx +0 -198
  329. package/src/components/config/TransitionZone.tsx +0 -173
  330. package/src/components/config/UnifiedWorkflowPipeline.tsx +0 -216
  331. package/src/components/config/WorkflowPipeline.tsx +0 -161
  332. package/src/components/source-control/BranchList.tsx +0 -93
  333. package/src/components/source-control/BranchPanel.tsx +0 -105
  334. package/src/components/source-control/CommitLog.tsx +0 -100
  335. package/src/components/source-control/CommitRow.tsx +0 -47
  336. package/src/components/source-control/GitHubPanel.tsx +0 -110
  337. package/src/components/source-control/GitHubSetupGuide.tsx +0 -52
  338. package/src/components/source-control/GitOverviewBar.tsx +0 -101
  339. package/src/components/source-control/PullRequestList.tsx +0 -69
  340. package/src/components/source-control/WorktreeList.tsx +0 -80
  341. package/src/components/ui/badge.tsx +0 -41
  342. package/src/components/ui/button.tsx +0 -55
  343. package/src/components/ui/card.tsx +0 -78
  344. package/src/components/ui/dialog.tsx +0 -94
  345. package/src/components/ui/popover.tsx +0 -33
  346. package/src/components/ui/scroll-area.tsx +0 -54
  347. package/src/components/ui/separator.tsx +0 -28
  348. package/src/components/ui/tabs.tsx +0 -52
  349. package/src/components/ui/toggle-switch.tsx +0 -35
  350. package/src/components/ui/tooltip.tsx +0 -27
  351. package/src/components/workflow/AddEdgeDialog.tsx +0 -217
  352. package/src/components/workflow/AddListDialog.tsx +0 -201
  353. package/src/components/workflow/ChecklistEditor.tsx +0 -239
  354. package/src/components/workflow/CommandPrefixManager.tsx +0 -118
  355. package/src/components/workflow/ConfigSettingsPanel.tsx +0 -189
  356. package/src/components/workflow/DirectionSelector.tsx +0 -133
  357. package/src/components/workflow/DispatchConfigPanel.tsx +0 -180
  358. package/src/components/workflow/EdgeDetailPanel.tsx +0 -236
  359. package/src/components/workflow/EdgePropertyEditor.tsx +0 -251
  360. package/src/components/workflow/EditToolbar.tsx +0 -138
  361. package/src/components/workflow/HookDetailPanel.tsx +0 -250
  362. package/src/components/workflow/HookExecutionLog.tsx +0 -24
  363. package/src/components/workflow/HookSourceModal.tsx +0 -129
  364. package/src/components/workflow/HooksDashboard.tsx +0 -363
  365. package/src/components/workflow/ListPropertyEditor.tsx +0 -251
  366. package/src/components/workflow/MigrationPreviewDialog.tsx +0 -237
  367. package/src/components/workflow/MovementRulesPanel.tsx +0 -188
  368. package/src/components/workflow/NodeDetailPanel.tsx +0 -245
  369. package/src/components/workflow/PresetSelector.tsx +0 -414
  370. package/src/components/workflow/SkillCommandBuilder.tsx +0 -174
  371. package/src/components/workflow/WorkflowEdgeComponent.tsx +0 -145
  372. package/src/components/workflow/WorkflowNode.tsx +0 -147
  373. package/src/components/workflow/graphLayout.ts +0 -186
  374. package/src/components/workflow/mergeHooks.ts +0 -85
  375. package/src/components/workflow/useEditHistory.ts +0 -88
  376. package/src/components/workflow/useWorkflowEditor.ts +0 -262
  377. package/src/components/workflow/validateConfig.ts +0 -70
  378. package/src/hooks/useActiveDispatches.ts +0 -198
  379. package/src/hooks/useBoardSettings.ts +0 -170
  380. package/src/hooks/useCardDisplay.ts +0 -57
  381. package/src/hooks/useCcHooks.ts +0 -24
  382. package/src/hooks/useConfigTree.ts +0 -51
  383. package/src/hooks/useEnforcementRules.ts +0 -46
  384. package/src/hooks/useEvents.ts +0 -59
  385. package/src/hooks/useFileEditor.ts +0 -165
  386. package/src/hooks/useGates.ts +0 -57
  387. package/src/hooks/useIdeaActions.ts +0 -53
  388. package/src/hooks/useKanbanDnd.ts +0 -410
  389. package/src/hooks/useOrbitalConfig.ts +0 -54
  390. package/src/hooks/usePipeline.ts +0 -47
  391. package/src/hooks/usePipelineData.ts +0 -338
  392. package/src/hooks/useReconnect.ts +0 -25
  393. package/src/hooks/useScopeFilters.ts +0 -125
  394. package/src/hooks/useScopeSessions.ts +0 -44
  395. package/src/hooks/useScopes.ts +0 -67
  396. package/src/hooks/useSearch.ts +0 -67
  397. package/src/hooks/useSettings.tsx +0 -187
  398. package/src/hooks/useSocket.ts +0 -25
  399. package/src/hooks/useSourceControl.ts +0 -105
  400. package/src/hooks/useSprintPreflight.ts +0 -55
  401. package/src/hooks/useSprints.ts +0 -154
  402. package/src/hooks/useStatusBarHighlight.ts +0 -18
  403. package/src/hooks/useSwimlaneBoardSettings.ts +0 -104
  404. package/src/hooks/useTheme.ts +0 -9
  405. package/src/hooks/useTransitionReadiness.ts +0 -53
  406. package/src/hooks/useVersion.ts +0 -155
  407. package/src/hooks/useViolations.ts +0 -65
  408. package/src/hooks/useWorkflow.tsx +0 -125
  409. package/src/hooks/useZoomModifier.ts +0 -19
  410. package/src/index.css +0 -797
  411. package/src/layouts/DashboardLayout.tsx +0 -113
  412. package/src/lib/collisionDetection.ts +0 -20
  413. package/src/lib/scope-fields.ts +0 -61
  414. package/src/lib/swimlane.ts +0 -146
  415. package/src/lib/utils.ts +0 -15
  416. package/src/main.tsx +0 -19
  417. package/src/socket.ts +0 -11
  418. package/src/types/index.ts +0 -497
  419. package/src/views/AgentFeed.tsx +0 -339
  420. package/src/views/DeployPipeline.tsx +0 -59
  421. package/src/views/EnforcementView.tsx +0 -378
  422. package/src/views/PrimitivesConfig.tsx +0 -500
  423. package/src/views/QualityGates.tsx +0 -1012
  424. package/src/views/ScopeBoard.tsx +0 -454
  425. package/src/views/SessionTimeline.tsx +0 -516
  426. package/src/views/Settings.tsx +0 -183
  427. package/src/views/SourceControl.tsx +0 -95
  428. package/src/views/WorkflowVisualizer.tsx +0 -382
  429. package/tailwind.config.js +0 -161
  430. package/tsconfig.json +0 -25
  431. package/vite.config.ts +0 -38
@@ -0,0 +1,138 @@
1
+ import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
2
+ import express from 'express';
3
+ import request from 'supertest';
4
+ import { createScopeRoutes } from '../routes/scope-routes.js';
5
+ import { WorkflowEngine } from '../../shared/workflow-engine.js';
6
+ import { DEFAULT_CONFIG } from '../../shared/__fixtures__/workflow-configs.js';
7
+ import { createTestDb } from './helpers/db.js';
8
+ import { createMockEmitter } from './helpers/mock-emitter.js';
9
+ function makeScope(overrides) {
10
+ return {
11
+ title: `Scope ${overrides.id}`,
12
+ slug: undefined,
13
+ status: 'backlog',
14
+ priority: null,
15
+ effort_estimate: null,
16
+ category: null,
17
+ tags: [],
18
+ blocked_by: [],
19
+ blocks: [],
20
+ file_path: `/scopes/backlog/${String(overrides.id).padStart(3, '0')}-test.md`,
21
+ created_at: null,
22
+ updated_at: null,
23
+ raw_content: '',
24
+ sessions: {},
25
+ is_ghost: false,
26
+ favourite: false,
27
+ ...overrides,
28
+ };
29
+ }
30
+ describe('scope-routes', () => {
31
+ let app;
32
+ let cleanup;
33
+ const testScopes = [
34
+ makeScope({ id: 1, title: 'First Scope' }),
35
+ makeScope({ id: 2, title: 'Second Scope' }),
36
+ ];
37
+ const mockScopeService = {
38
+ getAll: vi.fn().mockReturnValue(testScopes),
39
+ getById: vi.fn((id) => testScopes.find(s => s.id === id)),
40
+ updateStatus: vi.fn().mockReturnValue({ ok: true }),
41
+ updateFields: vi.fn().mockReturnValue({ ok: true }),
42
+ createIdeaFile: vi.fn().mockReturnValue({ slug: 'new-idea', title: 'New Idea' }),
43
+ updateIdeaFile: vi.fn().mockReturnValue(true),
44
+ deleteIdeaFile: vi.fn().mockReturnValue(true),
45
+ promoteIdea: vi.fn().mockReturnValue({ id: 10, filePath: '/scopes/planning/010-new.md', title: 'New', description: '' }),
46
+ approveGhostIdea: vi.fn().mockReturnValue(true),
47
+ };
48
+ const mockReadinessService = {
49
+ getReadiness: vi.fn((id) => id === 1 ? { scope_id: 1, transitions: [], blockers: [] } : null),
50
+ };
51
+ beforeAll(() => {
52
+ const { db, cleanup: c } = createTestDb();
53
+ cleanup = c;
54
+ const emitter = createMockEmitter();
55
+ const engine = new WorkflowEngine(DEFAULT_CONFIG);
56
+ const router = createScopeRoutes({
57
+ db,
58
+ io: emitter,
59
+ scopeService: mockScopeService,
60
+ readinessService: mockReadinessService,
61
+ projectRoot: '/tmp/test-project',
62
+ projectName: 'Test',
63
+ engine,
64
+ config: { claude: { dispatchFlags: { permissionMode: 'bypass', verbose: false, noMarkdown: false, printMode: false, outputFormat: null, allowedTools: [], disallowedTools: [], appendSystemPrompt: '' } }, dispatch: { envVars: {}, maxConcurrent: 5, maxBatchSize: 20, staleTimeoutMinutes: 10 } },
65
+ });
66
+ app = express();
67
+ app.use(express.json());
68
+ app.use('/api/orbital', router);
69
+ });
70
+ afterAll(() => cleanup?.());
71
+ describe('GET /scopes', () => {
72
+ it('returns all scopes', async () => {
73
+ const res = await request(app).get('/api/orbital/scopes');
74
+ expect(res.status).toBe(200);
75
+ expect(res.body).toHaveLength(2);
76
+ });
77
+ });
78
+ describe('GET /scopes/:id', () => {
79
+ it('returns scope by ID', async () => {
80
+ const res = await request(app).get('/api/orbital/scopes/1');
81
+ expect(res.status).toBe(200);
82
+ expect(res.body.id).toBe(1);
83
+ });
84
+ it('returns 404 for unknown scope', async () => {
85
+ const res = await request(app).get('/api/orbital/scopes/999');
86
+ expect(res.status).toBe(404);
87
+ });
88
+ });
89
+ describe('PATCH /scopes/:id', () => {
90
+ it('updates scope fields', async () => {
91
+ const res = await request(app)
92
+ .patch('/api/orbital/scopes/1')
93
+ .send({ title: 'Updated Title' });
94
+ expect(res.status).toBe(200);
95
+ expect(mockScopeService.updateFields).toHaveBeenCalled();
96
+ });
97
+ });
98
+ describe('PATCH /scopes/bulk/status', () => {
99
+ it('bulk updates scope statuses', async () => {
100
+ const res = await request(app)
101
+ .patch('/api/orbital/scopes/bulk/status')
102
+ .send({ scopes: [{ id: 1, status: 'implementing' }] });
103
+ expect(res.status).toBe(200);
104
+ });
105
+ });
106
+ describe('POST /ideas', () => {
107
+ it('creates idea with title', async () => {
108
+ const res = await request(app)
109
+ .post('/api/orbital/ideas')
110
+ .send({ title: 'New Idea', description: 'A description' });
111
+ expect(res.status).toBe(201);
112
+ expect(res.body.slug).toBe('new-idea');
113
+ });
114
+ it('rejects empty title', async () => {
115
+ const res = await request(app)
116
+ .post('/api/orbital/ideas')
117
+ .send({ title: ' ', description: '' });
118
+ expect(res.status).toBe(400);
119
+ });
120
+ });
121
+ describe('DELETE /ideas/:slug', () => {
122
+ it('deletes idea', async () => {
123
+ const res = await request(app).delete('/api/orbital/ideas/test-idea');
124
+ expect(res.status).toBe(200);
125
+ });
126
+ });
127
+ describe('GET /scopes/:id/readiness', () => {
128
+ it('returns readiness for known scope', async () => {
129
+ const res = await request(app).get('/api/orbital/scopes/1/readiness');
130
+ expect(res.status).toBe(200);
131
+ expect(res.body.scope_id).toBe(1);
132
+ });
133
+ it('returns 404 for unknown scope', async () => {
134
+ const res = await request(app).get('/api/orbital/scopes/999/readiness');
135
+ expect(res.status).toBe(404);
136
+ });
137
+ });
138
+ });
@@ -0,0 +1,102 @@
1
+ import { describe, it, expect, beforeAll, vi } from 'vitest';
2
+ import express from 'express';
3
+ import request from 'supertest';
4
+ import { createSprintRoutes } from '../routes/sprint-routes.js';
5
+ describe('sprint-routes', () => {
6
+ let app;
7
+ const mockSprintService = {
8
+ create: vi.fn().mockReturnValue({ id: 1, name: 'Sprint 1', status: 'assembling', scope_ids: [], scopes: [], layers: null, progress: { pending: 0, in_progress: 0, completed: 0, failed: 0, skipped: 0 } }),
9
+ getAll: vi.fn().mockReturnValue([]),
10
+ getById: vi.fn((id) => id === 1 ? { id: 1, name: 'Sprint 1', status: 'assembling', scope_ids: [], scopes: [] } : null),
11
+ rename: vi.fn().mockReturnValue(true),
12
+ delete: vi.fn().mockReturnValue(true),
13
+ addScopes: vi.fn().mockReturnValue({ added: [1, 2], unmet_dependencies: [] }),
14
+ removeScopes: vi.fn().mockReturnValue(true),
15
+ };
16
+ const mockSprintOrchestrator = {
17
+ startSprint: vi.fn().mockReturnValue({ ok: true, layers: [[1], [2]] }),
18
+ cancelSprint: vi.fn().mockReturnValue({ ok: true }),
19
+ getExecutionGraph: vi.fn().mockReturnValue({ nodes: [], edges: [] }),
20
+ };
21
+ const mockBatchOrchestrator = {
22
+ dispatch: vi.fn().mockReturnValue({ ok: true }),
23
+ };
24
+ beforeAll(() => {
25
+ const router = createSprintRoutes({
26
+ sprintService: mockSprintService,
27
+ sprintOrchestrator: mockSprintOrchestrator,
28
+ batchOrchestrator: mockBatchOrchestrator,
29
+ });
30
+ app = express();
31
+ app.use(express.json());
32
+ app.use('/api/orbital', router);
33
+ });
34
+ describe('POST /sprints', () => {
35
+ it('creates sprint with name (201)', async () => {
36
+ const res = await request(app)
37
+ .post('/api/orbital/sprints')
38
+ .send({ name: 'New Sprint' });
39
+ expect(res.status).toBe(201);
40
+ expect(mockSprintService.create).toHaveBeenCalledWith('New Sprint', expect.anything());
41
+ });
42
+ it('rejects empty name (400)', async () => {
43
+ const res = await request(app)
44
+ .post('/api/orbital/sprints')
45
+ .send({ name: ' ' });
46
+ expect(res.status).toBe(400);
47
+ });
48
+ });
49
+ describe('GET /sprints', () => {
50
+ it('returns sprint list', async () => {
51
+ const res = await request(app).get('/api/orbital/sprints');
52
+ expect(res.status).toBe(200);
53
+ expect(Array.isArray(res.body)).toBe(true);
54
+ });
55
+ it('passes status filter', async () => {
56
+ await request(app).get('/api/orbital/sprints?status=assembling');
57
+ expect(mockSprintService.getAll).toHaveBeenCalledWith('assembling', undefined);
58
+ });
59
+ });
60
+ describe('PATCH /sprints/:id', () => {
61
+ it('renames sprint', async () => {
62
+ const res = await request(app)
63
+ .patch('/api/orbital/sprints/1')
64
+ .send({ name: 'Renamed' });
65
+ expect(res.status).toBe(200);
66
+ });
67
+ it('rejects missing name (400)', async () => {
68
+ const res = await request(app)
69
+ .patch('/api/orbital/sprints/1')
70
+ .send({});
71
+ expect(res.status).toBe(400);
72
+ });
73
+ });
74
+ describe('DELETE /sprints/:id', () => {
75
+ it('deletes sprint', async () => {
76
+ const res = await request(app).delete('/api/orbital/sprints/1');
77
+ expect(res.status).toBe(200);
78
+ });
79
+ });
80
+ describe('POST /sprints/:id/scopes', () => {
81
+ it('adds scopes to sprint', async () => {
82
+ const res = await request(app)
83
+ .post('/api/orbital/sprints/1/scopes')
84
+ .send({ scope_ids: [1, 2] });
85
+ expect(res.status).toBe(200);
86
+ });
87
+ it('rejects empty scope_ids (400)', async () => {
88
+ const res = await request(app)
89
+ .post('/api/orbital/sprints/1/scopes')
90
+ .send({ scope_ids: [] });
91
+ expect(res.status).toBe(400);
92
+ });
93
+ });
94
+ describe('POST /sprints/:id/dispatch', () => {
95
+ it('dispatches sprint', async () => {
96
+ const res = await request(app)
97
+ .post('/api/orbital/sprints/1/dispatch')
98
+ .send({});
99
+ expect(res.status).toBe(200);
100
+ });
101
+ });
102
+ });
@@ -0,0 +1,107 @@
1
+ import { describe, it, expect, beforeAll, vi } from 'vitest';
2
+ import express from 'express';
3
+ import request from 'supertest';
4
+ import { createWorkflowRoutes } from '../routes/workflow-routes.js';
5
+ import { WorkflowEngine } from '../../shared/workflow-engine.js';
6
+ import { DEFAULT_CONFIG, MINIMAL_CONFIG } from '../../shared/__fixtures__/workflow-configs.js';
7
+ describe('workflow-routes', () => {
8
+ let app;
9
+ const engine = new WorkflowEngine(DEFAULT_CONFIG);
10
+ const mockWorkflowService = {
11
+ getActive: vi.fn().mockReturnValue(DEFAULT_CONFIG),
12
+ updateActive: vi.fn().mockReturnValue({ valid: true, errors: [], warnings: [] }),
13
+ listPresets: vi.fn().mockReturnValue([{ name: 'default', createdAt: '2026-01-01', listCount: 7, edgeCount: 14 }]),
14
+ savePreset: vi.fn(),
15
+ getPreset: vi.fn((name) => name === 'default' ? DEFAULT_CONFIG : (() => { throw new Error('Not found'); })()),
16
+ deletePreset: vi.fn((name) => { if (name === 'default')
17
+ throw new Error('Cannot delete default'); }),
18
+ previewMigration: vi.fn().mockReturnValue({ orphanedScopes: [], addedLists: [], removedLists: [], scopeMoves: [] }),
19
+ applyMigration: vi.fn().mockReturnValue({ orphanedScopes: [], addedLists: [], removedLists: [], scopeMoves: [] }),
20
+ getEngine: vi.fn().mockReturnValue(engine),
21
+ };
22
+ beforeAll(() => {
23
+ const router = createWorkflowRoutes({
24
+ workflowService: mockWorkflowService,
25
+ projectRoot: '/tmp/test-project',
26
+ });
27
+ app = express();
28
+ app.use(express.json());
29
+ app.use('/api/orbital', router);
30
+ });
31
+ describe('GET /workflow', () => {
32
+ it('returns active workflow config', async () => {
33
+ const res = await request(app).get('/api/orbital/workflow');
34
+ expect(res.status).toBe(200);
35
+ expect(res.body.success).toBe(true);
36
+ expect(res.body.data.name).toBe('Default Workflow');
37
+ });
38
+ });
39
+ describe('PUT /workflow', () => {
40
+ it('updates workflow config', async () => {
41
+ const res = await request(app)
42
+ .put('/api/orbital/workflow')
43
+ .send(MINIMAL_CONFIG);
44
+ expect(res.status).toBe(200);
45
+ });
46
+ it('returns 400 for invalid config', async () => {
47
+ mockWorkflowService.updateActive.mockReturnValueOnce({ valid: false, errors: ['Missing lists'], warnings: [] });
48
+ const res = await request(app)
49
+ .put('/api/orbital/workflow')
50
+ .send({ version: 1, name: 'Bad', lists: [], edges: [] });
51
+ expect(res.status).toBe(400);
52
+ });
53
+ });
54
+ describe('GET /workflow/presets', () => {
55
+ it('returns preset list', async () => {
56
+ const res = await request(app).get('/api/orbital/workflow/presets');
57
+ expect(res.status).toBe(200);
58
+ expect(res.body.success).toBe(true);
59
+ expect(Array.isArray(res.body.data)).toBe(true);
60
+ });
61
+ });
62
+ describe('POST /workflow/presets', () => {
63
+ it('saves preset with valid name', async () => {
64
+ const res = await request(app)
65
+ .post('/api/orbital/workflow/presets')
66
+ .send({ name: 'my-preset' });
67
+ expect(res.status).toBe(200);
68
+ expect(mockWorkflowService.savePreset).toHaveBeenCalledWith('my-preset');
69
+ });
70
+ it('rejects missing name (400)', async () => {
71
+ const res = await request(app)
72
+ .post('/api/orbital/workflow/presets')
73
+ .send({});
74
+ expect(res.status).toBe(400);
75
+ });
76
+ });
77
+ describe('DELETE /workflow/presets/:name', () => {
78
+ it('rejects deleting default preset', async () => {
79
+ const res = await request(app).delete('/api/orbital/workflow/presets/default');
80
+ expect(res.status).toBe(400);
81
+ });
82
+ });
83
+ describe('GET /workflow/hooks', () => {
84
+ it('returns hooks with edge mapping', async () => {
85
+ const res = await request(app).get('/api/orbital/workflow/hooks');
86
+ expect(res.status).toBe(200);
87
+ expect(res.body.success).toBe(true);
88
+ });
89
+ });
90
+ describe('GET /workflow/hooks/:id/source', () => {
91
+ it('rejects path traversal attempts', async () => {
92
+ // The engine has hooks with targets like '.claude/hooks/blocker-check.sh'
93
+ // Trying to traverse out should fail
94
+ const res = await request(app).get('/api/orbital/workflow/hooks/../../etc/passwd/source');
95
+ expect([400, 404]).toContain(res.status);
96
+ });
97
+ });
98
+ describe('POST /workflow/preview', () => {
99
+ it('returns migration preview', async () => {
100
+ const res = await request(app)
101
+ .post('/api/orbital/workflow/preview')
102
+ .send(MINIMAL_CONFIG);
103
+ expect(res.status).toBe(200);
104
+ expect(res.body.success).toBe(true);
105
+ });
106
+ });
107
+ });
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Config migration runner for orbital.config.json.
3
+ *
4
+ * Applies incremental, idempotent migrations when upgrading between
5
+ * package versions. Also fills schema defaults for new fields that
6
+ * don't require explicit migration logic.
7
+ */
8
+ import fs from 'fs';
9
+ // ─── Migration Registry ─────────────────────────────────────
10
+ /**
11
+ * All config migrations, ordered by target version.
12
+ *
13
+ * Rules:
14
+ * - Each migration MUST be idempotent (safe to run twice)
15
+ * - Each migration MUST check before applying (don't blindly overwrite)
16
+ * - ID format: "fromVersion->toVersion"
17
+ */
18
+ const MIGRATIONS = [
19
+ // Future migrations go here. Example:
20
+ // {
21
+ // id: '0.2.0->0.3.0',
22
+ // description: 'Add notifications config section',
23
+ // migrate: (config) => {
24
+ // if (!('notifications' in config)) {
25
+ // config.notifications = { enabled: false };
26
+ // }
27
+ // return config;
28
+ // },
29
+ // },
30
+ ];
31
+ // ─── Schema Defaults ────────────────────────────────────────
32
+ /** Default values from the schema, keyed by property path. */
33
+ const SCHEMA_DEFAULTS = {
34
+ projectName: 'My Project',
35
+ scopesDir: 'scopes',
36
+ eventsDir: '.claude/orbital-events',
37
+ dbDir: '.claude/orbital',
38
+ configDir: '.claude/config',
39
+ serverPort: 4444,
40
+ clientPort: 4445,
41
+ logLevel: 'info',
42
+ categories: ['feature', 'bugfix', 'refactor', 'infrastructure', 'docs'],
43
+ };
44
+ const NESTED_DEFAULTS = {
45
+ terminal: {
46
+ adapter: 'auto',
47
+ profilePrefix: 'Orbital',
48
+ },
49
+ claude: {
50
+ executable: 'claude',
51
+ flags: ['--dangerously-skip-permissions'],
52
+ },
53
+ commands: {
54
+ typeCheck: null,
55
+ lint: null,
56
+ build: null,
57
+ test: null,
58
+ },
59
+ };
60
+ /**
61
+ * Run all pending config migrations and fill schema defaults.
62
+ *
63
+ * @param configPath - Path to orbital.config.json
64
+ * @param appliedMigrations - IDs already recorded in the manifest
65
+ * @returns List of migration IDs that were applied
66
+ */
67
+ export function migrateConfig(configPath, appliedMigrations) {
68
+ const result = { applied: [], defaultsFilled: [], errors: [] };
69
+ if (!fs.existsSync(configPath))
70
+ return result;
71
+ let config;
72
+ try {
73
+ config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
74
+ }
75
+ catch {
76
+ result.errors.push('Failed to parse orbital.config.json');
77
+ return result;
78
+ }
79
+ const appliedSet = new Set(appliedMigrations);
80
+ // Apply explicit migrations in order
81
+ for (const migration of MIGRATIONS) {
82
+ if (appliedSet.has(migration.id))
83
+ continue;
84
+ try {
85
+ config = migration.migrate(config);
86
+ result.applied.push(migration.id);
87
+ }
88
+ catch (err) {
89
+ result.errors.push(`Migration ${migration.id} failed: ${String(err)}`);
90
+ }
91
+ }
92
+ // Fill schema defaults for missing top-level properties
93
+ for (const [key, defaultValue] of Object.entries(SCHEMA_DEFAULTS)) {
94
+ if (!(key in config)) {
95
+ config[key] = defaultValue;
96
+ result.defaultsFilled.push(key);
97
+ }
98
+ }
99
+ // Fill nested defaults
100
+ for (const [section, defaults] of Object.entries(NESTED_DEFAULTS)) {
101
+ if (!(section in config)) {
102
+ config[section] = { ...defaults };
103
+ result.defaultsFilled.push(section);
104
+ }
105
+ else if (typeof config[section] === 'object' && config[section] !== null) {
106
+ const sectionObj = config[section];
107
+ for (const [key, defaultValue] of Object.entries(defaults)) {
108
+ if (!(key in sectionObj)) {
109
+ sectionObj[key] = defaultValue;
110
+ result.defaultsFilled.push(`${section}.${key}`);
111
+ }
112
+ }
113
+ }
114
+ }
115
+ // Write back if any changes were made
116
+ if (result.applied.length > 0 || result.defaultsFilled.length > 0) {
117
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
118
+ }
119
+ return result;
120
+ }
121
+ /**
122
+ * Get all migration IDs that haven't been applied yet.
123
+ */
124
+ export function getPendingMigrations(appliedMigrations) {
125
+ const appliedSet = new Set(appliedMigrations);
126
+ return MIGRATIONS
127
+ .filter(m => !appliedSet.has(m.id))
128
+ .map(m => m.id);
129
+ }
130
+ /**
131
+ * Get all registered migrations (for display/debug).
132
+ */
133
+ export function getAllMigrations() {
134
+ return MIGRATIONS.map(m => ({ id: m.id, description: m.description }));
135
+ }
@@ -3,6 +3,8 @@ import fs from 'fs';
3
3
  import { execFileSync } from 'child_process';
4
4
  import os from 'os';
5
5
  import { createLogger } from './utils/logger.js';
6
+ import { DEFAULT_DISPATCH_FLAGS, DEFAULT_DISPATCH_CONFIG } from '../shared/api-types.js';
7
+ import { loadGlobalConfig as loadGlobal } from './global-config.js';
6
8
  // ─── Defaults ───────────────────────────────────────────────
7
9
  const DEFAULT_CONFIG = {
8
10
  projectName: 'Project',
@@ -19,22 +21,25 @@ const DEFAULT_CONFIG = {
19
21
  claude: {
20
22
  executable: 'claude',
21
23
  flags: ['--dangerously-skip-permissions'],
24
+ dispatchFlags: DEFAULT_DISPATCH_FLAGS,
22
25
  },
26
+ dispatch: DEFAULT_DISPATCH_CONFIG,
23
27
  commands: {
24
28
  typeCheck: null,
25
29
  lint: null,
26
30
  build: null,
27
31
  test: null,
28
- validateTemplates: null,
29
- validateDocs: null,
30
- checkRules: null,
31
32
  },
32
33
  logLevel: 'info',
34
+ telemetry: {
35
+ enabled: false,
36
+ url: '',
37
+ headers: {},
38
+ },
33
39
  categories: ['feature', 'bugfix', 'refactor', 'infrastructure', 'docs'],
34
40
  agents: [
35
41
  { id: 'attacker', label: 'Attacker', emoji: '\u{1F5E1}\u{FE0F}', color: '#ff1744' },
36
42
  { id: 'chaos', label: 'Chaos', emoji: '\u{1F4A5}', color: '#F97316' },
37
- { id: 'solana-expert', label: 'Solana Expert', emoji: '\u{26D3}\u{FE0F}', color: '#8B5CF6' },
38
43
  { id: 'frontend-designer', label: 'Frontend Designer', emoji: '\u{1F3A8}', color: '#EC4899' },
39
44
  { id: 'architect', label: 'Architect', emoji: '\u{1F3D7}\u{FE0F}', color: '#536dfe' },
40
45
  { id: 'rules-enforcer', label: 'Rules Enforcer', emoji: '\u{1F4CB}', color: '#6B7280' },
@@ -89,6 +94,15 @@ export function getClaudeSessionsDir(projectRoot) {
89
94
  */
90
95
  export function loadConfig(projectRoot) {
91
96
  const root = projectRoot ?? resolveProjectRoot();
97
+ // Try loading edition overrides (e.g. edition.json at repo root)
98
+ const editionPath = path.join(root, 'edition.json');
99
+ let editionConfig = {};
100
+ if (fs.existsSync(editionPath)) {
101
+ try {
102
+ editionConfig = JSON.parse(fs.readFileSync(editionPath, 'utf-8'));
103
+ }
104
+ catch { /* malformed edition.json — ignore */ }
105
+ }
92
106
  // Try loading user config
93
107
  const configPath = path.join(root, '.claude', 'orbital.config.json');
94
108
  let userConfig = {};
@@ -101,8 +115,11 @@ export function loadConfig(projectRoot) {
101
115
  log.warn('Failed to parse orbital.config.json — using defaults', { error: err.message });
102
116
  }
103
117
  }
104
- // Merge with defaults
105
- const projectName = userConfig.projectName ?? DEFAULT_CONFIG.projectName;
118
+ // Merge with defaults — derive project name from directory if not configured
119
+ const defaultProjectName = path.basename(root)
120
+ .replace(/[-_]+/g, ' ')
121
+ .replace(/\b\w/g, (c) => c.toUpperCase());
122
+ const projectName = userConfig.projectName ?? defaultProjectName;
106
123
  const scopesDir = path.resolve(root, userConfig.scopesDir ?? DEFAULT_CONFIG.scopesDir);
107
124
  const eventsDir = path.resolve(root, userConfig.eventsDir ?? DEFAULT_CONFIG.eventsDir);
108
125
  const dbDir = path.resolve(root, userConfig.dbDir ?? DEFAULT_CONFIG.dbDir);
@@ -113,10 +130,27 @@ export function loadConfig(projectRoot) {
113
130
  ...DEFAULT_CONFIG.terminal,
114
131
  ...(userConfig.terminal ?? {}),
115
132
  };
133
+ // Global settings — seed from ~/.orbital/config.json
134
+ let globalDispatchFlags = DEFAULT_DISPATCH_FLAGS;
135
+ let globalDispatch = DEFAULT_DISPATCH_CONFIG;
136
+ let globalTelemetry = {};
137
+ try {
138
+ const global = loadGlobal();
139
+ if (global.dispatchFlags)
140
+ globalDispatchFlags = { ...DEFAULT_DISPATCH_FLAGS, ...global.dispatchFlags };
141
+ if (global.dispatch)
142
+ globalDispatch = { ...DEFAULT_DISPATCH_CONFIG, ...global.dispatch };
143
+ if (global.telemetry)
144
+ globalTelemetry = global.telemetry;
145
+ }
146
+ catch { /* global config may not exist yet */ }
147
+ const userClaude = userConfig.claude ?? {};
116
148
  const claude = {
117
149
  ...DEFAULT_CONFIG.claude,
118
- ...(userConfig.claude ?? {}),
150
+ ...userClaude,
151
+ dispatchFlags: globalDispatchFlags,
119
152
  };
153
+ const dispatch = globalDispatch;
120
154
  const commands = {
121
155
  ...DEFAULT_CONFIG.commands,
122
156
  ...(userConfig.commands ?? {}),
@@ -124,6 +158,14 @@ export function loadConfig(projectRoot) {
124
158
  const logLevel = userConfig.logLevel ?? DEFAULT_CONFIG.logLevel;
125
159
  const categories = userConfig.categories ?? DEFAULT_CONFIG.categories;
126
160
  const agents = userConfig.agents ?? DEFAULT_CONFIG.agents;
161
+ const telemetry = {
162
+ ...DEFAULT_CONFIG.telemetry,
163
+ ...globalTelemetry,
164
+ ...(editionConfig.telemetry ?? {}),
165
+ ...(userConfig.telemetry ?? {}),
166
+ };
167
+ if (process.env.ORBITAL_TELEMETRY === 'false')
168
+ telemetry.enabled = false;
127
169
  return {
128
170
  projectName,
129
171
  projectRoot: root,
@@ -135,10 +177,12 @@ export function loadConfig(projectRoot) {
135
177
  clientPort,
136
178
  terminal,
137
179
  claude,
180
+ dispatch,
138
181
  commands,
139
182
  logLevel,
140
183
  categories,
141
184
  agents,
185
+ telemetry,
142
186
  };
143
187
  }
144
188
  // ─── Singleton ──────────────────────────────────────────────
@@ -2,30 +2,26 @@ import Database from 'better-sqlite3';
2
2
  import path from 'path';
3
3
  import fs from 'fs';
4
4
  import { SCHEMA_DDL } from './schema.js';
5
- import { getConfig } from './config.js';
6
5
  import { createLogger } from './utils/logger.js';
7
6
  const log = createLogger('database');
8
- function getDbPaths() {
9
- const config = getConfig();
10
- return { dir: config.dbDir, file: path.join(config.dbDir, 'orbital.db') };
11
- }
12
- let db = null;
13
- export function getDatabase() {
14
- if (db)
15
- return db;
16
- const { dir, file } = getDbPaths();
17
- fs.mkdirSync(dir, { recursive: true });
18
- db = new Database(file);
7
+ // ─── Factory (multi-project) ────────────────────────────────
8
+ /**
9
+ * Open a project-scoped SQLite database at the given directory.
10
+ * Creates the directory if needed, applies schema DDL and migrations.
11
+ *
12
+ * Each call returns a NEW connection — callers manage their own lifecycle.
13
+ */
14
+ export function openProjectDatabase(dbDir) {
15
+ fs.mkdirSync(dbDir, { recursive: true });
16
+ const file = path.join(dbDir, 'orbital.db');
17
+ const database = new Database(file);
19
18
  log.info('Database initialized', { path: file });
20
- // Performance pragmas for a local dev tool
21
- db.pragma('journal_mode = WAL');
22
- db.pragma('synchronous = NORMAL');
23
- db.pragma('foreign_keys = ON');
24
- // Run schema migrations (SQLite db.exec, not child_process)
25
- db.exec(SCHEMA_DDL);
26
- // Incremental migrations for existing databases
27
- runMigrations(db);
28
- return db;
19
+ database.pragma('journal_mode = WAL');
20
+ database.pragma('synchronous = NORMAL');
21
+ database.pragma('foreign_keys = ON');
22
+ database.exec(SCHEMA_DDL);
23
+ runMigrations(database);
24
+ return database;
29
25
  }
30
26
  /** Check if a table exists in the database */
31
27
  function tableExists(database, tableName) {
@@ -44,6 +40,10 @@ function runMigrations(database) {
44
40
  if (!sessionCols.some((c) => c.name === 'action')) {
45
41
  database.exec('ALTER TABLE sessions ADD COLUMN action TEXT');
46
42
  }
43
+ // Migration 9: Add telemetry_sent_at column to sessions
44
+ if (!sessionCols.some((c) => c.name === 'telemetry_sent_at')) {
45
+ database.exec('ALTER TABLE sessions ADD COLUMN telemetry_sent_at TEXT');
46
+ }
47
47
  // Migration 8: Add batch group columns to sprints
48
48
  const sprintCols = database.pragma('table_info(sprints)');
49
49
  if (!sprintCols.some((c) => c.name === 'target_column')) {
@@ -81,10 +81,3 @@ function runMigrations(database) {
81
81
  `);
82
82
  }
83
83
  }
84
- export function closeDatabase() {
85
- if (db) {
86
- db.close();
87
- db = null;
88
- log.debug('Database closed');
89
- }
90
- }