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,142 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import type { SprintService, SprintStatus, GroupTargetColumn, GroupType } from '../services/sprint-service.js';
|
|
3
|
+
import type { SprintOrchestrator } from '../services/sprint-orchestrator.js';
|
|
4
|
+
import type { BatchOrchestrator } from '../services/batch-orchestrator.js';
|
|
5
|
+
|
|
6
|
+
interface SprintRouteDeps {
|
|
7
|
+
sprintService: SprintService;
|
|
8
|
+
sprintOrchestrator: SprintOrchestrator;
|
|
9
|
+
batchOrchestrator: BatchOrchestrator;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function createSprintRoutes({ sprintService, sprintOrchestrator, batchOrchestrator }: SprintRouteDeps): Router {
|
|
13
|
+
const router = Router();
|
|
14
|
+
|
|
15
|
+
router.post('/sprints', (req, res) => {
|
|
16
|
+
const { name, target_column, group_type } = req.body as {
|
|
17
|
+
name?: string; target_column?: GroupTargetColumn; group_type?: GroupType;
|
|
18
|
+
};
|
|
19
|
+
if (!name || !name.trim()) {
|
|
20
|
+
res.status(400).json({ error: 'name is required' });
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const sprint = sprintService.create(name.trim(), { target_column, group_type });
|
|
24
|
+
res.status(201).json(sprint);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
router.get('/sprints', (req, res) => {
|
|
28
|
+
const status = req.query.status as string | undefined;
|
|
29
|
+
const targetColumn = req.query.target_column as string | undefined;
|
|
30
|
+
const sprints = sprintService.getAll(
|
|
31
|
+
status as SprintStatus | undefined,
|
|
32
|
+
targetColumn as GroupTargetColumn | undefined,
|
|
33
|
+
);
|
|
34
|
+
res.json(sprints);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
router.get('/sprints/:id', (req, res) => {
|
|
38
|
+
const sprint = sprintService.getById(Number(req.params.id));
|
|
39
|
+
if (!sprint) {
|
|
40
|
+
res.status(404).json({ error: 'Sprint not found' });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
res.json(sprint);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
router.patch('/sprints/:id', (req, res) => {
|
|
47
|
+
const { name } = req.body as { name?: string };
|
|
48
|
+
if (!name || !name.trim()) {
|
|
49
|
+
res.status(400).json({ error: 'name is required' });
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const renamed = sprintService.rename(Number(req.params.id), name.trim());
|
|
53
|
+
if (!renamed) {
|
|
54
|
+
res.status(400).json({ error: 'Sprint not found or not in assembling state' });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
res.json({ ok: true });
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
router.delete('/sprints/:id', (req, res) => {
|
|
61
|
+
const deleted = sprintService.delete(Number(req.params.id));
|
|
62
|
+
if (!deleted) {
|
|
63
|
+
res.status(400).json({ error: 'Cannot delete sprint (must be assembling)' });
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
res.json({ ok: true });
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
router.post('/sprints/:id/scopes', (req, res) => {
|
|
70
|
+
const { scope_ids } = req.body as { scope_ids?: number[] };
|
|
71
|
+
if (!Array.isArray(scope_ids) || scope_ids.length === 0) {
|
|
72
|
+
res.status(400).json({ error: 'scope_ids must be a non-empty array' });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const result = sprintService.addScopes(Number(req.params.id), scope_ids);
|
|
76
|
+
if (!result) {
|
|
77
|
+
res.status(400).json({ error: 'Sprint not found or not in assembling state' });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
res.json(result);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
router.delete('/sprints/:id/scopes', (req, res) => {
|
|
84
|
+
const { scope_ids } = req.body as { scope_ids?: number[] };
|
|
85
|
+
if (!Array.isArray(scope_ids) || scope_ids.length === 0) {
|
|
86
|
+
res.status(400).json({ error: 'scope_ids must be a non-empty array' });
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const removed = sprintService.removeScopes(Number(req.params.id), scope_ids);
|
|
90
|
+
if (!removed) {
|
|
91
|
+
res.status(400).json({ error: 'Sprint not found or not in assembling state' });
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
res.json({ ok: true });
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
router.post('/sprints/:id/dispatch', async (req, res) => {
|
|
98
|
+
const id = Number(req.params.id);
|
|
99
|
+
const sprint = sprintService.getById(id);
|
|
100
|
+
if (!sprint) {
|
|
101
|
+
res.status(404).json({ error: 'Sprint not found' });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (sprint.group_type === 'batch') {
|
|
106
|
+
const { mergeMode } = req.body as { mergeMode?: string };
|
|
107
|
+
const result = await batchOrchestrator.dispatch(id, mergeMode);
|
|
108
|
+
if (!result.ok) {
|
|
109
|
+
res.status(400).json({ error: result.error });
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
res.json({ ok: true });
|
|
113
|
+
} else {
|
|
114
|
+
const result = await sprintOrchestrator.startSprint(id);
|
|
115
|
+
if (!result.ok) {
|
|
116
|
+
res.status(400).json({ error: result.error });
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
res.json({ ok: true, layers: result.layers });
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
router.post('/sprints/:id/cancel', (req, res) => {
|
|
124
|
+
const cancelled = sprintOrchestrator.cancelSprint(Number(req.params.id));
|
|
125
|
+
if (!cancelled) {
|
|
126
|
+
res.status(400).json({ error: 'Sprint not found or cannot be cancelled' });
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
res.json({ ok: true });
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
router.get('/sprints/:id/graph', (req, res) => {
|
|
133
|
+
const graph = sprintOrchestrator.getExecutionGraph(Number(req.params.id));
|
|
134
|
+
if (!graph) {
|
|
135
|
+
res.status(404).json({ error: 'Sprint not found' });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
res.json(graph);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return router;
|
|
142
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { execFile } from 'child_process';
|
|
3
|
+
import { promisify } from 'util';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import type { Server } from 'socket.io';
|
|
8
|
+
import { createLogger } from '../utils/logger.js';
|
|
9
|
+
|
|
10
|
+
const log = createLogger('version');
|
|
11
|
+
|
|
12
|
+
const execFileAsync = promisify(execFile);
|
|
13
|
+
|
|
14
|
+
interface VersionRouteDeps {
|
|
15
|
+
io: Server;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Resolve the root directory of the orbital-command package itself. */
|
|
19
|
+
function getOrbitalRoot(): string {
|
|
20
|
+
const __selfDir = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
// Walk up until we find package.json (handles both dev and compiled paths)
|
|
22
|
+
let dir = __selfDir;
|
|
23
|
+
for (let i = 0; i < 6; i++) {
|
|
24
|
+
if (fs.existsSync(path.join(dir, 'package.json'))) {
|
|
25
|
+
return dir;
|
|
26
|
+
}
|
|
27
|
+
dir = path.dirname(dir);
|
|
28
|
+
}
|
|
29
|
+
// Fallback: assume dev layout (server/routes/ → 2 levels up)
|
|
30
|
+
return path.resolve(__selfDir, '../..');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function git(args: string[], cwd: string, timeoutMs = 15_000): Promise<string> {
|
|
34
|
+
const { stdout } = await execFileAsync('git', args, { cwd, timeout: timeoutMs });
|
|
35
|
+
return stdout.trim();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function createVersionRoutes({ io }: VersionRouteDeps): Router {
|
|
39
|
+
const router = Router();
|
|
40
|
+
const orbitalRoot = getOrbitalRoot();
|
|
41
|
+
|
|
42
|
+
// GET /version — current version info
|
|
43
|
+
router.get('/version', async (_req, res) => {
|
|
44
|
+
try {
|
|
45
|
+
const pkgPath = path.join(orbitalRoot, 'package.json');
|
|
46
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
47
|
+
|
|
48
|
+
let commitSha = 'unknown';
|
|
49
|
+
let branch = 'unknown';
|
|
50
|
+
try {
|
|
51
|
+
commitSha = await git(['rev-parse', '--short', 'HEAD'], orbitalRoot);
|
|
52
|
+
branch = await git(['rev-parse', '--abbrev-ref', 'HEAD'], orbitalRoot);
|
|
53
|
+
} catch {
|
|
54
|
+
// Not a git repo (installed via npm) — version only
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
res.json({
|
|
58
|
+
version: pkg.version,
|
|
59
|
+
commitSha,
|
|
60
|
+
branch,
|
|
61
|
+
});
|
|
62
|
+
} catch (err) {
|
|
63
|
+
log.error('Version route error', { error: (err as Error).message });
|
|
64
|
+
res.status(500).json({ error: `Failed to read version: ${(err as Error).message}` });
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// GET /version/check — fetch from remote and compare SHAs
|
|
69
|
+
router.get('/version/check', async (_req, res) => {
|
|
70
|
+
try {
|
|
71
|
+
const branch = await git(['rev-parse', '--abbrev-ref', 'HEAD'], orbitalRoot);
|
|
72
|
+
await git(['fetch', 'origin', branch, '--quiet'], orbitalRoot);
|
|
73
|
+
|
|
74
|
+
const localSha = await git(['rev-parse', 'HEAD'], orbitalRoot);
|
|
75
|
+
const remoteSha = await git(['rev-parse', `origin/${branch}`], orbitalRoot);
|
|
76
|
+
|
|
77
|
+
let behindCount = 0;
|
|
78
|
+
if (localSha !== remoteSha) {
|
|
79
|
+
const countStr = await git(
|
|
80
|
+
['rev-list', '--count', `HEAD..origin/${branch}`],
|
|
81
|
+
orbitalRoot,
|
|
82
|
+
);
|
|
83
|
+
behindCount = parseInt(countStr, 10) || 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
res.json({
|
|
87
|
+
updateAvailable: behindCount > 0,
|
|
88
|
+
behindCount,
|
|
89
|
+
localSha: localSha.slice(0, 7),
|
|
90
|
+
remoteSha: remoteSha.slice(0, 7),
|
|
91
|
+
branch,
|
|
92
|
+
});
|
|
93
|
+
} catch (err) {
|
|
94
|
+
log.error('Version route error', { error: (err as Error).message });
|
|
95
|
+
res.status(500).json({ error: `Failed to check for updates: ${(err as Error).message}` });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// POST /version/update — git pull + npm install
|
|
100
|
+
router.post('/version/update', async (req, res) => {
|
|
101
|
+
if (req.headers['x-orbital-action'] !== 'update') {
|
|
102
|
+
res.status(403).json({ error: 'Missing required X-Orbital-Action header' });
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let stage = 'guard';
|
|
107
|
+
try {
|
|
108
|
+
// Guard: refuse if working tree is dirty
|
|
109
|
+
const status = await git(['status', '--porcelain'], orbitalRoot);
|
|
110
|
+
if (status.length > 0) {
|
|
111
|
+
res.status(409).json({
|
|
112
|
+
error: 'Working tree has uncommitted changes. Commit or stash before updating.',
|
|
113
|
+
dirty: true,
|
|
114
|
+
});
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
stage = 'pulling';
|
|
119
|
+
io.emit('version:updating', { stage });
|
|
120
|
+
|
|
121
|
+
await git(['pull', '--ff-only'], orbitalRoot);
|
|
122
|
+
|
|
123
|
+
stage = 'installing';
|
|
124
|
+
io.emit('version:updating', { stage });
|
|
125
|
+
|
|
126
|
+
await execFileAsync('npm', ['install'], {
|
|
127
|
+
cwd: orbitalRoot,
|
|
128
|
+
timeout: 120_000,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
io.emit('version:updated', { success: true });
|
|
132
|
+
|
|
133
|
+
// Read updated version
|
|
134
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(orbitalRoot, 'package.json'), 'utf-8'));
|
|
135
|
+
const commitSha = await git(['rev-parse', '--short', 'HEAD'], orbitalRoot);
|
|
136
|
+
|
|
137
|
+
res.json({
|
|
138
|
+
success: true,
|
|
139
|
+
version: pkg.version,
|
|
140
|
+
commitSha,
|
|
141
|
+
message: 'Update complete. Restart the server to apply changes.',
|
|
142
|
+
});
|
|
143
|
+
} catch (err) {
|
|
144
|
+
log.error('Version route error', { error: (err as Error).message });
|
|
145
|
+
const recovery = stage === 'installing'
|
|
146
|
+
? ' Git pull succeeded — run `npm install` manually to finish.'
|
|
147
|
+
: stage === 'pulling'
|
|
148
|
+
? ' No files were changed — safe to retry.'
|
|
149
|
+
: '';
|
|
150
|
+
io.emit('version:updated', { success: false, error: (err as Error).message });
|
|
151
|
+
res.status(500).json({ error: `Update failed at stage "${stage}": ${(err as Error).message}.${recovery}` });
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return router;
|
|
156
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import type { WorkflowConfig } from '../../shared/workflow-config.js';
|
|
5
|
+
import type { WorkflowService } from '../services/workflow-service.js';
|
|
6
|
+
import { parseCcHooks } from '../utils/cc-hooks-parser.js';
|
|
7
|
+
|
|
8
|
+
interface WorkflowRouteDeps {
|
|
9
|
+
workflowService: WorkflowService;
|
|
10
|
+
projectRoot: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function createWorkflowRoutes({ workflowService, projectRoot }: WorkflowRouteDeps): Router {
|
|
14
|
+
const router = Router();
|
|
15
|
+
|
|
16
|
+
// GET /workflow — returns active config
|
|
17
|
+
router.get('/workflow', (_req, res) => {
|
|
18
|
+
try {
|
|
19
|
+
res.json({ success: true, data: workflowService.getActive() });
|
|
20
|
+
} catch (err) {
|
|
21
|
+
res.status(500).json({ success: false, error: errMsg(err) });
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// PUT /workflow — validate and update active config
|
|
26
|
+
router.put('/workflow', (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const config = req.body as WorkflowConfig;
|
|
29
|
+
const result = workflowService.updateActive(config);
|
|
30
|
+
if (!result.valid) {
|
|
31
|
+
res.status(400).json({ success: false, error: 'Validation failed', data: result });
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
res.json({ success: true, data: result });
|
|
35
|
+
} catch (err) {
|
|
36
|
+
res.status(500).json({ success: false, error: errMsg(err) });
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// GET /workflow/presets — list all presets
|
|
41
|
+
router.get('/workflow/presets', (_req, res) => {
|
|
42
|
+
try {
|
|
43
|
+
res.json({ success: true, data: workflowService.listPresets() });
|
|
44
|
+
} catch (err) {
|
|
45
|
+
res.status(500).json({ success: false, error: errMsg(err) });
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// POST /workflow/presets — save current config as named preset
|
|
50
|
+
router.post('/workflow/presets', (req, res) => {
|
|
51
|
+
try {
|
|
52
|
+
const { name } = req.body as { name: string };
|
|
53
|
+
if (!name) {
|
|
54
|
+
res.status(400).json({ success: false, error: 'name is required' });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
workflowService.savePreset(name);
|
|
58
|
+
res.json({ success: true });
|
|
59
|
+
} catch (err) {
|
|
60
|
+
const msg = errMsg(err);
|
|
61
|
+
const status = msg.includes('Cannot overwrite') || msg.includes('must be') ? 400 : 500;
|
|
62
|
+
res.status(status).json({ success: false, error: msg });
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// GET /workflow/presets/:name — get specific preset
|
|
67
|
+
router.get('/workflow/presets/:name', (req, res) => {
|
|
68
|
+
try {
|
|
69
|
+
const config = workflowService.getPreset(req.params.name);
|
|
70
|
+
res.json({ success: true, data: config });
|
|
71
|
+
} catch (err) {
|
|
72
|
+
const msg = errMsg(err);
|
|
73
|
+
const status = msg.includes('not found') ? 404 : 500;
|
|
74
|
+
res.status(status).json({ success: false, error: msg });
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// DELETE /workflow/presets/:name — delete preset
|
|
79
|
+
router.delete('/workflow/presets/:name', (req, res) => {
|
|
80
|
+
try {
|
|
81
|
+
workflowService.deletePreset(req.params.name);
|
|
82
|
+
res.json({ success: true });
|
|
83
|
+
} catch (err) {
|
|
84
|
+
const msg = errMsg(err);
|
|
85
|
+
const status = msg.includes('Cannot delete') ? 400 : msg.includes('not found') ? 404 : 500;
|
|
86
|
+
res.status(status).json({ success: false, error: msg });
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// GET /workflow/hooks — returns all hooks with edge mapping
|
|
91
|
+
router.get('/workflow/hooks', (_req, res) => {
|
|
92
|
+
try {
|
|
93
|
+
const engine = workflowService.getEngine();
|
|
94
|
+
const hooks = engine.getAllHooks();
|
|
95
|
+
const edgeHookMap: Record<string, string[]> = {};
|
|
96
|
+
for (const edge of engine.getAllEdges()) {
|
|
97
|
+
if (edge.hooks && edge.hooks.length > 0) {
|
|
98
|
+
edgeHookMap[`${edge.from}:${edge.to}`] = edge.hooks;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
res.json({ success: true, data: { hooks, edgeHookMap } });
|
|
102
|
+
} catch (err) {
|
|
103
|
+
res.status(500).json({ success: false, error: errMsg(err) });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// POST /workflow/preview — dry-run migration preview
|
|
108
|
+
router.post('/workflow/preview', (req, res) => {
|
|
109
|
+
try {
|
|
110
|
+
const config = req.body as WorkflowConfig;
|
|
111
|
+
const plan = workflowService.previewMigration(config);
|
|
112
|
+
res.json({ success: true, data: plan });
|
|
113
|
+
} catch (err) {
|
|
114
|
+
res.status(500).json({ success: false, error: errMsg(err) });
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// GET /workflow/hooks/:id/source — read hook source file
|
|
119
|
+
router.get('/workflow/hooks/:id/source', async (req, res) => {
|
|
120
|
+
try {
|
|
121
|
+
const hookId = req.params.id;
|
|
122
|
+
const engine = workflowService.getEngine();
|
|
123
|
+
const hook = engine.getAllHooks().find((h) => h.id === hookId);
|
|
124
|
+
if (!hook) {
|
|
125
|
+
res.status(404).json({ success: false, error: `Hook '${hookId}' not found` });
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (hook.target.includes('..')) {
|
|
129
|
+
res.status(400).json({ success: false, error: 'Invalid hook target path' });
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const filePath = path.resolve(projectRoot, hook.target);
|
|
133
|
+
const content = await readFile(filePath, 'utf-8');
|
|
134
|
+
const lineCount = content.split('\n').length;
|
|
135
|
+
res.json({ success: true, data: { hookId, filePath: hook.target, content, lineCount } });
|
|
136
|
+
} catch (err) {
|
|
137
|
+
const msg = errMsg(err);
|
|
138
|
+
const status = msg.includes('ENOENT') ? 404 : 500;
|
|
139
|
+
res.status(status).json({ success: false, error: msg });
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// GET /workflow/claude-hooks — returns all Claude Code hooks from settings.local.json
|
|
144
|
+
router.get('/workflow/claude-hooks', (_req, res) => {
|
|
145
|
+
try {
|
|
146
|
+
const settingsPath = path.resolve(projectRoot, '.claude/settings.local.json');
|
|
147
|
+
const data = parseCcHooks(settingsPath);
|
|
148
|
+
res.json({ success: true, data });
|
|
149
|
+
} catch (err) {
|
|
150
|
+
res.status(500).json({ success: false, error: errMsg(err) });
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// GET /workflow/hooks/source — read any hook source file by path
|
|
155
|
+
router.get('/workflow/hooks/source', async (req, res) => {
|
|
156
|
+
try {
|
|
157
|
+
const hookPath = req.query.path as string | undefined;
|
|
158
|
+
if (!hookPath) {
|
|
159
|
+
res.status(400).json({ success: false, error: 'path query parameter is required' });
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (hookPath.includes('..')) {
|
|
163
|
+
res.status(400).json({ success: false, error: 'Invalid path: directory traversal not allowed' });
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const filePath = path.resolve(projectRoot, hookPath);
|
|
167
|
+
const content = await readFile(filePath, 'utf-8');
|
|
168
|
+
const lineCount = content.split('\n').length;
|
|
169
|
+
res.json({ success: true, data: { filePath: hookPath, content, lineCount } });
|
|
170
|
+
} catch (err) {
|
|
171
|
+
const msg = errMsg(err);
|
|
172
|
+
const status = msg.includes('ENOENT') ? 404 : 500;
|
|
173
|
+
res.status(status).json({ success: false, error: msg });
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// POST /workflow/apply — apply new config with orphan mappings
|
|
178
|
+
router.post('/workflow/apply', (req, res) => {
|
|
179
|
+
try {
|
|
180
|
+
const { config, orphanMappings } = req.body as {
|
|
181
|
+
config: WorkflowConfig;
|
|
182
|
+
orphanMappings: Record<string, string>;
|
|
183
|
+
};
|
|
184
|
+
const plan = workflowService.applyMigration(config, orphanMappings ?? {});
|
|
185
|
+
res.json({ success: true, data: plan });
|
|
186
|
+
} catch (err) {
|
|
187
|
+
const msg = errMsg(err);
|
|
188
|
+
const status = msg.includes('Missing orphan') || msg.includes('Validation failed') || msg.includes('not a valid') ? 400 : 500;
|
|
189
|
+
res.status(status).json({ success: false, error: msg });
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
return router;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function errMsg(err: unknown): string {
|
|
197
|
+
return err instanceof Error ? err.message : String(err);
|
|
198
|
+
}
|
package/server/schema.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export const SCHEMA_DDL = `
|
|
2
|
+
-- Events from hooks/watchers
|
|
3
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
4
|
+
id TEXT PRIMARY KEY,
|
|
5
|
+
type TEXT NOT NULL,
|
|
6
|
+
scope_id INTEGER,
|
|
7
|
+
session_id TEXT,
|
|
8
|
+
agent TEXT,
|
|
9
|
+
data TEXT DEFAULT '{}',
|
|
10
|
+
timestamp TEXT NOT NULL,
|
|
11
|
+
processed INTEGER DEFAULT 0
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
-- Quality gate results per scope
|
|
15
|
+
CREATE TABLE IF NOT EXISTS quality_gates (
|
|
16
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
17
|
+
scope_id INTEGER,
|
|
18
|
+
gate_name TEXT NOT NULL,
|
|
19
|
+
status TEXT NOT NULL,
|
|
20
|
+
details TEXT,
|
|
21
|
+
duration_ms INTEGER,
|
|
22
|
+
run_at TEXT NOT NULL,
|
|
23
|
+
commit_sha TEXT
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
-- Deployment tracking
|
|
27
|
+
CREATE TABLE IF NOT EXISTS deployments (
|
|
28
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
29
|
+
environment TEXT NOT NULL,
|
|
30
|
+
status TEXT NOT NULL,
|
|
31
|
+
commit_sha TEXT,
|
|
32
|
+
branch TEXT,
|
|
33
|
+
pr_number INTEGER,
|
|
34
|
+
health_check_url TEXT,
|
|
35
|
+
started_at TEXT,
|
|
36
|
+
completed_at TEXT,
|
|
37
|
+
details TEXT DEFAULT '{}'
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
-- Session history
|
|
41
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
42
|
+
id TEXT PRIMARY KEY,
|
|
43
|
+
scope_id INTEGER,
|
|
44
|
+
claude_session_id TEXT,
|
|
45
|
+
action TEXT,
|
|
46
|
+
started_at TEXT,
|
|
47
|
+
ended_at TEXT,
|
|
48
|
+
handoff_file TEXT,
|
|
49
|
+
summary TEXT,
|
|
50
|
+
discoveries TEXT DEFAULT '[]',
|
|
51
|
+
next_steps TEXT DEFAULT '[]',
|
|
52
|
+
progress_pct INTEGER
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
-- Sprint containers for batching scope dispatch
|
|
56
|
+
CREATE TABLE IF NOT EXISTS sprints (
|
|
57
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
58
|
+
name TEXT NOT NULL,
|
|
59
|
+
status TEXT NOT NULL DEFAULT 'assembling',
|
|
60
|
+
concurrency_cap INTEGER NOT NULL DEFAULT 5,
|
|
61
|
+
created_at TEXT NOT NULL,
|
|
62
|
+
updated_at TEXT NOT NULL,
|
|
63
|
+
dispatched_at TEXT,
|
|
64
|
+
completed_at TEXT,
|
|
65
|
+
dispatch_meta TEXT DEFAULT '{}'
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
CREATE TABLE IF NOT EXISTS sprint_scopes (
|
|
69
|
+
sprint_id INTEGER NOT NULL,
|
|
70
|
+
scope_id INTEGER NOT NULL,
|
|
71
|
+
layer INTEGER,
|
|
72
|
+
dispatch_status TEXT NOT NULL DEFAULT 'pending',
|
|
73
|
+
dispatched_at TEXT,
|
|
74
|
+
completed_at TEXT,
|
|
75
|
+
error TEXT,
|
|
76
|
+
PRIMARY KEY (sprint_id, scope_id),
|
|
77
|
+
FOREIGN KEY (sprint_id) REFERENCES sprints(id) ON DELETE CASCADE
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
-- Indexes for common queries
|
|
81
|
+
CREATE INDEX IF NOT EXISTS idx_events_type ON events(type);
|
|
82
|
+
CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
|
|
83
|
+
CREATE INDEX IF NOT EXISTS idx_events_scope_id ON events(scope_id);
|
|
84
|
+
CREATE INDEX IF NOT EXISTS idx_gates_scope_id ON quality_gates(scope_id);
|
|
85
|
+
CREATE INDEX IF NOT EXISTS idx_gates_run_at ON quality_gates(run_at);
|
|
86
|
+
CREATE INDEX IF NOT EXISTS idx_deployments_env ON deployments(environment);
|
|
87
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_scope ON sessions(scope_id);
|
|
88
|
+
CREATE INDEX IF NOT EXISTS idx_sprints_status ON sprints(status);
|
|
89
|
+
CREATE INDEX IF NOT EXISTS idx_sprint_scopes_sprint ON sprint_scopes(sprint_id);
|
|
90
|
+
`;
|