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
@@ -108,5 +108,82 @@ export function createGitRoutes({ gitService, githubService, engine }: GitRoutes
108
108
  }
109
109
  });
110
110
 
111
+ // ─── GitHub Auth Flow ──────────────────────────────────────
112
+
113
+ router.post('/github/connect', async (req, res) => {
114
+ try {
115
+ const { method, token } = req.body as { method?: string; token?: string };
116
+ if (method === 'pat' && token) {
117
+ const result = await githubService.connectWithToken(token);
118
+ res.json(result);
119
+ } else {
120
+ const result = await githubService.connectOAuth();
121
+ res.json(result);
122
+ }
123
+ } catch (err) {
124
+ res.status(500).json({ error: 'Failed to connect', details: String(err) });
125
+ }
126
+ });
127
+
128
+ router.get('/github/auth-status', async (_req, res) => {
129
+ try {
130
+ const status = await githubService.getAuthStatus();
131
+ res.json(status);
132
+ } catch (err) {
133
+ res.status(500).json({ error: 'Failed to check auth', details: String(err) });
134
+ }
135
+ });
136
+
137
+ router.post('/github/disconnect', async (_req, res) => {
138
+ try {
139
+ const result = await githubService.disconnect();
140
+ res.json(result);
141
+ } catch (err) {
142
+ res.status(500).json({ error: 'Failed to disconnect', details: String(err) });
143
+ }
144
+ });
145
+
146
+ // ─── GitHub CI Checks ──────────────────────────────────────
147
+
148
+ router.get('/github/checks/:ref', async (req, res) => {
149
+ try {
150
+ const checks = await githubService.getCheckRuns(req.params.ref);
151
+ res.json(checks);
152
+ } catch (err) {
153
+ res.status(500).json({ error: 'Failed to get checks', details: String(err) });
154
+ }
155
+ });
156
+
157
+ // ─── Git Health Metrics ────────────────────────────────────
158
+
159
+ router.get('/git/health', async (_req, res) => {
160
+ try {
161
+ // Get PR ages for health calculation
162
+ let prAges: number[] = [];
163
+ try {
164
+ const prs = await githubService.getOpenPRs();
165
+ const now = Date.now();
166
+ prAges = prs.map(pr => Math.round((now - new Date(pr.createdAt).getTime()) / (1000 * 60 * 60 * 24)));
167
+ } catch { /* ok — GitHub may not be connected */ }
168
+
169
+ const health = await gitService.getHealthMetrics(prAges);
170
+ res.json(health);
171
+ } catch (err) {
172
+ res.status(500).json({ error: 'Failed to get health metrics', details: String(err) });
173
+ }
174
+ });
175
+
176
+ // ─── Git Activity Series ───────────────────────────────────
177
+
178
+ router.get('/git/activity', async (req, res) => {
179
+ try {
180
+ const days = Number(req.query.days) || 30;
181
+ const activity = await gitService.getActivitySeries(days);
182
+ res.json(activity);
183
+ } catch (err) {
184
+ res.status(500).json({ error: 'Failed to get activity', details: String(err) });
185
+ }
186
+ });
187
+
111
188
  return router;
112
189
  }
