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.
- package/bin/orbital.js +676 -53
- package/dist/assets/PrimitivesConfig-CrmQXYh4.js +32 -0
- package/dist/assets/QualityGates-BbasOsF3.js +21 -0
- package/dist/assets/SessionTimeline-CGeJsVvy.js +1 -0
- package/dist/assets/Settings-oiM496mc.js +12 -0
- package/dist/assets/SourceControl-B1fP2nJL.js +41 -0
- package/dist/assets/WorkflowVisualizer-CWLYf-f0.js +74 -0
- package/dist/assets/arrow-down-CPy85_J6.js +6 -0
- package/dist/assets/charts-DbDg0Psc.js +68 -0
- package/dist/assets/circle-x-Cwz6ZQDV.js +6 -0
- package/dist/assets/file-text-C46Xr65c.js +6 -0
- package/dist/assets/formatDistanceToNow-BMqsSP44.js +1 -0
- package/dist/assets/globe-Cn2yNZUD.js +6 -0
- package/dist/assets/index-Aj4sV8Al.css +1 -0
- package/dist/assets/index-Bc9dK3MW.js +354 -0
- package/dist/assets/key-OPaNTWJ5.js +6 -0
- package/dist/assets/minus-GMsbpKym.js +6 -0
- package/dist/assets/shield-DwAFkDYI.js +6 -0
- package/dist/assets/ui-BmsSg9jU.js +53 -0
- package/dist/assets/useWorkflowEditor-BJkTX_NR.js +16 -0
- package/dist/assets/{vendor-Dzv9lrRc.js → vendor-Bqt8AJn2.js} +1 -1
- package/dist/assets/zap-DfbUoOty.js +11 -0
- package/dist/favicon.svg +1 -0
- package/dist/index.html +6 -5
- package/dist/server/server/__tests__/data-routes.test.js +124 -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 +137 -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 +138 -0
- package/dist/server/server/config.js +17 -2
- package/dist/server/server/database.js +27 -12
- package/dist/server/server/global-config.js +143 -0
- package/dist/server/server/index.js +882 -252
- package/dist/server/server/init.js +579 -194
- 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.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 +255 -0
- package/dist/server/server/project-emitter.js +41 -0
- package/dist/server/server/project-manager.js +297 -0
- package/dist/server/server/routes/config-routes.js +1 -3
- package/dist/server/server/routes/data-routes.js +22 -110
- package/dist/server/server/routes/dispatch-routes.js +15 -9
- 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 +37 -23
- 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 +2 -0
- package/dist/server/server/services/batch-orchestrator.js +26 -16
- package/dist/server/server/services/claude-session-service.js +17 -14
- 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 +217 -126
- package/dist/server/server/services/scope-service.test.js +137 -0
- package/dist/server/server/services/sprint-orchestrator.js +7 -6
- package/dist/server/server/services/sprint-service.js +21 -1
- 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/telemetry-service.js +143 -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/update-planner.js +279 -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 +77 -20
- package/dist/server/server/utils/dispatch-utils.test.js +182 -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 +10 -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/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 +155 -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 +35 -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 +74 -0
- package/dist/server/shared/__fixtures__/workflow-configs.js +75 -0
- 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.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 +20 -15
- package/schemas/orbital.config.schema.json +16 -1
- package/scripts/postinstall.js +55 -7
- package/server/__tests__/data-routes.test.ts +149 -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 +157 -0
- package/server/__tests__/sprint-routes.test.ts +118 -0
- package/server/__tests__/workflow-routes.test.ts +120 -0
- package/server/config-migrator.ts +163 -0
- package/server/config.ts +26 -2
- package/server/database.ts +35 -18
- package/server/global-config.ts +200 -0
- package/server/index.ts +975 -287
- package/server/init.ts +625 -182
- 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/scope-parser.test.ts +270 -0
- package/server/parsers/scope-parser.ts +79 -31
- package/server/project-context.ts +309 -0
- package/server/project-emitter.ts +50 -0
- package/server/project-manager.ts +369 -0
- package/server/routes/config-routes.ts +3 -5
- package/server/routes/data-routes.ts +28 -141
- package/server/routes/dispatch-routes.ts +19 -11
- package/server/routes/git-routes.ts +77 -0
- package/server/routes/manifest-routes.ts +388 -0
- package/server/routes/scope-routes.ts +29 -25
- 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 +2 -0
- package/server/services/batch-orchestrator.ts +24 -16
- package/server/services/claude-session-service.ts +16 -14
- 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 +220 -126
- package/server/services/sprint-orchestrator.ts +7 -7
- package/server/services/sprint-service.test.ts +271 -0
- package/server/services/sprint-service.ts +27 -3
- package/server/services/sync-service.ts +482 -0
- package/server/services/sync-types.ts +77 -0
- package/server/services/telemetry-service.ts +195 -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/update-planner.ts +346 -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 +97 -27
- package/server/utils/logger.ts +40 -3
- package/server/utils/package-info.ts +32 -0
- package/server/utils/route-helpers.ts +12 -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/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 +187 -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 +43 -0
- package/server/wizard/phases/workflow-setup.ts +28 -0
- package/server/wizard/types.ts +56 -0
- package/server/wizard/ui.ts +93 -0
- package/shared/__fixtures__/workflow-configs.ts +80 -0
- package/shared/default-workflow.json +65 -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-normalizer.test.ts +119 -0
- package/shared/workflow-normalizer.ts +118 -0
- package/templates/hooks/end-session.sh +3 -1
- 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-create-cleanup.sh +2 -2
- package/templates/hooks/scope-create-gate.sh +0 -1
- package/templates/hooks/scope-helpers.sh +18 -0
- package/templates/hooks/scope-prepare.sh +66 -11
- package/templates/migrations/renames.json +1 -0
- package/templates/orbital.config.json +7 -2
- package/templates/settings-hooks.json +1 -1
- package/templates/skills/git-commit/SKILL.md +9 -4
- package/templates/skills/git-dev/SKILL.md +8 -3
- package/templates/skills/git-main/SKILL.md +8 -2
- package/templates/skills/git-production/SKILL.md +6 -2
- package/templates/skills/git-staging/SKILL.md +8 -3
- package/templates/skills/scope-create/SKILL.md +17 -3
- package/templates/skills/scope-fix-review/SKILL.md +6 -3
- package/templates/skills/scope-implement/SKILL.md +4 -1
- package/templates/skills/scope-post-review/SKILL.md +63 -5
- package/templates/skills/scope-pre-review/SKILL.md +5 -2
- 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 -49
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health diagnostics — `orbital doctor`
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { execFileSync } from 'child_process';
|
|
7
|
+
import pc from 'picocolors';
|
|
8
|
+
export async function runDoctor(projectRoot, packageVersion) {
|
|
9
|
+
console.log(`\n ${pc.bold('Orbital Command')} ${pc.cyan(`v${packageVersion}`)}\n`);
|
|
10
|
+
const checks = [];
|
|
11
|
+
// 1. Check for latest version on npm
|
|
12
|
+
try {
|
|
13
|
+
const latest = execFileSync('npm', ['view', 'orbital-command', 'version'], {
|
|
14
|
+
encoding: 'utf8',
|
|
15
|
+
timeout: 5000,
|
|
16
|
+
}).trim();
|
|
17
|
+
if (latest && latest !== packageVersion) {
|
|
18
|
+
checks.push({ label: 'Latest', status: pc.yellow(`v${latest} available (run \`npm update -g orbital-command\`)`) });
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
checks.push({ label: 'Latest', status: pc.green(`v${packageVersion} (up to date)`) });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
checks.push({ label: 'Latest', status: pc.dim('could not check (npm unreachable)') });
|
|
26
|
+
}
|
|
27
|
+
// 2. Node version
|
|
28
|
+
const nodeVersion = process.version;
|
|
29
|
+
const major = parseInt(nodeVersion.slice(1), 10);
|
|
30
|
+
if (major >= 18) {
|
|
31
|
+
checks.push({ label: 'Node', status: pc.green(`${nodeVersion} (supported)`) });
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
checks.push({ label: 'Node', status: pc.red(`${nodeVersion} (requires Node 18+)`) });
|
|
35
|
+
}
|
|
36
|
+
// 3. Global registry
|
|
37
|
+
const homedir = process.env.HOME || process.env.USERPROFILE || '~';
|
|
38
|
+
const registryPath = path.join(homedir, '.orbital', 'config.json');
|
|
39
|
+
if (fs.existsSync(registryPath)) {
|
|
40
|
+
try {
|
|
41
|
+
const registry = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
|
42
|
+
const count = (registry.projects || []).length;
|
|
43
|
+
checks.push({ label: 'Global', status: pc.green(`~/.orbital/ exists (${count} project${count !== 1 ? 's' : ''} registered)`) });
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
checks.push({ label: 'Global', status: pc.yellow('~/.orbital/ exists (registry unreadable)') });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
checks.push({ label: 'Global', status: pc.dim('~/.orbital/ not found (run `orbital init` to create)') });
|
|
51
|
+
}
|
|
52
|
+
// 4. Project initialization
|
|
53
|
+
const configPath = path.join(projectRoot, '.claude', 'orbital.config.json');
|
|
54
|
+
if (fs.existsSync(configPath)) {
|
|
55
|
+
try {
|
|
56
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
57
|
+
const name = config.projectName || path.basename(projectRoot);
|
|
58
|
+
checks.push({ label: 'Project', status: pc.green(`initialized (${name})`) });
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
checks.push({ label: 'Project', status: pc.yellow('config exists but unreadable') });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
checks.push({ label: 'Project', status: pc.dim('not initialized (run `orbital init`)') });
|
|
66
|
+
}
|
|
67
|
+
// 5. Workflow
|
|
68
|
+
const workflowPath = path.join(projectRoot, '.claude', 'config', 'workflow.json');
|
|
69
|
+
if (fs.existsSync(workflowPath)) {
|
|
70
|
+
try {
|
|
71
|
+
const wf = JSON.parse(fs.readFileSync(workflowPath, 'utf8'));
|
|
72
|
+
const listCount = (wf.lists || []).length;
|
|
73
|
+
const mode = wf.branchingMode || 'unknown';
|
|
74
|
+
checks.push({ label: 'Workflow', status: pc.green(`${wf.name || 'Custom'} (${listCount} lists, ${mode})`) });
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
checks.push({ label: 'Workflow', status: pc.yellow('workflow.json exists but unreadable') });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
checks.push({ label: 'Workflow', status: pc.dim('no workflow configured') });
|
|
82
|
+
}
|
|
83
|
+
// 6. Database
|
|
84
|
+
const dbPath = path.join(projectRoot, '.claude', 'orbital', 'orbital.db');
|
|
85
|
+
if (fs.existsSync(dbPath)) {
|
|
86
|
+
const stats = fs.statSync(dbPath);
|
|
87
|
+
const sizeMb = (stats.size / (1024 * 1024)).toFixed(1);
|
|
88
|
+
checks.push({ label: 'Database', status: pc.green(`${sizeMb} MB`) });
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
checks.push({ label: 'Database', status: pc.dim('not yet created (starts on first launch)') });
|
|
92
|
+
}
|
|
93
|
+
// 7. Template staleness
|
|
94
|
+
if (fs.existsSync(configPath)) {
|
|
95
|
+
try {
|
|
96
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
97
|
+
if (config.templateVersion && config.templateVersion !== packageVersion) {
|
|
98
|
+
checks.push({ label: 'Templates', status: pc.yellow(`outdated (v${config.templateVersion} → v${packageVersion}, run \`orbital update\`)`) });
|
|
99
|
+
}
|
|
100
|
+
else if (config.templateVersion) {
|
|
101
|
+
checks.push({ label: 'Templates', status: pc.green('synced') });
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
checks.push({ label: 'Templates', status: pc.yellow('no version stamp (run `orbital update`)') });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch { /* skip */ }
|
|
108
|
+
}
|
|
109
|
+
// Print all checks
|
|
110
|
+
const maxLabel = Math.max(...checks.map(c => c.label.length));
|
|
111
|
+
for (const check of checks) {
|
|
112
|
+
console.log(` ${pc.dim(check.label.padEnd(maxLabel + 2))} ${check.status}`);
|
|
113
|
+
}
|
|
114
|
+
console.log();
|
|
115
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
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
|
+
import fs from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import crypto from 'crypto';
|
|
13
|
+
import * as p from '@clack/prompts';
|
|
14
|
+
import pc from 'picocolors';
|
|
15
|
+
import { buildSetupState, buildProjectState, ORBITAL_HOME } from './detect.js';
|
|
16
|
+
import { phaseSetupWizard } from './phases/setup-wizard.js';
|
|
17
|
+
import { phaseWelcome } from './phases/welcome.js';
|
|
18
|
+
import { phaseProjectSetup } from './phases/project-setup.js';
|
|
19
|
+
import { phaseWorkflowSetup } from './phases/workflow-setup.js';
|
|
20
|
+
import { phaseConfirm, showPostInstall } from './phases/confirm.js';
|
|
21
|
+
import { NOTES } from './ui.js';
|
|
22
|
+
import { runConfigEditor } from './config-editor.js';
|
|
23
|
+
import { runDoctor } from './doctor.js';
|
|
24
|
+
export { runConfigEditor, runDoctor };
|
|
25
|
+
// ─── Phase 1: Setup Wizard ─────────────────────────────────────
|
|
26
|
+
/**
|
|
27
|
+
* First-time setup. Creates ~/.orbital/, seeds primitives,
|
|
28
|
+
* optionally links projects (running Phase 2 for each).
|
|
29
|
+
*/
|
|
30
|
+
export async function runSetupWizard(packageVersion) {
|
|
31
|
+
const state = buildSetupState(packageVersion);
|
|
32
|
+
p.intro(`${pc.bgCyan(pc.black(' Orbital Command '))} ${pc.dim(`v${packageVersion}`)}`);
|
|
33
|
+
await phaseSetupWizard(state);
|
|
34
|
+
// If user linked projects, run Phase 2 for each
|
|
35
|
+
for (const projectRoot of state.linkedProjects) {
|
|
36
|
+
p.log.step(`Setting up ${pc.cyan(path.basename(projectRoot))}...`);
|
|
37
|
+
await runProjectSetupInline(projectRoot, packageVersion);
|
|
38
|
+
}
|
|
39
|
+
if (state.linkedProjects.length === 0) {
|
|
40
|
+
p.note(NOTES.setupComplete, 'Done');
|
|
41
|
+
}
|
|
42
|
+
p.outro(state.linkedProjects.length > 0
|
|
43
|
+
? `Run ${pc.cyan('orbital launch --open')} to open the dashboard.`
|
|
44
|
+
: `Run ${pc.cyan('orbital init')} in a project directory to get started.`);
|
|
45
|
+
}
|
|
46
|
+
// ─── Phase 2: Project Setup ────────────────────────────────────
|
|
47
|
+
/**
|
|
48
|
+
* Per-project setup. Walks through name, commands, workflow, then
|
|
49
|
+
* calls runInit() to scaffold files into .claude/.
|
|
50
|
+
*/
|
|
51
|
+
export async function runProjectSetup(projectRoot, packageVersion, args) {
|
|
52
|
+
const state = buildProjectState(projectRoot, packageVersion);
|
|
53
|
+
const force = args.includes('--force');
|
|
54
|
+
p.intro(`${pc.bgCyan(pc.black(' Orbital Command '))} ${pc.dim(`v${packageVersion}`)}`);
|
|
55
|
+
// Welcome gate: detect re-init / reconfigure
|
|
56
|
+
const forceFromWelcome = await phaseWelcome(state);
|
|
57
|
+
const useForce = force || forceFromWelcome;
|
|
58
|
+
await runProjectPhases(state, useForce);
|
|
59
|
+
p.outro(`Run ${pc.cyan('orbital launch --open')} to open the dashboard.`);
|
|
60
|
+
}
|
|
61
|
+
// ─── Shared project phases (used by both flows) ────────────────
|
|
62
|
+
/**
|
|
63
|
+
* Run the project setup phases and install. Used by both
|
|
64
|
+
* standalone runProjectSetup() and inline from runSetupWizard().
|
|
65
|
+
*/
|
|
66
|
+
async function runProjectPhases(state, useForce) {
|
|
67
|
+
await phaseProjectSetup(state);
|
|
68
|
+
await phaseWorkflowSetup(state);
|
|
69
|
+
await phaseConfirm(state);
|
|
70
|
+
// Install
|
|
71
|
+
const s = p.spinner();
|
|
72
|
+
s.start('Installing into project...');
|
|
73
|
+
try {
|
|
74
|
+
const { runInit } = await import('../init.js');
|
|
75
|
+
runInit(state.projectRoot, {
|
|
76
|
+
force: useForce,
|
|
77
|
+
quiet: true,
|
|
78
|
+
preset: state.workflowPreset,
|
|
79
|
+
projectName: state.projectName,
|
|
80
|
+
serverPort: state.serverPort,
|
|
81
|
+
clientPort: state.clientPort,
|
|
82
|
+
commands: state.selectedCommands,
|
|
83
|
+
});
|
|
84
|
+
registerProject(state.projectRoot, state.projectName);
|
|
85
|
+
stampTemplateVersion(state.projectRoot, state.packageVersion);
|
|
86
|
+
s.stop('Project ready.');
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
s.stop('Installation failed.');
|
|
90
|
+
p.log.error(err instanceof Error ? err.message : String(err));
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
showPostInstall(state);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Inline project setup — called from Phase 1 when user links a project.
|
|
97
|
+
* Skips intro/outro since the setup wizard already has those.
|
|
98
|
+
*/
|
|
99
|
+
async function runProjectSetupInline(projectRoot, packageVersion) {
|
|
100
|
+
const state = buildProjectState(projectRoot, packageVersion);
|
|
101
|
+
// Skip welcome gate for inline — this is a fresh project being linked
|
|
102
|
+
await runProjectPhases(state, false);
|
|
103
|
+
}
|
|
104
|
+
// ─── Registration ──────────────────────────────────────────────
|
|
105
|
+
function registerProject(projectRoot, projectName) {
|
|
106
|
+
const registryPath = path.join(ORBITAL_HOME, 'config.json');
|
|
107
|
+
let registry;
|
|
108
|
+
try {
|
|
109
|
+
registry = fs.existsSync(registryPath)
|
|
110
|
+
? JSON.parse(fs.readFileSync(registryPath, 'utf8'))
|
|
111
|
+
: { version: 1, projects: [] };
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
registry = { version: 1, projects: [] };
|
|
115
|
+
}
|
|
116
|
+
if (!registry.projects)
|
|
117
|
+
registry.projects = [];
|
|
118
|
+
if (registry.projects.some((proj) => proj.path === projectRoot))
|
|
119
|
+
return;
|
|
120
|
+
const COLORS = [
|
|
121
|
+
'210 80% 55%', '340 75% 55%', '160 60% 45%', '30 90% 55%',
|
|
122
|
+
'270 65% 55%', '50 85% 50%', '180 55% 45%', '0 70% 55%',
|
|
123
|
+
];
|
|
124
|
+
const usedColors = registry.projects.map((proj) => proj.color);
|
|
125
|
+
const color = COLORS.find(c => !usedColors.includes(c)) || COLORS[0];
|
|
126
|
+
const name = projectName || path.basename(projectRoot);
|
|
127
|
+
const baseSlug = path.basename(projectRoot).toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '') || 'project';
|
|
128
|
+
const existingIds = registry.projects.map((proj) => proj.id);
|
|
129
|
+
const slug = existingIds.includes(baseSlug)
|
|
130
|
+
? `${baseSlug}-${crypto.createHash('sha256').update(projectRoot).digest('hex').slice(0, 4)}`
|
|
131
|
+
: baseSlug;
|
|
132
|
+
registry.projects.push({
|
|
133
|
+
id: slug,
|
|
134
|
+
path: projectRoot,
|
|
135
|
+
name,
|
|
136
|
+
color,
|
|
137
|
+
registeredAt: new Date().toISOString(),
|
|
138
|
+
enabled: true,
|
|
139
|
+
});
|
|
140
|
+
fs.writeFileSync(registryPath, JSON.stringify(registry, null, 2), 'utf8');
|
|
141
|
+
}
|
|
142
|
+
// ─── Template Version Stamping ─────────────────────────────────
|
|
143
|
+
function stampTemplateVersion(projectRoot, packageVersion) {
|
|
144
|
+
const configPath = path.join(projectRoot, '.claude', 'orbital.config.json');
|
|
145
|
+
if (!fs.existsSync(configPath))
|
|
146
|
+
return;
|
|
147
|
+
try {
|
|
148
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
149
|
+
if (config.templateVersion !== packageVersion) {
|
|
150
|
+
config.templateVersion = packageVersion;
|
|
151
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch { /* ignore malformed config */ }
|
|
155
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 3: Confirm choices, run installation, show next steps.
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import * as p from '@clack/prompts';
|
|
7
|
+
import { NOTES, formatSummary } from '../ui.js';
|
|
8
|
+
export async function phaseConfirm(state) {
|
|
9
|
+
p.note(formatSummary(state), 'Ready to Initialize');
|
|
10
|
+
const proceed = await p.confirm({
|
|
11
|
+
message: 'Proceed with installation?',
|
|
12
|
+
initialValue: true,
|
|
13
|
+
});
|
|
14
|
+
if (p.isCancel(proceed) || !proceed) {
|
|
15
|
+
p.cancel('Setup cancelled.');
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function showPostInstall(state) {
|
|
20
|
+
// Count installed artifacts
|
|
21
|
+
const claudeDir = path.join(state.projectRoot, '.claude');
|
|
22
|
+
const counts = {
|
|
23
|
+
hooks: countDir(path.join(claudeDir, 'hooks')),
|
|
24
|
+
skills: countDir(path.join(claudeDir, 'skills')),
|
|
25
|
+
agents: countDir(path.join(claudeDir, 'agents')),
|
|
26
|
+
};
|
|
27
|
+
p.note(NOTES.postInstall(counts), 'Installation Complete');
|
|
28
|
+
p.note(NOTES.nextSteps, 'Getting Started');
|
|
29
|
+
}
|
|
30
|
+
function countDir(dir) {
|
|
31
|
+
if (!fs.existsSync(dir))
|
|
32
|
+
return 0;
|
|
33
|
+
try {
|
|
34
|
+
return fs.readdirSync(dir).filter(f => !f.startsWith('.')).length;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 1: Project configuration — name, commands, ports.
|
|
3
|
+
*/
|
|
4
|
+
import * as p from '@clack/prompts';
|
|
5
|
+
import { NOTES, formatDetectedCommands } from '../ui.js';
|
|
6
|
+
import { detectProjectName, detectCommands, detectPortConflict } from '../detect.js';
|
|
7
|
+
export async function phaseProjectSetup(state) {
|
|
8
|
+
p.note(NOTES.projectConfig, 'Project Configuration');
|
|
9
|
+
// 1. Project name
|
|
10
|
+
const defaultName = detectProjectName(state.projectRoot);
|
|
11
|
+
const name = await p.text({
|
|
12
|
+
message: 'Project name',
|
|
13
|
+
placeholder: defaultName,
|
|
14
|
+
defaultValue: defaultName,
|
|
15
|
+
});
|
|
16
|
+
if (p.isCancel(name)) {
|
|
17
|
+
p.cancel('Setup cancelled.');
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
state.projectName = name;
|
|
21
|
+
// 2. Command detection
|
|
22
|
+
const detected = detectCommands(state.projectRoot);
|
|
23
|
+
state.detectedCommands = detected;
|
|
24
|
+
const detectedCount = Object.values(detected).filter(v => v !== null).length;
|
|
25
|
+
if (detectedCount > 0) {
|
|
26
|
+
p.note(formatDetectedCommands(detected), `Detected ${detectedCount} command(s) from package.json`);
|
|
27
|
+
const useDetected = await p.confirm({
|
|
28
|
+
message: 'Use these detected commands for quality gates?',
|
|
29
|
+
initialValue: true,
|
|
30
|
+
});
|
|
31
|
+
if (p.isCancel(useDetected)) {
|
|
32
|
+
p.cancel('Setup cancelled.');
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
if (useDetected) {
|
|
36
|
+
state.selectedCommands = { ...detected };
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
state.selectedCommands = await promptCommands(detected);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
p.log.info('No build commands detected from package.json. You can configure them later with `orbital config`.');
|
|
44
|
+
state.selectedCommands = detected;
|
|
45
|
+
}
|
|
46
|
+
// 3. Port conflict detection
|
|
47
|
+
const conflict = detectPortConflict(4444);
|
|
48
|
+
if (conflict) {
|
|
49
|
+
p.log.warn(`Port 4444 is already used by "${conflict}".`);
|
|
50
|
+
const serverPort = await p.text({
|
|
51
|
+
message: 'Server port',
|
|
52
|
+
placeholder: '4446',
|
|
53
|
+
defaultValue: '4446',
|
|
54
|
+
validate: (val) => {
|
|
55
|
+
const n = Number(val);
|
|
56
|
+
if (isNaN(n) || n < 1 || n > 65535)
|
|
57
|
+
return 'Must be a valid port (1-65535)';
|
|
58
|
+
return undefined;
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
if (p.isCancel(serverPort)) {
|
|
62
|
+
p.cancel('Setup cancelled.');
|
|
63
|
+
process.exit(0);
|
|
64
|
+
}
|
|
65
|
+
state.serverPort = Number(serverPort);
|
|
66
|
+
state.clientPort = state.serverPort + 1;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function promptCommands(defaults) {
|
|
70
|
+
const commands = {};
|
|
71
|
+
const labels = {
|
|
72
|
+
typeCheck: 'Type check command',
|
|
73
|
+
lint: 'Lint command',
|
|
74
|
+
build: 'Build command',
|
|
75
|
+
test: 'Test command',
|
|
76
|
+
};
|
|
77
|
+
for (const [key, defaultVal] of Object.entries(defaults)) {
|
|
78
|
+
const val = await p.text({
|
|
79
|
+
message: labels[key] || key,
|
|
80
|
+
placeholder: defaultVal || 'none',
|
|
81
|
+
defaultValue: defaultVal || '',
|
|
82
|
+
});
|
|
83
|
+
if (p.isCancel(val)) {
|
|
84
|
+
p.cancel('Setup cancelled.');
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
commands[key] = val || null;
|
|
88
|
+
}
|
|
89
|
+
return commands;
|
|
90
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
import fs from 'fs';
|
|
8
|
+
import * as p from '@clack/prompts';
|
|
9
|
+
import pc from 'picocolors';
|
|
10
|
+
import { NOTES } from '../ui.js';
|
|
11
|
+
import { isValidProjectPath, resolveProjectPath, ORBITAL_HOME } from '../detect.js';
|
|
12
|
+
export async function phaseSetupWizard(state) {
|
|
13
|
+
// Welcome and core concepts
|
|
14
|
+
p.note(NOTES.setupWelcome, 'Welcome');
|
|
15
|
+
// Create ~/.orbital/ and seed primitives
|
|
16
|
+
const s = p.spinner();
|
|
17
|
+
s.start('Setting up Orbital Command...');
|
|
18
|
+
try {
|
|
19
|
+
const { ensureOrbitalHome } = await import('../../global-config.js');
|
|
20
|
+
const { seedGlobalPrimitives } = await import('../../init.js');
|
|
21
|
+
ensureOrbitalHome();
|
|
22
|
+
seedGlobalPrimitives();
|
|
23
|
+
// Create empty registry if it doesn't exist
|
|
24
|
+
const registryPath = `${ORBITAL_HOME}/config.json`;
|
|
25
|
+
if (!fs.existsSync(registryPath)) {
|
|
26
|
+
fs.writeFileSync(registryPath, JSON.stringify({ version: 1, projects: [] }, null, 2), 'utf8');
|
|
27
|
+
}
|
|
28
|
+
s.stop('Orbital Command is ready.');
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
s.stop('Setup failed.');
|
|
32
|
+
p.log.error(err instanceof Error ? err.message : String(err));
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
// Offer to link projects
|
|
36
|
+
p.note(NOTES.addProject, 'Projects');
|
|
37
|
+
let addMore = true;
|
|
38
|
+
while (addMore) {
|
|
39
|
+
const wantsProject = await p.confirm({
|
|
40
|
+
message: state.linkedProjects.length === 0
|
|
41
|
+
? 'Add a project now?'
|
|
42
|
+
: 'Add another project?',
|
|
43
|
+
initialValue: state.linkedProjects.length === 0,
|
|
44
|
+
});
|
|
45
|
+
if (p.isCancel(wantsProject) || !wantsProject) {
|
|
46
|
+
addMore = false;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
const projectPath = await p.text({
|
|
50
|
+
message: 'Project path',
|
|
51
|
+
placeholder: '~/Code/my-project',
|
|
52
|
+
validate: (val) => {
|
|
53
|
+
if (!val || !val.trim())
|
|
54
|
+
return 'Path is required';
|
|
55
|
+
return isValidProjectPath(val.trim());
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
if (p.isCancel(projectPath)) {
|
|
59
|
+
addMore = false;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
const resolved = resolveProjectPath(projectPath.trim());
|
|
63
|
+
state.linkedProjects.push(resolved);
|
|
64
|
+
p.log.success(`Added: ${pc.cyan(resolved)}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 2 welcome gate — project-scoped only.
|
|
3
|
+
*
|
|
4
|
+
* If the project is already initialized, offers re-init or config editor.
|
|
5
|
+
* If not, returns false to continue the project setup flow.
|
|
6
|
+
*/
|
|
7
|
+
import * as p from '@clack/prompts';
|
|
8
|
+
import pc from 'picocolors';
|
|
9
|
+
import { NOTES } from '../ui.js';
|
|
10
|
+
import { runConfigEditor } from '../config-editor.js';
|
|
11
|
+
export async function phaseWelcome(state) {
|
|
12
|
+
if (state.isProjectInitialized) {
|
|
13
|
+
p.note(NOTES.reconfigure, pc.yellow('Already Initialized'));
|
|
14
|
+
const action = await p.select({
|
|
15
|
+
message: 'What would you like to do?',
|
|
16
|
+
options: [
|
|
17
|
+
{ value: 'reinit', label: 'Re-initialize (--force)', hint: 'reset all templates to defaults' },
|
|
18
|
+
{ value: 'configure', label: 'Open config editor', hint: 'modify settings without resetting' },
|
|
19
|
+
{ value: 'cancel', label: 'Cancel' },
|
|
20
|
+
],
|
|
21
|
+
});
|
|
22
|
+
if (p.isCancel(action) || action === 'cancel') {
|
|
23
|
+
p.cancel('Cancelled.');
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
if (action === 'configure') {
|
|
27
|
+
await runConfigEditor(state.projectRoot, state.packageVersion, []);
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
// Re-init — continue through the full project setup with force
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
// Not initialized — continue normally
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 2: Workflow preset selection.
|
|
3
|
+
*/
|
|
4
|
+
import * as p from '@clack/prompts';
|
|
5
|
+
import { WORKFLOW_PRESETS } from '../types.js';
|
|
6
|
+
import { NOTES } from '../ui.js';
|
|
7
|
+
export async function phaseWorkflowSetup(state) {
|
|
8
|
+
p.note(NOTES.workflow, 'Workflow Selection');
|
|
9
|
+
const preset = await p.select({
|
|
10
|
+
message: 'Choose a workflow preset',
|
|
11
|
+
options: WORKFLOW_PRESETS.map(p => ({
|
|
12
|
+
value: p.value,
|
|
13
|
+
label: p.label,
|
|
14
|
+
hint: p.hint,
|
|
15
|
+
})),
|
|
16
|
+
});
|
|
17
|
+
if (p.isCancel(preset)) {
|
|
18
|
+
p.cancel('Setup cancelled.');
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
state.workflowPreset = preset;
|
|
22
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the interactive CLI wizard.
|
|
3
|
+
*
|
|
4
|
+
* Two wizard flows:
|
|
5
|
+
* Phase 1 (SetupState) — first-time Orbital setup, ~/.orbital/ creation
|
|
6
|
+
* Phase 2 (ProjectSetupState) — per-project scaffolding into .claude/
|
|
7
|
+
*/
|
|
8
|
+
export const WORKFLOW_PRESETS = [
|
|
9
|
+
{
|
|
10
|
+
value: 'default',
|
|
11
|
+
label: 'Default',
|
|
12
|
+
hint: '7 lists, trunk-based — Icebox → Planning → Backlog → Implementing → Review → Completed → Main',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
value: 'minimal',
|
|
16
|
+
label: 'Minimal',
|
|
17
|
+
hint: '3 lists — To Do → In Progress → Done',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
value: 'development',
|
|
21
|
+
label: 'Development',
|
|
22
|
+
hint: '5 lists, dev branch — Backlog → Implementing → Review → Completed → Dev',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
value: 'gitflow',
|
|
26
|
+
label: 'Gitflow',
|
|
27
|
+
hint: '9 lists, multi-branch — Full pipeline with Dev, Staging, and Production',
|
|
28
|
+
},
|
|
29
|
+
];
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared UI helpers, concept notes, and formatting for the wizard.
|
|
3
|
+
*/
|
|
4
|
+
import pc from 'picocolors';
|
|
5
|
+
// ─── Concept Notes ──────────────────────────────────────────────
|
|
6
|
+
export const NOTES = {
|
|
7
|
+
// Phase 1: Setup wizard (runs on install / first use)
|
|
8
|
+
setupWelcome: `Orbital Command is a mission control layer for Claude Code.
|
|
9
|
+
|
|
10
|
+
It gives your projects a real-time dashboard with a Kanban board,
|
|
11
|
+
${pc.cyan('scopes')} (work items), ${pc.cyan('workflow stages')}, ${pc.cyan('quality gates')},
|
|
12
|
+
${pc.cyan('dispatches')} (automated Claude sessions), and a ${pc.cyan('sprint orchestrator')}.
|
|
13
|
+
|
|
14
|
+
Everything is driven by config files and hooks inside your project's
|
|
15
|
+
${pc.cyan('.claude/')} directory — no database or external service required.`,
|
|
16
|
+
setupComplete: `${pc.bold('Setup complete.')}
|
|
17
|
+
|
|
18
|
+
${pc.cyan('orbital init')} Add a project (run in a project directory)
|
|
19
|
+
${pc.cyan('orbital launch --open')} Start the dashboard
|
|
20
|
+
${pc.cyan('orbital doctor')} Health check & version info`,
|
|
21
|
+
addProject: `You can add projects now or later with ${pc.cyan('orbital init')}.
|
|
22
|
+
Each project gets its own workflow, scopes, and quality gates.`,
|
|
23
|
+
// Phase 2: Project setup (runs per-project)
|
|
24
|
+
reconfigure: `This project is already initialized with Orbital Command.
|
|
25
|
+
You can reconfigure settings or run ${pc.cyan('orbital init --force')} to reset.`,
|
|
26
|
+
projectConfig: `${pc.bold('Project Config')} ${pc.dim('(.claude/orbital.config.json)')}
|
|
27
|
+
|
|
28
|
+
Each project gets its own config inside ${pc.cyan('.claude/')}. The project
|
|
29
|
+
config controls the name shown in the dashboard, ports, build commands
|
|
30
|
+
used by quality gates, and agent definitions.`,
|
|
31
|
+
workflow: `${pc.bold('Workflows')} ${pc.dim('(Scopes, Lists, Dispatches)')}
|
|
32
|
+
|
|
33
|
+
Orbital organizes work into ${pc.cyan('scopes')} (cards) that move through
|
|
34
|
+
${pc.cyan('lists')} (Kanban columns). Transitions between lists can trigger
|
|
35
|
+
commands, quality gates, and Claude Code sessions (${pc.cyan('dispatches')}).
|
|
36
|
+
|
|
37
|
+
Choose a preset to start — you can customize it later from the
|
|
38
|
+
dashboard's workflow editor or by editing ${pc.cyan('.claude/config/workflow.json')}.`,
|
|
39
|
+
postInstall: (counts) => `${pc.bold('What was just created')}
|
|
40
|
+
|
|
41
|
+
${pc.cyan('Hooks')} (${counts.hooks}) Lifecycle scripts that enforce rules on transitions
|
|
42
|
+
${pc.cyan('Skills')} (${counts.skills}) Slash commands for Claude (/scope-create, /git-commit, etc.)
|
|
43
|
+
${pc.cyan('Agents')} (${counts.agents}) Team specifications for code review, architecture, security
|
|
44
|
+
${pc.cyan('Workflow')} Your selected preset defining lists and transitions
|
|
45
|
+
${pc.cyan('Quality Gates')} Automated checks (lint, typecheck, tests) before transitions`,
|
|
46
|
+
nextSteps: `${pc.bold('Next Steps')}
|
|
47
|
+
|
|
48
|
+
1. ${pc.cyan('orbital launch --open')} Open the dashboard
|
|
49
|
+
2. Create a scope from the board or use ${pc.cyan('/scope-create')}
|
|
50
|
+
3. Use ${pc.cyan('/scope-implement')} to start working on a scope
|
|
51
|
+
|
|
52
|
+
${pc.bold('Useful Commands')}
|
|
53
|
+
|
|
54
|
+
${pc.cyan('orbital status')} See template sync status
|
|
55
|
+
${pc.cyan('orbital config')} Modify project settings
|
|
56
|
+
${pc.cyan('orbital update')} Sync to latest templates
|
|
57
|
+
${pc.cyan('orbital doctor')} Health check & version info`,
|
|
58
|
+
};
|
|
59
|
+
// ─── Formatting Helpers ─────────────────────────────────────────
|
|
60
|
+
export function formatDetectedCommands(commands) {
|
|
61
|
+
const entries = Object.entries(commands).filter(([, v]) => v !== null);
|
|
62
|
+
if (entries.length === 0)
|
|
63
|
+
return pc.dim(' No commands detected');
|
|
64
|
+
return entries
|
|
65
|
+
.map(([key, val]) => ` ${pc.cyan(key.padEnd(12))} ${val}`)
|
|
66
|
+
.join('\n');
|
|
67
|
+
}
|
|
68
|
+
export function formatSummary(state) {
|
|
69
|
+
return [
|
|
70
|
+
` Project: ${pc.cyan(state.projectName || 'Unknown')}`,
|
|
71
|
+
` Workflow: ${pc.cyan(state.workflowPreset || 'default')}`,
|
|
72
|
+
` Ports: ${pc.cyan(String(state.serverPort || 4444))} (server) / ${pc.cyan(String(state.clientPort || 4445))} (client)`,
|
|
73
|
+
].join('\n');
|
|
74
|
+
}
|