orbital-command 0.1.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/LICENSE +21 -0
- package/README.md +396 -0
- package/bin/orbital.js +362 -0
- package/dist/assets/WorkflowVisualizer-BZ21PIIF.js +84 -0
- package/dist/assets/WorkflowVisualizer-BZV40eAE.css +1 -0
- package/dist/assets/charts-D__PA1zp.js +72 -0
- package/dist/assets/index-D1G6i0nS.css +1 -0
- package/dist/assets/index-DpItvKpf.js +419 -0
- package/dist/assets/ui-BvF022GT.js +53 -0
- package/dist/assets/vendor-Dzv9lrRc.js +59 -0
- package/dist/index.html +19 -0
- package/dist/scanner-sweep.png +0 -0
- package/dist/server/server/adapters/index.js +34 -0
- package/dist/server/server/adapters/iterm2-adapter.js +29 -0
- package/dist/server/server/adapters/subprocess-adapter.js +21 -0
- package/dist/server/server/adapters/terminal-adapter.js +1 -0
- package/dist/server/server/config.js +156 -0
- package/dist/server/server/database.js +90 -0
- package/dist/server/server/index.js +372 -0
- package/dist/server/server/init.js +811 -0
- package/dist/server/server/parsers/event-parser.js +64 -0
- package/dist/server/server/parsers/scope-parser.js +188 -0
- package/dist/server/server/routes/config-routes.js +163 -0
- package/dist/server/server/routes/data-routes.js +461 -0
- package/dist/server/server/routes/dispatch-routes.js +215 -0
- package/dist/server/server/routes/git-routes.js +92 -0
- package/dist/server/server/routes/scope-routes.js +215 -0
- package/dist/server/server/routes/sprint-routes.js +116 -0
- package/dist/server/server/routes/version-routes.js +130 -0
- package/dist/server/server/routes/workflow-routes.js +185 -0
- package/dist/server/server/schema.js +90 -0
- package/dist/server/server/services/batch-orchestrator.js +253 -0
- package/dist/server/server/services/claude-session-service.js +352 -0
- package/dist/server/server/services/config-service.js +132 -0
- package/dist/server/server/services/deploy-service.js +51 -0
- package/dist/server/server/services/event-service.js +63 -0
- package/dist/server/server/services/gate-service.js +83 -0
- package/dist/server/server/services/git-service.js +309 -0
- package/dist/server/server/services/github-service.js +145 -0
- package/dist/server/server/services/readiness-service.js +184 -0
- package/dist/server/server/services/scope-cache.js +72 -0
- package/dist/server/server/services/scope-service.js +424 -0
- package/dist/server/server/services/sprint-orchestrator.js +312 -0
- package/dist/server/server/services/sprint-service.js +293 -0
- package/dist/server/server/services/workflow-service.js +397 -0
- package/dist/server/server/utils/cc-hooks-parser.js +49 -0
- package/dist/server/server/utils/dispatch-utils.js +305 -0
- package/dist/server/server/utils/logger.js +86 -0
- package/dist/server/server/utils/terminal-launcher.js +388 -0
- package/dist/server/server/utils/worktree-manager.js +98 -0
- package/dist/server/server/watchers/event-watcher.js +81 -0
- package/dist/server/server/watchers/scope-watcher.js +33 -0
- package/dist/server/shared/api-types.js +5 -0
- package/dist/server/shared/default-workflow.json +616 -0
- package/dist/server/shared/workflow-config.js +44 -0
- package/dist/server/shared/workflow-engine.js +353 -0
- package/index.html +15 -0
- package/package.json +110 -0
- package/postcss.config.js +6 -0
- package/schemas/orbital.config.schema.json +83 -0
- package/scripts/postinstall.js +24 -0
- package/scripts/start.sh +20 -0
- package/server/adapters/index.ts +41 -0
- package/server/adapters/iterm2-adapter.ts +37 -0
- package/server/adapters/subprocess-adapter.ts +25 -0
- package/server/adapters/terminal-adapter.ts +24 -0
- package/server/config.ts +234 -0
- package/server/database.ts +107 -0
- package/server/index.ts +452 -0
- package/server/init.ts +891 -0
- package/server/parsers/event-parser.ts +74 -0
- package/server/parsers/scope-parser.ts +240 -0
- package/server/routes/config-routes.ts +182 -0
- package/server/routes/data-routes.ts +548 -0
- package/server/routes/dispatch-routes.ts +275 -0
- package/server/routes/git-routes.ts +112 -0
- package/server/routes/scope-routes.ts +262 -0
- package/server/routes/sprint-routes.ts +142 -0
- package/server/routes/version-routes.ts +156 -0
- package/server/routes/workflow-routes.ts +198 -0
- package/server/schema.ts +90 -0
- package/server/services/batch-orchestrator.ts +286 -0
- package/server/services/claude-session-service.ts +441 -0
- package/server/services/config-service.ts +151 -0
- package/server/services/deploy-service.ts +98 -0
- package/server/services/event-service.ts +98 -0
- package/server/services/gate-service.ts +126 -0
- package/server/services/git-service.ts +391 -0
- package/server/services/github-service.ts +183 -0
- package/server/services/readiness-service.ts +250 -0
- package/server/services/scope-cache.ts +81 -0
- package/server/services/scope-service.ts +476 -0
- package/server/services/sprint-orchestrator.ts +361 -0
- package/server/services/sprint-service.ts +415 -0
- package/server/services/workflow-service.ts +461 -0
- package/server/utils/cc-hooks-parser.ts +70 -0
- package/server/utils/dispatch-utils.ts +395 -0
- package/server/utils/logger.ts +109 -0
- package/server/utils/terminal-launcher.ts +462 -0
- package/server/utils/worktree-manager.ts +104 -0
- package/server/watchers/event-watcher.ts +100 -0
- package/server/watchers/scope-watcher.ts +38 -0
- package/shared/api-types.ts +20 -0
- package/shared/default-workflow.json +616 -0
- package/shared/workflow-config.ts +170 -0
- package/shared/workflow-engine.ts +427 -0
- package/src/App.tsx +33 -0
- package/src/components/AgentBadge.tsx +40 -0
- package/src/components/BatchPreflightModal.tsx +115 -0
- package/src/components/CardDisplayToggle.tsx +74 -0
- package/src/components/ColumnHeaderActions.tsx +55 -0
- package/src/components/ColumnMenu.tsx +99 -0
- package/src/components/DeployHistory.tsx +141 -0
- package/src/components/DispatchModal.tsx +164 -0
- package/src/components/DispatchPopover.tsx +139 -0
- package/src/components/DragOverlay.tsx +25 -0
- package/src/components/DriftSidebar.tsx +140 -0
- package/src/components/EnvironmentStrip.tsx +88 -0
- package/src/components/ErrorBoundary.tsx +62 -0
- package/src/components/FilterChip.tsx +105 -0
- package/src/components/GateIndicator.tsx +33 -0
- package/src/components/IdeaDetailModal.tsx +190 -0
- package/src/components/IdeaFormDialog.tsx +113 -0
- package/src/components/KanbanColumn.tsx +201 -0
- package/src/components/MarkdownRenderer.tsx +114 -0
- package/src/components/NeonGrid.tsx +128 -0
- package/src/components/PromotionQueue.tsx +89 -0
- package/src/components/ScopeCard.tsx +234 -0
- package/src/components/ScopeDetailModal.tsx +255 -0
- package/src/components/ScopeFilterBar.tsx +152 -0
- package/src/components/SearchInput.tsx +102 -0
- package/src/components/SessionPanel.tsx +335 -0
- package/src/components/SprintContainer.tsx +303 -0
- package/src/components/SprintDependencyDialog.tsx +78 -0
- package/src/components/SprintPreflightModal.tsx +138 -0
- package/src/components/StatusBar.tsx +168 -0
- package/src/components/SwimCell.tsx +67 -0
- package/src/components/SwimLaneRow.tsx +94 -0
- package/src/components/SwimlaneBoardView.tsx +108 -0
- package/src/components/VersionBadge.tsx +139 -0
- package/src/components/ViewModeSelector.tsx +114 -0
- package/src/components/config/AgentChip.tsx +53 -0
- package/src/components/config/AgentCreateDialog.tsx +321 -0
- package/src/components/config/AgentEditor.tsx +175 -0
- package/src/components/config/DirectoryTree.tsx +582 -0
- package/src/components/config/FileEditor.tsx +550 -0
- package/src/components/config/HookChip.tsx +50 -0
- package/src/components/config/StageCard.tsx +198 -0
- package/src/components/config/TransitionZone.tsx +173 -0
- package/src/components/config/UnifiedWorkflowPipeline.tsx +216 -0
- package/src/components/config/WorkflowPipeline.tsx +161 -0
- package/src/components/source-control/BranchList.tsx +93 -0
- package/src/components/source-control/BranchPanel.tsx +105 -0
- package/src/components/source-control/CommitLog.tsx +100 -0
- package/src/components/source-control/CommitRow.tsx +47 -0
- package/src/components/source-control/GitHubPanel.tsx +110 -0
- package/src/components/source-control/GitHubSetupGuide.tsx +52 -0
- package/src/components/source-control/GitOverviewBar.tsx +101 -0
- package/src/components/source-control/PullRequestList.tsx +69 -0
- package/src/components/source-control/WorktreeList.tsx +80 -0
- package/src/components/ui/badge.tsx +41 -0
- package/src/components/ui/button.tsx +55 -0
- package/src/components/ui/card.tsx +78 -0
- package/src/components/ui/dialog.tsx +94 -0
- package/src/components/ui/popover.tsx +33 -0
- package/src/components/ui/scroll-area.tsx +54 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/tabs.tsx +52 -0
- package/src/components/ui/toggle-switch.tsx +35 -0
- package/src/components/ui/tooltip.tsx +27 -0
- package/src/components/workflow/AddEdgeDialog.tsx +217 -0
- package/src/components/workflow/AddListDialog.tsx +201 -0
- package/src/components/workflow/ChecklistEditor.tsx +239 -0
- package/src/components/workflow/CommandPrefixManager.tsx +118 -0
- package/src/components/workflow/ConfigSettingsPanel.tsx +189 -0
- package/src/components/workflow/DirectionSelector.tsx +133 -0
- package/src/components/workflow/DispatchConfigPanel.tsx +180 -0
- package/src/components/workflow/EdgeDetailPanel.tsx +236 -0
- package/src/components/workflow/EdgePropertyEditor.tsx +251 -0
- package/src/components/workflow/EditToolbar.tsx +138 -0
- package/src/components/workflow/HookDetailPanel.tsx +250 -0
- package/src/components/workflow/HookExecutionLog.tsx +24 -0
- package/src/components/workflow/HookSourceModal.tsx +129 -0
- package/src/components/workflow/HooksDashboard.tsx +363 -0
- package/src/components/workflow/ListPropertyEditor.tsx +251 -0
- package/src/components/workflow/MigrationPreviewDialog.tsx +237 -0
- package/src/components/workflow/MovementRulesPanel.tsx +188 -0
- package/src/components/workflow/NodeDetailPanel.tsx +245 -0
- package/src/components/workflow/PresetSelector.tsx +414 -0
- package/src/components/workflow/SkillCommandBuilder.tsx +174 -0
- package/src/components/workflow/WorkflowEdgeComponent.tsx +145 -0
- package/src/components/workflow/WorkflowNode.tsx +147 -0
- package/src/components/workflow/graphLayout.ts +186 -0
- package/src/components/workflow/mergeHooks.ts +85 -0
- package/src/components/workflow/useEditHistory.ts +88 -0
- package/src/components/workflow/useWorkflowEditor.ts +262 -0
- package/src/components/workflow/validateConfig.ts +70 -0
- package/src/hooks/useActiveDispatches.ts +198 -0
- package/src/hooks/useBoardSettings.ts +170 -0
- package/src/hooks/useCardDisplay.ts +57 -0
- package/src/hooks/useCcHooks.ts +24 -0
- package/src/hooks/useConfigTree.ts +51 -0
- package/src/hooks/useEnforcementRules.ts +46 -0
- package/src/hooks/useEvents.ts +59 -0
- package/src/hooks/useFileEditor.ts +165 -0
- package/src/hooks/useGates.ts +57 -0
- package/src/hooks/useIdeaActions.ts +53 -0
- package/src/hooks/useKanbanDnd.ts +410 -0
- package/src/hooks/useOrbitalConfig.ts +54 -0
- package/src/hooks/usePipeline.ts +47 -0
- package/src/hooks/usePipelineData.ts +338 -0
- package/src/hooks/useReconnect.ts +25 -0
- package/src/hooks/useScopeFilters.ts +125 -0
- package/src/hooks/useScopeSessions.ts +44 -0
- package/src/hooks/useScopes.ts +67 -0
- package/src/hooks/useSearch.ts +67 -0
- package/src/hooks/useSettings.tsx +187 -0
- package/src/hooks/useSocket.ts +25 -0
- package/src/hooks/useSourceControl.ts +105 -0
- package/src/hooks/useSprintPreflight.ts +55 -0
- package/src/hooks/useSprints.ts +154 -0
- package/src/hooks/useStatusBarHighlight.ts +18 -0
- package/src/hooks/useSwimlaneBoardSettings.ts +104 -0
- package/src/hooks/useTheme.ts +9 -0
- package/src/hooks/useTransitionReadiness.ts +53 -0
- package/src/hooks/useVersion.ts +155 -0
- package/src/hooks/useViolations.ts +65 -0
- package/src/hooks/useWorkflow.tsx +125 -0
- package/src/hooks/useZoomModifier.ts +19 -0
- package/src/index.css +797 -0
- package/src/layouts/DashboardLayout.tsx +113 -0
- package/src/lib/collisionDetection.ts +20 -0
- package/src/lib/scope-fields.ts +61 -0
- package/src/lib/swimlane.ts +146 -0
- package/src/lib/utils.ts +15 -0
- package/src/main.tsx +19 -0
- package/src/socket.ts +11 -0
- package/src/types/index.ts +497 -0
- package/src/views/AgentFeed.tsx +339 -0
- package/src/views/DeployPipeline.tsx +59 -0
- package/src/views/EnforcementView.tsx +378 -0
- package/src/views/PrimitivesConfig.tsx +500 -0
- package/src/views/QualityGates.tsx +1012 -0
- package/src/views/ScopeBoard.tsx +454 -0
- package/src/views/SessionTimeline.tsx +516 -0
- package/src/views/Settings.tsx +183 -0
- package/src/views/SourceControl.tsx +95 -0
- package/src/views/WorkflowVisualizer.tsx +382 -0
- package/tailwind.config.js +161 -0
- package/templates/agents/AUTO-INVOKE.md +180 -0
- package/templates/agents/CONFLICT-RESOLUTION.md +128 -0
- package/templates/agents/QUICK-REFERENCE.md +122 -0
- package/templates/agents/README.md +188 -0
- package/templates/agents/SKILL-TRIGGERS.md +100 -0
- package/templates/agents/blue-team/frontend-designer.md +424 -0
- package/templates/agents/green-team/architect.md +526 -0
- package/templates/agents/green-team/rules-enforcer.md +131 -0
- package/templates/agents/red-team/attacker-learned.md +24 -0
- package/templates/agents/red-team/attacker.md +486 -0
- package/templates/agents/red-team/chaos.md +548 -0
- package/templates/agents/reference/component-registry.md +82 -0
- package/templates/agents/workflows/full-mode.md +218 -0
- package/templates/agents/workflows/quick-mode.md +118 -0
- package/templates/agents/workflows/security-mode.md +283 -0
- package/templates/anti-patterns/dangerous-shortcuts.md +427 -0
- package/templates/config/agent-triggers.json +92 -0
- package/templates/hooks/agent-team-gate.sh +31 -0
- package/templates/hooks/agent-trigger.sh +97 -0
- package/templates/hooks/block-push.sh +66 -0
- package/templates/hooks/block-workarounds.sh +61 -0
- package/templates/hooks/blocker-check.sh +28 -0
- package/templates/hooks/completion-checklist.sh +28 -0
- package/templates/hooks/decision-capture.sh +15 -0
- package/templates/hooks/dependency-check.sh +27 -0
- package/templates/hooks/end-session.sh +31 -0
- package/templates/hooks/exploration-logger.sh +37 -0
- package/templates/hooks/files-changed-summary.sh +37 -0
- package/templates/hooks/get-session-id.sh +49 -0
- package/templates/hooks/git-commit-guard.sh +34 -0
- package/templates/hooks/init-session.sh +93 -0
- package/templates/hooks/orbital-emit.sh +79 -0
- package/templates/hooks/orbital-report-deploy.sh +78 -0
- package/templates/hooks/orbital-report-gates.sh +40 -0
- package/templates/hooks/orbital-report-violation.sh +36 -0
- package/templates/hooks/orbital-scope-update.sh +53 -0
- package/templates/hooks/phase-verify-reminder.sh +26 -0
- package/templates/hooks/review-gate-check.sh +82 -0
- package/templates/hooks/scope-commit-logger.sh +37 -0
- package/templates/hooks/scope-create-cleanup.sh +36 -0
- package/templates/hooks/scope-create-gate.sh +80 -0
- package/templates/hooks/scope-create-tracker.sh +17 -0
- package/templates/hooks/scope-file-sync.sh +53 -0
- package/templates/hooks/scope-gate.sh +35 -0
- package/templates/hooks/scope-helpers.sh +188 -0
- package/templates/hooks/scope-lifecycle-gate.sh +139 -0
- package/templates/hooks/scope-prepare.sh +244 -0
- package/templates/hooks/scope-transition.sh +172 -0
- package/templates/hooks/session-enforcer.sh +143 -0
- package/templates/hooks/time-tracker.sh +33 -0
- package/templates/lessons-learned.md +15 -0
- package/templates/orbital.config.json +35 -0
- package/templates/presets/development.json +42 -0
- package/templates/presets/gitflow.json +712 -0
- package/templates/presets/minimal.json +23 -0
- package/templates/quick/rules.md +218 -0
- package/templates/scopes/_template.md +255 -0
- package/templates/settings-hooks.json +98 -0
- package/templates/skills/git-commit/SKILL.md +85 -0
- package/templates/skills/git-dev/SKILL.md +99 -0
- package/templates/skills/git-hotfix/SKILL.md +223 -0
- package/templates/skills/git-main/SKILL.md +84 -0
- package/templates/skills/git-production/SKILL.md +165 -0
- package/templates/skills/git-staging/SKILL.md +112 -0
- package/templates/skills/scope-create/SKILL.md +81 -0
- package/templates/skills/scope-fix-review/SKILL.md +168 -0
- package/templates/skills/scope-implement/SKILL.md +110 -0
- package/templates/skills/scope-post-review/SKILL.md +144 -0
- package/templates/skills/scope-pre-review/SKILL.md +211 -0
- package/templates/skills/scope-verify/SKILL.md +201 -0
- package/templates/skills/session-init/SKILL.md +62 -0
- package/templates/skills/session-resume/SKILL.md +201 -0
- package/templates/skills/test-checks/SKILL.md +171 -0
- package/templates/skills/test-code-review/SKILL.md +252 -0
- package/tsconfig.json +25 -0
- package/vite.config.ts +38 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { getHookEnforcement } from './workflow-config.js';
|
|
2
|
+
// ─── WorkflowEngine ─────────────────────────────────────────
|
|
3
|
+
//
|
|
4
|
+
// Config-driven query layer over the WorkflowConfig JSON.
|
|
5
|
+
// Pure class — no I/O, no global state, no side effects.
|
|
6
|
+
export class WorkflowEngine {
|
|
7
|
+
config;
|
|
8
|
+
listMap;
|
|
9
|
+
edgeMap;
|
|
10
|
+
edgesByFrom;
|
|
11
|
+
statusOrder;
|
|
12
|
+
hookMap;
|
|
13
|
+
terminalStatuses;
|
|
14
|
+
allowedPrefixes;
|
|
15
|
+
constructor(config) {
|
|
16
|
+
this.init(config);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Hot-reload the engine with a new config. All services holding a reference
|
|
20
|
+
* to this engine instance will see the updated config immediately.
|
|
21
|
+
*/
|
|
22
|
+
reload(config) {
|
|
23
|
+
this.init(config);
|
|
24
|
+
}
|
|
25
|
+
init(config) {
|
|
26
|
+
this.config = config;
|
|
27
|
+
if (!config.lists.length)
|
|
28
|
+
throw new Error('WorkflowConfig must have at least 1 list');
|
|
29
|
+
if (!config.edges.length)
|
|
30
|
+
throw new Error('WorkflowConfig must have at least 1 edge');
|
|
31
|
+
const entryPoints = config.lists.filter((l) => l.isEntryPoint);
|
|
32
|
+
if (entryPoints.length !== 1) {
|
|
33
|
+
throw new Error(`WorkflowConfig must have exactly 1 entry point, found ${entryPoints.length}`);
|
|
34
|
+
}
|
|
35
|
+
this.listMap = new Map(config.lists.map((l) => [l.id, l]));
|
|
36
|
+
this.edgeMap = new Map(config.edges.map((e) => [`${e.from}:${e.to}`, e]));
|
|
37
|
+
this.edgesByFrom = new Map();
|
|
38
|
+
for (const edge of config.edges) {
|
|
39
|
+
const existing = this.edgesByFrom.get(edge.from);
|
|
40
|
+
if (existing)
|
|
41
|
+
existing.push(edge);
|
|
42
|
+
else
|
|
43
|
+
this.edgesByFrom.set(edge.from, [edge]);
|
|
44
|
+
}
|
|
45
|
+
this.statusOrder = new Map(config.lists.map((l) => [l.id, l.order]));
|
|
46
|
+
this.hookMap = new Map((config.hooks ?? []).map((h) => [h.id, h]));
|
|
47
|
+
this.terminalStatuses = new Set(config.terminalStatuses ?? []);
|
|
48
|
+
this.allowedPrefixes = config.allowedCommandPrefixes ?? [];
|
|
49
|
+
}
|
|
50
|
+
// ─── Config Access ──────────────────────────────────────────
|
|
51
|
+
getConfig() {
|
|
52
|
+
return this.config;
|
|
53
|
+
}
|
|
54
|
+
getBranchingMode() {
|
|
55
|
+
return this.config.branchingMode ?? 'trunk';
|
|
56
|
+
}
|
|
57
|
+
// ─── List Queries ───────────────────────────────────────────
|
|
58
|
+
getLists() {
|
|
59
|
+
return [...this.config.lists].sort((a, b) => a.order - b.order);
|
|
60
|
+
}
|
|
61
|
+
getList(id) {
|
|
62
|
+
return this.listMap.get(id);
|
|
63
|
+
}
|
|
64
|
+
getEntryPoint() {
|
|
65
|
+
return this.config.lists.find((l) => l.isEntryPoint);
|
|
66
|
+
}
|
|
67
|
+
getBatchLists() {
|
|
68
|
+
return this.config.lists.filter((l) => l.supportsBatch);
|
|
69
|
+
}
|
|
70
|
+
getSprintLists() {
|
|
71
|
+
return this.config.lists.filter((l) => l.supportsSprint);
|
|
72
|
+
}
|
|
73
|
+
getBoardColumns() {
|
|
74
|
+
return this.getLists().map((l) => ({ id: l.id, label: l.label, color: l.color }));
|
|
75
|
+
}
|
|
76
|
+
// ─── Edge Queries ───────────────────────────────────────────
|
|
77
|
+
findEdge(from, to) {
|
|
78
|
+
return this.edgeMap.get(`${from}:${to}`);
|
|
79
|
+
}
|
|
80
|
+
isValidTransition(from, to) {
|
|
81
|
+
return this.edgeMap.has(`${from}:${to}`);
|
|
82
|
+
}
|
|
83
|
+
getValidTargets(from) {
|
|
84
|
+
return (this.edgesByFrom.get(from) ?? []).map((e) => e.to);
|
|
85
|
+
}
|
|
86
|
+
getAllEdges() {
|
|
87
|
+
return this.config.edges;
|
|
88
|
+
}
|
|
89
|
+
getEdgesByDirection(dir) {
|
|
90
|
+
return this.config.edges.filter((e) => e.direction === dir);
|
|
91
|
+
}
|
|
92
|
+
// ─── Validation ─────────────────────────────────────────────
|
|
93
|
+
validateTransition(from, to, context) {
|
|
94
|
+
if (!this.listMap.has(to)) {
|
|
95
|
+
return { ok: false, error: `Invalid status: '${to}'`, code: 'INVALID_STATUS' };
|
|
96
|
+
}
|
|
97
|
+
// bulk-sync and rollback are trusted contexts — skip transition checks
|
|
98
|
+
if (context === 'bulk-sync' || context === 'rollback')
|
|
99
|
+
return { ok: true };
|
|
100
|
+
// Same status is a no-op, always allowed
|
|
101
|
+
if (from === to)
|
|
102
|
+
return { ok: true };
|
|
103
|
+
const edge = this.findEdge(from, to);
|
|
104
|
+
if (!edge) {
|
|
105
|
+
return { ok: false, error: `Transition '${from}' -> '${to}' is not allowed`, code: 'INVALID_TRANSITION' };
|
|
106
|
+
}
|
|
107
|
+
// patch context cannot trigger dispatch-only transitions
|
|
108
|
+
if (context === 'patch' && edge.dispatchOnly) {
|
|
109
|
+
return { ok: false, error: `Transition '${from}' -> '${to}' requires dispatch (use a skill command)`, code: 'DISPATCH_REQUIRED' };
|
|
110
|
+
}
|
|
111
|
+
return { ok: true };
|
|
112
|
+
}
|
|
113
|
+
isValidStatus(status) {
|
|
114
|
+
return this.listMap.has(status);
|
|
115
|
+
}
|
|
116
|
+
isTerminalStatus(status) {
|
|
117
|
+
return this.terminalStatuses.has(status);
|
|
118
|
+
}
|
|
119
|
+
// ─── Commands ───────────────────────────────────────────────
|
|
120
|
+
buildCommand(edge, scopeId) {
|
|
121
|
+
if (!edge.command)
|
|
122
|
+
return null;
|
|
123
|
+
return edge.command.replace('{id}', String(scopeId));
|
|
124
|
+
}
|
|
125
|
+
isAllowedCommand(cmd) {
|
|
126
|
+
return this.allowedPrefixes.some((prefix) => cmd.startsWith(prefix));
|
|
127
|
+
}
|
|
128
|
+
// ─── Batch / Sprint ─────────────────────────────────────────
|
|
129
|
+
getBatchTargetStatus(column) {
|
|
130
|
+
const edges = this.edgesByFrom.get(column) ?? [];
|
|
131
|
+
const edge = edges.find((e) => (e.direction === 'forward' || e.direction === 'shortcut') && e.dispatchOnly);
|
|
132
|
+
return edge?.to;
|
|
133
|
+
}
|
|
134
|
+
getBatchCommand(column) {
|
|
135
|
+
const edges = this.edgesByFrom.get(column) ?? [];
|
|
136
|
+
const edge = edges.find((e) => (e.direction === 'forward' || e.direction === 'shortcut') && e.dispatchOnly);
|
|
137
|
+
if (!edge?.command)
|
|
138
|
+
return undefined;
|
|
139
|
+
// Strip {id} placeholder — batch consumers handle substitution themselves
|
|
140
|
+
return edge.command.replace(' {id}', '').replace('{id}', '');
|
|
141
|
+
}
|
|
142
|
+
// ─── Event Inference ────────────────────────────────────────
|
|
143
|
+
inferStatus(eventType, currentStatus, data) {
|
|
144
|
+
const rules = (this.config.eventInference ?? []).filter((r) => r.eventType === eventType);
|
|
145
|
+
if (!rules.length)
|
|
146
|
+
return null;
|
|
147
|
+
// Check rules with conditions first (more specific), then without
|
|
148
|
+
const withConditions = rules.filter((r) => r.conditions && Object.keys(r.conditions).length > 0);
|
|
149
|
+
const withoutConditions = rules.filter((r) => !r.conditions || Object.keys(r.conditions).length === 0);
|
|
150
|
+
const matchedRule = withConditions.find((r) => this.matchesConditions(r.conditions, data))
|
|
151
|
+
?? withoutConditions[0]
|
|
152
|
+
?? null;
|
|
153
|
+
if (!matchedRule)
|
|
154
|
+
return null;
|
|
155
|
+
// Handle dispatch resolution (e.g. AGENT_COMPLETED with outcome)
|
|
156
|
+
if (matchedRule.conditions?.dispatchResolution === true) {
|
|
157
|
+
const outcome = data.outcome;
|
|
158
|
+
return { dispatchResolution: true, resolution: outcome === 'failure' ? 'failed' : 'completed' };
|
|
159
|
+
}
|
|
160
|
+
// Determine target status
|
|
161
|
+
let newStatus;
|
|
162
|
+
if (matchedRule.targetStatus === '' && matchedRule.dataField) {
|
|
163
|
+
const rawValue = String(data[matchedRule.dataField] ?? '');
|
|
164
|
+
if (matchedRule.dataMap) {
|
|
165
|
+
newStatus = matchedRule.dataMap[rawValue] ?? matchedRule.dataMap['_default'] ?? '';
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
newStatus = rawValue;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
newStatus = matchedRule.targetStatus;
|
|
173
|
+
}
|
|
174
|
+
if (!newStatus)
|
|
175
|
+
return null;
|
|
176
|
+
// Forward-only guard: event inference must not regress scope status
|
|
177
|
+
if (matchedRule.forwardOnly) {
|
|
178
|
+
const currentOrder = this.statusOrder.get(currentStatus) ?? -1;
|
|
179
|
+
const newOrder = this.statusOrder.get(newStatus) ?? -1;
|
|
180
|
+
if (newOrder <= currentOrder)
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
return newStatus;
|
|
184
|
+
}
|
|
185
|
+
matchesConditions(conditions, data) {
|
|
186
|
+
for (const [key, expected] of Object.entries(conditions)) {
|
|
187
|
+
// dispatchResolution is a flag, not a data check
|
|
188
|
+
if (key === 'dispatchResolution')
|
|
189
|
+
continue;
|
|
190
|
+
const actual = data[key];
|
|
191
|
+
if (Array.isArray(expected)) {
|
|
192
|
+
if (!expected.includes(actual))
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
if (actual !== expected)
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
// ─── Git / Lifecycle ────────────────────────────────────────
|
|
203
|
+
getListByGitBranch(branch) {
|
|
204
|
+
return this.config.lists.find((l) => l.gitBranch === branch);
|
|
205
|
+
}
|
|
206
|
+
getGitBranch(listId) {
|
|
207
|
+
return this.listMap.get(listId)?.gitBranch;
|
|
208
|
+
}
|
|
209
|
+
getSessionKey(listId) {
|
|
210
|
+
return this.listMap.get(listId)?.sessionKey;
|
|
211
|
+
}
|
|
212
|
+
getActiveHooksForList(listId) {
|
|
213
|
+
return this.listMap.get(listId)?.activeHooks ?? [];
|
|
214
|
+
}
|
|
215
|
+
getAgentsForEdge(from, to) {
|
|
216
|
+
return this.findEdge(from, to)?.agents ?? [];
|
|
217
|
+
}
|
|
218
|
+
// ─── Status Order ───────────────────────────────────────────
|
|
219
|
+
getStatusOrder(status) {
|
|
220
|
+
return this.statusOrder.get(status) ?? -1;
|
|
221
|
+
}
|
|
222
|
+
isForwardMovement(from, to) {
|
|
223
|
+
return this.getStatusOrder(to) > this.getStatusOrder(from);
|
|
224
|
+
}
|
|
225
|
+
// ─── Hooks ──────────────────────────────────────────────────
|
|
226
|
+
getHooksForEdge(from, to) {
|
|
227
|
+
const edge = this.findEdge(from, to);
|
|
228
|
+
if (!edge?.hooks?.length)
|
|
229
|
+
return [];
|
|
230
|
+
return edge.hooks
|
|
231
|
+
.map((id) => this.hookMap.get(id))
|
|
232
|
+
.filter((h) => h !== undefined);
|
|
233
|
+
}
|
|
234
|
+
getAllHooks() {
|
|
235
|
+
return this.config.hooks ?? [];
|
|
236
|
+
}
|
|
237
|
+
getHookEnforcement(hook) {
|
|
238
|
+
return getHookEnforcement(hook);
|
|
239
|
+
}
|
|
240
|
+
getHooksByCategory(category) {
|
|
241
|
+
return (this.config.hooks ?? []).filter((h) => h.category === category);
|
|
242
|
+
}
|
|
243
|
+
// ─── Generation ─────────────────────────────────────────────
|
|
244
|
+
generateCSSVariables() {
|
|
245
|
+
return this.getLists()
|
|
246
|
+
.map((l) => `--status-${l.id}: ${l.color};`)
|
|
247
|
+
.join('\n');
|
|
248
|
+
}
|
|
249
|
+
generateShellManifest() {
|
|
250
|
+
const lines = [];
|
|
251
|
+
const sorted = this.getLists();
|
|
252
|
+
// Header
|
|
253
|
+
lines.push('#!/bin/bash');
|
|
254
|
+
lines.push('# Auto-generated by WorkflowEngine — DO NOT EDIT');
|
|
255
|
+
lines.push(`# Generated: ${new Date().toISOString()}`);
|
|
256
|
+
lines.push(`# Workflow: "${this.config.name}" (version ${this.config.version})`);
|
|
257
|
+
lines.push('');
|
|
258
|
+
// Branching mode
|
|
259
|
+
lines.push('# ─── Branching mode (trunk or worktree) ───');
|
|
260
|
+
lines.push(`WORKFLOW_BRANCHING_MODE="${this.getBranchingMode()}"`);
|
|
261
|
+
lines.push('');
|
|
262
|
+
// Valid statuses
|
|
263
|
+
lines.push('# ─── Valid statuses (space-separated) ───');
|
|
264
|
+
lines.push(`WORKFLOW_STATUSES="${sorted.map((l) => l.id).join(' ')}"`);
|
|
265
|
+
lines.push('');
|
|
266
|
+
// Directory statuses
|
|
267
|
+
lines.push('# ─── Statuses that have a scopes/ subdirectory ───');
|
|
268
|
+
const dirStatuses = sorted.filter((l) => l.hasDirectory).map((l) => l.id);
|
|
269
|
+
lines.push(`WORKFLOW_DIR_STATUSES="${dirStatuses.join(' ')}"`);
|
|
270
|
+
lines.push('');
|
|
271
|
+
// Terminal statuses
|
|
272
|
+
lines.push('# ─── Terminal statuses ───');
|
|
273
|
+
lines.push(`WORKFLOW_TERMINAL_STATUSES="${[...this.terminalStatuses].join(' ')}"`);
|
|
274
|
+
lines.push('');
|
|
275
|
+
// Entry point
|
|
276
|
+
lines.push('# ─── Entry point status ───');
|
|
277
|
+
lines.push(`WORKFLOW_ENTRY_STATUS="${this.getEntryPoint().id}"`);
|
|
278
|
+
lines.push('');
|
|
279
|
+
// Edges
|
|
280
|
+
lines.push('# ─── Transition edges (from:to:sessionKey) ───');
|
|
281
|
+
lines.push('WORKFLOW_EDGES=(');
|
|
282
|
+
for (const edge of this.config.edges) {
|
|
283
|
+
const targetList = this.listMap.get(edge.to);
|
|
284
|
+
const sessionKey = targetList?.sessionKey ?? '';
|
|
285
|
+
lines.push(` "${edge.from}:${edge.to}:${sessionKey}"`);
|
|
286
|
+
}
|
|
287
|
+
lines.push(')');
|
|
288
|
+
lines.push('');
|
|
289
|
+
// Branch map
|
|
290
|
+
lines.push('# ─── Branch-to-transition mapping (gitBranch:from:to:sessionKey) ───');
|
|
291
|
+
lines.push('WORKFLOW_BRANCH_MAP=(');
|
|
292
|
+
for (const edge of this.config.edges) {
|
|
293
|
+
const targetList = this.listMap.get(edge.to);
|
|
294
|
+
if (targetList?.gitBranch) {
|
|
295
|
+
const sessionKey = targetList.sessionKey ?? '';
|
|
296
|
+
lines.push(` "${targetList.gitBranch}:${edge.from}:${edge.to}:${sessionKey}"`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
lines.push(')');
|
|
300
|
+
lines.push('');
|
|
301
|
+
// Commit branch patterns
|
|
302
|
+
lines.push('# ─── Commit session branch patterns (regex) ───');
|
|
303
|
+
lines.push(`WORKFLOW_COMMIT_BRANCHES="${this.config.commitBranchPatterns ?? ''}"`);
|
|
304
|
+
lines.push('');
|
|
305
|
+
// Direction aliases (deployment edges: forward+dispatchOnly targeting deployment-group lists)
|
|
306
|
+
lines.push('# ─── Backward-compat direction aliases (alias:from:to:sessionKey) ───');
|
|
307
|
+
lines.push('WORKFLOW_DIRECTION_ALIASES=(');
|
|
308
|
+
for (const edge of this.config.edges) {
|
|
309
|
+
if (edge.direction !== 'forward' || !edge.dispatchOnly)
|
|
310
|
+
continue;
|
|
311
|
+
const targetList = this.listMap.get(edge.to);
|
|
312
|
+
if (!targetList)
|
|
313
|
+
continue;
|
|
314
|
+
// Generate aliases for deployment-group targets (dev, staging, production)
|
|
315
|
+
const group = targetList.group;
|
|
316
|
+
if (group === 'deployment') {
|
|
317
|
+
const sessionKey = targetList.sessionKey ?? '';
|
|
318
|
+
lines.push(` "to-${edge.to}:${edge.from}:${edge.to}:${sessionKey}"`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
lines.push(')');
|
|
322
|
+
lines.push('');
|
|
323
|
+
// Helper functions
|
|
324
|
+
lines.push('# ─── Helper functions ──────────────────────────────');
|
|
325
|
+
lines.push('');
|
|
326
|
+
lines.push('status_to_dir() {');
|
|
327
|
+
lines.push(' local scope_status="$1"');
|
|
328
|
+
lines.push(' for s in $WORKFLOW_DIR_STATUSES; do');
|
|
329
|
+
lines.push(' [ "$s" = "$scope_status" ] && echo "$scope_status" && return 0');
|
|
330
|
+
lines.push(' done');
|
|
331
|
+
lines.push(' echo "$WORKFLOW_ENTRY_STATUS"');
|
|
332
|
+
lines.push('}');
|
|
333
|
+
lines.push('');
|
|
334
|
+
lines.push('status_to_branch() {');
|
|
335
|
+
lines.push(' local status="$1"');
|
|
336
|
+
lines.push(' for entry in "${WORKFLOW_BRANCH_MAP[@]}"; do');
|
|
337
|
+
lines.push(' IFS=\':\' read -r branch from to skey <<< "$entry"');
|
|
338
|
+
lines.push(' [ "$to" = "$status" ] && echo "$branch" && return 0');
|
|
339
|
+
lines.push(' done');
|
|
340
|
+
lines.push(' echo ""');
|
|
341
|
+
lines.push('}');
|
|
342
|
+
lines.push('');
|
|
343
|
+
lines.push('is_valid_status() {');
|
|
344
|
+
lines.push(' local status="$1"');
|
|
345
|
+
lines.push(' for s in $WORKFLOW_STATUSES; do');
|
|
346
|
+
lines.push(' [ "$s" = "$status" ] && return 0');
|
|
347
|
+
lines.push(' done');
|
|
348
|
+
lines.push(' return 1');
|
|
349
|
+
lines.push('}');
|
|
350
|
+
return lines.join('\n') + '\n';
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
export default WorkflowEngine;
|
package/index.html
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" class="h-full bg-gray-950" data-theme="neon-glass">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Orbital Command</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600;700&family=Space+Grotesk:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
|
10
|
+
</head>
|
|
11
|
+
<body class="h-full">
|
|
12
|
+
<div id="root" class="h-full"></div>
|
|
13
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "orbital-command",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Orbital Command — mission control dashboard for Claude Code projects",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"claude-code",
|
|
9
|
+
"project-management",
|
|
10
|
+
"kanban",
|
|
11
|
+
"mission-control",
|
|
12
|
+
"dashboard",
|
|
13
|
+
"workflow",
|
|
14
|
+
"ai-agents",
|
|
15
|
+
"scope-management",
|
|
16
|
+
"sprint",
|
|
17
|
+
"devtools"
|
|
18
|
+
],
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/SakaraLabs/orbital-command.git"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/SakaraLabs/orbital-command#readme",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/SakaraLabs/orbital-command/issues"
|
|
26
|
+
},
|
|
27
|
+
"bin": {
|
|
28
|
+
"orbital": "bin/orbital.js"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"bin/",
|
|
32
|
+
"dist/",
|
|
33
|
+
"server/",
|
|
34
|
+
"shared/",
|
|
35
|
+
"src/",
|
|
36
|
+
"templates/",
|
|
37
|
+
"schemas/",
|
|
38
|
+
"scripts/",
|
|
39
|
+
"index.html",
|
|
40
|
+
"vite.config.ts",
|
|
41
|
+
"tailwind.config.js",
|
|
42
|
+
"postcss.config.js",
|
|
43
|
+
"tsconfig.json",
|
|
44
|
+
"README.md",
|
|
45
|
+
"LICENSE"
|
|
46
|
+
],
|
|
47
|
+
"scripts": {
|
|
48
|
+
"dev": "orbital dev",
|
|
49
|
+
"dev:local": "trap 'lsof -ti:4444,4445 | xargs kill 2>/dev/null' EXIT; concurrently -n server,client -c blue,green \"npm run dev:server\" \"npm run dev:client\"",
|
|
50
|
+
"dev:server": "tsx watch server/index.ts",
|
|
51
|
+
"dev:client": "vite",
|
|
52
|
+
"build": "./node_modules/.bin/vite build",
|
|
53
|
+
"build:server": "./node_modules/.bin/tsc -p tsconfig.server.json",
|
|
54
|
+
"typecheck": "./node_modules/.bin/tsc --noEmit && ./node_modules/.bin/tsc --noEmit -p tsconfig.server.json",
|
|
55
|
+
"postinstall": "node scripts/postinstall.js",
|
|
56
|
+
"prepublishOnly": "npm run typecheck && npm run build && npm run build:server",
|
|
57
|
+
"preview": "vite preview"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"@dnd-kit/core": "^6.3.1",
|
|
61
|
+
"@dnd-kit/sortable": "^10.0.0",
|
|
62
|
+
"@dnd-kit/utilities": "^3.2.2",
|
|
63
|
+
"@radix-ui/react-dialog": "^1.1.4",
|
|
64
|
+
"@radix-ui/react-dropdown-menu": "^2.1.4",
|
|
65
|
+
"@radix-ui/react-popover": "^1.1.15",
|
|
66
|
+
"@radix-ui/react-scroll-area": "^1.2.2",
|
|
67
|
+
"@radix-ui/react-select": "^2.1.4",
|
|
68
|
+
"@radix-ui/react-separator": "^1.1.1",
|
|
69
|
+
"@radix-ui/react-slot": "^1.1.1",
|
|
70
|
+
"@radix-ui/react-tabs": "^1.1.2",
|
|
71
|
+
"@radix-ui/react-tooltip": "^1.1.6",
|
|
72
|
+
"@xyflow/react": "^12.10.1",
|
|
73
|
+
"better-sqlite3": "^12.0.0",
|
|
74
|
+
"chokidar": "^4.0.3",
|
|
75
|
+
"class-variance-authority": "^0.7.1",
|
|
76
|
+
"clsx": "^2.1.1",
|
|
77
|
+
"date-fns": "^4.1.0",
|
|
78
|
+
"express": "^4.21.2",
|
|
79
|
+
"framer-motion": "^11.15.0",
|
|
80
|
+
"gray-matter": "^4.0.3",
|
|
81
|
+
"lucide-react": "^0.577.0",
|
|
82
|
+
"react": "^18.3.1",
|
|
83
|
+
"react-dom": "^18.3.1",
|
|
84
|
+
"react-markdown": "^10.1.0",
|
|
85
|
+
"react-router-dom": "^6.30.2",
|
|
86
|
+
"recharts": "^2.15.0",
|
|
87
|
+
"remark-gfm": "^4.0.1",
|
|
88
|
+
"socket.io": "^4.8.1",
|
|
89
|
+
"socket.io-client": "^4.8.1",
|
|
90
|
+
"tailwind-merge": "^2.6.0",
|
|
91
|
+
"tsx": "^4.19.2",
|
|
92
|
+
"vite": "^6.0.7"
|
|
93
|
+
},
|
|
94
|
+
"devDependencies": {
|
|
95
|
+
"@types/better-sqlite3": "^7.6.12",
|
|
96
|
+
"@types/express": "^5.0.0",
|
|
97
|
+
"@types/react": "^18.3.18",
|
|
98
|
+
"@types/react-dom": "^18.3.5",
|
|
99
|
+
"@vitejs/plugin-react": "^4.7.0",
|
|
100
|
+
"autoprefixer": "^10.4.27",
|
|
101
|
+
"concurrently": "^9.2.1",
|
|
102
|
+
"postcss": "^8.5.8",
|
|
103
|
+
"tailwindcss": "^3.4.19",
|
|
104
|
+
"tailwindcss-animate": "^1.0.7",
|
|
105
|
+
"typescript": "^5.7.3"
|
|
106
|
+
},
|
|
107
|
+
"engines": {
|
|
108
|
+
"node": ">=18.0.0"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft-07/schema",
|
|
3
|
+
"$id": "https://orbital-command.dev/schemas/orbital.config.schema.json",
|
|
4
|
+
"title": "Orbital Command Configuration",
|
|
5
|
+
"description": "Configuration file for Orbital Command — mission control for Claude Code projects",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"projectName": { "type": "string", "description": "Display name in dashboard header", "default": "My Project" },
|
|
9
|
+
"scopesDir": { "type": "string", "description": "Directory for scope documents (relative to project root)", "default": "scopes" },
|
|
10
|
+
"eventsDir": { "type": "string", "description": "Directory for event bus files (relative to project root)", "default": ".claude/orbital-events" },
|
|
11
|
+
"dbDir": { "type": "string", "description": "Directory for SQLite database (relative to project root)", "default": ".claude/orbital" },
|
|
12
|
+
"configDir": { "type": "string", "description": "Directory for workflow config + manifest (relative to project root)", "default": ".claude/config" },
|
|
13
|
+
"serverPort": { "type": "integer", "description": "API server port", "default": 4444, "minimum": 1, "maximum": 65535 },
|
|
14
|
+
"clientPort": { "type": "integer", "description": "Vite dev server port", "default": 4445, "minimum": 1, "maximum": 65535 },
|
|
15
|
+
"terminal": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {
|
|
18
|
+
"adapter": { "type": "string", "enum": ["auto", "iterm2", "subprocess", "none"], "default": "auto", "description": "Terminal adapter for session launching" },
|
|
19
|
+
"profilePrefix": { "type": "string", "default": "Orbital", "description": "Prefix for iTerm2 dynamic profile names" }
|
|
20
|
+
},
|
|
21
|
+
"additionalProperties": false
|
|
22
|
+
},
|
|
23
|
+
"claude": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"properties": {
|
|
26
|
+
"executable": { "type": "string", "default": "claude", "description": "Path to Claude Code CLI executable" },
|
|
27
|
+
"flags": { "type": "array", "items": { "type": "string" }, "default": ["--dangerously-skip-permissions"], "description": "Default flags for Claude Code sessions" }
|
|
28
|
+
},
|
|
29
|
+
"additionalProperties": false
|
|
30
|
+
},
|
|
31
|
+
"commands": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"description": "Build/test/lint commands. Set to null to skip.",
|
|
34
|
+
"properties": {
|
|
35
|
+
"typeCheck": { "type": ["string", "null"], "default": null },
|
|
36
|
+
"lint": { "type": ["string", "null"], "default": null },
|
|
37
|
+
"build": { "type": ["string", "null"], "default": null },
|
|
38
|
+
"test": { "type": ["string", "null"], "default": null },
|
|
39
|
+
"validateTemplates": { "type": ["string", "null"], "default": null },
|
|
40
|
+
"validateDocs": { "type": ["string", "null"], "default": null },
|
|
41
|
+
"checkRules": { "type": ["string", "null"], "default": null }
|
|
42
|
+
},
|
|
43
|
+
"additionalProperties": false
|
|
44
|
+
},
|
|
45
|
+
"categories": {
|
|
46
|
+
"type": "array",
|
|
47
|
+
"items": { "type": "string" },
|
|
48
|
+
"default": ["feature", "bugfix", "refactor", "infrastructure", "docs"],
|
|
49
|
+
"description": "Scope categories shown in the dashboard"
|
|
50
|
+
},
|
|
51
|
+
"agents": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"description": "Agent definitions for the dashboard and agent system",
|
|
54
|
+
"items": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"required": ["id", "label", "emoji", "color"],
|
|
57
|
+
"properties": {
|
|
58
|
+
"id": { "type": "string", "description": "Agent identifier (lowercase, kebab-case)" },
|
|
59
|
+
"label": { "type": "string", "description": "Display name" },
|
|
60
|
+
"emoji": { "type": "string", "description": "Emoji icon for the agent" },
|
|
61
|
+
"color": { "type": "string", "description": "Hex color for dashboard display" }
|
|
62
|
+
},
|
|
63
|
+
"additionalProperties": false
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"logLevel": {
|
|
67
|
+
"type": "string",
|
|
68
|
+
"enum": ["debug", "info", "warn", "error"],
|
|
69
|
+
"default": "info",
|
|
70
|
+
"description": "Server log verbosity level"
|
|
71
|
+
},
|
|
72
|
+
"healthChecks": {
|
|
73
|
+
"type": "object",
|
|
74
|
+
"description": "Health check URLs by environment",
|
|
75
|
+
"properties": {
|
|
76
|
+
"staging": { "type": "string", "format": "uri" },
|
|
77
|
+
"production": { "type": "string", "format": "uri" }
|
|
78
|
+
},
|
|
79
|
+
"additionalProperties": { "type": "string", "format": "uri" }
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"additionalProperties": false
|
|
83
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Safety net for esbuild's platform-specific binary.
|
|
4
|
+
//
|
|
5
|
+
// esbuild (a transitive dependency of Vite) ships as a stub that downloads
|
|
6
|
+
// its native binary via postinstall. If that step was skipped or failed
|
|
7
|
+
// silently, this script re-triggers the download.
|
|
8
|
+
|
|
9
|
+
import { existsSync } from 'fs';
|
|
10
|
+
import { execFileSync } from 'child_process';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
13
|
+
|
|
14
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
16
|
+
const esbuildInstall = path.join(packageRoot, 'node_modules', 'esbuild', 'install.js');
|
|
17
|
+
|
|
18
|
+
if (existsSync(esbuildInstall)) {
|
|
19
|
+
try {
|
|
20
|
+
execFileSync('node', [esbuildInstall], { stdio: 'pipe' });
|
|
21
|
+
} catch {
|
|
22
|
+
// Already installed or not applicable
|
|
23
|
+
}
|
|
24
|
+
}
|
package/scripts/start.sh
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Start Orbital Command
|
|
3
|
+
# Usage: ./scripts/start.sh
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
+
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
9
|
+
|
|
10
|
+
cd "$PROJECT_DIR"
|
|
11
|
+
|
|
12
|
+
# Install deps if needed
|
|
13
|
+
if [ ! -d "node_modules" ]; then
|
|
14
|
+
echo "[Orbital] Installing dependencies..."
|
|
15
|
+
npm install
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# Start both server and client
|
|
19
|
+
echo "[Orbital] Starting Orbital Command..."
|
|
20
|
+
npm run dev
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { TerminalAdapter } from './terminal-adapter.js';
|
|
2
|
+
import { getConfig } from '../config.js';
|
|
3
|
+
import { ITerm2Adapter, isITerm2Available } from './iterm2-adapter.js';
|
|
4
|
+
import { SubprocessAdapter } from './subprocess-adapter.js';
|
|
5
|
+
|
|
6
|
+
export type { TerminalAdapter, LaunchOptions, CategorizedLaunchOptions, WindowCategory } from './terminal-adapter.js';
|
|
7
|
+
|
|
8
|
+
let _adapter: TerminalAdapter | null = null;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get the terminal adapter singleton.
|
|
12
|
+
* Auto-detects based on config and platform:
|
|
13
|
+
* - "iterm2" → iTerm2 adapter (macOS only)
|
|
14
|
+
* - "subprocess" → cross-platform subprocess fallback
|
|
15
|
+
* - "none" → no-op adapter (for headless/CI)
|
|
16
|
+
* - "auto" → iTerm2 if available, else subprocess
|
|
17
|
+
*/
|
|
18
|
+
export function getTerminalAdapter(): TerminalAdapter {
|
|
19
|
+
if (_adapter) return _adapter;
|
|
20
|
+
|
|
21
|
+
const preference = getConfig().terminal.adapter;
|
|
22
|
+
|
|
23
|
+
if (preference === 'none') {
|
|
24
|
+
_adapter = { launch: async () => {}, launchCategorized: async () => {} };
|
|
25
|
+
return _adapter;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (preference === 'iterm2' || preference === 'auto') {
|
|
29
|
+
try {
|
|
30
|
+
if (preference === 'iterm2' || isITerm2Available()) {
|
|
31
|
+
_adapter = new ITerm2Adapter();
|
|
32
|
+
return _adapter;
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
// iTerm2 adapter not available — fall through
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
_adapter = new SubprocessAdapter();
|
|
40
|
+
return _adapter;
|
|
41
|
+
}
|