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,118 @@
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
+
6
+ describe('sprint-routes', () => {
7
+ let app: express.Express;
8
+
9
+ const mockSprintService = {
10
+ 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 } }),
11
+ getAll: vi.fn().mockReturnValue([]),
12
+ getById: vi.fn((id: number) => id === 1 ? { id: 1, name: 'Sprint 1', status: 'assembling', scope_ids: [], scopes: [] } : null),
13
+ rename: vi.fn().mockReturnValue(true),
14
+ delete: vi.fn().mockReturnValue(true),
15
+ addScopes: vi.fn().mockReturnValue({ added: [1, 2], unmet_dependencies: [] }),
16
+ removeScopes: vi.fn().mockReturnValue(true),
17
+ };
18
+
19
+ const mockSprintOrchestrator = {
20
+ startSprint: vi.fn().mockReturnValue({ ok: true, layers: [[1], [2]] }),
21
+ cancelSprint: vi.fn().mockReturnValue({ ok: true }),
22
+ getExecutionGraph: vi.fn().mockReturnValue({ nodes: [], edges: [] }),
23
+ };
24
+
25
+ const mockBatchOrchestrator = {
26
+ dispatch: vi.fn().mockReturnValue({ ok: true }),
27
+ };
28
+
29
+ beforeAll(() => {
30
+ const router = createSprintRoutes({
31
+ sprintService: mockSprintService as any,
32
+ sprintOrchestrator: mockSprintOrchestrator as any,
33
+ batchOrchestrator: mockBatchOrchestrator as any,
34
+ });
35
+
36
+ app = express();
37
+ app.use(express.json());
38
+ app.use('/api/orbital', router);
39
+ });
40
+
41
+ describe('POST /sprints', () => {
42
+ it('creates sprint with name (201)', async () => {
43
+ const res = await request(app)
44
+ .post('/api/orbital/sprints')
45
+ .send({ name: 'New Sprint' });
46
+ expect(res.status).toBe(201);
47
+ expect(mockSprintService.create).toHaveBeenCalledWith('New Sprint', expect.anything());
48
+ });
49
+
50
+ it('rejects empty name (400)', async () => {
51
+ const res = await request(app)
52
+ .post('/api/orbital/sprints')
53
+ .send({ name: ' ' });
54
+ expect(res.status).toBe(400);
55
+ });
56
+ });
57
+
58
+ describe('GET /sprints', () => {
59
+ it('returns sprint list', async () => {
60
+ const res = await request(app).get('/api/orbital/sprints');
61
+ expect(res.status).toBe(200);
62
+ expect(Array.isArray(res.body)).toBe(true);
63
+ });
64
+
65
+ it('passes status filter', async () => {
66
+ await request(app).get('/api/orbital/sprints?status=assembling');
67
+ expect(mockSprintService.getAll).toHaveBeenCalledWith('assembling', undefined);
68
+ });
69
+ });
70
+
71
+ describe('PATCH /sprints/:id', () => {
72
+ it('renames sprint', async () => {
73
+ const res = await request(app)
74
+ .patch('/api/orbital/sprints/1')
75
+ .send({ name: 'Renamed' });
76
+ expect(res.status).toBe(200);
77
+ });
78
+
79
+ it('rejects missing name (400)', async () => {
80
+ const res = await request(app)
81
+ .patch('/api/orbital/sprints/1')
82
+ .send({});
83
+ expect(res.status).toBe(400);
84
+ });
85
+ });
86
+
87
+ describe('DELETE /sprints/:id', () => {
88
+ it('deletes sprint', async () => {
89
+ const res = await request(app).delete('/api/orbital/sprints/1');
90
+ expect(res.status).toBe(200);
91
+ });
92
+ });
93
+
94
+ describe('POST /sprints/:id/scopes', () => {
95
+ it('adds scopes to sprint', async () => {
96
+ const res = await request(app)
97
+ .post('/api/orbital/sprints/1/scopes')
98
+ .send({ scope_ids: [1, 2] });
99
+ expect(res.status).toBe(200);
100
+ });
101
+
102
+ it('rejects empty scope_ids (400)', async () => {
103
+ const res = await request(app)
104
+ .post('/api/orbital/sprints/1/scopes')
105
+ .send({ scope_ids: [] });
106
+ expect(res.status).toBe(400);
107
+ });
108
+ });
109
+
110
+ describe('POST /sprints/:id/dispatch', () => {
111
+ it('dispatches sprint', async () => {
112
+ const res = await request(app)
113
+ .post('/api/orbital/sprints/1/dispatch')
114
+ .send({});
115
+ expect(res.status).toBe(200);
116
+ });
117
+ });
118
+ });
@@ -0,0 +1,120 @@
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
+
8
+ describe('workflow-routes', () => {
9
+ let app: express.Express;
10
+ const engine = new WorkflowEngine(DEFAULT_CONFIG);
11
+
12
+ const mockWorkflowService = {
13
+ getActive: vi.fn().mockReturnValue(DEFAULT_CONFIG),
14
+ updateActive: vi.fn().mockReturnValue({ valid: true, errors: [], warnings: [] }),
15
+ listPresets: vi.fn().mockReturnValue([{ name: 'default', createdAt: '2026-01-01', listCount: 7, edgeCount: 14 }]),
16
+ savePreset: vi.fn(),
17
+ getPreset: vi.fn((name: string) => name === 'default' ? DEFAULT_CONFIG : (() => { throw new Error('Not found'); })()),
18
+ deletePreset: vi.fn((name: string) => { if (name === 'default') throw new Error('Cannot delete default'); }),
19
+ previewMigration: vi.fn().mockReturnValue({ orphanedScopes: [], addedLists: [], removedLists: [], scopeMoves: [] }),
20
+ applyMigration: vi.fn().mockReturnValue({ orphanedScopes: [], addedLists: [], removedLists: [], scopeMoves: [] }),
21
+ getEngine: vi.fn().mockReturnValue(engine),
22
+ };
23
+
24
+ beforeAll(() => {
25
+ const router = createWorkflowRoutes({
26
+ workflowService: mockWorkflowService as any,
27
+ projectRoot: '/tmp/test-project',
28
+ });
29
+
30
+ app = express();
31
+ app.use(express.json());
32
+ app.use('/api/orbital', router);
33
+ });
34
+
35
+ describe('GET /workflow', () => {
36
+ it('returns active workflow config', async () => {
37
+ const res = await request(app).get('/api/orbital/workflow');
38
+ expect(res.status).toBe(200);
39
+ expect(res.body.success).toBe(true);
40
+ expect(res.body.data.name).toBe('Default Workflow');
41
+ });
42
+ });
43
+
44
+ describe('PUT /workflow', () => {
45
+ it('updates workflow config', async () => {
46
+ const res = await request(app)
47
+ .put('/api/orbital/workflow')
48
+ .send(MINIMAL_CONFIG);
49
+ expect(res.status).toBe(200);
50
+ });
51
+
52
+ it('returns 400 for invalid config', async () => {
53
+ mockWorkflowService.updateActive.mockReturnValueOnce({ valid: false, errors: ['Missing lists'], warnings: [] });
54
+ const res = await request(app)
55
+ .put('/api/orbital/workflow')
56
+ .send({ version: 1, name: 'Bad', lists: [], edges: [] });
57
+ expect(res.status).toBe(400);
58
+ });
59
+ });
60
+
61
+ describe('GET /workflow/presets', () => {
62
+ it('returns preset list', async () => {
63
+ const res = await request(app).get('/api/orbital/workflow/presets');
64
+ expect(res.status).toBe(200);
65
+ expect(res.body.success).toBe(true);
66
+ expect(Array.isArray(res.body.data)).toBe(true);
67
+ });
68
+ });
69
+
70
+ describe('POST /workflow/presets', () => {
71
+ it('saves preset with valid name', async () => {
72
+ const res = await request(app)
73
+ .post('/api/orbital/workflow/presets')
74
+ .send({ name: 'my-preset' });
75
+ expect(res.status).toBe(200);
76
+ expect(mockWorkflowService.savePreset).toHaveBeenCalledWith('my-preset');
77
+ });
78
+
79
+ it('rejects missing name (400)', async () => {
80
+ const res = await request(app)
81
+ .post('/api/orbital/workflow/presets')
82
+ .send({});
83
+ expect(res.status).toBe(400);
84
+ });
85
+ });
86
+
87
+ describe('DELETE /workflow/presets/:name', () => {
88
+ it('rejects deleting default preset', async () => {
89
+ const res = await request(app).delete('/api/orbital/workflow/presets/default');
90
+ expect(res.status).toBe(400);
91
+ });
92
+ });
93
+
94
+ describe('GET /workflow/hooks', () => {
95
+ it('returns hooks with edge mapping', async () => {
96
+ const res = await request(app).get('/api/orbital/workflow/hooks');
97
+ expect(res.status).toBe(200);
98
+ expect(res.body.success).toBe(true);
99
+ });
100
+ });
101
+
102
+ describe('GET /workflow/hooks/:id/source', () => {
103
+ it('rejects path traversal attempts', async () => {
104
+ // The engine has hooks with targets like '.claude/hooks/blocker-check.sh'
105
+ // Trying to traverse out should fail
106
+ const res = await request(app).get('/api/orbital/workflow/hooks/../../etc/passwd/source');
107
+ expect([400, 404]).toContain(res.status);
108
+ });
109
+ });
110
+
111
+ describe('POST /workflow/preview', () => {
112
+ it('returns migration preview', async () => {
113
+ const res = await request(app)
114
+ .post('/api/orbital/workflow/preview')
115
+ .send(MINIMAL_CONFIG);
116
+ expect(res.status).toBe(200);
117
+ expect(res.body.success).toBe(true);
118
+ });
119
+ });
120
+ });
@@ -0,0 +1,160 @@
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
+
9
+ import fs from 'fs';
10
+ import type { ConfigMigration } from './manifest-types.js';
11
+
12
+ // ─── Migration Registry ─────────────────────────────────────
13
+
14
+ /**
15
+ * All config migrations, ordered by target version.
16
+ *
17
+ * Rules:
18
+ * - Each migration MUST be idempotent (safe to run twice)
19
+ * - Each migration MUST check before applying (don't blindly overwrite)
20
+ * - ID format: "fromVersion->toVersion"
21
+ */
22
+ const MIGRATIONS: ConfigMigration[] = [
23
+ // Future migrations go here. Example:
24
+ // {
25
+ // id: '0.2.0->0.3.0',
26
+ // description: 'Add notifications config section',
27
+ // migrate: (config) => {
28
+ // if (!('notifications' in config)) {
29
+ // config.notifications = { enabled: false };
30
+ // }
31
+ // return config;
32
+ // },
33
+ // },
34
+ ];
35
+
36
+ // ─── Schema Defaults ────────────────────────────────────────
37
+
38
+ /** Default values from the schema, keyed by property path. */
39
+ const SCHEMA_DEFAULTS: Record<string, unknown> = {
40
+ projectName: 'My Project',
41
+ scopesDir: 'scopes',
42
+ eventsDir: '.claude/orbital-events',
43
+ dbDir: '.claude/orbital',
44
+ configDir: '.claude/config',
45
+ serverPort: 4444,
46
+ clientPort: 4445,
47
+ logLevel: 'info',
48
+ categories: ['feature', 'bugfix', 'refactor', 'infrastructure', 'docs'],
49
+ };
50
+
51
+ const NESTED_DEFAULTS: Record<string, Record<string, unknown>> = {
52
+ terminal: {
53
+ adapter: 'auto',
54
+ profilePrefix: 'Orbital',
55
+ },
56
+ claude: {
57
+ executable: 'claude',
58
+ flags: ['--dangerously-skip-permissions'],
59
+ },
60
+ commands: {
61
+ typeCheck: null,
62
+ lint: null,
63
+ build: null,
64
+ test: null,
65
+ },
66
+ };
67
+
68
+ // ─── Public API ─────────────────────────────────────────────
69
+
70
+ export interface MigrationResult {
71
+ applied: string[];
72
+ defaultsFilled: string[];
73
+ errors: string[];
74
+ }
75
+
76
+ /**
77
+ * Run all pending config migrations and fill schema defaults.
78
+ *
79
+ * @param configPath - Path to orbital.config.json
80
+ * @param appliedMigrations - IDs already recorded in the manifest
81
+ * @returns List of migration IDs that were applied
82
+ */
83
+ export function migrateConfig(
84
+ configPath: string,
85
+ appliedMigrations: string[],
86
+ ): MigrationResult {
87
+ const result: MigrationResult = { applied: [], defaultsFilled: [], errors: [] };
88
+
89
+ if (!fs.existsSync(configPath)) return result;
90
+
91
+ let config: Record<string, unknown>;
92
+ try {
93
+ config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
94
+ } catch {
95
+ result.errors.push('Failed to parse orbital.config.json');
96
+ return result;
97
+ }
98
+
99
+ const appliedSet = new Set(appliedMigrations);
100
+
101
+ // Apply explicit migrations in order
102
+ for (const migration of MIGRATIONS) {
103
+ if (appliedSet.has(migration.id)) continue;
104
+
105
+ try {
106
+ config = migration.migrate(config);
107
+ result.applied.push(migration.id);
108
+ } catch (err) {
109
+ result.errors.push(`Migration ${migration.id} failed: ${String(err)}`);
110
+ }
111
+ }
112
+
113
+ // Fill schema defaults for missing top-level properties
114
+ for (const [key, defaultValue] of Object.entries(SCHEMA_DEFAULTS)) {
115
+ if (!(key in config)) {
116
+ config[key] = defaultValue;
117
+ result.defaultsFilled.push(key);
118
+ }
119
+ }
120
+
121
+ // Fill nested defaults
122
+ for (const [section, defaults] of Object.entries(NESTED_DEFAULTS)) {
123
+ if (!(section in config)) {
124
+ config[section] = { ...defaults };
125
+ result.defaultsFilled.push(section);
126
+ } else if (typeof config[section] === 'object' && config[section] !== null) {
127
+ const sectionObj = config[section] as Record<string, unknown>;
128
+ for (const [key, defaultValue] of Object.entries(defaults)) {
129
+ if (!(key in sectionObj)) {
130
+ sectionObj[key] = defaultValue;
131
+ result.defaultsFilled.push(`${section}.${key}`);
132
+ }
133
+ }
134
+ }
135
+ }
136
+
137
+ // Write back if any changes were made
138
+ if (result.applied.length > 0 || result.defaultsFilled.length > 0) {
139
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
140
+ }
141
+
142
+ return result;
143
+ }
144
+
145
+ /**
146
+ * Get all migration IDs that haven't been applied yet.
147
+ */
148
+ export function getPendingMigrations(appliedMigrations: string[]): string[] {
149
+ const appliedSet = new Set(appliedMigrations);
150
+ return MIGRATIONS
151
+ .filter(m => !appliedSet.has(m.id))
152
+ .map(m => m.id);
153
+ }
154
+
155
+ /**
156
+ * Get all registered migrations (for display/debug).
157
+ */
158
+ export function getAllMigrations(): Array<{ id: string; description: string }> {
159
+ return MIGRATIONS.map(m => ({ id: m.id, description: m.description }));
160
+ }
package/server/config.ts CHANGED
@@ -14,6 +14,7 @@ export interface TerminalConfig {
14
14
  export interface ClaudeConfig {
15
15
  executable: string;
16
16
  flags: string[];
17
+ dispatchFlags: DispatchFlags;
17
18
  }
18
19
 
19
20
  export interface CommandsConfig {
@@ -21,13 +22,18 @@ export interface CommandsConfig {
21
22
  lint: string | null;
22
23
  build: string | null;
23
24
  test: string | null;
24
- validateTemplates: string | null;
25
- validateDocs: string | null;
26
- checkRules: string | null;
27
25
  }
28
26
 
29
- export type { AgentConfig } from '../shared/api-types.js';
30
- import type { AgentConfig } from '../shared/api-types.js';
27
+ export type { AgentConfig, DispatchFlags, DispatchConfig } from '../shared/api-types.js';
28
+ import type { AgentConfig, DispatchFlags, DispatchConfig } from '../shared/api-types.js';
29
+ import { DEFAULT_DISPATCH_FLAGS, DEFAULT_DISPATCH_CONFIG } from '../shared/api-types.js';
30
+ import { loadGlobalConfig as loadGlobal } from './global-config.js';
31
+
32
+ export interface TelemetryConfig {
33
+ enabled: boolean;
34
+ url: string;
35
+ headers: Record<string, string>;
36
+ }
31
37
 
32
38
  export interface OrbitalConfig {
33
39
  projectName: string;
@@ -49,6 +55,9 @@ export interface OrbitalConfig {
49
55
  // Claude Code CLI
50
56
  claude: ClaudeConfig;
51
57
 
58
+ // Dispatch operational settings
59
+ dispatch: DispatchConfig;
60
+
52
61
  // Build/test commands
53
62
  commands: CommandsConfig;
54
63
 
@@ -58,6 +67,9 @@ export interface OrbitalConfig {
58
67
  // Dynamic configuration
59
68
  categories: string[];
60
69
  agents: AgentConfig[];
70
+
71
+ // Telemetry
72
+ telemetry: TelemetryConfig;
61
73
  }
62
74
 
63
75
  // ─── Defaults ───────────────────────────────────────────────
@@ -77,22 +89,25 @@ const DEFAULT_CONFIG: Omit<OrbitalConfig, 'projectRoot'> = {
77
89
  claude: {
78
90
  executable: 'claude',
79
91
  flags: ['--dangerously-skip-permissions'],
92
+ dispatchFlags: DEFAULT_DISPATCH_FLAGS,
80
93
  },
94
+ dispatch: DEFAULT_DISPATCH_CONFIG,
81
95
  commands: {
82
96
  typeCheck: null,
83
97
  lint: null,
84
98
  build: null,
85
99
  test: null,
86
- validateTemplates: null,
87
- validateDocs: null,
88
- checkRules: null,
89
100
  },
90
101
  logLevel: 'info' as const,
102
+ telemetry: {
103
+ enabled: false,
104
+ url: '',
105
+ headers: {},
106
+ },
91
107
  categories: ['feature', 'bugfix', 'refactor', 'infrastructure', 'docs'],
92
108
  agents: [
93
109
  { id: 'attacker', label: 'Attacker', emoji: '\u{1F5E1}\u{FE0F}', color: '#ff1744' },
94
110
  { id: 'chaos', label: 'Chaos', emoji: '\u{1F4A5}', color: '#F97316' },
95
- { id: 'solana-expert', label: 'Solana Expert', emoji: '\u{26D3}\u{FE0F}', color: '#8B5CF6' },
96
111
  { id: 'frontend-designer', label: 'Frontend Designer', emoji: '\u{1F3A8}', color: '#EC4899' },
97
112
  { id: 'architect', label: 'Architect', emoji: '\u{1F3D7}\u{FE0F}', color: '#536dfe' },
98
113
  { id: 'rules-enforcer', label: 'Rules Enforcer', emoji: '\u{1F4CB}', color: '#6B7280' },
@@ -154,6 +169,15 @@ export function getClaudeSessionsDir(projectRoot: string): string {
154
169
  export function loadConfig(projectRoot?: string): OrbitalConfig {
155
170
  const root = projectRoot ?? resolveProjectRoot();
156
171
 
172
+ // Try loading edition overrides (e.g. edition.json at repo root)
173
+ const editionPath = path.join(root, 'edition.json');
174
+ let editionConfig: Record<string, unknown> = {};
175
+ if (fs.existsSync(editionPath)) {
176
+ try {
177
+ editionConfig = JSON.parse(fs.readFileSync(editionPath, 'utf-8'));
178
+ } catch { /* malformed edition.json — ignore */ }
179
+ }
180
+
157
181
  // Try loading user config
158
182
  const configPath = path.join(root, '.claude', 'orbital.config.json');
159
183
  let userConfig: Record<string, unknown> = {};
@@ -168,8 +192,11 @@ export function loadConfig(projectRoot?: string): OrbitalConfig {
168
192
  }
169
193
  }
170
194
 
171
- // Merge with defaults
172
- const projectName = (userConfig.projectName as string) ?? DEFAULT_CONFIG.projectName;
195
+ // Merge with defaults — derive project name from directory if not configured
196
+ const defaultProjectName = path.basename(root)
197
+ .replace(/[-_]+/g, ' ')
198
+ .replace(/\b\w/g, (c) => c.toUpperCase());
199
+ const projectName = (userConfig.projectName as string) ?? defaultProjectName;
173
200
 
174
201
  const scopesDir = path.resolve(root, (userConfig.scopesDir as string) ?? DEFAULT_CONFIG.scopesDir);
175
202
  const eventsDir = path.resolve(root, (userConfig.eventsDir as string) ?? DEFAULT_CONFIG.eventsDir);
@@ -184,11 +211,26 @@ export function loadConfig(projectRoot?: string): OrbitalConfig {
184
211
  ...(userConfig.terminal as Partial<TerminalConfig> ?? {}),
185
212
  };
186
213
 
214
+ // Global settings — seed from ~/.orbital/config.json
215
+ let globalDispatchFlags = DEFAULT_DISPATCH_FLAGS;
216
+ let globalDispatch = DEFAULT_DISPATCH_CONFIG;
217
+ let globalTelemetry: Partial<TelemetryConfig> = {};
218
+ try {
219
+ const global = loadGlobal();
220
+ if (global.dispatchFlags) globalDispatchFlags = { ...DEFAULT_DISPATCH_FLAGS, ...global.dispatchFlags };
221
+ if (global.dispatch) globalDispatch = { ...DEFAULT_DISPATCH_CONFIG, ...global.dispatch };
222
+ if (global.telemetry) globalTelemetry = global.telemetry;
223
+ } catch { /* global config may not exist yet */ }
224
+
225
+ const userClaude = (userConfig.claude as Partial<ClaudeConfig>) ?? {};
187
226
  const claude: ClaudeConfig = {
188
227
  ...DEFAULT_CONFIG.claude,
189
- ...(userConfig.claude as Partial<ClaudeConfig> ?? {}),
228
+ ...userClaude,
229
+ dispatchFlags: globalDispatchFlags,
190
230
  };
191
231
 
232
+ const dispatch: DispatchConfig = globalDispatch;
233
+
192
234
  const commands: CommandsConfig = {
193
235
  ...DEFAULT_CONFIG.commands,
194
236
  ...(userConfig.commands as Partial<CommandsConfig> ?? {}),
@@ -198,6 +240,14 @@ export function loadConfig(projectRoot?: string): OrbitalConfig {
198
240
  const categories = (userConfig.categories as string[]) ?? DEFAULT_CONFIG.categories;
199
241
  const agents = (userConfig.agents as AgentConfig[]) ?? DEFAULT_CONFIG.agents;
200
242
 
243
+ const telemetry: TelemetryConfig = {
244
+ ...DEFAULT_CONFIG.telemetry,
245
+ ...globalTelemetry,
246
+ ...(editionConfig.telemetry as Partial<TelemetryConfig> ?? {}),
247
+ ...(userConfig.telemetry as Partial<TelemetryConfig> ?? {}),
248
+ };
249
+ if (process.env.ORBITAL_TELEMETRY === 'false') telemetry.enabled = false;
250
+
201
251
  return {
202
252
  projectName,
203
253
  projectRoot: root,
@@ -209,10 +259,12 @@ export function loadConfig(projectRoot?: string): OrbitalConfig {
209
259
  clientPort,
210
260
  terminal,
211
261
  claude,
262
+ dispatch,
212
263
  commands,
213
264
  logLevel,
214
265
  categories,
215
266
  agents,
267
+ telemetry,
216
268
  };
217
269
  }
218
270
 
@@ -2,39 +2,33 @@ 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
 
8
7
  const log = createLogger('database');
9
8
 
10
- function getDbPaths(): { dir: string; file: string } {
11
- const config = getConfig();
12
- return { dir: config.dbDir, file: path.join(config.dbDir, 'orbital.db') };
13
- }
14
-
15
- let db: Database.Database | null = null;
16
-
17
- export function getDatabase(): Database.Database {
18
- if (db) return db;
9
+ // ─── Factory (multi-project) ────────────────────────────────
19
10
 
20
- const { dir, file } = getDbPaths();
21
- fs.mkdirSync(dir, { recursive: true });
11
+ /**
12
+ * Open a project-scoped SQLite database at the given directory.
13
+ * Creates the directory if needed, applies schema DDL and migrations.
14
+ *
15
+ * Each call returns a NEW connection — callers manage their own lifecycle.
16
+ */
17
+ export function openProjectDatabase(dbDir: string): Database.Database {
18
+ fs.mkdirSync(dbDir, { recursive: true });
19
+ const file = path.join(dbDir, 'orbital.db');
22
20
 
23
- db = new Database(file);
21
+ const database = new Database(file);
24
22
  log.info('Database initialized', { path: file });
25
23
 
26
- // Performance pragmas for a local dev tool
27
- db.pragma('journal_mode = WAL');
28
- db.pragma('synchronous = NORMAL');
29
- db.pragma('foreign_keys = ON');
24
+ database.pragma('journal_mode = WAL');
25
+ database.pragma('synchronous = NORMAL');
26
+ database.pragma('foreign_keys = ON');
30
27
 
31
- // Run schema migrations (SQLite db.exec, not child_process)
32
- db.exec(SCHEMA_DDL);
28
+ database.exec(SCHEMA_DDL);
29
+ runMigrations(database);
33
30
 
34
- // Incremental migrations for existing databases
35
- runMigrations(db);
36
-
37
- return db;
31
+ return database;
38
32
  }
39
33
 
40
34
  /** Check if a table exists in the database */
@@ -59,6 +53,11 @@ function runMigrations(database: Database.Database): void {
59
53
  database.exec('ALTER TABLE sessions ADD COLUMN action TEXT');
60
54
  }
61
55
 
56
+ // Migration 9: Add telemetry_sent_at column to sessions
57
+ if (!sessionCols.some((c) => c.name === 'telemetry_sent_at')) {
58
+ database.exec('ALTER TABLE sessions ADD COLUMN telemetry_sent_at TEXT');
59
+ }
60
+
62
61
  // Migration 8: Add batch group columns to sprints
63
62
  const sprintCols = database.pragma('table_info(sprints)') as Array<{ name: string }>;
64
63
  if (!sprintCols.some((c) => c.name === 'target_column')) {
@@ -97,11 +96,3 @@ function runMigrations(database: Database.Database): void {
97
96
  `);
98
97
  }
99
98
  }
100
-
101
- export function closeDatabase(): void {
102
- if (db) {
103
- db.close();
104
- db = null;
105
- log.debug('Database closed');
106
- }
107
- }