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,37 @@
|
|
|
1
|
+
import { execFileSync } from 'child_process';
|
|
2
|
+
import type { TerminalAdapter, LaunchOptions, CategorizedLaunchOptions } from './terminal-adapter.js';
|
|
3
|
+
import {
|
|
4
|
+
launchInTerminal,
|
|
5
|
+
launchInCategorizedTerminal,
|
|
6
|
+
} from '../utils/terminal-launcher.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* iTerm2 terminal adapter for macOS.
|
|
10
|
+
* Uses AppleScript to create windows, tabs, and dynamic profiles.
|
|
11
|
+
* Provides categorized window grouping (tabs grouped by workflow stage).
|
|
12
|
+
*/
|
|
13
|
+
export class ITerm2Adapter implements TerminalAdapter {
|
|
14
|
+
async launch(command: string, _opts?: LaunchOptions): Promise<void> {
|
|
15
|
+
await launchInTerminal(command);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async launchCategorized(command: string, fullCmd: string, opts?: CategorizedLaunchOptions): Promise<void> {
|
|
19
|
+
await launchInCategorizedTerminal(command, fullCmd, opts?.tabName);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ensureProfiles is handled directly via ensureDynamicProfiles(engine) in server startup
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Check if iTerm2 is available on this system */
|
|
26
|
+
export function isITerm2Available(): boolean {
|
|
27
|
+
if (process.platform !== 'darwin') return false;
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
execFileSync('osascript', [
|
|
31
|
+
'-e', 'tell application "System Events" to (name of processes) contains "iTerm2"',
|
|
32
|
+
], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
33
|
+
return true;
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import type { TerminalAdapter, LaunchOptions, CategorizedLaunchOptions } from './terminal-adapter.js';
|
|
3
|
+
import { createLogger } from '../utils/logger.js';
|
|
4
|
+
|
|
5
|
+
const log = createLogger('terminal');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Cross-platform terminal adapter using child_process.spawn.
|
|
9
|
+
* Runs Claude sessions as background subprocesses — works on any OS.
|
|
10
|
+
* Does not provide windowed/tabbed grouping (use iTerm2 adapter for that).
|
|
11
|
+
*/
|
|
12
|
+
export class SubprocessAdapter implements TerminalAdapter {
|
|
13
|
+
async launch(command: string, _opts?: LaunchOptions): Promise<void> {
|
|
14
|
+
const child = spawn('sh', ['-c', command], {
|
|
15
|
+
detached: true,
|
|
16
|
+
stdio: 'ignore',
|
|
17
|
+
});
|
|
18
|
+
child.on('error', (err: Error) => log.error('Subprocess launch failed', { error: err.message }));
|
|
19
|
+
child.unref();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async launchCategorized(_command: string, fullCmd: string, _opts?: CategorizedLaunchOptions): Promise<void> {
|
|
23
|
+
await this.launch(fullCmd);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal adapter interface for cross-platform session launching.
|
|
3
|
+
* Implementations handle the specifics of opening new terminal windows/tabs.
|
|
4
|
+
*/
|
|
5
|
+
export interface TerminalAdapter {
|
|
6
|
+
/** Launch a command in a new terminal window/process */
|
|
7
|
+
launch(command: string, opts?: LaunchOptions): Promise<void>;
|
|
8
|
+
|
|
9
|
+
/** Launch a command in a categorized window (tabs grouped by category) */
|
|
10
|
+
launchCategorized(command: string, fullCmd: string, opts?: CategorizedLaunchOptions): Promise<void>;
|
|
11
|
+
|
|
12
|
+
/** Set up any required profiles/configuration (e.g., iTerm2 dynamic profiles) */
|
|
13
|
+
ensureProfiles?(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface LaunchOptions {
|
|
17
|
+
name?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface CategorizedLaunchOptions {
|
|
21
|
+
tabName?: string | null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type WindowCategory = 'Scoping' | 'Planning' | 'Implementing' | 'Reviewing' | 'Deploying';
|
package/server/config.ts
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { execFileSync } from 'child_process';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { createLogger } from './utils/logger.js';
|
|
6
|
+
|
|
7
|
+
// ─── Types ──────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
export interface TerminalConfig {
|
|
10
|
+
adapter: 'auto' | 'iterm2' | 'subprocess' | 'none';
|
|
11
|
+
profilePrefix: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ClaudeConfig {
|
|
15
|
+
executable: string;
|
|
16
|
+
flags: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CommandsConfig {
|
|
20
|
+
typeCheck: string | null;
|
|
21
|
+
lint: string | null;
|
|
22
|
+
build: string | null;
|
|
23
|
+
test: string | null;
|
|
24
|
+
validateTemplates: string | null;
|
|
25
|
+
validateDocs: string | null;
|
|
26
|
+
checkRules: string | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type { AgentConfig } from '../shared/api-types.js';
|
|
30
|
+
import type { AgentConfig } from '../shared/api-types.js';
|
|
31
|
+
|
|
32
|
+
export interface OrbitalConfig {
|
|
33
|
+
projectName: string;
|
|
34
|
+
projectRoot: string;
|
|
35
|
+
|
|
36
|
+
// Directories (resolved to absolute paths)
|
|
37
|
+
scopesDir: string;
|
|
38
|
+
eventsDir: string;
|
|
39
|
+
dbDir: string;
|
|
40
|
+
configDir: string;
|
|
41
|
+
|
|
42
|
+
// Ports
|
|
43
|
+
serverPort: number;
|
|
44
|
+
clientPort: number;
|
|
45
|
+
|
|
46
|
+
// Terminal integration
|
|
47
|
+
terminal: TerminalConfig;
|
|
48
|
+
|
|
49
|
+
// Claude Code CLI
|
|
50
|
+
claude: ClaudeConfig;
|
|
51
|
+
|
|
52
|
+
// Build/test commands
|
|
53
|
+
commands: CommandsConfig;
|
|
54
|
+
|
|
55
|
+
// Logging
|
|
56
|
+
logLevel: 'debug' | 'info' | 'warn' | 'error';
|
|
57
|
+
|
|
58
|
+
// Dynamic configuration
|
|
59
|
+
categories: string[];
|
|
60
|
+
agents: AgentConfig[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ─── Defaults ───────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
const DEFAULT_CONFIG: Omit<OrbitalConfig, 'projectRoot'> = {
|
|
66
|
+
projectName: 'Project',
|
|
67
|
+
scopesDir: 'scopes',
|
|
68
|
+
eventsDir: '.claude/orbital-events',
|
|
69
|
+
dbDir: '.claude/orbital',
|
|
70
|
+
configDir: '.claude/config',
|
|
71
|
+
serverPort: 4444,
|
|
72
|
+
clientPort: 4445,
|
|
73
|
+
terminal: {
|
|
74
|
+
adapter: 'auto',
|
|
75
|
+
profilePrefix: 'Orbital',
|
|
76
|
+
},
|
|
77
|
+
claude: {
|
|
78
|
+
executable: 'claude',
|
|
79
|
+
flags: ['--dangerously-skip-permissions'],
|
|
80
|
+
},
|
|
81
|
+
commands: {
|
|
82
|
+
typeCheck: null,
|
|
83
|
+
lint: null,
|
|
84
|
+
build: null,
|
|
85
|
+
test: null,
|
|
86
|
+
validateTemplates: null,
|
|
87
|
+
validateDocs: null,
|
|
88
|
+
checkRules: null,
|
|
89
|
+
},
|
|
90
|
+
logLevel: 'info' as const,
|
|
91
|
+
categories: ['feature', 'bugfix', 'refactor', 'infrastructure', 'docs'],
|
|
92
|
+
agents: [
|
|
93
|
+
{ id: 'attacker', label: 'Attacker', emoji: '\u{1F5E1}\u{FE0F}', color: '#ff1744' },
|
|
94
|
+
{ id: 'chaos', label: 'Chaos', emoji: '\u{1F4A5}', color: '#F97316' },
|
|
95
|
+
{ id: 'solana-expert', label: 'Solana Expert', emoji: '\u{26D3}\u{FE0F}', color: '#8B5CF6' },
|
|
96
|
+
{ id: 'frontend-designer', label: 'Frontend Designer', emoji: '\u{1F3A8}', color: '#EC4899' },
|
|
97
|
+
{ id: 'architect', label: 'Architect', emoji: '\u{1F3D7}\u{FE0F}', color: '#536dfe' },
|
|
98
|
+
{ id: 'rules-enforcer', label: 'Rules Enforcer', emoji: '\u{1F4CB}', color: '#6B7280' },
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// ─── Project Root Resolution ────────────────────────────────
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Resolve the project root directory.
|
|
106
|
+
* Priority: ORBITAL_PROJECT_ROOT env > git rev-parse > cwd walk > cwd
|
|
107
|
+
*/
|
|
108
|
+
export function resolveProjectRoot(): string {
|
|
109
|
+
// 1. Explicit env var
|
|
110
|
+
if (process.env.ORBITAL_PROJECT_ROOT) {
|
|
111
|
+
return path.resolve(process.env.ORBITAL_PROJECT_ROOT);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 2. git rev-parse --show-toplevel
|
|
115
|
+
try {
|
|
116
|
+
const gitRoot = execFileSync('git', ['rev-parse', '--show-toplevel'], {
|
|
117
|
+
encoding: 'utf-8',
|
|
118
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
119
|
+
}).trim();
|
|
120
|
+
if (gitRoot) return gitRoot;
|
|
121
|
+
} catch {
|
|
122
|
+
// Not in a git repo — continue
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 3. Walk up from cwd looking for .git directory
|
|
126
|
+
let dir = process.cwd();
|
|
127
|
+
while (dir !== path.dirname(dir)) {
|
|
128
|
+
if (fs.existsSync(path.join(dir, '.git'))) return dir;
|
|
129
|
+
dir = path.dirname(dir);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 4. Fall back to cwd
|
|
133
|
+
return process.cwd();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ─── Claude Sessions Path ───────────────────────────────────
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Derive the Claude Code sessions directory for a project.
|
|
140
|
+
* Claude Code encodes the project path by replacing `/` with `-`.
|
|
141
|
+
* The leading `/` becomes the leading `-` in the directory name.
|
|
142
|
+
*/
|
|
143
|
+
export function getClaudeSessionsDir(projectRoot: string): string {
|
|
144
|
+
const encoded = projectRoot.replace(/\//g, '-');
|
|
145
|
+
return path.join(os.homedir(), '.claude', 'projects', encoded);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ─── Config Loading ─────────────────────────────────────────
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Load and merge orbital.config.json with defaults.
|
|
152
|
+
* Resolves all relative paths to absolute using projectRoot.
|
|
153
|
+
*/
|
|
154
|
+
export function loadConfig(projectRoot?: string): OrbitalConfig {
|
|
155
|
+
const root = projectRoot ?? resolveProjectRoot();
|
|
156
|
+
|
|
157
|
+
// Try loading user config
|
|
158
|
+
const configPath = path.join(root, '.claude', 'orbital.config.json');
|
|
159
|
+
let userConfig: Record<string, unknown> = {};
|
|
160
|
+
|
|
161
|
+
const log = createLogger('config');
|
|
162
|
+
|
|
163
|
+
if (fs.existsSync(configPath)) {
|
|
164
|
+
try {
|
|
165
|
+
userConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
166
|
+
} catch (err) {
|
|
167
|
+
log.warn('Failed to parse orbital.config.json — using defaults', { error: (err as Error).message });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Merge with defaults
|
|
172
|
+
const projectName = (userConfig.projectName as string) ?? DEFAULT_CONFIG.projectName;
|
|
173
|
+
|
|
174
|
+
const scopesDir = path.resolve(root, (userConfig.scopesDir as string) ?? DEFAULT_CONFIG.scopesDir);
|
|
175
|
+
const eventsDir = path.resolve(root, (userConfig.eventsDir as string) ?? DEFAULT_CONFIG.eventsDir);
|
|
176
|
+
const dbDir = path.resolve(root, (userConfig.dbDir as string) ?? DEFAULT_CONFIG.dbDir);
|
|
177
|
+
const configDir = path.resolve(root, (userConfig.configDir as string) ?? DEFAULT_CONFIG.configDir);
|
|
178
|
+
|
|
179
|
+
const serverPort = (userConfig.serverPort as number) ?? DEFAULT_CONFIG.serverPort;
|
|
180
|
+
const clientPort = (userConfig.clientPort as number) ?? DEFAULT_CONFIG.clientPort;
|
|
181
|
+
|
|
182
|
+
const terminal: TerminalConfig = {
|
|
183
|
+
...DEFAULT_CONFIG.terminal,
|
|
184
|
+
...(userConfig.terminal as Partial<TerminalConfig> ?? {}),
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const claude: ClaudeConfig = {
|
|
188
|
+
...DEFAULT_CONFIG.claude,
|
|
189
|
+
...(userConfig.claude as Partial<ClaudeConfig> ?? {}),
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const commands: CommandsConfig = {
|
|
193
|
+
...DEFAULT_CONFIG.commands,
|
|
194
|
+
...(userConfig.commands as Partial<CommandsConfig> ?? {}),
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const logLevel = (userConfig.logLevel as OrbitalConfig['logLevel']) ?? DEFAULT_CONFIG.logLevel;
|
|
198
|
+
const categories = (userConfig.categories as string[]) ?? DEFAULT_CONFIG.categories;
|
|
199
|
+
const agents = (userConfig.agents as AgentConfig[]) ?? DEFAULT_CONFIG.agents;
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
projectName,
|
|
203
|
+
projectRoot: root,
|
|
204
|
+
scopesDir,
|
|
205
|
+
eventsDir,
|
|
206
|
+
dbDir,
|
|
207
|
+
configDir,
|
|
208
|
+
serverPort,
|
|
209
|
+
clientPort,
|
|
210
|
+
terminal,
|
|
211
|
+
claude,
|
|
212
|
+
commands,
|
|
213
|
+
logLevel,
|
|
214
|
+
categories,
|
|
215
|
+
agents,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ─── Singleton ──────────────────────────────────────────────
|
|
220
|
+
|
|
221
|
+
let _config: OrbitalConfig | null = null;
|
|
222
|
+
|
|
223
|
+
/** Get the global config singleton. Lazy-loaded on first access. */
|
|
224
|
+
export function getConfig(): OrbitalConfig {
|
|
225
|
+
if (!_config) {
|
|
226
|
+
_config = loadConfig();
|
|
227
|
+
}
|
|
228
|
+
return _config;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Reset the config singleton (for testing or hot-reload). */
|
|
232
|
+
export function resetConfig(): void {
|
|
233
|
+
_config = null;
|
|
234
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import { SCHEMA_DDL } from './schema.js';
|
|
5
|
+
import { getConfig } from './config.js';
|
|
6
|
+
import { createLogger } from './utils/logger.js';
|
|
7
|
+
|
|
8
|
+
const log = createLogger('database');
|
|
9
|
+
|
|
10
|
+
function getDbPaths(): { dir: string; file: string } {
|
|
11
|
+
const config = getConfig();
|
|
12
|
+
return { dir: config.dbDir, file: path.join(config.dbDir, 'orbital.db') };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let db: Database.Database | null = null;
|
|
16
|
+
|
|
17
|
+
export function getDatabase(): Database.Database {
|
|
18
|
+
if (db) return db;
|
|
19
|
+
|
|
20
|
+
const { dir, file } = getDbPaths();
|
|
21
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
22
|
+
|
|
23
|
+
db = new Database(file);
|
|
24
|
+
log.info('Database initialized', { path: file });
|
|
25
|
+
|
|
26
|
+
// Performance pragmas for a local dev tool
|
|
27
|
+
db.pragma('journal_mode = WAL');
|
|
28
|
+
db.pragma('synchronous = NORMAL');
|
|
29
|
+
db.pragma('foreign_keys = ON');
|
|
30
|
+
|
|
31
|
+
// Run schema migrations (SQLite db.exec, not child_process)
|
|
32
|
+
db.exec(SCHEMA_DDL);
|
|
33
|
+
|
|
34
|
+
// Incremental migrations for existing databases
|
|
35
|
+
runMigrations(db);
|
|
36
|
+
|
|
37
|
+
return db;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Check if a table exists in the database */
|
|
41
|
+
function tableExists(database: Database.Database, tableName: string): boolean {
|
|
42
|
+
const row = database.prepare(
|
|
43
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name=?",
|
|
44
|
+
).get(tableName) as { name: string } | undefined;
|
|
45
|
+
return row !== undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Run incremental migrations for schema changes on existing databases */
|
|
49
|
+
function runMigrations(database: Database.Database): void {
|
|
50
|
+
log.debug('Running database migrations');
|
|
51
|
+
// Migration 2: Add claude_session_id column to sessions
|
|
52
|
+
const sessionCols = database.pragma('table_info(sessions)') as Array<{ name: string }>;
|
|
53
|
+
if (!sessionCols.some((c) => c.name === 'claude_session_id')) {
|
|
54
|
+
database.exec('ALTER TABLE sessions ADD COLUMN claude_session_id TEXT');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Migration 6: Add action column to sessions for frontmatter lifecycle phase
|
|
58
|
+
if (!sessionCols.some((c) => c.name === 'action')) {
|
|
59
|
+
database.exec('ALTER TABLE sessions ADD COLUMN action TEXT');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Migration 8: Add batch group columns to sprints
|
|
63
|
+
const sprintCols = database.pragma('table_info(sprints)') as Array<{ name: string }>;
|
|
64
|
+
if (!sprintCols.some((c) => c.name === 'target_column')) {
|
|
65
|
+
database.exec("ALTER TABLE sprints ADD COLUMN target_column TEXT DEFAULT 'backlog'");
|
|
66
|
+
database.exec("ALTER TABLE sprints ADD COLUMN group_type TEXT DEFAULT 'sprint'");
|
|
67
|
+
database.exec("ALTER TABLE sprints ADD COLUMN dispatch_result TEXT DEFAULT '{}'");
|
|
68
|
+
database.exec('CREATE INDEX IF NOT EXISTS idx_sprints_target_column ON sprints(target_column)');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Migration 7: Drop scopes table — scopes are now served from in-memory cache.
|
|
72
|
+
// Recreate sprint_scopes without the FK to scopes.
|
|
73
|
+
if (tableExists(database, 'scopes')) {
|
|
74
|
+
database.exec(`
|
|
75
|
+
-- Backup sprint_scopes data
|
|
76
|
+
CREATE TABLE IF NOT EXISTS sprint_scopes_backup AS SELECT * FROM sprint_scopes;
|
|
77
|
+
-- Drop tables with FK dependencies first
|
|
78
|
+
DROP TABLE IF EXISTS sprint_scopes;
|
|
79
|
+
-- Drop the scopes table
|
|
80
|
+
DROP TABLE IF EXISTS scopes;
|
|
81
|
+
-- Recreate sprint_scopes without FK to scopes
|
|
82
|
+
CREATE TABLE IF NOT EXISTS sprint_scopes (
|
|
83
|
+
sprint_id INTEGER NOT NULL,
|
|
84
|
+
scope_id INTEGER NOT NULL,
|
|
85
|
+
layer INTEGER,
|
|
86
|
+
dispatch_status TEXT NOT NULL DEFAULT 'pending',
|
|
87
|
+
dispatched_at TEXT,
|
|
88
|
+
completed_at TEXT,
|
|
89
|
+
error TEXT,
|
|
90
|
+
PRIMARY KEY (sprint_id, scope_id),
|
|
91
|
+
FOREIGN KEY (sprint_id) REFERENCES sprints(id) ON DELETE CASCADE
|
|
92
|
+
);
|
|
93
|
+
-- Restore data
|
|
94
|
+
INSERT OR IGNORE INTO sprint_scopes SELECT * FROM sprint_scopes_backup;
|
|
95
|
+
-- Cleanup
|
|
96
|
+
DROP TABLE IF EXISTS sprint_scopes_backup;
|
|
97
|
+
`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function closeDatabase(): void {
|
|
102
|
+
if (db) {
|
|
103
|
+
db.close();
|
|
104
|
+
db = null;
|
|
105
|
+
log.debug('Database closed');
|
|
106
|
+
}
|
|
107
|
+
}
|