@@ -0,0 +1,388 @@
1
+ /**
2
+ * REST API routes for the manifest-based primitive management system.
3
+ * Exposes status, validation, update, pin/unpin, reset, and diff operations.
4
+ */
5
+
6
+ import path from 'path';
7
+ import fs from 'fs';
8
+ import { execFileSync } from 'child_process';
9
+ import { Router } from 'express';
10
+ import {
11
+ loadManifest,
12
+ saveManifest,
13
+ hashFile,
14
+ computeFileStatus,
15
+ refreshFileStatuses,
16
+ summarizeManifest,
17
+ reverseRemapPath,
18
+ safeBackupFile,
19
+ safeCopyTemplate,
20
+ safeRestoreFile,
21
+ } from '../manifest.js';
22
+ import { validate } from '../validator.js';
23
+ import { computeUpdatePlan, loadRenameMap } from '../update-planner.js';
24
+ import { runInit, runUpdate } from '../init.js';
25
+ import { needsLegacyMigration, migrateFromLegacy } from '../migrate-legacy.js';
26
+ import type { Emitter } from '../project-emitter.js';
27
+ import { errMsg, isValidRelativePath } from '../utils/route-helpers.js';
28
+
29
+ // ─── Types ──────────────────────────────────────────────────
30
+
31
+ interface ManifestRouteDeps {
32
+ projectRoot: string;
33
+ templatesDir: string;
34
+ packageVersion: string;
35
+ io: Emitter;
36
+ }
37
+
38
+
39
+ // ─── Route Factory ──────────────────────────────────────────
40
+
41
+ export function createManifestRoutes({
42
+ projectRoot,
43
+ templatesDir,
44
+ packageVersion,
45
+ io,
46
+ }: ManifestRouteDeps): Router {
47
+ const router = Router();
48
+ const claudeDir = path.join(projectRoot, '.claude');
49
+
50
+ // ─── GET /manifest/status — summary overview ────────────
51
+
52
+ router.get('/manifest/status', (_req, res) => {
53
+ try {
54
+ const manifest = loadManifest(projectRoot);
55
+
56
+ if (!manifest) {
57
+ return res.json({
58
+ success: true,
59
+ data: {
60
+ exists: false,
61
+ packageVersion,
62
+ installedVersion: '',
63
+ needsUpdate: true,
64
+ preset: '',
65
+ files: { total: 0, synced: 0, modified: 0, pinned: 0, userOwned: 0, byType: {} },
66
+ lastUpdated: '',
67
+ },
68
+ });
69
+ }
70
+
71
+ refreshFileStatuses(manifest, claudeDir);
72
+ const summary = summarizeManifest(manifest);
73
+
74
+ res.json({
75
+ success: true,
76
+ data: {
77
+ exists: true,
78
+ packageVersion,
79
+ installedVersion: manifest.packageVersion,
80
+ needsUpdate: manifest.packageVersion !== packageVersion,
81
+ preset: manifest.preset,
82
+ files: summary,
83
+ lastUpdated: manifest.updatedAt,
84
+ },
85
+ });
86
+ } catch (err) {
87
+ res.status(500).json({ success: false, error: errMsg(err) });
88
+ }
89
+ });
90
+
91
+ // ─── GET /manifest/files — file inventory ───────────────
92
+
93
+ router.get('/manifest/files', (_req, res) => {
94
+ try {
95
+ const manifest = loadManifest(projectRoot);
96
+ if (!manifest) {
97
+ return res.json({ success: true, data: [] });
98
+ }
99
+
100
+ refreshFileStatuses(manifest, claudeDir);
101
+
102
+ const files = Object.entries(manifest.files).map(([filePath, record]) => ({
103
+ path: filePath,
104
+ origin: record.origin,
105
+ status: record.status,
106
+ templateHash: record.templateHash,
107
+ installedHash: record.installedHash,
108
+ pinnedAt: record.pinnedAt,
109
+ pinnedReason: record.pinnedReason,
110
+ hasPrev: fs.existsSync(path.join(claudeDir, filePath + '.prev')),
111
+ }));
112
+
113
+ res.json({ success: true, data: files });
114
+ } catch (err) {
115
+ res.status(500).json({ success: false, error: errMsg(err) });
116
+ }
117
+ });
118
+
119
+ // ─── GET /manifest/validate — run validation ────────────
120
+
121
+ router.get('/manifest/validate', (_req, res) => {
122
+ try {
123
+ const report = validate(projectRoot, packageVersion);
124
+ res.json({ success: true, data: report });
125
+ } catch (err) {
126
+ res.status(500).json({ success: false, error: errMsg(err) });
127
+ }
128
+ });
129
+
130
+ // ─── POST /manifest/init — initialize manifest ───────────
131
+
132
+ router.post('/manifest/init', (_req, res) => {
133
+ try {
134
+ // If manifest already exists, just return success
135
+ if (loadManifest(projectRoot)) {
136
+ return res.json({ success: true, message: 'Already initialized' });
137
+ }
138
+
139
+ // If legacy install exists, migrate it
140
+ if (needsLegacyMigration(projectRoot)) {
141
+ migrateFromLegacy(projectRoot, templatesDir, packageVersion);
142
+ io.emit('manifest:changed', { action: 'initialized' });
143
+ return res.json({ success: true });
144
+ }
145
+
146
+ // No existing install at all — run full init
147
+ runInit(projectRoot, { force: false });
148
+ io.emit('manifest:changed', { action: 'initialized' });
149
+ res.json({ success: true });
150
+ } catch (err) {
151
+ res.status(500).json({ success: false, error: errMsg(err) });
152
+ }
153
+ });
154
+
155
+ // ─── POST /manifest/update — run update or dry-run ──────
156
+
157
+ router.post('/manifest/update', (req, res) => {
158
+ const { dryRun = true } = req.body as { dryRun?: boolean };
159
+
160
+ try {
161
+ // Ensure manifest exists (migrate legacy if needed)
162
+ let manifest = loadManifest(projectRoot);
163
+ if (!manifest && needsLegacyMigration(projectRoot)) {
164
+ migrateFromLegacy(projectRoot, templatesDir, packageVersion);
165
+ manifest = loadManifest(projectRoot);
166
+ }
167
+
168
+ if (!manifest) {
169
+ return res.status(400).json({ success: false, error: 'No manifest. Run orbital first.' });
170
+ }
171
+
172
+ if (dryRun) {
173
+ refreshFileStatuses(manifest, claudeDir);
174
+ const renameMap = loadRenameMap(templatesDir, manifest.packageVersion, packageVersion);
175
+ const plan = computeUpdatePlan({
176
+ templatesDir,
177
+ claudeDir,
178
+ manifest,
179
+ newVersion: packageVersion,
180
+ renameMap,
181
+ });
182
+ return res.json({ success: true, data: plan });
183
+ }
184
+
185
+ // Execute actual update
186
+ runUpdate(projectRoot, { dryRun: false });
187
+ io.emit('manifest:changed', { action: 'updated' });
188
+ res.json({ success: true });
189
+ } catch (err) {
190
+ res.status(500).json({ success: false, error: errMsg(err) });
191
+ }
192
+ });
193
+
194
+ // ─── POST /manifest/pin — pin a file ───────────────────
195
+
196
+ router.post('/manifest/pin', (req, res) => {
197
+ const { file, reason } = req.body as { file: string; reason?: string };
198
+ if (!file || !isValidRelativePath(file)) {
199
+ return res.status(400).json({ success: false, error: 'Valid file path required' });
200
+ }
201
+
202
+ try {
203
+ const manifest = loadManifest(projectRoot);
204
+ if (!manifest) return res.status(400).json({ success: false, error: 'No manifest' });
205
+
206
+ const record = manifest.files[file];
207
+ if (!record) return res.status(404).json({ success: false, error: 'File not tracked' });
208
+ if (record.origin === 'user') return res.status(400).json({ success: false, error: 'Cannot pin user-owned file' });
209
+
210
+ record.status = 'pinned';
211
+ record.pinnedAt = new Date().toISOString();
212
+ if (reason) record.pinnedReason = reason;
213
+
214
+ saveManifest(projectRoot, manifest);
215
+ io.emit('manifest:changed', { action: 'pinned', file });
216
+ res.json({ success: true });
217
+ } catch (err) {
218
+ res.status(500).json({ success: false, error: errMsg(err) });
219
+ }
220
+ });
221
+
222
+ // ─── POST /manifest/unpin — unpin a file ────────────────
223
+
224
+ router.post('/manifest/unpin', (req, res) => {
225
+ const { file } = req.body as { file: string };
226
+ if (!file || !isValidRelativePath(file)) {
227
+ return res.status(400).json({ success: false, error: 'Valid file path required' });
228
+ }
229
+
230
+ try {
231
+ const manifest = loadManifest(projectRoot);
232
+ if (!manifest) return res.status(400).json({ success: false, error: 'No manifest' });
233
+
234
+ const record = manifest.files[file];
235
+ if (!record || record.status !== 'pinned') {
236
+ return res.status(400).json({ success: false, error: 'File is not pinned' });
237
+ }
238
+
239
+ // Clear pinned state before recomputing status
240
+ record.status = 'synced';
241
+ delete record.pinnedAt;
242
+ delete record.pinnedReason;
243
+
244
+ const absPath = path.join(claudeDir, file);
245
+ if (fs.existsSync(absPath)) {
246
+ const currentHash = hashFile(absPath);
247
+ record.status = computeFileStatus(record, currentHash);
248
+ }
249
+
250
+ saveManifest(projectRoot, manifest);
251
+ io.emit('manifest:changed', { action: 'unpinned', file });
252
+ res.json({ success: true });
253
+ } catch (err) {
254
+ res.status(500).json({ success: false, error: errMsg(err) });
255
+ }
256
+ });
257
+
258
+ // ─── POST /manifest/reset — reset file to template ──────
259
+
260
+ router.post('/manifest/reset', (req, res) => {
261
+ const { file } = req.body as { file: string };
262
+ if (!file || !isValidRelativePath(file)) {
263
+ return res.status(400).json({ success: false, error: 'Valid file path required' });
264
+ }
265
+
266
+ try {
267
+ const manifest = loadManifest(projectRoot);
268
+ if (!manifest) return res.status(400).json({ success: false, error: 'No manifest' });
269
+
270
+ const record = manifest.files[file];
271
+ if (!record || record.origin !== 'template') {
272
+ return res.status(400).json({ success: false, error: 'Not a template file' });
273
+ }
274
+
275
+ // Resolve template source path
276
+ const templateRelPath = reverseRemapPath(file);
277
+ const templatePath = path.join(templatesDir, templateRelPath);
278
+ if (!fs.existsSync(templatePath)) {
279
+ return res.status(404).json({ success: false, error: 'Template file not found' });
280
+ }
281
+
282
+ const localPath = path.join(claudeDir, file);
283
+
284
+ // Back up current version so user can revert (symlink-safe)
285
+ safeBackupFile(localPath);
286
+
287
+ // Copy template to destination (skips if symlink)
288
+ safeCopyTemplate(templatePath, localPath);
289
+
290
+ const newHash = hashFile(localPath);
291
+ record.status = 'synced';
292
+ record.templateHash = newHash;
293
+ record.installedHash = newHash;
294
+ delete record.pinnedAt;
295
+ delete record.pinnedReason;
296
+
297
+ saveManifest(projectRoot, manifest);
298
+ io.emit('manifest:changed', { action: 'reset', file });
299
+ res.json({ success: true });
300
+ } catch (err) {
301
+ res.status(500).json({ success: false, error: errMsg(err) });
302
+ }
303
+ });
304
+
305
+ // ─── POST /manifest/revert — restore file from .prev backup ──
306
+
307
+ router.post('/manifest/revert', (req, res) => {
308
+ const { file } = req.body as { file: string };
309
+ if (!file || !isValidRelativePath(file)) {
310
+ return res.status(400).json({ success: false, error: 'Valid file path required' });
311
+ }
312
+
313
+ try {
314
+ const manifest = loadManifest(projectRoot);
315
+ if (!manifest) return res.status(400).json({ success: false, error: 'No manifest' });
316
+
317
+ const record = manifest.files[file];
318
+ if (!record) return res.status(404).json({ success: false, error: 'File not tracked' });
319
+
320
+ const localPath = path.join(claudeDir, file);
321
+
322
+ if (!safeRestoreFile(localPath)) {
323
+ return res.status(404).json({ success: false, error: 'No previous version available' });
324
+ }
325
+
326
+ // Recompute status — file may now be a symlink or regular file
327
+ if (fs.existsSync(localPath)) {
328
+ const stat = fs.lstatSync(localPath);
329
+ if (stat.isSymbolicLink()) {
330
+ record.status = 'synced'; // restored symlink points at template
331
+ } else {
332
+ const currentHash = hashFile(localPath);
333
+ record.installedHash = currentHash;
334
+ record.status = computeFileStatus(record, currentHash);
335
+ }
336
+ } else {
337
+ record.status = 'missing';
338
+ }
339
+
340
+ saveManifest(projectRoot, manifest);
341
+ io.emit('manifest:changed', { action: 'reverted', file });
342
+ res.json({ success: true });
343
+ } catch (err) {
344
+ res.status(500).json({ success: false, error: errMsg(err) });
345
+ }
346
+ });
347
+
348
+ // ─── GET /manifest/diff — diff template vs local ─────────
349
+
350
+ router.get('/manifest/diff', (req, res) => {
351
+ const file = req.query.file as string;
352
+ if (!file || !isValidRelativePath(file)) {
353
+ return res.status(400).json({ success: false, error: 'Valid file path required' });
354
+ }
355
+
356
+ try {
357
+ const templateRelPath = reverseRemapPath(file);
358
+ const rawTemplatePath = path.join(templatesDir, templateRelPath);
359
+ // Resolve symlinks so git diff compares file content, not symlink metadata
360
+ const templatePath = fs.existsSync(rawTemplatePath) ? fs.realpathSync(rawTemplatePath) : rawTemplatePath;
361
+ const localPath = path.join(claudeDir, file);
362
+
363
+ if (!fs.existsSync(templatePath)) {
364
+ return res.status(404).json({ success: false, error: 'Template file not found' });
365
+ }
366
+ if (!fs.existsSync(localPath)) {
367
+ return res.status(404).json({ success: false, error: 'Local file not found' });
368
+ }
369
+
370
+ let diff = '';
371
+ try {
372
+ diff = execFileSync('git', ['diff', '--no-index', '--', templatePath, localPath], {
373
+ encoding: 'utf-8',
374
+ });
375
+ } catch (e: unknown) {
376
+ // git diff exits 1 when files differ
377
+ const err = e as { stdout?: string };
378
+ diff = err.stdout || 'Files differ';
379
+ }
380
+
381
+ res.json({ success: true, data: { diff } });
382
+ } catch (err) {
383
+ res.status(500).json({ success: false, error: errMsg(err) });
384
+ }
385
+ });
386
+
387
+ return router;
388
+ }
@@ -1,27 +1,34 @@
1
1
  import { Router } from 'express';
