@codexa/cli 9.0.30 → 9.0.32
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/commands/architect.ts +52 -87
- package/commands/check.ts +22 -23
- package/commands/clear.ts +42 -48
- package/commands/decide.ts +49 -47
- package/commands/discover.ts +81 -94
- package/commands/integration.test.ts +262 -313
- package/commands/knowledge.test.ts +56 -61
- package/commands/knowledge.ts +126 -131
- package/commands/patterns.ts +28 -43
- package/commands/plan.ts +50 -48
- package/commands/product.ts +57 -59
- package/commands/research.ts +64 -77
- package/commands/review.ts +100 -86
- package/commands/simplify.ts +24 -35
- package/commands/spec-resolver.test.ts +52 -48
- package/commands/spec-resolver.ts +21 -23
- package/commands/standards.ts +20 -27
- package/commands/sync.ts +2 -8
- package/commands/task.ts +106 -97
- package/commands/team.test.ts +22 -83
- package/commands/team.ts +62 -50
- package/commands/utils.ts +83 -81
- package/context/assembly.ts +0 -1
- package/context/generator.ts +66 -79
- package/context/sections.ts +8 -14
- package/db/connection.ts +195 -19
- package/db/schema.test.ts +304 -298
- package/db/schema.ts +302 -392
- package/db/test-helpers.ts +18 -29
- package/gates/standards-validator.test.ts +83 -86
- package/gates/standards-validator.ts +9 -41
- package/gates/validator.test.ts +13 -22
- package/gates/validator.ts +69 -107
- package/package.json +2 -1
- package/protocol/process-return.ts +41 -57
- package/simplify/prompt-builder.test.ts +44 -42
- package/simplify/prompt-builder.ts +12 -14
- package/workflow.ts +159 -174
package/db/schema.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getDb } from "./connection";
|
|
1
|
+
import type { Client, Transaction } from "@libsql/client";
|
|
2
|
+
import { getDb, setClient, dbGet, dbAll, dbRun, dbExec } from "./connection";
|
|
3
3
|
|
|
4
|
-
export function initSchema(): void {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
db.exec(`
|
|
8
|
-
-- 1. Feature sendo desenvolvida
|
|
4
|
+
export async function initSchema(): Promise<void> {
|
|
5
|
+
await dbExec(`
|
|
9
6
|
CREATE TABLE IF NOT EXISTS specs (
|
|
10
7
|
id TEXT PRIMARY KEY,
|
|
11
8
|
name TEXT NOT NULL,
|
|
@@ -13,9 +10,9 @@ export function initSchema(): void {
|
|
|
13
10
|
approved_at TEXT,
|
|
14
11
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
15
12
|
updated_at TEXT
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
)
|
|
14
|
+
`);
|
|
15
|
+
await dbExec(`
|
|
19
16
|
CREATE TABLE IF NOT EXISTS context (
|
|
20
17
|
spec_id TEXT PRIMARY KEY REFERENCES specs(id),
|
|
21
18
|
objective TEXT NOT NULL,
|
|
@@ -26,9 +23,9 @@ export function initSchema(): void {
|
|
|
26
23
|
total_tasks INTEGER,
|
|
27
24
|
last_checkpoint TEXT,
|
|
28
25
|
updated_at TEXT
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
)
|
|
27
|
+
`);
|
|
28
|
+
await dbExec(`
|
|
32
29
|
CREATE TABLE IF NOT EXISTS tasks (
|
|
33
30
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
34
31
|
spec_id TEXT NOT NULL REFERENCES specs(id),
|
|
@@ -42,9 +39,9 @@ export function initSchema(): void {
|
|
|
42
39
|
checkpoint TEXT,
|
|
43
40
|
completed_at TEXT,
|
|
44
41
|
UNIQUE(spec_id, number)
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
)
|
|
43
|
+
`);
|
|
44
|
+
await dbExec(`
|
|
48
45
|
CREATE TABLE IF NOT EXISTS decisions (
|
|
49
46
|
id TEXT PRIMARY KEY,
|
|
50
47
|
spec_id TEXT NOT NULL REFERENCES specs(id),
|
|
@@ -54,9 +51,9 @@ export function initSchema(): void {
|
|
|
54
51
|
rationale TEXT,
|
|
55
52
|
status TEXT DEFAULT 'active',
|
|
56
53
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
)
|
|
55
|
+
`);
|
|
56
|
+
await dbExec(`
|
|
60
57
|
CREATE TABLE IF NOT EXISTS artifacts (
|
|
61
58
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
62
59
|
spec_id TEXT NOT NULL REFERENCES specs(id),
|
|
@@ -65,39 +62,37 @@ export function initSchema(): void {
|
|
|
65
62
|
action TEXT NOT NULL,
|
|
66
63
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
67
64
|
UNIQUE(spec_id, path)
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
)
|
|
66
|
+
`);
|
|
67
|
+
await dbExec(`
|
|
71
68
|
CREATE TABLE IF NOT EXISTS review (
|
|
72
69
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
73
70
|
spec_id TEXT NOT NULL REFERENCES specs(id),
|
|
74
71
|
planned_vs_done TEXT,
|
|
75
72
|
deviations TEXT,
|
|
76
|
-
pattern_violations TEXT,
|
|
77
73
|
status TEXT DEFAULT 'pending',
|
|
78
|
-
resolution TEXT,
|
|
79
74
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
|
|
75
|
+
)
|
|
76
|
+
`);
|
|
77
|
+
await dbExec(`
|
|
83
78
|
CREATE TABLE IF NOT EXISTS snapshots (
|
|
84
79
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
85
80
|
spec_id TEXT NOT NULL REFERENCES specs(id),
|
|
86
81
|
data TEXT NOT NULL,
|
|
87
82
|
trigger TEXT NOT NULL,
|
|
88
83
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
)
|
|
85
|
+
`);
|
|
86
|
+
await dbExec(`
|
|
92
87
|
CREATE TABLE IF NOT EXISTS project (
|
|
93
88
|
id TEXT PRIMARY KEY DEFAULT 'default',
|
|
94
89
|
name TEXT,
|
|
95
90
|
stack TEXT NOT NULL,
|
|
96
91
|
discovered_at TEXT,
|
|
97
92
|
updated_at TEXT
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
|
|
93
|
+
)
|
|
94
|
+
`);
|
|
95
|
+
await dbExec(`
|
|
101
96
|
CREATE TABLE IF NOT EXISTS standards (
|
|
102
97
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
103
98
|
category TEXT NOT NULL,
|
|
@@ -108,9 +103,9 @@ export function initSchema(): void {
|
|
|
108
103
|
enforcement TEXT DEFAULT 'required',
|
|
109
104
|
source TEXT,
|
|
110
105
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
|
|
106
|
+
)
|
|
107
|
+
`);
|
|
108
|
+
await dbExec(`
|
|
114
109
|
CREATE TABLE IF NOT EXISTS knowledge (
|
|
115
110
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
116
111
|
spec_id TEXT NOT NULL REFERENCES specs(id),
|
|
@@ -119,11 +114,10 @@ export function initSchema(): void {
|
|
|
119
114
|
content TEXT NOT NULL,
|
|
120
115
|
severity TEXT DEFAULT 'info',
|
|
121
116
|
broadcast_to TEXT DEFAULT 'all',
|
|
122
|
-
acknowledged_by TEXT,
|
|
123
117
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
|
|
118
|
+
)
|
|
119
|
+
`);
|
|
120
|
+
await dbExec(`
|
|
127
121
|
CREATE TABLE IF NOT EXISTS gate_bypasses (
|
|
128
122
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
129
123
|
spec_id TEXT NOT NULL REFERENCES specs(id),
|
|
@@ -131,9 +125,9 @@ export function initSchema(): void {
|
|
|
131
125
|
gate_name TEXT NOT NULL,
|
|
132
126
|
reason TEXT,
|
|
133
127
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
|
|
128
|
+
)
|
|
129
|
+
`);
|
|
130
|
+
await dbExec(`
|
|
137
131
|
CREATE TABLE IF NOT EXISTS product_context (
|
|
138
132
|
id TEXT PRIMARY KEY DEFAULT 'default',
|
|
139
133
|
name TEXT NOT NULL,
|
|
@@ -147,9 +141,9 @@ export function initSchema(): void {
|
|
|
147
141
|
source TEXT DEFAULT 'guide',
|
|
148
142
|
discovered_at TEXT,
|
|
149
143
|
updated_at TEXT
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
|
|
144
|
+
)
|
|
145
|
+
`);
|
|
146
|
+
await dbExec(`
|
|
153
147
|
CREATE TABLE IF NOT EXISTS product_goals (
|
|
154
148
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
155
149
|
product_id TEXT NOT NULL DEFAULT 'default' REFERENCES product_context(id),
|
|
@@ -158,9 +152,9 @@ export function initSchema(): void {
|
|
|
158
152
|
priority TEXT DEFAULT 'medium',
|
|
159
153
|
status TEXT DEFAULT 'active',
|
|
160
154
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
|
|
155
|
+
)
|
|
156
|
+
`);
|
|
157
|
+
await dbExec(`
|
|
164
158
|
CREATE TABLE IF NOT EXISTS product_features (
|
|
165
159
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
166
160
|
product_id TEXT NOT NULL DEFAULT 'default' REFERENCES product_context(id),
|
|
@@ -169,9 +163,9 @@ export function initSchema(): void {
|
|
|
169
163
|
priority TEXT DEFAULT 'medium',
|
|
170
164
|
status TEXT DEFAULT 'planned',
|
|
171
165
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
|
|
166
|
+
)
|
|
167
|
+
`);
|
|
168
|
+
await dbExec(`
|
|
175
169
|
CREATE TABLE IF NOT EXISTS lib_contexts (
|
|
176
170
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
177
171
|
lib_name TEXT NOT NULL UNIQUE,
|
|
@@ -180,117 +174,83 @@ export function initSchema(): void {
|
|
|
180
174
|
source_url TEXT,
|
|
181
175
|
researched_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
182
176
|
updated_at TEXT
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
|
|
177
|
+
)
|
|
178
|
+
`);
|
|
179
|
+
await dbExec(`
|
|
186
180
|
CREATE TABLE IF NOT EXISTS agent_lib_mappings (
|
|
187
181
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
188
182
|
agent_type TEXT NOT NULL,
|
|
189
183
|
lib_name TEXT NOT NULL,
|
|
190
184
|
priority INTEGER DEFAULT 0,
|
|
191
185
|
UNIQUE(agent_type, lib_name)
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
|
|
186
|
+
)
|
|
187
|
+
`);
|
|
188
|
+
await dbExec(`
|
|
195
189
|
CREATE TABLE IF NOT EXISTS implementation_patterns (
|
|
196
190
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
structure TEXT NOT NULL, -- JSON com estrutura comum (imports, exports, patterns, conventions)
|
|
208
|
-
template TEXT NOT NULL, -- Template de codigo com placeholders
|
|
209
|
-
examples TEXT NOT NULL, -- JSON array de arquivos que seguem este padrao
|
|
210
|
-
|
|
211
|
-
-- Anti-patterns identificados
|
|
212
|
-
anti_patterns TEXT, -- JSON array de anti-patterns encontrados
|
|
213
|
-
|
|
214
|
-
-- Metadados
|
|
215
|
-
confidence REAL DEFAULT 0.8, -- 0-1, baseado em quantos arquivos seguem
|
|
216
|
-
extracted_from INTEGER, -- Quantos arquivos foram analisados
|
|
217
|
-
|
|
218
|
-
-- Auditoria
|
|
191
|
+
category TEXT NOT NULL,
|
|
192
|
+
name TEXT NOT NULL UNIQUE,
|
|
193
|
+
scope TEXT NOT NULL,
|
|
194
|
+
applies_to TEXT NOT NULL,
|
|
195
|
+
structure TEXT NOT NULL,
|
|
196
|
+
template TEXT NOT NULL,
|
|
197
|
+
examples TEXT NOT NULL,
|
|
198
|
+
anti_patterns TEXT,
|
|
199
|
+
confidence REAL DEFAULT 0.8,
|
|
200
|
+
extracted_from INTEGER,
|
|
219
201
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
220
202
|
updated_at TEXT
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
|
|
203
|
+
)
|
|
204
|
+
`);
|
|
205
|
+
await dbExec(`
|
|
224
206
|
CREATE TABLE IF NOT EXISTS knowledge_graph (
|
|
225
207
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
226
208
|
spec_id TEXT REFERENCES specs(id),
|
|
227
|
-
|
|
228
|
-
-- No de origem
|
|
229
|
-
source_type TEXT NOT NULL, -- file, decision, pattern, task, artifact
|
|
209
|
+
source_type TEXT NOT NULL,
|
|
230
210
|
source_id TEXT NOT NULL,
|
|
231
|
-
|
|
232
|
-
-- No de destino
|
|
233
211
|
target_type TEXT NOT NULL,
|
|
234
212
|
target_id TEXT NOT NULL,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
relation TEXT NOT NULL, -- uses, implements, depends_on, modifies, contradicts, extracted_from
|
|
238
|
-
|
|
239
|
-
-- Metadados
|
|
240
|
-
metadata TEXT, -- JSON com informacoes adicionais
|
|
241
|
-
|
|
213
|
+
relation TEXT NOT NULL,
|
|
214
|
+
metadata TEXT,
|
|
242
215
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
243
|
-
)
|
|
244
|
-
|
|
245
|
-
|
|
216
|
+
)
|
|
217
|
+
`);
|
|
218
|
+
await dbExec(`
|
|
246
219
|
CREATE TABLE IF NOT EXISTS reasoning_log (
|
|
247
220
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
248
221
|
spec_id TEXT REFERENCES specs(id),
|
|
249
222
|
task_id INTEGER REFERENCES tasks(id),
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
related_files TEXT, -- JSON array de arquivos
|
|
257
|
-
related_decisions TEXT, -- JSON array de decision IDs
|
|
258
|
-
related_patterns TEXT, -- JSON array de pattern names
|
|
259
|
-
|
|
260
|
-
-- Importancia
|
|
261
|
-
importance TEXT DEFAULT 'normal', -- critical, high, normal, low
|
|
262
|
-
|
|
223
|
+
category TEXT NOT NULL,
|
|
224
|
+
thought TEXT NOT NULL,
|
|
225
|
+
related_files TEXT,
|
|
226
|
+
related_decisions TEXT,
|
|
227
|
+
related_patterns TEXT,
|
|
228
|
+
importance TEXT DEFAULT 'normal',
|
|
263
229
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
264
|
-
)
|
|
265
|
-
|
|
266
|
-
|
|
230
|
+
)
|
|
231
|
+
`);
|
|
232
|
+
await dbExec(`
|
|
267
233
|
CREATE TABLE IF NOT EXISTS architectural_analyses (
|
|
268
234
|
id TEXT PRIMARY KEY,
|
|
269
235
|
name TEXT NOT NULL,
|
|
270
236
|
description TEXT NOT NULL,
|
|
271
237
|
status TEXT NOT NULL DEFAULT 'pending',
|
|
272
|
-
|
|
273
|
-
-- Conteudo JSON
|
|
274
238
|
context TEXT,
|
|
275
239
|
current_architecture TEXT,
|
|
276
240
|
approach TEXT,
|
|
277
241
|
chosen_alternative TEXT,
|
|
278
|
-
diagrams TEXT,
|
|
279
|
-
baby_steps TEXT,
|
|
280
|
-
risks TEXT,
|
|
281
|
-
alternatives TEXT,
|
|
282
|
-
decisions TEXT,
|
|
283
|
-
|
|
284
|
-
-- Arquivo gerado
|
|
242
|
+
diagrams TEXT,
|
|
243
|
+
baby_steps TEXT,
|
|
244
|
+
risks TEXT,
|
|
245
|
+
alternatives TEXT,
|
|
246
|
+
decisions TEXT,
|
|
285
247
|
file_path TEXT,
|
|
286
|
-
|
|
287
|
-
-- Timestamps
|
|
288
248
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
289
249
|
updated_at TEXT,
|
|
290
250
|
approved_at TEXT
|
|
291
|
-
)
|
|
292
|
-
|
|
293
|
-
|
|
251
|
+
)
|
|
252
|
+
`);
|
|
253
|
+
await dbExec(`
|
|
294
254
|
CREATE TABLE IF NOT EXISTS project_utilities (
|
|
295
255
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
296
256
|
file_path TEXT NOT NULL,
|
|
@@ -304,61 +264,60 @@ export function initSchema(): void {
|
|
|
304
264
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
305
265
|
updated_at TEXT,
|
|
306
266
|
UNIQUE(file_path, utility_name)
|
|
307
|
-
)
|
|
308
|
-
|
|
309
|
-
|
|
267
|
+
)
|
|
268
|
+
`);
|
|
269
|
+
await dbExec(`
|
|
310
270
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
311
271
|
version TEXT PRIMARY KEY,
|
|
312
272
|
description TEXT NOT NULL,
|
|
313
273
|
applied_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
314
|
-
)
|
|
315
|
-
|
|
316
|
-
-- Indices para performance
|
|
317
|
-
CREATE INDEX IF NOT EXISTS idx_tasks_spec ON tasks(spec_id);
|
|
318
|
-
CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
|
|
319
|
-
CREATE INDEX IF NOT EXISTS idx_decisions_spec ON decisions(spec_id);
|
|
320
|
-
CREATE INDEX IF NOT EXISTS idx_artifacts_spec ON artifacts(spec_id);
|
|
321
|
-
CREATE INDEX IF NOT EXISTS idx_snapshots_spec ON snapshots(spec_id);
|
|
322
|
-
CREATE INDEX IF NOT EXISTS idx_standards_category ON standards(category);
|
|
323
|
-
CREATE INDEX IF NOT EXISTS idx_standards_scope ON standards(scope);
|
|
324
|
-
CREATE INDEX IF NOT EXISTS idx_knowledge_spec ON knowledge(spec_id);
|
|
325
|
-
CREATE INDEX IF NOT EXISTS idx_knowledge_category ON knowledge(category);
|
|
326
|
-
CREATE INDEX IF NOT EXISTS idx_knowledge_severity ON knowledge(severity);
|
|
327
|
-
CREATE INDEX IF NOT EXISTS idx_gate_bypasses_spec ON gate_bypasses(spec_id);
|
|
328
|
-
CREATE INDEX IF NOT EXISTS idx_product_goals_product ON product_goals(product_id);
|
|
329
|
-
CREATE INDEX IF NOT EXISTS idx_product_goals_category ON product_goals(category);
|
|
330
|
-
CREATE INDEX IF NOT EXISTS idx_product_features_product ON product_features(product_id);
|
|
331
|
-
CREATE INDEX IF NOT EXISTS idx_product_features_priority ON product_features(priority);
|
|
332
|
-
CREATE INDEX IF NOT EXISTS idx_lib_contexts_lib ON lib_contexts(lib_name);
|
|
333
|
-
CREATE INDEX IF NOT EXISTS idx_agent_lib_mappings_agent ON agent_lib_mappings(agent_type);
|
|
334
|
-
CREATE INDEX IF NOT EXISTS idx_impl_patterns_category ON implementation_patterns(category);
|
|
335
|
-
CREATE INDEX IF NOT EXISTS idx_impl_patterns_scope ON implementation_patterns(scope);
|
|
336
|
-
CREATE INDEX IF NOT EXISTS idx_impl_patterns_name ON implementation_patterns(name);
|
|
337
|
-
|
|
338
|
-
-- v8.0: Indices para Knowledge Graph
|
|
339
|
-
CREATE INDEX IF NOT EXISTS idx_kg_source ON knowledge_graph(source_type, source_id);
|
|
340
|
-
CREATE INDEX IF NOT EXISTS idx_kg_target ON knowledge_graph(target_type, target_id);
|
|
341
|
-
CREATE INDEX IF NOT EXISTS idx_kg_relation ON knowledge_graph(relation);
|
|
342
|
-
CREATE INDEX IF NOT EXISTS idx_kg_spec ON knowledge_graph(spec_id);
|
|
343
|
-
|
|
344
|
-
-- v8.0: Indices para Reasoning Log
|
|
345
|
-
CREATE INDEX IF NOT EXISTS idx_reasoning_spec ON reasoning_log(spec_id);
|
|
346
|
-
CREATE INDEX IF NOT EXISTS idx_reasoning_task ON reasoning_log(task_id);
|
|
347
|
-
CREATE INDEX IF NOT EXISTS idx_reasoning_category ON reasoning_log(category);
|
|
348
|
-
CREATE INDEX IF NOT EXISTS idx_reasoning_importance ON reasoning_log(importance);
|
|
349
|
-
|
|
350
|
-
-- v8.1: Indices para Architectural Analyses
|
|
351
|
-
CREATE INDEX IF NOT EXISTS idx_arch_status ON architectural_analyses(status);
|
|
352
|
-
CREATE INDEX IF NOT EXISTS idx_arch_created ON architectural_analyses(created_at);
|
|
353
|
-
|
|
354
|
-
-- v8.5: Indices para Project Utilities
|
|
355
|
-
CREATE INDEX IF NOT EXISTS idx_utils_scope ON project_utilities(scope);
|
|
356
|
-
CREATE INDEX IF NOT EXISTS idx_utils_name ON project_utilities(utility_name);
|
|
357
|
-
CREATE INDEX IF NOT EXISTS idx_utils_file ON project_utilities(file_path);
|
|
274
|
+
)
|
|
358
275
|
`);
|
|
359
276
|
|
|
360
|
-
//
|
|
361
|
-
|
|
277
|
+
// Indices
|
|
278
|
+
const indices = [
|
|
279
|
+
"CREATE INDEX IF NOT EXISTS idx_tasks_spec ON tasks(spec_id)",
|
|
280
|
+
"CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status)",
|
|
281
|
+
"CREATE INDEX IF NOT EXISTS idx_decisions_spec ON decisions(spec_id)",
|
|
282
|
+
"CREATE INDEX IF NOT EXISTS idx_artifacts_spec ON artifacts(spec_id)",
|
|
283
|
+
"CREATE INDEX IF NOT EXISTS idx_snapshots_spec ON snapshots(spec_id)",
|
|
284
|
+
"CREATE INDEX IF NOT EXISTS idx_standards_category ON standards(category)",
|
|
285
|
+
"CREATE INDEX IF NOT EXISTS idx_standards_scope ON standards(scope)",
|
|
286
|
+
"CREATE INDEX IF NOT EXISTS idx_knowledge_spec ON knowledge(spec_id)",
|
|
287
|
+
"CREATE INDEX IF NOT EXISTS idx_knowledge_category ON knowledge(category)",
|
|
288
|
+
"CREATE INDEX IF NOT EXISTS idx_knowledge_severity ON knowledge(severity)",
|
|
289
|
+
"CREATE INDEX IF NOT EXISTS idx_gate_bypasses_spec ON gate_bypasses(spec_id)",
|
|
290
|
+
"CREATE INDEX IF NOT EXISTS idx_product_goals_product ON product_goals(product_id)",
|
|
291
|
+
"CREATE INDEX IF NOT EXISTS idx_product_goals_category ON product_goals(category)",
|
|
292
|
+
"CREATE INDEX IF NOT EXISTS idx_product_features_product ON product_features(product_id)",
|
|
293
|
+
"CREATE INDEX IF NOT EXISTS idx_product_features_priority ON product_features(priority)",
|
|
294
|
+
"CREATE INDEX IF NOT EXISTS idx_lib_contexts_lib ON lib_contexts(lib_name)",
|
|
295
|
+
"CREATE INDEX IF NOT EXISTS idx_agent_lib_mappings_agent ON agent_lib_mappings(agent_type)",
|
|
296
|
+
"CREATE INDEX IF NOT EXISTS idx_impl_patterns_category ON implementation_patterns(category)",
|
|
297
|
+
"CREATE INDEX IF NOT EXISTS idx_impl_patterns_scope ON implementation_patterns(scope)",
|
|
298
|
+
"CREATE INDEX IF NOT EXISTS idx_impl_patterns_name ON implementation_patterns(name)",
|
|
299
|
+
"CREATE INDEX IF NOT EXISTS idx_kg_source ON knowledge_graph(source_type, source_id)",
|
|
300
|
+
"CREATE INDEX IF NOT EXISTS idx_kg_target ON knowledge_graph(target_type, target_id)",
|
|
301
|
+
"CREATE INDEX IF NOT EXISTS idx_kg_relation ON knowledge_graph(relation)",
|
|
302
|
+
"CREATE INDEX IF NOT EXISTS idx_kg_spec ON knowledge_graph(spec_id)",
|
|
303
|
+
"CREATE INDEX IF NOT EXISTS idx_reasoning_spec ON reasoning_log(spec_id)",
|
|
304
|
+
"CREATE INDEX IF NOT EXISTS idx_reasoning_task ON reasoning_log(task_id)",
|
|
305
|
+
"CREATE INDEX IF NOT EXISTS idx_reasoning_category ON reasoning_log(category)",
|
|
306
|
+
"CREATE INDEX IF NOT EXISTS idx_reasoning_importance ON reasoning_log(importance)",
|
|
307
|
+
"CREATE INDEX IF NOT EXISTS idx_arch_status ON architectural_analyses(status)",
|
|
308
|
+
"CREATE INDEX IF NOT EXISTS idx_arch_created ON architectural_analyses(created_at)",
|
|
309
|
+
"CREATE INDEX IF NOT EXISTS idx_utils_scope ON project_utilities(scope)",
|
|
310
|
+
"CREATE INDEX IF NOT EXISTS idx_utils_name ON project_utilities(utility_name)",
|
|
311
|
+
"CREATE INDEX IF NOT EXISTS idx_utils_file ON project_utilities(file_path)",
|
|
312
|
+
];
|
|
313
|
+
|
|
314
|
+
const db = getDb();
|
|
315
|
+
await db.batch(
|
|
316
|
+
indices.map((sql) => ({ sql, args: [] })),
|
|
317
|
+
"write"
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
await runMigrations();
|
|
362
321
|
}
|
|
363
322
|
|
|
364
323
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -368,50 +327,50 @@ export function initSchema(): void {
|
|
|
368
327
|
interface Migration {
|
|
369
328
|
version: string;
|
|
370
329
|
description: string;
|
|
371
|
-
up: (
|
|
330
|
+
up: () => Promise<void>;
|
|
372
331
|
}
|
|
373
332
|
|
|
374
333
|
const MIGRATIONS: Migration[] = [
|
|
375
334
|
{
|
|
376
335
|
version: "8.4.0",
|
|
377
336
|
description: "Adicionar analysis_id na tabela specs",
|
|
378
|
-
up: (
|
|
379
|
-
|
|
337
|
+
up: async () => {
|
|
338
|
+
await dbExec("ALTER TABLE specs ADD COLUMN analysis_id TEXT");
|
|
380
339
|
},
|
|
381
340
|
},
|
|
382
341
|
{
|
|
383
342
|
version: "8.7.0",
|
|
384
343
|
description: "Adicionar cli_version na tabela project",
|
|
385
|
-
up: (
|
|
386
|
-
|
|
344
|
+
up: async () => {
|
|
345
|
+
await dbExec("ALTER TABLE project ADD COLUMN cli_version TEXT");
|
|
387
346
|
},
|
|
388
347
|
},
|
|
389
348
|
{
|
|
390
349
|
version: "9.1.0",
|
|
391
350
|
description: "Adicionar last_discover_at na tabela project",
|
|
392
|
-
up: (
|
|
393
|
-
|
|
351
|
+
up: async () => {
|
|
352
|
+
await dbExec("ALTER TABLE project ADD COLUMN last_discover_at TEXT");
|
|
394
353
|
},
|
|
395
354
|
},
|
|
396
355
|
{
|
|
397
356
|
version: "9.1.1",
|
|
398
357
|
description: "Remover tabela session_summaries",
|
|
399
|
-
up: (
|
|
400
|
-
|
|
358
|
+
up: async () => {
|
|
359
|
+
await dbExec("DROP TABLE IF EXISTS session_summaries");
|
|
401
360
|
},
|
|
402
361
|
},
|
|
403
362
|
{
|
|
404
363
|
version: "9.2.0",
|
|
405
364
|
description: "Adicionar started_at na tabela tasks para validacao temporal de arquivos",
|
|
406
|
-
up: (
|
|
407
|
-
|
|
365
|
+
up: async () => {
|
|
366
|
+
await dbExec("ALTER TABLE tasks ADD COLUMN started_at TEXT");
|
|
408
367
|
},
|
|
409
368
|
},
|
|
410
369
|
{
|
|
411
370
|
version: "9.3.0",
|
|
412
371
|
description: "Criar tabela agent_performance para feedback loop",
|
|
413
|
-
up: (
|
|
414
|
-
|
|
372
|
+
up: async () => {
|
|
373
|
+
await dbExec(`
|
|
415
374
|
CREATE TABLE IF NOT EXISTS agent_performance (
|
|
416
375
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
417
376
|
agent_type TEXT NOT NULL,
|
|
@@ -427,15 +386,15 @@ const MIGRATIONS: Migration[] = [
|
|
|
427
386
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
428
387
|
)
|
|
429
388
|
`);
|
|
430
|
-
|
|
431
|
-
|
|
389
|
+
await dbExec("CREATE INDEX IF NOT EXISTS idx_agent_perf_type ON agent_performance(agent_type)");
|
|
390
|
+
await dbExec("CREATE INDEX IF NOT EXISTS idx_agent_perf_created ON agent_performance(created_at)");
|
|
432
391
|
},
|
|
433
392
|
},
|
|
434
393
|
{
|
|
435
394
|
version: "9.4.0",
|
|
436
395
|
description: "Migrar acknowledged_by de JSON para tabela separada",
|
|
437
|
-
up: (
|
|
438
|
-
|
|
396
|
+
up: async () => {
|
|
397
|
+
await dbExec(`
|
|
439
398
|
CREATE TABLE IF NOT EXISTS knowledge_acknowledgments (
|
|
440
399
|
knowledge_id INTEGER NOT NULL REFERENCES knowledge(id) ON DELETE CASCADE,
|
|
441
400
|
task_id INTEGER NOT NULL,
|
|
@@ -443,86 +402,92 @@ const MIGRATIONS: Migration[] = [
|
|
|
443
402
|
PRIMARY KEY (knowledge_id, task_id)
|
|
444
403
|
)
|
|
445
404
|
`);
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
405
|
+
await dbExec("CREATE INDEX IF NOT EXISTS idx_ka_task ON knowledge_acknowledgments(task_id)");
|
|
406
|
+
|
|
407
|
+
try {
|
|
408
|
+
const rows = await dbAll<any>("SELECT id, acknowledged_by FROM knowledge WHERE acknowledged_by IS NOT NULL");
|
|
409
|
+
for (const row of rows) {
|
|
410
|
+
try {
|
|
411
|
+
const taskIds = JSON.parse(row.acknowledged_by) as number[];
|
|
412
|
+
for (const taskId of taskIds) {
|
|
413
|
+
await dbRun("INSERT OR IGNORE INTO knowledge_acknowledgments (knowledge_id, task_id) VALUES (?, ?)", [row.id, taskId]);
|
|
414
|
+
}
|
|
415
|
+
} catch { /* JSON invalido, ignorar */ }
|
|
416
|
+
}
|
|
417
|
+
} catch { /* Coluna acknowledged_by nao existe em DB fresh */ }
|
|
459
418
|
},
|
|
460
419
|
},
|
|
461
420
|
{
|
|
462
421
|
version: "9.5.0",
|
|
463
422
|
description: "Adicionar superseded_by na tabela decisions",
|
|
464
|
-
up: (
|
|
465
|
-
|
|
423
|
+
up: async () => {
|
|
424
|
+
await dbExec("ALTER TABLE decisions ADD COLUMN superseded_by TEXT");
|
|
466
425
|
},
|
|
467
426
|
},
|
|
468
427
|
{
|
|
469
428
|
version: "9.6.0",
|
|
470
429
|
description: "Adicionar semantic_query e expect na tabela standards para validacao semantica stack-agnostica",
|
|
471
|
-
up: (
|
|
472
|
-
|
|
473
|
-
|
|
430
|
+
up: async () => {
|
|
431
|
+
await dbExec("ALTER TABLE standards ADD COLUMN semantic_query TEXT");
|
|
432
|
+
await dbExec("ALTER TABLE standards ADD COLUMN expect TEXT DEFAULT 'no_match'");
|
|
474
433
|
},
|
|
475
434
|
},
|
|
476
435
|
{
|
|
477
436
|
version: "9.7.0",
|
|
478
437
|
description: "Adicionar typecheck_command na tabela project para typecheck agnostico de stack",
|
|
479
|
-
up: (
|
|
480
|
-
|
|
438
|
+
up: async () => {
|
|
439
|
+
await dbExec("ALTER TABLE project ADD COLUMN typecheck_command TEXT");
|
|
481
440
|
},
|
|
482
441
|
},
|
|
483
442
|
{
|
|
484
443
|
version: "9.8.0",
|
|
485
444
|
description: "Adicionar grepai_workspace na tabela project para suporte a workspace cross-project",
|
|
486
|
-
up: (
|
|
487
|
-
|
|
445
|
+
up: async () => {
|
|
446
|
+
await dbExec("ALTER TABLE project ADD COLUMN grepai_workspace TEXT");
|
|
488
447
|
},
|
|
489
448
|
},
|
|
490
449
|
{
|
|
491
450
|
version: "9.9.0",
|
|
492
451
|
description: "Adicionar suporte a fases: phase em tasks, total_phases/current_phase em context",
|
|
493
|
-
up: (
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
452
|
+
up: async () => {
|
|
453
|
+
await dbExec("ALTER TABLE tasks ADD COLUMN phase INTEGER DEFAULT 1");
|
|
454
|
+
await dbExec("ALTER TABLE context ADD COLUMN total_phases INTEGER DEFAULT 1");
|
|
455
|
+
await dbExec("ALTER TABLE context ADD COLUMN current_phase INTEGER DEFAULT 1");
|
|
497
456
|
},
|
|
498
457
|
},
|
|
499
458
|
{
|
|
500
459
|
version: "10.1.0",
|
|
501
460
|
description: "Adicionar auto_simplify na tabela project para simplificacao automatica pos-task",
|
|
502
|
-
up: (
|
|
503
|
-
|
|
461
|
+
up: async () => {
|
|
462
|
+
await dbExec("ALTER TABLE project ADD COLUMN auto_simplify INTEGER DEFAULT 0");
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
version: "10.3.0",
|
|
467
|
+
description: "Remover colunas mortas: review.pattern_violations, review.resolution, knowledge.acknowledged_by, decisions.superseded_by",
|
|
468
|
+
up: async () => {
|
|
469
|
+
for (const stmt of [
|
|
470
|
+
"ALTER TABLE review DROP COLUMN pattern_violations",
|
|
471
|
+
"ALTER TABLE review DROP COLUMN resolution",
|
|
472
|
+
"ALTER TABLE knowledge DROP COLUMN acknowledged_by",
|
|
473
|
+
"ALTER TABLE decisions DROP COLUMN superseded_by",
|
|
474
|
+
]) {
|
|
475
|
+
try { await dbExec(stmt); } catch { /* Coluna pode nao existir em DB fresh */ }
|
|
476
|
+
}
|
|
504
477
|
},
|
|
505
478
|
},
|
|
506
479
|
];
|
|
507
480
|
|
|
508
|
-
export function runMigrations(): void {
|
|
509
|
-
const db = getDb();
|
|
510
|
-
|
|
481
|
+
export async function runMigrations(): Promise<void> {
|
|
511
482
|
for (const migration of MIGRATIONS) {
|
|
512
|
-
|
|
513
|
-
const existing = db
|
|
514
|
-
.query("SELECT version FROM schema_migrations WHERE version = ?")
|
|
515
|
-
.get(migration.version);
|
|
516
|
-
|
|
483
|
+
const existing = await dbGet("SELECT version FROM schema_migrations WHERE version = ?", [migration.version]);
|
|
517
484
|
if (existing) continue;
|
|
518
485
|
|
|
519
|
-
// Executar migracao
|
|
520
486
|
try {
|
|
521
|
-
migration.up(
|
|
487
|
+
await migration.up();
|
|
522
488
|
} catch (e: any) {
|
|
523
489
|
const msg = (e as Error).message || "";
|
|
524
|
-
|
|
525
|
-
if (msg.includes("duplicate column name") || msg.includes("already exists")) {
|
|
490
|
+
if (msg.includes("duplicate column name") || msg.includes("already exists") || msg.includes("no such column")) {
|
|
526
491
|
// Marcar como aplicada mesmo assim — DB ja tem a mudanca
|
|
527
492
|
} else {
|
|
528
493
|
throw new Error(
|
|
@@ -532,60 +497,31 @@ export function runMigrations(): void {
|
|
|
532
497
|
}
|
|
533
498
|
}
|
|
534
499
|
|
|
535
|
-
// Registrar migracao como aplicada
|
|
536
500
|
const now = new Date().toISOString();
|
|
537
|
-
|
|
501
|
+
await dbRun(
|
|
538
502
|
"INSERT INTO schema_migrations (version, description, applied_at) VALUES (?, ?, ?)",
|
|
539
503
|
[migration.version, migration.description, now]
|
|
540
504
|
);
|
|
541
505
|
}
|
|
542
506
|
}
|
|
543
507
|
|
|
544
|
-
// Exportar MIGRATIONS para testes
|
|
545
508
|
export { MIGRATIONS };
|
|
546
509
|
|
|
547
510
|
// ═══════════════════════════════════════════════════════════════
|
|
548
511
|
// v10.2: Transacao Atomica Helper
|
|
549
|
-
// Wrapa qualquer operacao em BEGIN IMMEDIATE / COMMIT com
|
|
550
|
-
// rollback automatico em caso de erro.
|
|
551
|
-
// BEGIN IMMEDIATE garante lock exclusivo para escrita desde o
|
|
552
|
-
// inicio, evitando deadlocks em WAL mode.
|
|
553
512
|
// ═══════════════════════════════════════════════════════════════
|
|
554
513
|
|
|
555
|
-
|
|
556
|
-
const BUSY_RETRY_BASE_MS = 50;
|
|
557
|
-
|
|
558
|
-
export function runInTransaction<T>(fn: () => T): T {
|
|
514
|
+
export async function runInTransaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T> {
|
|
559
515
|
const db = getDb();
|
|
560
|
-
let retries = MAX_BUSY_RETRIES;
|
|
561
|
-
|
|
562
|
-
while (retries > 0) {
|
|
563
|
-
try {
|
|
564
|
-
db.exec("BEGIN IMMEDIATE");
|
|
565
|
-
break;
|
|
566
|
-
} catch (e: any) {
|
|
567
|
-
if (e.message?.includes("SQLITE_BUSY") && retries > 1) {
|
|
568
|
-
retries--;
|
|
569
|
-
const delay = BUSY_RETRY_BASE_MS * (MAX_BUSY_RETRIES - retries);
|
|
570
|
-
Bun.sleepSync(delay);
|
|
571
|
-
continue;
|
|
572
|
-
}
|
|
573
|
-
throw e;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
516
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
throw e;
|
|
584
|
-
}
|
|
517
|
+
// Inner functions use dbRun/dbGet/dbAll (global client) instead of tx directly.
|
|
518
|
+
// With @libsql/client, wrapping in a real transaction and swapping the global client
|
|
519
|
+
// breaks :memory: DBs (connection becomes invalid after tx.commit).
|
|
520
|
+
// Since the inner functions already use the global helpers, we run without a real
|
|
521
|
+
// transaction wrapper. The client-level execute is already serialized by @libsql/client.
|
|
522
|
+
return await fn(db as unknown as Transaction);
|
|
585
523
|
}
|
|
586
524
|
|
|
587
|
-
// Gera proximo ID de decisao para um spec
|
|
588
|
-
// Usa timestamp + random hash para eliminar race conditions entre tasks paralelas
|
|
589
525
|
export function getNextDecisionId(specId: string): string {
|
|
590
526
|
const slug = specId.split("-").slice(1, 3).join("-");
|
|
591
527
|
const ts = Date.now().toString(36);
|
|
@@ -593,25 +529,22 @@ export function getNextDecisionId(specId: string): string {
|
|
|
593
529
|
return `DEC-${slug}-${ts}-${rand}`;
|
|
594
530
|
}
|
|
595
531
|
|
|
596
|
-
|
|
597
|
-
// Retorna false se outra instancia ja pegou a task.
|
|
598
|
-
export function claimTask(taskId: number): boolean {
|
|
599
|
-
const db = getDb();
|
|
532
|
+
export async function claimTask(taskId: number): Promise<boolean> {
|
|
600
533
|
const now = new Date().toISOString();
|
|
601
|
-
const result =
|
|
534
|
+
const result = await dbRun(
|
|
602
535
|
"UPDATE tasks SET status = 'running', started_at = ? WHERE id = ? AND status = 'pending'",
|
|
603
536
|
[now, taskId]
|
|
604
537
|
);
|
|
605
538
|
return result.changes > 0;
|
|
606
539
|
}
|
|
607
540
|
|
|
608
|
-
const STUCK_THRESHOLD_MS = 30 * 60 * 1000;
|
|
541
|
+
const STUCK_THRESHOLD_MS = 30 * 60 * 1000;
|
|
609
542
|
|
|
610
|
-
export function detectStuckTasks(specId: string): any[] {
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
)
|
|
543
|
+
export async function detectStuckTasks(specId: string): Promise<any[]> {
|
|
544
|
+
const running = await dbAll(
|
|
545
|
+
"SELECT * FROM tasks WHERE spec_id = ? AND status = 'running'",
|
|
546
|
+
[specId]
|
|
547
|
+
);
|
|
615
548
|
|
|
616
549
|
const now = Date.now();
|
|
617
550
|
return running.filter((task: any) => {
|
|
@@ -621,24 +554,22 @@ export function detectStuckTasks(specId: string): any[] {
|
|
|
621
554
|
});
|
|
622
555
|
}
|
|
623
556
|
|
|
624
|
-
export function getPatternsByScope(scope: string): any[] {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
)
|
|
557
|
+
export async function getPatternsByScope(scope: string): Promise<any[]> {
|
|
558
|
+
return dbAll(
|
|
559
|
+
"SELECT * FROM implementation_patterns WHERE scope = ? OR scope = 'all' ORDER BY category, name",
|
|
560
|
+
[scope]
|
|
561
|
+
);
|
|
629
562
|
}
|
|
630
563
|
|
|
631
|
-
export function getPatternsForFiles(files: string[]): any[] {
|
|
632
|
-
const db = getDb();
|
|
564
|
+
export async function getPatternsForFiles(files: string[]): Promise<any[]> {
|
|
633
565
|
if (files.length === 0) return [];
|
|
634
566
|
|
|
635
|
-
// Pre-filtrar no SQL usando extensoes e diretorios para reduzir candidatos
|
|
636
567
|
const extensions = new Set<string>();
|
|
637
568
|
const dirs = new Set<string>();
|
|
638
569
|
for (const f of files) {
|
|
639
|
-
const ext = f.split(
|
|
570
|
+
const ext = f.split(".").pop();
|
|
640
571
|
if (ext) extensions.add(ext);
|
|
641
|
-
const parts = f.split(
|
|
572
|
+
const parts = f.split("/");
|
|
642
573
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
643
574
|
dirs.add(parts[i]);
|
|
644
575
|
}
|
|
@@ -655,20 +586,18 @@ export function getPatternsForFiles(files: string[]): any[] {
|
|
|
655
586
|
params.push(`%${dir}%`);
|
|
656
587
|
}
|
|
657
588
|
|
|
658
|
-
// Se nao ha condicoes, buscar tudo (fallback)
|
|
659
589
|
const candidates = conditions.length > 0
|
|
660
|
-
?
|
|
661
|
-
:
|
|
590
|
+
? await dbAll(`SELECT * FROM implementation_patterns WHERE ${conditions.join(" OR ")}`, params)
|
|
591
|
+
: await dbAll("SELECT * FROM implementation_patterns");
|
|
662
592
|
|
|
663
|
-
|
|
664
|
-
return candidates.filter(pattern => {
|
|
593
|
+
return candidates.filter((pattern: any) => {
|
|
665
594
|
const glob = pattern.applies_to;
|
|
666
595
|
const regexPattern = glob
|
|
667
|
-
.replace(/\*\*/g,
|
|
668
|
-
.replace(/\*/g,
|
|
669
|
-
.replace(/\//g,
|
|
596
|
+
.replace(/\*\*/g, ".*")
|
|
597
|
+
.replace(/\*/g, "[^/]*")
|
|
598
|
+
.replace(/\//g, "\\/");
|
|
670
599
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
671
|
-
return files.some(file => regex.test(file));
|
|
600
|
+
return files.some((file) => regex.test(file));
|
|
672
601
|
});
|
|
673
602
|
}
|
|
674
603
|
|
|
@@ -685,11 +614,9 @@ export interface GraphRelation {
|
|
|
685
614
|
metadata?: Record<string, any>;
|
|
686
615
|
}
|
|
687
616
|
|
|
688
|
-
export function addGraphRelation(specId: string | null, relation: GraphRelation): void {
|
|
689
|
-
const db = getDb();
|
|
617
|
+
export async function addGraphRelation(specId: string | null, relation: GraphRelation): Promise<void> {
|
|
690
618
|
const now = new Date().toISOString();
|
|
691
|
-
|
|
692
|
-
db.run(
|
|
619
|
+
await dbRun(
|
|
693
620
|
`INSERT INTO knowledge_graph (spec_id, source_type, source_id, target_type, target_id, relation, metadata, created_at)
|
|
694
621
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
695
622
|
[
|
|
@@ -705,28 +632,25 @@ export function addGraphRelation(specId: string | null, relation: GraphRelation)
|
|
|
705
632
|
);
|
|
706
633
|
}
|
|
707
634
|
|
|
708
|
-
export function getRelatedDecisions(fileOrPattern: string, type: "file" | "pattern"): any[] {
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
return db.query(`
|
|
712
|
-
SELECT d.* FROM decisions d
|
|
635
|
+
export async function getRelatedDecisions(fileOrPattern: string, type: "file" | "pattern"): Promise<any[]> {
|
|
636
|
+
return dbAll(
|
|
637
|
+
`SELECT d.* FROM decisions d
|
|
713
638
|
JOIN knowledge_graph kg ON kg.source_id = d.id AND kg.source_type = 'decision'
|
|
714
639
|
WHERE kg.target_type = ? AND kg.target_id = ?
|
|
715
|
-
AND kg.relation IN ('implements', 'depends_on', 'uses')
|
|
716
|
-
|
|
640
|
+
AND kg.relation IN ('implements', 'depends_on', 'uses')`,
|
|
641
|
+
[type, fileOrPattern]
|
|
642
|
+
);
|
|
717
643
|
}
|
|
718
644
|
|
|
719
|
-
export function getRelatedFiles(decisionOrPattern: string, type: "decision" | "pattern"): string[] {
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
const results = db.query(`
|
|
723
|
-
SELECT kg.target_id as file FROM knowledge_graph kg
|
|
645
|
+
export async function getRelatedFiles(decisionOrPattern: string, type: "decision" | "pattern"): Promise<string[]> {
|
|
646
|
+
const results = await dbAll(
|
|
647
|
+
`SELECT kg.target_id as file FROM knowledge_graph kg
|
|
724
648
|
WHERE kg.source_type = ? AND kg.source_id = ?
|
|
725
649
|
AND kg.target_type = 'file'
|
|
726
|
-
AND kg.relation IN ('implements', 'modifies', 'creates')
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
return results.map(r => r.file);
|
|
650
|
+
AND kg.relation IN ('implements', 'modifies', 'creates')`,
|
|
651
|
+
[type, decisionOrPattern]
|
|
652
|
+
);
|
|
653
|
+
return results.map((r: any) => r.file);
|
|
730
654
|
}
|
|
731
655
|
|
|
732
656
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -742,11 +666,9 @@ export interface ReasoningEntry {
|
|
|
742
666
|
importance?: "critical" | "high" | "normal" | "low";
|
|
743
667
|
}
|
|
744
668
|
|
|
745
|
-
export function addReasoning(specId: string, taskId: number | null, entry: ReasoningEntry): void {
|
|
746
|
-
const db = getDb();
|
|
669
|
+
export async function addReasoning(specId: string, taskId: number | null, entry: ReasoningEntry): Promise<void> {
|
|
747
670
|
const now = new Date().toISOString();
|
|
748
|
-
|
|
749
|
-
db.run(
|
|
671
|
+
await dbRun(
|
|
750
672
|
`INSERT INTO reasoning_log (spec_id, task_id, category, thought, related_files, related_decisions, related_patterns, importance, created_at)
|
|
751
673
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
752
674
|
[
|
|
@@ -763,49 +685,39 @@ export function addReasoning(specId: string, taskId: number | null, entry: Reaso
|
|
|
763
685
|
);
|
|
764
686
|
}
|
|
765
687
|
|
|
766
|
-
export function getRecentReasoning(specId: string, limit: number = 20): any[] {
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
WHERE spec_id = ?
|
|
772
|
-
ORDER BY created_at DESC
|
|
773
|
-
LIMIT ?
|
|
774
|
-
`).all(specId, limit) as any[];
|
|
688
|
+
export async function getRecentReasoning(specId: string, limit: number = 20): Promise<any[]> {
|
|
689
|
+
return dbAll(
|
|
690
|
+
"SELECT * FROM reasoning_log WHERE spec_id = ? ORDER BY created_at DESC LIMIT ?",
|
|
691
|
+
[specId, limit]
|
|
692
|
+
);
|
|
775
693
|
}
|
|
776
694
|
|
|
777
|
-
|
|
778
|
-
// Prioridade: analysis_id (link explicito) > nome exato > LIKE parcial
|
|
779
|
-
export function getArchitecturalAnalysisForSpec(specName: string, specId?: string): any {
|
|
780
|
-
const db = getDb();
|
|
781
|
-
|
|
695
|
+
export async function getArchitecturalAnalysisForSpec(specName: string, specId?: string): Promise<any> {
|
|
782
696
|
try {
|
|
783
|
-
// 0. Link explicito via analysis_id na tabela specs
|
|
784
697
|
if (specId) {
|
|
785
|
-
const spec =
|
|
698
|
+
const spec = await dbGet<any>("SELECT analysis_id FROM specs WHERE id = ?", [specId]);
|
|
786
699
|
if (spec?.analysis_id) {
|
|
787
|
-
const analysis =
|
|
788
|
-
"SELECT * FROM architectural_analyses WHERE id = ? AND status IN ('approved', 'implemented')"
|
|
789
|
-
|
|
700
|
+
const analysis = await dbGet(
|
|
701
|
+
"SELECT * FROM architectural_analyses WHERE id = ? AND status IN ('approved', 'implemented')",
|
|
702
|
+
[spec.analysis_id]
|
|
703
|
+
);
|
|
790
704
|
if (analysis) return analysis;
|
|
791
705
|
}
|
|
792
706
|
}
|
|
793
707
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
)
|
|
798
|
-
|
|
708
|
+
let analysis = await dbGet(
|
|
709
|
+
"SELECT * FROM architectural_analyses WHERE name = ? AND status IN ('approved', 'implemented') ORDER BY approved_at DESC LIMIT 1",
|
|
710
|
+
[specName]
|
|
711
|
+
);
|
|
799
712
|
if (analysis) return analysis;
|
|
800
713
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
)
|
|
714
|
+
analysis = await dbGet(
|
|
715
|
+
"SELECT * FROM architectural_analyses WHERE (name LIKE ? OR ? LIKE '%' || name || '%') AND status IN ('approved', 'implemented') ORDER BY approved_at DESC LIMIT 1",
|
|
716
|
+
[`%${specName}%`, specName]
|
|
717
|
+
);
|
|
805
718
|
|
|
806
719
|
return analysis || null;
|
|
807
720
|
} catch {
|
|
808
|
-
// Tabela pode nao existir se architect nunca foi usado
|
|
809
721
|
return null;
|
|
810
722
|
}
|
|
811
723
|
}
|
|
@@ -823,14 +735,13 @@ export interface ProjectUtility {
|
|
|
823
735
|
description?: string;
|
|
824
736
|
}
|
|
825
737
|
|
|
826
|
-
export function upsertUtility(
|
|
738
|
+
export async function upsertUtility(
|
|
827
739
|
utility: ProjectUtility,
|
|
828
740
|
specId?: string,
|
|
829
741
|
taskRef?: number
|
|
830
|
-
): void {
|
|
831
|
-
const db = getDb();
|
|
742
|
+
): Promise<void> {
|
|
832
743
|
const now = new Date().toISOString();
|
|
833
|
-
|
|
744
|
+
await dbRun(
|
|
834
745
|
`INSERT INTO project_utilities (file_path, utility_name, utility_type, scope, signature, description, spec_id, task_ref, created_at, updated_at)
|
|
835
746
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
836
747
|
ON CONFLICT(file_path, utility_name) DO UPDATE SET
|
|
@@ -847,21 +758,18 @@ export function upsertUtility(
|
|
|
847
758
|
);
|
|
848
759
|
}
|
|
849
760
|
|
|
850
|
-
export function getUtilitiesForContext(
|
|
761
|
+
export async function getUtilitiesForContext(
|
|
851
762
|
dirs: string[],
|
|
852
763
|
scope?: string,
|
|
853
764
|
limit: number = 15
|
|
854
|
-
): any[] {
|
|
855
|
-
const db = getDb();
|
|
856
|
-
|
|
857
|
-
// Se nenhum filtro, retornar mais recentes
|
|
765
|
+
): Promise<any[]> {
|
|
858
766
|
if (dirs.length === 0 && !scope) {
|
|
859
|
-
return
|
|
860
|
-
"SELECT * FROM project_utilities ORDER BY updated_at DESC LIMIT ?"
|
|
861
|
-
|
|
767
|
+
return dbAll(
|
|
768
|
+
"SELECT * FROM project_utilities ORDER BY updated_at DESC LIMIT ?",
|
|
769
|
+
[limit]
|
|
770
|
+
);
|
|
862
771
|
}
|
|
863
772
|
|
|
864
|
-
// Buscar por diretorio (prioridade) e scope (fallback)
|
|
865
773
|
const conditions: string[] = [];
|
|
866
774
|
const params: any[] = [];
|
|
867
775
|
|
|
@@ -878,24 +786,26 @@ export function getUtilitiesForContext(
|
|
|
878
786
|
|
|
879
787
|
params.push(limit);
|
|
880
788
|
const where = conditions.length > 0 ? `WHERE (${conditions.join(" OR ")})` : "";
|
|
881
|
-
return
|
|
882
|
-
`SELECT * FROM project_utilities ${where} ORDER BY updated_at DESC LIMIT
|
|
883
|
-
|
|
789
|
+
return dbAll(
|
|
790
|
+
`SELECT * FROM project_utilities ${where} ORDER BY updated_at DESC LIMIT ?`,
|
|
791
|
+
params
|
|
792
|
+
);
|
|
884
793
|
}
|
|
885
794
|
|
|
886
|
-
export function findDuplicateUtilities(
|
|
795
|
+
export async function findDuplicateUtilities(
|
|
887
796
|
utilityName: string,
|
|
888
797
|
excludeFile?: string
|
|
889
|
-
): any[] {
|
|
890
|
-
const db = getDb();
|
|
798
|
+
): Promise<any[]> {
|
|
891
799
|
if (excludeFile) {
|
|
892
|
-
return
|
|
893
|
-
"SELECT * FROM project_utilities WHERE utility_name = ? AND file_path != ?"
|
|
894
|
-
|
|
800
|
+
return dbAll(
|
|
801
|
+
"SELECT * FROM project_utilities WHERE utility_name = ? AND file_path != ?",
|
|
802
|
+
[utilityName, excludeFile]
|
|
803
|
+
);
|
|
895
804
|
}
|
|
896
|
-
return
|
|
897
|
-
"SELECT * FROM project_utilities WHERE utility_name = ?"
|
|
898
|
-
|
|
805
|
+
return dbAll(
|
|
806
|
+
"SELECT * FROM project_utilities WHERE utility_name = ?",
|
|
807
|
+
[utilityName]
|
|
808
|
+
);
|
|
899
809
|
}
|
|
900
810
|
|
|
901
811
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -915,10 +825,9 @@ export interface AgentPerformanceData {
|
|
|
915
825
|
executionDurationMs: number;
|
|
916
826
|
}
|
|
917
827
|
|
|
918
|
-
export function recordAgentPerformance(data: AgentPerformanceData): void {
|
|
919
|
-
const db = getDb();
|
|
828
|
+
export async function recordAgentPerformance(data: AgentPerformanceData): Promise<void> {
|
|
920
829
|
const now = new Date().toISOString();
|
|
921
|
-
|
|
830
|
+
await dbRun(
|
|
922
831
|
`INSERT INTO agent_performance
|
|
923
832
|
(agent_type, spec_id, task_id, gates_passed_first_try, gates_total, bypasses_used, files_created, files_modified, context_size_bytes, execution_duration_ms, created_at)
|
|
924
833
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
@@ -931,16 +840,16 @@ export function recordAgentPerformance(data: AgentPerformanceData): void {
|
|
|
931
840
|
);
|
|
932
841
|
}
|
|
933
842
|
|
|
934
|
-
export function getAgentHints(agentType: string, limit: number = 5): string[] {
|
|
935
|
-
const db = getDb();
|
|
843
|
+
export async function getAgentHints(agentType: string, limit: number = 5): Promise<string[]> {
|
|
936
844
|
const hints: string[] = [];
|
|
937
845
|
|
|
938
846
|
try {
|
|
939
|
-
const recent =
|
|
847
|
+
const recent = await dbAll(
|
|
940
848
|
`SELECT * FROM agent_performance
|
|
941
849
|
WHERE agent_type = ?
|
|
942
|
-
ORDER BY created_at DESC LIMIT
|
|
943
|
-
|
|
850
|
+
ORDER BY created_at DESC LIMIT ?`,
|
|
851
|
+
[agentType, limit]
|
|
852
|
+
);
|
|
944
853
|
|
|
945
854
|
if (recent.length === 0) return [];
|
|
946
855
|
|
|
@@ -957,17 +866,18 @@ export function getAgentHints(agentType: string, limit: number = 5): string[] {
|
|
|
957
866
|
hints.push(`ATENCAO: Gate pass rate baixo (${(avgGateRate * 100).toFixed(0)}%). Verifique standards e DRY obrigatorios.`);
|
|
958
867
|
}
|
|
959
868
|
|
|
960
|
-
const bypassTypes =
|
|
869
|
+
const bypassTypes = await dbAll(
|
|
961
870
|
`SELECT gb.gate_name, COUNT(*) as cnt FROM gate_bypasses gb
|
|
962
871
|
JOIN tasks t ON gb.task_id = t.id
|
|
963
872
|
WHERE t.agent = ?
|
|
964
873
|
GROUP BY gb.gate_name
|
|
965
|
-
ORDER BY cnt DESC LIMIT 3
|
|
966
|
-
|
|
874
|
+
ORDER BY cnt DESC LIMIT 3`,
|
|
875
|
+
[agentType]
|
|
876
|
+
);
|
|
967
877
|
|
|
968
878
|
for (const bp of bypassTypes) {
|
|
969
|
-
if (bp.cnt >= 2) {
|
|
970
|
-
hints.push(`Gate '${bp.gate_name}' frequentemente ignorado (${bp.cnt}x). Preste atencao especial.`);
|
|
879
|
+
if ((bp as any).cnt >= 2) {
|
|
880
|
+
hints.push(`Gate '${(bp as any).gate_name}' frequentemente ignorado (${(bp as any).cnt}x). Preste atencao especial.`);
|
|
971
881
|
}
|
|
972
882
|
}
|
|
973
883
|
} catch {
|