@codexa/cli 9.0.2 → 9.0.4
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.test.ts +531 -0
- package/commands/architect.ts +75 -17
- package/commands/check.ts +7 -17
- package/commands/clear.ts +40 -1
- package/commands/decide.ts +37 -49
- package/commands/discover.ts +136 -28
- package/commands/knowledge.test.ts +160 -0
- package/commands/knowledge.ts +192 -102
- package/commands/patterns.test.ts +169 -0
- package/commands/patterns.ts +6 -13
- package/commands/plan.test.ts +73 -0
- package/commands/plan.ts +18 -66
- package/commands/product.ts +8 -17
- package/commands/research.ts +4 -3
- package/commands/review.ts +190 -28
- package/commands/spec-resolver.test.ts +119 -0
- package/commands/spec-resolver.ts +90 -0
- package/commands/standards.ts +7 -15
- package/commands/sync.ts +89 -0
- package/commands/task.ts +72 -167
- package/commands/utils.test.ts +100 -0
- package/commands/utils.ts +78 -706
- package/db/schema.test.ts +760 -0
- package/db/schema.ts +284 -130
- package/gates/validator.test.ts +675 -0
- package/gates/validator.ts +112 -27
- package/package.json +3 -1
- package/protocol/process-return.ts +25 -93
- package/protocol/subagent-protocol.test.ts +936 -0
- package/protocol/subagent-protocol.ts +19 -1
- package/workflow.ts +176 -67
package/db/schema.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
1
2
|
import { getDb } from "./connection";
|
|
2
3
|
|
|
3
4
|
export function initSchema(): void {
|
|
@@ -263,30 +264,7 @@ export function initSchema(): void {
|
|
|
263
264
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
264
265
|
);
|
|
265
266
|
|
|
266
|
-
-- 20.
|
|
267
|
-
CREATE TABLE IF NOT EXISTS session_summaries (
|
|
268
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
269
|
-
spec_id TEXT NOT NULL REFERENCES specs(id),
|
|
270
|
-
|
|
271
|
-
-- Periodo
|
|
272
|
-
start_time TEXT NOT NULL,
|
|
273
|
-
end_time TEXT NOT NULL,
|
|
274
|
-
|
|
275
|
-
-- Conteudo
|
|
276
|
-
summary TEXT NOT NULL, -- O que foi feito
|
|
277
|
-
decisions TEXT, -- JSON array de decisoes tomadas
|
|
278
|
-
blockers TEXT, -- JSON array de problemas encontrados
|
|
279
|
-
next_steps TEXT, -- JSON array de recomendacoes
|
|
280
|
-
|
|
281
|
-
-- Estatisticas
|
|
282
|
-
tasks_completed INTEGER DEFAULT 0,
|
|
283
|
-
files_created INTEGER DEFAULT 0,
|
|
284
|
-
files_modified INTEGER DEFAULT 0,
|
|
285
|
-
|
|
286
|
-
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
287
|
-
);
|
|
288
|
-
|
|
289
|
-
-- 21. Architectural Analyses (v8.1 - migrado de architect.ts para schema central)
|
|
267
|
+
-- 20. Architectural Analyses (v8.1 - migrado de architect.ts para schema central)
|
|
290
268
|
CREATE TABLE IF NOT EXISTS architectural_analyses (
|
|
291
269
|
id TEXT PRIMARY KEY,
|
|
292
270
|
name TEXT NOT NULL,
|
|
@@ -329,6 +307,13 @@ export function initSchema(): void {
|
|
|
329
307
|
UNIQUE(file_path, utility_name)
|
|
330
308
|
);
|
|
331
309
|
|
|
310
|
+
-- 23. Schema Migrations tracking (v9.2)
|
|
311
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
312
|
+
version TEXT PRIMARY KEY,
|
|
313
|
+
description TEXT NOT NULL,
|
|
314
|
+
applied_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
315
|
+
);
|
|
316
|
+
|
|
332
317
|
-- Indices para performance
|
|
333
318
|
CREATE INDEX IF NOT EXISTS idx_tasks_spec ON tasks(spec_id);
|
|
334
319
|
CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
|
|
@@ -363,10 +348,6 @@ export function initSchema(): void {
|
|
|
363
348
|
CREATE INDEX IF NOT EXISTS idx_reasoning_category ON reasoning_log(category);
|
|
364
349
|
CREATE INDEX IF NOT EXISTS idx_reasoning_importance ON reasoning_log(importance);
|
|
365
350
|
|
|
366
|
-
-- v8.0: Indices para Session Summaries
|
|
367
|
-
CREATE INDEX IF NOT EXISTS idx_session_spec ON session_summaries(spec_id);
|
|
368
|
-
CREATE INDEX IF NOT EXISTS idx_session_time ON session_summaries(end_time);
|
|
369
|
-
|
|
370
351
|
-- v8.1: Indices para Architectural Analyses
|
|
371
352
|
CREATE INDEX IF NOT EXISTS idx_arch_status ON architectural_analyses(status);
|
|
372
353
|
CREATE INDEX IF NOT EXISTS idx_arch_created ON architectural_analyses(created_at);
|
|
@@ -377,21 +358,169 @@ export function initSchema(): void {
|
|
|
377
358
|
CREATE INDEX IF NOT EXISTS idx_utils_file ON project_utilities(file_path);
|
|
378
359
|
`);
|
|
379
360
|
|
|
380
|
-
//
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
} catch {
|
|
384
|
-
// Coluna ja existe - ignorar
|
|
385
|
-
}
|
|
361
|
+
// Executar migracoes versionadas
|
|
362
|
+
runMigrations();
|
|
363
|
+
}
|
|
386
364
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
365
|
+
// ═══════════════════════════════════════════════════════════════
|
|
366
|
+
// v9.2: Sistema de Migracoes Versionado
|
|
367
|
+
// ═══════════════════════════════════════════════════════════════
|
|
368
|
+
|
|
369
|
+
interface Migration {
|
|
370
|
+
version: string;
|
|
371
|
+
description: string;
|
|
372
|
+
up: (db: Database) => void;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const MIGRATIONS: Migration[] = [
|
|
376
|
+
{
|
|
377
|
+
version: "8.4.0",
|
|
378
|
+
description: "Adicionar analysis_id na tabela specs",
|
|
379
|
+
up: (db) => {
|
|
380
|
+
db.exec(`ALTER TABLE specs ADD COLUMN analysis_id TEXT`);
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
version: "8.7.0",
|
|
385
|
+
description: "Adicionar cli_version na tabela project",
|
|
386
|
+
up: (db) => {
|
|
387
|
+
db.exec(`ALTER TABLE project ADD COLUMN cli_version TEXT`);
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
version: "9.1.0",
|
|
392
|
+
description: "Adicionar last_discover_at na tabela project",
|
|
393
|
+
up: (db) => {
|
|
394
|
+
db.exec(`ALTER TABLE project ADD COLUMN last_discover_at TEXT`);
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
version: "9.1.1",
|
|
399
|
+
description: "Remover tabela session_summaries",
|
|
400
|
+
up: (db) => {
|
|
401
|
+
db.exec(`DROP TABLE IF EXISTS session_summaries`);
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
version: "9.2.0",
|
|
406
|
+
description: "Adicionar started_at na tabela tasks para validacao temporal de arquivos",
|
|
407
|
+
up: (db) => {
|
|
408
|
+
db.exec(`ALTER TABLE tasks ADD COLUMN started_at TEXT`);
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
version: "9.3.0",
|
|
413
|
+
description: "Criar tabela agent_performance para feedback loop",
|
|
414
|
+
up: (db) => {
|
|
415
|
+
db.exec(`
|
|
416
|
+
CREATE TABLE IF NOT EXISTS agent_performance (
|
|
417
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
418
|
+
agent_type TEXT NOT NULL,
|
|
419
|
+
spec_id TEXT NOT NULL,
|
|
420
|
+
task_id INTEGER NOT NULL,
|
|
421
|
+
gates_passed_first_try INTEGER DEFAULT 0,
|
|
422
|
+
gates_total INTEGER DEFAULT 0,
|
|
423
|
+
bypasses_used INTEGER DEFAULT 0,
|
|
424
|
+
files_created INTEGER DEFAULT 0,
|
|
425
|
+
files_modified INTEGER DEFAULT 0,
|
|
426
|
+
context_size_bytes INTEGER DEFAULT 0,
|
|
427
|
+
execution_duration_ms INTEGER DEFAULT 0,
|
|
428
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
429
|
+
)
|
|
430
|
+
`);
|
|
431
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_agent_perf_type ON agent_performance(agent_type)`);
|
|
432
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_agent_perf_created ON agent_performance(created_at)`);
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
version: "9.4.0",
|
|
437
|
+
description: "Migrar acknowledged_by de JSON para tabela separada",
|
|
438
|
+
up: (db) => {
|
|
439
|
+
db.exec(`
|
|
440
|
+
CREATE TABLE IF NOT EXISTS knowledge_acknowledgments (
|
|
441
|
+
knowledge_id INTEGER NOT NULL REFERENCES knowledge(id) ON DELETE CASCADE,
|
|
442
|
+
task_id INTEGER NOT NULL,
|
|
443
|
+
acknowledged_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
444
|
+
PRIMARY KEY (knowledge_id, task_id)
|
|
445
|
+
)
|
|
446
|
+
`);
|
|
447
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_ka_task ON knowledge_acknowledgments(task_id)`);
|
|
448
|
+
|
|
449
|
+
// Migrar dados existentes do campo JSON
|
|
450
|
+
const rows = db.query("SELECT id, acknowledged_by FROM knowledge WHERE acknowledged_by IS NOT NULL").all() as any[];
|
|
451
|
+
const insert = db.prepare("INSERT OR IGNORE INTO knowledge_acknowledgments (knowledge_id, task_id) VALUES (?, ?)");
|
|
452
|
+
for (const row of rows) {
|
|
453
|
+
try {
|
|
454
|
+
const taskIds = JSON.parse(row.acknowledged_by) as number[];
|
|
455
|
+
for (const taskId of taskIds) {
|
|
456
|
+
insert.run(row.id, taskId);
|
|
457
|
+
}
|
|
458
|
+
} catch { /* JSON invalido, ignorar */ }
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
];
|
|
463
|
+
|
|
464
|
+
export function runMigrations(): void {
|
|
465
|
+
const db = getDb();
|
|
466
|
+
|
|
467
|
+
for (const migration of MIGRATIONS) {
|
|
468
|
+
// Verificar se ja foi aplicada
|
|
469
|
+
const existing = db
|
|
470
|
+
.query("SELECT version FROM schema_migrations WHERE version = ?")
|
|
471
|
+
.get(migration.version);
|
|
472
|
+
|
|
473
|
+
if (existing) continue;
|
|
474
|
+
|
|
475
|
+
// Executar migracao
|
|
476
|
+
try {
|
|
477
|
+
migration.up(db);
|
|
478
|
+
} catch (e: any) {
|
|
479
|
+
const msg = (e as Error).message || "";
|
|
480
|
+
// "duplicate column name" e esperado em DBs criadas antes do sistema de migracoes
|
|
481
|
+
if (msg.includes("duplicate column name") || msg.includes("already exists")) {
|
|
482
|
+
// Marcar como aplicada mesmo assim — DB ja tem a mudanca
|
|
483
|
+
} else {
|
|
484
|
+
throw new Error(
|
|
485
|
+
`Migracao ${migration.version} falhou: ${msg}\n` +
|
|
486
|
+
`Descricao: ${migration.description}`
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Registrar migracao como aplicada
|
|
492
|
+
const now = new Date().toISOString();
|
|
493
|
+
db.run(
|
|
494
|
+
"INSERT INTO schema_migrations (version, description, applied_at) VALUES (?, ?, ?)",
|
|
495
|
+
[migration.version, migration.description, now]
|
|
496
|
+
);
|
|
392
497
|
}
|
|
393
498
|
}
|
|
394
499
|
|
|
500
|
+
// Exportar MIGRATIONS para testes
|
|
501
|
+
export { MIGRATIONS };
|
|
502
|
+
|
|
503
|
+
// Gera proximo ID de decisao para um spec
|
|
504
|
+
// Usa timestamp + random hash para eliminar race conditions entre tasks paralelas
|
|
505
|
+
export function getNextDecisionId(specId: string): string {
|
|
506
|
+
const slug = specId.split("-").slice(1, 3).join("-");
|
|
507
|
+
const ts = Date.now().toString(36);
|
|
508
|
+
const rand = Math.random().toString(36).substring(2, 6);
|
|
509
|
+
return `DEC-${slug}-${ts}-${rand}`;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Claim atomico de task: retorna true se task estava pending e agora esta running.
|
|
513
|
+
// Retorna false se outra instancia ja pegou a task.
|
|
514
|
+
export function claimTask(taskId: number): boolean {
|
|
515
|
+
const db = getDb();
|
|
516
|
+
const now = new Date().toISOString();
|
|
517
|
+
const result = db.run(
|
|
518
|
+
"UPDATE tasks SET status = 'running', started_at = ? WHERE id = ? AND status = 'pending'",
|
|
519
|
+
[now, taskId]
|
|
520
|
+
);
|
|
521
|
+
return result.changes > 0;
|
|
522
|
+
}
|
|
523
|
+
|
|
395
524
|
export function getPatternsByScope(scope: string): any[] {
|
|
396
525
|
const db = getDb();
|
|
397
526
|
return db.query(
|
|
@@ -401,21 +530,45 @@ export function getPatternsByScope(scope: string): any[] {
|
|
|
401
530
|
|
|
402
531
|
export function getPatternsForFiles(files: string[]): any[] {
|
|
403
532
|
const db = getDb();
|
|
404
|
-
|
|
533
|
+
if (files.length === 0) return [];
|
|
534
|
+
|
|
535
|
+
// Pre-filtrar no SQL usando extensoes e diretorios para reduzir candidatos
|
|
536
|
+
const extensions = new Set<string>();
|
|
537
|
+
const dirs = new Set<string>();
|
|
538
|
+
for (const f of files) {
|
|
539
|
+
const ext = f.split('.').pop();
|
|
540
|
+
if (ext) extensions.add(ext);
|
|
541
|
+
const parts = f.split('/');
|
|
542
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
543
|
+
dirs.add(parts[i]);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
405
546
|
|
|
406
|
-
|
|
407
|
-
|
|
547
|
+
const conditions: string[] = [];
|
|
548
|
+
const params: string[] = [];
|
|
549
|
+
for (const ext of extensions) {
|
|
550
|
+
conditions.push("applies_to LIKE ?");
|
|
551
|
+
params.push(`%${ext}%`);
|
|
552
|
+
}
|
|
553
|
+
for (const dir of dirs) {
|
|
554
|
+
conditions.push("applies_to LIKE ?");
|
|
555
|
+
params.push(`%${dir}%`);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Se nao ha condicoes, buscar tudo (fallback)
|
|
559
|
+
const candidates = conditions.length > 0
|
|
560
|
+
? db.query(`SELECT * FROM implementation_patterns WHERE ${conditions.join(' OR ')}`).all(...params) as any[]
|
|
561
|
+
: db.query("SELECT * FROM implementation_patterns").all() as any[];
|
|
562
|
+
|
|
563
|
+
// Regex match apenas nos candidatos pre-filtrados
|
|
564
|
+
return candidates.filter(pattern => {
|
|
408
565
|
const glob = pattern.applies_to;
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
.replace(/\//g, '\\/');
|
|
416
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
417
|
-
return regex.test(file);
|
|
418
|
-
});
|
|
566
|
+
const regexPattern = glob
|
|
567
|
+
.replace(/\*\*/g, '.*')
|
|
568
|
+
.replace(/\*/g, '[^/]*')
|
|
569
|
+
.replace(/\//g, '\\/');
|
|
570
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
571
|
+
return files.some(file => regex.test(file));
|
|
419
572
|
});
|
|
420
573
|
}
|
|
421
574
|
|
|
@@ -478,22 +631,6 @@ export function getRelatedFiles(decisionOrPattern: string, type: "decision" | "p
|
|
|
478
631
|
return results.map(r => r.file);
|
|
479
632
|
}
|
|
480
633
|
|
|
481
|
-
export function findContradictions(specId: string): Array<{ decision1: string; decision2: string; createdAt: string }> {
|
|
482
|
-
const db = getDb();
|
|
483
|
-
|
|
484
|
-
return db.query(`
|
|
485
|
-
SELECT
|
|
486
|
-
d1.title as decision1,
|
|
487
|
-
d2.title as decision2,
|
|
488
|
-
kg.created_at as createdAt
|
|
489
|
-
FROM knowledge_graph kg
|
|
490
|
-
JOIN decisions d1 ON kg.source_id = d1.id
|
|
491
|
-
JOIN decisions d2 ON kg.target_id = d2.id
|
|
492
|
-
WHERE kg.relation = 'contradicts'
|
|
493
|
-
AND kg.spec_id = ?
|
|
494
|
-
`).all(specId) as any[];
|
|
495
|
-
}
|
|
496
|
-
|
|
497
634
|
// ═══════════════════════════════════════════════════════════════
|
|
498
635
|
// v8.0: Reasoning Log Helpers
|
|
499
636
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -539,68 +676,6 @@ export function getRecentReasoning(specId: string, limit: number = 20): any[] {
|
|
|
539
676
|
`).all(specId, limit) as any[];
|
|
540
677
|
}
|
|
541
678
|
|
|
542
|
-
// ═══════════════════════════════════════════════════════════════
|
|
543
|
-
// v8.0: Session Summary Helpers
|
|
544
|
-
// ═══════════════════════════════════════════════════════════════
|
|
545
|
-
|
|
546
|
-
export interface SessionSummaryData {
|
|
547
|
-
startTime: string;
|
|
548
|
-
endTime: string;
|
|
549
|
-
summary: string;
|
|
550
|
-
decisions?: string[];
|
|
551
|
-
blockers?: string[];
|
|
552
|
-
nextSteps?: string[];
|
|
553
|
-
tasksCompleted?: number;
|
|
554
|
-
filesCreated?: number;
|
|
555
|
-
filesModified?: number;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
export function addSessionSummary(specId: string, data: SessionSummaryData): void {
|
|
559
|
-
const db = getDb();
|
|
560
|
-
const now = new Date().toISOString();
|
|
561
|
-
|
|
562
|
-
db.run(
|
|
563
|
-
`INSERT INTO session_summaries (spec_id, start_time, end_time, summary, decisions, blockers, next_steps, tasks_completed, files_created, files_modified, created_at)
|
|
564
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
565
|
-
[
|
|
566
|
-
specId,
|
|
567
|
-
data.startTime,
|
|
568
|
-
data.endTime,
|
|
569
|
-
data.summary,
|
|
570
|
-
data.decisions ? JSON.stringify(data.decisions) : null,
|
|
571
|
-
data.blockers ? JSON.stringify(data.blockers) : null,
|
|
572
|
-
data.nextSteps ? JSON.stringify(data.nextSteps) : null,
|
|
573
|
-
data.tasksCompleted || 0,
|
|
574
|
-
data.filesCreated || 0,
|
|
575
|
-
data.filesModified || 0,
|
|
576
|
-
now,
|
|
577
|
-
]
|
|
578
|
-
);
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
export function getLastSessionSummary(specId: string): any {
|
|
582
|
-
const db = getDb();
|
|
583
|
-
|
|
584
|
-
return db.query(`
|
|
585
|
-
SELECT * FROM session_summaries
|
|
586
|
-
WHERE spec_id = ?
|
|
587
|
-
ORDER BY created_at DESC
|
|
588
|
-
LIMIT 1
|
|
589
|
-
`).get(specId);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
// v8.3: Retorna ultimas N sessoes para contexto composto
|
|
593
|
-
export function getSessionSummaries(specId: string, limit: number = 5): any[] {
|
|
594
|
-
const db = getDb();
|
|
595
|
-
|
|
596
|
-
return db.query(`
|
|
597
|
-
SELECT * FROM session_summaries
|
|
598
|
-
WHERE spec_id = ?
|
|
599
|
-
ORDER BY created_at DESC
|
|
600
|
-
LIMIT ?
|
|
601
|
-
`).all(specId, limit) as any[];
|
|
602
|
-
}
|
|
603
|
-
|
|
604
679
|
// v8.4: Busca analise arquitetural associada a um spec
|
|
605
680
|
// Prioridade: analysis_id (link explicito) > nome exato > LIKE parcial
|
|
606
681
|
export function getArchitecturalAnalysisForSpec(specName: string, specId?: string): any {
|
|
@@ -724,3 +799,82 @@ export function findDuplicateUtilities(
|
|
|
724
799
|
"SELECT * FROM project_utilities WHERE utility_name = ?"
|
|
725
800
|
).all(utilityName) as any[];
|
|
726
801
|
}
|
|
802
|
+
|
|
803
|
+
// ═══════════════════════════════════════════════════════════════
|
|
804
|
+
// v9.3: Agent Performance Tracking (Feedback Loop)
|
|
805
|
+
// ═══════════════════════════════════════════════════════════════
|
|
806
|
+
|
|
807
|
+
export interface AgentPerformanceData {
|
|
808
|
+
agentType: string;
|
|
809
|
+
specId: string;
|
|
810
|
+
taskId: number;
|
|
811
|
+
gatesPassedFirstTry: number;
|
|
812
|
+
gatesTotal: number;
|
|
813
|
+
bypassesUsed: number;
|
|
814
|
+
filesCreated: number;
|
|
815
|
+
filesModified: number;
|
|
816
|
+
contextSizeBytes: number;
|
|
817
|
+
executionDurationMs: number;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
export function recordAgentPerformance(data: AgentPerformanceData): void {
|
|
821
|
+
const db = getDb();
|
|
822
|
+
const now = new Date().toISOString();
|
|
823
|
+
db.run(
|
|
824
|
+
`INSERT INTO agent_performance
|
|
825
|
+
(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)
|
|
826
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
827
|
+
[
|
|
828
|
+
data.agentType, data.specId, data.taskId,
|
|
829
|
+
data.gatesPassedFirstTry, data.gatesTotal, data.bypassesUsed,
|
|
830
|
+
data.filesCreated, data.filesModified,
|
|
831
|
+
data.contextSizeBytes, data.executionDurationMs, now,
|
|
832
|
+
]
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
export function getAgentHints(agentType: string, limit: number = 5): string[] {
|
|
837
|
+
const db = getDb();
|
|
838
|
+
const hints: string[] = [];
|
|
839
|
+
|
|
840
|
+
try {
|
|
841
|
+
const recent = db.query(
|
|
842
|
+
`SELECT * FROM agent_performance
|
|
843
|
+
WHERE agent_type = ?
|
|
844
|
+
ORDER BY created_at DESC LIMIT ?`
|
|
845
|
+
).all(agentType, limit) as any[];
|
|
846
|
+
|
|
847
|
+
if (recent.length === 0) return [];
|
|
848
|
+
|
|
849
|
+
const avgBypass = recent.reduce((sum: number, r: any) => sum + r.bypasses_used, 0) / recent.length;
|
|
850
|
+
const avgGateRate = recent.reduce((sum: number, r: any) => {
|
|
851
|
+
return sum + (r.gates_total > 0 ? r.gates_passed_first_try / r.gates_total : 1);
|
|
852
|
+
}, 0) / recent.length;
|
|
853
|
+
|
|
854
|
+
if (avgBypass > 0.5) {
|
|
855
|
+
hints.push(`ATENCAO: Este agente usa bypasses frequentemente (media ${avgBypass.toFixed(1)}/task). Revise standards antes de iniciar.`);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (avgGateRate < 0.7) {
|
|
859
|
+
hints.push(`ATENCAO: Gate pass rate baixo (${(avgGateRate * 100).toFixed(0)}%). Verifique standards e DRY obrigatorios.`);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
const bypassTypes = db.query(
|
|
863
|
+
`SELECT gb.gate_name, COUNT(*) as cnt FROM gate_bypasses gb
|
|
864
|
+
JOIN tasks t ON gb.task_id = t.id
|
|
865
|
+
WHERE t.agent = ?
|
|
866
|
+
GROUP BY gb.gate_name
|
|
867
|
+
ORDER BY cnt DESC LIMIT 3`
|
|
868
|
+
).all(agentType) as any[];
|
|
869
|
+
|
|
870
|
+
for (const bp of bypassTypes) {
|
|
871
|
+
if (bp.cnt >= 2) {
|
|
872
|
+
hints.push(`Gate '${bp.gate_name}' frequentemente ignorado (${bp.cnt}x). Preste atencao especial.`);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
} catch {
|
|
876
|
+
// Tabela pode nao existir ainda
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
return hints;
|
|
880
|
+
}
|