shipwright-cli 1.10.0 → 2.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.
Files changed (121) hide show
  1. package/README.md +221 -55
  2. package/completions/_shipwright +264 -32
  3. package/completions/shipwright.bash +118 -26
  4. package/completions/shipwright.fish +80 -2
  5. package/dashboard/server.ts +208 -0
  6. package/docs/strategy/01-market-research.md +619 -0
  7. package/docs/strategy/02-mission-and-brand.md +587 -0
  8. package/docs/strategy/03-gtm-and-roadmap.md +759 -0
  9. package/docs/strategy/QUICK-START.txt +289 -0
  10. package/docs/strategy/README.md +172 -0
  11. package/docs/tmux-research/TMUX-ARCHITECTURE.md +567 -0
  12. package/docs/tmux-research/TMUX-AUDIT.md +925 -0
  13. package/docs/tmux-research/TMUX-BEST-PRACTICES-2025-2026.md +829 -0
  14. package/docs/tmux-research/TMUX-QUICK-REFERENCE.md +543 -0
  15. package/docs/tmux-research/TMUX-RESEARCH-INDEX.md +438 -0
  16. package/package.json +4 -2
  17. package/scripts/lib/helpers.sh +7 -0
  18. package/scripts/sw +323 -2
  19. package/scripts/sw-activity.sh +500 -0
  20. package/scripts/sw-adaptive.sh +925 -0
  21. package/scripts/sw-adversarial.sh +1 -1
  22. package/scripts/sw-architecture-enforcer.sh +1 -1
  23. package/scripts/sw-auth.sh +613 -0
  24. package/scripts/sw-autonomous.sh +754 -0
  25. package/scripts/sw-changelog.sh +704 -0
  26. package/scripts/sw-checkpoint.sh +1 -1
  27. package/scripts/sw-ci.sh +602 -0
  28. package/scripts/sw-cleanup.sh +1 -1
  29. package/scripts/sw-code-review.sh +698 -0
  30. package/scripts/sw-connect.sh +1 -1
  31. package/scripts/sw-context.sh +605 -0
  32. package/scripts/sw-cost.sh +44 -3
  33. package/scripts/sw-daemon.sh +568 -138
  34. package/scripts/sw-dashboard.sh +1 -1
  35. package/scripts/sw-db.sh +1380 -0
  36. package/scripts/sw-decompose.sh +539 -0
  37. package/scripts/sw-deps.sh +551 -0
  38. package/scripts/sw-developer-simulation.sh +1 -1
  39. package/scripts/sw-discovery.sh +412 -0
  40. package/scripts/sw-docs-agent.sh +539 -0
  41. package/scripts/sw-docs.sh +1 -1
  42. package/scripts/sw-doctor.sh +107 -1
  43. package/scripts/sw-dora.sh +615 -0
  44. package/scripts/sw-durable.sh +710 -0
  45. package/scripts/sw-e2e-orchestrator.sh +535 -0
  46. package/scripts/sw-eventbus.sh +393 -0
  47. package/scripts/sw-feedback.sh +479 -0
  48. package/scripts/sw-fix.sh +1 -1
  49. package/scripts/sw-fleet-discover.sh +567 -0
  50. package/scripts/sw-fleet-viz.sh +404 -0
  51. package/scripts/sw-fleet.sh +8 -1
  52. package/scripts/sw-github-app.sh +596 -0
  53. package/scripts/sw-github-checks.sh +4 -4
  54. package/scripts/sw-github-deploy.sh +1 -1
  55. package/scripts/sw-github-graphql.sh +1 -1
  56. package/scripts/sw-guild.sh +569 -0
  57. package/scripts/sw-heartbeat.sh +1 -1
  58. package/scripts/sw-hygiene.sh +559 -0
  59. package/scripts/sw-incident.sh +656 -0
  60. package/scripts/sw-init.sh +237 -24
  61. package/scripts/sw-instrument.sh +699 -0
  62. package/scripts/sw-intelligence.sh +1 -1
  63. package/scripts/sw-jira.sh +1 -1
  64. package/scripts/sw-launchd.sh +363 -28
  65. package/scripts/sw-linear.sh +1 -1
  66. package/scripts/sw-logs.sh +1 -1
  67. package/scripts/sw-loop.sh +267 -21
  68. package/scripts/sw-memory.sh +18 -1
  69. package/scripts/sw-mission-control.sh +487 -0
  70. package/scripts/sw-model-router.sh +545 -0
  71. package/scripts/sw-otel.sh +596 -0
  72. package/scripts/sw-oversight.sh +764 -0
  73. package/scripts/sw-pipeline-composer.sh +1 -1
  74. package/scripts/sw-pipeline-vitals.sh +1 -1
  75. package/scripts/sw-pipeline.sh +947 -35
  76. package/scripts/sw-pm.sh +758 -0
  77. package/scripts/sw-pr-lifecycle.sh +522 -0
  78. package/scripts/sw-predictive.sh +8 -1
  79. package/scripts/sw-prep.sh +1 -1
  80. package/scripts/sw-ps.sh +1 -1
  81. package/scripts/sw-public-dashboard.sh +798 -0
  82. package/scripts/sw-quality.sh +595 -0
  83. package/scripts/sw-reaper.sh +1 -1
  84. package/scripts/sw-recruit.sh +2248 -0
  85. package/scripts/sw-regression.sh +642 -0
  86. package/scripts/sw-release-manager.sh +736 -0
  87. package/scripts/sw-release.sh +706 -0
  88. package/scripts/sw-remote.sh +1 -1
  89. package/scripts/sw-replay.sh +520 -0
  90. package/scripts/sw-retro.sh +691 -0
  91. package/scripts/sw-scale.sh +444 -0
  92. package/scripts/sw-security-audit.sh +505 -0
  93. package/scripts/sw-self-optimize.sh +1 -1
  94. package/scripts/sw-session.sh +1 -1
  95. package/scripts/sw-setup.sh +263 -127
  96. package/scripts/sw-standup.sh +712 -0
  97. package/scripts/sw-status.sh +44 -2
  98. package/scripts/sw-strategic.sh +806 -0
  99. package/scripts/sw-stream.sh +450 -0
  100. package/scripts/sw-swarm.sh +620 -0
  101. package/scripts/sw-team-stages.sh +511 -0
  102. package/scripts/sw-templates.sh +4 -4
  103. package/scripts/sw-testgen.sh +566 -0
  104. package/scripts/sw-tmux-pipeline.sh +554 -0
  105. package/scripts/sw-tmux-role-color.sh +58 -0
  106. package/scripts/sw-tmux-status.sh +128 -0
  107. package/scripts/sw-tmux.sh +1 -1
  108. package/scripts/sw-trace.sh +485 -0
  109. package/scripts/sw-tracker-github.sh +188 -0
  110. package/scripts/sw-tracker-jira.sh +172 -0
  111. package/scripts/sw-tracker-linear.sh +251 -0
  112. package/scripts/sw-tracker.sh +117 -2
  113. package/scripts/sw-triage.sh +627 -0
  114. package/scripts/sw-upgrade.sh +1 -1
  115. package/scripts/sw-ux.sh +677 -0
  116. package/scripts/sw-webhook.sh +627 -0
  117. package/scripts/sw-widgets.sh +530 -0
  118. package/scripts/sw-worktree.sh +1 -1
  119. package/templates/pipelines/autonomous.json +2 -2
  120. package/tmux/shipwright-overlay.conf +35 -17
  121. package/tmux/tmux.conf +23 -21
