@ai-sdlc/orchestrator 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.
Files changed (203) hide show
  1. package/dist/adapters.d.ts +60 -0
  2. package/dist/adapters.js +160 -0
  3. package/dist/admission.d.ts +49 -0
  4. package/dist/admission.js +90 -0
  5. package/dist/analysis/analyzer.d.ts +10 -0
  6. package/dist/analysis/analyzer.js +67 -0
  7. package/dist/analysis/complexity-scorer.d.ts +24 -0
  8. package/dist/analysis/complexity-scorer.js +39 -0
  9. package/dist/analysis/context-builder.d.ts +14 -0
  10. package/dist/analysis/context-builder.js +79 -0
  11. package/dist/analysis/convention-detector.d.ts +9 -0
  12. package/dist/analysis/convention-detector.js +177 -0
  13. package/dist/analysis/dependency-parser.d.ts +17 -0
  14. package/dist/analysis/dependency-parser.js +167 -0
  15. package/dist/analysis/file-walker.d.ts +18 -0
  16. package/dist/analysis/file-walker.js +158 -0
  17. package/dist/analysis/hotspot-analyzer.d.ts +28 -0
  18. package/dist/analysis/hotspot-analyzer.js +85 -0
  19. package/dist/analysis/index.d.ts +13 -0
  20. package/dist/analysis/index.js +12 -0
  21. package/dist/analysis/pattern-detector.d.ts +10 -0
  22. package/dist/analysis/pattern-detector.js +86 -0
  23. package/dist/analysis/types.d.ts +94 -0
  24. package/dist/analysis/types.js +5 -0
  25. package/dist/audit-archival.d.ts +48 -0
  26. package/dist/audit-archival.js +72 -0
  27. package/dist/audit-export.d.ts +45 -0
  28. package/dist/audit-export.js +100 -0
  29. package/dist/audit-extended.d.ts +28 -0
  30. package/dist/audit-extended.js +38 -0
  31. package/dist/audit-scheduler.d.ts +32 -0
  32. package/dist/audit-scheduler.js +52 -0
  33. package/dist/audit-sqlite-sink.d.ts +11 -0
  34. package/dist/audit-sqlite-sink.js +51 -0
  35. package/dist/autonomy-tracker.d.ts +76 -0
  36. package/dist/autonomy-tracker.js +277 -0
  37. package/dist/check-runs.d.ts +37 -0
  38. package/dist/check-runs.js +72 -0
  39. package/dist/cli/commands/agents.d.ts +6 -0
  40. package/dist/cli/commands/agents.js +33 -0
  41. package/dist/cli/commands/complexity.d.ts +6 -0
  42. package/dist/cli/commands/complexity.js +35 -0
  43. package/dist/cli/commands/cost.d.ts +6 -0
  44. package/dist/cli/commands/cost.js +53 -0
  45. package/dist/cli/commands/dashboard.d.ts +9 -0
  46. package/dist/cli/commands/dashboard.js +50 -0
  47. package/dist/cli/commands/health.d.ts +6 -0
  48. package/dist/cli/commands/health.js +33 -0
  49. package/dist/cli/commands/init.d.ts +6 -0
  50. package/dist/cli/commands/init.js +127 -0
  51. package/dist/cli/commands/routing.d.ts +6 -0
  52. package/dist/cli/commands/routing.js +46 -0
  53. package/dist/cli/commands/run.d.ts +6 -0
  54. package/dist/cli/commands/run.js +36 -0
  55. package/dist/cli/commands/start.d.ts +6 -0
  56. package/dist/cli/commands/start.js +26 -0
  57. package/dist/cli/commands/status.d.ts +6 -0
  58. package/dist/cli/commands/status.js +34 -0
  59. package/dist/cli/dashboard-renderer.d.ts +24 -0
  60. package/dist/cli/dashboard-renderer.js +129 -0
  61. package/dist/cli/formatters/index.d.ts +6 -0
  62. package/dist/cli/formatters/index.js +18 -0
  63. package/dist/cli/formatters/json.d.ts +5 -0
  64. package/dist/cli/formatters/json.js +7 -0
  65. package/dist/cli/formatters/minimal.d.ts +5 -0
  66. package/dist/cli/formatters/minimal.js +36 -0
  67. package/dist/cli/formatters/table.d.ts +5 -0
  68. package/dist/cli/formatters/table.js +184 -0
  69. package/dist/cli/index.d.ts +6 -0
  70. package/dist/cli/index.js +35 -0
  71. package/dist/compliance-extended.d.ts +41 -0
  72. package/dist/compliance-extended.js +58 -0
  73. package/dist/config.d.ts +26 -0
  74. package/dist/config.js +66 -0
  75. package/dist/context-enrichment.d.ts +33 -0
  76. package/dist/context-enrichment.js +125 -0
  77. package/dist/cost-governance.d.ts +22 -0
  78. package/dist/cost-governance.js +106 -0
  79. package/dist/cost-tracker.d.ts +75 -0
  80. package/dist/cost-tracker.js +177 -0
  81. package/dist/defaults.d.ts +140 -0
  82. package/dist/defaults.js +218 -0
  83. package/dist/deploy/flyio-target.d.ts +19 -0
  84. package/dist/deploy/flyio-target.js +114 -0
  85. package/dist/deploy/index.d.ts +15 -0
  86. package/dist/deploy/index.js +11 -0
  87. package/dist/deploy/kubernetes-target.d.ts +25 -0
  88. package/dist/deploy/kubernetes-target.js +124 -0
  89. package/dist/deploy/metrics-collector.d.ts +31 -0
  90. package/dist/deploy/metrics-collector.js +75 -0
  91. package/dist/deploy/rollout-controller.d.ts +42 -0
  92. package/dist/deploy/rollout-controller.js +192 -0
  93. package/dist/deploy/rollout-types.d.ts +86 -0
  94. package/dist/deploy/rollout-types.js +5 -0
  95. package/dist/deploy/types.d.ts +47 -0
  96. package/dist/deploy/types.js +5 -0
  97. package/dist/deploy/vercel-target.d.ts +16 -0
  98. package/dist/deploy/vercel-target.js +115 -0
  99. package/dist/discovery.d.ts +26 -0
  100. package/dist/discovery.js +46 -0
  101. package/dist/episodic-enhanced.d.ts +88 -0
  102. package/dist/episodic-enhanced.js +212 -0
  103. package/dist/execute.d.ts +81 -0
  104. package/dist/execute.js +703 -0
  105. package/dist/fix-ci.d.ts +61 -0
  106. package/dist/fix-ci.js +380 -0
  107. package/dist/handoff-executor.d.ts +61 -0
  108. package/dist/handoff-executor.js +119 -0
  109. package/dist/index.d.ts +70 -0
  110. package/dist/index.js +87 -0
  111. package/dist/instrumented.d.ts +27 -0
  112. package/dist/instrumented.js +34 -0
  113. package/dist/logger.d.ts +17 -0
  114. package/dist/logger.js +37 -0
  115. package/dist/multi-repo/impact-analyzer.d.ts +22 -0
  116. package/dist/multi-repo/impact-analyzer.js +95 -0
  117. package/dist/multi-repo/index.d.ts +5 -0
  118. package/dist/multi-repo/index.js +4 -0
  119. package/dist/multi-repo/monorepo-detector.d.ts +16 -0
  120. package/dist/multi-repo/monorepo-detector.js +246 -0
  121. package/dist/multi-repo/service-map-builder.d.ts +24 -0
  122. package/dist/multi-repo/service-map-builder.js +235 -0
  123. package/dist/multi-repo/types.d.ts +54 -0
  124. package/dist/multi-repo/types.js +5 -0
  125. package/dist/notifications/index.d.ts +4 -0
  126. package/dist/notifications/index.js +4 -0
  127. package/dist/notifications/notification-router.d.ts +62 -0
  128. package/dist/notifications/notification-router.js +206 -0
  129. package/dist/notifications/slack-messenger.d.ts +26 -0
  130. package/dist/notifications/slack-messenger.js +69 -0
  131. package/dist/notifications/teams-messenger.d.ts +21 -0
  132. package/dist/notifications/teams-messenger.js +85 -0
  133. package/dist/notifications.d.ts +13 -0
  134. package/dist/notifications.js +15 -0
  135. package/dist/orchestration.d.ts +20 -0
  136. package/dist/orchestration.js +53 -0
  137. package/dist/orchestrator.d.ts +160 -0
  138. package/dist/orchestrator.js +372 -0
  139. package/dist/otel-exporter.d.ts +37 -0
  140. package/dist/otel-exporter.js +128 -0
  141. package/dist/plugin.d.ts +45 -0
  142. package/dist/plugin.js +8 -0
  143. package/dist/policy-evaluators.d.ts +40 -0
  144. package/dist/policy-evaluators.js +57 -0
  145. package/dist/process-escalation.d.ts +38 -0
  146. package/dist/process-escalation.js +129 -0
  147. package/dist/progressive-gates.d.ts +54 -0
  148. package/dist/progressive-gates.js +135 -0
  149. package/dist/provenance.d.ts +29 -0
  150. package/dist/provenance.js +61 -0
  151. package/dist/reconcilers.d.ts +28 -0
  152. package/dist/reconcilers.js +37 -0
  153. package/dist/runners/claude-code.d.ts +17 -0
  154. package/dist/runners/claude-code.js +200 -0
  155. package/dist/runners/codex.d.ts +19 -0
  156. package/dist/runners/codex.js +153 -0
  157. package/dist/runners/copilot-stub.d.ts +9 -0
  158. package/dist/runners/copilot-stub.js +15 -0
  159. package/dist/runners/copilot.d.ts +15 -0
  160. package/dist/runners/copilot.js +119 -0
  161. package/dist/runners/cursor-stub.d.ts +9 -0
  162. package/dist/runners/cursor-stub.js +15 -0
  163. package/dist/runners/cursor.d.ts +20 -0
  164. package/dist/runners/cursor.js +140 -0
  165. package/dist/runners/devin-stub.d.ts +9 -0
  166. package/dist/runners/devin-stub.js +15 -0
  167. package/dist/runners/generic-llm.d.ts +51 -0
  168. package/dist/runners/generic-llm.js +125 -0
  169. package/dist/runners/index.d.ts +8 -0
  170. package/dist/runners/index.js +7 -0
  171. package/dist/runners/runner-registry.d.ts +50 -0
  172. package/dist/runners/runner-registry.js +156 -0
  173. package/dist/runners/types.d.ts +59 -0
  174. package/dist/runners/types.js +7 -0
  175. package/dist/security.d.ts +51 -0
  176. package/dist/security.js +70 -0
  177. package/dist/shared.d.ts +134 -0
  178. package/dist/shared.js +284 -0
  179. package/dist/state/index.d.ts +4 -0
  180. package/dist/state/index.js +3 -0
  181. package/dist/state/schema.d.ts +16 -0
  182. package/dist/state/schema.js +264 -0
  183. package/dist/state/store.d.ts +109 -0
  184. package/dist/state/store.js +644 -0
  185. package/dist/state/types.d.ts +190 -0
  186. package/dist/state/types.js +5 -0
  187. package/dist/structured-logger.d.ts +20 -0
  188. package/dist/structured-logger.js +64 -0
  189. package/dist/task-decomposer.d.ts +61 -0
  190. package/dist/task-decomposer.js +265 -0
  191. package/dist/telemetry-extended.d.ts +24 -0
  192. package/dist/telemetry-extended.js +31 -0
  193. package/dist/types.d.ts +9 -0
  194. package/dist/types.js +7 -0
  195. package/dist/validate-agent-output.d.ts +30 -0
  196. package/dist/validate-agent-output.js +81 -0
  197. package/dist/validate-issue.d.ts +25 -0
  198. package/dist/validate-issue.js +102 -0
  199. package/dist/watch.d.ts +43 -0
  200. package/dist/watch.js +99 -0
  201. package/dist/webhook-manager.d.ts +38 -0
  202. package/dist/webhook-manager.js +66 -0
  203. package/package.json +59 -0
