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
package/server/index.ts CHANGED
@@ -4,91 +4,61 @@ import { Server } from 'socket.io';
4
4
  import path from 'path';
5
5
  import fs from 'fs';
6
6
  import { fileURLToPath } from 'url';
7
- import { getDatabase, closeDatabase } from './database.js';
8
- import { getConfig, resetConfig } from './config.js';
9
- import { ScopeCache } from './services/scope-cache.js';
10
- import { ScopeService } from './services/scope-service.js';
11
- import { EventService } from './services/event-service.js';
12
- import { GateService } from './services/gate-service.js';
13
- import { DeployService } from './services/deploy-service.js';
14
- import { SprintService } from './services/sprint-service.js';
15
- import { SprintOrchestrator } from './services/sprint-orchestrator.js';
16
- import { BatchOrchestrator } from './services/batch-orchestrator.js';
17
- import { ReadinessService } from './services/readiness-service.js';
18
- import { startScopeWatcher } from './watchers/scope-watcher.js';
19
- import { startEventWatcher } from './watchers/event-watcher.js';
20
- import { ensureDynamicProfiles } from './utils/terminal-launcher.js';
21
- import { syncClaudeSessionsToDB } from './services/claude-session-service.js';
22
- import { resolveStaleDispatches, resolveActiveDispatchesForScope, resolveDispatchesByPid, resolveDispatchesByDispatchId, linkPidToDispatch } from './utils/dispatch-utils.js';
23
- import { createScopeRoutes } from './routes/scope-routes.js';
24
- import { createDataRoutes } from './routes/data-routes.js';
25
- import { createDispatchRoutes } from './routes/dispatch-routes.js';
26
- import { createSprintRoutes } from './routes/sprint-routes.js';
27
- import { createWorkflowRoutes } from './routes/workflow-routes.js';
28
- import { createConfigRoutes } from './routes/config-routes.js';
29
- import { createGitRoutes } from './routes/git-routes.js';
30
7
  import { createVersionRoutes } from './routes/version-routes.js';
31
- import { WorkflowService } from './services/workflow-service.js';
32
- import { GitService } from './services/git-service.js';
33
- import { GitHubService } from './services/github-service.js';
34
- import { WorkflowEngine } from '../shared/workflow-engine.js';
35
- import defaultWorkflow from '../shared/default-workflow.json' with { type: 'json' };
36
- import type { WorkflowConfig } from '../shared/workflow-config.js';
8
+ import { createAggregateRoutes } from './routes/aggregate-routes.js';
37
9
  import { createLogger, setLogLevel } from './utils/logger.js';
38
10
  import type { LogLevel } from './utils/logger.js';
39
11
 
40
12
  import type http from 'http';
41
- import type Database from 'better-sqlite3';
42
13
 