@@ -7,8 +7,13 @@
7
7
  for cmd in shipwright sw cct
8
8
  complete -c $cmd -f
9
9
 
10
- # Top-level commands
11
- set -l all_cmds session status ps logs templates doctor cleanup reaper upgrade loop pipeline worktree prep daemon memory cost init help version
10
+ # Top-level commands (includes groups and flat commands)
11
+ set -l all_cmds agent quality observe release intel session status ps logs templates doctor cleanup reaper upgrade loop pipeline worktree prep daemon memory cost init help version
12
+ complete -c $cmd -n "not __fish_seen_subcommand_from $all_cmds" -a "agent" -d "Agent management (recruit, swarm, standup, guild, oversight)"
13
+ complete -c $cmd -n "not __fish_seen_subcommand_from $all_cmds" -a "quality" -d "Quality & review (code-review, security-audit, testgen, hygiene)"
14
+ complete -c $cmd -n "not __fish_seen_subcommand_from $all_cmds" -a "observe" -d "Observability (vitals, dora, retro, stream, activity, replay)"
15
+ complete -c $cmd -n "not __fish_seen_subcommand_from $all_cmds" -a "release" -d "Release & deploy (release, release-manager, changelog, deploy)"
16
+ complete -c $cmd -n "not __fish_seen_subcommand_from $all_cmds" -a "intel" -d "Intelligence (predict, intelligence, strategic, optimize)"
12
17
  complete -c $cmd -n "not __fish_seen_subcommand_from $all_cmds" -a "session" -d "Create a new tmux window for a Claude team"