2
2
  import { spawn } from 'child_process';
3
3
  import type Database from 'better-sqlite3';
4
- import type { Server } from 'socket.io';
4
+ import type { Emitter } from '../project-emitter.js';
5
5
  import type { ScopeService } from '../services/scope-service.js';
6
6
  import type { ReadinessService } from '../services/readiness-service.js';
7
7
  import type { WorkflowEngine } from '../../shared/workflow-engine.js';
8
- import { launchInTerminal, escapeForAnsiC, buildSessionName, snapshotSessionPids, discoverNewSession, renameSession } from '../utils/terminal-launcher.js';
8
+ import { launchInTerminal, escapeForAnsiC, shellQuote, buildSessionName, snapshotSessionPids, discoverNewSession, renameSession } from '../utils/terminal-launcher.js';
9
9
  import { resolveDispatchEvent, linkPidToDispatch } from '../utils/dispatch-utils.js';
10
- import { getConfig } from '../config.js';
10
+ import { buildClaudeFlags, buildEnvVarPrefix } from '../utils/flag-builder.js';
11
+ import type { OrbitalConfig } from '../config.js';
11
12
  import { createLogger } from '../utils/logger.js';
12
13
 
13
14
  const log = createLogger('dispatch');
14
15
 
15
16
  interface ScopeRouteDeps {
16
17
  db: Database.Database;
17
- io: Server;
18
+ io: Emitter;
18
19
  scopeService: ScopeService;
19
20
  readinessService: ReadinessService;
20
21
  projectRoot: string;
22
+ projectName: string;
21
23
  engine: WorkflowEngine;
24
+ config: OrbitalConfig;
22
25
  }
