aiwcli 0.11.1 → 0.12.1
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/commands/clear.d.ts +8 -0
- package/dist/commands/clear.js +86 -0
- package/dist/lib/bmad-installer.d.ts +2 -27
- package/dist/lib/bmad-installer.js +3 -43
- package/dist/lib/claude-settings-types.d.ts +2 -1
- package/dist/lib/env-compat.d.ts +0 -8
- package/dist/lib/env-compat.js +0 -12
- package/dist/lib/git/index.d.ts +0 -1
- package/dist/lib/gitignore-manager.d.ts +0 -2
- package/dist/lib/gitignore-manager.js +1 -1
- package/dist/lib/hooks-merger.d.ts +1 -15
- package/dist/lib/hooks-merger.js +1 -1
- package/dist/lib/index.d.ts +3 -7
- package/dist/lib/index.js +3 -11
- package/dist/lib/output.d.ts +2 -1
- package/dist/lib/settings-hierarchy.d.ts +1 -13
- package/dist/lib/settings-hierarchy.js +1 -1
- package/dist/lib/template-installer.d.ts +5 -9
- package/dist/lib/template-installer.js +3 -13
- package/dist/lib/template-linter.d.ts +3 -10
- package/dist/lib/template-linter.js +2 -2
- package/dist/lib/template-resolver.d.ts +6 -0
- package/dist/lib/template-resolver.js +10 -0
- package/dist/lib/template-settings-reconstructor.d.ts +1 -1
- package/dist/lib/template-settings-reconstructor.js +17 -24
- package/dist/lib/terminal.d.ts +3 -14
- package/dist/lib/terminal.js +0 -4
- package/dist/lib/version.d.ts +2 -11
- package/dist/lib/version.js +3 -3
- package/dist/lib/windsurf-hooks-merger.d.ts +1 -15
- package/dist/lib/windsurf-hooks-merger.js +1 -1
- package/dist/templates/_shared/.codex/workflows/handoff.md +1 -1
- package/dist/templates/_shared/.windsurf/workflows/handoff.md +1 -1
- package/dist/templates/_shared/hooks-ts/session_end.ts +75 -4
- package/dist/templates/_shared/hooks-ts/session_start.ts +11 -13
- package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +6 -8
- package/dist/templates/_shared/lib-ts/CLAUDE.md +56 -7
- package/dist/templates/_shared/lib-ts/base/hook-utils.ts +176 -29
- package/dist/templates/_shared/lib-ts/base/logger.ts +1 -1
- package/dist/templates/_shared/lib-ts/base/state-io.ts +11 -2
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +181 -165
- 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/lib-ts/package.json +1 -2
- package/dist/templates/_shared/lib-ts/templates/plan-context.ts +27 -34
- package/dist/templates/_shared/lib-ts/types.ts +17 -2
- package/dist/templates/_shared/scripts/resume_handoff.ts +4 -4
- package/dist/templates/_shared/scripts/save_handoff.ts +7 -7
- package/dist/templates/_shared/scripts/status_line.ts +104 -71
- package/dist/templates/_shared/workflows/handoff.md +1 -1
- package/dist/templates/cc-native/.claude/settings.json +182 -175
- package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +23 -1
- package/dist/templates/cc-native/_cc-native/agents/plan-questions/PLAN-QUESTIONER.md +70 -0
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +6 -1
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +142 -111
- package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_subagent.ts +54 -0
- package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_write.ts +52 -0
- package/dist/templates/cc-native/_cc-native/hooks/mark_questions_asked.ts +53 -0
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +19 -19
- package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +6 -5
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +114 -83
- package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +107 -10
- package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +1 -1
- package/dist/templates/cc-native/_cc-native/lib-ts/corroboration.ts +6 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +0 -4
- package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +40 -219
- package/dist/templates/cc-native/_cc-native/lib-ts/plan-enhancement.ts +41 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/plan-questions.ts +102 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +26 -227
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/base/base-agent.ts +217 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +4 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/claude-agent.ts +65 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/codex-agent.ts +185 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/gemini-agent.ts +39 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/orchestrator-claude-agent.ts +195 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/schemas.ts +201 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +2 -2
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +17 -16
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +13 -108
- package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +3 -3
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +2 -14
- package/oclif.manifest.json +1 -1
- package/package.json +1 -2
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +0 -119
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +0 -130
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +0 -107
- /package/dist/templates/cc-native/_cc-native/agents/{ARCH-EVOLUTION.md → plan-review/ARCH-EVOLUTION.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{ARCH-PATTERNS.md → plan-review/ARCH-PATTERNS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{ARCH-STRUCTURE.md → plan-review/ARCH-STRUCTURE.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{ASSUMPTION-TRACER.md → plan-review/ASSUMPTION-TRACER.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{CLARITY-AUDITOR.md → plan-review/CLARITY-AUDITOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{COMPLETENESS-FEASIBILITY.md → plan-review/COMPLETENESS-FEASIBILITY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{COMPLETENESS-GAPS.md → plan-review/COMPLETENESS-GAPS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{COMPLETENESS-ORDERING.md → plan-review/COMPLETENESS-ORDERING.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{CONSTRAINT-VALIDATOR.md → plan-review/CONSTRAINT-VALIDATOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{DESIGN-ADR-VALIDATOR.md → plan-review/DESIGN-ADR-VALIDATOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{DESIGN-SCALE-MATCHER.md → plan-review/DESIGN-SCALE-MATCHER.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{DEVILS-ADVOCATE.md → plan-review/DEVILS-ADVOCATE.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{DOCUMENTATION-PHILOSOPHY.md → plan-review/DOCUMENTATION-PHILOSOPHY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{HANDOFF-READINESS.md → plan-review/HANDOFF-READINESS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{HIDDEN-COMPLEXITY.md → plan-review/HIDDEN-COMPLEXITY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{INCREMENTAL-DELIVERY.md → plan-review/INCREMENTAL-DELIVERY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{RISK-DEPENDENCY.md → plan-review/RISK-DEPENDENCY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{RISK-FMEA.md → plan-review/RISK-FMEA.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{RISK-PREMORTEM.md → plan-review/RISK-PREMORTEM.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{RISK-REVERSIBILITY.md → plan-review/RISK-REVERSIBILITY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{SCOPE-BOUNDARY.md → plan-review/SCOPE-BOUNDARY.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{SIMPLICITY-GUARDIAN.md → plan-review/SIMPLICITY-GUARDIAN.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{SKEPTIC.md → plan-review/SKEPTIC.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-BEHAVIOR-AUDITOR.md → plan-review/TESTDRIVEN-BEHAVIOR-AUDITOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-CHARACTERIZATION.md → plan-review/TESTDRIVEN-CHARACTERIZATION.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-FIRST-VALIDATOR.md → plan-review/TESTDRIVEN-FIRST-VALIDATOR.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-PYRAMID-ANALYZER.md → plan-review/TESTDRIVEN-PYRAMID-ANALYZER.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TRADEOFF-COSTS.md → plan-review/TRADEOFF-COSTS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{TRADEOFF-STAKEHOLDERS.md → plan-review/TRADEOFF-STAKEHOLDERS.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{VERIFY-COVERAGE.md → plan-review/VERIFY-COVERAGE.md} +0 -0
- /package/dist/templates/cc-native/_cc-native/agents/{VERIFY-STRENGTH.md → plan-review/VERIFY-STRENGTH.md} +0 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex CLI agent reviewer implementation.
|
|
3
|
+
* Uses codex exec with temp files for schema and output.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as fs from "node:fs";
|
|
7
|
+
import * as os from "node:os";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
|
|
10
|
+
import { logDebug, logWarn } from "../../../../_shared/lib-ts/base/logger.js";
|
|
11
|
+
import { getInternalSubprocessEnv, execFileAsync } from "../../../../_shared/lib-ts/base/subprocess-utils.js";
|
|
12
|
+
import { debugLog, debugRaw } from "../../debug.js";
|
|
13
|
+
import { parseJsonMaybe, coerceToReview } from "../../json-parser.js";
|
|
14
|
+
import type { ReviewerResult } from "../../types.js";
|
|
15
|
+
import { BaseCliAgent, type ExecResult } from "../base/base-agent.js";
|
|
16
|
+
import { AGENT_REVIEW_PROMPT_PREFIX } from "../schemas.js";
|
|
17
|
+
import { makeResult } from "../types.js";
|
|
18
|
+
|
|
19
|
+
/** Temp directory for Codex schema/output files */
|
|
20
|
+
const tmpDir: string | null = null;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Codex CLI-based agent reviewer.
|
|
24
|
+
* Codex has no --system-prompt flag, so we embed schema and persona in stdin.
|
|
25
|
+
* Uses temp files for schema and output.
|
|
26
|
+
*/
|
|
27
|
+
export class CodexAgent extends BaseCliAgent<ReviewerResult> {
|
|
28
|
+
private tempDir: string | null = null;
|
|
29
|
+
|
|
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;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
protected buildPrompt(plan: string): string {
|
|
46
|
+
// Codex has no --system-prompt flag, so we prepend the agent persona to stdin.
|
|
47
|
+
return [
|
|
48
|
+
AGENT_REVIEW_PROMPT_PREFIX,
|
|
49
|
+
"---",
|
|
50
|
+
this.agent.system_prompt || "",
|
|
51
|
+
"---",
|
|
52
|
+
`Return ONLY a JSON object matching this schema:\n${JSON.stringify(this.schema)}`,
|
|
53
|
+
"",
|
|
54
|
+
"PLAN:",
|
|
55
|
+
"<<<",
|
|
56
|
+
plan,
|
|
57
|
+
">>>",
|
|
58
|
+
].join("\n\n");
|
|
59
|
+
}
|
|
60
|
+
|
|
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
|
+
}
|
|
71
|
+
|
|
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
|
+
}
|
|
76
|
+
|
|
77
|
+
protected extractOutput(result: ExecResult): { raw: string; err: string } {
|
|
78
|
+
const outPath = this.getOutputPath();
|
|
79
|
+
let raw = "";
|
|
80
|
+
const outExists = fs.existsSync(outPath);
|
|
81
|
+
|
|
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);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Override review() to handle temp file creation in try block.
|
|
125
|
+
* Codex writes output to a temp file instead of stdout.
|
|
126
|
+
*/
|
|
127
|
+
async review(plan: string): Promise<ReviewerResult> {
|
|
128
|
+
const cliPath = this.findCli();
|
|
129
|
+
if (!cliPath) {
|
|
130
|
+
return this.makeSkipResult(`${this.getCliName()} CLI not found on PATH`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
logDebug(this.agent.name, `Found ${this.getCliName()} CLI at: ${cliPath}`);
|
|
134
|
+
|
|
135
|
+
const prompt = this.buildPrompt(plan);
|
|
136
|
+
const args = this.buildCliArgs();
|
|
137
|
+
|
|
138
|
+
logDebug(this.agent.name, `Running ${this.getCliName()} with model: ${this.agent.model}, timeout: ${this.timeout}s`);
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
const env = getInternalSubprocessEnv();
|
|
142
|
+
const result = await execFileAsync(cliPath, args, {
|
|
143
|
+
input: prompt,
|
|
144
|
+
timeout: this.timeout * 1000,
|
|
145
|
+
env: env as Record<string, string>,
|
|
146
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
147
|
+
shell: process.platform === "win32",
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (result.killed || result.signal === "SIGTERM") {
|
|
151
|
+
return this.handleTimeout();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Extract from temp file if exists, fallback to stdout
|
|
155
|
+
const { raw, err } = this.extractOutput(result);
|
|
156
|
+
|
|
157
|
+
// Log exit code and stderr tail for ALL non-zero exits
|
|
158
|
+
if (result.exitCode !== 0) {
|
|
159
|
+
const stderrTail = err.slice(-500);
|
|
160
|
+
logWarn(this.agent.name, `Codex exited with code ${result.exitCode}, stderr_len=${err.length}, stderr_tail: ${stderrTail}`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (!raw && !err && !this.outputFileExists() && result.exitCode !== 0) {
|
|
164
|
+
return this.handleExitError(result);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
this.logSubprocessResult(result, raw, err);
|
|
168
|
+
|
|
169
|
+
const obj = this.parseOutput(raw, result);
|
|
170
|
+
this.logParsedResult(obj);
|
|
171
|
+
|
|
172
|
+
return this.coerceResult(obj, raw, err);
|
|
173
|
+
} finally {
|
|
174
|
+
await this.cleanup();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private getOutputPath(): string {
|
|
179
|
+
return path.join(this.tempDir!, "output.json");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private outputFileExists(): boolean {
|
|
183
|
+
return this.tempDir ? fs.existsSync(this.getOutputPath()) : false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini CLI agent reviewer implementation (stub).
|
|
3
|
+
* Placeholder for future implementation.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ReviewerResult } from "../../types.js";
|
|
7
|
+
import { BaseCliAgent } from "../base/base-agent.js";
|
|
8
|
+
import { makeResult } from "../types.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Gemini CLI-based agent reviewer (NOT IMPLEMENTED).
|
|
12
|
+
* All methods throw "not implemented" errors.
|
|
13
|
+
* This is a placeholder for future development.
|
|
14
|
+
*/
|
|
15
|
+
export class GeminiAgent extends BaseCliAgent<ReviewerResult> {
|
|
16
|
+
protected buildCliArgs(): string[] {
|
|
17
|
+
throw new Error("GeminiAgent not implemented");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
protected buildPrompt(_plan: string): string {
|
|
21
|
+
throw new Error("GeminiAgent not implemented");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
protected coerceResult(_obj: Record<string, unknown> | null, _raw: string, _err: string): ReviewerResult {
|
|
25
|
+
throw new Error("GeminiAgent not implemented");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
protected getCliName(): string {
|
|
29
|
+
throw new Error("GeminiAgent not implemented");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
protected makeErrorResult(type: "skip" | "error", message: string): ReviewerResult {
|
|
33
|
+
return makeResult(this.agent.name, false, type, {}, "", message);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
protected parseOutput(_raw: string, _result: unknown): Record<string, unknown> | null {
|
|
37
|
+
throw new Error("GeminiAgent not implemented");
|
|
38
|
+
}
|
|
39
|
+
}
|
package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/orchestrator-claude-agent.ts
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrator agent implementation using BaseCliAgent framework.
|
|
3
|
+
* Analyzes plan complexity and selects reviewer agents via Claude CLI.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { logDebug } from "../../../../_shared/lib-ts/base/logger.js";
|
|
7
|
+
import { shellQuoteWin } from "../../../../_shared/lib-ts/base/subprocess-utils.js";
|
|
8
|
+
import { parseCliOutput } from "../../cli-output-parser.js";
|
|
9
|
+
import type { AgentConfig, OrchestratorResult, ComplexityCategory } from "../../types.js";
|
|
10
|
+
import { BaseCliAgent } from "../base/base-agent.js";
|
|
11
|
+
import { buildOrchestratorSchema, ORCHESTRATOR_SCHEMA } from "../schemas.js";
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Constants
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
const DEFAULT_COMPLEXITY_CATEGORIES = [
|
|
18
|
+
"code",
|
|
19
|
+
"infrastructure",
|
|
20
|
+
"documentation",
|
|
21
|
+
"life",
|
|
22
|
+
"business",
|
|
23
|
+
"design",
|
|
24
|
+
"research",
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const DEFAULT_AGENT_SELECTION: Record<string, unknown> = {
|
|
28
|
+
simple: { min: 3, max: 3 },
|
|
29
|
+
medium: { min: 8, max: 8 },
|
|
30
|
+
high: { min: 12, max: 12 },
|
|
31
|
+
fallbackCount: 3,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Claude CLI-based orchestrator agent.
|
|
36
|
+
* Extends BaseCliAgent<OrchestratorResult> to reuse subprocess execution infrastructure.
|
|
37
|
+
*/
|
|
38
|
+
export class OrchestratorClaudeAgent extends BaseCliAgent<OrchestratorResult> {
|
|
39
|
+
private categories: string[];
|
|
40
|
+
private fallbackCount: number;
|
|
41
|
+
private mandatoryCount: number;
|
|
42
|
+
private nonMandatory: AgentConfig[];
|
|
43
|
+
private settings: Record<string, unknown>;
|
|
44
|
+
private validNames: string[];
|
|
45
|
+
|
|
46
|
+
constructor(
|
|
47
|
+
agent: AgentConfig,
|
|
48
|
+
agentLibrary: AgentConfig[],
|
|
49
|
+
mandatoryNames: Set<string>,
|
|
50
|
+
settings: Record<string, unknown>,
|
|
51
|
+
timeout: number,
|
|
52
|
+
contextPath?: string,
|
|
53
|
+
sessionName?: string,
|
|
54
|
+
) {
|
|
55
|
+
// Build schema dynamically based on valid agent names
|
|
56
|
+
const nonMandatory = agentLibrary.filter(
|
|
57
|
+
(a) => a.enabled && !mandatoryNames.has(a.name),
|
|
58
|
+
);
|
|
59
|
+
const validNames = nonMandatory.map((a) => a.name);
|
|
60
|
+
const categories = (settings.complexityCategories as string[]) ?? DEFAULT_COMPLEXITY_CATEGORIES;
|
|
61
|
+
|
|
62
|
+
const schema = validNames.length > 0
|
|
63
|
+
? buildOrchestratorSchema(validNames, categories)
|
|
64
|
+
: ORCHESTRATOR_SCHEMA;
|
|
65
|
+
|
|
66
|
+
super(agent, schema, timeout, contextPath, sessionName);
|
|
67
|
+
|
|
68
|
+
this.nonMandatory = nonMandatory;
|
|
69
|
+
this.validNames = validNames;
|
|
70
|
+
this.categories = categories;
|
|
71
|
+
this.settings = settings;
|
|
72
|
+
|
|
73
|
+
const selection = (settings.agentSelection as Record<string, unknown>) ?? DEFAULT_AGENT_SELECTION;
|
|
74
|
+
this.fallbackCount = (selection.fallbackCount as number) ?? 2;
|
|
75
|
+
this.mandatoryCount = agentLibrary.filter((a) => mandatoryNames.has(a.name)).length;
|
|
76
|
+
|
|
77
|
+
logDebug("orchestrator", `Mandatory agents (always run): ${[...mandatoryNames].sort().join(", ")}`);
|
|
78
|
+
logDebug("orchestrator", `Non-mandatory agents for selection: ${validNames.join(", ")}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
protected buildCliArgs(): string[] {
|
|
82
|
+
const schemaJson = JSON.stringify(this.schema);
|
|
83
|
+
|
|
84
|
+
const systemPrompt = `You are a plan orchestrator for code review. Your job is to analyze plans and select appropriate reviewer agents.
|
|
85
|
+
|
|
86
|
+
You MUST call StructuredOutput immediately with your analysis. Do NOT ask questions or use any other tools.
|
|
87
|
+
|
|
88
|
+
When selecting agents:
|
|
89
|
+
- Match agent expertise to plan requirements
|
|
90
|
+
- Consider what each agent specializes in
|
|
91
|
+
- Only select agents whose categories match the plan category
|
|
92
|
+
- Fewer agents for simple plans, more for complex plans`;
|
|
93
|
+
|
|
94
|
+
return [
|
|
95
|
+
"--model", this.agent.model,
|
|
96
|
+
"--output-format", "json",
|
|
97
|
+
"--json-schema", shellQuoteWin(schemaJson),
|
|
98
|
+
"--max-turns", "3",
|
|
99
|
+
"--setting-sources", process.platform === "win32" ? '""' : "",
|
|
100
|
+
"--system-prompt", shellQuoteWin(systemPrompt),
|
|
101
|
+
"-p",
|
|
102
|
+
];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
protected buildPrompt(plan: string): string {
|
|
106
|
+
const selection = (this.settings.agentSelection as Record<string, unknown>) ?? DEFAULT_AGENT_SELECTION;
|
|
107
|
+
|
|
108
|
+
const agentList = this.nonMandatory
|
|
109
|
+
.map(
|
|
110
|
+
(a) =>
|
|
111
|
+
`- ${a.name} [${a.categories.join(", ")}]\n Focus: ${a.focus}\n Expertise: ${a.description}`,
|
|
112
|
+
)
|
|
113
|
+
.join("\n");
|
|
114
|
+
const categoryList = this.categories.join("/");
|
|
115
|
+
|
|
116
|
+
const simpleAdditional = Math.max(0, ((selection.simple as Record<string, number> | undefined)?.max ?? 3) - this.mandatoryCount);
|
|
117
|
+
const mediumAdditional = Math.max(0, ((selection.medium as Record<string, number> | undefined)?.max ?? 8) - this.mandatoryCount);
|
|
118
|
+
const highAdditional = Math.max(0, ((selection.high as Record<string, number> | undefined)?.max ?? 12) - this.mandatoryCount);
|
|
119
|
+
|
|
120
|
+
return `Analyze this plan and select appropriate reviewer agents.
|
|
121
|
+
|
|
122
|
+
Available agents (select ONLY from this list):
|
|
123
|
+
${agentList}
|
|
124
|
+
|
|
125
|
+
Selection rules (number of ADDITIONAL agents to select from the list above):
|
|
126
|
+
- simple complexity = ${simpleAdditional} agents
|
|
127
|
+
- medium complexity = ${mediumAdditional} agents
|
|
128
|
+
- high complexity = ${highAdditional} agents
|
|
129
|
+
- Only select agents whose categories match the plan category (${categoryList})
|
|
130
|
+
- Non-technical plans (life, business) typically need 0 code-focused agents
|
|
131
|
+
- Note: mandatory agents run separately and are NOT listed above
|
|
132
|
+
|
|
133
|
+
PLAN:
|
|
134
|
+
<<<
|
|
135
|
+
${plan}
|
|
136
|
+
>>>
|
|
137
|
+
|
|
138
|
+
Call StructuredOutput now with: complexity, category, selectedAgents, reasoning`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
protected coerceResult(obj: Record<string, unknown> | null, _raw: string, _err: string): OrchestratorResult {
|
|
142
|
+
if (!obj) {
|
|
143
|
+
return this.makeFallback("Orchestrator output could not be parsed", "Failed to parse orchestrator output");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Extract and validate fields
|
|
147
|
+
const rawComplexity = String(obj.complexity ?? "medium");
|
|
148
|
+
const complexity: ComplexityCategory =
|
|
149
|
+
rawComplexity === "simple" || rawComplexity === "medium" || rawComplexity === "high"
|
|
150
|
+
? rawComplexity
|
|
151
|
+
: "medium";
|
|
152
|
+
|
|
153
|
+
let category = (obj.category as string) ?? "code";
|
|
154
|
+
if (!this.categories.includes(category)) category = "code";
|
|
155
|
+
|
|
156
|
+
let {selectedAgents} = obj;
|
|
157
|
+
if (!Array.isArray(selectedAgents)) selectedAgents = [];
|
|
158
|
+
|
|
159
|
+
const reasoning = String(obj.reasoning ?? "").trim() || "No reasoning provided";
|
|
160
|
+
const skipReason = obj.skipReason as string | undefined;
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
complexity,
|
|
164
|
+
category,
|
|
165
|
+
selected_agents: selectedAgents as string[],
|
|
166
|
+
reasoning,
|
|
167
|
+
skip_reason: skipReason || undefined,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
protected getCliName(): string {
|
|
172
|
+
return "claude";
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
protected makeErrorResult(type: "skip" | "error", message: string): OrchestratorResult {
|
|
176
|
+
return this.makeFallback(
|
|
177
|
+
type === "skip" ? `Orchestrator skipped - ${message}` : message,
|
|
178
|
+
message,
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
protected parseOutput(raw: string, _result: unknown): Record<string, unknown> | null {
|
|
183
|
+
return parseCliOutput(raw);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private makeFallback(reasoning: string, error: string): OrchestratorResult {
|
|
187
|
+
return {
|
|
188
|
+
complexity: "medium",
|
|
189
|
+
category: "code",
|
|
190
|
+
selected_agents: this.nonMandatory.slice(0, this.fallbackCount).map((a) => a.name),
|
|
191
|
+
reasoning,
|
|
192
|
+
error,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON schemas and prompt constants for plan reviewers.
|
|
3
|
+
* Centralized schema definitions used by Claude/Codex/Gemini agents and orchestrator.
|
|
4
|
+
* See cc-native-plan-review-spec.md §4.10
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Prompt Constants
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
/** Prefix for agent review prompts (embedded in stdin or --system-prompt) */
|
|
12
|
+
export const AGENT_REVIEW_PROMPT_PREFIX = `# SINGLE-TURN PLAN REVIEW
|
|
13
|
+
|
|
14
|
+
## CRITICAL: ONE TURN ONLY
|
|
15
|
+
You have exactly ONE response to complete this review. Do NOT attempt multi-step workflows, context queries, or phased analysis. Analyze the plan and output your review immediately.
|
|
16
|
+
|
|
17
|
+
## YOUR TASK
|
|
18
|
+
Review the plan below from your area of expertise. Then call StructuredOutput with your assessment.
|
|
19
|
+
|
|
20
|
+
## REQUIRED OUTPUT (all fields must have content)
|
|
21
|
+
Call StructuredOutput with:
|
|
22
|
+
- **verdict**: "pass" (no concerns), "warn" (some concerns), or "fail" (critical issues)
|
|
23
|
+
- **summary**: 2-3 sentences with your overall assessment and key findings (REQUIRED)
|
|
24
|
+
- **issues**: Array of concerns found. Format each as:
|
|
25
|
+
{"severity": "high/medium/low", "category": "...", "issue": "...", "suggested_fix": "...", "dimension": "..."}
|
|
26
|
+
- **dimension**: Classify each issue into exactly one dimension:
|
|
27
|
+
completeness, simplicity, security, performance, reliability,
|
|
28
|
+
maintainability, testability, scope, feasibility, or clarity.
|
|
29
|
+
Examples: "missing error handling" → reliability, "excessive abstraction" → simplicity,
|
|
30
|
+
"no test strategy" → testability, "missing deployment steps" → completeness,
|
|
31
|
+
"unclear interaction between components" → clarity.
|
|
32
|
+
- **missing_sections**: Topics the plan should address but doesn't
|
|
33
|
+
- **questions**: Things that need clarification before implementation
|
|
34
|
+
|
|
35
|
+
## IMPORTANT RULES
|
|
36
|
+
1. A "warn" verdict MUST include at least one issue explaining why
|
|
37
|
+
2. Summary MUST explain your reasoning, not just "looks good" or empty
|
|
38
|
+
3. Focus on your expertise area (architecture, security, performance, etc.)
|
|
39
|
+
4. Output StructuredOutput NOW - no other tools, no questions, no delays
|
|
40
|
+
5. Return ONLY your top 3 most critical issues. Prioritize high-severity over medium/low. Quality over quantity.
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
/** Prefix for Codex/Gemini review prompts (legacy, may be deprecated) */
|
|
44
|
+
export const REVIEW_PROMPT_PREFIX = `You are a senior staff software engineer acting as a strict plan reviewer.
|
|
45
|
+
|
|
46
|
+
Review the PLAN below. Focus on:
|
|
47
|
+
- missing steps, unclear assumptions, edge cases
|
|
48
|
+
- security/privacy concerns
|
|
49
|
+
- testing/rollout/rollback completeness
|
|
50
|
+
- operational concerns (observability, failure modes)
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// JSON Schemas
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
/** JSON schema for review structured output (agents: Claude, Codex, Gemini) */
|
|
58
|
+
export const REVIEW_SCHEMA: Record<string, unknown> = {
|
|
59
|
+
type: "object",
|
|
60
|
+
properties: {
|
|
61
|
+
verdict: { type: "string", enum: ["pass", "warn", "fail"] },
|
|
62
|
+
summary: { type: "string", minLength: 20 },
|
|
63
|
+
issues: {
|
|
64
|
+
type: "array",
|
|
65
|
+
items: {
|
|
66
|
+
type: "object",
|
|
67
|
+
properties: {
|
|
68
|
+
severity: { type: "string", enum: ["high", "medium", "low"] },
|
|
69
|
+
category: { type: "string" },
|
|
70
|
+
issue: { type: "string" },
|
|
71
|
+
suggested_fix: { type: "string" },
|
|
72
|
+
dimension: {
|
|
73
|
+
type: "string",
|
|
74
|
+
enum: [
|
|
75
|
+
"completeness", "simplicity", "security", "performance",
|
|
76
|
+
"reliability", "maintainability", "testability", "scope",
|
|
77
|
+
"feasibility", "clarity",
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
required: ["severity", "category", "issue", "suggested_fix", "dimension"],
|
|
82
|
+
additionalProperties: false,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
missing_sections: { type: "array", items: { type: "string" } },
|
|
86
|
+
questions: { type: "array", items: { type: "string" } },
|
|
87
|
+
},
|
|
88
|
+
required: ["verdict", "summary", "issues", "missing_sections", "questions"],
|
|
89
|
+
additionalProperties: false,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Build orchestrator JSON schema with enum-constrained agent names.
|
|
94
|
+
* Dynamic schema that restricts selectedAgents to valid agent names.
|
|
95
|
+
*/
|
|
96
|
+
export function buildOrchestratorSchema(
|
|
97
|
+
validAgentNames: string[],
|
|
98
|
+
categories: string[],
|
|
99
|
+
): Record<string, unknown> {
|
|
100
|
+
const itemsSchema: Record<string, unknown> = { type: "string" };
|
|
101
|
+
if (validAgentNames.length > 0) {
|
|
102
|
+
itemsSchema.enum = validAgentNames;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
type: "object",
|
|
107
|
+
properties: {
|
|
108
|
+
complexity: { type: "string", enum: ["simple", "medium", "high"] },
|
|
109
|
+
category: { type: "string", enum: categories },
|
|
110
|
+
selectedAgents: {
|
|
111
|
+
type: "array",
|
|
112
|
+
items: itemsSchema,
|
|
113
|
+
},
|
|
114
|
+
reasoning: { type: "string" },
|
|
115
|
+
skipReason: { type: "string" },
|
|
116
|
+
},
|
|
117
|
+
required: ["complexity", "category", "selectedAgents", "reasoning"],
|
|
118
|
+
additionalProperties: false,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// Plan Questions Schema
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
/** Prefix for plan question generation prompts */
|
|
127
|
+
export const QUESTIONS_PROMPT_PREFIX = `# PLAN QUESTION GENERATION
|
|
128
|
+
|
|
129
|
+
## CRITICAL: ONE TURN ONLY
|
|
130
|
+
You have exactly ONE response. Do NOT attempt multi-step workflows or tool use beyond StructuredOutput.
|
|
131
|
+
|
|
132
|
+
## YOUR TASK
|
|
133
|
+
You are reviewing a plan that was written by another agent. You have NO access to the codebase, NO session history, and NO exploration context. You see ONLY the plan text.
|
|
134
|
+
|
|
135
|
+
This is intentional. Plans must be executable by a fresh agent in a new session. If the plan assumes knowledge that isn't written down, that's a gap.
|
|
136
|
+
|
|
137
|
+
## WHAT TO LOOK FOR
|
|
138
|
+
- Questions the plan doesn't answer but should
|
|
139
|
+
- Assumptions the plan makes without stating them
|
|
140
|
+
- Ambiguities where a reader could interpret something two ways
|
|
141
|
+
- Missing context that would be obvious to the author but not a new reader
|
|
142
|
+
|
|
143
|
+
## IMPORTANT
|
|
144
|
+
- Focus on questions that would change the implementation approach if answered differently
|
|
145
|
+
- Don't ask about things clearly stated in the plan
|
|
146
|
+
- Don't generate generic questions — every question should be specific to THIS plan
|
|
147
|
+
- Aim for 3-6 high-value questions
|
|
148
|
+
`;
|
|
149
|
+
|
|
150
|
+
/** JSON schema for plan question generation output */
|
|
151
|
+
export const QUESTIONS_SCHEMA: Record<string, unknown> = {
|
|
152
|
+
type: "object",
|
|
153
|
+
properties: {
|
|
154
|
+
questions: {
|
|
155
|
+
type: "array",
|
|
156
|
+
items: { type: "string" },
|
|
157
|
+
description: "Questions the user should answer before this plan is implemented",
|
|
158
|
+
},
|
|
159
|
+
assumptions: {
|
|
160
|
+
type: "array",
|
|
161
|
+
items: { type: "string" },
|
|
162
|
+
description: "Assumptions the plan makes that are not explicitly stated",
|
|
163
|
+
},
|
|
164
|
+
ambiguities: {
|
|
165
|
+
type: "array",
|
|
166
|
+
items: { type: "string" },
|
|
167
|
+
description: "Parts of the plan that could be interpreted multiple ways",
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
required: ["questions", "assumptions", "ambiguities"],
|
|
171
|
+
additionalProperties: false,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
// Orchestrator Schemas
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
|
|
178
|
+
/** JSON schema for orchestrator structured output (static fallback) */
|
|
179
|
+
export const ORCHESTRATOR_SCHEMA: Record<string, unknown> = {
|
|
180
|
+
type: "object",
|
|
181
|
+
properties: {
|
|
182
|
+
complexity: { type: "string", enum: ["simple", "medium", "high"] },
|
|
183
|
+
category: {
|
|
184
|
+
type: "string",
|
|
185
|
+
enum: [
|
|
186
|
+
"code",
|
|
187
|
+
"infrastructure",
|
|
188
|
+
"documentation",
|
|
189
|
+
"life",
|
|
190
|
+
"business",
|
|
191
|
+
"design",
|
|
192
|
+
"research",
|
|
193
|
+
],
|
|
194
|
+
},
|
|
195
|
+
selectedAgents: { type: "array", items: { type: "string" } },
|
|
196
|
+
reasoning: { type: "string" },
|
|
197
|
+
skipReason: { type: "string" },
|
|
198
|
+
},
|
|
199
|
+
required: ["complexity", "category", "selectedAgents", "reasoning"],
|
|
200
|
+
additionalProperties: false,
|
|
201
|
+
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* See cc-native-plan-review-spec.md §4.9
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { ReviewerResult, Verdict, ReviewData } from "../types.js";
|
|
7
7
|
|
|
8
8
|
// Re-export for convenience
|
|
9
9
|
|
|
@@ -13,7 +13,7 @@ export function makeResult(
|
|
|
13
13
|
name: string,
|
|
14
14
|
ok: boolean,
|
|
15
15
|
verdict: Verdict,
|
|
16
|
-
data: Record<string, unknown
|
|
16
|
+
data: ReviewData | Record<string, unknown>,
|
|
17
17
|
raw: string,
|
|
18
18
|
err: string,
|
|
19
19
|
): ReviewerResult {
|