13
18
  complete -c $cmd -n "not __fish_seen_subcommand_from $all_cmds" -a "status" -d "Show dashboard of running teams and agents"
14
19
  complete -c $cmd -n "not __fish_seen_subcommand_from $all_cmds" -a "ps" -d "Show running agent processes and status"
@@ -29,6 +34,40 @@ for cmd in shipwright sw cct
29
34
  complete -c $cmd -n "not __fish_seen_subcommand_from $all_cmds" -a "help" -d "Show help message"
30
35
  complete -c $cmd -n "not __fish_seen_subcommand_from $all_cmds" -a "version" -d "Show version"
31
36
 
37
+ # agent subcommands
38
+ complete -c $cmd -n "__fish_seen_subcommand_from agent" -a "recruit" -d "Agent recruitment & talent management"
39
+ complete -c $cmd -n "__fish_seen_subcommand_from agent" -a "swarm" -d "Dynamic agent swarm management"
40
+ complete -c $cmd -n "__fish_seen_subcommand_from agent" -a "standup" -d "Automated daily standups"
41
+ complete -c $cmd -n "__fish_seen_subcommand_from agent" -a "guild" -d "Knowledge guilds & cross-team learning"
42
+ complete -c $cmd -n "__fish_seen_subcommand_from agent" -a "oversight" -d "Quality oversight board"
43
+
44
+ # quality subcommands
45
+ complete -c $cmd -n "__fish_seen_subcommand_from quality" -a "code-review" -d "Clean code & architecture analysis"
46
+ complete -c $cmd -n "__fish_seen_subcommand_from quality" -a "security-audit" -d "Comprehensive security auditing"
47
+ complete -c $cmd -n "__fish_seen_subcommand_from quality" -a "testgen" -d "Autonomous test generation"
48
+ complete -c $cmd -n "__fish_seen_subcommand_from quality" -a "hygiene" -d "Repository organization & cleanup"
49
+
50
+ # observe subcommands
51
+ complete -c $cmd -n "__fish_seen_subcommand_from observe" -a "vitals" -d "Pipeline vitals — real-time scoring"
52
+ complete -c $cmd -n "__fish_seen_subcommand_from observe" -a "dora" -d "DORA metrics dashboard"
53
+ complete -c $cmd -n "__fish_seen_subcommand_from observe" -a "retro" -d "Sprint retrospective engine"
54
+ complete -c $cmd -n "__fish_seen_subcommand_from observe" -a "stream" -d "Live terminal output streaming"
55
+ complete -c $cmd -n "__fish_seen_subcommand_from observe" -a "activity" -d "Live agent activity stream"
56
+ complete -c $cmd -n "__fish_seen_subcommand_from observe" -a "replay" -d "Pipeline DVR — view past runs"
57
+ complete -c $cmd -n "__fish_seen_subcommand_from observe" -a "status" -d "Team status dashboard"
58
+
59
+ # release subcommands
60
+ complete -c $cmd -n "__fish_seen_subcommand_from release" -a "release" -d "Release train automation"
61
+ complete -c $cmd -n "__fish_seen_subcommand_from release" -a "release-manager" -d "Autonomous release pipeline"
62
+ complete -c $cmd -n "__fish_seen_subcommand_from release" -a "changelog" -d "Automated release notes"
63
+ complete -c $cmd -n "__fish_seen_subcommand_from release" -a "deploy" -d "Deployments — deployment history"
64
+
65
+ # intel subcommands
66
+ complete -c $cmd -n "__fish_seen_subcommand_from intel" -a "predict" -d "Predictive risk assessment"
67
+ complete -c $cmd -n "__fish_seen_subcommand_from intel" -a "intelligence" -d "Intelligence engine analysis"
68
+ complete -c $cmd -n "__fish_seen_subcommand_from intel" -a "strategic" -d "Strategic intelligence agent"
69
+ complete -c $cmd -n "__fish_seen_subcommand_from intel" -a "optimize" -d "Self-optimization based on DORA"
70
+
32
71
  # pipeline subcommands
