aiwcli 0.13.7 → 0.14.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 +11 -1
- package/dist/commands/launch.d.ts +8 -0
- package/dist/commands/launch.js +96 -5
- package/dist/templates/_shared/.claude/skills/codex/SKILL.md +42 -0
- package/dist/templates/_shared/.claude/skills/codex/prompt.md +10 -0
- package/dist/templates/_shared/lib-ts/agent-exec/backends/headless.ts +33 -0
- package/dist/templates/_shared/lib-ts/agent-exec/backends/index.ts +6 -0
- package/dist/templates/_shared/lib-ts/agent-exec/backends/tmux.ts +145 -0
- package/dist/templates/_shared/lib-ts/agent-exec/base-agent.ts +229 -0
- package/dist/templates/_shared/lib-ts/agent-exec/execution-backend.ts +50 -0
- package/dist/templates/_shared/lib-ts/agent-exec/index.ts +4 -0
- package/dist/templates/_shared/lib-ts/base/cli-args.ts +283 -0
- package/dist/templates/_shared/lib-ts/base/inference.ts +53 -47
- package/dist/templates/_shared/lib-ts/base/models.ts +16 -0
- package/dist/templates/_shared/lib-ts/base/preflight.ts +98 -0
- package/dist/templates/_shared/lib-ts/base/tmux-driver.ts +381 -0
- package/dist/templates/_shared/lib-ts/base/utils.ts +8 -0
- package/dist/templates/_shared/lib-ts/context/context-formatter.ts +35 -11
- package/dist/templates/_shared/lib-ts/types.ts +17 -0
- package/dist/templates/_shared/scripts/status_line.ts +57 -28
- package/dist/templates/_shared/skills/prompt-codex/CLAUDE.md +46 -0
- package/dist/templates/_shared/skills/prompt-codex/scripts/launch-codex.ts +254 -0
- package/dist/templates/cc-native/.claude/settings.json +121 -1
- package/dist/templates/cc-native/_cc-native/CLAUDE.md +73 -0
- package/dist/templates/cc-native/_cc-native/cc-native.config.json +2 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/CLAUDE.md +70 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/settings.ts +3 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +5 -10
- package/dist/templates/cc-native/_cc-native/plan-review/lib/agent-selection.ts +2 -2
- package/dist/templates/cc-native/_cc-native/plan-review/lib/preflight.ts +14 -80
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/agent.ts +19 -7
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/base/base-agent.ts +4 -215
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/index.ts +1 -1
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/claude-agent.ts +9 -39
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/codex-agent.ts +19 -21
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/gemini-agent.ts +2 -1
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/orchestrator-claude-agent.ts +13 -15
- package/oclif.manifest.json +21 -3
- package/package.json +1 -1
|
@@ -2,97 +2,29 @@
|
|
|
2
2
|
* Preflight health checks for plan review agents.
|
|
3
3
|
* Validates provider+model combos work before committing agents to them.
|
|
4
4
|
* Runs minimal "ping" requests in parallel per unique provider:model combo.
|
|
5
|
+
*
|
|
6
|
+
* Uses shared checkProviderModel() from _shared/lib-ts/base/preflight.ts.
|
|
7
|
+
* This module provides the batch orchestrator and provider registry specific
|
|
8
|
+
* to the plan review pipeline.
|
|
5
9
|
*/
|
|
6
10
|
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
11
|
+
import { preflightCommandConfig } from "../../../_shared/lib-ts/base/cli-args.js";
|
|
12
|
+
import { logInfo, logWarn } from "../../../_shared/lib-ts/base/logger.js";
|
|
13
|
+
import { checkProviderModel, type PreflightCommandConfig } from "../../../_shared/lib-ts/base/preflight.js";
|
|
9
14
|
import type { ModelsConfig, PreflightCheckResult, PreflightReport } from "../../lib-ts/types.js";
|
|
10
|
-
import { claudePreflightArgs, CLAUDE_PREFLIGHT_INPUT } from "./reviewers/providers/claude-agent.js";
|
|
11
|
-
import { codexPreflightArgs, CODEX_PREFLIGHT_INPUT } from "./reviewers/providers/codex-agent.js";
|
|
12
15
|
|
|
13
16
|
const HOOK = "preflight";
|
|
14
17
|
const DEFAULT_TIMEOUT_MS = 15000;
|
|
15
18
|
|
|
16
19
|
// ---------------------------------------------------------------------------
|
|
17
|
-
// Provider Registry
|
|
20
|
+
// Provider Registry (built from centralized cli-args)
|
|
18
21
|
// ---------------------------------------------------------------------------
|
|
19
22
|
|
|
20
|
-
interface PreflightCommandConfig {
|
|
21
|
-
cliName: string;
|
|
22
|
-
buildArgs: (model: string) => string[];
|
|
23
|
-
input: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
23
|
const PREFLIGHT_COMMANDS: Record<string, PreflightCommandConfig> = {
|
|
27
|
-
claude:
|
|
28
|
-
codex:
|
|
24
|
+
claude: preflightCommandConfig("claude"),
|
|
25
|
+
codex: preflightCommandConfig("codex"),
|
|
29
26
|
};
|
|
30
27
|
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
|
-
// Error Classification
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
|
|
35
|
-
function classifyError(stderr: string, exitCode: number | null, killed: boolean, signal: string | null): string {
|
|
36
|
-
if (killed || signal === "SIGTERM") return "Preflight timed out";
|
|
37
|
-
if (/model.*not found|not available/i.test(stderr)) return "Model not available for this account";
|
|
38
|
-
if (/rate limit|429/i.test(stderr)) return "Rate limited";
|
|
39
|
-
if (/auth|api key|401/i.test(stderr)) return "Authentication failed";
|
|
40
|
-
if (/quota|billing/i.test(stderr)) return "Quota/billing issue";
|
|
41
|
-
return `Exit code ${exitCode}`;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// ---------------------------------------------------------------------------
|
|
45
|
-
// Single Check
|
|
46
|
-
// ---------------------------------------------------------------------------
|
|
47
|
-
|
|
48
|
-
async function checkProviderModel(
|
|
49
|
-
provider: string,
|
|
50
|
-
model: string,
|
|
51
|
-
timeoutMs: number,
|
|
52
|
-
): Promise<PreflightCheckResult> {
|
|
53
|
-
const config = PREFLIGHT_COMMANDS[provider];
|
|
54
|
-
if (!config) {
|
|
55
|
-
return { provider, model, available: false, latencyMs: 0, error: `Unknown provider: ${provider}` };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const cliPath = findExecutable(config.cliName);
|
|
59
|
-
if (!cliPath) {
|
|
60
|
-
return { provider, model, available: false, latencyMs: 0, error: `CLI '${config.cliName}' not found on PATH` };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const start = Date.now();
|
|
64
|
-
try {
|
|
65
|
-
const env = getInternalSubprocessEnv();
|
|
66
|
-
const result = await execFileAsync(cliPath, config.buildArgs(model), {
|
|
67
|
-
input: config.input,
|
|
68
|
-
timeout: timeoutMs,
|
|
69
|
-
env: env as Record<string, string>,
|
|
70
|
-
maxBuffer: 1 * 1024 * 1024,
|
|
71
|
-
shell: process.platform === "win32",
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const latencyMs = Date.now() - start;
|
|
75
|
-
|
|
76
|
-
if (result.killed || result.signal === "SIGTERM") {
|
|
77
|
-
return { provider, model, available: false, latencyMs, error: "Preflight timed out" };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (result.exitCode !== 0) {
|
|
81
|
-
const error = classifyError(result.stderr, result.exitCode, result.killed, result.signal);
|
|
82
|
-
logWarn(HOOK, `${provider}:${model} failed: ${error} (stderr: ${result.stderr.slice(-200)})`);
|
|
83
|
-
return { provider, model, available: false, latencyMs, error };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
logDebug(HOOK, `${provider}:${model} passed (${latencyMs}ms)`);
|
|
87
|
-
return { provider, model, available: true, latencyMs };
|
|
88
|
-
} catch (err) {
|
|
89
|
-
const latencyMs = Date.now() - start;
|
|
90
|
-
const error = err instanceof Error ? err.message : String(err);
|
|
91
|
-
logWarn(HOOK, `${provider}:${model} exception: ${error}`);
|
|
92
|
-
return { provider, model, available: false, latencyMs, error };
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
28
|
// ---------------------------------------------------------------------------
|
|
97
29
|
// Run All Checks
|
|
98
30
|
// ---------------------------------------------------------------------------
|
|
@@ -130,9 +62,11 @@ export async function runPreflight(
|
|
|
130
62
|
|
|
131
63
|
logInfo(HOOK, `Checking ${checks.length} provider:model combo(s): ${checks.map(c => `${c.provider}:${c.model}`).join(", ")}`);
|
|
132
64
|
|
|
133
|
-
// Run all checks in parallel
|
|
65
|
+
// Run all checks in parallel (pass provider-specific config from registry)
|
|
134
66
|
const results = await Promise.all(
|
|
135
|
-
checks.map(({ provider, model }) =>
|
|
67
|
+
checks.map(({ provider, model }) =>
|
|
68
|
+
checkProviderModel(provider, model, PREFLIGHT_COMMANDS[provider]!, effectiveTimeout, HOOK),
|
|
69
|
+
),
|
|
136
70
|
);
|
|
137
71
|
|
|
138
72
|
// Build available map
|
|
@@ -4,19 +4,24 @@
|
|
|
4
4
|
* See cc-native-plan-review-spec.md §4.10
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import type { ExecutionBackend } from "../../../../_shared/lib-ts/agent-exec/execution-backend.js";
|
|
8
|
+
import { logWarn } from "../../../../_shared/lib-ts/base/logger.js";
|
|
9
|
+
import { debugLog, debugRaw } from "../../../lib-ts/debug.js";
|
|
10
|
+
import type { AgentConfig, ReviewerResult, ReviewOptions } from "../../../lib-ts/types.js";
|
|
7
11
|
import { ClaudeAgent } from "./providers/claude-agent.js";
|
|
8
12
|
import { CodexAgent } from "./providers/codex-agent.js";
|
|
9
13
|
import { GeminiAgent } from "./providers/gemini-agent.js";
|
|
10
14
|
import type { Reviewer } from "./types.js";
|
|
11
15
|
import { makeResult } from "./types.js";
|
|
12
|
-
import { logWarn } from "../../../../_shared/lib-ts/base/logger.js";
|
|
13
|
-
import type { AgentConfig, ReviewerResult, ReviewOptions } from "../../../lib-ts/types.js";
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Agent reviewer — runs a CLI instance with a custom persona.
|
|
17
19
|
*/
|
|
18
20
|
export class AgentReviewer implements Reviewer {
|
|
19
|
-
constructor(
|
|
21
|
+
constructor(
|
|
22
|
+
private agent: AgentConfig,
|
|
23
|
+
private backend?: ExecutionBackend,
|
|
24
|
+
) {}
|
|
20
25
|
|
|
21
26
|
async review(
|
|
22
27
|
plan: string,
|
|
@@ -30,6 +35,7 @@ export class AgentReviewer implements Reviewer {
|
|
|
30
35
|
options.timeout,
|
|
31
36
|
options.context_path,
|
|
32
37
|
options.session_name ?? "unknown",
|
|
38
|
+
this.backend,
|
|
33
39
|
);
|
|
34
40
|
}
|
|
35
41
|
}
|
|
@@ -46,21 +52,27 @@ export async function runAgentReview(
|
|
|
46
52
|
timeout: number,
|
|
47
53
|
contextPath?: string,
|
|
48
54
|
sessionName = "unknown",
|
|
55
|
+
backend?: ExecutionBackend,
|
|
49
56
|
): Promise<ReviewerResult> {
|
|
50
57
|
try {
|
|
51
|
-
|
|
58
|
+
const config = {
|
|
59
|
+
agent, schema, timeout, contextPath, sessionName,
|
|
60
|
+
debugLogger: { log: debugLog, raw: debugRaw },
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
let reviewer: ClaudeAgent | CodexAgent | GeminiAgent;
|
|
52
64
|
|
|
53
65
|
switch (agent.provider) {
|
|
54
66
|
case "codex": {
|
|
55
|
-
reviewer = new CodexAgent(
|
|
67
|
+
reviewer = new CodexAgent(config, backend);
|
|
56
68
|
break;
|
|
57
69
|
}
|
|
58
70
|
case "gemini": {
|
|
59
|
-
reviewer = new GeminiAgent(
|
|
71
|
+
reviewer = new GeminiAgent(config, backend);
|
|
60
72
|
break;
|
|
61
73
|
}
|
|
62
74
|
default: {
|
|
63
|
-
reviewer = new ClaudeAgent(
|
|
75
|
+
reviewer = new ClaudeAgent(config, backend);
|
|
64
76
|
break;
|
|
65
77
|
}
|
|
66
78
|
}
|
|
@@ -1,218 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Provider-specific implementations (Claude, Codex, Gemini) extend this class.
|
|
2
|
+
* Re-export shim — BaseCliAgent now lives in _shared/lib-ts/agent-exec/base-agent.ts.
|
|
3
|
+
* This file preserves all existing import paths for provider implementations.
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import { debugLog, debugRaw } from "../../../../lib-ts/debug.js";
|
|
10
|
-
import type { AgentConfig } from "../../../../lib-ts/types.js";
|
|
11
|
-
|
|
12
|
-
/** Result from execFileAsync */
|
|
13
|
-
export interface ExecResult {
|
|
14
|
-
stdout: string;
|
|
15
|
-
stderr: string;
|
|
16
|
-
exitCode: number | null;
|
|
17
|
-
signal: string | null;
|
|
18
|
-
killed: boolean;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Abstract base class for all CLI agent subprocess invocations.
|
|
23
|
-
* Parameterized over return type T — ReviewerResult for reviewers,
|
|
24
|
-
* OrchestratorResult for the orchestrator.
|
|
25
|
-
* Subclasses implement provider-specific details.
|
|
26
|
-
*/
|
|
27
|
-
export abstract class BaseCliAgent<T> {
|
|
28
|
-
protected agent: AgentConfig;
|
|
29
|
-
protected contextPath?: string;
|
|
30
|
-
protected schema: Record<string, unknown>;
|
|
31
|
-
protected sessionName: string;
|
|
32
|
-
protected timeout: number;
|
|
33
|
-
|
|
34
|
-
constructor(
|
|
35
|
-
agent: AgentConfig,
|
|
36
|
-
schema: Record<string, unknown>,
|
|
37
|
-
timeout: number,
|
|
38
|
-
contextPath?: string,
|
|
39
|
-
sessionName = "unknown",
|
|
40
|
-
) {
|
|
41
|
-
this.agent = agent;
|
|
42
|
-
this.schema = schema;
|
|
43
|
-
this.timeout = timeout;
|
|
44
|
-
this.contextPath = contextPath;
|
|
45
|
-
this.sessionName = sessionName;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Build the command-line arguments for the CLI */
|
|
49
|
-
protected abstract buildCliArgs(): string[];
|
|
50
|
-
|
|
51
|
-
// ─── Abstract Methods (Subclass Implements) ────────────────────────────
|
|
52
|
-
|
|
53
|
-
/** Build the stdin prompt for the CLI */
|
|
54
|
-
protected abstract buildPrompt(plan: string): string;
|
|
55
|
-
|
|
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
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Coerce parsed JSON into the result type T */
|
|
62
|
-
protected abstract coerceResult(obj: Record<string, unknown> | null, raw: string, err: string): T;
|
|
63
|
-
|
|
64
|
-
/** Extract stdout/stderr from subprocess result. Override for file-based output (Codex). */
|
|
65
|
-
protected extractOutput(result: ExecResult): { raw: string; err: string } {
|
|
66
|
-
return {
|
|
67
|
-
raw: result.stdout.trim(),
|
|
68
|
-
err: result.stderr.trim(),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
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
|
-
|
|
82
|
-
/** Get default error message for coerceToReview */
|
|
83
|
-
protected getDefaultErrorMessage(): string {
|
|
84
|
-
return `Retry or check ${this.getCliName()} configuration.`;
|
|
85
|
-
}
|
|
86
|
-
|
|
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);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** Handle timeout scenario */
|
|
95
|
-
protected handleTimeout(): T {
|
|
96
|
-
const msg = `${this.getCliName()} TIMEOUT after ${this.timeout}s`;
|
|
97
|
-
logWarn(this.agent.name, msg);
|
|
98
|
-
return this.makeErrorResult("error", `${this.agent.name} timed out after ${this.timeout}s`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// ─── Shared Infrastructure ──────────────────────────────────────────────
|
|
102
|
-
|
|
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
|
-
}
|
|
120
|
-
|
|
121
|
-
/** Log subprocess execution results */
|
|
122
|
-
protected logSubprocessResult(result: ExecResult, raw: string, err: string): void {
|
|
123
|
-
logDebug(this.agent.name, `Exit code: ${result.exitCode}`);
|
|
124
|
-
logDebug(this.agent.name, `stdout length: ${raw.length} chars`);
|
|
125
|
-
if (err) logDebug(this.agent.name, `stderr: ${err.slice(0, 500)}`);
|
|
126
|
-
|
|
127
|
-
// Debug logging
|
|
128
|
-
if (this.contextPath) {
|
|
129
|
-
debugRaw(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "stdout", raw);
|
|
130
|
-
if (err) {
|
|
131
|
-
debugRaw(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "stderr", err);
|
|
132
|
-
}
|
|
133
|
-
debugLog(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "subprocess_info", {
|
|
134
|
-
exit_code: result.exitCode,
|
|
135
|
-
stdout_len: raw.length,
|
|
136
|
-
stderr_len: err.length,
|
|
137
|
-
model: this.agent.model,
|
|
138
|
-
provider: this.agent.provider,
|
|
139
|
-
timeout: this.timeout,
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (raw) logDebug(this.agent.name, `stdout preview: ${raw.slice(0, 500)}`);
|
|
144
|
-
}
|
|
145
|
-
|
|
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`);
|
|
167
|
-
}
|
|
168
|
-
|
|
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 normalizedCliPath = normalizePathForCli(cliPath);
|
|
180
|
-
const result = await execFileAsync(normalizedCliPath, args, {
|
|
181
|
-
input: prompt,
|
|
182
|
-
timeout: this.timeout * 1000,
|
|
183
|
-
env: env as Record<string, string>,
|
|
184
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
185
|
-
shell: process.platform === "win32",
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
// 4. Handle timeout
|
|
189
|
-
if (result.killed || result.signal === "SIGTERM") {
|
|
190
|
-
return this.handleTimeout();
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// 5. Extract output (provider-specific)
|
|
194
|
-
const { raw, err } = this.extractOutput(result);
|
|
195
|
-
|
|
196
|
-
// 6. Handle exit errors
|
|
197
|
-
if (!raw && !err && result.exitCode !== 0) {
|
|
198
|
-
return this.handleExitError(result);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// 7. Log subprocess results
|
|
202
|
-
this.logSubprocessResult(result, raw, err);
|
|
203
|
-
|
|
204
|
-
// 8. Parse JSON output (provider-specific)
|
|
205
|
-
const obj = this.parseOutput(raw, result);
|
|
206
|
-
|
|
207
|
-
// 9. Log parsed result
|
|
208
|
-
this.logParsedResult(obj);
|
|
209
|
-
|
|
210
|
-
// 10. Coerce to result type T (provider-specific)
|
|
211
|
-
const coerced = this.coerceResult(obj, raw, err);
|
|
212
|
-
|
|
213
|
-
// 11. Cleanup (optional override)
|
|
214
|
-
await this.cleanup();
|
|
215
|
-
|
|
216
|
-
return coerced;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
6
|
+
export { BaseCliAgent, type AgentExecutionConfig, type AgentDebugLogger } from "../../../../../_shared/lib-ts/agent-exec/base-agent.js";
|
|
7
|
+
export type { ExecutionResult as ExecResult } from "../../../../../_shared/lib-ts/agent-exec/execution-backend.js";
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export { AgentReviewer, runAgentReview } from "./agent.js";
|
|
7
|
-
export { BaseCliAgent } from "./base/base-agent.js";
|
|
7
|
+
export { BaseCliAgent, type AgentExecutionConfig, type AgentDebugLogger } from "./base/base-agent.js";
|
|
8
8
|
export { ClaudeAgent } from "./providers/claude-agent.js";
|
|
9
9
|
export { CodexAgent } from "./providers/codex-agent.js";
|
|
10
10
|
export { GeminiAgent } from "./providers/gemini-agent.js";
|
package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/claude-agent.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Uses claude CLI with --json-schema and --system-prompt flags.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { buildCliInvocation, reviewSpec } from "../../../../../_shared/lib-ts/base/cli-args.js";
|
|
7
|
+
import type { ExecutionResult } from "../../../../../_shared/lib-ts/agent-exec/execution-backend.js";
|
|
7
8
|
import { parseCliOutput } from "../../../../lib-ts/cli-output-parser.js";
|
|
8
9
|
import { coerceToReview } from "../../../../lib-ts/json-parser.js";
|
|
9
10
|
import type { ReviewerResult } from "../../../../lib-ts/types.js";
|
|
@@ -15,46 +16,15 @@ import { makeResult } from "../types.js";
|
|
|
15
16
|
* Claude CLI-based agent reviewer.
|
|
16
17
|
* Extends BaseCliAgent with Claude-specific prompt and argument handling.
|
|
17
18
|
*/
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
// Preflight (standalone — no instance needed)
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
|
|
22
|
-
export const CLAUDE_PREFLIGHT_INPUT = "Respond with exactly: ok";
|
|
23
|
-
|
|
24
|
-
export function claudePreflightArgs(model: string): string[] {
|
|
25
|
-
return [
|
|
26
|
-
"--model", model,
|
|
27
|
-
"--max-turns", "1",
|
|
28
|
-
"--output-format", "json",
|
|
29
|
-
"--setting-sources", process.platform === "win32" ? '""' : "",
|
|
30
|
-
"-p",
|
|
31
|
-
"--no-session-persistence",
|
|
32
|
-
];
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// ---------------------------------------------------------------------------
|
|
36
|
-
// Agent Class
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
|
|
39
19
|
export class ClaudeAgent extends BaseCliAgent<ReviewerResult> {
|
|
40
20
|
protected buildCliArgs(): string[] {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"--output-format", "json",
|
|
45
|
-
"--json-schema", shellQuoteWin(schemaJson),
|
|
46
|
-
"--max-turns", "3",
|
|
47
|
-
"--setting-sources", process.platform === "win32" ? '""' : "",
|
|
48
|
-
"-p",
|
|
49
|
-
"--no-session-persistence", // Prevent subprocess from creating session records
|
|
50
|
-
];
|
|
51
|
-
|
|
52
|
-
if (this.agent.system_prompt) {
|
|
53
|
-
const fullPrompt = AGENT_REVIEW_PROMPT_PREFIX + "\n\n---\n\n" + this.agent.system_prompt;
|
|
54
|
-
cmdArgs.push("--system-prompt", shellQuoteWin(fullPrompt));
|
|
55
|
-
}
|
|
21
|
+
const fullPrompt = this.agent.system_prompt
|
|
22
|
+
? AGENT_REVIEW_PROMPT_PREFIX + "\n\n---\n\n" + this.agent.system_prompt
|
|
23
|
+
: undefined;
|
|
56
24
|
|
|
57
|
-
return
|
|
25
|
+
return buildCliInvocation(
|
|
26
|
+
reviewSpec("claude", this.agent.model, this.schema, fullPrompt),
|
|
27
|
+
).args;
|
|
58
28
|
}
|
|
59
29
|
|
|
60
30
|
protected buildPrompt(plan: string): string {
|
|
@@ -81,7 +51,7 @@ ${plan}
|
|
|
81
51
|
return makeResult(this.agent.name, false, type, {}, "", message);
|
|
82
52
|
}
|
|
83
53
|
|
|
84
|
-
protected parseOutput(raw: string, _result:
|
|
54
|
+
protected parseOutput(raw: string, _result: ExecutionResult): Record<string, unknown> | null {
|
|
85
55
|
return parseCliOutput(raw, ["verdict", "summary"]);
|
|
86
56
|
}
|
|
87
57
|
}
|
package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/codex-agent.ts
CHANGED
|
@@ -7,25 +7,17 @@ import * as fs from "node:fs";
|
|
|
7
7
|
import * as os from "node:os";
|
|
8
8
|
import * as path from "node:path";
|
|
9
9
|
|
|
10
|
+
import { buildCliInvocation } from "../../../../../_shared/lib-ts/base/cli-args.js";
|
|
11
|
+
import type { ExecutionResult } from "../../../../../_shared/lib-ts/agent-exec/execution-backend.js";
|
|
10
12
|
import { logDebug, logWarn } from "../../../../../_shared/lib-ts/base/logger.js";
|
|
11
|
-
import { getInternalSubprocessEnv,
|
|
13
|
+
import { getInternalSubprocessEnv, normalizePathForCli, shellQuoteWin } from "../../../../../_shared/lib-ts/base/subprocess-utils.js";
|
|
12
14
|
import { debugLog, debugRaw } from "../../../../lib-ts/debug.js";
|
|
13
15
|
import { parseJsonMaybe, coerceToReview } from "../../../../lib-ts/json-parser.js";
|
|
14
16
|
import type { ReviewerResult } from "../../../../lib-ts/types.js";
|
|
15
|
-
import { BaseCliAgent
|
|
17
|
+
import { BaseCliAgent } from "../base/base-agent.js";
|
|
16
18
|
import { AGENT_REVIEW_PROMPT_PREFIX } from "../schemas.js";
|
|
17
19
|
import { makeResult } from "../types.js";
|
|
18
20
|
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
// Preflight (standalone — no instance needed)
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
|
|
23
|
-
export const CODEX_PREFLIGHT_INPUT = "Respond with exactly: ok";
|
|
24
|
-
|
|
25
|
-
export function codexPreflightArgs(model: string): string[] {
|
|
26
|
-
return ["exec", "--sandbox", "read-only", "--model", model, "-"];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
21
|
// ---------------------------------------------------------------------------
|
|
30
22
|
// Agent Class
|
|
31
23
|
// ---------------------------------------------------------------------------
|
|
@@ -52,11 +44,14 @@ export class CodexAgent extends BaseCliAgent<ReviewerResult> {
|
|
|
52
44
|
const normalizedSchema = shellQuoteWin(normalizePathForCli(schemaPath));
|
|
53
45
|
const normalizedOut = shellQuoteWin(normalizePathForCli(outPath));
|
|
54
46
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
47
|
+
return buildCliInvocation({
|
|
48
|
+
provider: "codex",
|
|
49
|
+
model: this.agent.model,
|
|
50
|
+
mode: "structured",
|
|
51
|
+
sandbox: "read-only",
|
|
52
|
+
outputSchemaPath: normalizedSchema,
|
|
53
|
+
outputFilePath: normalizedOut,
|
|
54
|
+
}).args;
|
|
60
55
|
}
|
|
61
56
|
|
|
62
57
|
protected buildPrompt(plan: string): string {
|
|
@@ -91,7 +86,7 @@ export class CodexAgent extends BaseCliAgent<ReviewerResult> {
|
|
|
91
86
|
return makeResult(this.agent.name, ok, verdict, norm, raw, err);
|
|
92
87
|
}
|
|
93
88
|
|
|
94
|
-
protected extractOutput(result:
|
|
89
|
+
protected extractOutput(result: ExecutionResult): { raw: string; err: string } {
|
|
95
90
|
const outPath = this.getOutputPath();
|
|
96
91
|
let raw = "";
|
|
97
92
|
const outExists = fs.existsSync(outPath);
|
|
@@ -133,7 +128,7 @@ export class CodexAgent extends BaseCliAgent<ReviewerResult> {
|
|
|
133
128
|
return makeResult(this.agent.name, false, type, {}, "", message);
|
|
134
129
|
}
|
|
135
130
|
|
|
136
|
-
protected parseOutput(raw: string, result:
|
|
131
|
+
protected parseOutput(raw: string, result: ExecutionResult): Record<string, unknown> | null {
|
|
137
132
|
return parseJsonMaybe(raw) ?? parseJsonMaybe(result.stdout);
|
|
138
133
|
}
|
|
139
134
|
|
|
@@ -156,10 +151,13 @@ export class CodexAgent extends BaseCliAgent<ReviewerResult> {
|
|
|
156
151
|
|
|
157
152
|
try {
|
|
158
153
|
const env = getInternalSubprocessEnv();
|
|
159
|
-
const
|
|
154
|
+
const normalizedCliPath = normalizePathForCli(cliPath);
|
|
155
|
+
const result = await this.backend.execute({
|
|
156
|
+
cliPath: normalizedCliPath,
|
|
157
|
+
args,
|
|
160
158
|
input: prompt,
|
|
161
|
-
timeout: this.timeout * 1000,
|
|
162
159
|
env: env as Record<string, string>,
|
|
160
|
+
timeoutMs: this.timeout * 1000,
|
|
163
161
|
maxBuffer: 10 * 1024 * 1024,
|
|
164
162
|
shell: process.platform === "win32",
|
|
165
163
|
});
|
package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/gemini-agent.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Placeholder for future implementation.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import type { ExecutionResult } from "../../../../../_shared/lib-ts/agent-exec/execution-backend.js";
|
|
6
7
|
import type { ReviewerResult } from "../../../../lib-ts/types.js";
|
|
7
8
|
import { BaseCliAgent } from "../base/base-agent.js";
|
|
8
9
|
import { makeResult } from "../types.js";
|
|
@@ -33,7 +34,7 @@ export class GeminiAgent extends BaseCliAgent<ReviewerResult> {
|
|
|
33
34
|
return makeResult(this.agent.name, false, type, {}, "", message);
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
protected parseOutput(_raw: string, _result:
|
|
37
|
+
protected parseOutput(_raw: string, _result: ExecutionResult): Record<string, unknown> | null {
|
|
37
38
|
throw new Error("GeminiAgent not implemented");
|
|
38
39
|
}
|
|
39
40
|
}
|
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
* Analyzes plan complexity and selects reviewer agents via Claude CLI.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { buildCliInvocation, reviewSpec } from "../../../../../_shared/lib-ts/base/cli-args.js";
|
|
7
|
+
import type { ExecutionBackend } from "../../../../../_shared/lib-ts/agent-exec/execution-backend.js";
|
|
8
|
+
import type { ExecutionResult } from "../../../../../_shared/lib-ts/agent-exec/execution-backend.js";
|
|
6
9
|
import { logDebug } from "../../../../../_shared/lib-ts/base/logger.js";
|
|
7
|
-
import {
|
|
10
|
+
import { debugLog, debugRaw } from "../../../../lib-ts/debug.js";
|
|
8
11
|
import { parseCliOutput } from "../../../../lib-ts/cli-output-parser.js";
|
|
9
12
|
import type { AgentConfig, OrchestratorResult, ComplexityCategory } from "../../../../lib-ts/types.js";
|
|
10
13
|
import { BaseCliAgent } from "../base/base-agent.js";
|
|
@@ -51,6 +54,7 @@ export class OrchestratorClaudeAgent extends BaseCliAgent<OrchestratorResult> {
|
|
|
51
54
|
timeout: number,
|
|
52
55
|
contextPath?: string,
|
|
53
56
|
sessionName?: string,
|
|
57
|
+
backend?: ExecutionBackend,
|
|
54
58
|
) {
|
|
55
59
|
// Build schema dynamically based on valid agent names
|
|
56
60
|
const nonMandatory = agentLibrary.filter(
|
|
@@ -63,7 +67,10 @@ export class OrchestratorClaudeAgent extends BaseCliAgent<OrchestratorResult> {
|
|
|
63
67
|
? buildOrchestratorSchema(validNames, categories)
|
|
64
68
|
: ORCHESTRATOR_SCHEMA;
|
|
65
69
|
|
|
66
|
-
super(
|
|
70
|
+
super({
|
|
71
|
+
agent, schema, timeout, contextPath, sessionName,
|
|
72
|
+
debugLogger: { log: debugLog, raw: debugRaw },
|
|
73
|
+
}, backend);
|
|
67
74
|
|
|
68
75
|
this.nonMandatory = nonMandatory;
|
|
69
76
|
this.validNames = validNames;
|
|
@@ -79,8 +86,6 @@ export class OrchestratorClaudeAgent extends BaseCliAgent<OrchestratorResult> {
|
|
|
79
86
|
}
|
|
80
87
|
|
|
81
88
|
protected buildCliArgs(): string[] {
|
|
82
|
-
const schemaJson = JSON.stringify(this.schema);
|
|
83
|
-
|
|
84
89
|
const systemPrompt = `You are a plan orchestrator for code review. Your job is to analyze plans and select appropriate reviewer agents.
|
|
85
90
|
|
|
86
91
|
You MUST call StructuredOutput immediately with your analysis. Do NOT ask questions or use any other tools.
|
|
@@ -91,16 +96,9 @@ When selecting agents:
|
|
|
91
96
|
- Only select agents whose categories match the plan category
|
|
92
97
|
- Fewer agents for simple plans, more for complex plans`;
|
|
93
98
|
|
|
94
|
-
return
|
|
95
|
-
"
|
|
96
|
-
|
|
97
|
-
"--json-schema", shellQuoteWin(schemaJson),
|
|
98
|
-
"--max-turns", "3",
|
|
99
|
-
"--setting-sources", process.platform === "win32" ? '""' : "",
|
|
100
|
-
"--system-prompt", shellQuoteWin(systemPrompt),
|
|
101
|
-
"-p",
|
|
102
|
-
"--no-session-persistence", // Prevent subprocess from creating session records
|
|
103
|
-
];
|
|
99
|
+
return buildCliInvocation(
|
|
100
|
+
reviewSpec("claude", this.agent.model, this.schema, systemPrompt),
|
|
101
|
+
).args;
|
|
104
102
|
}
|
|
105
103
|
|
|
106
104
|
protected buildPrompt(plan: string): string {
|
|
@@ -180,7 +178,7 @@ Call StructuredOutput now with: complexity, category, selectedAgents, reasoning`
|
|
|
180
178
|
);
|
|
181
179
|
}
|
|
182
180
|
|
|
183
|
-
protected parseOutput(raw: string, _result:
|
|
181
|
+
protected parseOutput(raw: string, _result: ExecutionResult): Record<string, unknown> | null {
|
|
184
182
|
return parseCliOutput(raw);
|
|
185
183
|
}
|
|
186
184
|
|