karajan-code 1.59.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/package.json +1 -1
- package/scripts/install.js +18 -15
- package/src/agents/aider-agent.js +0 -7
- package/src/agents/claude-agent.js +34 -6
- package/src/agents/codex-agent.js +2 -6
- package/src/agents/gemini-agent.js +0 -4
- package/src/agents/opencode-agent.js +0 -4
- package/src/{becaria → ci}/dispatch.js +13 -13
- package/src/{becaria → ci}/pr-diff.js +1 -1
- package/src/cli.js +3 -2
- package/src/commands/agents.js +0 -1
- package/src/commands/doctor.js +22 -40
- package/src/commands/init.js +8 -8
- package/src/commands/report.js +0 -13
- package/src/commands/review.js +17 -17
- package/src/commands/run.js +1 -2
- package/src/commands/status.js +1 -7
- package/src/config.js +10 -52
- package/src/domains/domain-loader.js +85 -4
- package/src/git/automation.js +9 -9
- package/src/guards/output-guard.js +0 -17
- package/src/mcp/handlers/direct-handlers.js +1 -1
- package/src/mcp/handlers/run-handler.js +1 -2
- package/src/mcp/sovereignty-guard.js +2 -2
- package/src/mcp/tools.js +3 -2
- package/src/orchestrator/brain-coordinator.js +165 -0
- package/src/orchestrator/ci-integration.js +139 -0
- package/src/orchestrator/config-init.js +435 -0
- package/src/orchestrator/direct-actions.js +186 -0
- package/src/orchestrator/feedback-enrichment.js +140 -0
- package/src/orchestrator/feedback-queue.js +152 -0
- package/src/orchestrator/flow-control.js +159 -0
- package/src/orchestrator/post-loop-stages.js +20 -15
- package/src/orchestrator/preflight-checks.js +7 -3
- package/src/orchestrator/role-output-compressor.js +190 -0
- package/src/orchestrator/session-journal.js +344 -0
- package/src/orchestrator/solomon-escalation.js +38 -5
- package/src/orchestrator/solomon-rules.js +32 -33
- package/src/orchestrator/stages/reviewer-stage.js +16 -5
- package/src/orchestrator/stages/sonar-stage.js +24 -1
- package/src/orchestrator/verification-gate.js +146 -0
- package/src/orchestrator.js +435 -661
- package/src/planning-game/client.js +2 -2
- package/src/planning-game/pipeline-adapter.js +1 -1
- package/src/prompts/coder.js +17 -3
- package/src/prompts/reviewer.js +2 -2
- package/src/prompts/rtk-snippet.js +3 -5
- package/src/review/diff-generator.js +66 -17
- package/src/review/snapshot-diff.js +122 -0
- package/src/review/tdd-policy.js +4 -2
- package/src/roles/agent-role.js +135 -0
- package/src/roles/architect-role.js +61 -108
- package/src/roles/audit-role.js +25 -71
- package/src/roles/coder-role.js +32 -57
- package/src/roles/discover-role.js +30 -71
- package/src/roles/hu-reviewer-role.js +51 -99
- package/src/roles/impeccable-role.js +34 -91
- package/src/roles/index.js +2 -0
- package/src/roles/karajan-brain-role.js +134 -0
- package/src/roles/planner-role.js +40 -75
- package/src/roles/refactorer-role.js +22 -64
- package/src/roles/researcher-role.js +52 -118
- package/src/roles/reviewer-role.js +69 -126
- package/src/roles/security-role.js +38 -102
- package/src/roles/solomon-role.js +71 -15
- package/src/roles/tester-role.js +121 -95
- package/src/roles/triage-role.js +50 -111
- package/src/session-store.js +1 -1
- package/src/skills/skill-detector.js +11 -0
- package/src/sonar/config-resolver.js +20 -1
- package/src/sonar/credentials.js +17 -0
- package/src/sonar/project-key.js +1 -1
- package/src/sonar/scanner.js +1 -1
- package/src/utils/banner.js +66 -0
- package/src/utils/display.js +35 -17
- package/src/utils/injection-guard.js +23 -8
- package/src/utils/logger.js +6 -1
- package/src/utils/process.js +3 -4
- package/src/utils/role-assigner.js +131 -0
- package/src/utils/update-check.js +5 -1
- package/src/utils/welcome.js +3 -8
- package/src/webperf/devtools-detect.js +70 -23
- package/templates/roles/coder.md +7 -0
- package/templates/roles/karajan-brain.md +95 -0
- package/templates/roles/solomon.md +67 -106
- package/templates/workflows/automerge.yml +4 -4
- package/templates/workflows/houston-override.yml +4 -4
- package/templates/workflows/{becaria-gateway.yml → kj-ci-gateway.yml} +12 -12
- package/src/proxy/adapters/anthropic.js +0 -131
- package/src/proxy/adapters/gemini.js +0 -106
- package/src/proxy/adapters/index.js +0 -51
- package/src/proxy/adapters/openai.js +0 -71
- package/src/proxy/compression/ai-compressor.js +0 -209
- package/src/proxy/compression/dedup-cache.js +0 -100
- package/src/proxy/compression/deterministic/bash-build.js +0 -60
- package/src/proxy/compression/deterministic/bash-git.js +0 -101
- package/src/proxy/compression/deterministic/bash-infra.js +0 -64
- package/src/proxy/compression/deterministic/bash-misc.js +0 -53
- package/src/proxy/compression/deterministic/bash-pkg.js +0 -49
- package/src/proxy/compression/deterministic/bash-test.js +0 -61
- package/src/proxy/compression/deterministic/glob.js +0 -26
- package/src/proxy/compression/deterministic/grep.js +0 -53
- package/src/proxy/compression/deterministic/index.js +0 -58
- package/src/proxy/compression/deterministic/read.js +0 -22
- package/src/proxy/compression/deterministic/utils.js +0 -33
- package/src/proxy/compression/pipeline.js +0 -270
- package/src/proxy/middleware/prompt-injector.js +0 -165
- package/src/proxy/middleware/response-interceptor.js +0 -223
- package/src/proxy/progress-monitor.js +0 -98
- package/src/proxy/proxy-lifecycle.js +0 -198
- package/src/proxy/proxy-server.js +0 -239
- package/src/proxy/security.js +0 -45
- /package/src/{becaria → ci}/index.js +0 -0
- /package/src/{becaria → ci}/repo.js +0 -0
|
@@ -1,122 +1,75 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createAgent as defaultCreateAgent } from "../agents/index.js";
|
|
1
|
+
import { AgentRole } from "./agent-role.js";
|
|
3
2
|
import { buildArchitectPrompt, parseArchitectOutput } from "../prompts/architect.js";
|
|
4
3
|
|
|
5
|
-
function resolveProvider(config) {
|
|
6
|
-
return (
|
|
7
|
-
config?.roles?.architect?.provider ||
|
|
8
|
-
config?.roles?.coder?.provider ||
|
|
9
|
-
"claude"
|
|
10
|
-
);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function buildSummary(parsed) {
|
|
14
|
-
const parts = [];
|
|
15
|
-
const arch = parsed.architecture;
|
|
16
|
-
|
|
17
|
-
if (arch.type) parts.push(arch.type);
|
|
18
|
-
|
|
19
|
-
const layers = arch.layers?.length || 0;
|
|
20
|
-
if (layers) parts.push(`${layers} layer${layers === 1 ? "" : "s"}`);
|
|
21
|
-
|
|
22
|
-
const patterns = arch.patterns?.length || 0;
|
|
23
|
-
if (patterns) parts.push(`${patterns} pattern${patterns === 1 ? "" : "s"}`);
|
|
24
|
-
|
|
25
|
-
const entities = arch.dataModel?.entities?.length || 0;
|
|
26
|
-
if (entities) parts.push(`${entities} entit${entities === 1 ? "y" : "ies"}`);
|
|
27
|
-
|
|
28
|
-
const questions = parsed.questions?.length || 0;
|
|
29
|
-
if (questions) parts.push(`${questions} question${questions === 1 ? "" : "s"}`);
|
|
30
|
-
|
|
31
|
-
return parts.length
|
|
32
|
-
? `Architecture complete: ${parts.join(", ")} (verdict: ${parsed.verdict})`
|
|
33
|
-
: "Architecture complete";
|
|
34
|
-
}
|
|
35
|
-
|
|
36
4
|
const EMPTY_ARCHITECTURE = {
|
|
37
|
-
type: "",
|
|
38
|
-
|
|
39
|
-
patterns: [],
|
|
40
|
-
dataModel: { entities: [] },
|
|
41
|
-
apiContracts: [],
|
|
42
|
-
dependencies: [],
|
|
43
|
-
tradeoffs: []
|
|
5
|
+
type: "", layers: [], patterns: [], dataModel: { entities: [] },
|
|
6
|
+
apiContracts: [], dependencies: [], tradeoffs: []
|
|
44
7
|
};
|
|
45
8
|
|
|
46
|
-
export class ArchitectRole extends
|
|
47
|
-
constructor(
|
|
48
|
-
super({ name: "architect"
|
|
49
|
-
|
|
9
|
+
export class ArchitectRole extends AgentRole {
|
|
10
|
+
constructor(opts) {
|
|
11
|
+
super({ ...opts, name: "architect" });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
extractInput(input) {
|
|
15
|
+
if (typeof input === "string") return { task: input, researchContext: null, onOutput: null };
|
|
16
|
+
return {
|
|
17
|
+
task: input?.task || this.context?.task || "",
|
|
18
|
+
researchContext: input?.researchContext || null,
|
|
19
|
+
onOutput: input?.onOutput || null
|
|
20
|
+
};
|
|
50
21
|
}
|
|
51
22
|
|
|
52
|
-
async
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
:
|
|
56
|
-
|
|
57
|
-
|
|
23
|
+
async buildPrompt({ task, researchContext }) {
|
|
24
|
+
const prompt = await buildArchitectPrompt({
|
|
25
|
+
task, instructions: this.instructions, researchContext,
|
|
26
|
+
productContext: this.config?.productContext || null,
|
|
27
|
+
domainContext: this.config?.domainContext || null
|
|
28
|
+
});
|
|
29
|
+
return { prompt };
|
|
30
|
+
}
|
|
58
31
|
|
|
59
|
-
|
|
60
|
-
const agent = this._createAgent(provider, this.config, this.logger);
|
|
32
|
+
parseOutput(raw) { return parseArchitectOutput(raw); }
|
|
61
33
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
34
|
+
buildSuccessResult(parsed, provider) {
|
|
35
|
+
return {
|
|
36
|
+
verdict: parsed.verdict,
|
|
37
|
+
architecture: parsed.architecture,
|
|
38
|
+
questions: parsed.questions,
|
|
39
|
+
provider
|
|
40
|
+
};
|
|
41
|
+
}
|
|
66
42
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
43
|
+
buildSummary(parsed) {
|
|
44
|
+
const parts = [];
|
|
45
|
+
const arch = parsed.architecture;
|
|
46
|
+
if (arch.type) parts.push(arch.type);
|
|
47
|
+
const layers = arch.layers?.length || 0;
|
|
48
|
+
if (layers) parts.push(`${layers} layer${layers === 1 ? "" : "s"}`);
|
|
49
|
+
const patterns = arch.patterns?.length || 0;
|
|
50
|
+
if (patterns) parts.push(`${patterns} pattern${patterns === 1 ? "" : "s"}`);
|
|
51
|
+
const entities = arch.dataModel?.entities?.length || 0;
|
|
52
|
+
if (entities) parts.push(`${entities} entit${entities === 1 ? "y" : "ies"}`);
|
|
53
|
+
const questions = parsed.questions?.length || 0;
|
|
54
|
+
if (questions) parts.push(`${questions} question${questions === 1 ? "" : "s"}`);
|
|
55
|
+
return parts.length
|
|
56
|
+
? `Architecture complete: ${parts.join(", ")} (verdict: ${parsed.verdict})`
|
|
57
|
+
: "Architecture complete";
|
|
58
|
+
}
|
|
78
59
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
},
|
|
91
|
-
summary: "Architecture complete (unstructured output)",
|
|
92
|
-
usage: result.usage
|
|
93
|
-
};
|
|
94
|
-
}
|
|
60
|
+
handleParseNull(agentResult, provider) {
|
|
61
|
+
return {
|
|
62
|
+
ok: true,
|
|
63
|
+
result: {
|
|
64
|
+
verdict: "needs_clarification", architecture: { ...EMPTY_ARCHITECTURE },
|
|
65
|
+
questions: [], raw: agentResult.output, provider
|
|
66
|
+
},
|
|
67
|
+
summary: "Architecture complete (unstructured output)",
|
|
68
|
+
usage: agentResult.usage
|
|
69
|
+
};
|
|
70
|
+
}
|
|
95
71
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
result: {
|
|
99
|
-
verdict: parsed.verdict,
|
|
100
|
-
architecture: parsed.architecture,
|
|
101
|
-
questions: parsed.questions,
|
|
102
|
-
provider
|
|
103
|
-
},
|
|
104
|
-
summary: buildSummary(parsed),
|
|
105
|
-
usage: result.usage
|
|
106
|
-
};
|
|
107
|
-
} catch { /* agent output is not structured JSON */
|
|
108
|
-
return {
|
|
109
|
-
ok: true,
|
|
110
|
-
result: {
|
|
111
|
-
verdict: "needs_clarification",
|
|
112
|
-
architecture: { ...EMPTY_ARCHITECTURE },
|
|
113
|
-
questions: [],
|
|
114
|
-
raw: result.output,
|
|
115
|
-
provider
|
|
116
|
-
},
|
|
117
|
-
summary: "Architecture complete (unstructured output)",
|
|
118
|
-
usage: result.usage
|
|
119
|
-
};
|
|
120
|
-
}
|
|
72
|
+
handleParseError(_err, agentResult, provider) {
|
|
73
|
+
return this.handleParseNull(agentResult, provider);
|
|
121
74
|
}
|
|
122
75
|
}
|
package/src/roles/audit-role.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createAgent as defaultCreateAgent } from "../agents/index.js";
|
|
1
|
+
import { AgentRole } from "./agent-role.js";
|
|
3
2
|
import { buildAuditPrompt, parseAuditOutput, AUDIT_DIMENSIONS } from "../prompts/audit.js";
|
|
4
3
|
import { measureBasalCost, loadPreviousAudit, saveAuditSnapshot, computeGrowthDelta } from "../audit/basal-cost.js";
|
|
5
4
|
|
|
6
|
-
function
|
|
7
|
-
return
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
function parseDimensions(dimensionsStr) {
|
|
6
|
+
if (!dimensionsStr || dimensionsStr === "all") return null;
|
|
7
|
+
const requested = dimensionsStr.split(",").map(d => d.trim().toLowerCase());
|
|
8
|
+
const mapped = requested.map(d => d === "quality" ? "codeQuality" : d);
|
|
9
|
+
const valid = mapped.filter(d => AUDIT_DIMENSIONS.includes(d));
|
|
10
|
+
return valid.length > 0 ? valid : null;
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
function buildSummary(parsed) {
|
|
@@ -18,45 +17,21 @@ function buildSummary(parsed) {
|
|
|
18
17
|
if (summary.high > 0) parts.push(`${summary.high} high`);
|
|
19
18
|
if (summary.medium > 0) parts.push(`${summary.medium} medium`);
|
|
20
19
|
if (summary.low > 0) parts.push(`${summary.low} low`);
|
|
21
|
-
|
|
22
20
|
const findingsStr = parts.length > 0 ? parts.join(", ") : "no issues";
|
|
23
21
|
return `Overall health: ${summary.overallHealth}. ${summary.totalFindings} findings (${findingsStr})`;
|
|
24
22
|
}
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
return {
|
|
31
|
-
task: input?.task || context?.task || "",
|
|
32
|
-
onOutput: input?.onOutput || null,
|
|
33
|
-
dimensions: input?.dimensions || null,
|
|
34
|
-
context: input?.context || null
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function parseDimensions(dimensionsStr) {
|
|
39
|
-
if (!dimensionsStr || dimensionsStr === "all") return null;
|
|
40
|
-
const requested = dimensionsStr.split(",").map(d => d.trim().toLowerCase());
|
|
41
|
-
// Map "quality" shorthand to "codeQuality"
|
|
42
|
-
const mapped = requested.map(d => d === "quality" ? "codeQuality" : d);
|
|
43
|
-
const valid = mapped.filter(d => AUDIT_DIMENSIONS.includes(d));
|
|
44
|
-
return valid.length > 0 ? valid : null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export class AuditRole extends BaseRole {
|
|
48
|
-
constructor({ config, logger, emitter = null, createAgentFn = null }) {
|
|
49
|
-
super({ name: "audit", config, logger, emitter });
|
|
50
|
-
this._createAgent = createAgentFn || defaultCreateAgent;
|
|
24
|
+
export class AuditRole extends AgentRole {
|
|
25
|
+
constructor(opts) {
|
|
26
|
+
super({ ...opts, name: "audit" });
|
|
51
27
|
}
|
|
52
28
|
|
|
53
29
|
async execute(input) {
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
const agent = this._createAgent(provider, this.config, this.logger);
|
|
30
|
+
const task = typeof input === "string" ? input : input?.task || this.context?.task || "";
|
|
31
|
+
const onOutput = typeof input === "string" ? null : input?.onOutput || null;
|
|
32
|
+
const rawDimensions = typeof input === "object" ? input?.dimensions || null : null;
|
|
33
|
+
const context = typeof input === "object" ? input?.context || null : null;
|
|
34
|
+
const dimensions = typeof rawDimensions === "string" ? parseDimensions(rawDimensions) : rawDimensions;
|
|
60
35
|
|
|
61
36
|
const projectDir = this.config?.projectDir || process.cwd();
|
|
62
37
|
let basalCost = null;
|
|
@@ -65,59 +40,38 @@ export class AuditRole extends BaseRole {
|
|
|
65
40
|
basalCost = await measureBasalCost(projectDir);
|
|
66
41
|
const previous = await loadPreviousAudit(projectDir);
|
|
67
42
|
growthDelta = computeGrowthDelta(basalCost, previous);
|
|
68
|
-
} catch {
|
|
69
|
-
// basal cost is best-effort, don't fail the audit
|
|
70
|
-
}
|
|
43
|
+
} catch { /* basal cost is best-effort */ }
|
|
71
44
|
|
|
45
|
+
const provider = this.resolveProvider();
|
|
46
|
+
const agent = this.createAgentInstance(provider);
|
|
72
47
|
const prompt = buildAuditPrompt({ task, instructions: this.instructions, dimensions, context, basalCost, growthDelta });
|
|
73
48
|
const runArgs = { prompt, role: "audit" };
|
|
74
49
|
if (onOutput) runArgs.onOutput = onOutput;
|
|
75
50
|
const result = await agent.runTask(runArgs);
|
|
76
51
|
|
|
77
52
|
if (!result.ok) {
|
|
78
|
-
return {
|
|
79
|
-
ok: false,
|
|
80
|
-
result: { error: result.error || result.output || "Audit failed", provider },
|
|
81
|
-
summary: `Audit failed: ${result.error || "unknown error"}`,
|
|
82
|
-
usage: result.usage
|
|
83
|
-
};
|
|
53
|
+
return { ok: false, result: { error: result.error || result.output || "Audit failed", provider }, summary: `Audit failed: ${result.error || "unknown error"}`, usage: result.usage };
|
|
84
54
|
}
|
|
85
55
|
|
|
86
56
|
try {
|
|
87
57
|
const parsed = parseAuditOutput(result.output);
|
|
88
58
|
if (!parsed) {
|
|
89
|
-
return {
|
|
90
|
-
ok: true,
|
|
91
|
-
result: { raw: result.output, provider },
|
|
92
|
-
summary: "Audit complete (unstructured output)",
|
|
93
|
-
usage: result.usage
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (basalCost) {
|
|
98
|
-
try { await saveAuditSnapshot(projectDir, basalCost); } catch { /* best-effort */ }
|
|
59
|
+
return { ok: true, result: { raw: result.output, provider }, summary: "Audit complete (unstructured output)", usage: result.usage };
|
|
99
60
|
}
|
|
61
|
+
if (basalCost) { try { await saveAuditSnapshot(projectDir, basalCost); } catch { /* best-effort */ } }
|
|
100
62
|
|
|
101
63
|
return {
|
|
102
64
|
ok: true,
|
|
103
65
|
result: {
|
|
104
|
-
summary: parsed.summary,
|
|
105
|
-
dimensions: parsed.dimensions,
|
|
66
|
+
summary: parsed.summary, dimensions: parsed.dimensions,
|
|
106
67
|
topRecommendations: parsed.topRecommendations,
|
|
107
|
-
basalCost: basalCost || undefined,
|
|
108
|
-
growthDelta: growthDelta || undefined,
|
|
109
|
-
provider
|
|
68
|
+
basalCost: basalCost || undefined, growthDelta: growthDelta || undefined, provider
|
|
110
69
|
},
|
|
111
70
|
summary: buildSummary(parsed),
|
|
112
71
|
usage: result.usage
|
|
113
72
|
};
|
|
114
|
-
} catch {
|
|
115
|
-
return {
|
|
116
|
-
ok: true,
|
|
117
|
-
result: { raw: result.output, provider },
|
|
118
|
-
summary: "Audit complete (unstructured output)",
|
|
119
|
-
usage: result.usage
|
|
120
|
-
};
|
|
73
|
+
} catch {
|
|
74
|
+
return { ok: true, result: { raw: result.output, provider }, summary: "Audit complete (unstructured output)", usage: result.usage };
|
|
121
75
|
}
|
|
122
76
|
}
|
|
123
77
|
}
|
package/src/roles/coder-role.js
CHANGED
|
@@ -1,78 +1,53 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createAgent as defaultCreateAgent } from "../agents/index.js";
|
|
1
|
+
import { AgentRole } from "./agent-role.js";
|
|
3
2
|
import { buildCoderPrompt } from "../prompts/coder.js";
|
|
4
3
|
import { isHostAgent } from "../utils/agent-detect.js";
|
|
5
4
|
import { HostAgent } from "../agents/host-agent.js";
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"claude"
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class CoderRole extends BaseRole {
|
|
16
|
-
constructor({ config, logger, emitter = null, createAgentFn = null, askHost = null }) {
|
|
17
|
-
super({ name: "coder", config, logger, emitter });
|
|
18
|
-
this._createAgent = createAgentFn || defaultCreateAgent;
|
|
19
|
-
this._askHost = askHost;
|
|
6
|
+
export class CoderRole extends AgentRole {
|
|
7
|
+
constructor(opts) {
|
|
8
|
+
super({ ...opts, name: "coder" });
|
|
9
|
+
this._askHost = opts.askHost || null;
|
|
20
10
|
}
|
|
21
11
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
: input || {};
|
|
26
|
-
|
|
27
|
-
const provider = resolveProvider(this.config);
|
|
28
|
-
const useHost = this._askHost && isHostAgent(provider);
|
|
29
|
-
const agent = useHost
|
|
30
|
-
? new HostAgent(this.config, this.logger, { askHost: this._askHost })
|
|
31
|
-
: this._createAgent(provider, this.config, this.logger);
|
|
12
|
+
resolveProvider() {
|
|
13
|
+
return this.config?.roles?.coder?.provider || this.config?.coder || "claude";
|
|
14
|
+
}
|
|
32
15
|
|
|
33
|
-
|
|
16
|
+
createAgentInstance(provider) {
|
|
17
|
+
if (this._askHost && isHostAgent(provider)) {
|
|
34
18
|
this.logger.info(`Host-as-coder: delegating to host AI (skipping ${provider} subprocess)`);
|
|
19
|
+
return new HostAgent(this.config, this.logger, { askHost: this._askHost });
|
|
35
20
|
}
|
|
21
|
+
return this._createAgent(provider, this.config, this.logger);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
extractInput(input) {
|
|
25
|
+
if (typeof input === "string") return { task: input, reviewerFeedback: null, sonarSummary: null, deferredContext: null, onOutput: null };
|
|
26
|
+
return {
|
|
27
|
+
task: input?.task || this.context?.task || "",
|
|
28
|
+
reviewerFeedback: input?.reviewerFeedback || null,
|
|
29
|
+
sonarSummary: input?.sonarSummary || null,
|
|
30
|
+
deferredContext: input?.deferredContext || null,
|
|
31
|
+
onOutput: input?.onOutput || null
|
|
32
|
+
};
|
|
33
|
+
}
|
|
36
34
|
|
|
35
|
+
async buildPrompt({ task, reviewerFeedback, sonarSummary, deferredContext }) {
|
|
37
36
|
const prompt = await buildCoderPrompt({
|
|
38
|
-
task
|
|
39
|
-
reviewerFeedback: reviewerFeedback || null,
|
|
40
|
-
sonarSummary: sonarSummary || null,
|
|
41
|
-
deferredContext: deferredContext || null,
|
|
37
|
+
task, reviewerFeedback, sonarSummary, deferredContext,
|
|
42
38
|
coderRules: this.instructions,
|
|
43
39
|
methodology: this.config?.development?.methodology || "tdd",
|
|
44
40
|
serenaEnabled: Boolean(this.config?.serena?.enabled),
|
|
45
41
|
rtkAvailable: Boolean(this.config?.rtk?.available),
|
|
46
|
-
proxyEnabled: Boolean(this.config?.proxy?.enabled),
|
|
47
42
|
productContext: this.config?.productContext || null,
|
|
48
43
|
domainContext: this.config?.domainContext || null
|
|
49
44
|
});
|
|
45
|
+
return { prompt };
|
|
46
|
+
}
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const result = await agent.runTask(coderArgs);
|
|
55
|
-
|
|
56
|
-
if (!result.ok) {
|
|
57
|
-
return {
|
|
58
|
-
ok: false,
|
|
59
|
-
result: {
|
|
60
|
-
...result,
|
|
61
|
-
error: result.error || result.output || "Coder failed",
|
|
62
|
-
provider
|
|
63
|
-
},
|
|
64
|
-
summary: `Coder failed: ${result.error || result.output || "unknown error"}`
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
ok: true,
|
|
70
|
-
result: {
|
|
71
|
-
...result,
|
|
72
|
-
output: result.output || "",
|
|
73
|
-
provider
|
|
74
|
-
},
|
|
75
|
-
summary: "Coder completed"
|
|
76
|
-
};
|
|
48
|
+
buildSuccessResult(parsed, provider, agentResult) {
|
|
49
|
+
return { ...agentResult, output: agentResult.output || "", provider };
|
|
77
50
|
}
|
|
51
|
+
|
|
52
|
+
buildSummary() { return "Coder completed"; }
|
|
78
53
|
}
|
|
@@ -1,23 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createAgent as defaultCreateAgent } from "../agents/index.js";
|
|
1
|
+
import { AgentRole } from "./agent-role.js";
|
|
3
2
|
import { buildDiscoverPrompt, parseDiscoverOutput } from "../prompts/discover.js";
|
|
4
3
|
|
|
5
|
-
function resolveProvider(config) {
|
|
6
|
-
return (
|
|
7
|
-
config?.roles?.discover?.provider ||
|
|
8
|
-
config?.roles?.coder?.provider ||
|
|
9
|
-
"claude"
|
|
10
|
-
);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
4
|
function pluralize(count, singular, plural) {
|
|
14
5
|
return `${count} ${count === 1 ? singular : plural}`;
|
|
15
6
|
}
|
|
16
7
|
|
|
17
|
-
|
|
8
|
+
const MODE_FIELDS = {
|
|
9
|
+
momtest: { key: "momTestQuestions", fallback: [] },
|
|
10
|
+
wendel: { key: "wendelChecklist", fallback: [] },
|
|
11
|
+
classify: { key: "classification", fallback: null },
|
|
12
|
+
jtbd: { key: "jtbds", fallback: [] }
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function buildSummary(parsed, mode) {
|
|
16
|
+
const gapCount = parsed.gaps?.length || 0;
|
|
18
17
|
const parts = [];
|
|
19
18
|
if (gapCount > 0) parts.push(`${pluralize(gapCount, "gap", "gaps")} found`);
|
|
20
|
-
|
|
21
19
|
if (mode === "momtest") {
|
|
22
20
|
const qCount = parsed.momTestQuestions?.length || 0;
|
|
23
21
|
if (qCount > 0) parts.push(`${pluralize(qCount, "Mom Test question", "Mom Test questions")}`);
|
|
@@ -33,63 +31,23 @@ function collectModeParts(parsed, mode, gapCount) {
|
|
|
33
31
|
const jCount = parsed.jtbds?.length || 0;
|
|
34
32
|
if (jCount > 0) parts.push(`${pluralize(jCount, "JTBD", "JTBDs")} generated`);
|
|
35
33
|
}
|
|
36
|
-
return parts;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function buildSummary(parsed, mode) {
|
|
40
|
-
const gapCount = parsed.gaps?.length || 0;
|
|
41
|
-
const parts = collectModeParts(parsed, mode, gapCount);
|
|
42
34
|
if (parts.length === 0) return "Discovery complete: task is ready";
|
|
43
35
|
return `Discovery complete: ${parts.join(", ")} (verdict: ${parsed.verdict})`;
|
|
44
36
|
}
|
|
45
37
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
return {
|
|
51
|
-
task: input?.task || context?.task || "",
|
|
52
|
-
onOutput: input?.onOutput || null,
|
|
53
|
-
mode: input?.mode || "gaps",
|
|
54
|
-
context: input?.context || null
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function buildUnstructuredResult(output, mode, provider, usage) {
|
|
59
|
-
return {
|
|
60
|
-
ok: true,
|
|
61
|
-
result: { verdict: "ready", gaps: [], mode, raw: output, provider },
|
|
62
|
-
summary: "Discovery complete (unstructured output)",
|
|
63
|
-
usage
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const MODE_FIELDS = {
|
|
68
|
-
momtest: { key: "momTestQuestions", fallback: [] },
|
|
69
|
-
wendel: { key: "wendelChecklist", fallback: [] },
|
|
70
|
-
classify: { key: "classification", fallback: null },
|
|
71
|
-
jtbd: { key: "jtbds", fallback: [] }
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
function buildResultFromParsed(parsed, mode, provider) {
|
|
75
|
-
const resultObj = { verdict: parsed.verdict, gaps: parsed.gaps, mode, provider };
|
|
76
|
-
const fieldDef = MODE_FIELDS[mode];
|
|
77
|
-
if (fieldDef) {
|
|
78
|
-
resultObj[fieldDef.key] = parsed[fieldDef.key] ?? fieldDef.fallback;
|
|
79
|
-
}
|
|
80
|
-
return resultObj;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export class DiscoverRole extends BaseRole {
|
|
84
|
-
constructor({ config, logger, emitter = null, createAgentFn = null }) {
|
|
85
|
-
super({ name: "discover", config, logger, emitter });
|
|
86
|
-
this._createAgent = createAgentFn || defaultCreateAgent;
|
|
38
|
+
export class DiscoverRole extends AgentRole {
|
|
39
|
+
constructor(opts) {
|
|
40
|
+
super({ ...opts, name: "discover" });
|
|
87
41
|
}
|
|
88
42
|
|
|
89
43
|
async execute(input) {
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
const
|
|
44
|
+
const task = typeof input === "string" ? input : input?.task || this.context?.task || "";
|
|
45
|
+
const onOutput = typeof input === "string" ? null : input?.onOutput || null;
|
|
46
|
+
const mode = typeof input === "object" ? input?.mode || "gaps" : "gaps";
|
|
47
|
+
const context = typeof input === "object" ? input?.context || null : null;
|
|
48
|
+
|
|
49
|
+
const provider = this.resolveProvider();
|
|
50
|
+
const agent = this.createAgentInstance(provider);
|
|
93
51
|
|
|
94
52
|
const prompt = buildDiscoverPrompt({ task, instructions: this.instructions, mode, context });
|
|
95
53
|
const runArgs = { prompt, role: "discover" };
|
|
@@ -107,16 +65,17 @@ export class DiscoverRole extends BaseRole {
|
|
|
107
65
|
|
|
108
66
|
try {
|
|
109
67
|
const parsed = parseDiscoverOutput(result.output);
|
|
110
|
-
if (!parsed)
|
|
68
|
+
if (!parsed) {
|
|
69
|
+
return { ok: true, result: { verdict: "ready", gaps: [], mode, raw: result.output, provider }, summary: "Discovery complete (unstructured output)", usage: result.usage };
|
|
70
|
+
}
|
|
111
71
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return buildUnstructuredResult(result.output, mode, provider, result.usage);
|
|
72
|
+
const resultObj = { verdict: parsed.verdict, gaps: parsed.gaps, mode, provider };
|
|
73
|
+
const fieldDef = MODE_FIELDS[mode];
|
|
74
|
+
if (fieldDef) resultObj[fieldDef.key] = parsed[fieldDef.key] ?? fieldDef.fallback;
|
|
75
|
+
|
|
76
|
+
return { ok: true, result: resultObj, summary: buildSummary(parsed, mode), usage: result.usage };
|
|
77
|
+
} catch {
|
|
78
|
+
return { ok: true, result: { verdict: "ready", gaps: [], mode, raw: result.output, provider }, summary: "Discovery complete (unstructured output)", usage: result.usage };
|
|
120
79
|
}
|
|
121
80
|
}
|
|
122
81
|
}
|