33
72
  complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -a "start" -d "Start a new pipeline run"
34
73
  complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -a "resume" -d "Resume from last stage"
@@ -81,16 +120,28 @@ for cmd in shipwright sw cct
81
120
  complete -c $cmd -n "__fish_seen_subcommand_from prep" -l verbose -d "Verbose output"
82
121
 
83
122
  # loop flags
123
+ complete -c $cmd -n "__fish_seen_subcommand_from loop" -l repo -d "Change to directory before running" -r
124
+ complete -c $cmd -n "__fish_seen_subcommand_from loop" -l local -d "Local-only mode (no GitHub)"
84
125
  complete -c $cmd -n "__fish_seen_subcommand_from loop" -l test-cmd -d "Test command to verify each iteration" -r
126
+ complete -c $cmd -n "__fish_seen_subcommand_from loop" -l fast-test-cmd -d "Fast/subset test command" -r
127
+ complete -c $cmd -n "__fish_seen_subcommand_from loop" -l fast-test-interval -d "Run full tests every N iterations" -r
85
128
  complete -c $cmd -n "__fish_seen_subcommand_from loop" -l max-iterations -d "Maximum loop iterations" -r
86
129
  complete -c $cmd -n "__fish_seen_subcommand_from loop" -l model -d "Claude model to use" -ra "opus sonnet haiku"
87
130
  complete -c $cmd -n "__fish_seen_subcommand_from loop" -l agents -d "Number of agents" -r
131
+ complete -c $cmd -n "__fish_seen_subcommand_from loop" -l roles -d "Role per agent" -r
132
+ complete -c $cmd -n "__fish_seen_subcommand_from loop" -l worktree -d "Use git worktrees for isolation"
88
133
  complete -c $cmd -n "__fish_seen_subcommand_from loop" -l audit -d "Enable self-reflection each iteration"
89
134
  complete -c $cmd -n "__fish_seen_subcommand_from loop" -l audit-agent -d "Use separate auditor agent"
90
135
  complete -c $cmd -n "__fish_seen_subcommand_from loop" -l quality-gates -d "Enable automated quality checks"
91
136
  complete -c $cmd -n "__fish_seen_subcommand_from loop" -l definition-of-done -d "Custom completion checklist" -rF
137
+ complete -c $cmd -n "__fish_seen_subcommand_from loop" -l no-auto-extend -d "Disable auto-extension"
138
+ complete -c $cmd -n "__fish_seen_subcommand_from loop" -l extension-size -d "Additional iterations per extension" -r
139
+ complete -c $cmd -n "__fish_seen_subcommand_from loop" -l max-extensions -d "Max number of auto-extensions" -r
92
140
  complete -c $cmd -n "__fish_seen_subcommand_from loop" -l resume -d "Resume interrupted loop"
141
+ complete -c $cmd -n "__fish_seen_subcommand_from loop" -l max-restarts -d "Max session restarts" -r
142
+ complete -c $cmd -n "__fish_seen_subcommand_from loop" -l max-turns -d "Max API turns per session" -r
93
143
  complete -c $cmd -n "__fish_seen_subcommand_from loop" -l skip-permissions -d "Skip permission prompts"
