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