orbital-command 0.1.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (380) hide show
  1. package/bin/orbital.js +676 -53
  2. package/dist/assets/PrimitivesConfig-CrmQXYh4.js +32 -0
  3. package/dist/assets/QualityGates-BbasOsF3.js +21 -0
  4. package/dist/assets/SessionTimeline-CGeJsVvy.js +1 -0
  5. package/dist/assets/Settings-oiM496mc.js +12 -0
  6. package/dist/assets/SourceControl-B1fP2nJL.js +41 -0
  7. package/dist/assets/WorkflowVisualizer-CWLYf-f0.js +74 -0
  8. package/dist/assets/arrow-down-CPy85_J6.js +6 -0
  9. package/dist/assets/charts-DbDg0Psc.js +68 -0
  10. package/dist/assets/circle-x-Cwz6ZQDV.js +6 -0
  11. package/dist/assets/file-text-C46Xr65c.js +6 -0
  12. package/dist/assets/formatDistanceToNow-BMqsSP44.js +1 -0
  13. package/dist/assets/globe-Cn2yNZUD.js +6 -0
  14. package/dist/assets/index-Aj4sV8Al.css +1 -0
  15. package/dist/assets/index-Bc9dK3MW.js +354 -0
  16. package/dist/assets/key-OPaNTWJ5.js +6 -0
  17. package/dist/assets/minus-GMsbpKym.js +6 -0
  18. package/dist/assets/shield-DwAFkDYI.js +6 -0
  19. package/dist/assets/ui-BmsSg9jU.js +53 -0
  20. package/dist/assets/useWorkflowEditor-BJkTX_NR.js +16 -0
  21. package/dist/assets/{vendor-Dzv9lrRc.js → vendor-Bqt8AJn2.js} +1 -1
  22. package/dist/assets/zap-DfbUoOty.js +11 -0
  23. package/dist/favicon.svg +1 -0
  24. package/dist/index.html +6 -5
  25. package/dist/server/server/__tests__/data-routes.test.js +124 -0
  26. package/dist/server/server/__tests__/helpers/db.js +17 -0
  27. package/dist/server/server/__tests__/helpers/mock-emitter.js +8 -0
  28. package/dist/server/server/__tests__/scope-routes.test.js +137 -0
  29. package/dist/server/server/__tests__/sprint-routes.test.js +102 -0
  30. package/dist/server/server/__tests__/workflow-routes.test.js +107 -0
  31. package/dist/server/server/config-migrator.js +138 -0
  32. package/dist/server/server/config.js +17 -2
  33. package/dist/server/server/database.js +27 -12
  34. package/dist/server/server/global-config.js +143 -0
  35. package/dist/server/server/index.js +882 -252
  36. package/dist/server/server/init.js +579 -194
  37. package/dist/server/server/launch.js +29 -0
  38. package/dist/server/server/manifest-types.js +8 -0
  39. package/dist/server/server/manifest.js +454 -0
  40. package/dist/server/server/migrate-legacy.js +229 -0
  41. package/dist/server/server/parsers/event-parser.test.js +117 -0
  42. package/dist/server/server/parsers/scope-parser.js +74 -28
  43. package/dist/server/server/parsers/scope-parser.test.js +230 -0
  44. package/dist/server/server/project-context.js +255 -0
  45. package/dist/server/server/project-emitter.js +41 -0
  46. package/dist/server/server/project-manager.js +297 -0
  47. package/dist/server/server/routes/config-routes.js +1 -3
  48. package/dist/server/server/routes/data-routes.js +22 -110
  49. package/dist/server/server/routes/dispatch-routes.js +15 -9
  50. package/dist/server/server/routes/git-routes.js +74 -0
  51. package/dist/server/server/routes/manifest-routes.js +319 -0
  52. package/dist/server/server/routes/scope-routes.js +37 -23
  53. package/dist/server/server/routes/sync-routes.js +134 -0
  54. package/dist/server/server/routes/version-routes.js +1 -15
  55. package/dist/server/server/routes/workflow-routes.js +9 -3
  56. package/dist/server/server/schema.js +2 -0
  57. package/dist/server/server/services/batch-orchestrator.js +26 -16
  58. package/dist/server/server/services/claude-session-service.js +17 -14
  59. package/dist/server/server/services/deploy-service.test.js +119 -0
  60. package/dist/server/server/services/event-service.js +64 -1
  61. package/dist/server/server/services/event-service.test.js +191 -0
  62. package/dist/server/server/services/gate-service.test.js +105 -0
  63. package/dist/server/server/services/git-service.js +108 -4
  64. package/dist/server/server/services/github-service.js +110 -2
  65. package/dist/server/server/services/readiness-service.test.js +190 -0
  66. package/dist/server/server/services/scope-cache.js +5 -1
  67. package/dist/server/server/services/scope-cache.test.js +142 -0
  68. package/dist/server/server/services/scope-service.js +217 -126
  69. package/dist/server/server/services/scope-service.test.js +137 -0
  70. package/dist/server/server/services/sprint-orchestrator.js +7 -6
  71. package/dist/server/server/services/sprint-service.js +21 -1
  72. package/dist/server/server/services/sprint-service.test.js +238 -0
  73. package/dist/server/server/services/sync-service.js +434 -0
  74. package/dist/server/server/services/sync-types.js +2 -0
  75. package/dist/server/server/services/telemetry-service.js +143 -0
  76. package/dist/server/server/services/workflow-service.js +26 -5
  77. package/dist/server/server/services/workflow-service.test.js +159 -0
  78. package/dist/server/server/settings-sync.js +284 -0
  79. package/dist/server/server/update-planner.js +279 -0
  80. package/dist/server/server/utils/cc-hooks-parser.js +3 -0
  81. package/dist/server/server/utils/cc-hooks-parser.test.js +86 -0
  82. package/dist/server/server/utils/dispatch-utils.js +77 -20
  83. package/dist/server/server/utils/dispatch-utils.test.js +182 -0
  84. package/dist/server/server/utils/logger.js +37 -3
  85. package/dist/server/server/utils/package-info.js +30 -0
  86. package/dist/server/server/utils/route-helpers.js +10 -0
  87. package/dist/server/server/utils/terminal-launcher.js +79 -25
  88. package/dist/server/server/utils/worktree-manager.js +13 -4
  89. package/dist/server/server/validator.js +230 -0
  90. package/dist/server/server/watchers/global-watcher.js +63 -0
  91. package/dist/server/server/watchers/scope-watcher.js +27 -12
  92. package/dist/server/server/wizard/config-editor.js +237 -0
  93. package/dist/server/server/wizard/detect.js +96 -0
  94. package/dist/server/server/wizard/doctor.js +115 -0
  95. package/dist/server/server/wizard/index.js +155 -0
  96. package/dist/server/server/wizard/phases/confirm.js +39 -0
  97. package/dist/server/server/wizard/phases/project-setup.js +90 -0
  98. package/dist/server/server/wizard/phases/setup-wizard.js +66 -0
  99. package/dist/server/server/wizard/phases/welcome.js +35 -0
  100. package/dist/server/server/wizard/phases/workflow-setup.js +22 -0
  101. package/dist/server/server/wizard/types.js +29 -0
  102. package/dist/server/server/wizard/ui.js +74 -0
  103. package/dist/server/shared/__fixtures__/workflow-configs.js +75 -0
  104. package/dist/server/shared/default-workflow.json +65 -0
  105. package/dist/server/shared/onboarding-tour.test.js +81 -0
  106. package/dist/server/shared/project-colors.js +24 -0
  107. package/dist/server/shared/workflow-config.test.js +84 -0
  108. package/dist/server/shared/workflow-engine.test.js +302 -0
  109. package/dist/server/shared/workflow-normalizer.js +101 -0
  110. package/dist/server/shared/workflow-normalizer.test.js +100 -0
  111. package/dist/server/src/components/onboarding/tour-steps.js +84 -0
  112. package/package.json +20 -15
  113. package/schemas/orbital.config.schema.json +16 -1
  114. package/scripts/postinstall.js +55 -7
  115. package/server/__tests__/data-routes.test.ts +149 -0
  116. package/server/__tests__/helpers/db.ts +19 -0
  117. package/server/__tests__/helpers/mock-emitter.ts +10 -0
  118. package/server/__tests__/scope-routes.test.ts +157 -0
  119. package/server/__tests__/sprint-routes.test.ts +118 -0
  120. package/server/__tests__/workflow-routes.test.ts +120 -0
  121. package/server/config-migrator.ts +163 -0
  122. package/server/config.ts +26 -2
  123. package/server/database.ts +35 -18
  124. package/server/global-config.ts +200 -0
  125. package/server/index.ts +975 -287
  126. package/server/init.ts +625 -182
  127. package/server/launch.ts +32 -0
  128. package/server/manifest-types.ts +145 -0
  129. package/server/manifest.ts +494 -0
  130. package/server/migrate-legacy.ts +290 -0
  131. package/server/parsers/event-parser.test.ts +135 -0
  132. package/server/parsers/scope-parser.test.ts +270 -0
  133. package/server/parsers/scope-parser.ts +79 -31
  134. package/server/project-context.ts +309 -0
  135. package/server/project-emitter.ts +50 -0
  136. package/server/project-manager.ts +369 -0
  137. package/server/routes/config-routes.ts +3 -5
  138. package/server/routes/data-routes.ts +28 -141
  139. package/server/routes/dispatch-routes.ts +19 -11
  140. package/server/routes/git-routes.ts +77 -0
  141. package/server/routes/manifest-routes.ts +388 -0
  142. package/server/routes/scope-routes.ts +29 -25
  143. package/server/routes/sync-routes.ts +175 -0
  144. package/server/routes/version-routes.ts +1 -16
  145. package/server/routes/workflow-routes.ts +9 -3
  146. package/server/schema.ts +2 -0
  147. package/server/services/batch-orchestrator.ts +24 -16
  148. package/server/services/claude-session-service.ts +16 -14
  149. package/server/services/deploy-service.test.ts +145 -0
  150. package/server/services/deploy-service.ts +2 -2
  151. package/server/services/event-service.test.ts +242 -0
  152. package/server/services/event-service.ts +92 -3
  153. package/server/services/gate-service.test.ts +131 -0
  154. package/server/services/gate-service.ts +2 -2
  155. package/server/services/git-service.ts +137 -4
  156. package/server/services/github-service.ts +120 -2
  157. package/server/services/readiness-service.test.ts +217 -0
  158. package/server/services/scope-cache.test.ts +167 -0
  159. package/server/services/scope-cache.ts +4 -1
  160. package/server/services/scope-service.test.ts +169 -0
  161. package/server/services/scope-service.ts +220 -126
  162. package/server/services/sprint-orchestrator.ts +7 -7
  163. package/server/services/sprint-service.test.ts +271 -0
  164. package/server/services/sprint-service.ts +27 -3
  165. package/server/services/sync-service.ts +482 -0
  166. package/server/services/sync-types.ts +77 -0
  167. package/server/services/telemetry-service.ts +195 -0
  168. package/server/services/workflow-service.test.ts +190 -0
  169. package/server/services/workflow-service.ts +29 -9
  170. package/server/settings-sync.ts +359 -0
  171. package/server/update-planner.ts +346 -0
  172. package/server/utils/cc-hooks-parser.test.ts +96 -0
  173. package/server/utils/cc-hooks-parser.ts +4 -0
  174. package/server/utils/dispatch-utils.test.ts +245 -0
  175. package/server/utils/dispatch-utils.ts +97 -27
  176. package/server/utils/logger.ts +40 -3
  177. package/server/utils/package-info.ts +32 -0
  178. package/server/utils/route-helpers.ts +12 -0
  179. package/server/utils/terminal-launcher.ts +85 -25
  180. package/server/utils/worktree-manager.ts +9 -4
  181. package/server/validator.ts +270 -0
  182. package/server/watchers/global-watcher.ts +77 -0
  183. package/server/watchers/scope-watcher.ts +21 -9
  184. package/server/wizard/config-editor.ts +248 -0
  185. package/server/wizard/detect.ts +104 -0
  186. package/server/wizard/doctor.ts +114 -0
  187. package/server/wizard/index.ts +187 -0
  188. package/server/wizard/phases/confirm.ts +45 -0
  189. package/server/wizard/phases/project-setup.ts +106 -0
  190. package/server/wizard/phases/setup-wizard.ts +78 -0
  191. package/server/wizard/phases/welcome.ts +43 -0
  192. package/server/wizard/phases/workflow-setup.ts +28 -0
  193. package/server/wizard/types.ts +56 -0
  194. package/server/wizard/ui.ts +93 -0
  195. package/shared/__fixtures__/workflow-configs.ts +80 -0
  196. package/shared/default-workflow.json +65 -0
  197. package/shared/onboarding-tour.test.ts +94 -0
  198. package/shared/project-colors.ts +24 -0
  199. package/shared/workflow-config.test.ts +111 -0
  200. package/shared/workflow-config.ts +7 -0
  201. package/shared/workflow-engine.test.ts +388 -0
  202. package/shared/workflow-normalizer.test.ts +119 -0
  203. package/shared/workflow-normalizer.ts +118 -0
  204. package/templates/hooks/end-session.sh +3 -1
  205. package/templates/hooks/orbital-emit.sh +2 -2
  206. package/templates/hooks/orbital-report-deploy.sh +4 -4
  207. package/templates/hooks/orbital-report-gates.sh +4 -4
  208. package/templates/hooks/orbital-scope-update.sh +1 -1
  209. package/templates/hooks/scope-create-cleanup.sh +2 -2
  210. package/templates/hooks/scope-create-gate.sh +0 -1
  211. package/templates/hooks/scope-helpers.sh +18 -0
  212. package/templates/hooks/scope-prepare.sh +66 -11
  213. package/templates/migrations/renames.json +1 -0
  214. package/templates/orbital.config.json +7 -2
  215. package/templates/settings-hooks.json +1 -1
  216. package/templates/skills/git-commit/SKILL.md +9 -4
  217. package/templates/skills/git-dev/SKILL.md +8 -3
  218. package/templates/skills/git-main/SKILL.md +8 -2
  219. package/templates/skills/git-production/SKILL.md +6 -2
  220. package/templates/skills/git-staging/SKILL.md +8 -3
  221. package/templates/skills/scope-create/SKILL.md +17 -3
  222. package/templates/skills/scope-fix-review/SKILL.md +6 -3
  223. package/templates/skills/scope-implement/SKILL.md +4 -1
  224. package/templates/skills/scope-post-review/SKILL.md +63 -5
  225. package/templates/skills/scope-pre-review/SKILL.md +5 -2
  226. package/templates/skills/scope-verify/SKILL.md +5 -3
  227. package/templates/skills/test-code-review/SKILL.md +41 -33
  228. package/templates/skills/test-scaffold/SKILL.md +222 -0
  229. package/dist/assets/WorkflowVisualizer-BZ21PIIF.js +0 -84
  230. package/dist/assets/charts-D__PA1zp.js +0 -72
  231. package/dist/assets/index-D1G6i0nS.css +0 -1
  232. package/dist/assets/index-DpItvKpf.js +0 -419
  233. package/dist/assets/ui-BvF022GT.js +0 -53
  234. package/index.html +0 -15
  235. package/postcss.config.js +0 -6
  236. package/src/App.tsx +0 -33
  237. package/src/components/AgentBadge.tsx +0 -40
  238. package/src/components/BatchPreflightModal.tsx +0 -115
  239. package/src/components/CardDisplayToggle.tsx +0 -74
  240. package/src/components/ColumnHeaderActions.tsx +0 -55
  241. package/src/components/ColumnMenu.tsx +0 -99
  242. package/src/components/DeployHistory.tsx +0 -141
  243. package/src/components/DispatchModal.tsx +0 -164
  244. package/src/components/DispatchPopover.tsx +0 -139
  245. package/src/components/DragOverlay.tsx +0 -25
  246. package/src/components/DriftSidebar.tsx +0 -140
  247. package/src/components/EnvironmentStrip.tsx +0 -88
  248. package/src/components/ErrorBoundary.tsx +0 -62
  249. package/src/components/FilterChip.tsx +0 -105
  250. package/src/components/GateIndicator.tsx +0 -33
  251. package/src/components/IdeaDetailModal.tsx +0 -190
  252. package/src/components/IdeaFormDialog.tsx +0 -113
  253. package/src/components/KanbanColumn.tsx +0 -201
  254. package/src/components/MarkdownRenderer.tsx +0 -114
  255. package/src/components/NeonGrid.tsx +0 -128
  256. package/src/components/PromotionQueue.tsx +0 -89
  257. package/src/components/ScopeCard.tsx +0 -234
  258. package/src/components/ScopeDetailModal.tsx +0 -255
  259. package/src/components/ScopeFilterBar.tsx +0 -152
  260. package/src/components/SearchInput.tsx +0 -102
  261. package/src/components/SessionPanel.tsx +0 -335
  262. package/src/components/SprintContainer.tsx +0 -303
  263. package/src/components/SprintDependencyDialog.tsx +0 -78
  264. package/src/components/SprintPreflightModal.tsx +0 -138
  265. package/src/components/StatusBar.tsx +0 -168
  266. package/src/components/SwimCell.tsx +0 -67
  267. package/src/components/SwimLaneRow.tsx +0 -94
  268. package/src/components/SwimlaneBoardView.tsx +0 -108
  269. package/src/components/VersionBadge.tsx +0 -139
  270. package/src/components/ViewModeSelector.tsx +0 -114
  271. package/src/components/config/AgentChip.tsx +0 -53
  272. package/src/components/config/AgentCreateDialog.tsx +0 -321
  273. package/src/components/config/AgentEditor.tsx +0 -175
  274. package/src/components/config/DirectoryTree.tsx +0 -582
  275. package/src/components/config/FileEditor.tsx +0 -550
  276. package/src/components/config/HookChip.tsx +0 -50
  277. package/src/components/config/StageCard.tsx +0 -198
  278. package/src/components/config/TransitionZone.tsx +0 -173
  279. package/src/components/config/UnifiedWorkflowPipeline.tsx +0 -216
  280. package/src/components/config/WorkflowPipeline.tsx +0 -161
  281. package/src/components/source-control/BranchList.tsx +0 -93
  282. package/src/components/source-control/BranchPanel.tsx +0 -105
  283. package/src/components/source-control/CommitLog.tsx +0 -100
  284. package/src/components/source-control/CommitRow.tsx +0 -47
  285. package/src/components/source-control/GitHubPanel.tsx +0 -110
  286. package/src/components/source-control/GitHubSetupGuide.tsx +0 -52
  287. package/src/components/source-control/GitOverviewBar.tsx +0 -101
  288. package/src/components/source-control/PullRequestList.tsx +0 -69
  289. package/src/components/source-control/WorktreeList.tsx +0 -80
  290. package/src/components/ui/badge.tsx +0 -41
  291. package/src/components/ui/button.tsx +0 -55
  292. package/src/components/ui/card.tsx +0 -78
  293. package/src/components/ui/dialog.tsx +0 -94
  294. package/src/components/ui/popover.tsx +0 -33
  295. package/src/components/ui/scroll-area.tsx +0 -54
  296. package/src/components/ui/separator.tsx +0 -28
  297. package/src/components/ui/tabs.tsx +0 -52
  298. package/src/components/ui/toggle-switch.tsx +0 -35
  299. package/src/components/ui/tooltip.tsx +0 -27
  300. package/src/components/workflow/AddEdgeDialog.tsx +0 -217
  301. package/src/components/workflow/AddListDialog.tsx +0 -201
  302. package/src/components/workflow/ChecklistEditor.tsx +0 -239
  303. package/src/components/workflow/CommandPrefixManager.tsx +0 -118
  304. package/src/components/workflow/ConfigSettingsPanel.tsx +0 -189
  305. package/src/components/workflow/DirectionSelector.tsx +0 -133
  306. package/src/components/workflow/DispatchConfigPanel.tsx +0 -180
  307. package/src/components/workflow/EdgeDetailPanel.tsx +0 -236
  308. package/src/components/workflow/EdgePropertyEditor.tsx +0 -251
  309. package/src/components/workflow/EditToolbar.tsx +0 -138
  310. package/src/components/workflow/HookDetailPanel.tsx +0 -250
  311. package/src/components/workflow/HookExecutionLog.tsx +0 -24
  312. package/src/components/workflow/HookSourceModal.tsx +0 -129
  313. package/src/components/workflow/HooksDashboard.tsx +0 -363
  314. package/src/components/workflow/ListPropertyEditor.tsx +0 -251
  315. package/src/components/workflow/MigrationPreviewDialog.tsx +0 -237
  316. package/src/components/workflow/MovementRulesPanel.tsx +0 -188
  317. package/src/components/workflow/NodeDetailPanel.tsx +0 -245
  318. package/src/components/workflow/PresetSelector.tsx +0 -414
  319. package/src/components/workflow/SkillCommandBuilder.tsx +0 -174
  320. package/src/components/workflow/WorkflowEdgeComponent.tsx +0 -145
  321. package/src/components/workflow/WorkflowNode.tsx +0 -147
  322. package/src/components/workflow/graphLayout.ts +0 -186
  323. package/src/components/workflow/mergeHooks.ts +0 -85
  324. package/src/components/workflow/useEditHistory.ts +0 -88
  325. package/src/components/workflow/useWorkflowEditor.ts +0 -262
  326. package/src/components/workflow/validateConfig.ts +0 -70
  327. package/src/hooks/useActiveDispatches.ts +0 -198
  328. package/src/hooks/useBoardSettings.ts +0 -170
  329. package/src/hooks/useCardDisplay.ts +0 -57
  330. package/src/hooks/useCcHooks.ts +0 -24
  331. package/src/hooks/useConfigTree.ts +0 -51
  332. package/src/hooks/useEnforcementRules.ts +0 -46
  333. package/src/hooks/useEvents.ts +0 -59
  334. package/src/hooks/useFileEditor.ts +0 -165
  335. package/src/hooks/useGates.ts +0 -57
  336. package/src/hooks/useIdeaActions.ts +0 -53
  337. package/src/hooks/useKanbanDnd.ts +0 -410
  338. package/src/hooks/useOrbitalConfig.ts +0 -54
  339. package/src/hooks/usePipeline.ts +0 -47
  340. package/src/hooks/usePipelineData.ts +0 -338
  341. package/src/hooks/useReconnect.ts +0 -25
  342. package/src/hooks/useScopeFilters.ts +0 -125
  343. package/src/hooks/useScopeSessions.ts +0 -44
  344. package/src/hooks/useScopes.ts +0 -67
  345. package/src/hooks/useSearch.ts +0 -67
  346. package/src/hooks/useSettings.tsx +0 -187
  347. package/src/hooks/useSocket.ts +0 -25
  348. package/src/hooks/useSourceControl.ts +0 -105
  349. package/src/hooks/useSprintPreflight.ts +0 -55
  350. package/src/hooks/useSprints.ts +0 -154
  351. package/src/hooks/useStatusBarHighlight.ts +0 -18
  352. package/src/hooks/useSwimlaneBoardSettings.ts +0 -104
  353. package/src/hooks/useTheme.ts +0 -9
  354. package/src/hooks/useTransitionReadiness.ts +0 -53
  355. package/src/hooks/useVersion.ts +0 -155
  356. package/src/hooks/useViolations.ts +0 -65
  357. package/src/hooks/useWorkflow.tsx +0 -125
  358. package/src/hooks/useZoomModifier.ts +0 -19
  359. package/src/index.css +0 -797
  360. package/src/layouts/DashboardLayout.tsx +0 -113
  361. package/src/lib/collisionDetection.ts +0 -20
  362. package/src/lib/scope-fields.ts +0 -61
  363. package/src/lib/swimlane.ts +0 -146
  364. package/src/lib/utils.ts +0 -15
  365. package/src/main.tsx +0 -19
  366. package/src/socket.ts +0 -11
  367. package/src/types/index.ts +0 -497
  368. package/src/views/AgentFeed.tsx +0 -339
  369. package/src/views/DeployPipeline.tsx +0 -59
  370. package/src/views/EnforcementView.tsx +0 -378
  371. package/src/views/PrimitivesConfig.tsx +0 -500
  372. package/src/views/QualityGates.tsx +0 -1012
  373. package/src/views/ScopeBoard.tsx +0 -454
  374. package/src/views/SessionTimeline.tsx +0 -516
  375. package/src/views/Settings.tsx +0 -183
  376. package/src/views/SourceControl.tsx +0 -95
  377. package/src/views/WorkflowVisualizer.tsx +0 -382
  378. package/tailwind.config.js +0 -161
  379. package/tsconfig.json +0 -25
  380. package/vite.config.ts +0 -49