144
+ complete -c $cmd -n "__fish_seen_subcommand_from loop" -l verbose -d "Show full Claude output"
94
145
 
95
146
  # logs flags
96
147
  complete -c $cmd -n "__fish_seen_subcommand_from logs" -l follow -d "Tail logs in real time"
@@ -104,4 +155,31 @@ for cmd in shipwright sw cct
104
155
 
105
156
  # reaper flags
106
157
  complete -c $cmd -n "__fish_seen_subcommand_from reaper" -l watch -d "Continuous watch mode"
158
+
159
+ # pipeline flags
160
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l issue -d "GitHub issue number" -r
161
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l goal -d "Goal description" -r
162
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l repo -d "Change to directory before running" -r
163
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l local -d "Local-only mode (no GitHub)"
164
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l pipeline -d "Pipeline template" -r
165
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l template -d "Pipeline template" -r
166
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l test-cmd -d "Test command to run" -r
167
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l model -d "AI model to use" -ra "opus sonnet haiku"
168
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l agents -d "Number of agents" -r
169
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l skip-gates -d "Auto-approve all gates"
170
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l base -d "Base branch for PR" -r
171
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l reviewers -d "PR reviewers" -r
172
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l labels -d "PR labels" -r
173
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l no-github -d "Disable GitHub integration"
174
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l no-github-label -d "Don't modify issue labels"
175
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l ci -d "CI mode (non-interactive)"
176
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l ignore-budget -d "Skip budget enforcement"
177
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l worktree -d "Run in isolated worktree"
178
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l dry-run -d "Show what would happen"
179
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l slack-webhook -d "Slack webhook URL" -r
180
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l self-heal -d "Build retry cycles" -r
181
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l max-iterations -d "Max build loop iterations" -r
182
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l max-restarts -d "Max session restarts" -r
183
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l fast-test-cmd -d "Fast/subset test command" -r
184
+ complete -c $cmd -n "__fish_seen_subcommand_from pipeline" -l completed-stages -d "Skip these stages" -r
107
185
  end
@@ -12,6 +12,7 @@ import {
12
12
  } from "fs";
13
13
  import { join, extname } from "path";
14
14
  import { execSync } from "child_process";
15
+ import { Database } from "bun:sqlite";
15
16
 
16
17
  // ─── Config ──────────────────────────────────────────────────────────
