@codexa/cli 9.0.22 → 9.0.24
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/review.ts +12 -0
- package/commands/simplify.ts +142 -0
- package/commands/task.ts +23 -1
- package/db/schema.ts +7 -0
- package/package.json +2 -1
- package/simplify/prompt-builder.test.ts +256 -0
- package/simplify/prompt-builder.ts +198 -0
- package/workflow.ts +24 -0
package/commands/review.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { enforceGate } from "../gates/validator";
|
|
|
4
4
|
import { resolveSpec } from "./spec-resolver";
|
|
5
5
|
import { CodexaError, GateError } from "../errors";
|
|
6
6
|
import { cleanupContextFiles } from "../context/file-writer";
|
|
7
|
+
import { simplifyAudit } from "./simplify";
|
|
7
8
|
|
|
8
9
|
// ═══════════════════════════════════════════════════════════════
|
|
9
10
|
// P1-2: Review Score — Threshold Automatico
|
|
@@ -264,6 +265,17 @@ export function reviewStart(json: boolean = false, specId?: string): void {
|
|
|
264
265
|
console.log(`\nNenhum desvio encontrado.`);
|
|
265
266
|
}
|
|
266
267
|
|
|
268
|
+
// v10.1: Simplify audit
|
|
269
|
+
try {
|
|
270
|
+
const simplifyContextPath = simplifyAudit(spec.id);
|
|
271
|
+
if (simplifyContextPath) {
|
|
272
|
+
console.log(`\nAuditoria de Simplificacao:`);
|
|
273
|
+
console.log(` Prompt gerado com standards do projeto`);
|
|
274
|
+
console.log(` Contexto: ${simplifyContextPath}`);
|
|
275
|
+
console.log(` Invoque o simplifier agent em modo audit para analise de qualidade`);
|
|
276
|
+
}
|
|
277
|
+
} catch { /* nao-critico */ }
|
|
278
|
+
|
|
267
279
|
if (score.total < 50) {
|
|
268
280
|
console.log(`\n[!] Score abaixo de 50. Aprovacao requer: review approve --force --force-reason "motivo"`);
|
|
269
281
|
} else {
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2
|
+
// SIMPLIFY COMMAND (v10.1)
|
|
3
|
+
// Generates simplification prompts enriched with Codexa standards.
|
|
4
|
+
// Used post-task (Option A) and pre-review (Option C).
|
|
5
|
+
// ═══════════════════════════════════════════════════════════════
|
|
6
|
+
|
|
7
|
+
import { getDb } from "../db/connection";
|
|
8
|
+
import { initSchema } from "../db/schema";
|
|
9
|
+
import { resolveSpec } from "./spec-resolver";
|
|
10
|
+
import { getAgentDomain, domainToScope } from "../context/domains";
|
|
11
|
+
import { CodexaError } from "../errors";
|
|
12
|
+
import {
|
|
13
|
+
buildSimplifyPrompt,
|
|
14
|
+
writeSimplifyContext,
|
|
15
|
+
type SimplifyFile,
|
|
16
|
+
type SimplifyMode,
|
|
17
|
+
} from "../simplify/prompt-builder";
|
|
18
|
+
|
|
19
|
+
// ═══════════════════════════════════════════════════════════════
|
|
20
|
+
// SIMPLIFY FOR TASK (Option A)
|
|
21
|
+
// ═══════════════════════════════════════════════════════════════
|
|
22
|
+
|
|
23
|
+
export function simplifyTask(
|
|
24
|
+
taskId: string,
|
|
25
|
+
options: { audit?: boolean; specId?: string } = {}
|
|
26
|
+
): void {
|
|
27
|
+
initSchema();
|
|
28
|
+
|
|
29
|
+
const db = getDb();
|
|
30
|
+
const id = parseInt(taskId);
|
|
31
|
+
const task = db.query("SELECT * FROM tasks WHERE id = ?").get(id) as any;
|
|
32
|
+
|
|
33
|
+
if (!task) {
|
|
34
|
+
throw new CodexaError(`Task #${id} nao encontrada.`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (task.status !== "done") {
|
|
38
|
+
throw new CodexaError(
|
|
39
|
+
`Task #${id} nao esta concluida (status: ${task.status}). Complete com 'task done' primeiro.`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Get files from artifacts
|
|
44
|
+
const artifacts = db
|
|
45
|
+
.query(
|
|
46
|
+
"SELECT path, action FROM artifacts WHERE spec_id = ? AND task_ref = ?"
|
|
47
|
+
)
|
|
48
|
+
.all(task.spec_id, task.number) as any[];
|
|
49
|
+
|
|
50
|
+
if (artifacts.length === 0) {
|
|
51
|
+
console.log(`\nTask #${task.number} nao tem arquivos registrados. Nada para simplificar.\n`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const files: SimplifyFile[] = artifacts.map((a: any) => ({
|
|
56
|
+
path: a.path,
|
|
57
|
+
action: a.action === "created" ? "created" as const : "modified" as const,
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
// Determine agent domain for scope filtering
|
|
61
|
+
const domain = getAgentDomain(task.agent);
|
|
62
|
+
const scope = domainToScope(domain);
|
|
63
|
+
const mode: SimplifyMode = options.audit ? "audit" : "refactor";
|
|
64
|
+
|
|
65
|
+
// Build prompt with standards
|
|
66
|
+
const prompt = buildSimplifyPrompt(files, scope, mode);
|
|
67
|
+
|
|
68
|
+
// Write context file
|
|
69
|
+
const contextPath = writeSimplifyContext(`task-${task.number}`, prompt);
|
|
70
|
+
|
|
71
|
+
// Output
|
|
72
|
+
const modeLabel = mode === "audit" ? "AUDITORIA" : "REFATORACAO";
|
|
73
|
+
console.log(`\n${"=".repeat(55)}`);
|
|
74
|
+
console.log(`SIMPLIFY (${modeLabel}) — Task #${task.number}`);
|
|
75
|
+
console.log(`${"=".repeat(55)}`);
|
|
76
|
+
console.log(` Arquivos: ${files.length}`);
|
|
77
|
+
console.log(` Scope: ${scope}`);
|
|
78
|
+
console.log(` Modo: ${mode}`);
|
|
79
|
+
console.log(` Contexto: ${contextPath}`);
|
|
80
|
+
console.log(`\n O orchestrador deve invocar o simplifier agent com o prompt acima.`);
|
|
81
|
+
console.log(` Leia o contexto com: Read ${contextPath}`);
|
|
82
|
+
console.log(`${"=".repeat(55)}\n`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ═══════════════════════════════════════════════════════════════
|
|
86
|
+
// SIMPLIFY AUDIT FOR REVIEW (Option C)
|
|
87
|
+
// ═══════════════════════════════════════════════════════════════
|
|
88
|
+
|
|
89
|
+
export function simplifyAudit(specId?: string): string | null {
|
|
90
|
+
initSchema();
|
|
91
|
+
|
|
92
|
+
const db = getDb();
|
|
93
|
+
const spec = resolveSpec(specId, ["implementing", "reviewing"]);
|
|
94
|
+
|
|
95
|
+
// Get all feature artifacts
|
|
96
|
+
const artifacts = db
|
|
97
|
+
.query("SELECT DISTINCT path, action FROM artifacts WHERE spec_id = ?")
|
|
98
|
+
.all(spec.id) as any[];
|
|
99
|
+
|
|
100
|
+
if (artifacts.length === 0) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const files: SimplifyFile[] = artifacts.map((a: any) => ({
|
|
105
|
+
path: a.path,
|
|
106
|
+
action: a.action === "created" ? "created" as const : "modified" as const,
|
|
107
|
+
}));
|
|
108
|
+
|
|
109
|
+
// Use "all" scope for review (covers all domains)
|
|
110
|
+
const prompt = buildSimplifyPrompt(files, "all", "audit");
|
|
111
|
+
|
|
112
|
+
// Write context file
|
|
113
|
+
const contextPath = writeSimplifyContext(`audit-${spec.id}`, prompt);
|
|
114
|
+
|
|
115
|
+
return contextPath;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ═══════════════════════════════════════════════════════════════
|
|
119
|
+
// AUTO-SIMPLIFY CHECK
|
|
120
|
+
// ═══════════════════════════════════════════════════════════════
|
|
121
|
+
|
|
122
|
+
export function isAutoSimplifyEnabled(): boolean {
|
|
123
|
+
try {
|
|
124
|
+
const db = getDb();
|
|
125
|
+
const project = db
|
|
126
|
+
.query("SELECT auto_simplify FROM project WHERE id = 'default'")
|
|
127
|
+
.get() as any;
|
|
128
|
+
return project?.auto_simplify === 1;
|
|
129
|
+
} catch {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function setAutoSimplify(enabled: boolean): void {
|
|
135
|
+
initSchema();
|
|
136
|
+
const db = getDb();
|
|
137
|
+
db.run(
|
|
138
|
+
"UPDATE project SET auto_simplify = ?, updated_at = ? WHERE id = 'default'",
|
|
139
|
+
[enabled ? 1 : 0, new Date().toISOString()]
|
|
140
|
+
);
|
|
141
|
+
console.log(`\nauto_simplify ${enabled ? "habilitado" : "desabilitado"}.\n`);
|
|
142
|
+
}
|
package/commands/task.ts
CHANGED
|
@@ -16,6 +16,8 @@ import { getModelForTask } from "../context/model-profiles";
|
|
|
16
16
|
import { getContextBudget, formatContextWarning, estimateTokens } from "../context/monitor";
|
|
17
17
|
import { invalidateCache } from "../context/cache";
|
|
18
18
|
import { cleanupContextFiles } from "../context/file-writer";
|
|
19
|
+
import { isAutoSimplifyEnabled } from "./simplify";
|
|
20
|
+
import { buildSimplifyPrompt, writeSimplifyContext, type SimplifyFile } from "../simplify/prompt-builder";
|
|
19
21
|
|
|
20
22
|
export function taskNext(json: boolean = false, specId?: string): void {
|
|
21
23
|
initSchema();
|
|
@@ -369,7 +371,7 @@ export function taskStart(ids: string, json: boolean = false, minimalContext: bo
|
|
|
369
371
|
console.log(`\nAo concluir, use: task done <id> --checkpoint "resumo"\n`);
|
|
370
372
|
}
|
|
371
373
|
|
|
372
|
-
export function taskDone(id: string, options: { checkpoint: string; files?: string; force?: boolean; forceReason?: string; output?: string }): void {
|
|
374
|
+
export function taskDone(id: string, options: { checkpoint: string; files?: string; force?: boolean; forceReason?: string; output?: string; simplify?: boolean }): void {
|
|
373
375
|
initSchema();
|
|
374
376
|
|
|
375
377
|
const taskId = parseInt(id);
|
|
@@ -505,6 +507,26 @@ export function taskDone(id: string, options: { checkpoint: string; files?: stri
|
|
|
505
507
|
|
|
506
508
|
// Nota: Validacao de standards agora e feita pelo Gate 4.2 (standards-follow) em enforceGate
|
|
507
509
|
|
|
510
|
+
// v10.1: Simplify pos-task (opt-in)
|
|
511
|
+
try {
|
|
512
|
+
const shouldSimplify = options.simplify || isAutoSimplifyEnabled();
|
|
513
|
+
if (shouldSimplify && expectedFiles.length > 0) {
|
|
514
|
+
const domain = getAgentDomain(task.agent);
|
|
515
|
+
const scope = domainToScope(domain);
|
|
516
|
+
const simplifyFiles: SimplifyFile[] = expectedFiles.map((f: string) => ({
|
|
517
|
+
path: f,
|
|
518
|
+
action: (subagentData?.files_created?.includes(f) ? "created" : "modified") as "created" | "modified",
|
|
519
|
+
}));
|
|
520
|
+
|
|
521
|
+
const prompt = buildSimplifyPrompt(simplifyFiles, scope, "refactor");
|
|
522
|
+
const contextPath = writeSimplifyContext(`task-${task.number}`, prompt);
|
|
523
|
+
|
|
524
|
+
console.log(`\n[simplify] Prompt gerado com ${simplifyFiles.length} arquivo(s) e standards do projeto`);
|
|
525
|
+
console.log(`[simplify] Contexto: ${contextPath}`);
|
|
526
|
+
console.log(`[simplify] Invoque o simplifier agent com o prompt acima`);
|
|
527
|
+
}
|
|
528
|
+
} catch { /* nao-critico: nao falhar task done por simplify */ }
|
|
529
|
+
|
|
508
530
|
// v9.0: Atualizar patterns incrementalmente com arquivos da task
|
|
509
531
|
try {
|
|
510
532
|
if (subagentData && subagentData.status === "completed") {
|
package/db/schema.ts
CHANGED
|
@@ -496,6 +496,13 @@ const MIGRATIONS: Migration[] = [
|
|
|
496
496
|
db.exec(`ALTER TABLE context ADD COLUMN current_phase INTEGER DEFAULT 1`);
|
|
497
497
|
},
|
|
498
498
|
},
|
|
499
|
+
{
|
|
500
|
+
version: "10.1.0",
|
|
501
|
+
description: "Adicionar auto_simplify na tabela project para simplificacao automatica pos-task",
|
|
502
|
+
up: (db) => {
|
|
503
|
+
db.exec(`ALTER TABLE project ADD COLUMN auto_simplify INTEGER DEFAULT 0`);
|
|
504
|
+
},
|
|
505
|
+
},
|
|
499
506
|
];
|
|
500
507
|
|
|
501
508
|
export function runMigrations(): void {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codexa/cli",
|
|
3
|
-
"version": "9.0.
|
|
3
|
+
"version": "9.0.24",
|
|
4
4
|
"description": "Orchestrated workflow system for Claude Code - manages feature development through parallel subagents with structured phases, gates, and quality enforcement.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"gates/",
|
|
16
16
|
"protocol/",
|
|
17
17
|
"detectors/",
|
|
18
|
+
"simplify/",
|
|
18
19
|
"templates/"
|
|
19
20
|
],
|
|
20
21
|
"scripts": {
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "bun:test";
|
|
2
|
+
import { getDb } from "../db/connection";
|
|
3
|
+
import { initSchema, runMigrations } from "../db/schema";
|
|
4
|
+
import {
|
|
5
|
+
buildSimplifyPrompt,
|
|
6
|
+
getStandardsForSimplify,
|
|
7
|
+
formatStandardsBlock,
|
|
8
|
+
type SimplifyFile,
|
|
9
|
+
} from "./prompt-builder";
|
|
10
|
+
import { writeFileSync, mkdirSync, existsSync, rmSync } from "fs";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
|
|
13
|
+
// ═══════════════════════════════════════════════════════════════
|
|
14
|
+
// HELPERS
|
|
15
|
+
// ═══════════════════════════════════════════════════════════════
|
|
16
|
+
|
|
17
|
+
const TMP_DIR = join(process.cwd(), ".codexa", "test-tmp-simplify");
|
|
18
|
+
|
|
19
|
+
function ensureTmpDir() {
|
|
20
|
+
if (!existsSync(TMP_DIR)) {
|
|
21
|
+
mkdirSync(TMP_DIR, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function createTmpFile(name: string, content: string): string {
|
|
26
|
+
ensureTmpDir();
|
|
27
|
+
const path = join(TMP_DIR, name);
|
|
28
|
+
writeFileSync(path, content);
|
|
29
|
+
return path;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function insertStandard(opts: {
|
|
33
|
+
category: string;
|
|
34
|
+
scope: string;
|
|
35
|
+
rule: string;
|
|
36
|
+
enforcement?: string;
|
|
37
|
+
examples?: string;
|
|
38
|
+
anti_examples?: string;
|
|
39
|
+
semantic_query?: string | null;
|
|
40
|
+
}) {
|
|
41
|
+
const db = getDb();
|
|
42
|
+
db.run(
|
|
43
|
+
`INSERT INTO standards (category, scope, rule, examples, anti_examples, enforcement, semantic_query, expect, source, created_at)
|
|
44
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 'no_match', 'test', datetime('now'))`,
|
|
45
|
+
[
|
|
46
|
+
opts.category,
|
|
47
|
+
opts.scope,
|
|
48
|
+
opts.rule,
|
|
49
|
+
opts.examples || null,
|
|
50
|
+
opts.anti_examples || null,
|
|
51
|
+
opts.enforcement || "required",
|
|
52
|
+
opts.semantic_query || null,
|
|
53
|
+
]
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ═══════════════════════════════════════════════════════════════
|
|
58
|
+
// TESTS
|
|
59
|
+
// ═══════════════════════════════════════════════════════════════
|
|
60
|
+
|
|
61
|
+
describe("simplify prompt-builder", () => {
|
|
62
|
+
beforeEach(() => {
|
|
63
|
+
initSchema();
|
|
64
|
+
runMigrations();
|
|
65
|
+
const db = getDb();
|
|
66
|
+
db.run("DELETE FROM standards");
|
|
67
|
+
// Cleanup tmp
|
|
68
|
+
if (existsSync(TMP_DIR)) {
|
|
69
|
+
rmSync(TMP_DIR, { recursive: true, force: true });
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// ── getStandardsForSimplify ─────────────────────────────────
|
|
74
|
+
|
|
75
|
+
describe("getStandardsForSimplify", () => {
|
|
76
|
+
it("returns standards filtered by scope", () => {
|
|
77
|
+
insertStandard({ category: "code", scope: "all", rule: "Global rule" });
|
|
78
|
+
insertStandard({ category: "code", scope: "backend", rule: "Backend rule" });
|
|
79
|
+
insertStandard({ category: "code", scope: "frontend", rule: "Frontend rule" });
|
|
80
|
+
|
|
81
|
+
const backendStds = getStandardsForSimplify("backend");
|
|
82
|
+
expect(backendStds.length).toBe(2);
|
|
83
|
+
expect(backendStds.map(s => s.rule)).toContain("Global rule");
|
|
84
|
+
expect(backendStds.map(s => s.rule)).toContain("Backend rule");
|
|
85
|
+
expect(backendStds.map(s => s.rule)).not.toContain("Frontend rule");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("returns all standards when scope is 'all'", () => {
|
|
89
|
+
insertStandard({ category: "code", scope: "all", rule: "Global rule" });
|
|
90
|
+
insertStandard({ category: "code", scope: "backend", rule: "Backend rule" });
|
|
91
|
+
|
|
92
|
+
const allStds = getStandardsForSimplify("all");
|
|
93
|
+
expect(allStds.length).toBe(1);
|
|
94
|
+
expect(allStds[0].rule).toBe("Global rule");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("returns empty array when no standards exist", () => {
|
|
98
|
+
const stds = getStandardsForSimplify("backend");
|
|
99
|
+
expect(stds.length).toBe(0);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("orders required before recommended", () => {
|
|
103
|
+
insertStandard({ category: "code", scope: "all", rule: "Recommended", enforcement: "recommended" });
|
|
104
|
+
insertStandard({ category: "code", scope: "all", rule: "Required", enforcement: "required" });
|
|
105
|
+
|
|
106
|
+
const stds = getStandardsForSimplify("all");
|
|
107
|
+
expect(stds[0].rule).toBe("Required");
|
|
108
|
+
expect(stds[1].rule).toBe("Recommended");
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// ── formatStandardsBlock ────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
describe("formatStandardsBlock", () => {
|
|
115
|
+
it("formats standards with examples and anti_examples", () => {
|
|
116
|
+
const standards = [
|
|
117
|
+
{
|
|
118
|
+
id: 1,
|
|
119
|
+
category: "code",
|
|
120
|
+
scope: "all",
|
|
121
|
+
rule: "Use function declaration",
|
|
122
|
+
examples: JSON.stringify(["function foo() {}"]),
|
|
123
|
+
anti_examples: JSON.stringify(["const foo = () => {}"]),
|
|
124
|
+
enforcement: "required",
|
|
125
|
+
semantic_query: null,
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
const block = formatStandardsBlock(standards);
|
|
130
|
+
expect(block).toContain("## Standards do Projeto");
|
|
131
|
+
expect(block).toContain("### Obrigatorios (required)");
|
|
132
|
+
expect(block).toContain("[code] Use function declaration");
|
|
133
|
+
expect(block).toContain("Bom: function foo() {}");
|
|
134
|
+
expect(block).toContain("Ruim: const foo = () => {}");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("formats standards with semantic_query", () => {
|
|
138
|
+
const standards = [
|
|
139
|
+
{
|
|
140
|
+
id: 1,
|
|
141
|
+
category: "practice",
|
|
142
|
+
scope: "all",
|
|
143
|
+
rule: "No console.log in production",
|
|
144
|
+
examples: null,
|
|
145
|
+
anti_examples: null,
|
|
146
|
+
enforcement: "required",
|
|
147
|
+
semantic_query: "console.log statements in non-test files",
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
const block = formatStandardsBlock(standards);
|
|
152
|
+
expect(block).toContain('Detectar: "console.log statements in non-test files"');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("separates required and recommended sections", () => {
|
|
156
|
+
const standards = [
|
|
157
|
+
{
|
|
158
|
+
id: 1, category: "code", scope: "all", rule: "Required rule",
|
|
159
|
+
examples: null, anti_examples: null, enforcement: "required", semantic_query: null,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: 2, category: "code", scope: "all", rule: "Recommended rule",
|
|
163
|
+
examples: null, anti_examples: null, enforcement: "recommended", semantic_query: null,
|
|
164
|
+
},
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
const block = formatStandardsBlock(standards);
|
|
168
|
+
expect(block).toContain("### Obrigatorios (required)");
|
|
169
|
+
expect(block).toContain("### Recomendados (recommended)");
|
|
170
|
+
expect(block).toContain("Required rule");
|
|
171
|
+
expect(block).toContain("Recommended rule");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("returns empty string when no standards", () => {
|
|
175
|
+
expect(formatStandardsBlock([])).toBe("");
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// ── buildSimplifyPrompt ─────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
describe("buildSimplifyPrompt", () => {
|
|
182
|
+
it("builds refactor prompt with standards and files", () => {
|
|
183
|
+
insertStandard({
|
|
184
|
+
category: "code",
|
|
185
|
+
scope: "all",
|
|
186
|
+
rule: "Use function declarations",
|
|
187
|
+
examples: JSON.stringify(["function foo() {}"]),
|
|
188
|
+
anti_examples: JSON.stringify(["const foo = () => {}"]),
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const filePath = createTmpFile("test.ts", "export function hello() {}");
|
|
192
|
+
const files: SimplifyFile[] = [{ path: filePath, action: "created" }];
|
|
193
|
+
|
|
194
|
+
const prompt = buildSimplifyPrompt(files, "all", "refactor");
|
|
195
|
+
|
|
196
|
+
expect(prompt).toContain("## Standards do Projeto");
|
|
197
|
+
expect(prompt).toContain("Use function declarations");
|
|
198
|
+
expect(prompt).toContain("## Arquivos para Simplificar");
|
|
199
|
+
expect(prompt).toContain("(criado)");
|
|
200
|
+
expect(prompt).toContain("## Instrucoes");
|
|
201
|
+
expect(prompt).toContain("Preservem funcionalidade");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("builds audit prompt with analysis-only instructions", () => {
|
|
205
|
+
insertStandard({ category: "code", scope: "all", rule: "Some rule" });
|
|
206
|
+
|
|
207
|
+
const filePath = createTmpFile("test2.ts", "export const x = 1;");
|
|
208
|
+
const files: SimplifyFile[] = [{ path: filePath, action: "modified" }];
|
|
209
|
+
|
|
210
|
+
const prompt = buildSimplifyPrompt(files, "all", "audit");
|
|
211
|
+
|
|
212
|
+
expect(prompt).toContain("NAO aplique mudancas");
|
|
213
|
+
expect(prompt).toContain("reporte oportunidades");
|
|
214
|
+
expect(prompt).toContain("(modificado)");
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("handles no standards gracefully", () => {
|
|
218
|
+
const filePath = createTmpFile("test3.ts", "export const y = 2;");
|
|
219
|
+
const files: SimplifyFile[] = [{ path: filePath, action: "created" }];
|
|
220
|
+
|
|
221
|
+
const prompt = buildSimplifyPrompt(files, "all", "refactor");
|
|
222
|
+
|
|
223
|
+
// Should still have files and instructions sections
|
|
224
|
+
expect(prompt).toContain("## Arquivos para Simplificar");
|
|
225
|
+
expect(prompt).toContain("## Instrucoes");
|
|
226
|
+
// Should NOT have standards section
|
|
227
|
+
expect(prompt).not.toContain("## Standards do Projeto");
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("filters out non-existing files", () => {
|
|
231
|
+
const filePath = createTmpFile("exists.ts", "export const z = 3;");
|
|
232
|
+
const files: SimplifyFile[] = [
|
|
233
|
+
{ path: filePath, action: "created" },
|
|
234
|
+
{ path: "/nonexistent/file.ts", action: "modified" },
|
|
235
|
+
];
|
|
236
|
+
|
|
237
|
+
const prompt = buildSimplifyPrompt(files, "all", "refactor");
|
|
238
|
+
|
|
239
|
+
expect(prompt).toContain("exists.ts");
|
|
240
|
+
expect(prompt).not.toContain("nonexistent");
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("filters standards by scope", () => {
|
|
244
|
+
insertStandard({ category: "code", scope: "backend", rule: "Backend only rule" });
|
|
245
|
+
insertStandard({ category: "code", scope: "frontend", rule: "Frontend only rule" });
|
|
246
|
+
|
|
247
|
+
const filePath = createTmpFile("test4.ts", "export const a = 1;");
|
|
248
|
+
const files: SimplifyFile[] = [{ path: filePath, action: "created" }];
|
|
249
|
+
|
|
250
|
+
const prompt = buildSimplifyPrompt(files, "backend", "refactor");
|
|
251
|
+
|
|
252
|
+
expect(prompt).toContain("Backend only rule");
|
|
253
|
+
expect(prompt).not.toContain("Frontend only rule");
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
});
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2
|
+
// SIMPLIFY PROMPT BUILDER (v10.1)
|
|
3
|
+
// Builds rich simplification prompts using Codexa standards
|
|
4
|
+
// from SQLite. Generates context files for the simplifier agent.
|
|
5
|
+
// ═══════════════════════════════════════════════════════════════
|
|
6
|
+
|
|
7
|
+
import { getDb } from "../db/connection";
|
|
8
|
+
import { existsSync } from "fs";
|
|
9
|
+
import { resolve, join } from "path";
|
|
10
|
+
import { ensureContextDir } from "../context/file-writer";
|
|
11
|
+
|
|
12
|
+
// ═══════════════════════════════════════════════════════════════
|
|
13
|
+
// INTERFACES
|
|
14
|
+
// ═══════════════════════════════════════════════════════════════
|
|
15
|
+
|
|
16
|
+
interface StandardRow {
|
|
17
|
+
id: number;
|
|
18
|
+
category: string;
|
|
19
|
+
scope: string;
|
|
20
|
+
rule: string;
|
|
21
|
+
examples: string | null;
|
|
22
|
+
anti_examples: string | null;
|
|
23
|
+
enforcement: string;
|
|
24
|
+
semantic_query: string | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SimplifyFile {
|
|
28
|
+
path: string;
|
|
29
|
+
action: "created" | "modified";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type SimplifyMode = "refactor" | "audit";
|
|
33
|
+
|
|
34
|
+
// ═══════════════════════════════════════════════════════════════
|
|
35
|
+
// STANDARDS RETRIEVAL
|
|
36
|
+
// ═══════════════════════════════════════════════════════════════
|
|
37
|
+
|
|
38
|
+
export function getStandardsForSimplify(agentDomain: string): StandardRow[] {
|
|
39
|
+
const db = getDb();
|
|
40
|
+
return db
|
|
41
|
+
.query(
|
|
42
|
+
`SELECT id, category, scope, rule, examples, anti_examples, enforcement, semantic_query
|
|
43
|
+
FROM standards
|
|
44
|
+
WHERE (scope = 'all' OR scope = ?)
|
|
45
|
+
ORDER BY enforcement DESC, category`
|
|
46
|
+
)
|
|
47
|
+
.all(agentDomain) as StandardRow[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ═══════════════════════════════════════════════════════════════
|
|
51
|
+
// STANDARDS FORMATTING
|
|
52
|
+
// ═══════════════════════════════════════════════════════════════
|
|
53
|
+
|
|
54
|
+
export function formatStandardsBlock(standards: StandardRow[]): string {
|
|
55
|
+
if (standards.length === 0) return "";
|
|
56
|
+
|
|
57
|
+
const required = standards.filter(s => s.enforcement === "required");
|
|
58
|
+
const recommended = standards.filter(s => s.enforcement === "recommended");
|
|
59
|
+
|
|
60
|
+
let block = "## Standards do Projeto\n";
|
|
61
|
+
|
|
62
|
+
if (required.length > 0) {
|
|
63
|
+
block += "\n### Obrigatorios (required)\n";
|
|
64
|
+
block += formatStandardsList(required);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (recommended.length > 0) {
|
|
68
|
+
block += "\n### Recomendados (recommended)\n";
|
|
69
|
+
block += formatStandardsList(recommended);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return block;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function formatStandardsList(standards: StandardRow[]): string {
|
|
76
|
+
let result = "";
|
|
77
|
+
|
|
78
|
+
for (const std of standards) {
|
|
79
|
+
result += `\n- [${std.category}] ${std.rule}`;
|
|
80
|
+
|
|
81
|
+
// Add good examples
|
|
82
|
+
const examples = parseJsonArray(std.examples);
|
|
83
|
+
if (examples.length > 0) {
|
|
84
|
+
for (const ex of examples.slice(0, 2)) {
|
|
85
|
+
result += `\n Bom: ${ex}`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Add bad examples (anti-patterns)
|
|
90
|
+
const antiExamples = parseJsonArray(std.anti_examples);
|
|
91
|
+
if (antiExamples.length > 0) {
|
|
92
|
+
for (const anti of antiExamples.slice(0, 2)) {
|
|
93
|
+
result += `\n Ruim: ${anti}`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Add semantic detection hint
|
|
98
|
+
if (std.semantic_query) {
|
|
99
|
+
result += `\n Detectar: "${std.semantic_query}"`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return result + "\n";
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function parseJsonArray(json: string | null): string[] {
|
|
107
|
+
if (!json) return [];
|
|
108
|
+
try {
|
|
109
|
+
const parsed = JSON.parse(json);
|
|
110
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
111
|
+
} catch {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ═══════════════════════════════════════════════════════════════
|
|
117
|
+
// PROMPT BUILDER
|
|
118
|
+
// ═══════════════════════════════════════════════════════════════
|
|
119
|
+
|
|
120
|
+
export function buildSimplifyPrompt(
|
|
121
|
+
files: SimplifyFile[],
|
|
122
|
+
agentDomain: string,
|
|
123
|
+
mode: SimplifyMode
|
|
124
|
+
): string {
|
|
125
|
+
const standards = getStandardsForSimplify(agentDomain);
|
|
126
|
+
const existingFiles = files.filter(f => existsSync(f.path));
|
|
127
|
+
|
|
128
|
+
let prompt = "";
|
|
129
|
+
|
|
130
|
+
// Standards section
|
|
131
|
+
const standardsBlock = formatStandardsBlock(standards);
|
|
132
|
+
if (standardsBlock) {
|
|
133
|
+
prompt += standardsBlock + "\n";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Files section
|
|
137
|
+
prompt += "## Arquivos para Simplificar\n\n";
|
|
138
|
+
if (existingFiles.length === 0) {
|
|
139
|
+
prompt += "Nenhum arquivo encontrado.\n";
|
|
140
|
+
} else {
|
|
141
|
+
for (const file of existingFiles) {
|
|
142
|
+
prompt += `- \`${file.path}\` (${file.action === "created" ? "criado" : "modificado"})\n`;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Instructions section
|
|
147
|
+
prompt += "\n## Instrucoes\n\n";
|
|
148
|
+
|
|
149
|
+
if (mode === "refactor") {
|
|
150
|
+
prompt += `Voce e um especialista em simplificacao de codigo. Analise os arquivos listados e aplique refinamentos que:
|
|
151
|
+
|
|
152
|
+
1. **Preservem funcionalidade**: Nunca altere o que o codigo faz — apenas como faz.
|
|
153
|
+
2. **Sigam os standards acima**: Aplique todas as regras obrigatorias. Considere as recomendadas.
|
|
154
|
+
3. **Melhorem clareza**: Reduza complexidade, nesting, redundancias, abstracoes desnecessarias.
|
|
155
|
+
4. **Mantenham equilibrio**: Evite over-simplificacao. Codigo explicito > codigo compacto.
|
|
156
|
+
5. **Foquem no escopo**: Apenas refine os arquivos listados.
|
|
157
|
+
|
|
158
|
+
Apos simplificar, liste as mudancas significativas realizadas.`;
|
|
159
|
+
} else {
|
|
160
|
+
prompt += `Voce e um especialista em simplificacao de codigo. Analise os arquivos listados e reporte oportunidades de simplificacao.
|
|
161
|
+
|
|
162
|
+
**NAO aplique mudancas.** Apenas gere um relatorio com:
|
|
163
|
+
|
|
164
|
+
1. **Oportunidades por arquivo**: Liste cada oportunidade com severidade (alta/media/baixa).
|
|
165
|
+
2. **Violacoes de standards**: Identifique codigo que viola os standards obrigatorios acima.
|
|
166
|
+
3. **Over-engineering**: Identifique abstracoes desnecessarias, complexidade excessiva.
|
|
167
|
+
4. **Resumo**: Contagem de oportunidades por severidade.
|
|
168
|
+
|
|
169
|
+
Formato do relatorio:
|
|
170
|
+
\`\`\`
|
|
171
|
+
### <arquivo>
|
|
172
|
+
- [alta] <descricao da oportunidade>
|
|
173
|
+
- [media] <descricao>
|
|
174
|
+
- [baixa] <descricao>
|
|
175
|
+
|
|
176
|
+
### Resumo
|
|
177
|
+
- Alta: N oportunidades
|
|
178
|
+
- Media: N oportunidades
|
|
179
|
+
- Baixa: N oportunidades
|
|
180
|
+
\`\`\``;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return prompt;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ═══════════════════════════════════════════════════════════════
|
|
187
|
+
// CONTEXT FILE WRITER
|
|
188
|
+
// ═══════════════════════════════════════════════════════════════
|
|
189
|
+
|
|
190
|
+
export function writeSimplifyContext(
|
|
191
|
+
identifier: string,
|
|
192
|
+
content: string
|
|
193
|
+
): string {
|
|
194
|
+
const dir = ensureContextDir();
|
|
195
|
+
const filePath = join(dir, `simplify-${identifier}.md`);
|
|
196
|
+
Bun.write(filePath, content);
|
|
197
|
+
return resolve(filePath);
|
|
198
|
+
}
|
package/workflow.ts
CHANGED
|
@@ -269,6 +269,7 @@ taskCmd
|
|
|
269
269
|
.option("--files <files>", "Arquivos criados/modificados (extraido automaticamente de --output)")
|
|
270
270
|
.option("--force", "Ignorar validacao de standards (sera registrado)")
|
|
271
271
|
.option("--force-reason <reason>", "Motivo do bypass de validacao")
|
|
272
|
+
.option("--simplify", "Gerar prompt de simplificacao com standards do projeto")
|
|
272
273
|
.action(wrapAction((id: string, options) => {
|
|
273
274
|
// Resolver --output-file: ler conteudo do arquivo
|
|
274
275
|
if (options.outputFile && !options.output) {
|
|
@@ -305,6 +306,7 @@ taskCmd
|
|
|
305
306
|
force: options.force,
|
|
306
307
|
forceReason: options.forceReason,
|
|
307
308
|
output: options.output,
|
|
309
|
+
simplify: options.simplify,
|
|
308
310
|
});
|
|
309
311
|
}));
|
|
310
312
|
|
|
@@ -318,6 +320,20 @@ taskCmd
|
|
|
318
320
|
taskPhaseAdvance({ noCompact: options.compact === false, json: options.json, spec: options.spec });
|
|
319
321
|
}));
|
|
320
322
|
|
|
323
|
+
// ═══════════════════════════════════════════════════════════════
|
|
324
|
+
// SIMPLIFY (v10.1)
|
|
325
|
+
// ═══════════════════════════════════════════════════════════════
|
|
326
|
+
|
|
327
|
+
program
|
|
328
|
+
.command("simplify <taskId>")
|
|
329
|
+
.description("Gera prompt de simplificacao com standards do projeto para uma task")
|
|
330
|
+
.option("--audit", "Modo auditoria (apenas relatorio, sem aplicar mudancas)")
|
|
331
|
+
.option("--spec <id>", "ID do spec (padrao: mais recente)")
|
|
332
|
+
.action(wrapAction((taskId: string, options) => {
|
|
333
|
+
const { simplifyTask } = require("./commands/simplify");
|
|
334
|
+
simplifyTask(taskId, { audit: options.audit, specId: options.spec });
|
|
335
|
+
}));
|
|
336
|
+
|
|
321
337
|
// ═══════════════════════════════════════════════════════════════
|
|
322
338
|
// DECISOES
|
|
323
339
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -610,6 +626,8 @@ discoverCmd
|
|
|
610
626
|
.option("--testing <value>", "Testing (vitest, jest, playwright)")
|
|
611
627
|
.option("--typecheck-command <cmd>", "Comando custom de typecheck (ex: 'mypy --strict', 'go vet ./...'). Vazio para usar preset automatico.")
|
|
612
628
|
.option("--grepai-workspace <name>", "Nome do workspace grepai para busca semantica cross-project. Vazio para remover.")
|
|
629
|
+
.option("--auto-simplify", "Habilitar simplificacao automatica pos-task com standards do projeto")
|
|
630
|
+
.option("--no-auto-simplify", "Desabilitar simplificacao automatica pos-task")
|
|
613
631
|
.action((options) => {
|
|
614
632
|
// Tratar typecheck-command separadamente
|
|
615
633
|
if (options.typecheckCommand !== undefined) {
|
|
@@ -648,10 +666,16 @@ discoverCmd
|
|
|
648
666
|
// Auto-regenerate deep-explore agent with new workspace config
|
|
649
667
|
ensureDeepExploreAgent();
|
|
650
668
|
}
|
|
669
|
+
// Tratar auto-simplify
|
|
670
|
+
if (options.autoSimplify !== undefined) {
|
|
671
|
+
const { setAutoSimplify } = require("./commands/simplify");
|
|
672
|
+
setAutoSimplify(!!options.autoSimplify);
|
|
673
|
+
}
|
|
651
674
|
// Tratar stack options normalmente
|
|
652
675
|
const stackOptions = { ...options };
|
|
653
676
|
delete stackOptions.typecheckCommand;
|
|
654
677
|
delete stackOptions.grepaiWorkspace;
|
|
678
|
+
delete stackOptions.autoSimplify;
|
|
655
679
|
if (Object.keys(stackOptions).length > 0) {
|
|
656
680
|
discoverSetStack(stackOptions);
|
|
657
681
|
}
|