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.
- package/README.md +67 -42
- package/bin/commands/config.js +19 -0
- package/bin/commands/events.js +40 -0
- package/bin/commands/launch.js +126 -0
- package/bin/commands/manifest.js +283 -0
- package/bin/commands/registry.js +104 -0
- package/bin/commands/update.js +24 -0
- package/bin/lib/helpers.js +229 -0
- package/bin/orbital.js +147 -319
- package/dist/assets/Landing-CfQdHR0N.js +11 -0
- package/dist/assets/PrimitivesConfig-DThSipFy.js +32 -0
- package/dist/assets/QualityGates-B4kxM5UU.js +26 -0
- package/dist/assets/SessionTimeline-Bz1iZnmg.js +1 -0
- package/dist/assets/Settings-DLcZwbCT.js +12 -0
- package/dist/assets/SourceControl-BMNIz7Lt.js +36 -0
- package/dist/assets/WorkflowVisualizer-CxuSBOYu.js +69 -0
- package/dist/assets/arrow-down-DVPp6_qp.js +6 -0
- package/dist/assets/bot-NFaJBDn_.js +6 -0
- package/dist/assets/charts-LGLb8hyU.js +68 -0
- package/dist/assets/circle-x-IsFCkBZu.js +6 -0
- package/dist/assets/file-text-J1cebZXF.js +6 -0
- package/dist/assets/globe-WzeyHsUc.js +6 -0
- package/dist/assets/index-BdJ57EhC.css +1 -0
- package/dist/assets/index-o4ScMAuR.js +349 -0
- package/dist/assets/key-CKR8JJSj.js +6 -0
- package/dist/assets/minus-CHBsJyjp.js +6 -0
- package/dist/assets/radio-xqZaR-Uk.js +6 -0
- package/dist/assets/rocket-D_xvvNG6.js +6 -0
- package/dist/assets/shield-TdB1yv_a.js +6 -0
- package/dist/assets/ui-BmsSg9jU.js +53 -0
- package/dist/assets/useSocketListener-0L5yiN5i.js +1 -0
- package/dist/assets/useWorkflowEditor-CqeRWVQX.js +11 -0
- package/dist/assets/{vendor-Dzv9lrRc.js → vendor-Bqt8AJn2.js} +1 -1
- package/dist/assets/workflow-constants-Rw-GmgHZ.js +6 -0
- package/dist/assets/zap-C9wqYMpl.js +6 -0
- package/dist/favicon.svg +1 -0
- package/dist/index.html +6 -5
- package/dist/server/server/__tests__/data-routes.test.js +126 -0
- package/dist/server/server/__tests__/helpers/db.js +17 -0
- package/dist/server/server/__tests__/helpers/mock-emitter.js +8 -0
- package/dist/server/server/__tests__/scope-routes.test.js +138 -0
- package/dist/server/server/__tests__/sprint-routes.test.js +102 -0
- package/dist/server/server/__tests__/workflow-routes.test.js +107 -0
- package/dist/server/server/config-migrator.js +135 -0
- package/dist/server/server/config.js +51 -7
- package/dist/server/server/database.js +21 -28
- package/dist/server/server/global-config.js +143 -0
- package/dist/server/server/index.js +118 -276
- package/dist/server/server/init.js +243 -225
- package/dist/server/server/launch.js +29 -0
- package/dist/server/server/manifest-types.js +8 -0
- package/dist/server/server/manifest.js +454 -0
- package/dist/server/server/migrate-legacy.js +229 -0
- package/dist/server/server/parsers/event-parser.js +4 -1
- package/dist/server/server/parsers/event-parser.test.js +117 -0
- package/dist/server/server/parsers/scope-parser.js +74 -28
- package/dist/server/server/parsers/scope-parser.test.js +230 -0
- package/dist/server/server/project-context.js +265 -0
- package/dist/server/server/project-emitter.js +41 -0
- package/dist/server/server/project-manager.js +297 -0
- package/dist/server/server/routes/aggregate-routes.js +871 -0
- package/dist/server/server/routes/config-routes.js +41 -90
- package/dist/server/server/routes/data-routes.js +25 -123
- package/dist/server/server/routes/dispatch-routes.js +37 -15
- package/dist/server/server/routes/git-routes.js +74 -0
- package/dist/server/server/routes/manifest-routes.js +319 -0
- package/dist/server/server/routes/scope-routes.js +45 -28
- package/dist/server/server/routes/sync-routes.js +134 -0
- package/dist/server/server/routes/version-routes.js +1 -15
- package/dist/server/server/routes/workflow-routes.js +9 -3
- package/dist/server/server/schema.js +3 -0
- package/dist/server/server/services/batch-orchestrator.js +41 -17
- package/dist/server/server/services/claude-session-service.js +17 -14
- package/dist/server/server/services/config-service.js +10 -1
- package/dist/server/server/services/deploy-service.test.js +119 -0
- package/dist/server/server/services/event-service.js +64 -1
- package/dist/server/server/services/event-service.test.js +191 -0
- package/dist/server/server/services/gate-service.test.js +105 -0
- package/dist/server/server/services/git-service.js +108 -4
- package/dist/server/server/services/github-service.js +110 -2
- package/dist/server/server/services/readiness-service.test.js +190 -0
- package/dist/server/server/services/scope-cache.js +5 -1
- package/dist/server/server/services/scope-cache.test.js +142 -0
- package/dist/server/server/services/scope-service.js +222 -131
- package/dist/server/server/services/scope-service.test.js +137 -0
- package/dist/server/server/services/sprint-orchestrator.js +29 -15
- package/dist/server/server/services/sprint-service.js +23 -3
- package/dist/server/server/services/sprint-service.test.js +238 -0
- package/dist/server/server/services/sync-service.js +434 -0
- package/dist/server/server/services/sync-types.js +2 -0
- package/dist/server/server/services/workflow-service.js +26 -5
- package/dist/server/server/services/workflow-service.test.js +159 -0
- package/dist/server/server/settings-sync.js +284 -0
- package/dist/server/server/uninstall.js +195 -0
- package/dist/server/server/update-planner.js +279 -0
- package/dist/server/server/update.js +212 -0
- package/dist/server/server/utils/cc-hooks-parser.js +3 -0
- package/dist/server/server/utils/cc-hooks-parser.test.js +86 -0
- package/dist/server/server/utils/dispatch-utils.js +83 -24
- package/dist/server/server/utils/dispatch-utils.test.js +182 -0
- package/dist/server/server/utils/flag-builder.js +54 -0
- package/dist/server/server/utils/json-fields.js +14 -0
- package/dist/server/server/utils/json-fields.test.js +73 -0
- package/dist/server/server/utils/logger.js +37 -3
- package/dist/server/server/utils/package-info.js +30 -0
- package/dist/server/server/utils/route-helpers.js +47 -0
- package/dist/server/server/utils/route-helpers.test.js +115 -0
- package/dist/server/server/utils/terminal-launcher.js +79 -25
- package/dist/server/server/utils/worktree-manager.js +13 -4
- package/dist/server/server/validator.js +230 -0
- package/dist/server/server/watchers/event-watcher.js +28 -13
- package/dist/server/server/watchers/global-watcher.js +63 -0
- package/dist/server/server/watchers/scope-watcher.js +27 -12
- package/dist/server/server/wizard/config-editor.js +237 -0
- package/dist/server/server/wizard/detect.js +96 -0
- package/dist/server/server/wizard/doctor.js +115 -0
- package/dist/server/server/wizard/index.js +340 -0
- package/dist/server/server/wizard/phases/confirm.js +39 -0
- package/dist/server/server/wizard/phases/project-setup.js +90 -0
- package/dist/server/server/wizard/phases/setup-wizard.js +66 -0
- package/dist/server/server/wizard/phases/welcome.js +32 -0
- package/dist/server/server/wizard/phases/workflow-setup.js +22 -0
- package/dist/server/server/wizard/types.js +29 -0
- package/dist/server/server/wizard/ui.js +73 -0
- package/dist/server/shared/__fixtures__/workflow-configs.js +75 -0
- package/dist/server/shared/api-types.js +80 -1
- package/dist/server/shared/default-workflow.json +65 -0
- package/dist/server/shared/onboarding-tour.test.js +81 -0
- package/dist/server/shared/project-colors.js +24 -0
- package/dist/server/shared/workflow-config.test.js +84 -0
- package/dist/server/shared/workflow-engine.js +1 -1
- package/dist/server/shared/workflow-engine.test.js +302 -0
- package/dist/server/shared/workflow-normalizer.js +101 -0
- package/dist/server/shared/workflow-normalizer.test.js +100 -0
- package/dist/server/src/components/onboarding/tour-steps.js +84 -0
- package/package.json +34 -29
- package/schemas/orbital.config.schema.json +2 -5
- package/scripts/postinstall.js +18 -6
- package/scripts/release.sh +53 -0
- package/server/__tests__/data-routes.test.ts +151 -0
- package/server/__tests__/helpers/db.ts +19 -0
- package/server/__tests__/helpers/mock-emitter.ts +10 -0
- package/server/__tests__/scope-routes.test.ts +158 -0
- package/server/__tests__/sprint-routes.test.ts +118 -0
- package/server/__tests__/workflow-routes.test.ts +120 -0
- package/server/config-migrator.ts +160 -0
- package/server/config.ts +64 -12
- package/server/database.ts +22 -31
- package/server/global-config.ts +204 -0
- package/server/index.ts +139 -316
- package/server/init.ts +266 -234
- package/server/launch.ts +32 -0
- package/server/manifest-types.ts +145 -0
- package/server/manifest.ts +494 -0
- package/server/migrate-legacy.ts +290 -0
- package/server/parsers/event-parser.test.ts +135 -0
- package/server/parsers/event-parser.ts +4 -1
- package/server/parsers/scope-parser.test.ts +270 -0
- package/server/parsers/scope-parser.ts +79 -31
- package/server/project-context.ts +325 -0
- package/server/project-emitter.ts +50 -0
- package/server/project-manager.ts +368 -0
- package/server/routes/aggregate-routes.ts +968 -0
- package/server/routes/config-routes.ts +43 -85
- package/server/routes/data-routes.ts +34 -156
- package/server/routes/dispatch-routes.ts +46 -17
- package/server/routes/git-routes.ts +77 -0
- package/server/routes/manifest-routes.ts +388 -0
- package/server/routes/scope-routes.ts +39 -30
- package/server/routes/sync-routes.ts +175 -0
- package/server/routes/version-routes.ts +1 -16
- package/server/routes/workflow-routes.ts +9 -3
- package/server/schema.ts +3 -0
- package/server/services/batch-orchestrator.ts +41 -17
- package/server/services/claude-session-service.ts +16 -14
- package/server/services/config-service.ts +10 -1
- package/server/services/deploy-service.test.ts +145 -0
- package/server/services/deploy-service.ts +2 -2
- package/server/services/event-service.test.ts +242 -0
- package/server/services/event-service.ts +92 -3
- package/server/services/gate-service.test.ts +131 -0
- package/server/services/gate-service.ts +2 -2
- package/server/services/git-service.ts +137 -4
- package/server/services/github-service.ts +120 -2
- package/server/services/readiness-service.test.ts +217 -0
- package/server/services/scope-cache.test.ts +167 -0
- package/server/services/scope-cache.ts +4 -1
- package/server/services/scope-service.test.ts +169 -0
- package/server/services/scope-service.ts +224 -130
- package/server/services/sprint-orchestrator.ts +30 -15
- package/server/services/sprint-service.test.ts +271 -0
- package/server/services/sprint-service.ts +29 -5
- package/server/services/sync-service.ts +482 -0
- package/server/services/sync-types.ts +77 -0
- package/server/services/workflow-service.test.ts +190 -0
- package/server/services/workflow-service.ts +29 -9
- package/server/settings-sync.ts +359 -0
- package/server/uninstall.ts +214 -0
- package/server/update-planner.ts +346 -0
- package/server/update.ts +263 -0
- package/server/utils/cc-hooks-parser.test.ts +96 -0
- package/server/utils/cc-hooks-parser.ts +4 -0
- package/server/utils/dispatch-utils.test.ts +245 -0
- package/server/utils/dispatch-utils.ts +102 -30
- package/server/utils/flag-builder.ts +56 -0
- package/server/utils/json-fields.test.ts +83 -0
- package/server/utils/json-fields.ts +14 -0
- package/server/utils/logger.ts +40 -3
- package/server/utils/package-info.ts +32 -0
- package/server/utils/route-helpers.test.ts +144 -0
- package/server/utils/route-helpers.ts +50 -0
- package/server/utils/terminal-launcher.ts +85 -25
- package/server/utils/worktree-manager.ts +9 -4
- package/server/validator.ts +270 -0
- package/server/watchers/event-watcher.ts +24 -12
- package/server/watchers/global-watcher.ts +77 -0
- package/server/watchers/scope-watcher.ts +21 -9
- package/server/wizard/config-editor.ts +248 -0
- package/server/wizard/detect.ts +104 -0
- package/server/wizard/doctor.ts +114 -0
- package/server/wizard/index.ts +438 -0
- package/server/wizard/phases/confirm.ts +45 -0
- package/server/wizard/phases/project-setup.ts +106 -0
- package/server/wizard/phases/setup-wizard.ts +78 -0
- package/server/wizard/phases/welcome.ts +39 -0
- package/server/wizard/phases/workflow-setup.ts +28 -0
- package/server/wizard/types.ts +56 -0
- package/server/wizard/ui.ts +92 -0
- package/shared/__fixtures__/workflow-configs.ts +80 -0
- package/shared/api-types.ts +106 -0
- package/shared/onboarding-tour.test.ts +94 -0
- package/shared/project-colors.ts +24 -0
- package/shared/workflow-config.test.ts +111 -0
- package/shared/workflow-config.ts +7 -0
- package/shared/workflow-engine.test.ts +388 -0
- package/shared/workflow-engine.ts +1 -1
- package/shared/workflow-normalizer.test.ts +119 -0
- package/shared/workflow-normalizer.ts +118 -0
- package/templates/agents/QUICK-REFERENCE.md +1 -0
- package/templates/agents/README.md +1 -0
- package/templates/agents/SKILL-TRIGGERS.md +11 -0
- package/templates/agents/green-team/deep-dive.md +361 -0
- package/templates/hooks/end-session.sh +4 -1
- package/templates/hooks/init-session.sh +1 -0
- package/templates/hooks/orbital-emit.sh +2 -2
- package/templates/hooks/orbital-report-deploy.sh +4 -4
- package/templates/hooks/orbital-report-gates.sh +4 -4
- package/templates/hooks/orbital-scope-update.sh +1 -1
- package/templates/hooks/scope-commit-logger.sh +2 -2
- package/templates/hooks/scope-create-cleanup.sh +2 -2
- package/templates/hooks/scope-create-gate.sh +2 -5
- package/templates/hooks/scope-gate.sh +4 -6
- package/templates/hooks/scope-helpers.sh +28 -1
- package/templates/hooks/scope-lifecycle-gate.sh +14 -5
- package/templates/hooks/scope-prepare.sh +67 -12
- package/templates/hooks/scope-transition.sh +14 -6
- package/templates/hooks/time-tracker.sh +2 -5
- package/templates/migrations/renames.json +1 -0
- package/templates/orbital.config.json +8 -6
- package/{shared/default-workflow.json → templates/presets/default.json} +65 -0
- package/templates/presets/development.json +4 -4
- package/templates/presets/gitflow.json +7 -0
- package/templates/prompts/README.md +23 -0
- package/templates/prompts/deep-dive-audit.md +94 -0
- package/templates/quick/rules.md +56 -5
- package/templates/settings-hooks.json +1 -1
- package/templates/skills/git-commit/SKILL.md +27 -7
- package/templates/skills/git-dev/SKILL.md +13 -4
- package/templates/skills/git-main/SKILL.md +13 -3
- package/templates/skills/git-production/SKILL.md +9 -2
- package/templates/skills/git-staging/SKILL.md +11 -3
- package/templates/skills/scope-create/SKILL.md +17 -3
- package/templates/skills/scope-fix-review/SKILL.md +14 -7
- package/templates/skills/scope-implement/SKILL.md +15 -4
- package/templates/skills/scope-post-review/SKILL.md +77 -7
- package/templates/skills/scope-pre-review/SKILL.md +11 -4
- package/templates/skills/scope-verify/SKILL.md +5 -3
- package/templates/skills/test-code-review/SKILL.md +41 -33
- package/templates/skills/test-scaffold/SKILL.md +222 -0
- package/dist/assets/WorkflowVisualizer-BZ21PIIF.js +0 -84
- package/dist/assets/charts-D__PA1zp.js +0 -72
- package/dist/assets/index-D1G6i0nS.css +0 -1
- package/dist/assets/index-DpItvKpf.js +0 -419
- package/dist/assets/ui-BvF022GT.js +0 -53
- package/index.html +0 -15
- package/postcss.config.js +0 -6
- package/src/App.tsx +0 -33
- package/src/components/AgentBadge.tsx +0 -40
- package/src/components/BatchPreflightModal.tsx +0 -115
- package/src/components/CardDisplayToggle.tsx +0 -74
- package/src/components/ColumnHeaderActions.tsx +0 -55
- package/src/components/ColumnMenu.tsx +0 -99
- package/src/components/DeployHistory.tsx +0 -141
- package/src/components/DispatchModal.tsx +0 -164
- package/src/components/DispatchPopover.tsx +0 -139
- package/src/components/DragOverlay.tsx +0 -25
- package/src/components/DriftSidebar.tsx +0 -140
- package/src/components/EnvironmentStrip.tsx +0 -88
- package/src/components/ErrorBoundary.tsx +0 -62
- package/src/components/FilterChip.tsx +0 -105
- package/src/components/GateIndicator.tsx +0 -33
- package/src/components/IdeaDetailModal.tsx +0 -190
- package/src/components/IdeaFormDialog.tsx +0 -113
- package/src/components/KanbanColumn.tsx +0 -201
- package/src/components/MarkdownRenderer.tsx +0 -114
- package/src/components/NeonGrid.tsx +0 -128
- package/src/components/PromotionQueue.tsx +0 -89
- package/src/components/ScopeCard.tsx +0 -234
- package/src/components/ScopeDetailModal.tsx +0 -255
- package/src/components/ScopeFilterBar.tsx +0 -152
- package/src/components/SearchInput.tsx +0 -102
- package/src/components/SessionPanel.tsx +0 -335
- package/src/components/SprintContainer.tsx +0 -303
- package/src/components/SprintDependencyDialog.tsx +0 -78
- package/src/components/SprintPreflightModal.tsx +0 -138
- package/src/components/StatusBar.tsx +0 -168
- package/src/components/SwimCell.tsx +0 -67
- package/src/components/SwimLaneRow.tsx +0 -94
- package/src/components/SwimlaneBoardView.tsx +0 -108
- package/src/components/VersionBadge.tsx +0 -139
- package/src/components/ViewModeSelector.tsx +0 -114
- package/src/components/config/AgentChip.tsx +0 -53
- package/src/components/config/AgentCreateDialog.tsx +0 -321
- package/src/components/config/AgentEditor.tsx +0 -175
- package/src/components/config/DirectoryTree.tsx +0 -582
- package/src/components/config/FileEditor.tsx +0 -550
- package/src/components/config/HookChip.tsx +0 -50
- package/src/components/config/StageCard.tsx +0 -198
- package/src/components/config/TransitionZone.tsx +0 -173
- package/src/components/config/UnifiedWorkflowPipeline.tsx +0 -216
- package/src/components/config/WorkflowPipeline.tsx +0 -161
- package/src/components/source-control/BranchList.tsx +0 -93
- package/src/components/source-control/BranchPanel.tsx +0 -105
- package/src/components/source-control/CommitLog.tsx +0 -100
- package/src/components/source-control/CommitRow.tsx +0 -47
- package/src/components/source-control/GitHubPanel.tsx +0 -110
- package/src/components/source-control/GitHubSetupGuide.tsx +0 -52
- package/src/components/source-control/GitOverviewBar.tsx +0 -101
- package/src/components/source-control/PullRequestList.tsx +0 -69
- package/src/components/source-control/WorktreeList.tsx +0 -80
- package/src/components/ui/badge.tsx +0 -41
- package/src/components/ui/button.tsx +0 -55
- package/src/components/ui/card.tsx +0 -78
- package/src/components/ui/dialog.tsx +0 -94
- package/src/components/ui/popover.tsx +0 -33
- package/src/components/ui/scroll-area.tsx +0 -54
- package/src/components/ui/separator.tsx +0 -28
- package/src/components/ui/tabs.tsx +0 -52
- package/src/components/ui/toggle-switch.tsx +0 -35
- package/src/components/ui/tooltip.tsx +0 -27
- package/src/components/workflow/AddEdgeDialog.tsx +0 -217
- package/src/components/workflow/AddListDialog.tsx +0 -201
- package/src/components/workflow/ChecklistEditor.tsx +0 -239
- package/src/components/workflow/CommandPrefixManager.tsx +0 -118
- package/src/components/workflow/ConfigSettingsPanel.tsx +0 -189
- package/src/components/workflow/DirectionSelector.tsx +0 -133
- package/src/components/workflow/DispatchConfigPanel.tsx +0 -180
- package/src/components/workflow/EdgeDetailPanel.tsx +0 -236
- package/src/components/workflow/EdgePropertyEditor.tsx +0 -251
- package/src/components/workflow/EditToolbar.tsx +0 -138
- package/src/components/workflow/HookDetailPanel.tsx +0 -250
- package/src/components/workflow/HookExecutionLog.tsx +0 -24
- package/src/components/workflow/HookSourceModal.tsx +0 -129
- package/src/components/workflow/HooksDashboard.tsx +0 -363
- package/src/components/workflow/ListPropertyEditor.tsx +0 -251
- package/src/components/workflow/MigrationPreviewDialog.tsx +0 -237
- package/src/components/workflow/MovementRulesPanel.tsx +0 -188
- package/src/components/workflow/NodeDetailPanel.tsx +0 -245
- package/src/components/workflow/PresetSelector.tsx +0 -414
- package/src/components/workflow/SkillCommandBuilder.tsx +0 -174
- package/src/components/workflow/WorkflowEdgeComponent.tsx +0 -145
- package/src/components/workflow/WorkflowNode.tsx +0 -147
- package/src/components/workflow/graphLayout.ts +0 -186
- package/src/components/workflow/mergeHooks.ts +0 -85
- package/src/components/workflow/useEditHistory.ts +0 -88
- package/src/components/workflow/useWorkflowEditor.ts +0 -262
- package/src/components/workflow/validateConfig.ts +0 -70
- package/src/hooks/useActiveDispatches.ts +0 -198
- package/src/hooks/useBoardSettings.ts +0 -170
- package/src/hooks/useCardDisplay.ts +0 -57
- package/src/hooks/useCcHooks.ts +0 -24
- package/src/hooks/useConfigTree.ts +0 -51
- package/src/hooks/useEnforcementRules.ts +0 -46
- package/src/hooks/useEvents.ts +0 -59
- package/src/hooks/useFileEditor.ts +0 -165
- package/src/hooks/useGates.ts +0 -57
- package/src/hooks/useIdeaActions.ts +0 -53
- package/src/hooks/useKanbanDnd.ts +0 -410
- package/src/hooks/useOrbitalConfig.ts +0 -54
- package/src/hooks/usePipeline.ts +0 -47
- package/src/hooks/usePipelineData.ts +0 -338
- package/src/hooks/useReconnect.ts +0 -25
- package/src/hooks/useScopeFilters.ts +0 -125
- package/src/hooks/useScopeSessions.ts +0 -44
- package/src/hooks/useScopes.ts +0 -67
- package/src/hooks/useSearch.ts +0 -67
- package/src/hooks/useSettings.tsx +0 -187
- package/src/hooks/useSocket.ts +0 -25
- package/src/hooks/useSourceControl.ts +0 -105
- package/src/hooks/useSprintPreflight.ts +0 -55
- package/src/hooks/useSprints.ts +0 -154
- package/src/hooks/useStatusBarHighlight.ts +0 -18
- package/src/hooks/useSwimlaneBoardSettings.ts +0 -104
- package/src/hooks/useTheme.ts +0 -9
- package/src/hooks/useTransitionReadiness.ts +0 -53
- package/src/hooks/useVersion.ts +0 -155
- package/src/hooks/useViolations.ts +0 -65
- package/src/hooks/useWorkflow.tsx +0 -125
- package/src/hooks/useZoomModifier.ts +0 -19
- package/src/index.css +0 -797
- package/src/layouts/DashboardLayout.tsx +0 -113
- package/src/lib/collisionDetection.ts +0 -20
- package/src/lib/scope-fields.ts +0 -61
- package/src/lib/swimlane.ts +0 -146
- package/src/lib/utils.ts +0 -15
- package/src/main.tsx +0 -19
- package/src/socket.ts +0 -11
- package/src/types/index.ts +0 -497
- package/src/views/AgentFeed.tsx +0 -339
- package/src/views/DeployPipeline.tsx +0 -59
- package/src/views/EnforcementView.tsx +0 -378
- package/src/views/PrimitivesConfig.tsx +0 -500
- package/src/views/QualityGates.tsx +0 -1012
- package/src/views/ScopeBoard.tsx +0 -454
- package/src/views/SessionTimeline.tsx +0 -516
- package/src/views/Settings.tsx +0 -183
- package/src/views/SourceControl.tsx +0 -95
- package/src/views/WorkflowVisualizer.tsx +0 -382
- package/tailwind.config.js +0 -161
- package/tsconfig.json +0 -25
- package/vite.config.ts +0 -38
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive CLI wizard — main orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Entry points:
|
|
5
|
+
* runSetupWizard() — Phase 1: first-time Orbital setup (~/.orbital/)
|
|
6
|
+
* runProjectSetup() — Phase 2: per-project scaffolding (.claude/)
|
|
7
|
+
* runConfigEditor() — interactive config editor (orbital config)
|
|
8
|
+
* runDoctor() — health diagnostics (orbital doctor)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { spawn, execFileSync } from 'child_process';
|
|
14
|
+
import * as p from '@clack/prompts';
|
|
15
|
+
import pc from 'picocolors';
|
|
16
|
+
import { buildSetupState, buildProjectState } from './detect.js';
|
|
17
|
+
import { phaseSetupWizard } from './phases/setup-wizard.js';
|
|
18
|
+
import { phaseWelcome } from './phases/welcome.js';
|
|
19
|
+
import { phaseProjectSetup } from './phases/project-setup.js';
|
|
20
|
+
import { phaseWorkflowSetup } from './phases/workflow-setup.js';
|
|
21
|
+
import { phaseConfirm, showPostInstall } from './phases/confirm.js';
|
|
22
|
+
import { NOTES } from './ui.js';
|
|
23
|
+
import { runConfigEditor } from './config-editor.js';
|
|
24
|
+
import { runDoctor } from './doctor.js';
|
|
25
|
+
import { isITerm2Available } from '../adapters/iterm2-adapter.js';
|
|
26
|
+
import { registerProject } from '../global-config.js';
|
|
27
|
+
|
|
28
|
+
export { runConfigEditor, runDoctor };
|
|
29
|
+
|
|
30
|
+
// ─── Phase 1: Setup Wizard ─────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* First-time setup. Creates ~/.orbital/, seeds primitives,
|
|
34
|
+
* optionally links projects (running Phase 2 for each).
|
|
35
|
+
*/
|
|
36
|
+
export async function runSetupWizard(packageVersion: string): Promise<void> {
|
|
37
|
+
const state = buildSetupState(packageVersion);
|
|
38
|
+
|
|
39
|
+
p.intro(`${pc.bgCyan(pc.black(' Orbital Command '))} ${pc.dim(`v${packageVersion}`)}`);
|
|
40
|
+
|
|
41
|
+
await phaseSetupWizard(state);
|
|
42
|
+
|
|
43
|
+
// If user linked projects, run Phase 2 for each
|
|
44
|
+
for (const projectRoot of state.linkedProjects) {
|
|
45
|
+
p.log.step(`Setting up ${pc.cyan(path.basename(projectRoot))}...`);
|
|
46
|
+
await runProjectSetupInline(projectRoot, packageVersion);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (state.linkedProjects.length === 0) {
|
|
50
|
+
p.note(NOTES.setupComplete, 'Done');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
p.outro(
|
|
54
|
+
state.linkedProjects.length > 0
|
|
55
|
+
? `Run ${pc.cyan('orbital')} to launch the dashboard.`
|
|
56
|
+
: `Run ${pc.cyan('orbital')} in a project directory to get started.`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ─── Phase 2: Project Setup ────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Per-project setup. Walks through name, commands, workflow, then
|
|
64
|
+
* calls runInit() to scaffold files into .claude/.
|
|
65
|
+
*/
|
|
66
|
+
export async function runProjectSetup(projectRoot: string, packageVersion: string, args: string[]): Promise<void> {
|
|
67
|
+
const state = buildProjectState(projectRoot, packageVersion);
|
|
68
|
+
const force = args.includes('--force');
|
|
69
|
+
|
|
70
|
+
p.intro(`${pc.bgCyan(pc.black(' Orbital Command '))} ${pc.dim(`v${packageVersion}`)}`);
|
|
71
|
+
|
|
72
|
+
// Welcome gate: detect re-init / reconfigure
|
|
73
|
+
const forceFromWelcome = await phaseWelcome(state);
|
|
74
|
+
const useForce = force || forceFromWelcome;
|
|
75
|
+
|
|
76
|
+
await runProjectPhases(state, useForce);
|
|
77
|
+
|
|
78
|
+
p.outro(`Run ${pc.cyan('orbital')} to launch the dashboard.`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ─── Shared project phases (used by both flows) ────────────────
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Run the project setup phases and install. Used by both
|
|
85
|
+
* standalone runProjectSetup() and inline from runSetupWizard().
|
|
86
|
+
*/
|
|
87
|
+
async function runProjectPhases(state: ReturnType<typeof buildProjectState>, useForce: boolean): Promise<void> {
|
|
88
|
+
await phaseProjectSetup(state);
|
|
89
|
+
await phaseWorkflowSetup(state);
|
|
90
|
+
await phaseConfirm(state);
|
|
91
|
+
|
|
92
|
+
// Install
|
|
93
|
+
const s = p.spinner();
|
|
94
|
+
s.start('Installing into project...');
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const { runInit } = await import('../init.js');
|
|
98
|
+
|
|
99
|
+
runInit(state.projectRoot, {
|
|
100
|
+
force: useForce,
|
|
101
|
+
quiet: true,
|
|
102
|
+
preset: state.workflowPreset,
|
|
103
|
+
projectName: state.projectName,
|
|
104
|
+
serverPort: state.serverPort,
|
|
105
|
+
clientPort: state.clientPort,
|
|
106
|
+
commands: state.selectedCommands,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
registerProject(state.projectRoot, { name: state.projectName });
|
|
110
|
+
stampTemplateVersion(state.projectRoot, state.packageVersion);
|
|
111
|
+
|
|
112
|
+
s.stop('Project ready.');
|
|
113
|
+
} catch (err) {
|
|
114
|
+
s.stop('Installation failed.');
|
|
115
|
+
p.log.error(err instanceof Error ? err.message : String(err));
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
showPostInstall(state);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Inline project setup — called from Phase 1 when user links a project.
|
|
124
|
+
* Skips intro/outro since the setup wizard already has those.
|
|
125
|
+
*/
|
|
126
|
+
async function runProjectSetupInline(projectRoot: string, packageVersion: string): Promise<void> {
|
|
127
|
+
const state = buildProjectState(projectRoot, packageVersion);
|
|
128
|
+
|
|
129
|
+
// Skip welcome gate for inline — this is a fresh project being linked
|
|
130
|
+
await runProjectPhases(state, false);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ─── Update Check ─────────────────────────────────────────────
|
|
134
|
+
|
|
135
|
+
interface UpdateInfo {
|
|
136
|
+
current: string;
|
|
137
|
+
latest: string;
|
|
138
|
+
isOutdated: boolean;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function checkForUpdate(
|
|
142
|
+
currentVersion: string,
|
|
143
|
+
cache: { lastUpdateCheck?: string; latestVersion?: string },
|
|
144
|
+
): Promise<{ info: UpdateInfo | null; cacheChanged: boolean }> {
|
|
145
|
+
// Use cache if checked within 24 hours
|
|
146
|
+
if (cache.lastUpdateCheck && cache.latestVersion) {
|
|
147
|
+
const age = Date.now() - new Date(cache.lastUpdateCheck).getTime();
|
|
148
|
+
if (age < 24 * 60 * 60 * 1000) {
|
|
149
|
+
const isOutdated = cache.latestVersion !== currentVersion;
|
|
150
|
+
return {
|
|
151
|
+
info: { current: currentVersion, latest: cache.latestVersion, isOutdated },
|
|
152
|
+
cacheChanged: false,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Fetch from npm registry
|
|
158
|
+
try {
|
|
159
|
+
const res = await fetch('https://registry.npmjs.org/orbital-command/latest', {
|
|
160
|
+
signal: AbortSignal.timeout(3000),
|
|
161
|
+
});
|
|
162
|
+
const data = await res.json() as { version: string };
|
|
163
|
+
const latest = data.version;
|
|
164
|
+
return {
|
|
165
|
+
info: { current: currentVersion, latest, isOutdated: latest !== currentVersion },
|
|
166
|
+
cacheChanged: true,
|
|
167
|
+
};
|
|
168
|
+
} catch {
|
|
169
|
+
return { info: null, cacheChanged: false };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ─── Hub Menu ─────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
export type HubAction = 'launch' | 'init' | 'config' | 'doctor' | 'update' | 'status' | 'reset';
|
|
176
|
+
|
|
177
|
+
export interface HubResult {
|
|
178
|
+
action: HubAction;
|
|
179
|
+
setItermPromptShown?: boolean;
|
|
180
|
+
updateCache?: { lastUpdateCheck: string; latestVersion?: string };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Context-aware hub menu — the main entry point for `orbital` (no args).
|
|
185
|
+
* Checks for updates, offers template sync, shows iTerm2 recommendation, then menu.
|
|
186
|
+
*/
|
|
187
|
+
export async function runHub(opts: {
|
|
188
|
+
packageVersion: string;
|
|
189
|
+
isProjectInitialized: boolean;
|
|
190
|
+
projectNames: string[];
|
|
191
|
+
itermPromptShown: boolean;
|
|
192
|
+
isMac: boolean;
|
|
193
|
+
lastUpdateCheck?: string;
|
|
194
|
+
latestVersion?: string;
|
|
195
|
+
projectPaths: Array<{ name: string; path: string }>;
|
|
196
|
+
}): Promise<HubResult> {
|
|
197
|
+
const result: HubResult = { action: 'launch' };
|
|
198
|
+
|
|
199
|
+
p.intro(`${pc.bgCyan(pc.black(' Orbital Command '))} ${pc.dim(`v${opts.packageVersion}`)}`);
|
|
200
|
+
|
|
201
|
+
// ── Update check ──
|
|
202
|
+
const updateCheck = await checkForUpdate(opts.packageVersion, {
|
|
203
|
+
lastUpdateCheck: opts.lastUpdateCheck,
|
|
204
|
+
latestVersion: opts.latestVersion,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
if (updateCheck.cacheChanged) {
|
|
208
|
+
result.updateCache = {
|
|
209
|
+
lastUpdateCheck: new Date().toISOString(),
|
|
210
|
+
latestVersion: updateCheck.info?.latest,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (updateCheck.info?.isOutdated) {
|
|
215
|
+
p.log.info(
|
|
216
|
+
`Update available: ${pc.dim(`v${updateCheck.info.current}`)} → ${pc.cyan(`v${updateCheck.info.latest}`)}`
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const updateChoice = await p.select({
|
|
220
|
+
message: 'Update Orbital Command now?',
|
|
221
|
+
options: [
|
|
222
|
+
{ value: 'update', label: 'Yes, update' },
|
|
223
|
+
{ value: 'skip', label: 'Skip for now' },
|
|
224
|
+
],
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
if (!p.isCancel(updateChoice) && updateChoice === 'update') {
|
|
228
|
+
const s = p.spinner();
|
|
229
|
+
s.start('Updating Orbital Command...');
|
|
230
|
+
try {
|
|
231
|
+
execFileSync('npm', ['update', '-g', 'orbital-command'], { stdio: 'pipe', timeout: 60000 });
|
|
232
|
+
s.stop(`Updated to v${updateCheck.info.latest}!`);
|
|
233
|
+
p.outro(`Run ${pc.cyan('orbital')} again to use the new version.`);
|
|
234
|
+
process.exit(0);
|
|
235
|
+
} catch (err) {
|
|
236
|
+
s.stop('Update failed.');
|
|
237
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
238
|
+
if (msg.includes('EACCES') || msg.includes('permission denied')) {
|
|
239
|
+
p.log.error('Permission denied. Try running with sudo or ensure npm is installed via nvm.');
|
|
240
|
+
} else if (msg.includes('ETIMEDOUT') || msg.includes('timeout')) {
|
|
241
|
+
p.log.error('Update timed out. Check your network connection and try again.');
|
|
242
|
+
} else {
|
|
243
|
+
p.log.error(msg);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ── Template staleness check ──
|
|
250
|
+
if (opts.projectPaths.length > 0) {
|
|
251
|
+
const mod = await import('../manifest.js');
|
|
252
|
+
const initMod = await import('../init.js');
|
|
253
|
+
const outdatedProjects: Array<{
|
|
254
|
+
name: string;
|
|
255
|
+
path: string;
|
|
256
|
+
details: string[];
|
|
257
|
+
}> = [];
|
|
258
|
+
|
|
259
|
+
for (const proj of opts.projectPaths) {
|
|
260
|
+
if (!fs.existsSync(proj.path)) {
|
|
261
|
+
p.log.warn(`${proj.name}: project path not found (${proj.path})`);
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
const manifest = mod.loadManifest(proj.path);
|
|
265
|
+
if (!manifest) continue;
|
|
266
|
+
const claudeDir = path.join(proj.path, '.claude');
|
|
267
|
+
mod.refreshFileStatuses(manifest, claudeDir);
|
|
268
|
+
const summary = mod.summarizeManifest(manifest);
|
|
269
|
+
const parts = Object.entries(summary.byType)
|
|
270
|
+
.filter(([, counts]) => counts.outdated > 0)
|
|
271
|
+
.map(([type, counts]) => `${counts.outdated} ${type}`);
|
|
272
|
+
if (parts.length > 0) {
|
|
273
|
+
outdatedProjects.push({ name: proj.name, path: proj.path, details: parts });
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (outdatedProjects.length > 0) {
|
|
278
|
+
const lines = outdatedProjects.map(proj =>
|
|
279
|
+
` ${pc.cyan(proj.name.padEnd(16))} ${proj.details.join(', ')} outdated`
|
|
280
|
+
);
|
|
281
|
+
const count = outdatedProjects.length;
|
|
282
|
+
p.note(lines.join('\n'), `${count} project${count > 1 ? 's have' : ' has'} outdated templates`);
|
|
283
|
+
|
|
284
|
+
const syncChoice = await p.select({
|
|
285
|
+
message: 'Update project templates now?',
|
|
286
|
+
options: [
|
|
287
|
+
{ value: 'update', label: 'Yes, update all safe files', hint: 'skips modified and pinned' },
|
|
288
|
+
{ value: 'skip', label: 'Skip for now' },
|
|
289
|
+
],
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
if (!p.isCancel(syncChoice) && syncChoice === 'update') {
|
|
293
|
+
for (const proj of outdatedProjects) {
|
|
294
|
+
const s = p.spinner();
|
|
295
|
+
s.start(`Updating ${proj.name}...`);
|
|
296
|
+
try {
|
|
297
|
+
initMod.runUpdate(proj.path, { dryRun: false });
|
|
298
|
+
s.stop(`${proj.name} updated.`);
|
|
299
|
+
} catch (err) {
|
|
300
|
+
s.stop(`${proj.name} failed.`);
|
|
301
|
+
p.log.warn(err instanceof Error ? err.message : String(err));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ── iTerm2 recommendation (macOS only, one-time) ──
|
|
309
|
+
if (opts.isMac && !opts.itermPromptShown && !isITerm2Available()) {
|
|
310
|
+
p.note(
|
|
311
|
+
`Sprint dispatch, batch orchestration, and session management\n` +
|
|
312
|
+
`use iTerm2 tabs to run parallel Claude Code sessions.\n` +
|
|
313
|
+
`Without it, sessions fall back to basic subprocess mode.`,
|
|
314
|
+
'iTerm2 Recommended',
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
const itermChoice = await p.select({
|
|
318
|
+
message: 'Install iTerm2?',
|
|
319
|
+
options: [
|
|
320
|
+
{ value: 'install', label: 'Open download page', hint: 'https://iterm2.com' },
|
|
321
|
+
{ value: 'skip', label: 'Skip for now' },
|
|
322
|
+
],
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
result.setItermPromptShown = true;
|
|
326
|
+
|
|
327
|
+
if (!p.isCancel(itermChoice) && itermChoice === 'install') {
|
|
328
|
+
spawn('open', ['https://iterm2.com'], { detached: true, stdio: 'ignore' }).unref();
|
|
329
|
+
p.log.info('Waiting for iTerm2 to install... (press any key to skip)');
|
|
330
|
+
|
|
331
|
+
await new Promise<void>((resolve) => {
|
|
332
|
+
let done = false;
|
|
333
|
+
const cleanup = (): void => {
|
|
334
|
+
if (done) return;
|
|
335
|
+
done = true;
|
|
336
|
+
process.stdin.setRawMode?.(false);
|
|
337
|
+
process.stdin.removeListener('data', onKey);
|
|
338
|
+
process.stdin.pause();
|
|
339
|
+
clearInterval(timer);
|
|
340
|
+
resolve();
|
|
341
|
+
};
|
|
342
|
+
const onKey = (): void => { cleanup(); };
|
|
343
|
+
const startTime = Date.now();
|
|
344
|
+
const MAX_WAIT = 10 * 60 * 1000; // 10 minutes
|
|
345
|
+
const timer = setInterval(() => {
|
|
346
|
+
if (isITerm2Available()) {
|
|
347
|
+
p.log.success('iTerm2 detected!');
|
|
348
|
+
cleanup();
|
|
349
|
+
} else if (Date.now() - startTime > MAX_WAIT) {
|
|
350
|
+
cleanup();
|
|
351
|
+
}
|
|
352
|
+
}, 3000);
|
|
353
|
+
process.stdin.setRawMode?.(true);
|
|
354
|
+
process.stdin.resume();
|
|
355
|
+
process.stdin.on('data', onKey);
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// ── Build menu options based on project state ──
|
|
361
|
+
const projectHint = opts.projectNames.length > 0
|
|
362
|
+
? pc.dim(` (${opts.projectNames.join(', ')})`)
|
|
363
|
+
: '';
|
|
364
|
+
|
|
365
|
+
const options: Array<{ value: HubAction; label: string; hint?: string }> = [];
|
|
366
|
+
|
|
367
|
+
if (opts.isProjectInitialized) {
|
|
368
|
+
options.push(
|
|
369
|
+
{ value: 'launch', label: `Launch dashboard${projectHint}` },
|
|
370
|
+
{ value: 'config', label: 'Config', hint: 'modify project settings' },
|
|
371
|
+
{ value: 'doctor', label: 'Doctor', hint: 'health check & diagnostics' },
|
|
372
|
+
{ value: 'update', label: 'Update templates', hint: 'sync to latest' },
|
|
373
|
+
{ value: 'status', label: 'Status', hint: 'template sync status' },
|
|
374
|
+
{ value: 'reset', label: 'Reset to defaults', hint: 'force-reset all templates' },
|
|
375
|
+
);
|
|
376
|
+
} else {
|
|
377
|
+
options.push(
|
|
378
|
+
{ value: 'init', label: 'Initialize this project' },
|
|
379
|
+
{ value: 'launch', label: `Launch dashboard${projectHint}` },
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const action = await p.select({
|
|
384
|
+
message: 'What would you like to do?',
|
|
385
|
+
options,
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
if (p.isCancel(action)) {
|
|
389
|
+
p.cancel('Cancelled.');
|
|
390
|
+
process.exit(0);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// ── Double-confirm for destructive reset ──
|
|
394
|
+
if (action === 'reset') {
|
|
395
|
+
p.note(
|
|
396
|
+
'This will overwrite ALL hooks, skills, agents, and workflow config\n' +
|
|
397
|
+
'with the default templates. Modified and pinned files will be replaced.\n' +
|
|
398
|
+
'Your scopes, database, and orbital.config.json are preserved.',
|
|
399
|
+
'Warning',
|
|
400
|
+
);
|
|
401
|
+
const confirmReset = await p.confirm({
|
|
402
|
+
message: 'Are you sure you want to reset all templates?',
|
|
403
|
+
initialValue: false,
|
|
404
|
+
});
|
|
405
|
+
if (p.isCancel(confirmReset) || !confirmReset) {
|
|
406
|
+
p.cancel('Reset cancelled.');
|
|
407
|
+
process.exit(0);
|
|
408
|
+
}
|
|
409
|
+
const doubleConfirm = await p.confirm({
|
|
410
|
+
message: 'This cannot be undone. Continue?',
|
|
411
|
+
initialValue: false,
|
|
412
|
+
});
|
|
413
|
+
if (p.isCancel(doubleConfirm) || !doubleConfirm) {
|
|
414
|
+
p.cancel('Reset cancelled.');
|
|
415
|
+
process.exit(0);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
result.action = action;
|
|
420
|
+
return result;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ─── Template Version Stamping ─────────────────────────────────
|
|
424
|
+
|
|
425
|
+
function stampTemplateVersion(projectRoot: string, packageVersion: string): void {
|
|
426
|
+
const configPath = path.join(projectRoot, '.claude', 'orbital.config.json');
|
|
427
|
+
if (!fs.existsSync(configPath)) return;
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
431
|
+
if (config.templateVersion !== packageVersion) {
|
|
432
|
+
config.templateVersion = packageVersion;
|
|
433
|
+
const tmp = configPath + `.tmp.${process.pid}`;
|
|
434
|
+
fs.writeFileSync(tmp, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
435
|
+
fs.renameSync(tmp, configPath);
|
|
436
|
+
}
|
|
437
|
+
} catch { /* ignore malformed config */ }
|
|
438
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 3: Confirm choices, run installation, show next steps.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import * as p from '@clack/prompts';
|
|
8
|
+
import type { ProjectSetupState } from '../types.js';
|
|
9
|
+
import { NOTES, formatSummary } from '../ui.js';
|
|
10
|
+
|
|
11
|
+
export async function phaseConfirm(state: ProjectSetupState): Promise<void> {
|
|
12
|
+
p.note(formatSummary(state), 'Ready to Initialize');
|
|
13
|
+
|
|
14
|
+
const proceed = await p.confirm({
|
|
15
|
+
message: 'Proceed with installation?',
|
|
16
|
+
initialValue: true,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
if (p.isCancel(proceed) || !proceed) {
|
|
20
|
+
p.cancel('Setup cancelled.');
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function showPostInstall(state: ProjectSetupState): void {
|
|
26
|
+
// Count installed artifacts
|
|
27
|
+
const claudeDir = path.join(state.projectRoot, '.claude');
|
|
28
|
+
const counts = {
|
|
29
|
+
hooks: countDir(path.join(claudeDir, 'hooks')),
|
|
30
|
+
skills: countDir(path.join(claudeDir, 'skills')),
|
|
31
|
+
agents: countDir(path.join(claudeDir, 'agents')),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
p.note(NOTES.postInstall(counts), 'Installation Complete');
|
|
35
|
+
p.note(NOTES.nextSteps, 'Getting Started');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function countDir(dir: string): number {
|
|
39
|
+
if (!fs.existsSync(dir)) return 0;
|
|
40
|
+
try {
|
|
41
|
+
return fs.readdirSync(dir).filter(f => !f.startsWith('.')).length;
|
|
42
|
+
} catch {
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 1: Project configuration — name, commands, ports.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as p from '@clack/prompts';
|
|
6
|
+
import type { ProjectSetupState } from '../types.js';
|
|
7
|
+
import { NOTES, formatDetectedCommands } from '../ui.js';
|
|
8
|
+
import { detectProjectName, detectCommands, detectPortConflict } from '../detect.js';
|
|
9
|
+
|
|
10
|
+
export async function phaseProjectSetup(state: ProjectSetupState): Promise<void> {
|
|
11
|
+
p.note(NOTES.projectConfig, 'Project Configuration');
|
|
12
|
+
|
|
13
|
+
// 1. Project name
|
|
14
|
+
const defaultName = detectProjectName(state.projectRoot);
|
|
15
|
+
const name = await p.text({
|
|
16
|
+
message: 'Project name',
|
|
17
|
+
placeholder: defaultName,
|
|
18
|
+
defaultValue: defaultName,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (p.isCancel(name)) {
|
|
22
|
+
p.cancel('Setup cancelled.');
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
state.projectName = name;
|
|
26
|
+
|
|
27
|
+
// 2. Command detection
|
|
28
|
+
const detected = detectCommands(state.projectRoot);
|
|
29
|
+
state.detectedCommands = detected;
|
|
30
|
+
const detectedCount = Object.values(detected).filter(v => v !== null).length;
|
|
31
|
+
|
|
32
|
+
if (detectedCount > 0) {
|
|
33
|
+
p.note(formatDetectedCommands(detected), `Detected ${detectedCount} command(s) from package.json`);
|
|
34
|
+
|
|
35
|
+
const useDetected = await p.confirm({
|
|
36
|
+
message: 'Use these detected commands for quality gates?',
|
|
37
|
+
initialValue: true,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (p.isCancel(useDetected)) {
|
|
41
|
+
p.cancel('Setup cancelled.');
|
|
42
|
+
process.exit(0);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (useDetected) {
|
|
46
|
+
state.selectedCommands = { ...detected };
|
|
47
|
+
} else {
|
|
48
|
+
state.selectedCommands = await promptCommands(detected);
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
p.log.info('No build commands detected from package.json. You can configure them later with `orbital config`.');
|
|
52
|
+
state.selectedCommands = detected;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 3. Port conflict detection
|
|
56
|
+
const conflict = detectPortConflict(4444);
|
|
57
|
+
if (conflict) {
|
|
58
|
+
p.log.warn(`Port 4444 is already used by "${conflict}".`);
|
|
59
|
+
|
|
60
|
+
const serverPort = await p.text({
|
|
61
|
+
message: 'Server port',
|
|
62
|
+
placeholder: '4446',
|
|
63
|
+
defaultValue: '4446',
|
|
64
|
+
validate: (val) => {
|
|
65
|
+
const n = Number(val);
|
|
66
|
+
if (isNaN(n) || n < 1 || n > 65535) return 'Must be a valid port (1-65535)';
|
|
67
|
+
return undefined;
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (p.isCancel(serverPort)) {
|
|
72
|
+
p.cancel('Setup cancelled.');
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
state.serverPort = Number(serverPort);
|
|
77
|
+
state.clientPort = state.serverPort + 1;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function promptCommands(defaults: Record<string, string | null>): Promise<Record<string, string | null>> {
|
|
82
|
+
const commands: Record<string, string | null> = {};
|
|
83
|
+
const labels: Record<string, string> = {
|
|
84
|
+
typeCheck: 'Type check command',
|
|
85
|
+
lint: 'Lint command',
|
|
86
|
+
build: 'Build command',
|
|
87
|
+
test: 'Test command',
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
for (const [key, defaultVal] of Object.entries(defaults)) {
|
|
91
|
+
const val = await p.text({
|
|
92
|
+
message: labels[key] || key,
|
|
93
|
+
placeholder: defaultVal || 'none',
|
|
94
|
+
defaultValue: defaultVal || '',
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (p.isCancel(val)) {
|
|
98
|
+
p.cancel('Setup cancelled.');
|
|
99
|
+
process.exit(0);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
commands[key] = val || null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return commands;
|
|
106
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 1: Setup Wizard — runs on first install or when ~/.orbital/ is missing.
|
|
3
|
+
*
|
|
4
|
+
* Creates the Orbital home directory, seeds primitives, and optionally
|
|
5
|
+
* lets the user link projects immediately.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import * as p from '@clack/prompts';
|
|
10
|
+
import pc from 'picocolors';
|
|
11
|
+
import type { SetupState } from '../types.js';
|
|
12
|
+
import { NOTES } from '../ui.js';
|
|
13
|
+
import { isValidProjectPath, resolveProjectPath, ORBITAL_HOME } from '../detect.js';
|
|
14
|
+
|
|
15
|
+
export async function phaseSetupWizard(state: SetupState): Promise<void> {
|
|
16
|
+
// Welcome and core concepts
|
|
17
|
+
p.note(NOTES.setupWelcome, 'Welcome');
|
|
18
|
+
|
|
19
|
+
// Create ~/.orbital/ and seed primitives
|
|
20
|
+
const s = p.spinner();
|
|
21
|
+
s.start('Setting up Orbital Command...');
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const { ensureOrbitalHome } = await import('../../global-config.js');
|
|
25
|
+
const { seedGlobalPrimitives } = await import('../../init.js');
|
|
26
|
+
|
|
27
|
+
ensureOrbitalHome();
|
|
28
|
+
seedGlobalPrimitives();
|
|
29
|
+
|
|
30
|
+
// Create empty registry if it doesn't exist
|
|
31
|
+
const registryPath = `${ORBITAL_HOME}/config.json`;
|
|
32
|
+
if (!fs.existsSync(registryPath)) {
|
|
33
|
+
fs.writeFileSync(registryPath, JSON.stringify({ version: 1, projects: [] }, null, 2), 'utf8');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
s.stop('Orbital Command is ready.');
|
|
37
|
+
} catch (err) {
|
|
38
|
+
s.stop('Setup failed.');
|
|
39
|
+
p.log.error(err instanceof Error ? err.message : String(err));
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Offer to link projects
|
|
44
|
+
p.note(NOTES.addProject, 'Projects');
|
|
45
|
+
|
|
46
|
+
let addMore = true;
|
|
47
|
+
while (addMore) {
|
|
48
|
+
const wantsProject = await p.confirm({
|
|
49
|
+
message: state.linkedProjects.length === 0
|
|
50
|
+
? 'Add a project now?'
|
|
51
|
+
: 'Add another project?',
|
|
52
|
+
initialValue: state.linkedProjects.length === 0,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (p.isCancel(wantsProject) || !wantsProject) {
|
|
56
|
+
addMore = false;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const projectPath = await p.text({
|
|
61
|
+
message: 'Project path',
|
|
62
|
+
placeholder: '~/Code/my-project',
|
|
63
|
+
validate: (val) => {
|
|
64
|
+
if (!val || !val.trim()) return 'Path is required';
|
|
65
|
+
return isValidProjectPath(val.trim());
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (p.isCancel(projectPath)) {
|
|
70
|
+
addMore = false;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const resolved = resolveProjectPath(projectPath.trim());
|
|
75
|
+
state.linkedProjects.push(resolved);
|
|
76
|
+
p.log.success(`Added: ${pc.cyan(resolved)}`);
|
|
77
|
+
}
|
|
78
|
+
}
|