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.
Files changed (115) hide show
  1. package/README.md +2 -0
  2. package/package.json +1 -1
  3. package/scripts/install.js +18 -15
  4. package/src/agents/aider-agent.js +0 -7
  5. package/src/agents/claude-agent.js +34 -6
  6. package/src/agents/codex-agent.js +2 -6
  7. package/src/agents/gemini-agent.js +0 -4
  8. package/src/agents/opencode-agent.js +0 -4
  9. package/src/{becaria → ci}/dispatch.js +13 -13
  10. package/src/{becaria → ci}/pr-diff.js +1 -1
  11. package/src/cli.js +3 -2
  12. package/src/commands/agents.js +0 -1
  13. package/src/commands/doctor.js +22 -40
  14. package/src/commands/init.js +8 -8
  15. package/src/commands/report.js +0 -13
  16. package/src/commands/review.js +17 -17
  17. package/src/commands/run.js +1 -2
  18. package/src/commands/status.js +1 -7
  19. package/src/config.js +10 -52
  20. package/src/domains/domain-loader.js +85 -4
  21. package/src/git/automation.js +9 -9
  22. package/src/guards/output-guard.js +0 -17
  23. package/src/mcp/handlers/direct-handlers.js +1 -1
  24. package/src/mcp/handlers/run-handler.js +1 -2
  25. package/src/mcp/sovereignty-guard.js +2 -2
  26. package/src/mcp/tools.js +3 -2
  27. package/src/orchestrator/brain-coordinator.js +165 -0
  28. package/src/orchestrator/ci-integration.js +139 -0
  29. package/src/orchestrator/config-init.js +435 -0
  30. package/src/orchestrator/direct-actions.js +186 -0
  31. package/src/orchestrator/feedback-enrichment.js +140 -0
  32. package/src/orchestrator/feedback-queue.js +152 -0
  33. package/src/orchestrator/flow-control.js +159 -0
  34. package/src/orchestrator/post-loop-stages.js +20 -15
  35. package/src/orchestrator/preflight-checks.js +7 -3
  36. package/src/orchestrator/role-output-compressor.js +190 -0
  37. package/src/orchestrator/session-journal.js +344 -0
  38. package/src/orchestrator/solomon-escalation.js +38 -5
  39. package/src/orchestrator/solomon-rules.js +32 -33
  40. package/src/orchestrator/stages/reviewer-stage.js +16 -5
  41. package/src/orchestrator/stages/sonar-stage.js +24 -1
  42. package/src/orchestrator/verification-gate.js +146 -0
  43. package/src/orchestrator.js +435 -661
  44. package/src/planning-game/client.js +2 -2
  45. package/src/planning-game/pipeline-adapter.js +1 -1
  46. package/src/prompts/coder.js +17 -3
  47. package/src/prompts/reviewer.js +2 -2
  48. package/src/prompts/rtk-snippet.js +3 -5
  49. package/src/review/diff-generator.js +66 -17
  50. package/src/review/snapshot-diff.js +122 -0
  51. package/src/review/tdd-policy.js +4 -2
  52. package/src/roles/agent-role.js +135 -0
  53. package/src/roles/architect-role.js +61 -108
  54. package/src/roles/audit-role.js +25 -71
  55. package/src/roles/coder-role.js +32 -57
  56. package/src/roles/discover-role.js +30 -71
  57. package/src/roles/hu-reviewer-role.js +51 -99
  58. package/src/roles/impeccable-role.js +34 -91
  59. package/src/roles/index.js +2 -0
  60. package/src/roles/karajan-brain-role.js +134 -0
  61. package/src/roles/planner-role.js +40 -75
  62. package/src/roles/refactorer-role.js +22 -64
  63. package/src/roles/researcher-role.js +52 -118
  64. package/src/roles/reviewer-role.js +69 -126
  65. package/src/roles/security-role.js +38 -102
  66. package/src/roles/solomon-role.js +71 -15
  67. package/src/roles/tester-role.js +121 -95
  68. package/src/roles/triage-role.js +50 -111
  69. package/src/session-store.js +1 -1
  70. package/src/skills/skill-detector.js +11 -0
  71. package/src/sonar/config-resolver.js +20 -1
  72. package/src/sonar/credentials.js +17 -0
  73. package/src/sonar/project-key.js +1 -1
  74. package/src/sonar/scanner.js +1 -1
  75. package/src/utils/banner.js +66 -0
  76. package/src/utils/display.js +35 -17
  77. package/src/utils/injection-guard.js +23 -8
  78. package/src/utils/logger.js +6 -1
  79. package/src/utils/process.js +3 -4
  80. package/src/utils/role-assigner.js +131 -0
  81. package/src/utils/update-check.js +5 -1
  82. package/src/utils/welcome.js +3 -8
  83. package/src/webperf/devtools-detect.js +70 -23
  84. package/templates/roles/coder.md +7 -0
  85. package/templates/roles/karajan-brain.md +95 -0
  86. package/templates/roles/solomon.md +67 -106
  87. package/templates/workflows/automerge.yml +4 -4
  88. package/templates/workflows/houston-override.yml +4 -4
  89. package/templates/workflows/{becaria-gateway.yml → kj-ci-gateway.yml} +12 -12
  90. package/src/proxy/adapters/anthropic.js +0 -131
  91. package/src/proxy/adapters/gemini.js +0 -106
  92. package/src/proxy/adapters/index.js +0 -51
  93. package/src/proxy/adapters/openai.js +0 -71
  94. package/src/proxy/compression/ai-compressor.js +0 -209
  95. package/src/proxy/compression/dedup-cache.js +0 -100
  96. package/src/proxy/compression/deterministic/bash-build.js +0 -60
  97. package/src/proxy/compression/deterministic/bash-git.js +0 -101
  98. package/src/proxy/compression/deterministic/bash-infra.js +0 -64
  99. package/src/proxy/compression/deterministic/bash-misc.js +0 -53
  100. package/src/proxy/compression/deterministic/bash-pkg.js +0 -49
  101. package/src/proxy/compression/deterministic/bash-test.js +0 -61
  102. package/src/proxy/compression/deterministic/glob.js +0 -26
  103. package/src/proxy/compression/deterministic/grep.js +0 -53
  104. package/src/proxy/compression/deterministic/index.js +0 -58
  105. package/src/proxy/compression/deterministic/read.js +0 -22
  106. package/src/proxy/compression/deterministic/utils.js +0 -33
  107. package/src/proxy/compression/pipeline.js +0 -270
  108. package/src/proxy/middleware/prompt-injector.js +0 -165
  109. package/src/proxy/middleware/response-interceptor.js +0 -223
  110. package/src/proxy/progress-monitor.js +0 -98
  111. package/src/proxy/proxy-lifecycle.js +0 -198
  112. package/src/proxy/proxy-server.js +0 -239
  113. package/src/proxy/security.js +0 -45
  114. /package/src/{becaria → ci}/index.js +0 -0
  115. /package/src/{becaria → ci}/repo.js +0 -0