23
26
 
24
- export function createScopeRoutes({ db, io, scopeService, readinessService, projectRoot, engine }: ScopeRouteDeps): Router {
27
+ function isValidSlug(slug: string): boolean {
28
+ return /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(slug) && slug.length <= 80;
29
+ }
30
+
31
+ export function createScopeRoutes({ db, io, scopeService, readinessService, projectRoot, projectName, engine, config }: ScopeRouteDeps): Router {
25
32
  const router = Router();
26
33
 
27
34
  // ─── Scope CRUD ──────────────────────────────────────────
@@ -67,7 +74,7 @@ export function createScopeRoutes({ db, io, scopeService, readinessService, proj
67
74
 
68
75
  router.patch('/scopes/:id', (req, res) => {
69
76
  const id = Number(req.params.id);
70
- const result = scopeService.updateScopeFrontmatter(id, req.body);
77
+ const result = scopeService.updateFields(id, req.body);
71
78
  if (!result.ok) {
72
79
  const code = result.code === 'NOT_FOUND' ? 404 : 400;
73
80
  res.status(code).json({ error: result.error, code: result.code });
@@ -89,14 +96,15 @@ export function createScopeRoutes({ db, io, scopeService, readinessService, proj
89
96
  res.status(201).json(idea);
90
97
  });
91
98
 
92
- router.patch('/ideas/:id', (req, res) => {
93
- const id = Number(req.params.id);
99
+ router.patch('/ideas/:slug', (req, res) => {
100
+ const { slug } = req.params;
101
+ if (!isValidSlug(slug)) { res.status(400).json({ error: 'Invalid slug' }); return; }
94
102
  const { title, description } = req.body as { title?: string; description?: string };
95
103
  if (!title?.trim()) {
96
104
  res.status(400).json({ error: 'title is required' });
97
105
  return;
98
106
  }
99
- const updated = scopeService.updateIdeaFile(id, title.trim(), (description ?? '').trim());
107
+ const updated = scopeService.updateIdeaFile(slug, title.trim(), (description ?? '').trim());
100
108
  if (!updated) {
101
109
  res.status(404).json({ error: 'Idea not found' });
102
110
  return;
@@ -104,9 +112,10 @@ export function createScopeRoutes({ db, io, scopeService, readinessService, proj
104
112
  res.json({ ok: true });
105
113
  });
106
114
 
107
- router.delete('/ideas/:id', (req, res) => {
108
- const id = Number(req.params.id);
109
- const deleted = scopeService.deleteIdeaFile(id);
115
+ router.delete('/ideas/:slug', (req, res) => {
116
+ const { slug } = req.params;
117
+ if (!isValidSlug(slug)) { res.status(400).json({ error: 'Invalid slug' }); return; }
118
+ const deleted = scopeService.deleteIdeaFile(slug);
110
119
  if (!deleted) {
111
120
  res.status(404).json({ error: 'Idea not found' });
112
121
  return;
@@ -114,9 +123,13 @@ export function createScopeRoutes({ db, io, scopeService, readinessService, proj
114
123
  res.json({ ok: true });
115
124
  });
116
125
 
117
- router.post('/ideas/:id/promote', async (req, res) => {
118
- const ideaId = Number(req.params.id);
119
- const result = scopeService.promoteIdea(ideaId);
126
+ router.post('/ideas/:slug/promote', async (req, res) => {
127
+ const { slug } = req.params;
128
+ if (!isValidSlug(slug)) { res.status(400).json({ error: 'Invalid slug' }); return; }
129
+ const entryPoint = engine.getEntryPoint();
130
+ const targets = engine.getValidTargets(entryPoint.id);
131
+ const promoteTarget = targets[0] ?? 'planning';
132
+ const result = scopeService.promoteIdea(slug, promoteTarget);
120
133
  if (!result) {
121
134
  res.status(404).json({ error: 'Idea not found' });
122
135
  return;
@@ -125,9 +138,6 @@ export function createScopeRoutes({ db, io, scopeService, readinessService, proj
125
138
  const scopeId = result.id;
126
139
 
127
140
  // Read command from workflow edge config (user-overridable)
128
- const entryPoint = engine.getEntryPoint();
129
- const targets = engine.getValidTargets(entryPoint.id);
130
- const promoteTarget = targets[0] ?? 'planning';
131
141
  const edge = engine.findEdge(entryPoint.id, promoteTarget);
132
142
  const edgeCommand = edge ? engine.buildCommand(edge, scopeId) : null;
133
143
  const command = edgeCommand ?? `/scope-create ${String(scopeId).padStart(3, '0')}`;
@@ -151,7 +161,9 @@ export function createScopeRoutes({ db, io, scopeService, readinessService, proj
151
161
  });
152
162
 
153
163
  const escaped = escapeForAnsiC(command);
154
- const fullCmd = `cd '${projectRoot}' && claude --dangerously-skip-permissions $'${escaped}'`;
164
+ const flagsStr = buildClaudeFlags(config.claude.dispatchFlags);
165
+ const envPrefix = buildEnvVarPrefix(config.dispatch.envVars);
166
+ const fullCmd = `cd '${shellQuote(projectRoot)}' && ${envPrefix}ORBITAL_DISPATCH_ID='${shellQuote(eventId)}' claude ${flagsStr} $'${escaped}'`;
155
167
 
156
168
  const promoteSessionName = buildSessionName({ scopeId, title: result.title, command });
157
169
  const promoteBeforePids = snapshotSessionPids(projectRoot);
@@ -184,18 +196,15 @@ export function createScopeRoutes({ db, io, scopeService, readinessService, proj
184
196
  }
185
197
  surpriseInProgress = true;
186
198
 
187
- const nextIdStart = scopeService.getNextIceboxId();
188
199
  const today = new Date().toISOString().split('T')[0];
189
- const idRange = Array.from({ length: 5 }, (_, i) => nextIdStart + i);
190
200
 
191
- const prompt = `You are analyzing the ${getConfig().projectName} codebase to suggest feature ideas. Your ONLY job is to create markdown files.
201
+ const prompt = `You are analyzing the ${projectName} codebase to suggest feature ideas. Your ONLY job is to create markdown files.
192
202
 
193
203
  Create exactly 3 idea files in the scopes/icebox/ directory. Each file must use this EXACT format:
194
204
 
195
- File: scopes/icebox/{ID}-{kebab-slug}.md
205
+ File: scopes/icebox/{kebab-slug}.md
196
206
 
197
207
  ---
198
- id: {ID}
199
208
  title: "{title}"
200
209
  status: icebox
201
210
  ghost: true
@@ -208,13 +217,12 @@ tags: []
208
217
 
209
218
  {2-3 sentence description of the feature, what problem it solves, and a rough approach.}
210
219
 
211
- Use these IDs: ${idRange[0]}, ${idRange[1]}, ${idRange[2]}
212
-
213
220
  Rules:
214
221
  - Focus on practical improvements: performance, UX, security, developer experience, monitoring, or reliability
215
222
  - Be specific and actionable — not vague architectural rewrites
216
223
  - Keep descriptions concise (2-3 sentences max)
217
- - Filenames must be {ID}-{kebab-case-slug}.md
224
+ - Filenames must be {kebab-case-slug}.md (NO numeric prefix)
225
+ - Do NOT include an id field in frontmatter
218
226
  - The ghost: true field is required in frontmatter
219
227
  - Do NOT create any other files or make any other changes`;
220
228
 
@@ -244,9 +252,10 @@ Rules:
244
252
  res.json({ ok: true, status: 'generating' });
245
253
  });
246
254
 
247
- router.post('/ideas/:id/approve', (req, res) => {
248
- const id = Number(req.params.id);
249
- const approved = scopeService.approveGhostIdea(id);
255
+ router.post('/ideas/:slug/approve', (req, res) => {
256
+ const { slug } = req.params;
257
+ if (!isValidSlug(slug)) { res.status(400).json({ error: 'Invalid slug' }); return; }
258
+ const approved = scopeService.approveGhostIdea(slug);
250
259
  if (!approved) {
251
260
  res.status(404).json({ error: 'Ghost idea not found' });
252
261
  return;