@@ -0,0 +1,644 @@
1
+ /**
2
+ * SQLite-backed state store for the AI-SDLC orchestrator.
3
+ *
4
+ * Uses better-sqlite3 for synchronous operations. The state store is
5
+ * optional — the orchestrator works without it.
6
+ */
7
+ import { CURRENT_SCHEMA_VERSION, MIGRATIONS } from './schema.js';
8
+ export class StateStore {
9
+ db;
10
+ constructor(db) {
11
+ this.db = db;
12
+ this.migrate();
13
+ }
14
+ /**
15
+ * Create a StateStore from a file path or :memory: for testing.
16
+ */
17
+ static open(pathOrDb) {
18
+ if (typeof pathOrDb === 'string') {
19
+ // Dynamic require to keep better-sqlite3 optional at import time
20
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
21
+ const SqliteConstructor = require('better-sqlite3');
22
+ const db = new SqliteConstructor(pathOrDb);
23
+ db.pragma('journal_mode = WAL');
24
+ return new StateStore(db);
25
+ }
26
+ return new StateStore(pathOrDb);
27
+ }
28
+ migrate() {
29
+ // Check current schema version
30
+ try {
31
+ const row = this.db.prepare('SELECT MAX(version) as v FROM schema_version').get();
32
+ const current = row?.v ?? 0;
33
+ if (current >= CURRENT_SCHEMA_VERSION)
34
+ return;
35
+ for (const migration of MIGRATIONS) {
36
+ if (migration.version > (current ?? 0)) {
37
+ this.db.exec(migration.sql);
38
+ this.db.prepare('INSERT INTO schema_version (version) VALUES (?)').run(migration.version);
39
+ }
40
+ }
41
+ }
42
+ catch {
43
+ // Table doesn't exist yet — run all migrations
44
+ for (const migration of MIGRATIONS) {
45
+ this.db.exec(migration.sql);
46
+ this.db.prepare('INSERT INTO schema_version (version) VALUES (?)').run(migration.version);
47
+ }
48
+ }
49
+ }
50
+ // ── Complexity Profiles ──────────────────────────────────────────
51
+ saveComplexityProfile(profile) {
52
+ const stmt = this.db.prepare(`
53
+ INSERT INTO complexity_profile (repo_path, score, files_count, modules_count, dependency_count, raw_data, architectural_patterns, hotspots, module_graph, conventions_data)
54
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
55
+ `);
56
+ const result = stmt.run(profile.repoPath, profile.score, profile.filesCount ?? null, profile.modulesCount ?? null, profile.dependencyCount ?? null, profile.rawData ?? null, profile.architecturalPatterns ?? null, profile.hotspots ?? null, profile.moduleGraph ?? null, profile.conventionsData ?? null);
57
+ return Number(result.lastInsertRowid);
58
+ }
59
+ getLatestComplexityProfile(repoPath) {
60
+ const row = this.db
61
+ .prepare('SELECT * FROM complexity_profile WHERE repo_path = ? ORDER BY analyzed_at DESC LIMIT 1')
62
+ .get(repoPath);
63
+ return row ? this.mapComplexityProfile(row) : undefined;
64
+ }
65
+ mapComplexityProfile(row) {
66
+ return {
67
+ id: row.id,
68
+ repoPath: row.repo_path,
69
+ score: row.score,
70
+ filesCount: row.files_count,
71
+ modulesCount: row.modules_count,
72
+ dependencyCount: row.dependency_count,
73
+ analyzedAt: row.analyzed_at,
74
+ rawData: row.raw_data,
75
+ architecturalPatterns: row.architectural_patterns,
76
+ hotspots: row.hotspots,
77
+ moduleGraph: row.module_graph,
78
+ conventionsData: row.conventions_data,
79
+ };
80
+ }
81
+ // ── Episodic Memory ──────────────────────────────────────────────
82
+ saveEpisodicRecord(record) {
83
+ const stmt = this.db.prepare(`
84
+ INSERT INTO episodic_memory (issue_number, pr_number, pipeline_type, outcome, duration_ms, files_changed, error_message, metadata,
85
+ agent_name, complexity_score, routing_strategy, gate_pass_count, gate_fail_count, cost_usd, is_regression, related_episodes)
86
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
87
+ `);
88
+ const result = stmt.run(record.issueNumber ?? null, record.prNumber ?? null, record.pipelineType, record.outcome, record.durationMs ?? null, record.filesChanged ?? null, record.errorMessage ?? null, record.metadata ?? null, record.agentName ?? null, record.complexityScore ?? null, record.routingStrategy ?? null, record.gatePassCount ?? null, record.gateFailCount ?? null, record.costUsd ?? null, record.isRegression ?? 0, record.relatedEpisodes ?? null);
89
+ return Number(result.lastInsertRowid);
90
+ }
91
+ getEpisodicRecords(issueNumber, limit = 50) {
92
+ const sql = issueNumber
93
+ ? 'SELECT * FROM episodic_memory WHERE issue_number = ? ORDER BY created_at DESC LIMIT ?'
94
+ : 'SELECT * FROM episodic_memory ORDER BY created_at DESC LIMIT ?';
95
+ const rows = (issueNumber ? this.db.prepare(sql).all(issueNumber, limit) : this.db.prepare(sql).all(limit));
96
+ return rows.map((r) => this.mapEpisodicRecord(r));
97
+ }
98
+ mapEpisodicRecord(row) {
99
+ return {
100
+ id: row.id,
101
+ issueNumber: row.issue_number,
102
+ prNumber: row.pr_number,
103
+ pipelineType: row.pipeline_type,
104
+ outcome: row.outcome,
105
+ durationMs: row.duration_ms,
106
+ filesChanged: row.files_changed,
107
+ errorMessage: row.error_message,
108
+ metadata: row.metadata,
109
+ createdAt: row.created_at,
110
+ agentName: row.agent_name,
111
+ complexityScore: row.complexity_score,
112
+ routingStrategy: row.routing_strategy,
113
+ gatePassCount: row.gate_pass_count,
114
+ gateFailCount: row.gate_fail_count,
115
+ costUsd: row.cost_usd,
116
+ isRegression: row.is_regression,
117
+ relatedEpisodes: row.related_episodes,
118
+ };
119
+ }
120
+ // ── Autonomy Ledger ──────────────────────────────────────────────
121
+ getAutonomyLedger(agentName) {
122
+ const row = this.db
123
+ .prepare('SELECT * FROM autonomy_ledger WHERE agent_name = ?')
124
+ .get(agentName);
125
+ return row ? this.mapAutonomyLedger(row) : undefined;
126
+ }
127
+ upsertAutonomyLedger(entry) {
128
+ this.db
129
+ .prepare(`INSERT INTO autonomy_ledger (agent_name, current_level, total_tasks, success_count, failure_count, last_task_at, metrics,
130
+ pr_approval_rate, rollback_count, security_incidents, promoted_at, demoted_at, time_at_level_ms)
131
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
132
+ ON CONFLICT(agent_name) DO UPDATE SET
133
+ current_level = excluded.current_level,
134
+ total_tasks = excluded.total_tasks,
135
+ success_count = excluded.success_count,
136
+ failure_count = excluded.failure_count,
137
+ last_task_at = excluded.last_task_at,
138
+ metrics = excluded.metrics,
139
+ pr_approval_rate = excluded.pr_approval_rate,
140
+ rollback_count = excluded.rollback_count,
141
+ security_incidents = excluded.security_incidents,
142
+ promoted_at = excluded.promoted_at,
143
+ demoted_at = excluded.demoted_at,
144
+ time_at_level_ms = excluded.time_at_level_ms`)
145
+ .run(entry.agentName, entry.currentLevel, entry.totalTasks, entry.successCount, entry.failureCount, entry.lastTaskAt ?? null, entry.metrics ?? null, entry.prApprovalRate ?? 0, entry.rollbackCount ?? 0, entry.securityIncidents ?? 0, entry.promotedAt ?? null, entry.demotedAt ?? null, entry.timeAtLevelMs ?? 0);
146
+ }
147
+ getAllAutonomyLedgerEntries() {
148
+ const rows = this.db
149
+ .prepare('SELECT * FROM autonomy_ledger ORDER BY agent_name')
150
+ .all();
151
+ return rows.map((r) => this.mapAutonomyLedger(r));
152
+ }
153
+ mapAutonomyLedger(row) {
154
+ return {
155
+ id: row.id,
156
+ agentName: row.agent_name,
157
+ currentLevel: row.current_level,
158
+ totalTasks: row.total_tasks,
159
+ successCount: row.success_count,
160
+ failureCount: row.failure_count,
161
+ lastTaskAt: row.last_task_at,
162
+ metrics: row.metrics,
163
+ prApprovalRate: row.pr_approval_rate,
164
+ rollbackCount: row.rollback_count,
165
+ securityIncidents: row.security_incidents,
166
+ promotedAt: row.promoted_at,
167
+ demotedAt: row.demoted_at,
168
+ timeAtLevelMs: row.time_at_level_ms,
169
+ };
170
+ }
171
+ // ── Pipeline Runs ────────────────────────────────────────────────
172
+ savePipelineRun(run) {
173
+ const stmt = this.db.prepare(`
174
+ INSERT INTO pipeline_runs (run_id, issue_number, pr_number, pipeline_type, status, current_stage, result, gate_results,
175
+ cost_usd, tokens_used, model, agent_name, complexity_score)
176
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
177
+ `);
178
+ const result = stmt.run(run.runId, run.issueNumber ?? null, run.prNumber ?? null, run.pipelineType, run.status, run.currentStage ?? null, run.result ?? null, run.gateResults ?? null, run.costUsd ?? 0, run.tokensUsed ?? 0, run.model ?? null, run.agentName ?? null, run.complexityScore ?? null);
179
+ return Number(result.lastInsertRowid);
180
+ }
181
+ updatePipelineRunStatus(runId, status, opts) {
182
+ const isTerminal = status === 'completed' || status === 'failed';
183
+ const sql = isTerminal
184
+ ? `UPDATE pipeline_runs SET status = ?, current_stage = ?,
185
+ completed_at = datetime('now'),
186
+ result = COALESCE(?, result),
187
+ gate_results = COALESCE(?, gate_results)
188
+ WHERE run_id = ?`
189
+ : `UPDATE pipeline_runs SET status = ?, current_stage = ?,
190
+ result = COALESCE(?, result),
191
+ gate_results = COALESCE(?, gate_results)
192
+ WHERE run_id = ?`;
193
+ this.db
194
+ .prepare(sql)
195
+ .run(status, opts?.currentStage ?? null, opts?.result ?? null, opts?.gateResults ?? null, runId);
196
+ }
197
+ getPipelineRun(runId) {
198
+ const row = this.db.prepare('SELECT * FROM pipeline_runs WHERE run_id = ?').get(runId);
199
+ return row ? this.mapPipelineRun(row) : undefined;
200
+ }
201
+ getPipelineRuns(issueNumber, limit = 50) {
202
+ const sql = issueNumber
203
+ ? 'SELECT * FROM pipeline_runs WHERE issue_number = ? ORDER BY started_at DESC LIMIT ?'
204
+ : 'SELECT * FROM pipeline_runs ORDER BY started_at DESC LIMIT ?';
205
+ const rows = (issueNumber ? this.db.prepare(sql).all(issueNumber, limit) : this.db.prepare(sql).all(limit));
206
+ return rows.map((r) => this.mapPipelineRun(r));
207
+ }
208
+ mapPipelineRun(row) {
209
+ return {
210
+ id: row.id,
211
+ runId: row.run_id,
212
+ issueNumber: row.issue_number,
213
+ prNumber: row.pr_number,
214
+ pipelineType: row.pipeline_type,
215
+ status: row.status,
216
+ currentStage: row.current_stage,
217
+ startedAt: row.started_at,
218
+ completedAt: row.completed_at,
219
+ result: row.result,
220
+ gateResults: row.gate_results,
221
+ costUsd: row.cost_usd,
222
+ tokensUsed: row.tokens_used,
223
+ model: row.model,
224
+ agentName: row.agent_name,
225
+ complexityScore: row.complexity_score,
226
+ };
227
+ }
228
+ // ── Conventions ──────────────────────────────────────────────────
229
+ saveConvention(convention) {
230
+ const stmt = this.db.prepare(`
231
+ INSERT INTO conventions (category, pattern, confidence, examples)
232
+ VALUES (?, ?, ?, ?)
233
+ `);
234
+ const result = stmt.run(convention.category, convention.pattern, convention.confidence ?? null, convention.examples ?? null);
235
+ return Number(result.lastInsertRowid);
236
+ }
237
+ getConventions(category) {
238
+ const sql = category
239
+ ? 'SELECT * FROM conventions WHERE category = ? ORDER BY detected_at DESC'
240
+ : 'SELECT * FROM conventions ORDER BY detected_at DESC';
241
+ const rows = (category ? this.db.prepare(sql).all(category) : this.db.prepare(sql).all());
242
+ return rows.map((r) => this.mapConvention(r));
243
+ }
244
+ mapConvention(row) {
245
+ return {
246
+ id: row.id,
247
+ category: row.category,
248
+ pattern: row.pattern,
249
+ confidence: row.confidence,
250
+ examples: row.examples,
251
+ detectedAt: row.detected_at,
252
+ };
253
+ }
254
+ // ── Hotspots ───────────────────────────────────────────────────
255
+ saveHotspot(record) {
256
+ const stmt = this.db.prepare(`
257
+ INSERT INTO hotspots (repo_path, file_path, churn_rate, complexity, commit_count, last_modified, note)
258
+ VALUES (?, ?, ?, ?, ?, ?, ?)
259
+ `);
260
+ const result = stmt.run(record.repoPath, record.filePath, record.churnRate, record.complexity, record.commitCount ?? null, record.lastModified ?? null, record.note ?? null);
261
+ return Number(result.lastInsertRowid);
262
+ }
263
+ getHotspots(repoPath, limit = 20) {
264
+ const rows = this.db
265
+ .prepare('SELECT * FROM hotspots WHERE repo_path = ? ORDER BY churn_rate DESC, complexity DESC LIMIT ?')
266
+ .all(repoPath, limit);
267
+ return rows.map((r) => this.mapHotspot(r));
268
+ }
269
+ mapHotspot(row) {
270
+ return {
271
+ id: row.id,
272
+ repoPath: row.repo_path,
273
+ filePath: row.file_path,
274
+ churnRate: row.churn_rate,
275
+ complexity: row.complexity,
276
+ commitCount: row.commit_count,
277
+ lastModified: row.last_modified,
278
+ note: row.note,
279
+ analyzedAt: row.analyzed_at,
280
+ };
281
+ }
282
+ // ── Routing History ───────────────────────────────────────────
283
+ saveRoutingDecision(decision) {
284
+ const stmt = this.db.prepare(`
285
+ INSERT INTO routing_history (issue_number, task_complexity, codebase_complexity, routing_strategy, agent_name, reason)
286
+ VALUES (?, ?, ?, ?, ?, ?)
287
+ `);
288
+ const result = stmt.run(decision.issueNumber ?? null, decision.taskComplexity, decision.codebaseComplexity, decision.routingStrategy, decision.agentName ?? null, decision.reason ?? null);
289
+ return Number(result.lastInsertRowid);
290
+ }
291
+ getRoutingHistory(limit = 50) {
292
+ const rows = this.db
293
+ .prepare('SELECT * FROM routing_history ORDER BY decided_at DESC LIMIT ?')
294
+ .all(limit);
295
+ return rows.map((r) => this.mapRoutingDecision(r));
296
+ }
297
+ mapRoutingDecision(row) {
298
+ return {
299
+ id: row.id,
300
+ issueNumber: row.issue_number,
301
+ taskComplexity: row.task_complexity,
302
+ codebaseComplexity: row.codebase_complexity,
303
+ routingStrategy: row.routing_strategy,
304
+ agentName: row.agent_name,
305
+ reason: row.reason,
306
+ decidedAt: row.decided_at,
307
+ };
308
+ }
309
+ // ── Cost Ledger ────────────────────────────────────────────────
310
+ saveCostEntry(entry) {
311
+ const stmt = this.db.prepare(`
312
+ INSERT INTO cost_ledger (run_id, agent_name, pipeline_type, model, input_tokens, output_tokens, total_tokens, cost_usd, issue_number, pr_number, stage_name, cache_read_tokens)
313
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
314
+ `);
315
+ const result = stmt.run(entry.runId, entry.agentName, entry.pipelineType, entry.model ?? null, entry.inputTokens ?? 0, entry.outputTokens ?? 0, entry.totalTokens ?? 0, entry.costUsd ?? 0, entry.issueNumber ?? null, entry.prNumber ?? null, entry.stageName ?? null, entry.cacheReadTokens ?? 0);
316
+ return Number(result.lastInsertRowid);
317
+ }
318
+ getCostEntries(opts) {
319
+ const conditions = [];
320
+ const params = [];
321
+ if (opts?.runId) {
322
+ conditions.push('run_id = ?');
323
+ params.push(opts.runId);
324
+ }
325
+ if (opts?.agentName) {
326
+ conditions.push('agent_name = ?');
327
+ params.push(opts.agentName);
328
+ }
329
+ if (opts?.since) {
330
+ conditions.push('created_at >= ?');
331
+ params.push(opts.since);
332
+ }
333
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
334
+ const limit = opts?.limit ?? 1000;
335
+ const sql = `SELECT * FROM cost_ledger ${where} ORDER BY created_at DESC LIMIT ?`;
336
+ params.push(limit);
337
+ const rows = this.db.prepare(sql).all(...params);
338
+ return rows.map((r) => this.mapCostEntry(r));
339
+ }
340
+ getCostSummary(since) {
341
+ const where = since ? 'WHERE created_at >= ?' : '';
342
+ const sql = `SELECT COALESCE(SUM(cost_usd), 0) as total_cost, COALESCE(SUM(total_tokens), 0) as total_tokens, COUNT(*) as entry_count FROM cost_ledger ${where}`;
343
+ const row = (since ? this.db.prepare(sql).get(since) : this.db.prepare(sql).get());
344
+ return {
345
+ totalCostUsd: row.total_cost,
346
+ totalTokens: row.total_tokens,
347
+ entryCount: row.entry_count,
348
+ };
349
+ }
350
+ mapCostEntry(row) {
351
+ return {
352
+ id: row.id,
353
+ runId: row.run_id,
354
+ agentName: row.agent_name,
355
+ pipelineType: row.pipeline_type,
356
+ model: row.model,
357
+ inputTokens: row.input_tokens,
358
+ outputTokens: row.output_tokens,
359
+ totalTokens: row.total_tokens,
360
+ costUsd: row.cost_usd,
361
+ issueNumber: row.issue_number,
362
+ prNumber: row.pr_number,
363
+ stageName: row.stage_name,
364
+ cacheReadTokens: row.cache_read_tokens,
365
+ createdAt: row.created_at,
366
+ };
367
+ }
368
+ // ── Gate Threshold Overrides ──────────────────────────────────
369
+ saveGateThresholdOverride(override) {
370
+ const stmt = this.db.prepare(`
371
+ INSERT INTO gate_threshold_overrides (gate_name, complexity_band, enforcement_level, threshold_overrides, active)
372
+ VALUES (?, ?, ?, ?, ?)
373
+ ON CONFLICT(gate_name, complexity_band) DO UPDATE SET
374
+ enforcement_level = excluded.enforcement_level,
375
+ threshold_overrides = excluded.threshold_overrides,
376
+ active = excluded.active
377
+ `);
378
+ const result = stmt.run(override.gateName, override.complexityBand, override.enforcementLevel, override.thresholdOverrides ?? null, override.active ?? 1);
379
+ return Number(result.lastInsertRowid);
380
+ }
381
+ getGateThresholdOverrides(gateName, complexityBand) {
382
+ const conditions = ['active = 1'];
383
+ const params = [];
384
+ if (gateName) {
385
+ conditions.push('gate_name = ?');
386
+ params.push(gateName);
387
+ }
388
+ if (complexityBand) {
389
+ conditions.push('complexity_band = ?');
390
+ params.push(complexityBand);
391
+ }
392
+ const sql = `SELECT * FROM gate_threshold_overrides WHERE ${conditions.join(' AND ')} ORDER BY gate_name`;
393
+ const rows = this.db.prepare(sql).all(...params);
394
+ return rows.map((r) => ({
395
+ id: r.id,
396
+ gateName: r.gate_name,
397
+ complexityBand: r.complexity_band,
398
+ enforcementLevel: r.enforcement_level,
399
+ thresholdOverrides: r.threshold_overrides,
400
+ active: r.active,
401
+ createdAt: r.created_at,
402
+ }));
403
+ }
404
+ // ── Autonomy Events ───────────────────────────────────────────
405
+ saveAutonomyEvent(event) {
406
+ const stmt = this.db.prepare(`
407
+ INSERT INTO autonomy_events (agent_name, event_type, from_level, to_level, trigger, metrics_snapshot, unmet_conditions)
408
+ VALUES (?, ?, ?, ?, ?, ?, ?)
409
+ `);
410
+ const result = stmt.run(event.agentName, event.eventType, event.fromLevel, event.toLevel, event.trigger ?? null, event.metricsSnapshot ?? null, event.unmetConditions ?? null);
411
+ return Number(result.lastInsertRowid);
412
+ }
413
+ getAutonomyEvents(agentName, limit = 100) {
414
+ const sql = agentName
415
+ ? 'SELECT * FROM autonomy_events WHERE agent_name = ? ORDER BY created_at DESC LIMIT ?'
416
+ : 'SELECT * FROM autonomy_events ORDER BY created_at DESC LIMIT ?';
417
+ const rows = (agentName ? this.db.prepare(sql).all(agentName, limit) : this.db.prepare(sql).all(limit));
418
+ return rows.map((r) => ({
419
+ id: r.id,
420
+ agentName: r.agent_name,
421
+ eventType: r.event_type,
422
+ fromLevel: r.from_level,
423
+ toLevel: r.to_level,
424
+ trigger: r.trigger,
425
+ metricsSnapshot: r.metrics_snapshot,
426
+ unmetConditions: r.unmet_conditions,
427
+ createdAt: r.created_at,
428
+ }));
429
+ }
430
+ // ── Episodic Search ───────────────────────────────────────────
431
+ searchEpisodicRecords(opts) {
432
+ const conditions = [];
433
+ const params = [];
434
+ if (opts.agentName) {
435
+ conditions.push('agent_name = ?');
436
+ params.push(opts.agentName);
437
+ }
438
+ if (opts.outcome) {
439
+ conditions.push('outcome = ?');
440
+ params.push(opts.outcome);
441
+ }
442
+ if (opts.since) {
443
+ conditions.push('created_at >= ?');
444
+ params.push(opts.since);
445
+ }
446
+ if (opts.files) {
447
+ conditions.push('metadata LIKE ?');
448
+ params.push(`%${opts.files}%`);
449
+ }
450
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
451
+ const limit = opts.limit ?? 50;
452
+ const sql = `SELECT * FROM episodic_memory ${where} ORDER BY created_at DESC LIMIT ?`;
453
+ params.push(limit);
454
+ const rows = this.db.prepare(sql).all(...params);
455
+ return rows.map((r) => this.mapEpisodicRecord(r));
456
+ }
457
+ // ── Codebase Profile (full) ───────────────────────────────────
458
+ /**
459
+ * Save a full codebase profile with JSON-serialized analysis data.
460
+ * Also saves individual hotspot records.
461
+ */
462
+ saveCodebaseProfile(profile) {
463
+ return this.saveComplexityProfile(profile);
464
+ }
465
+ // ── Handoff Events ──────────────────────────────────────────────
466
+ saveHandoffEvent(event) {
467
+ const stmt = this.db.prepare(`
468
+ INSERT INTO handoff_events (run_id, from_agent, to_agent, payload_hash, validation_result, error_message)
469
+ VALUES (?, ?, ?, ?, ?, ?)
470
+ `);
471
+ const result = stmt.run(event.runId, event.fromAgent, event.toAgent, event.payloadHash ?? null, event.validationResult, event.errorMessage ?? null);
472
+ return Number(result.lastInsertRowid);
473
+ }
474
+ getHandoffEvents(runId, limit = 100) {
475
+ const sql = runId
476
+ ? 'SELECT * FROM handoff_events WHERE run_id = ? ORDER BY created_at DESC LIMIT ?'
477
+ : 'SELECT * FROM handoff_events ORDER BY created_at DESC LIMIT ?';
478
+ const rows = (runId ? this.db.prepare(sql).all(runId, limit) : this.db.prepare(sql).all(limit));
479
+ return rows.map((r) => ({
480
+ id: r.id,
481
+ runId: r.run_id,
482
+ fromAgent: r.from_agent,
483
+ toAgent: r.to_agent,
484
+ payloadHash: r.payload_hash,
485
+ validationResult: r.validation_result,
486
+ errorMessage: r.error_message,
487
+ createdAt: r.created_at,
488
+ }));
489
+ }
490
+ // ── Deployments ─────────────────────────────────────────────────
491
+ saveDeployment(record) {
492
+ const stmt = this.db.prepare(`
493
+ INSERT INTO deployments (deployment_id, target_name, provider, version, environment, state, url, error, started_at, completed_at)
494
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
495
+ `);
496
+ const result = stmt.run(record.deploymentId, record.targetName, record.provider, record.version, record.environment, record.state, record.url ?? null, record.error ?? null, record.startedAt ?? null, record.completedAt ?? null);
497
+ return Number(result.lastInsertRowid);
498
+ }
499
+ updateDeployment(deploymentId, update) {
500
+ this.db
501
+ .prepare(`
502
+ UPDATE deployments SET state = ?, url = COALESCE(?, url), error = COALESCE(?, error), completed_at = COALESCE(?, completed_at)
503
+ WHERE deployment_id = ?
504
+ `)
505
+ .run(update.state, update.url ?? null, update.error ?? null, update.completedAt ?? null, deploymentId);
506
+ }
507
+ getDeployment(deploymentId) {
508
+ const row = this.db
509
+ .prepare('SELECT * FROM deployments WHERE deployment_id = ?')
510
+ .get(deploymentId);
511
+ return row ? this.mapDeployment(row) : undefined;
512
+ }
513
+ getDeployments(opts) {
514
+ const conditions = [];
515
+ const params = [];
516
+ if (opts?.targetName) {
517
+ conditions.push('target_name = ?');
518
+ params.push(opts.targetName);
519
+ }
520
+ if (opts?.environment) {
521
+ conditions.push('environment = ?');
522
+ params.push(opts.environment);
523
+ }
524
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
525
+ const limit = opts?.limit ?? 50;
526
+ params.push(limit);
527
+ const rows = this.db
528
+ .prepare(`SELECT * FROM deployments ${where} ORDER BY started_at DESC LIMIT ?`)
529
+ .all(...params);
530
+ return rows.map((r) => this.mapDeployment(r));
531
+ }
532
+ mapDeployment(row) {
533
+ return {
534
+ id: row.id,
535
+ deploymentId: row.deployment_id,
536
+ targetName: row.target_name,
537
+ provider: row.provider,
538
+ version: row.version,
539
+ environment: row.environment,
540
+ state: row.state,
541
+ url: row.url,
542
+ error: row.error,
543
+ startedAt: row.started_at,
544
+ completedAt: row.completed_at,
545
+ };
546
+ }
547
+ // ── Rollout Steps ──────────────────────────────────────────────
548
+ saveRolloutStep(record) {
549
+ const stmt = this.db.prepare(`
550
+ INSERT INTO rollout_steps (deployment_id, step_number, weight_percent, state, metrics_snapshot, started_at, completed_at)
551
+ VALUES (?, ?, ?, ?, ?, ?, ?)
552
+ `);
553
+ const result = stmt.run(record.deploymentId, record.stepNumber, record.weightPercent, record.state, record.metricsSnapshot ?? null, record.startedAt ?? null, record.completedAt ?? null);
554
+ return Number(result.lastInsertRowid);
555
+ }
556
+ getRolloutSteps(deploymentId) {
557
+ const rows = this.db
558
+ .prepare('SELECT * FROM rollout_steps WHERE deployment_id = ? ORDER BY step_number')
559
+ .all(deploymentId);
560
+ return rows.map((r) => ({
561
+ id: r.id,
562
+ deploymentId: r.deployment_id,
563
+ stepNumber: r.step_number,
564
+ weightPercent: r.weight_percent,
565
+ state: r.state,
566
+ metricsSnapshot: r.metrics_snapshot,
567
+ startedAt: r.started_at,
568
+ completedAt: r.completed_at,
569
+ }));
570
+ }
571
+ // ── Audit Entries ──────────────────────────────────────────────
572
+ saveAuditEntry(record) {
573
+ const stmt = this.db.prepare(`
574
+ INSERT INTO audit_entries (entry_id, actor, action, resource_type, resource_id, detail, hash, previous_hash, signature, created_at)
575
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, COALESCE(?, datetime('now')))
576
+ `);
577
+ const result = stmt.run(record.entryId, record.actor, record.action, record.resourceType ?? null, record.resourceId ?? null, record.detail ?? null, record.hash ?? null, record.previousHash ?? null, record.signature ?? null, record.createdAt ?? null);
578
+ return Number(result.lastInsertRowid);
579
+ }
580
+ queryAuditEntries(opts) {
581
+ const conditions = [];
582
+ const params = [];
583
+ if (opts?.actor) {
584
+ conditions.push('actor = ?');
585
+ params.push(opts.actor);
586
+ }
587
+ if (opts?.action) {
588
+ conditions.push('action = ?');
589
+ params.push(opts.action);
590
+ }
591
+ if (opts?.resourceType) {
592
+ conditions.push('resource_type = ?');
593
+ params.push(opts.resourceType);
594
+ }
595
+ if (opts?.resourceId) {
596
+ conditions.push('resource_id = ?');
597
+ params.push(opts.resourceId);
598
+ }
599
+ if (opts?.since) {
600
+ conditions.push('created_at >= ?');
601
+ params.push(opts.since);
602
+ }
603
+ if (opts?.until) {
604
+ conditions.push('created_at <= ?');
605
+ params.push(opts.until);
606
+ }
607
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
608
+ const limit = opts?.limit ?? 100;
609
+ const offset = opts?.offset ?? 0;
610
+ params.push(limit, offset);
611
+ const rows = this.db
612
+ .prepare(`SELECT * FROM audit_entries ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`)
613
+ .all(...params);
614
+ return rows.map((r) => this.mapAuditEntry(r));
615
+ }
616
+ getAuditEntry(entryId) {
617
+ const row = this.db.prepare('SELECT * FROM audit_entries WHERE entry_id = ?').get(entryId);
618
+ return row ? this.mapAuditEntry(row) : undefined;
619
+ }
620
+ mapAuditEntry(row) {
621
+ return {
622
+ id: row.id,
623
+ entryId: row.entry_id,
624
+ actor: row.actor,
625
+ action: row.action,
626
+ resourceType: row.resource_type,
627
+ resourceId: row.resource_id,
628
+ detail: row.detail,
629
+ hash: row.hash,
630
+ previousHash: row.previous_hash,
631
+ signature: row.signature,
632
+ createdAt: row.created_at,
633
+ };
634
+ }
635
+ // ── Utilities ────────────────────────────────────────────────────
636
+ /** Expose the underlying database for direct queries (e.g. dashboard). */
637
+ getDatabase() {
638
+ return this.db;
639
+ }
640
+ close() {
641
+ this.db.close();
642
+ }
643
+ }
644
+ //# sourceMappingURL=store.js.map