@@ -1,72 +1,30 @@
1
- import { BaseRole } from "./base-role.js";
2
- import { createAgent as defaultCreateAgent } from "../agents/index.js";
1
+ import { AgentRole } from "./agent-role.js";
3
2
 
4
- function resolveProvider(config) {
5
- return (
6
- config?.roles?.refactorer?.provider ||
7
- config?.roles?.coder?.provider ||
8
- "claude"
9
- );
10
- }
11
-
12
- function buildPrompt({ task, instructions }) {
13
- const sections = [];
14
-
15
- if (instructions) {
16
- sections.push(instructions, "");
3
+ export class RefactorerRole extends AgentRole {
4
+ constructor(opts) {
5
+ super({ ...opts, name: "refactorer" });
17
6
  }
18
7
 
19
- sections.push(
20
- "Refactor the current changes for clarity and maintainability without changing behavior.",
21
- "Do not expand scope and keep tests green.",
22
- "",
23
- "## Task context",
24
- task
25
- );
26
-
27
- return sections.join("\n");
28
- }
29
-
30
- export class RefactorerRole extends BaseRole {
31
- constructor({ config, logger, emitter = null, createAgentFn = null }) {
32
- super({ name: "refactorer", config, logger, emitter });
33
- this._createAgent = createAgentFn || defaultCreateAgent;
8
+ resolveProvider() {
9
+ return this.config?.roles?.refactorer?.provider || this.config?.roles?.coder?.provider || "claude";
34
10
  }
35
11
 
36
- async execute(input) {
37
- const task = typeof input === "string"
38
- ? input
39
- : input?.task || this.context?.task || "";
40
- const onOutput = typeof input === "string" ? null : input?.onOutput || null;
41
-
42
- const provider = resolveProvider(this.config);
43
- const agent = this._createAgent(provider, this.config, this.logger);
44
-
45
- const prompt = buildPrompt({ task, instructions: this.instructions });
46
- const runArgs = { prompt, role: "refactorer" };
47
- if (onOutput) runArgs.onOutput = onOutput;
48
- const result = await agent.runTask(runArgs);
49
-
50
- if (!result.ok) {
51
- return {
52
- ok: false,
53
- result: {
54
- ...result,
55
- error: result.error || result.output || "Refactorer failed",
56
- provider
57
- },
58
- summary: `Refactorer failed: ${result.error || "unknown error"}`
59
- };
60
- }
12
+ async buildPrompt({ task }) {
13
+ const sections = [];
14
+ if (this.instructions) sections.push(this.instructions, "");
15
+ sections.push(
16
+ "Refactor the current changes for clarity and maintainability without changing behavior.",
17
+ "Do not expand scope and keep tests green.",
18
+ "",
19
+ "## Task context",
20
+ task
21
+ );
22
+ return { prompt: sections.join("\n") };
23
+ }
61
24
 
62
- return {
63
- ok: true,
64
- result: {
65
- ...result,
66
- output: result.output?.trim() || "",
67
- provider
68
- },
69
- summary: "Refactoring applied"
70
- };
25
+ buildSuccessResult(parsed, provider, agentResult) {
26
+ return { ...agentResult, output: (agentResult.output || "").trim(), provider };
71
27
  }
28
+
29
+ buildSummary() { return "Refactoring applied"; }
72
30
  }
@@ -1,5 +1,5 @@
1
- import { BaseRole } from "./base-role.js";
2
- import { createAgent as defaultCreateAgent } from "../agents/index.js";
1
+ import { AgentRole } from "./agent-role.js";
2
+ import { extractFirstJson } from "../utils/json-extract.js";
3
3
 
4
4
  const SUBAGENT_PREAMBLE = [
5
5
  "IMPORTANT: You are running as a Karajan sub-agent.",
@@ -7,130 +7,64 @@ const SUBAGENT_PREAMBLE = [
7
7
  "Do NOT use any MCP tools. Focus only on researching the codebase."
8
8
  ].join(" ");
9
9
 
10
- function resolveProvider(config) {
11
- return (
12
- config?.roles?.researcher?.provider ||
13
- config?.roles?.coder?.provider ||
14
- "claude"
15
- );
16
- }
17
-
18
- function buildPrompt({ task, instructions }) {
19
- const sections = [SUBAGENT_PREAMBLE];
10
+ const EMPTY_RESEARCH = {
11
+ affected_files: [], patterns: [], constraints: [],
12
+ prior_decisions: [], risks: [], test_coverage: ""
13
+ };
20
14
 
21
- if (instructions) {
22
- sections.push(instructions);
15
+ export class ResearcherRole extends AgentRole {
16
+ constructor(opts) {
17
+ super({ ...opts, name: "researcher" });
23
18
  }
24
19
 
25
- sections.push(
26
- "Investigate the codebase for the following task.",
27
- "Identify affected files, patterns, constraints, prior decisions, risks, and test coverage.",
28
- "Return a single valid JSON object with your findings and nothing else.",
29
- 'JSON schema: {"affected_files":[string],"patterns":[string],"constraints":[string],"prior_decisions":[string],"risks":[string],"test_coverage":string}',
30
- `## Task\n${task}`
31
- );
32
-
33
- return sections.join("\n\n");
34
- }
35
-
36
- function parseResearchOutput(raw) {
37
- const text = raw?.trim() || "";
38
- const jsonMatch = /\{[\s\S]*\}/.exec(text);
39
- if (!jsonMatch) return null;
40
- return JSON.parse(jsonMatch[0]);
41
- }
42
-
43
- function buildSummary(parsed) {
44
- const files = parsed.affected_files?.length || 0;
45
- const risks = parsed.risks?.length || 0;
46
- const patterns = parsed.patterns?.length || 0;
47
- const parts = [];
48
- if (files) parts.push(`${files} file${files === 1 ? "" : "s"}`);
49
- if (risks) parts.push(`${risks} risk${risks === 1 ? "" : "s"}`);
50
- if (patterns) parts.push(`${patterns} pattern${patterns === 1 ? "" : "s"}`);
51
- return parts.length
52
- ? `Research complete: ${parts.join(", ")} identified`
53
- : "Research complete";
54
- }
55
-
56
- export class ResearcherRole extends BaseRole {
57
- constructor({ config, logger, emitter = null, createAgentFn = null }) {
58
- super({ name: "researcher", config, logger, emitter });
59
- this._createAgent = createAgentFn || defaultCreateAgent;
20
+ async buildPrompt({ task }) {
21
+ const sections = [SUBAGENT_PREAMBLE];
22
+ if (this.instructions) sections.push(this.instructions);
23
+ sections.push(
24
+ "Investigate the codebase for the following task.",
25
+ "Identify affected files, patterns, constraints, prior decisions, risks, and test coverage.",
26
+ "Return a single valid JSON object with your findings and nothing else.",
27
+ '{"affected_files":[string],"patterns":[string],"constraints":[string],"prior_decisions":[string],"risks":[string],"test_coverage":string}',
28
+ `## Task\n${task}`
29
+ );
30
+ return { prompt: sections.join("\n\n") };
60
31
  }
61
32
 
62
- async execute(input) {
63
- const task = typeof input === "string"
64
- ? input
65
- : input?.task || this.context?.task || "";
66
- const onOutput = typeof input === "string" ? null : input?.onOutput || null;
67
-
68
- const provider = resolveProvider(this.config);
69
- const agent = this._createAgent(provider, this.config, this.logger);
33
+ parseOutput(raw) { return extractFirstJson(raw); }
70
34
 
71
- const prompt = buildPrompt({ task, instructions: this.instructions });
72
- const runArgs = { prompt, role: "researcher" };
73
- if (onOutput) runArgs.onOutput = onOutput;
74
- const result = await agent.runTask(runArgs);
35
+ buildSuccessResult(parsed, provider) {
36
+ return {
37
+ affected_files: parsed.affected_files || [],
38
+ patterns: parsed.patterns || [],
39
+ constraints: parsed.constraints || [],
40
+ prior_decisions: parsed.prior_decisions || [],
41
+ risks: parsed.risks || [],
42
+ test_coverage: parsed.test_coverage || "",
43
+ provider
44
+ };
45
+ }
75
46
 
76
- if (!result.ok) {
77
- return {
78
- ok: false,
79
- result: {
80
- error: result.error || result.output || "Researcher failed",
81
- provider
82
- },
83
- summary: `Researcher failed: ${result.error || "unknown error"}`
84
- };
85
- }
47
+ buildSummary(parsed) {
48
+ const files = parsed.affected_files?.length || 0;
49
+ const risks = parsed.risks?.length || 0;
50
+ const patterns = parsed.patterns?.length || 0;
51
+ const parts = [];
52
+ if (files) parts.push(`${files} file${files === 1 ? "" : "s"}`);
53
+ if (risks) parts.push(`${risks} risk${risks === 1 ? "" : "s"}`);
54
+ if (patterns) parts.push(`${patterns} pattern${patterns === 1 ? "" : "s"}`);
55
+ return parts.length ? `Research complete: ${parts.join(", ")} identified` : "Research complete";
56
+ }
86
57
 
87
- try {
88
- const parsed = parseResearchOutput(result.output);
89
- if (!parsed) {
90
- return {
91
- ok: true,
92
- result: {
93
- affected_files: [],
94
- patterns: [],
95
- constraints: [],
96
- prior_decisions: [],
97
- risks: [],
98
- test_coverage: "",
99
- raw: result.output,
100
- provider
101
- },
102
- summary: "Research complete (unstructured output)"
103
- };
104
- }
58
+ handleParseNull(agentResult, provider) {
59
+ return {
60
+ ok: true,
61
+ result: { ...EMPTY_RESEARCH, raw: agentResult.output, provider },
62
+ summary: "Research complete (unstructured output)",
63
+ usage: agentResult.usage
64
+ };
65
+ }
105
66
 
106
- return {
107
- ok: true,
108
- result: {
109
- affected_files: parsed.affected_files || [],
110
- patterns: parsed.patterns || [],
111
- constraints: parsed.constraints || [],
112
- prior_decisions: parsed.prior_decisions || [],
113
- risks: parsed.risks || [],
114
- test_coverage: parsed.test_coverage || "",
115
- provider
116
- },
117
- summary: buildSummary(parsed)
118
- };
119
- } catch { /* agent output is not structured JSON */
120
- return {
121
- ok: true,
122
- result: {
123
- affected_files: [],
124
- patterns: [],
125
- constraints: [],
126
- prior_decisions: [],
127
- risks: [],
128
- test_coverage: "",
129
- raw: result.output,
130
- provider
131
- },
132
- summary: "Research complete (unstructured output)"
133
- };
134
- }
67
+ handleParseError(_err, agentResult, provider) {
68
+ return this.handleParseNull(agentResult, provider);
135
69
  }
136
70
  }
@@ -1,6 +1,6 @@
1
- import { BaseRole } from "./base-role.js";
2
- import { createAgent as defaultCreateAgent } from "../agents/index.js";
1
+ import { AgentRole } from "./agent-role.js";
3
2
  import { buildRtkInstructions } from "../prompts/rtk-snippet.js";
3
+ import { extractFirstJson } from "../utils/json-extract.js";
4
4
 
5
5
  const MAX_DIFF_LENGTH = 12000;
6
6
 
@@ -10,147 +10,90 @@ const SUBAGENT_PREAMBLE = [
10
10
  "Do NOT use any MCP tools. Focus only on reviewing the code."
11
11
  ].join(" ");
12
12
 
13
- function resolveProvider(config) {
14
- return (
15
- config?.roles?.reviewer?.provider ||
16
- config?.roles?.coder?.provider ||
17
- "claude"
18
- );
19
- }
20
-
21
13
  function truncateDiff(diff) {
22
14
  if (!diff) return "";
23
- return diff.length > MAX_DIFF_LENGTH
24
- ? `${diff.slice(0, MAX_DIFF_LENGTH)}\n\n[TRUNCATED]`
25
- : diff;
15
+ return diff.length > MAX_DIFF_LENGTH ? `${diff.slice(0, MAX_DIFF_LENGTH)}\n\n[TRUNCATED]` : diff;
26
16
  }
27
17
 
28
- function buildPrompt({ task, diff, reviewRules, reviewMode, instructions, rtkAvailable = false, proxyEnabled = false, productContext = null, domainContext = null }) {
29
- const sections = [];
30
-
31
- sections.push(SUBAGENT_PREAMBLE);
32
-
33
- if (instructions) {
34
- sections.push(instructions);
18
+ export class ReviewerRole extends AgentRole {
19
+ constructor(opts) {
20
+ super({ ...opts, name: "reviewer" });
35
21
  }
36
22
 
37
- sections.push(
38
- `You are a code reviewer in ${reviewMode || "standard"} mode.`,
39
- "Return only one valid JSON object and nothing else.",
40
- 'JSON schema:',
41
- '{"approved":boolean,"blocking_issues":[{"id":string,"severity":"critical|high|medium|low","file":string,"line":number,"description":string,"suggested_fix":string}],"non_blocking_suggestions":[string],"summary":string,"confidence":number}',
42
- `Task context:\n${task}`
43
- );
23
+ get agentMethod() { return "reviewTask"; }
44
24
 
45
- if (productContext) {
46
- sections.push(`## Product Context\n${productContext}`);
25
+ extractInput(input) {
26
+ if (typeof input === "string") return { task: input, diff: "", reviewRules: null, onOutput: null };
27
+ return {
28
+ task: input?.task || this.context?.task || "",
29
+ diff: input?.diff || "",
30
+ reviewRules: input?.reviewRules || null,
31
+ onOutput: input?.onOutput || null
32
+ };
47
33
  }
48
34
 
49
- if (domainContext) {
50
- sections.push(`## Domain Context\n${domainContext}`);
51
- }
35
+ async buildPrompt({ task, diff, reviewRules }) {
36
+ const sections = [SUBAGENT_PREAMBLE];
37
+ if (this.instructions) sections.push(this.instructions);
38
+ sections.push(
39
+ `You are a code reviewer in ${this.config?.review_mode || "standard"} mode.`,
40
+ "Return only one valid JSON object and nothing else.",
41
+ "JSON schema:",
42
+ '{"approved":boolean,"blocking_issues":[{"id":string,"severity":"critical|high|medium|low","file":string,"line":number,"description":string,"suggested_fix":string}],"non_blocking_suggestions":[string],"summary":string,"confidence":number}',
43
+ `Task context:\n${task}`
44
+ );
45
+
46
+ const pc = this.config?.productContext;
47
+ if (pc) sections.push(`## Product Context\n${pc}`);
48
+ const dc = this.config?.domainContext;
49
+ if (dc) sections.push(`## Domain Context\n${dc}`);
50
+
51
+ const rtkSnippet = buildRtkInstructions({
52
+ rtkAvailable: Boolean(this.config?.rtk?.available)
53
+ });
54
+ if (rtkSnippet) sections.push(rtkSnippet);
55
+ if (reviewRules) sections.push(`Review rules:\n${reviewRules}`);
56
+ sections.push(`Git diff:\n${truncateDiff(diff)}`);
52
57
 
53
- const rtkSnippet = buildRtkInstructions({ rtkAvailable, proxyEnabled });
54
- if (rtkSnippet) {
55
- sections.push(rtkSnippet);
58
+ return { prompt: sections.join("\n\n") };
56
59
  }
57
60
 
58
- if (reviewRules) {
59
- sections.push(`Review rules:\n${reviewRules}`);
61
+ parseOutput(raw) {
62
+ const parsed = extractFirstJson(raw);
63
+ if (!parsed) throw new Error("Failed to parse reviewer output: no JSON found");
64
+ return parsed;
60
65
  }
61
66
 
62
- sections.push(`Git diff:\n${truncateDiff(diff)}`);
63
-
64
- return sections.join("\n\n");
65
- }
66
-
67
- function parseReviewOutput(raw) {
68
- const text = raw?.trim() || "";
69
- const jsonMatch = /\{[\s\S]*\}/.exec(text);
70
- if (!jsonMatch) {
71
- throw new Error(`Failed to parse reviewer output: no JSON found`);
67
+ buildSuccessResult(parsed, provider, agentResult) {
68
+ return {
69
+ ...agentResult,
70
+ approved: parsed.approved,
71
+ blocking_issues: parsed.blocking_issues || [],
72
+ non_blocking_suggestions: parsed.non_blocking_suggestions || [],
73
+ confidence: parsed.confidence ?? null,
74
+ raw_summary: parsed.summary || ""
75
+ };
72
76
  }
73
- return JSON.parse(jsonMatch[0]);
74
- }
75
77
 
76
- export class ReviewerRole extends BaseRole {
77
- constructor({ config, logger, emitter = null, createAgentFn = null }) {
78
- super({ name: "reviewer", config, logger, emitter });
79
- this._createAgent = createAgentFn || defaultCreateAgent;
78
+ buildSummary(parsed) {
79
+ const blockingIssues = parsed.blocking_issues || [];
80
+ return parsed.approved
81
+ ? `Approved: ${parsed.summary || "no issues found"}`
82
+ : `Rejected: ${blockingIssues.length} blocking issue(s) — ${parsed.summary || ""}`;
80
83
  }
81
84
 
82
- async execute(input) {
83
- const { task, diff, reviewRules, onOutput } = typeof input === "string"
84
- ? { task: input, diff: "", reviewRules: null, onOutput: null }
85
- : input;
86
-
87
- const provider = resolveProvider(this.config);
88
- const agent = this._createAgent(provider, this.config, this.logger);
89
-
90
- const prompt = buildPrompt({
91
- task: task || this.context?.task || "",
92
- diff: diff || "",
93
- reviewRules: reviewRules || null,
94
- reviewMode: this.config?.review_mode || "standard",
95
- instructions: this.instructions,
96
- rtkAvailable: Boolean(this.config?.rtk?.available),
97
- proxyEnabled: Boolean(this.config?.proxy?.enabled),
98
- productContext: this.config?.productContext || null,
99
- domainContext: this.config?.domainContext || null
100
- });
101
-
102
- const reviewArgs = { prompt, role: "reviewer" };
103
- if (onOutput) reviewArgs.onOutput = onOutput;
104
-
105
- const result = await agent.reviewTask(reviewArgs);
106
-
107
- if (!result.ok) {
108
- return {
109
- ok: false,
110
- result: {
111
- error: result.error || result.output || "Reviewer agent failed",
112
- approved: false,
113
- blocking_issues: []
114
- },
115
- summary: `Reviewer failed: ${result.error || "unknown error"}`
116
- };
117
- }
118
-
119
- try {
120
- const parsed = parseReviewOutput(result.output);
121
- const blockingIssues = parsed.blocking_issues || [];
122
-
123
- return {
124
- ok: true,
125
- result: {
126
- ...result,
127
- approved: parsed.approved,
128
- blocking_issues: blockingIssues,
129
- non_blocking_suggestions: parsed.non_blocking_suggestions || [],
130
- confidence: parsed.confidence ?? null,
131
- raw_summary: parsed.summary || ""
132
- },
133
- summary: parsed.approved
134
- ? `Approved: ${parsed.summary || "no issues found"}`
135
- : `Rejected: ${blockingIssues.length} blocking issue(s) — ${parsed.summary || ""}`
136
- };
137
- } catch (err) {
138
- return {
139
- ok: true,
140
- result: {
141
- ...result,
142
- approved: false,
143
- blocking_issues: [{
144
- id: "PARSE_ERROR",
145
- severity: "high",
146
- description: `Reviewer output could not be parsed: ${err.message}`
147
- }],
148
- non_blocking_suggestions: [],
149
- confidence: 0,
150
- raw_summary: `Parse error: ${err.message}`
151
- },
152
- summary: `Reviewer output parse error: ${err.message}`
153
- };
154
- }
85
+ handleParseError(err, agentResult, _provider) {
86
+ return {
87
+ ok: true,
88
+ result: {
89
+ ...agentResult,
90
+ approved: false,
91
+ blocking_issues: [{ id: "PARSE_ERROR", severity: "high", description: `Reviewer output could not be parsed: ${err.message}` }],
92
+ non_blocking_suggestions: [],
93
+ confidence: 0,
94
+ raw_summary: `Parse error: ${err.message}`
95
+ },
96
+ summary: `Reviewer output parse error: ${err.message}`
97
+ };
155
98
  }
156
99
  }