package/bin/orbital.js CHANGED
@@ -31,6 +31,11 @@ function resolveBin(name) {
31
31
  // CLI Helpers
32
32
  // ---------------------------------------------------------------------------
33
33
 
34
+ function getFlagValue(args, flag) {
35
+ const idx = args.indexOf(flag);
36
+ return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : undefined;
37
+ }
38
+
34
39
  function detectProjectRoot() {
35
40
  try {
36
41
  return execFileSync('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8' }).trim();
@@ -131,59 +136,152 @@ async function loadSharedModule() {
131
136
  }
132
137
  }
133
138
 
139
+ /**
140
+ * Load the interactive wizard module.
141
+ */
142
+ async function loadWizardModule() {
143
+ try {
144
+ return await import('../dist/server/server/wizard/index.js');
145
+ } catch {
146
+ try {
147
+ return await import('../server/wizard/index.js');
148
+ } catch {
149
+ console.error('Error: Wizard module not found.');
150
+ console.error('Try reinstalling: npm install -g orbital-command');
151
+ process.exit(1);
152
+ }
153
+ }
154
+ }
155
+
134
156
  // ---------------------------------------------------------------------------
135
157
  // Commands
136
158
  // ---------------------------------------------------------------------------
137
159
 
160
+ function orbitalSetupDone() {
161
+ return fs.existsSync(path.join(ORBITAL_HOME, 'config.json'));
162
+ }
163
+
164
+ function autoRegisterProject(projectRoot) {
165
+ if (!fs.existsSync(ORBITAL_HOME)) fs.mkdirSync(ORBITAL_HOME, { recursive: true });
166
+ const registry = loadRegistry();
167
+ if (!(registry.projects || []).some(p => p.path === projectRoot)) {
168
+ cmdRegister([projectRoot]);
169
+ }
170
+ }
171
+
138
172
  async function cmdInit(args) {
139
- const force = args.includes('--force');
173
+ const isYes = args.includes('--yes') || args.includes('-y');
174
+ const isInteractive = process.stdout.isTTY && !process.env.CI && !isYes;
140
175
  const projectRoot = detectProjectRoot();
141
176
 
142
- const { runInit } = await loadSharedModule();
143
- runInit(projectRoot, { force });
177
+ if (isInteractive) {
178
+ const wiz = await loadWizardModule();
179
+ const version = getPackageVersion();
144
180
 
181
+ // If Orbital hasn't been set up yet, run Phase 1 first
182
+ if (!orbitalSetupDone()) {
183
+ await wiz.runSetupWizard(version);
184
+ }
185
+
186
+ // Phase 2: project setup for current directory
187
+ await wiz.runProjectSetup(projectRoot, version, args);
188
+ stampTemplateVersion(projectRoot);
189
+ return;
190
+ }
191
+
192
+ // Non-interactive / --yes fallback (existing behavior preserved)
193
+ const force = args.includes('--force');
194
+ const globalPrivate = loadRegistry().privateMode === true;
195
+ const isPrivate = args.includes('--private') || globalPrivate;
196
+ const preset = getFlagValue(args, '--preset');
197
+ const projectName = getFlagValue(args, '--project-name');
198
+ const serverPort = getFlagValue(args, '--server-port');
199
+ const clientPort = getFlagValue(args, '--client-port');
200
+
201
+ const { runInit } = await loadSharedModule();
202
+ runInit(projectRoot, {
203
+ force,
204
+ preset: preset || undefined,
205
+ projectName: projectName || undefined,
206
+ serverPort: serverPort ? Number(serverPort) : undefined,
207
+ clientPort: clientPort ? Number(clientPort) : undefined,
208
+ });
145
209
  stampTemplateVersion(projectRoot);
146
- console.log(`Run \`orbital dev\` to start the development server.\n`);
210
+
211
+ if (isPrivate) {
212
+ const configPath = path.join(projectRoot, '.claude', 'orbital.config.json');
213
+ if (fs.existsSync(configPath)) {
214
+ try {
215
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
216
+ if (!config.telemetry) config.telemetry = {};
217
+ config.telemetry.enabled = false;
218
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
219
+
220
+ } catch { /* leave config as-is */ }
221
+ }
222
+ }
223
+
224
+ autoRegisterProject(projectRoot);
225
+ console.log(`Run \`orbital launch\` to start the dashboard.\n`);
147
226
  }
148
227
 
149
- function cmdDev() {
228
+ function cmdLaunchOrDev(forceViteFlag) {
150
229
  const shouldOpen = process.argv.includes('--open');
230
+ const forceVite = forceViteFlag || process.argv.includes('--vite');
151
231
  const projectRoot = detectProjectRoot();
152
232
  const config = loadConfig(projectRoot);
153
233
  const serverPort = config.serverPort || 4444;
154
234
  const clientPort = config.clientPort || 4445;
155
235
 
156
- console.log(`\nOrbital Command — dev`);
236
+ autoRegisterProject(projectRoot);
237
+
238
+ // Detect packaged mode: dist/index.html exists → serve pre-built frontend
239
+ const hasPrebuiltFrontend = fs.existsSync(path.join(PACKAGE_ROOT, 'dist', 'index.html'));
240
+ const useVite = forceVite || !hasPrebuiltFrontend;
241
+
242
+ console.log(`\nOrbital Command — ${useVite ? 'dev' : 'launch'}`);
157
243
  console.log(`Project root: ${projectRoot}`);
158
- console.log(`Server: http://localhost:${serverPort}`);
159
- console.log(`Client: http://localhost:${clientPort}\n`);
244
+ if (useVite) {
245
+ console.log(`Server: http://localhost:${serverPort}`);
246
+ console.log(`Client: http://localhost:${clientPort} (Vite dev server)\n`);
247
+ } else {
248
+ console.log(`Dashboard: http://localhost:${serverPort}\n`);
249
+ }
160
250
 
161
251
  checkTemplatesStaleness(projectRoot);
162
252
 
163
253
  const env = {
164
254
  ...process.env,
165
- ORBITAL_PROJECT_ROOT: projectRoot,
255
+ ORBITAL_LAUNCH_MODE: 'central',
256
+ ORBITAL_AUTO_REGISTER: projectRoot,
166
257
  ORBITAL_SERVER_PORT: String(serverPort),
167
258
  };
168
259
 
169
- // Start the API server
260
+ const serverScript = path.join(PACKAGE_ROOT, 'server', 'launch.ts');
170
261
  const tsxBin = resolveBin('tsx');
171
262
  const serverProcess = tsxBin
172
- ? spawn(tsxBin, ['watch', path.join(PACKAGE_ROOT, 'server', 'index.ts')],
263
+ ? spawn(tsxBin, ['watch', serverScript],
173
264
  { stdio: 'inherit', env, cwd: PACKAGE_ROOT })
174
- : spawn('npx', ['tsx', 'watch', path.join(PACKAGE_ROOT, 'server', 'index.ts')],
265
+ : spawn('npx', ['tsx', 'watch', serverScript],
175
266
  { stdio: 'inherit', env, cwd: PACKAGE_ROOT });
176
267
 
177
- // Start the Vite dev server
178
- const viteBin = resolveBin('vite');
179
- const viteProcess = viteBin
180
- ? spawn(viteBin, ['--config', path.join(PACKAGE_ROOT, 'vite.config.ts'), '--port', String(clientPort)],
181
- { stdio: 'inherit', env, cwd: PACKAGE_ROOT })
182
- : spawn('npx', ['vite', '--config', path.join(PACKAGE_ROOT, 'vite.config.ts'), '--port', String(clientPort)],
183
- { stdio: 'inherit', env, cwd: PACKAGE_ROOT });
268
+ let viteProcess = null;
269
+
270
+ if (useVite) {
271
+ const viteBin = resolveBin('vite');
272
+ viteProcess = viteBin
273
+ ? spawn(viteBin, ['--config', path.join(PACKAGE_ROOT, 'vite.config.ts'), '--port', String(clientPort)],
274
+ { stdio: 'inherit', env, cwd: PACKAGE_ROOT })
275
+ : spawn('npx', ['vite', '--config', path.join(PACKAGE_ROOT, 'vite.config.ts'), '--port', String(clientPort)],
276
+ { stdio: 'inherit', env, cwd: PACKAGE_ROOT });
277
+ }
278
+
279
+ const dashboardUrl = useVite
280
+ ? `http://localhost:${clientPort}`
281
+ : `http://localhost:${serverPort}`;
184
282
 
185
283
  if (shouldOpen) {
186
- setTimeout(() => openBrowser(`http://localhost:${clientPort}`), 2000);
284
+ setTimeout(() => openBrowser(dashboardUrl), 2000);
187
285
  }
188
286
 
189
287
  let exiting = false;
@@ -192,7 +290,7 @@ function cmdDev() {
192
290
  if (exiting) return;
193
291
  exiting = true;
194
292
  serverProcess.kill();
195
- viteProcess.kill();
293
+ if (viteProcess) viteProcess.kill();
196
294
  process.exit(0);
197
295
  }
198
296
  process.on('SIGINT', cleanup);
@@ -202,16 +300,18 @@ function cmdDev() {
202
300
  if (exiting) return;
203
301
  exiting = true;
204
302
  console.log(`Server exited with code ${code}`);
205
- viteProcess.kill();
206
- process.exit(code || 0);
207
- });
208
- viteProcess.on('exit', (code) => {
209
- if (exiting) return;
210
- exiting = true;
211
- console.log(`Vite exited with code ${code}`);
212
- serverProcess.kill();
303
+ if (viteProcess) viteProcess.kill();
213
304
  process.exit(code || 0);
214
305
  });
306
+ if (viteProcess) {
307
+ viteProcess.on('exit', (code) => {
308
+ if (exiting) return;
309
+ exiting = true;
310
+ console.log(`Vite exited with code ${code}`);
311
+ serverProcess.kill();
312
+ process.exit(code || 0);
313
+ });
314
+ }
215
315
  }
216
316
 
217
317
  function cmdBuild() {
@@ -265,20 +365,442 @@ function cmdEmit(args) {
265
365
  console.log(` File: ${path.relative(projectRoot, filePath)}`);
266
366
  }
267
367
 
268
- async function cmdUpdate() {
368
+ async function cmdUpdate(args) {
269
369
  const projectRoot = detectProjectRoot();
370
+ const dryRun = args.includes('--dry-run');
270
371
 
271
372
  const { runUpdate } = await loadSharedModule();
272
- runUpdate(projectRoot);
373
+ runUpdate(projectRoot, { dryRun });
273
374
 
274
- stampTemplateVersion(projectRoot);
375
+ if (!dryRun) stampTemplateVersion(projectRoot);
275
376
  }
276
377
 
277
- async function cmdUninstall() {
378
+ async function cmdUninstall(args) {
278
379
  const projectRoot = detectProjectRoot();
380
+ const dryRun = args.includes('--dry-run');
381
+ const keepConfig = args.includes('--keep-config');
279
382
 
280
383
  const { runUninstall } = await loadSharedModule();
281
- runUninstall(projectRoot);
384
+ runUninstall(projectRoot, { dryRun, keepConfig });
385
+ }
386
+
387
+ // ---------------------------------------------------------------------------
388
+ // Manifest management commands
389
+ // ---------------------------------------------------------------------------
390
+
391
+ async function cmdValidate() {
392
+ const projectRoot = detectProjectRoot();
393
+
394
+ const mod = await loadSharedModule();
395
+ const report = mod.validate(projectRoot, getPackageVersion());
396
+ console.log(mod.formatValidationReport(report));
397
+ process.exit(report.errors > 0 ? 1 : 0);
398
+ }
399
+
400
+ async function cmdStatus() {
401
+ const projectRoot = detectProjectRoot();
402
+
403
+ const mod = await loadSharedModule();
404
+ const manifest = mod.loadManifest(projectRoot);
405
+
406
+ if (!manifest) {
407
+ console.log('\nNo manifest found. Run `orbital init` or `orbital update` to create one.\n');
408
+ return;
409
+ }
410
+
411
+ const claudeDir = path.join(projectRoot, '.claude');
412
+ mod.refreshFileStatuses(manifest, claudeDir);
413
+
414
+ const summary = mod.summarizeManifest(manifest);
415
+ const packageVersion = getPackageVersion();
416
+ const needsUpdate = manifest.packageVersion !== packageVersion;
417
+
418
+ console.log(`\nOrbital Command v${packageVersion}${needsUpdate ? ` (installed: ${manifest.packageVersion} → needs update)` : ''}\n`);
419
+
420
+ for (const [type, counts] of Object.entries(summary.byType)) {
421
+ const parts = [];
422
+ if (counts.synced) parts.push(`${counts.synced} synced`);
423
+ if (counts.outdated) parts.push(`${counts.outdated} outdated`);
424
+ if (counts.modified) parts.push(`${counts.modified} modified`);
425
+ if (counts.pinned) parts.push(`${counts.pinned} pinned`);
426
+ if (counts.userOwned) parts.push(`${counts.userOwned} user-owned`);
427
+ console.log(` ${type.padEnd(16)} ${parts.join(', ')}`);
428
+ }
429
+
430
+ // Show outdated files (template moved ahead, user hasn't touched)
431
+ const outdated = Object.entries(manifest.files)
432
+ .filter(([, r]) => r.status === 'outdated');
433
+ if (outdated.length > 0) {
434
+ console.log('\n Outdated files (safe to update):');
435
+ for (const [file] of outdated) {
436
+ console.log(` ${file}`);
437
+ }
438
+ }
439
+
440
+ // Show modified files (user edited)
441
+ const modified = Object.entries(manifest.files)
442
+ .filter(([, r]) => r.status === 'modified');
443
+ if (modified.length > 0) {
444
+ console.log('\n Modified files (user edited):');
445
+ for (const [file] of modified) {
446
+ console.log(` ${file} (run 'orbital diff ${file}')`);
447
+ }
448
+ }
449
+
450
+ // Show pinned files
451
+ const pinned = Object.entries(manifest.files)
452
+ .filter(([, r]) => r.status === 'pinned');
453
+ if (pinned.length > 0) {
454
+ console.log('\n Pinned files:');
455
+ for (const [file, record] of pinned) {
456
+ const reason = record.pinnedReason ? `"${record.pinnedReason}"` : '';
457
+ console.log(` ${file} ${reason}`);
458
+ }
459
+ }
460
+
461
+ console.log();
462
+ }
463
+
464
+ async function cmdPin(args) {
465
+ const projectRoot = detectProjectRoot();
466
+ const filePath = args.find(a => !a.startsWith('--'));
467
+ const reasonIdx = args.indexOf('--reason');
468
+ const reason = reasonIdx !== -1 ? args[reasonIdx + 1] : undefined;
469
+
470
+ if (!filePath) {
471
+ console.error('Usage: orbital pin <relative-path> [--reason "..."]');
472
+ process.exit(1);
473
+ }
474
+
475
+ const mod = await loadSharedModule();
476
+ const manifest = mod.loadManifest(projectRoot);
477
+ if (!manifest) {
478
+ console.error('No manifest found. Run `orbital init` first.');
479
+ process.exit(1);
480
+ }
481
+
482
+ const record = manifest.files[filePath];
483
+ if (!record) {
484
+ console.error(`File not tracked: ${filePath}`);
485
+ process.exit(1);
486
+ }
487
+ if (record.origin === 'user') {
488
+ console.error(`Cannot pin user-owned file: ${filePath}`);
489
+ process.exit(1);
490
+ }
491
+
492
+ record.status = 'pinned';
493
+ record.pinnedAt = new Date().toISOString();
494
+ if (reason) record.pinnedReason = reason;
495
+
496
+ mod.saveManifest(projectRoot, manifest);
497
+ console.log(`Pinned: ${filePath}${reason ? ` (${reason})` : ''}`);
498
+ }
499
+
500
+ async function cmdUnpin(args) {
501
+ const projectRoot = detectProjectRoot();
502
+ const filePath = args[0];
503
+
504
+ if (!filePath) {
505
+ console.error('Usage: orbital unpin <relative-path>');
506
+ process.exit(1);
507
+ }
508
+
509
+ const mod = await loadSharedModule();
510
+ const manifest = mod.loadManifest(projectRoot);
511
+ if (!manifest) {
512
+ console.error('No manifest found. Run `orbital init` first.');
513
+ process.exit(1);
514
+ }
515
+
516
+ const record = manifest.files[filePath];
517
+ if (!record || record.status !== 'pinned') {
518
+ console.error(`File is not pinned: ${filePath}`);
519
+ process.exit(1);
520
+ }
521
+
522
+ // Clear pinned state, then recompute
523
+ record.status = 'synced';
524
+ delete record.pinnedAt;
525
+ delete record.pinnedReason;
526
+
527
+ const absPath = path.join(projectRoot, '.claude', filePath);
528
+ if (fs.existsSync(absPath)) {
529
+ const currentHash = mod.hashFile(absPath);
530
+ record.status = mod.computeFileStatus(record, currentHash);
531
+ } else {
532
+ record.status = 'synced';
533
+ }
534
+
535
+ mod.saveManifest(projectRoot, manifest);
536
+ console.log(`Unpinned: ${filePath} (now ${record.status})`);
537
+ }
538
+
539
+ async function cmdPins() {
540
+ const projectRoot = detectProjectRoot();
541
+
542
+ const mod = await loadSharedModule();
543
+ const manifest = mod.loadManifest(projectRoot);
544
+ if (!manifest) {
545
+ console.error('No manifest found. Run `orbital init` first.');
546
+ process.exit(1);
547
+ }
548
+
549
+ const pinned = Object.entries(manifest.files)
550
+ .filter(([, r]) => r.status === 'pinned');
551
+
552
+ if (pinned.length === 0) {
553
+ console.log('No pinned files.');
554
+ return;
555
+ }
556
+
557
+ console.log(`\n Pinned files:\n`);
558
+ for (const [file, record] of pinned) {
559
+ const reason = record.pinnedReason || '(no reason)';
560
+ const date = record.pinnedAt ? new Date(record.pinnedAt).toLocaleDateString() : '';
561
+ console.log(` ${file}`);
562
+ console.log(` Reason: ${reason} Pinned: ${date}`);
563
+ if (record.templateHash !== record.installedHash) {
564
+ console.log(` Template has changed since pin — run 'orbital diff ${file}' to compare`);
565
+ }
566
+ }
567
+ console.log();
568
+ }
569
+
570
+ async function cmdDiff(args) {
571
+ const projectRoot = detectProjectRoot();
572
+ const filePath = args[0];
573
+
574
+ if (!filePath) {
575
+ console.error('Usage: orbital diff <relative-path>');
576
+ process.exit(1);
577
+ }
578
+
579
+ const mod = await loadSharedModule();
580
+ const manifest = mod.loadManifest(projectRoot);
581
+ if (!manifest) {
582
+ console.error('No manifest found. Run `orbital init` first.');
583
+ process.exit(1);
584
+ }
585
+
586
+ const record = manifest.files[filePath];
587
+ if (!record || record.origin !== 'template') {
588
+ console.error(`Not a template file: ${filePath}`);
589
+ process.exit(1);
590
+ }
591
+
592
+ // Resolve template path
593
+ let templateRelPath = filePath;
594
+ if (filePath.startsWith('config/workflows/')) {
595
+ templateRelPath = filePath.replace('config/workflows/', 'presets/');
596
+ }
597
+
598
+ const templatePath = path.join(PACKAGE_ROOT, 'templates', templateRelPath);
599
+ const localPath = path.join(projectRoot, '.claude', filePath);
600
+
601
+ if (!fs.existsSync(templatePath)) {
602
+ console.error(`Template file not found: ${templateRelPath}`);
603
+ process.exit(1);
604
+ }
605
+ if (!fs.existsSync(localPath)) {
606
+ console.log('Local file does not exist. Template content:');
607
+ console.log(fs.readFileSync(templatePath, 'utf-8'));
608
+ return;
609
+ }
610
+
611
+ // Use git diff for nice formatting (safe: no user input in arguments)
612
+ const { execFileSync } = await import('child_process');
613
+ try {
614
+ const output = execFileSync(
615
+ 'git', ['diff', '--no-index', '--color', '--', templatePath, localPath],
616
+ { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }
617
+ );
618
+ console.log(output);
619
+ } catch (e) {
620
+ // git diff exits 1 when files differ — that's expected
621
+ if (e.stdout) console.log(e.stdout);
622
+ else console.log('Files differ but git diff is unavailable.');
623
+ }
624
+ }
625
+
626
+ async function cmdReset(args) {
627
+ const projectRoot = detectProjectRoot();
628
+ const filePath = args[0];
629
+
630
+ if (!filePath) {
631
+ console.error('Usage: orbital reset <relative-path>');
632
+ process.exit(1);
633
+ }
634
+
635
+ const mod = await loadSharedModule();
636
+ const manifest = mod.loadManifest(projectRoot);
637
+ if (!manifest) {
638
+ console.error('No manifest found. Run `orbital init` first.');
639
+ process.exit(1);
640
+ }
641
+
642
+ const record = manifest.files[filePath];
643
+ if (!record || record.origin !== 'template') {
644
+ console.error(`Not a template file: ${filePath}`);
645
+ process.exit(1);
646
+ }
647
+
648
+ // Resolve and copy template file
649
+ let templateRelPath = filePath;
650
+ if (filePath.startsWith('config/workflows/')) {
651
+ templateRelPath = filePath.replace('config/workflows/', 'presets/');
652
+ }
653
+
654
+ const templatePath = path.join(PACKAGE_ROOT, 'templates', templateRelPath);
655
+ const localPath = path.join(projectRoot, '.claude', filePath);
656
+
657
+ if (!fs.existsSync(templatePath)) {
658
+ console.error(`Template file not found: ${templateRelPath}`);
659
+ process.exit(1);
660
+ }
661
+
662
+ fs.copyFileSync(templatePath, localPath);
663
+ const newHash = mod.hashFile(localPath);
664
+ record.status = 'synced';
665
+ record.templateHash = newHash;
666
+ record.installedHash = newHash;
667
+ delete record.pinnedAt;
668
+ delete record.pinnedReason;
669
+
670
+ mod.saveManifest(projectRoot, manifest);
671
+ console.log(`Reset: ${filePath} → synced with template`);
672
+ }
673
+
674
+ // ---------------------------------------------------------------------------
675
+ // Multi-project commands
676
+ // ---------------------------------------------------------------------------
677
+
678
+ const ORBITAL_HOME = path.join(process.env.HOME || process.env.USERPROFILE || '~', '.orbital');
679
+ const REGISTRY_PATH = path.join(ORBITAL_HOME, 'config.json');
680
+
681
+ function loadRegistry() {
682
+ if (!fs.existsSync(REGISTRY_PATH)) return { version: 1, projects: [] };
683
+ try {
684
+ return JSON.parse(fs.readFileSync(REGISTRY_PATH, 'utf8'));
685
+ } catch {
686
+ return { version: 1, projects: [] };
687
+ }
688
+ }
689
+
690
+ // cmdLaunch removed — merged into cmdLaunchOrDev() above
691
+
692
+ function cmdRegister(args) {
693
+ const targetPath = args[0] ? path.resolve(args[0]) : detectProjectRoot();
694
+ const nameFlag = args.indexOf('--alias');
695
+ const name = nameFlag >= 0 ? args[nameFlag + 1] : path.basename(targetPath);
696
+
697
+ // Ensure ~/.orbital/ exists
698
+ if (!fs.existsSync(ORBITAL_HOME)) fs.mkdirSync(ORBITAL_HOME, { recursive: true });
699
+
700
+ // Check the project has been initialized
701
+ if (!fs.existsSync(path.join(targetPath, '.claude'))) {
702
+ console.error(`Error: ${targetPath} has not been initialized with Orbital Command.`);
703
+ console.error(`Run \`orbital init\` in that directory first.`);
704
+ process.exit(1);
705
+ }
706
+
707
+ const registry = loadRegistry();
708
+
709
+ // Check if already registered
710
+ if (registry.projects?.some(p => p.path === targetPath)) {
711
+ console.log(`Project already registered: ${targetPath}`);
712
+ return;
713
+ }
714
+
715
+ // Color palette
716
+ const COLORS = [
717
+ '210 80% 55%', '340 75% 55%', '160 60% 45%', '30 90% 55%',
718
+ '270 65% 55%', '50 85% 50%', '180 55% 45%', '0 70% 55%',
719
+ '120 50% 42%', '300 60% 50%', '200 70% 50%', '15 80% 55%',
720
+ ];
721
+ const usedColors = (registry.projects || []).map(p => p.color);
722
+ const color = COLORS.find(c => !usedColors.includes(c)) || COLORS[0];
723
+
724
+ // Generate slug
725
+ const baseSlug = path.basename(targetPath).toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '') || 'project';
726
+ const existingIds = (registry.projects || []).map(p => p.id);
727
+ const slug = existingIds.includes(baseSlug)
728
+ ? `${baseSlug}-${crypto.createHash('sha256').update(targetPath).digest('hex').slice(0, 4)}`
729
+ : baseSlug;
730
+
731
+ const project = {
732
+ id: slug,
733
+ path: targetPath,
734
+ name,
735
+ color,
736
+ registeredAt: new Date().toISOString(),
737
+ enabled: true,
738
+ };
739
+
740
+ if (!registry.projects) registry.projects = [];
741
+ registry.projects.push(project);
742
+ fs.writeFileSync(REGISTRY_PATH, JSON.stringify(registry, null, 2), 'utf8');
743
+
744
+ console.log(`Registered project: ${name}`);
745
+ console.log(` ID: ${slug}`);
746
+ console.log(` Path: ${targetPath}`);
747
+ console.log(` Color: ${color}`);
748
+ }
749
+
750
+ function cmdUnregister(args) {
751
+ const idOrPath = args[0];
752
+ if (!idOrPath) {
753
+ console.error('Usage: orbital unregister <id-or-path>');
754
+ process.exit(1);
755
+ }
756
+
757
+ const absPath = path.isAbsolute(idOrPath) ? idOrPath : path.resolve(idOrPath);
758
+ const registry = loadRegistry();
759
+ const idx = (registry.projects || []).findIndex(p => p.id === idOrPath || p.path === absPath);
760
+
761
+ if (idx === -1) {
762
+ console.error(`Project not found: ${idOrPath}`);
763
+ process.exit(1);
764
+ }
765
+
766
+ const removed = registry.projects.splice(idx, 1)[0];
767
+ fs.writeFileSync(REGISTRY_PATH, JSON.stringify(registry, null, 2), 'utf8');
768
+
769
+ console.log(`Unregistered project: ${removed.name} (${removed.id})`);
770
+ console.log(` Project files in ${removed.path} are preserved.`);
771
+ }
772
+
773
+ function cmdProjects() {
774
+ const registry = loadRegistry();
775
+ const projects = registry.projects || [];
776
+
777
+ if (projects.length === 0) {
778
+ console.log('\nNo projects registered.');
779
+ console.log('Use `orbital register` or `orbital init` to add a project.\n');
780
+ return;
781
+ }
782
+
783
+ console.log(`\n ${'ID'.padEnd(22)} ${'NAME'.padEnd(22)} ${'STATUS'.padEnd(10)} PATH`);
784
+ console.log(` ${'─'.repeat(22)} ${'─'.repeat(22)} ${'─'.repeat(10)} ${'─'.repeat(30)}`);
785
+ for (const p of projects) {
786
+ const status = p.enabled ? (fs.existsSync(p.path) ? 'active' : 'offline') : 'disabled';
787
+ console.log(` ${p.id.padEnd(22)} ${p.name.padEnd(22)} ${status.padEnd(10)} ${p.path}`);
788
+ }
789
+ console.log();
790
+ }
791
+
792
+ async function cmdConfig(args) {
793
+ const { runConfigEditor } = await loadWizardModule();
794
+ const projectRoot = detectProjectRoot();
795
+ const version = getPackageVersion();
796
+ await runConfigEditor(projectRoot, version, args);
797
+ }
798
+
799
+ async function cmdDoctor() {
800
+ const { runDoctor } = await loadWizardModule();
801
+ const projectRoot = detectProjectRoot();
802
+ const version = getPackageVersion();
803
+ await runDoctor(projectRoot, version);
282
804
  }
283
805
 
284
806
  function printHelp() {
@@ -289,29 +811,66 @@ Usage:
289
811
  orbital <command> [options]
290
812
 
291
813
  Commands:
292
- init Scaffold Orbital Command into the current project
293
- dev Start the development server (API + Vite)
294
- build Production build of the dashboard
814
+ init Set up Orbital Command (interactive wizard)
815
+ launch Start the dashboard
816
+ config Modify project settings interactively
817
+ doctor Health check and version diagnostics
818
+ update Sync templates and apply migrations
819
+ status Show template sync status
820
+
821
+ Aliases:
822
+ setup Same as init
823
+ dev Same as launch --vite (development with HMR)
824
+
825
+ Config Subcommands:
826
+ config show Print current config as JSON
827
+ config set <k> <v> Set a config value non-interactively
828
+
829
+ Template Management:
830
+ validate Check cross-references and consistency
831
+ pin <path> Lock a file from updates
832
+ unpin <path> Unlock a pinned file
833
+ pins List all pinned files
834
+ diff <path> Show diff between template and local file
835
+ reset <path> Restore a file from the current template
836
+
837
+ Project Management:
838
+ register [path] Register a project with the dashboard
839
+ unregister <id> Remove a project from the dashboard
840
+ projects List all registered projects
841
+
842
+ Other:
843
+ build Production build of the dashboard frontend
295
844
  emit <TYPE> <JSON> Emit an orbital event
296
- update Re-copy hooks/skills/agents from package templates
297
845
  uninstall Remove Orbital artifacts from the project
298
846
 
299
847
  Init Options:
300
- --force Overwrite existing hooks, skills, and agents
301
- --skip-plugins Skip plugin installation
302
- --yes, -y Auto-accept all prompts
848
+ --force Overwrite existing hooks, skills, and agents
849
+ --yes, -y Skip the wizard, use auto-detected defaults
850
+ --private Disable telemetry for this project
851
+ --preset <name> Workflow preset (default/minimal/development/gitflow)
852
+ --project-name <n> Override auto-detected project name
853
+ --server-port <n> Override default server port (4444)
854
+ --client-port <n> Override default client port (4445)
855
+
856
+ Launch Options:
857
+ --open Open the dashboard in the browser
858
+ --vite Force Vite dev server for HMR
859
+
860
+ Update Options:
861
+ --dry-run Preview changes without applying them
303
862
 
304
- Dev Options:
305
- --open Open the dashboard in the default browser
863
+ Uninstall Options:
864
+ --dry-run Preview removal without applying
865
+ --keep-config Keep orbital.config.json for re-initialization
306
866
 
307
867
  Examples:
308
868
  orbital init
309
- orbital init --force
310
- orbital dev
311
- orbital dev --open
312
- orbital emit SCOPE_TRANSITION '{"scope":"042","from":"implementing","to":"review"}'
313
- orbital update
314
- orbital uninstall
869
+ orbital launch --open
870
+ orbital config
871
+ orbital doctor
872
+ orbital update --dry-run
873
+ orbital status
315
874
  `);
316
875
  }
317
876
 
@@ -323,11 +882,30 @@ const [command, ...args] = process.argv.slice(2);
323
882
 
324
883
  async function main() {
325
884
  switch (command) {
885
+ case 'launch':
886
+ cmdLaunchOrDev(false);
887
+ break;
326
888
  case 'init':
889
+ case 'setup':
327
890
  await cmdInit(args);
328
891
  break;
892
+ case 'config':
893
+ await cmdConfig(args);
894
+ break;
895
+ case 'doctor':
896
+ await cmdDoctor();
897
+ break;
329
898
  case 'dev':
330
- cmdDev();
899
+ cmdLaunchOrDev(true);
900
+ break;
901
+ case 'register':
902
+ cmdRegister(args);
903
+ break;
904
+ case 'unregister':
905
+ cmdUnregister(args);
906
+ break;
907
+ case 'projects':
908
+ cmdProjects();
331
909
  break;
332
910
  case 'build':
333
911
  cmdBuild();
@@ -336,18 +914,63 @@ async function main() {
336
914
  cmdEmit(args);
337
915
  break;
338
916
  case 'update':
339
- await cmdUpdate();
917
+ await cmdUpdate(args);
340
918
  break;
341
919
  case 'uninstall':
342
- await cmdUninstall();
920
+ await cmdUninstall(args);
921
+ break;
922
+ case 'status':
923
+ await cmdStatus();
924
+ break;
925
+ case 'validate':
926
+ await cmdValidate();
927
+ break;
928
+ case 'pin':
929
+ await cmdPin(args);
930
+ break;
931
+ case 'unpin':
932
+ await cmdUnpin(args);
343
933
  break;
934
+ case 'pins':
935
+ await cmdPins();
936
+ break;
937
+ case 'diff':
938
+ await cmdDiff(args);
939
+ break;
940
+ case 'reset':
941
+ await cmdReset(args);
942
+ break;
943
+ case 'private': {
944
+ const registry = loadRegistry();
945
+ const enable = args[0] !== 'off';
946
+ registry.privateMode = enable;
947
+ if (!fs.existsSync(ORBITAL_HOME)) fs.mkdirSync(ORBITAL_HOME, { recursive: true });
948
+ fs.writeFileSync(REGISTRY_PATH, JSON.stringify(registry, null, 2), 'utf8');
949
+ console.log(`Private mode ${enable ? 'enabled' : 'disabled'} globally.`);
950
+
951
+ break;
952
+ }
344
953
  case 'help':
345
954
  case '--help':
346
955
  case '-h':
347
956
  printHelp();
348
957
  break;
349
958
  case undefined:
350
- printHelp();
959
+ if (process.stdout.isTTY && !process.env.CI) {
960
+ const wiz = await loadWizardModule();
961
+ const version = getPackageVersion();
962
+ if (!orbitalSetupDone()) {
963
+ // First time — run Phase 1 setup
964
+ await wiz.runSetupWizard(version);
965
+ } else {
966
+ // Already set up — run Phase 2 for current directory
967
+ const projectRoot = detectProjectRoot();
968
+ await wiz.runProjectSetup(projectRoot, version, []);
969
+ stampTemplateVersion(projectRoot);
970
+ }
971
+ } else {
972
+ printHelp();
973
+ }
351
974
  break;
352
975
  default:
353
976
  console.error(`Unknown command: ${command}`);