aiwcli 0.12.0 → 0.12.2
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/dist/lib/template-installer.js +3 -3
- package/dist/lib/version.js +2 -2
- package/dist/templates/_shared/hooks-ts/session_end.ts +75 -4
- package/dist/templates/_shared/hooks-ts/session_start.ts +10 -1
- package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +12 -0
- package/dist/templates/_shared/lib-ts/base/hook-utils.ts +45 -29
- package/dist/templates/_shared/lib-ts/base/logger.ts +1 -1
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +1 -1
- package/dist/templates/_shared/lib-ts/context/context-formatter.ts +151 -29
- package/dist/templates/_shared/lib-ts/context/plan-manager.ts +14 -13
- package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +3 -2
- package/dist/templates/_shared/scripts/resume_handoff.ts +29 -4
- package/dist/templates/_shared/scripts/save_handoff.ts +7 -7
- package/dist/templates/_shared/scripts/status_line.ts +103 -70
- package/dist/templates/cc-native/.claude/settings.json +11 -12
- package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +1 -7
- package/dist/templates/cc-native/_cc-native/agents/plan-review/ARCH-EVOLUTION.md +62 -63
- package/dist/templates/cc-native/_cc-native/agents/plan-review/ARCH-PATTERNS.md +61 -62
- package/dist/templates/cc-native/_cc-native/agents/plan-review/ARCH-STRUCTURE.md +62 -63
- package/dist/templates/cc-native/_cc-native/agents/plan-review/ASSUMPTION-TRACER.md +56 -57
- package/dist/templates/cc-native/_cc-native/agents/plan-review/CLARITY-AUDITOR.md +53 -54
- package/dist/templates/cc-native/_cc-native/agents/plan-review/COMPLETENESS-FEASIBILITY.md +66 -67
- package/dist/templates/cc-native/_cc-native/agents/plan-review/COMPLETENESS-GAPS.md +70 -71
- package/dist/templates/cc-native/_cc-native/agents/plan-review/COMPLETENESS-ORDERING.md +62 -63
- package/dist/templates/cc-native/_cc-native/agents/plan-review/CONSTRAINT-VALIDATOR.md +72 -73
- package/dist/templates/cc-native/_cc-native/agents/plan-review/DESIGN-ADR-VALIDATOR.md +61 -62
- package/dist/templates/cc-native/_cc-native/agents/plan-review/DESIGN-SCALE-MATCHER.md +64 -65
- package/dist/templates/cc-native/_cc-native/agents/plan-review/DEVILS-ADVOCATE.md +56 -57
- package/dist/templates/cc-native/_cc-native/agents/plan-review/DOCUMENTATION-PHILOSOPHY.md +86 -87
- package/dist/templates/cc-native/_cc-native/agents/plan-review/HANDOFF-READINESS.md +59 -60
- package/dist/templates/cc-native/_cc-native/agents/plan-review/HIDDEN-COMPLEXITY.md +58 -59
- package/dist/templates/cc-native/_cc-native/agents/plan-review/INCREMENTAL-DELIVERY.md +66 -67
- package/dist/templates/cc-native/_cc-native/agents/plan-review/RISK-DEPENDENCY.md +62 -63
- package/dist/templates/cc-native/_cc-native/agents/plan-review/RISK-FMEA.md +66 -67
- package/dist/templates/cc-native/_cc-native/agents/plan-review/RISK-PREMORTEM.md +71 -72
- package/dist/templates/cc-native/_cc-native/agents/plan-review/RISK-REVERSIBILITY.md +74 -75
- package/dist/templates/cc-native/_cc-native/agents/plan-review/SCOPE-BOUNDARY.md +77 -78
- package/dist/templates/cc-native/_cc-native/agents/plan-review/SIMPLICITY-GUARDIAN.md +62 -63
- package/dist/templates/cc-native/_cc-native/agents/plan-review/SKEPTIC.md +68 -69
- package/dist/templates/cc-native/_cc-native/agents/plan-review/TESTDRIVEN-BEHAVIOR-AUDITOR.md +61 -62
- package/dist/templates/cc-native/_cc-native/agents/plan-review/TESTDRIVEN-CHARACTERIZATION.md +71 -72
- package/dist/templates/cc-native/_cc-native/agents/plan-review/TESTDRIVEN-FIRST-VALIDATOR.md +61 -62
- package/dist/templates/cc-native/_cc-native/agents/plan-review/TESTDRIVEN-PYRAMID-ANALYZER.md +61 -62
- package/dist/templates/cc-native/_cc-native/agents/plan-review/TRADEOFF-COSTS.md +67 -68
- package/dist/templates/cc-native/_cc-native/agents/plan-review/TRADEOFF-STAKEHOLDERS.md +65 -66
- package/dist/templates/cc-native/_cc-native/agents/plan-review/VERIFY-COVERAGE.md +74 -75
- package/dist/templates/cc-native/_cc-native/agents/plan-review/VERIFY-STRENGTH.md +69 -70
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +19 -2
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +28 -1013
- package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_subagent.ts +24 -8
- package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_write.ts +3 -2
- package/dist/templates/cc-native/_cc-native/hooks/mark_questions_asked.ts +5 -5
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +4 -4
- package/dist/templates/cc-native/_cc-native/lib-ts/agent-selection.ts +163 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +5 -5
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts/format.ts +597 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts/index.ts +26 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts/tracker.ts +107 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts/write.ts +119 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +19 -820
- package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +77 -5
- package/dist/templates/cc-native/_cc-native/lib-ts/graduation.ts +132 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +7 -8
- package/dist/templates/cc-native/_cc-native/lib-ts/output-builder.ts +130 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/plan-discovery.ts +80 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/plan-questions.ts +3 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/review-pipeline.ts +489 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +14 -11
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/base/base-agent.ts +108 -108
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +2 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/claude-agent.ts +18 -18
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/codex-agent.ts +75 -74
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/gemini-agent.ts +8 -8
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/orchestrator-claude-agent.ts +34 -34
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +4 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/settings.ts +184 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +35 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +48 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +3 -3
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
|
@@ -26,10 +26,10 @@ export interface ExecResult {
|
|
|
26
26
|
*/
|
|
27
27
|
export abstract class BaseCliAgent<T> {
|
|
28
28
|
protected agent: AgentConfig;
|
|
29
|
-
protected schema: Record<string, unknown>;
|
|
30
|
-
protected timeout: number;
|
|
31
29
|
protected contextPath?: string;
|
|
30
|
+
protected schema: Record<string, unknown>;
|
|
32
31
|
protected sessionName: string;
|
|
32
|
+
protected timeout: number;
|
|
33
33
|
|
|
34
34
|
constructor(
|
|
35
35
|
agent: AgentConfig,
|
|
@@ -45,90 +45,22 @@ export abstract class BaseCliAgent<T> {
|
|
|
45
45
|
this.sessionName = sessionName;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
/**
|
|
49
|
-
|
|
50
|
-
* Subclasses override abstract methods to customize behavior.
|
|
51
|
-
*/
|
|
52
|
-
async review(plan: string): Promise<T> {
|
|
53
|
-
// 1. Find CLI executable
|
|
54
|
-
const cliPath = this.findCli();
|
|
55
|
-
if (!cliPath) {
|
|
56
|
-
return this.makeSkipResult(`${this.getCliName()} CLI not found on PATH`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
logDebug(this.agent.name, `Found ${this.getCliName()} CLI at: ${cliPath}`);
|
|
60
|
-
|
|
61
|
-
// 2. Build prompt and args (provider-specific)
|
|
62
|
-
const prompt = this.buildPrompt(plan);
|
|
63
|
-
const args = this.buildCliArgs();
|
|
64
|
-
|
|
65
|
-
logInfo(this.agent.name, `Running ${this.getCliName()} with model: ${this.agent.model}, timeout: ${this.timeout}s`);
|
|
66
|
-
|
|
67
|
-
// 3. Execute subprocess
|
|
68
|
-
const env = getInternalSubprocessEnv();
|
|
69
|
-
const result = await execFileAsync(cliPath, args, {
|
|
70
|
-
input: prompt,
|
|
71
|
-
timeout: this.timeout * 1000,
|
|
72
|
-
env: env as Record<string, string>,
|
|
73
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
74
|
-
shell: process.platform === "win32",
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// 4. Handle timeout
|
|
78
|
-
if (result.killed || result.signal === "SIGTERM") {
|
|
79
|
-
return this.handleTimeout();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// 5. Extract output (provider-specific)
|
|
83
|
-
const { raw, err } = this.extractOutput(result);
|
|
84
|
-
|
|
85
|
-
// 6. Handle exit errors
|
|
86
|
-
if (!raw && !err && result.exitCode !== 0) {
|
|
87
|
-
return this.handleExitError(result);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// 7. Log subprocess results
|
|
91
|
-
this.logSubprocessResult(result, raw, err);
|
|
92
|
-
|
|
93
|
-
// 8. Parse JSON output (provider-specific)
|
|
94
|
-
const obj = this.parseOutput(raw, result);
|
|
95
|
-
|
|
96
|
-
// 9. Log parsed result
|
|
97
|
-
this.logParsedResult(obj);
|
|
98
|
-
|
|
99
|
-
// 10. Coerce to result type T (provider-specific)
|
|
100
|
-
const coerced = this.coerceResult(obj, raw, err);
|
|
101
|
-
|
|
102
|
-
// 11. Cleanup (optional override)
|
|
103
|
-
await this.cleanup();
|
|
104
|
-
|
|
105
|
-
return coerced;
|
|
106
|
-
}
|
|
48
|
+
/** Build the command-line arguments for the CLI */
|
|
49
|
+
protected abstract buildCliArgs(): string[];
|
|
107
50
|
|
|
108
51
|
// ─── Abstract Methods (Subclass Implements) ────────────────────────────
|
|
109
52
|
|
|
110
|
-
/** Get the CLI executable name (e.g., "claude", "codex") */
|
|
111
|
-
protected abstract getCliName(): string;
|
|
112
|
-
|
|
113
53
|
/** Build the stdin prompt for the CLI */
|
|
114
54
|
protected abstract buildPrompt(plan: string): string;
|
|
115
55
|
|
|
116
|
-
/**
|
|
117
|
-
protected
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
protected abstract parseOutput(raw: string, result: ExecResult): Record<string, unknown> | null;
|
|
56
|
+
/** Optional cleanup after subprocess execution */
|
|
57
|
+
protected async cleanup(): Promise<void> {
|
|
58
|
+
// Default: no-op. Subclasses override if needed (e.g., Codex temp files).
|
|
59
|
+
}
|
|
121
60
|
|
|
122
61
|
/** Coerce parsed JSON into the result type T */
|
|
123
62
|
protected abstract coerceResult(obj: Record<string, unknown> | null, raw: string, err: string): T;
|
|
124
63
|
|
|
125
|
-
// ─── Template Methods (Subclass Can Override) ──────────────────────────
|
|
126
|
-
|
|
127
|
-
/** Find the CLI executable. Override for custom search logic. */
|
|
128
|
-
protected findCli(): string | null {
|
|
129
|
-
return findExecutable(this.getCliName());
|
|
130
|
-
}
|
|
131
|
-
|
|
132
64
|
/** Extract stdout/stderr from subprocess result. Override for file-based output (Codex). */
|
|
133
65
|
protected extractOutput(result: ExecResult): { raw: string; err: string } {
|
|
134
66
|
return {
|
|
@@ -137,22 +69,26 @@ export abstract class BaseCliAgent<T> {
|
|
|
137
69
|
};
|
|
138
70
|
}
|
|
139
71
|
|
|
72
|
+
/** Find the CLI executable. Override for custom search logic. */
|
|
73
|
+
protected findCli(): string | null {
|
|
74
|
+
return findExecutable(this.getCliName());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ─── Template Methods (Subclass Can Override) ──────────────────────────
|
|
78
|
+
|
|
79
|
+
/** Get the CLI executable name (e.g., "claude", "codex") */
|
|
80
|
+
protected abstract getCliName(): string;
|
|
81
|
+
|
|
140
82
|
/** Get default error message for coerceToReview */
|
|
141
83
|
protected getDefaultErrorMessage(): string {
|
|
142
84
|
return `Retry or check ${this.getCliName()} configuration.`;
|
|
143
85
|
}
|
|
144
86
|
|
|
145
|
-
/**
|
|
146
|
-
protected
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
// ─── Shared Infrastructure ──────────────────────────────────────────────
|
|
151
|
-
|
|
152
|
-
/** Create skip result when CLI not found */
|
|
153
|
-
protected makeSkipResult(reason: string): T {
|
|
154
|
-
logWarn(this.agent.name, reason);
|
|
155
|
-
return this.makeErrorResult("skip", reason);
|
|
87
|
+
/** Handle non-zero exit with no output */
|
|
88
|
+
protected handleExitError(result: ExecResult): T {
|
|
89
|
+
const msg = `${this.agent.name} failed to run (exit ${result.exitCode})`;
|
|
90
|
+
logError(this.agent.name, `Process exited with code ${result.exitCode} and no output`);
|
|
91
|
+
return this.makeErrorResult("error", msg);
|
|
156
92
|
}
|
|
157
93
|
|
|
158
94
|
/** Handle timeout scenario */
|
|
@@ -162,15 +98,25 @@ export abstract class BaseCliAgent<T> {
|
|
|
162
98
|
return this.makeErrorResult("error", `${this.agent.name} timed out after ${this.timeout}s`);
|
|
163
99
|
}
|
|
164
100
|
|
|
165
|
-
|
|
166
|
-
protected handleExitError(result: ExecResult): T {
|
|
167
|
-
const msg = `${this.agent.name} failed to run (exit ${result.exitCode})`;
|
|
168
|
-
logError(this.agent.name, `Process exited with code ${result.exitCode} and no output`);
|
|
169
|
-
return this.makeErrorResult("error", msg);
|
|
170
|
-
}
|
|
101
|
+
// ─── Shared Infrastructure ──────────────────────────────────────────────
|
|
171
102
|
|
|
172
|
-
/**
|
|
173
|
-
protected
|
|
103
|
+
/** Log parsed JSON result */
|
|
104
|
+
protected logParsedResult(obj: Record<string, unknown> | null): void {
|
|
105
|
+
if (this.contextPath && obj) {
|
|
106
|
+
debugLog(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "parsed_result", {
|
|
107
|
+
parsed_keys: Object.keys(obj),
|
|
108
|
+
verdict: obj.verdict ?? null,
|
|
109
|
+
has_summary: Boolean(obj.summary),
|
|
110
|
+
issues_count: Array.isArray(obj.issues) ? (obj.issues as unknown[]).length : 0,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (obj) {
|
|
115
|
+
logInfo(this.agent.name, `Parsed JSON successfully, verdict: ${obj.verdict ?? "N/A"}`);
|
|
116
|
+
} else {
|
|
117
|
+
logWarn(this.agent.name, "Failed to parse JSON from output");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
174
120
|
|
|
175
121
|
/** Log subprocess execution results */
|
|
176
122
|
protected logSubprocessResult(result: ExecResult, raw: string, err: string): void {
|
|
@@ -197,21 +143,75 @@ export abstract class BaseCliAgent<T> {
|
|
|
197
143
|
if (raw) logDebug(this.agent.name, `stdout preview: ${raw.slice(0, 500)}`);
|
|
198
144
|
}
|
|
199
145
|
|
|
200
|
-
/**
|
|
201
|
-
protected
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
146
|
+
/** Construct a T for error/skip/timeout scenarios. Subclasses define shape. */
|
|
147
|
+
protected abstract makeErrorResult(type: "skip" | "error", message: string): T;
|
|
148
|
+
|
|
149
|
+
/** Create skip result when CLI not found */
|
|
150
|
+
protected makeSkipResult(reason: string): T {
|
|
151
|
+
logWarn(this.agent.name, reason);
|
|
152
|
+
return this.makeErrorResult("skip", reason);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Parse JSON from CLI output */
|
|
156
|
+
protected abstract parseOutput(raw: string, result: ExecResult): Record<string, unknown> | null;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Template method - orchestrates the review flow.
|
|
160
|
+
* Subclasses override abstract methods to customize behavior.
|
|
161
|
+
*/
|
|
162
|
+
async review(plan: string): Promise<T> {
|
|
163
|
+
// 1. Find CLI executable
|
|
164
|
+
const cliPath = this.findCli();
|
|
165
|
+
if (!cliPath) {
|
|
166
|
+
return this.makeSkipResult(`${this.getCliName()} CLI not found on PATH`);
|
|
209
167
|
}
|
|
210
168
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
169
|
+
logDebug(this.agent.name, `Found ${this.getCliName()} CLI at: ${cliPath}`);
|
|
170
|
+
|
|
171
|
+
// 2. Build prompt and args (provider-specific)
|
|
172
|
+
const prompt = this.buildPrompt(plan);
|
|
173
|
+
const args = this.buildCliArgs();
|
|
174
|
+
|
|
175
|
+
logInfo(this.agent.name, `Running ${this.getCliName()} with model: ${this.agent.model}, timeout: ${this.timeout}s`);
|
|
176
|
+
|
|
177
|
+
// 3. Execute subprocess
|
|
178
|
+
const env = getInternalSubprocessEnv();
|
|
179
|
+
const result = await execFileAsync(cliPath, args, {
|
|
180
|
+
input: prompt,
|
|
181
|
+
timeout: this.timeout * 1000,
|
|
182
|
+
env: env as Record<string, string>,
|
|
183
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
184
|
+
shell: process.platform === "win32",
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// 4. Handle timeout
|
|
188
|
+
if (result.killed || result.signal === "SIGTERM") {
|
|
189
|
+
return this.handleTimeout();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// 5. Extract output (provider-specific)
|
|
193
|
+
const { raw, err } = this.extractOutput(result);
|
|
194
|
+
|
|
195
|
+
// 6. Handle exit errors
|
|
196
|
+
if (!raw && !err && result.exitCode !== 0) {
|
|
197
|
+
return this.handleExitError(result);
|
|
215
198
|
}
|
|
199
|
+
|
|
200
|
+
// 7. Log subprocess results
|
|
201
|
+
this.logSubprocessResult(result, raw, err);
|
|
202
|
+
|
|
203
|
+
// 8. Parse JSON output (provider-specific)
|
|
204
|
+
const obj = this.parseOutput(raw, result);
|
|
205
|
+
|
|
206
|
+
// 9. Log parsed result
|
|
207
|
+
this.logParsedResult(obj);
|
|
208
|
+
|
|
209
|
+
// 10. Coerce to result type T (provider-specific)
|
|
210
|
+
const coerced = this.coerceResult(obj, raw, err);
|
|
211
|
+
|
|
212
|
+
// 11. Cleanup (optional override)
|
|
213
|
+
await this.cleanup();
|
|
214
|
+
|
|
215
|
+
return coerced;
|
|
216
216
|
}
|
|
217
217
|
}
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* See cc-native-plan-review-spec.md §4.9
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export type { Reviewer, ReviewerResult, ReviewOptions } from "./types.js";
|
|
7
|
-
export { makeResult } from "./types.js";
|
|
8
6
|
export { AgentReviewer, runAgentReview } from "./agent.js";
|
|
9
7
|
export { BaseCliAgent } from "./base/base-agent.js";
|
|
10
8
|
export { ClaudeAgent } from "./providers/claude-agent.js";
|
|
11
9
|
export { CodexAgent } from "./providers/codex-agent.js";
|
|
12
10
|
export { GeminiAgent } from "./providers/gemini-agent.js";
|
|
11
|
+
export type { Reviewer, ReviewerResult, ReviewOptions } from "./types.js";
|
|
12
|
+
export { makeResult } from "./types.js";
|
|
@@ -7,30 +7,15 @@ import { shellQuoteWin } from "../../../../_shared/lib-ts/base/subprocess-utils.
|
|
|
7
7
|
import { parseCliOutput } from "../../cli-output-parser.js";
|
|
8
8
|
import { coerceToReview } from "../../json-parser.js";
|
|
9
9
|
import type { ReviewerResult } from "../../types.js";
|
|
10
|
+
import { BaseCliAgent } from "../base/base-agent.js";
|
|
10
11
|
import { AGENT_REVIEW_PROMPT_PREFIX } from "../schemas.js";
|
|
11
12
|
import { makeResult } from "../types.js";
|
|
12
|
-
import { BaseCliAgent } from "../base/base-agent.js";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Claude CLI-based agent reviewer.
|
|
16
16
|
* Extends BaseCliAgent with Claude-specific prompt and argument handling.
|
|
17
17
|
*/
|
|
18
18
|
export class ClaudeAgent extends BaseCliAgent<ReviewerResult> {
|
|
19
|
-
protected getCliName(): string {
|
|
20
|
-
return "claude";
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
protected buildPrompt(plan: string): string {
|
|
24
|
-
return `IMMEDIATELY call StructuredOutput with your review of the plan below.
|
|
25
|
-
Do NOT output any text before calling StructuredOutput.
|
|
26
|
-
|
|
27
|
-
PLAN:
|
|
28
|
-
<<<
|
|
29
|
-
${plan}
|
|
30
|
-
>>>
|
|
31
|
-
`;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
19
|
protected buildCliArgs(): string[] {
|
|
35
20
|
const schemaJson = JSON.stringify(this.schema);
|
|
36
21
|
const cmdArgs = [
|
|
@@ -50,8 +35,15 @@ ${plan}
|
|
|
50
35
|
return cmdArgs;
|
|
51
36
|
}
|
|
52
37
|
|
|
53
|
-
protected
|
|
54
|
-
return
|
|
38
|
+
protected buildPrompt(plan: string): string {
|
|
39
|
+
return `IMMEDIATELY call StructuredOutput with your review of the plan below.
|
|
40
|
+
Do NOT output any text before calling StructuredOutput.
|
|
41
|
+
|
|
42
|
+
PLAN:
|
|
43
|
+
<<<
|
|
44
|
+
${plan}
|
|
45
|
+
>>>
|
|
46
|
+
`;
|
|
55
47
|
}
|
|
56
48
|
|
|
57
49
|
protected coerceResult(obj: Record<string, unknown> | null, raw: string, err: string): ReviewerResult {
|
|
@@ -59,7 +51,15 @@ ${plan}
|
|
|
59
51
|
return makeResult(this.agent.name, ok, verdict, norm, raw, err);
|
|
60
52
|
}
|
|
61
53
|
|
|
54
|
+
protected getCliName(): string {
|
|
55
|
+
return "claude";
|
|
56
|
+
}
|
|
57
|
+
|
|
62
58
|
protected makeErrorResult(type: "skip" | "error", message: string): ReviewerResult {
|
|
63
59
|
return makeResult(this.agent.name, false, type, {}, "", message);
|
|
64
60
|
}
|
|
61
|
+
|
|
62
|
+
protected parseOutput(raw: string, _result: unknown): Record<string, unknown> | null {
|
|
63
|
+
return parseCliOutput(raw, ["verdict", "summary"]);
|
|
64
|
+
}
|
|
65
65
|
}
|
|
@@ -5,18 +5,19 @@
|
|
|
5
5
|
|
|
6
6
|
import * as fs from "node:fs";
|
|
7
7
|
import * as os from "node:os";
|
|
8
|
-
import * as path from "node:path";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
|
|
9
10
|
import { logDebug, logWarn } from "../../../../_shared/lib-ts/base/logger.js";
|
|
10
11
|
import { getInternalSubprocessEnv, execFileAsync } from "../../../../_shared/lib-ts/base/subprocess-utils.js";
|
|
11
|
-
import { parseJsonMaybe, coerceToReview } from "../../json-parser.js";
|
|
12
12
|
import { debugLog, debugRaw } from "../../debug.js";
|
|
13
|
+
import { parseJsonMaybe, coerceToReview } from "../../json-parser.js";
|
|
13
14
|
import type { ReviewerResult } from "../../types.js";
|
|
15
|
+
import { BaseCliAgent, type ExecResult } from "../base/base-agent.js";
|
|
14
16
|
import { AGENT_REVIEW_PROMPT_PREFIX } from "../schemas.js";
|
|
15
17
|
import { makeResult } from "../types.js";
|
|
16
|
-
import { BaseCliAgent, type ExecResult } from "../base/base-agent.js";
|
|
17
18
|
|
|
18
19
|
/** Temp directory for Codex schema/output files */
|
|
19
|
-
|
|
20
|
+
const tmpDir: string | null = null;
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Codex CLI-based agent reviewer.
|
|
@@ -26,8 +27,19 @@ let tmpDir: string | null = null;
|
|
|
26
27
|
export class CodexAgent extends BaseCliAgent<ReviewerResult> {
|
|
27
28
|
private tempDir: string | null = null;
|
|
28
29
|
|
|
29
|
-
protected
|
|
30
|
-
|
|
30
|
+
protected buildCliArgs(): string[] {
|
|
31
|
+
// Create temp directory for schema and output files
|
|
32
|
+
this.tempDir = fs.mkdtempSync(path.join(os.tmpdir(), `codex-agent-${this.agent.name}-`));
|
|
33
|
+
|
|
34
|
+
const schemaPath = path.join(this.tempDir, "schema.json");
|
|
35
|
+
const outPath = path.join(this.tempDir, "output.json");
|
|
36
|
+
fs.writeFileSync(schemaPath, JSON.stringify(this.schema, null, 2), "utf-8");
|
|
37
|
+
|
|
38
|
+
const cmdArgs = ["exec", "--sandbox", "read-only"];
|
|
39
|
+
if (this.agent.model) cmdArgs.push("--model", this.agent.model);
|
|
40
|
+
cmdArgs.push("--output-schema", schemaPath, "-o", outPath, "-");
|
|
41
|
+
|
|
42
|
+
return cmdArgs;
|
|
31
43
|
}
|
|
32
44
|
|
|
33
45
|
protected buildPrompt(plan: string): string {
|
|
@@ -46,19 +58,66 @@ export class CodexAgent extends BaseCliAgent<ReviewerResult> {
|
|
|
46
58
|
].join("\n\n");
|
|
47
59
|
}
|
|
48
60
|
|
|
49
|
-
protected
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
protected async cleanup(): Promise<void> {
|
|
62
|
+
if (this.tempDir) {
|
|
63
|
+
try {
|
|
64
|
+
fs.rmSync(this.tempDir, { recursive: true, force: true });
|
|
65
|
+
} catch (error) {
|
|
66
|
+
logDebug(this.agent.name, `Failed to cleanup temp dir ${this.tempDir}: ${error}`);
|
|
67
|
+
}
|
|
68
|
+
this.tempDir = null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
52
71
|
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
72
|
+
protected coerceResult(obj: Record<string, unknown> | null, raw: string, err: string): ReviewerResult {
|
|
73
|
+
const [ok, verdict, norm] = coerceToReview(obj, this.getDefaultErrorMessage());
|
|
74
|
+
return makeResult(this.agent.name, ok, verdict, norm, raw, err);
|
|
75
|
+
}
|
|
56
76
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
77
|
+
protected extractOutput(result: ExecResult): { raw: string; err: string } {
|
|
78
|
+
const outPath = this.getOutputPath();
|
|
79
|
+
let raw = "";
|
|
80
|
+
const outExists = fs.existsSync(outPath);
|
|
60
81
|
|
|
61
|
-
|
|
82
|
+
if (outExists) {
|
|
83
|
+
raw = fs.readFileSync(outPath, "utf-8");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
logDebug(this.agent.name, `Codex output: exit=${result.exitCode}, outFile=${outExists} (${raw.length} chars), stdout=${result.stdout.length} chars`);
|
|
87
|
+
|
|
88
|
+
// Debug logging (override to include out_file_exists)
|
|
89
|
+
if (this.contextPath) {
|
|
90
|
+
debugRaw(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "stdout", raw || result.stdout);
|
|
91
|
+
if (result.stderr) {
|
|
92
|
+
debugRaw(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "stderr", result.stderr);
|
|
93
|
+
}
|
|
94
|
+
debugLog(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "subprocess_info", {
|
|
95
|
+
exit_code: result.exitCode,
|
|
96
|
+
stdout_len: (raw || result.stdout).length,
|
|
97
|
+
stderr_len: result.stderr.length,
|
|
98
|
+
out_file_exists: outExists,
|
|
99
|
+
model: this.agent.model,
|
|
100
|
+
provider: "codex",
|
|
101
|
+
timeout: this.timeout,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
raw: raw || result.stdout,
|
|
107
|
+
err: result.stderr.trim(),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
protected getCliName(): string {
|
|
112
|
+
return "codex";
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
protected makeErrorResult(type: "skip" | "error", message: string): ReviewerResult {
|
|
116
|
+
return makeResult(this.agent.name, false, type, {}, "", message);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
protected parseOutput(raw: string, result: ExecResult): Record<string, unknown> | null {
|
|
120
|
+
return parseJsonMaybe(raw) ?? parseJsonMaybe(result.stdout);
|
|
62
121
|
}
|
|
63
122
|
|
|
64
123
|
/**
|
|
@@ -116,64 +175,6 @@ export class CodexAgent extends BaseCliAgent<ReviewerResult> {
|
|
|
116
175
|
}
|
|
117
176
|
}
|
|
118
177
|
|
|
119
|
-
protected extractOutput(result: ExecResult): { raw: string; err: string } {
|
|
120
|
-
const outPath = this.getOutputPath();
|
|
121
|
-
let raw = "";
|
|
122
|
-
const outExists = fs.existsSync(outPath);
|
|
123
|
-
|
|
124
|
-
if (outExists) {
|
|
125
|
-
raw = fs.readFileSync(outPath, "utf-8");
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
logDebug(this.agent.name, `Codex output: exit=${result.exitCode}, outFile=${outExists} (${raw.length} chars), stdout=${result.stdout.length} chars`);
|
|
129
|
-
|
|
130
|
-
// Debug logging (override to include out_file_exists)
|
|
131
|
-
if (this.contextPath) {
|
|
132
|
-
debugRaw(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "stdout", raw || result.stdout);
|
|
133
|
-
if (result.stderr) {
|
|
134
|
-
debugRaw(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "stderr", result.stderr);
|
|
135
|
-
}
|
|
136
|
-
debugLog(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "subprocess_info", {
|
|
137
|
-
exit_code: result.exitCode,
|
|
138
|
-
stdout_len: (raw || result.stdout).length,
|
|
139
|
-
stderr_len: result.stderr.length,
|
|
140
|
-
out_file_exists: outExists,
|
|
141
|
-
model: this.agent.model,
|
|
142
|
-
provider: "codex",
|
|
143
|
-
timeout: this.timeout,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return {
|
|
148
|
-
raw: raw || result.stdout,
|
|
149
|
-
err: result.stderr.trim(),
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
protected parseOutput(raw: string, result: ExecResult): Record<string, unknown> | null {
|
|
154
|
-
return parseJsonMaybe(raw) ?? parseJsonMaybe(result.stdout);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
protected coerceResult(obj: Record<string, unknown> | null, raw: string, err: string): ReviewerResult {
|
|
158
|
-
const [ok, verdict, norm] = coerceToReview(obj, this.getDefaultErrorMessage());
|
|
159
|
-
return makeResult(this.agent.name, ok, verdict, norm, raw, err);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
protected makeErrorResult(type: "skip" | "error", message: string): ReviewerResult {
|
|
163
|
-
return makeResult(this.agent.name, false, type, {}, "", message);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
protected async cleanup(): Promise<void> {
|
|
167
|
-
if (this.tempDir) {
|
|
168
|
-
try {
|
|
169
|
-
fs.rmSync(this.tempDir, { recursive: true, force: true });
|
|
170
|
-
} catch (e) {
|
|
171
|
-
logDebug(this.agent.name, `Failed to cleanup temp dir ${this.tempDir}: ${e}`);
|
|
172
|
-
}
|
|
173
|
-
this.tempDir = null;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
178
|
private getOutputPath(): string {
|
|
178
179
|
return path.join(this.tempDir!, "output.json");
|
|
179
180
|
}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { ReviewerResult } from "../../types.js";
|
|
7
|
-
import { makeResult } from "../types.js";
|
|
8
7
|
import { BaseCliAgent } from "../base/base-agent.js";
|
|
8
|
+
import { makeResult } from "../types.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Gemini CLI-based agent reviewer (NOT IMPLEMENTED).
|
|
@@ -13,7 +13,7 @@ import { BaseCliAgent } from "../base/base-agent.js";
|
|
|
13
13
|
* This is a placeholder for future development.
|
|
14
14
|
*/
|
|
15
15
|
export class GeminiAgent extends BaseCliAgent<ReviewerResult> {
|
|
16
|
-
protected
|
|
16
|
+
protected buildCliArgs(): string[] {
|
|
17
17
|
throw new Error("GeminiAgent not implemented");
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -21,19 +21,19 @@ export class GeminiAgent extends BaseCliAgent<ReviewerResult> {
|
|
|
21
21
|
throw new Error("GeminiAgent not implemented");
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
protected
|
|
25
|
-
throw new Error("GeminiAgent not implemented");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
protected parseOutput(_raw: string, _result: unknown): Record<string, unknown> | null {
|
|
24
|
+
protected coerceResult(_obj: Record<string, unknown> | null, _raw: string, _err: string): ReviewerResult {
|
|
29
25
|
throw new Error("GeminiAgent not implemented");
|
|
30
26
|
}
|
|
31
27
|
|
|
32
|
-
protected
|
|
28
|
+
protected getCliName(): string {
|
|
33
29
|
throw new Error("GeminiAgent not implemented");
|
|
34
30
|
}
|
|
35
31
|
|
|
36
32
|
protected makeErrorResult(type: "skip" | "error", message: string): ReviewerResult {
|
|
37
33
|
return makeResult(this.agent.name, false, type, {}, "", message);
|
|
38
34
|
}
|
|
35
|
+
|
|
36
|
+
protected parseOutput(_raw: string, _result: unknown): Record<string, unknown> | null {
|
|
37
|
+
throw new Error("GeminiAgent not implemented");
|
|
38
|
+
}
|
|
39
39
|
}
|