43
- // ─── Types ──────────────────────────────────────────────────
44
-
45
- export interface ServerOverrides {
14
+ // ─── Central Server ─────────────────────────────────────────
15
+
16
+ import { ProjectManager } from './project-manager.js';
17
+ import { SyncService } from './services/sync-service.js';
18
+ import { startGlobalWatcher } from './watchers/global-watcher.js';
19
+ import { createSyncRoutes } from './routes/sync-routes.js';
20
+ import { seedGlobalPrimitives } from './init.js';
21
+ import {
22
+ ensureOrbitalHome,
23
+ loadGlobalConfig,
24
+ registerProject as registerProjectGlobal,
25
+ GLOBAL_PRIMITIVES_DIR,
26
+ ORBITAL_HOME,
27
+ } from './global-config.js';
28
+
29
+ export interface CentralServerOverrides {
46
30
  port?: number;
47
- projectRoot?: string;
31
+ clientPort?: number;
32
+ /** If set, auto-register this project on first launch */
33
+ autoRegisterPath?: string;
48
34
  }
49
35
 
50
- export interface ServerInstance {
36
+ export interface CentralServerInstance {
51
37
  app: express.Application;
52
38
  io: Server;
53
- db: Database.Database;
54
- workflowEngine: WorkflowEngine;
39
+ projectManager: ProjectManager;
40
+ syncService: SyncService;
55
41
  httpServer: http.Server;
56
42
  shutdown: () => Promise<void>;
57
43
  }
58
44
 
59
- // ─── Server Factory ─────────────────────────────────────────
60
-
61
- export async function startServer(overrides?: ServerOverrides): Promise<ServerInstance> {
62
- // Apply project root override before config loads
63
- if (overrides?.projectRoot) {
64
- process.env.ORBITAL_PROJECT_ROOT = overrides.projectRoot;
65
- resetConfig();
66
- }
45
+ export async function startCentralServer(overrides?: CentralServerOverrides): Promise<CentralServerInstance> {
46
+ ensureOrbitalHome();
67
47
 
68
- const config = getConfig();
69
48
  const envLevel = process.env.ORBITAL_LOG_LEVEL;
70
49
  if (envLevel && ['debug', 'info', 'warn', 'error'].includes(envLevel)) {
71
50
  setLogLevel(envLevel as LogLevel);
72
- } else {
73
- setLogLevel(config.logLevel);
74
51
  }
75
- const log = createLogger('server');
76
- const port = overrides?.port ?? config.serverPort;
77
-
78
- const workflowEngine = new WorkflowEngine(defaultWorkflow as WorkflowConfig);
79
-
80
- // Generate shell manifest for bash hooks (config-driven lifecycle)
81
- const MANIFEST_PATH = path.join(config.configDir, 'workflow-manifest.sh');
82
- if (!fs.existsSync(config.configDir)) fs.mkdirSync(config.configDir, { recursive: true });
83
- fs.writeFileSync(MANIFEST_PATH, workflowEngine.generateShellManifest(), 'utf-8');
84
-
85
- const ICEBOX_DIR = path.join(config.scopesDir, 'icebox');
86
- // Resolve path to the bundled default workflow config.
87
- const __selfDir2 = path.dirname(fileURLToPath(import.meta.url));
88
- const DEFAULT_CONFIG_PATH = path.resolve(__selfDir2, '../shared/default-workflow.json');
89
-
90
- // Ensure icebox directory exists for idea files
91
- if (!fs.existsSync(ICEBOX_DIR)) fs.mkdirSync(ICEBOX_DIR, { recursive: true });
52
+ const log = createLogger('central');
53
+ const port = overrides?.port ?? (Number(process.env.ORBITAL_SERVER_PORT) || 4444);
54
+ const clientPort = overrides?.clientPort ?? (Number(process.env.ORBITAL_CLIENT_PORT) || 4445);
55
+
56
+ // Auto-register current project if registry is empty
57
+ const globalConfig = loadGlobalConfig();
58
+ if (globalConfig.projects.length === 0 && overrides?.autoRegisterPath) {
59
+ registerProjectGlobal(overrides.autoRegisterPath);
60
+ log.info('Auto-registered current project', { path: overrides.autoRegisterPath });
61
+ }
92
62
 
93
63
  const app = express();
94
64
  const httpServer = createServer(app);
@@ -96,7 +66,6 @@ export async function startServer(overrides?: ServerOverrides): Promise<ServerIn
96
66
  const io = new Server(httpServer, {
97
67
  cors: {
98
68
  origin: (origin, callback) => {
99
- // Allow all localhost origins (dev tool, not production)
100
69
  if (!origin || origin.startsWith('http://localhost:')) {
101
70
  callback(null, true);
102
71
  } else {
@@ -107,324 +76,174 @@ export async function startServer(overrides?: ServerOverrides): Promise<ServerIn
107
76
  },
108
77
  });
109
78
 
110
- // Middleware
111
79
  app.use(express.json());
112
80
 
113
- // Initialize database
114
- const db = getDatabase();
115
-
116
- // Initialize services
117
- const scopeCache = new ScopeCache();
118
- const scopeService = new ScopeService(scopeCache, io, config.scopesDir, workflowEngine);
119
- const eventService = new EventService(db, io);
120
- const gateService = new GateService(db, io);
121
- const deployService = new DeployService(db, io);
122
- const sprintService = new SprintService(db, io, scopeService);
123
- const sprintOrchestrator = new SprintOrchestrator(db, io, sprintService, scopeService, workflowEngine);
124
- const batchOrchestrator = new BatchOrchestrator(db, io, sprintService, scopeService, workflowEngine);
125
- const readinessService = new ReadinessService(scopeService, gateService, workflowEngine, config.projectRoot);
126
- const workflowService = new WorkflowService(config.configDir, workflowEngine, config.scopesDir, DEFAULT_CONFIG_PATH);
127
- workflowService.setSocketServer(io);
128
-
129
- // Ensure in-memory engine reflects the actual active config (may differ from bundled default
130
- // if the user applied a custom preset)
131
- workflowEngine.reload(workflowService.getActive());
132
- const gitService = new GitService(config.projectRoot, scopeCache);
133
- const githubService = new GitHubService(config.projectRoot);
134
-
135
- // Wire active-group guard into scope service (blocks manual moves for scopes in active batches/sprints)
136
- scopeService.setActiveGroupCheck((scopeId) => sprintService.getActiveGroupForScope(scopeId));
137
-
138
- // ─── Event Wiring ──────────────────────────────────────────
139
-
140
- function inferScopeStatus(
141
- eventType: string,
142
- scopeId: unknown,
143
- data: Record<string, unknown>
144
- ): void {
145
- if (scopeId == null) return;
146
- const id = Number(scopeId);
147
- if (isNaN(id) || id <= 0) return;
148
-
149
- // Don't infer status for icebox idea cards
150
- const current = scopeService.getById(id);
151
- if (current?.status === 'icebox') return;
152
-
153
- const currentStatus = current?.status ?? '';
154
- const result = workflowEngine.inferStatus(eventType, currentStatus, data);
155
- if (result === null) return;
156
-
157
- // Handle dispatch resolution (AGENT_COMPLETED with outcome)
158
- if (typeof result === 'object' && 'dispatchResolution' in result) {
159
- resolveActiveDispatchesForScope(
160
- db, io, id,
161
- result.resolution as 'completed' | 'failed',
162
- );
163
- return;
164
- }
165
-
166
- scopeService.updateStatus(id, result, 'event');
167
- }
168
-
169
- eventService.onIngest((eventType, scopeId, data) => {
170
- // Handle SESSION_START: link PID to dispatch via dispatch_id env var
171
- if (eventType === 'SESSION_START' && typeof data.dispatch_id === 'string' && typeof data.pid === 'number') {
172
- linkPidToDispatch(db, data.dispatch_id, data.pid);
173
- log.info('SESSION_START: linked PID to dispatch', { pid: data.pid, dispatch_id: data.dispatch_id });
174
- return;
175
- }
81
+ // ─── Bind port early ──────────────────────────────────────
82
+ // Listen before async init so Vite's proxy doesn't get ECONNREFUSED
83
+ const actualPort = await new Promise<number>((resolve, reject) => {
84
+ let attempt = 0;
85
+ const maxAttempts = 10;
176
86
 
177
- // Handle SESSION_END: resolve dispatches by dispatch_id (preferred) or PID (fallback)
178
- if (eventType === 'SESSION_END') {
179
- let count = 0;
180
- if (typeof data.dispatch_id === 'string') {
181
- count = resolveDispatchesByDispatchId(db, io, data.dispatch_id);
182
- if (count > 0) {
183
- log.info('SESSION_END: resolved dispatches', { count, dispatch_id: data.dispatch_id });
184
- }
185
- }
186
- // PID fallback for old hooks without dispatch_id
187
- if (count === 0 && typeof data.pid === 'number') {
188
- count = resolveDispatchesByPid(db, io, data.pid);
189
- if (count > 0) {
190
- log.info('SESSION_END: resolved dispatches by PID fallback', { count, pid: data.pid });
191
- }
192
- }
193
- // Immediately resolve any batches/sprints whose session just ended,
194
- // rather than waiting for the next stale-check interval
195
- if (count > 0) {
196
- batchOrchestrator.resolveStaleBatches();
87
+ httpServer.on('error', (err: NodeJS.ErrnoException) => {
88
+ if (err.code === 'EADDRINUSE' && attempt < maxAttempts) {
89
+ attempt++;
90
+ httpServer.listen(port + attempt);
91
+ } else {
92
+ reject(new Error(`Failed to start server: ${err.message}`));
197
93
  }
198
- return;
199
- }
94
+ });
200
95
 
201
- inferScopeStatus(eventType, scopeId, data);
202
- });
96
+ httpServer.on('listening', () => {
97
+ const addr = httpServer.address();
98
+ resolve(typeof addr === 'object' && addr ? addr.port : port);
99
+ });
203
100
 
204
- scopeService.onStatusChange((scopeId, newStatus) => {
205
- if (newStatus === 'dev') {
206
- sprintOrchestrator.onScopeReachedDev(scopeId);
207
- }
208
- // Batch orchestrator tracks all status transitions (dev, staging, production)
209
- batchOrchestrator.onScopeStatusChanged(scopeId, newStatus);
101
+ httpServer.listen(port);
210
102
  });
211
103
 
212
- scopeService.onStatusChange((scopeId, newStatus) => {
213
- if (workflowEngine.isTerminalStatus(newStatus)) {
214
- resolveActiveDispatchesForScope(db, io, scopeId, 'completed');
215
- }
104
+ // Initialize ProjectManager and boot all registered projects
105
+ const projectManager = new ProjectManager(io);
106
+ await projectManager.initializeAll();
107
+
108
+ // Seed global primitives if empty (lazy fallback for first launch)
109
+ const globalPrimitivesEmpty = ['agents', 'skills', 'hooks'].every(t => {
110
+ const dir = path.join(GLOBAL_PRIMITIVES_DIR, t);
111
+ return !fs.existsSync(dir) || fs.readdirSync(dir).filter(f => !f.startsWith('.')).length === 0;
216
112
  });
113
+ if (globalPrimitivesEmpty) {
114
+ seedGlobalPrimitives();
115
+ log.info('Seeded global primitives from package templates');
116
+ }
117
+
118
+ // Initialize SyncService and global watcher
119
+ const syncService = new SyncService();
120
+ const globalWatcher = startGlobalWatcher(syncService, io);
217
121
 
218
- // ─── Routes ────────────────────────────────────────────────
122
+ // ─── Routes ──────────────────────────────────────────────
219
123
 
124
+ // Health check
220
125
  app.get('/api/orbital/health', (_req, res) => {
221
126
  res.json({ status: 'ok', uptime: process.uptime(), timestamp: new Date().toISOString() });
222
127
  });
223
128
 
224
- // Serve dynamic config to the frontend
225
- app.get('/api/orbital/config', (_req, res) => {
226
- res.json({
227
- projectName: config.projectName,
228
- categories: config.categories,
229
- agents: config.agents,
230
- serverPort: config.serverPort,
231
- clientPort: config.clientPort,
232
- });
129
+ // Project management + sync routes (top-level)
130
+ app.use('/api/orbital', createSyncRoutes({ syncService, projectManager }));
131
+ app.use('/api/orbital', createVersionRoutes({ io }));
132
+
133
+ // Per-project routes — dynamic middleware that resolves :projectId
134
+ app.use('/api/orbital/projects/:projectId', (req, res, next) => {
135
+ const projectId = req.params.projectId;
136
+ const router = projectManager.getRouter(projectId);
137
+ if (!router) {
138
+ const ctx = projectManager.getContext(projectId);
139
+ if (!ctx) return res.status(404).json({ error: `Project '${projectId}' not found` });
140
+ return res.status(503).json({ error: `Project '${projectId}' is offline` });
141
+ }
142
+ router(req, res, next);
233
143
  });
234
144
 
235
- app.use('/api/orbital', createScopeRoutes({ db, io, scopeService, readinessService, projectRoot: config.projectRoot, engine: workflowEngine }));
236
- app.use('/api/orbital', createDataRoutes({ db, io, gateService, deployService, engine: workflowEngine, projectRoot: config.projectRoot, inferScopeStatus }));
237
- app.use('/api/orbital', createDispatchRoutes({ db, io, scopeService, projectRoot: config.projectRoot, engine: workflowEngine }));
238
- app.use('/api/orbital', createSprintRoutes({ sprintService, sprintOrchestrator, batchOrchestrator }));
239
- app.use('/api/orbital', createWorkflowRoutes({ workflowService, projectRoot: config.projectRoot }));
240
- app.use('/api/orbital', createConfigRoutes({ projectRoot: config.projectRoot, workflowService, io }));
241
- app.use('/api/orbital', createGitRoutes({ gitService, githubService, engine: workflowEngine }));
242
- app.use('/api/orbital', createVersionRoutes({ io }));
145
+ // Aggregate endpoints (cross-project)
146
+ app.use('/api/orbital', createAggregateRoutes({ projectManager, io, syncService }));
243
147
 
244
- // ─── Static File Serving (production) ───────────────────────
148
+ // ─── Static File Serving ─────────────────────────────────
245
149
 
246
- // Resolve the Vite-built frontend dist directory (server/ → ../dist).
247
150
  const __selfDir = path.dirname(fileURLToPath(import.meta.url));
248
151
  const distDir = path.resolve(__selfDir, '../dist');
249
- if (fs.existsSync(path.join(distDir, 'index.html'))) {
152
+ const devMode = clientPort !== port;
153
+ const hasBuiltFrontend = !devMode && fs.existsSync(path.join(distDir, 'index.html'));
154
+ if (hasBuiltFrontend) {
250
155
  app.use(express.static(distDir));
251
156
  app.get('*', (req, res, next) => {
252
157
  if (req.path.startsWith('/api/') || req.path.startsWith('/socket.io')) return next();
253
158
  res.sendFile(path.join(distDir, 'index.html'));
254
159
  });
255
160
  } else {
256
- // Dev mode: redirect root to Vite dev server
257
- app.get('/', (_req, res) => res.redirect(`http://localhost:${config.clientPort}`));
161
+ app.get('/', (_req, res) => res.redirect(`http://localhost:${clientPort}`));
258
162
  }
259
163
 
260
- // ─── Socket.io ──────────────────────────────────────────────
164
+ // ─── Socket.io ───────────────────────────────────────────
261
165
 
262
166
  io.on('connection', (socket) => {
263
167
  log.debug('Client connected', { socketId: socket.id });
264
168
 
265
- socket.on('disconnect', () => {
266
- log.debug('Client disconnected', { socketId: socket.id });
169
+ socket.on('subscribe', (payload: { projectId?: string; scope?: string }) => {
170
+ if (payload.scope === 'all') {
171
+ socket.join('all-projects');
172
+ } else if (payload.projectId) {
173
+ socket.join(`project:${payload.projectId}`);
174
+ }
267
175
  });
268
- });
269
-
270
- // ─── Startup ───────────────────────────────────────────────
271
-
272
- // References for graceful shutdown
273
- let scopeWatcher: ReturnType<typeof startScopeWatcher>;
274
- let eventWatcher: ReturnType<typeof startEventWatcher>;
275
- let batchRecoveryInterval: ReturnType<typeof setInterval>;
276
- let staleCleanupInterval: ReturnType<typeof setInterval>;
277
- let sessionSyncInterval: ReturnType<typeof setInterval>;
278
- let gitPollInterval: ReturnType<typeof setInterval>;
279
-
280
- const actualPort = await new Promise<number>((resolve, reject) => {
281
- let attempt = 0;
282
- const maxAttempts = 10;
283
176
 
284
- httpServer.on('error', (err: NodeJS.ErrnoException) => {
285
- if (err.code === 'EADDRINUSE' && attempt < maxAttempts) {
286
- attempt++;
287
- const nextPort = port + attempt;
288
- log.warn('Port in use, trying next', { tried: port + attempt - 1, next: nextPort });
289
- httpServer.listen(nextPort);
290
- } else {
291
- reject(new Error(`Failed to start server: ${err.message}`));
177
+ socket.on('unsubscribe', (payload: { projectId?: string; scope?: string }) => {
178
+ if (payload.scope === 'all') {
179
+ socket.leave('all-projects');
180
+ } else if (payload.projectId) {
181
+ socket.leave(`project:${payload.projectId}`);
292
182
  }
293
183
  });
294
184
 
295
- httpServer.on('listening', () => {
296
- const addr = httpServer.address();
297
- const listenPort = typeof addr === 'object' && addr ? addr.port : port;
298
- resolve(listenPort);
185
+ socket.on('disconnect', () => {
186
+ log.debug('Client disconnected', { socketId: socket.id });
299
187
  });
300
-
301
- httpServer.listen(port);
302
188
  });
303
189
 
304
- // ─── Post-listen initialization ────────────────────────────
305
-
306
- // Sync scopes from filesystem on startup (populates in-memory cache)
307
- const scopeCount = scopeService.syncFromFilesystem();
190
+ // ─── Error Handling Middleware ─────────────────────────────
191
+ // Catches unhandled errors thrown from route handlers.
308
192
 
309
- // Resolve stale dispatch events (terminal scopes + age-based)
310
- const staleResolved = resolveStaleDispatches(db, io, scopeService, workflowEngine);
311
- if (staleResolved > 0) {
312
- log.info('Resolved stale dispatch events', { count: staleResolved });
313
- }
314
-
315
- // Write iTerm2 dispatch profiles (idempotent, fire-and-forget)
316
- ensureDynamicProfiles(workflowEngine);
317
-
318
- // Start file watchers
319
- scopeWatcher = startScopeWatcher(config.scopesDir, scopeService);
320
- eventWatcher = startEventWatcher(config.eventsDir, eventService);
321
-
322
- // Recover any active sprints/batches from before server restart
323
- sprintOrchestrator.recoverActiveSprints().catch(err => log.error('Sprint recovery failed', { error: err.message }));
324
- batchOrchestrator.recoverActiveBatches().catch(err => log.error('Batch recovery failed', { error: err.message }));
193
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
194
+ app.use((err: Error, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
195
+ log.error('Unhandled route error', { error: err.message, stack: err.stack });
196
+ if (!res.headersSent) {
197
+ res.status(500).json({ ok: false, error: 'Internal server error' });
198
+ }
199
+ });
325
200
 
326
- // Resolve stale batches on startup (catches stuck dispatches from previous runs)
327
- const staleBatchesResolved = batchOrchestrator.resolveStaleBatches();
328
- if (staleBatchesResolved > 0) {
329
- log.info('Resolved stale batches', { count: staleBatchesResolved });
330
- }
201
+ // ─── Startup Banner ──────────────────────────────────────
331
202
 
332
- // Poll active batch PIDs every 30s for two-phase completion (B-1)
333
- batchRecoveryInterval = setInterval(() => {
334
- batchOrchestrator.recoverActiveBatches().catch(err => log.error('Batch recovery failed', { error: err.message }));
335
- }, 30_000);
203
+ const projectList = projectManager.getProjectList();
204
+ const projectLines = projectList.map(p =>
205
+ `║ ${p.status === 'active' ? '●' : '○'} ${p.name.padEnd(20)} ${String(p.scopeCount).padStart(3)} scopes ${p.status.padEnd(8)} ║`
206
+ ).join('\n');
336
207
 
337
- // Periodic stale dispatch + batch cleanup (crash recovery — catches SIGKILL'd sessions)
338
- staleCleanupInterval = setInterval(() => {
339
- const count = resolveStaleDispatches(db, io, scopeService, workflowEngine);
340
- if (count > 0) {
341
- log.info('Periodic cleanup: resolved stale dispatches', { count });
342
- }
343
- const batchCount = batchOrchestrator.resolveStaleBatches();
344
- if (batchCount > 0) {
345
- log.info('Periodic cleanup: resolved stale batches', { count: batchCount });
346
- }
347
- }, 30_000);
348
-
349
- // Sync frontmatter-derived sessions into DB (non-blocking)
350
- syncClaudeSessionsToDB(db, scopeService).then((count) => {
351
- log.info('Synced frontmatter sessions', { count });
352
-
353
- // Purge legacy pattern-matched rows (no action = old regex system)
354
- const purged = db.prepare(
355
- "DELETE FROM sessions WHERE action IS NULL AND id LIKE 'claude-%'"
356
- ).run();
357
- if (purged.changes > 0) {
358
- log.info('Purged legacy pattern-matched session rows', { count: purged.changes });
359
- }
360
- }).catch(err => log.error('Session sync failed', { error: err.message }));
361
-
362
- // Re-sync every 5 minutes so new sessions appear without restart
363
- sessionSyncInterval = setInterval(() => {
364
- syncClaudeSessionsToDB(db, scopeService)
365
- .then((count) => {
366
- if (count > 0) io.emit('session:updated', { type: 'resync', count });
367
- })
368
- .catch(err => log.error('Session resync failed', { error: err.message }));
369
- }, 5 * 60 * 1000);
370
-
371
- // Poll git status every 10s — emit socket event on change
372
- let lastGitHash = '';
373
- gitPollInterval = setInterval(async () => {
374
- try {
375
- const hash = await gitService.getStatusHash();
376
- if (lastGitHash && hash !== lastGitHash) {
377
- gitService.clearCache();
378
- io.emit('git:status:changed');
379
- }
380
- lastGitHash = hash;
381
- } catch { /* ok */ }
382
- }, 10_000);
208
+ const dashboardPort = devMode ? clientPort : actualPort;
383
209
 
384
210
  // eslint-disable-next-line no-console
385
211
  console.log(`
386
212
  ╔══════════════════════════════════════════════════════╗
387
- ║ Orbital Command
388
- ║ ${config.projectName.padEnd(42)} ║
213
+ ║ Orbital Command — Central Server
389
214
  ║ ║
390
- ║ >>> Open: http://localhost:${actualPort} <<< ║
215
+ ║ >>> Open: http://localhost:${String(dashboardPort).padEnd(25)} <<<║
391
216
  ║ ║
392
217
  ╠══════════════════════════════════════════════════════╣
393
- ║ Scopes: ${String(scopeCount).padEnd(3)} loaded from filesystem ║
218
+ ${projectLines}
219
+ ╠══════════════════════════════════════════════════════╣
394
220
  ║ API: http://localhost:${actualPort}/api/orbital/* ║
395
221
  ║ Socket.io: ws://localhost:${actualPort} ║
222
+ ║ Home: ${ORBITAL_HOME.padEnd(39)} ║
396
223
  ╚══════════════════════════════════════════════════════╝
397
224
  `);
398
225
 
399
- // ─── Graceful Shutdown ─────────────────────────────────────
226
+ // ─── Graceful Shutdown ───────────────────────────────────
400
227
 
401
228
  let shuttingDown = false;
402
- function shutdown(): Promise<void> {
403
- if (shuttingDown) return Promise.resolve();
229
+ async function shutdown(): Promise<void> {
230
+ if (shuttingDown) return;
404
231
  shuttingDown = true;
405
- log.info('Shutting down');
406
- scopeWatcher.close();
407
- eventWatcher.close();
408
- clearInterval(batchRecoveryInterval);
409
- clearInterval(staleCleanupInterval);
410
- clearInterval(sessionSyncInterval);
411
- clearInterval(gitPollInterval);
232
+ log.info('Shutting down central server');
412
233
 
413
- return new Promise<void>((resolve) => {
414
- const forceTimeout = setTimeout(() => {
415
- closeDatabase();
416
- resolve();
417
- }, 2000);
234
+ if (globalWatcher) await globalWatcher.close();
235
+ await projectManager.shutdownAll();
418
236
 
237
+ return new Promise<void>((resolve) => {
238
+ const forceTimeout = setTimeout(resolve, 2000);
419
239
  io.close(() => {
420
240
  clearTimeout(forceTimeout);
421
- closeDatabase();
422
241
  resolve();
423
242
  });
424
243
  });
425
244
  }
426
245
 
427
- return { app, io, db, workflowEngine, httpServer, shutdown };
246
+ return { app, io, projectManager, syncService, httpServer, shutdown };
428
247
  }
429
248
 
430
249
  // ─── Direct Execution (backward compat: tsx watch server/index.ts) ───
@@ -436,7 +255,11 @@ const isDirectRun = process.argv[1] && (
436
255
  );
437
256
 
438
257
  if (isDirectRun) {
439
- startServer().then(({ shutdown }) => {
258
+ const projectRoot = process.env.ORBITAL_PROJECT_ROOT || process.cwd();
259
+ startCentralServer({
260
+ port: Number(process.env.ORBITAL_SERVER_PORT) || 4444,
261
+ autoRegisterPath: projectRoot,
262
+ }).then(({ shutdown }) => {
440
263
  process.on('SIGINT', async () => {
441
264
  await shutdown();
442
265
  process.exit(0);