17
18
  const PORT = parseInt(
@@ -40,6 +41,107 @@ const SESSION_SECRET = process.env.SESSION_SECRET || crypto.randomUUID();
40
41
  const SESSION_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
41
42
  const ALLOWED_PERMISSIONS = ["admin", "write"];
42
43
 
44
+ // ─── SQLite Database (optional) ──────────────────────────────────────
45
+ const DB_FILE = join(HOME, ".shipwright", "shipwright.db");
46
+ let db: Database | null = null;
47
+
48
+ function getDb(): Database | null {
49
+ if (db) return db;
50
+ try {
51
+ if (!existsSync(DB_FILE)) return null;
52
+ db = new Database(DB_FILE, { readonly: true });
53
+ db.exec("PRAGMA journal_mode=WAL;");
54
+ return db;
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+
60
+ function dbQueryEvents(since?: number, limit = 200): DaemonEvent[] {
61
+ const conn = getDb();
62
+ if (!conn) return [];
63
+ try {
64
+ const cutoff = since || 0;
65
+ const rows = conn
66
+ .query(
67
+ `SELECT ts, ts_epoch, type, job_id, stage, status, duration_secs, metadata
68
+ FROM events WHERE ts_epoch >= ? ORDER BY ts_epoch DESC LIMIT ?`,
69
+ )
70
+ .all(cutoff, limit) as Array<Record<string, unknown>>;
71
+ return rows.map((r) => {
72
+ const base: DaemonEvent = {
73
+ ts: r.ts as string,
74
+ ts_epoch: r.ts_epoch as number,
75
+ type: r.type as string,
76
+ };
77
+ if (r.job_id)
78
+ base.issue = parseInt(String(r.job_id).replace(/\D/g, "")) || undefined;
79
+ if (r.stage) base.stage = r.stage as string;
80
+ if (r.duration_secs) base.duration_s = r.duration_secs as number;
81
+ if (r.status) base.result = r.status as string;
82
+ if (r.metadata) {
83
+ try {
84
+ Object.assign(base, JSON.parse(r.metadata as string));
85
+ } catch {
86
+ /* ignore malformed metadata */
87
+ }
88
+ }
89
+ return base;
90
+ });
91
+ } catch {
92
+ return [];
93
+ }
94
+ }
95
+
96
+ function dbQueryJobs(status?: string): Array<Record<string, unknown>> {
97
+ const conn = getDb();
98
+ if (!conn) return [];
99
+ try {
100
+ if (status) {
101
+ return conn
102
+ .query(
103
+ "SELECT * FROM daemon_state WHERE status = ? ORDER BY started_at DESC",
104
+ )
105
+ .all(status) as Array<Record<string, unknown>>;
106
+ }
107
+ return conn
108
+ .query("SELECT * FROM daemon_state ORDER BY started_at DESC LIMIT 50")
109
+ .all() as Array<Record<string, unknown>>;
110
+ } catch {
111
+ return [];
112
+ }
113
+ }
114
+
115
+ function dbQueryCostsToday(): { total: number; count: number } {
116
+ const conn = getDb();
117
+ if (!conn) return { total: 0, count: 0 };
118
+ try {
119
+ const todayStart = new Date();
120
+ todayStart.setUTCHours(0, 0, 0, 0);
121
+ const epoch = Math.floor(todayStart.getTime() / 1000);
122
+ const row = conn
123
+ .query(
124
+ "SELECT COALESCE(SUM(cost_usd), 0) as total, COUNT(*) as count FROM cost_entries WHERE ts_epoch >= ?",
125
+ )
126
+ .get(epoch) as { total: number; count: number } | null;
127
+ return row || { total: 0, count: 0 };
128
+ } catch {
129
+ return { total: 0, count: 0 };
130
+ }
131
+ }
132
+
133
+ function dbQueryHeartbeats(): Array<Record<string, unknown>> {
134
+ const conn = getDb();
135
+ if (!conn) return [];
136
+ try {
137
+ return conn
138
+ .query("SELECT * FROM heartbeats ORDER BY updated_at DESC")
139
+ .all() as Array<Record<string, unknown>>;
140
+ } catch {
141
+ return [];
142
+ }
143
+ }
144
+
43
145
  // ─── ANSI helpers ────────────────────────────────────────────────────
44
146
  const CYAN = "\x1b[38;2;0;212;255m";
45
147
  const GREEN = "\x1b[38;2;74;222;128m";
@@ -602,6 +704,11 @@ function broadcastToClients(data: FleetState): void {
602
704
 
603
705
  // ─── Data Collection ─────────────────────────────────────────────────
604
706
  function readEvents(): DaemonEvent[] {
707
+ // Try SQLite first (faster for large event logs)
708
+ const dbEvents = dbQueryEvents(0, 10000);
709
+ if (dbEvents.length > 0) return dbEvents;
710
+
711
+ // Fallback to JSONL
605
712
  if (!existsSync(EVENTS_FILE)) return [];
606
713
  try {
607
714
  const content = readFileSync(EVENTS_FILE, "utf-8").trim();
@@ -2610,6 +2717,107 @@ const server = Bun.serve({
2610
2717
  });
2611
2718
  }
2612
2719
 
2720
+ // ── SQLite DB API endpoints ─────────────────────────────────────
2721
+
2722
+ // REST: Events from DB with since/limit params
2723
+ if (pathname === "/api/db/events") {
2724
+ const since = parseInt(url.searchParams.get("since") || "0");
2725
+ const limit = Math.min(
2726
+ parseInt(url.searchParams.get("limit") || "200"),
2727
+ 10000,
2728
+ );
2729
+ const events = dbQueryEvents(since, limit);
2730
+ return new Response(
2731
+ JSON.stringify({
2732
+ events,
2733
+ source: events.length > 0 ? "sqlite" : "none",
2734
+ }),
2735
+ {
2736
+ headers: { "Content-Type": "application/json", ...CORS_HEADERS },
2737
+ },
2738
+ );
2739
+ }
2740
+
2741
+ // REST: Active/completed jobs from DB
2742
+ if (pathname === "/api/db/jobs") {
2743
+ const status = url.searchParams.get("status") || undefined;
2744
+ const jobs = dbQueryJobs(status);
2745
+ return new Response(
2746
+ JSON.stringify({ jobs, source: jobs.length > 0 ? "sqlite" : "none" }),
2747
+ {
2748
+ headers: { "Content-Type": "application/json", ...CORS_HEADERS },
2749
+ },
2750
+ );
2751
+ }
2752
+
2753
+ // REST: Today's cost from DB
2754
+ if (pathname === "/api/db/costs/today") {
2755
+ const costs = dbQueryCostsToday();
2756
+ return new Response(JSON.stringify({ ...costs, source: "sqlite" }), {
2757
+ headers: { "Content-Type": "application/json", ...CORS_HEADERS },
2758
+ });
2759
+ }
2760
+
2761
+ // REST: Heartbeats from DB
2762
+ if (pathname === "/api/db/heartbeats") {
2763
+ const heartbeats = dbQueryHeartbeats();
2764
+ return new Response(
2765
+ JSON.stringify({
2766
+ heartbeats,
2767
+ source: heartbeats.length > 0 ? "sqlite" : "none",
2768
+ }),
2769
+ {
2770
+ headers: { "Content-Type": "application/json", ...CORS_HEADERS },
2771
+ },
2772
+ );
2773
+ }
2774
+
2775
+ // REST: DB health info
2776
+ if (pathname === "/api/db/health") {
2777
+ const conn = getDb();
2778
+ if (!conn) {
2779
+ return new Response(JSON.stringify({ available: false }), {
2780
+ headers: { "Content-Type": "application/json", ...CORS_HEADERS },
2781
+ });
2782
+ }
2783
+ try {
2784
+ const version = conn
2785
+ .query("SELECT MAX(version) as v FROM _schema")
2786
+ .get() as { v: number } | null;
2787
+ const walMode = conn.query("PRAGMA journal_mode").get() as {
2788
+ journal_mode: string;
2789
+ } | null;
2790
+ const eventCount = conn
2791
+ .query("SELECT COUNT(*) as c FROM events")
2792
+ .get() as { c: number } | null;
2793
+ const runCount = conn
2794
+ .query("SELECT COUNT(*) as c FROM pipeline_runs")
2795
+ .get() as { c: number } | null;
2796
+ const costCount = conn
2797
+ .query("SELECT COUNT(*) as c FROM cost_entries")
2798
+ .get() as { c: number } | null;
2799
+
2800
+ return new Response(
2801
+ JSON.stringify({
2802
+ available: true,
2803
+ schema_version: version?.v || 0,
2804
+ wal_mode: walMode?.journal_mode || "unknown",
2805
+ events: eventCount?.c || 0,
2806
+ runs: runCount?.c || 0,
2807
+ costs: costCount?.c || 0,
2808
+ }),
2809
+ { headers: { "Content-Type": "application/json", ...CORS_HEADERS } },
2810
+ );
2811
+ } catch {
2812
+ return new Response(
2813
+ JSON.stringify({ available: false, error: "query failed" }),
2814
+ {
2815
+ headers: { "Content-Type": "application/json", ...CORS_HEADERS },
2816
+ },
2817
+ );
2818
+ }
2819
+ }
2820
+
2613
2821
  // REST: Memory failure patterns for a specific issue
2614
2822
  if (pathname.startsWith("/api/memory/failures/")) {
2615
2823
  const issueNum = parseInt(pathname.split("/")[4] || "0");