aiwcli 0.13.8 → 0.15.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.
Files changed (47) hide show
  1. package/README.md +11 -1
  2. package/dist/commands/launch.d.ts +8 -0
  3. package/dist/commands/launch.js +96 -5
  4. package/dist/templates/_shared/.claude/skills/codex/SKILL.md +42 -0
  5. package/dist/templates/_shared/.claude/skills/codex/prompt.md +30 -0
  6. package/dist/templates/_shared/lib-ts/agent-exec/backends/headless.ts +33 -0
  7. package/dist/templates/_shared/lib-ts/agent-exec/backends/index.ts +6 -0
  8. package/dist/templates/_shared/lib-ts/agent-exec/backends/tmux.ts +145 -0
  9. package/dist/templates/_shared/lib-ts/agent-exec/base-agent.ts +229 -0
  10. package/dist/templates/_shared/lib-ts/agent-exec/execution-backend.ts +50 -0
  11. package/dist/templates/_shared/lib-ts/agent-exec/index.ts +6 -0
  12. package/dist/templates/_shared/lib-ts/agent-exec/structured-output.ts +166 -0
  13. package/dist/templates/_shared/lib-ts/base/cli-args.ts +287 -0
  14. package/dist/templates/_shared/lib-ts/base/inference.ts +53 -47
  15. package/dist/templates/_shared/lib-ts/base/models.ts +16 -0
  16. package/dist/templates/_shared/lib-ts/base/preflight.ts +98 -0
  17. package/dist/templates/_shared/lib-ts/base/state-io.ts +1 -1
  18. package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +4 -3
  19. package/dist/templates/_shared/lib-ts/base/tmux-driver.ts +381 -0
  20. package/dist/templates/_shared/lib-ts/base/utils.ts +8 -0
  21. package/dist/templates/_shared/lib-ts/context/context-formatter.ts +35 -11
  22. package/dist/templates/_shared/lib-ts/context/context-store.ts +3 -0
  23. package/dist/templates/_shared/lib-ts/types.ts +17 -0
  24. package/dist/templates/_shared/scripts/status_line.ts +93 -47
  25. package/dist/templates/_shared/skills/prompt-codex/CLAUDE.md +71 -0
  26. package/dist/templates/_shared/skills/prompt-codex/scripts/launch-codex.ts +387 -0
  27. package/dist/templates/_shared/skills/prompt-codex/scripts/watch-codex.ts +257 -0
  28. package/dist/templates/cc-native/.claude/settings.json +121 -1
  29. package/dist/templates/cc-native/_cc-native/CLAUDE.md +73 -0
  30. package/dist/templates/cc-native/_cc-native/lib-ts/CLAUDE.md +70 -0
  31. package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +9 -133
  32. package/dist/templates/cc-native/_cc-native/lib-ts/settings.ts +120 -43
  33. package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +1 -0
  34. package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +66 -12
  35. package/dist/templates/cc-native/_cc-native/plan-review/lib/agent-selection.ts +5 -4
  36. package/dist/templates/cc-native/_cc-native/plan-review/lib/orchestrator.ts +4 -4
  37. package/dist/templates/cc-native/_cc-native/plan-review/lib/preflight.ts +14 -80
  38. package/dist/templates/cc-native/_cc-native/plan-review/lib/review-pipeline.ts +16 -13
  39. package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/agent.ts +19 -7
  40. package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/base/base-agent.ts +4 -215
  41. package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/index.ts +1 -1
  42. package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/claude-agent.ts +9 -39
  43. package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/codex-agent.ts +19 -22
  44. package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/gemini-agent.ts +2 -1
  45. package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/orchestrator-claude-agent.ts +65 -36
  46. package/oclif.manifest.json +21 -3
  47. package/package.json +1 -1
@@ -178,6 +178,7 @@ export function getIterationState(
178
178
  passStreaks: {},
179
179
  lastPlanHash: "",
180
180
  lastPlanPath: "",
181
+ sessionId: "",
181
182
  };
182
183
  }
183
184
 
@@ -7,6 +7,10 @@
7
7
  // Re-export shared types used by cc-native consumers
8
8
  export type { ContextState, HookInput, HookOutput } from "../../_shared/lib-ts/types.js";
9
9
 
10
+ // Import AgentConfig for local use and re-export
11
+ import type { AgentConfig as _AgentConfig } from "../../_shared/lib-ts/types.js";
12
+ export type AgentConfig = _AgentConfig;
13
+
10
14
  // ---------------------------------------------------------------------------
11
15
  // Verdict & Decision Types
12
16
  // ---------------------------------------------------------------------------
@@ -118,22 +122,13 @@ export interface ReviewDecisionResult {
118
122
  // Agent & Orchestrator Configuration
119
123
  // ---------------------------------------------------------------------------
120
124
 
121
- /** Configuration for a Claude Code review agent */
122
- export interface AgentConfig {
123
- name: string;
124
- model: string;
125
- provider: string; // e.g. "claude" | "codex" — assigned at runtime by assignModelsToAgents()
126
- focus: string;
127
- categories: string[];
128
- description: string;
129
- system_prompt: string; // Markdown body content for --system-prompt
130
- reasoningEffort?: string; // e.g. "low", "medium", "high" — passed to codex -c model_reasoning_effort
131
- }
125
+ // AgentConfig re-exported from _shared/lib-ts/types.ts above
132
126
 
133
127
  /** Configuration for the plan orchestrator */
134
128
  export interface OrchestratorConfig {
135
129
  enabled: boolean;
136
130
  model: string;
131
+ provider?: string;
137
132
  timeout: number;
138
133
  }
139
134
 
@@ -141,7 +136,6 @@ export interface OrchestratorConfig {
141
136
  export interface ProviderConfig {
142
137
  enabled: boolean;
143
138
  models: string[];
144
- reasoningEffort?: string; // e.g. "low", "medium", "high" — codex model_reasoning_effort
145
139
  }
146
140
 
147
141
  /** Model provider pool configuration */
@@ -149,6 +143,66 @@ export interface ModelsConfig {
149
143
  providers: Record<string, ProviderConfig>;
150
144
  }
151
145
 
146
+ // ---------------------------------------------------------------------------
147
+ // Settings Interfaces (typed output of loadSettings())
148
+ // ---------------------------------------------------------------------------
149
+
150
+ /** Agent selection count range for a single complexity tier */
151
+ export interface AgentSelectionRange {
152
+ min: number;
153
+ max: number;
154
+ }
155
+
156
+ /** Agent selection configuration (per-tier ranges + fallback) */
157
+ export interface AgentSelectionConfig {
158
+ simple?: AgentSelectionRange;
159
+ medium?: AgentSelectionRange;
160
+ high?: AgentSelectionRange;
161
+ fallbackCount?: number;
162
+ }
163
+
164
+ /** Preflight health-check configuration */
165
+ export interface PreflightSettings {
166
+ enabled?: boolean;
167
+ timeoutMs?: number;
168
+ }
169
+
170
+ /** Plan review section of merged settings (the "planReview" key) */
171
+ export interface PlanReviewSettings {
172
+ enabled?: boolean;
173
+ reviewers?: {
174
+ codex?: { enabled?: boolean; model?: string; timeout?: number };
175
+ gemini?: { enabled?: boolean; model?: string; timeout?: number };
176
+ };
177
+ display?: Partial<DisplaySettings>;
178
+ }
179
+
180
+ /** Agent review section of merged settings (the "agentReview" key) */
181
+ export interface AgentReviewSettings {
182
+ enabled?: boolean;
183
+ timeout?: number;
184
+ orchestrator?: OrchestratorConfig;
185
+ legacyMode?: boolean;
186
+ highIssueThreshold?: number;
187
+ maxIssuesPerAgent?: number;
188
+ mandatoryAgents?: string[] | Record<string, string[]>;
189
+ agentSelection?: AgentSelectionConfig;
190
+ agentDefaults?: { model?: string };
191
+ complexityCategories?: string[];
192
+ sanitization?: { maxSessionIdLength?: number; maxTitleLength?: number };
193
+ reviewIterations?: Record<string, number>;
194
+ display?: Partial<DisplaySettings>;
195
+ preflight?: PreflightSettings;
196
+ fallbackByComplexity?: Record<string, number>;
197
+ }
198
+
199
+ /** Top-level settings object returned by loadSettings() */
200
+ export interface LoadedSettings {
201
+ planReview: PlanReviewSettings;
202
+ agentReview: AgentReviewSettings;
203
+ models: Record<string, unknown>;
204
+ }
205
+
152
206
  // ---------------------------------------------------------------------------
153
207
  // State Interfaces
154
208
  // ---------------------------------------------------------------------------
@@ -7,6 +7,7 @@ import { logDebug, logInfo, logWarn } from "../../../_shared/lib-ts/base/logger.
7
7
  import { findExecutable } from "../../../_shared/lib-ts/base/subprocess-utils.js";
8
8
  import type {
9
9
  AgentConfig,
10
+ AgentReviewSettings,
10
11
  ModelsConfig,
11
12
  OrchestratorResult,
12
13
  AgentSelectionResult,
@@ -89,7 +90,7 @@ export function assignModelsToAgents(
89
90
  }
90
91
  return [name, config] as [string, typeof config];
91
92
  })
92
- .filter((entry): entry is [string, { enabled: boolean; models: string[]; reasoningEffort?: string }] => entry !== null);
93
+ .filter((entry): entry is [string, { enabled: boolean; models: string[] }] => entry !== null);
93
94
 
94
95
  // Sort by provider priority (codex first)
95
96
  enabledProviders.sort((a, b) => {
@@ -110,7 +111,7 @@ export function assignModelsToAgents(
110
111
  return agents.map(agent => {
111
112
  const modelIdx = Math.floor(Math.random() * providerConfig.models.length);
112
113
  const model = providerConfig.models[modelIdx] ?? providerConfig.models[0] ?? agent.model;
113
- return { ...agent, provider: providerName, model, reasoningEffort: providerConfig.reasoningEffort };
114
+ return { ...agent, provider: providerName, model };
114
115
  });
115
116
  }
116
117
 
@@ -122,7 +123,7 @@ export interface AgentSelectionInput {
122
123
  enabledAgents: AgentConfig[];
123
124
  orchResult: OrchestratorResult | null;
124
125
  mandatoryConfig: unknown;
125
- agentSettings: Record<string, unknown>;
126
+ agentSettings: AgentReviewSettings;
126
127
  legacyMode: boolean;
127
128
  }
128
129
 
@@ -168,7 +169,7 @@ export function selectAgents(input: AgentSelectionInput): AgentSelectionResult {
168
169
  }
169
170
 
170
171
  // Enforce minimum agent count
171
- const fallbackByComplexity = agentSettings.fallbackByComplexity ?? { simple: 0, medium: 2, high: 4 };
172
+ const fallbackByComplexity: Record<string, number> = agentSettings.fallbackByComplexity ?? { simple: 0, medium: 2, high: 4 };
172
173
  const minAdditional = fallbackByComplexity[detectedComplexity] ?? 5;
173
174
  if (orchSelected.length < minAdditional && nonMandatory.length > 0) {
174
175
  const remaining = nonMandatory.filter(a => !orchSelected.includes(a));
@@ -6,7 +6,7 @@
6
6
 
7
7
  import { OrchestratorClaudeAgent } from "./reviewers/providers/orchestrator-claude-agent.js";
8
8
  import { logInfo, logWarn } from "../../../_shared/lib-ts/base/logger.js";
9
- import type { AgentConfig, OrchestratorConfig, OrchestratorResult } from "../../lib-ts/types.js";
9
+ import type { AgentConfig, AgentReviewSettings, OrchestratorConfig, OrchestratorResult } from "../../lib-ts/types.js";
10
10
 
11
11
  // Re-export for backward compatibility (moved to reviewers/schemas.ts)
12
12
  export { buildOrchestratorSchema } from "./reviewers/schemas.js";
@@ -23,7 +23,7 @@ export async function runOrchestrator(
23
23
  plan: string,
24
24
  agentLibrary: AgentConfig[],
25
25
  config: OrchestratorConfig,
26
- settings: Record<string, unknown>,
26
+ settings: AgentReviewSettings,
27
27
  mandatoryNames?: Set<string>,
28
28
  ): Promise<OrchestratorResult> {
29
29
  logInfo("orchestrator", "Starting plan analysis...");
@@ -34,7 +34,7 @@ export async function runOrchestrator(
34
34
  const orchestratorAgent: AgentConfig = {
35
35
  name: "orchestrator",
36
36
  model: config.model,
37
- provider: "claude",
37
+ provider: config.provider ?? "claude",
38
38
  focus: "plan analysis and agent selection",
39
39
  categories: [],
40
40
  description: "Plan orchestrator",
@@ -58,7 +58,7 @@ export async function runOrchestrator(
58
58
  } catch (error) {
59
59
  logWarn("orchestrator", `Unexpected error: ${error}`);
60
60
  const nonMandatory = agentLibrary.filter((a) => !mandatory.has(a.name));
61
- const fallbackCount = ((settings.agentSelection as Record<string, unknown>)?.fallbackCount as number) ?? 2;
61
+ const fallbackCount = settings.agentSelection?.fallbackCount ?? 2;
62
62
  return {
63
63
  complexity: "medium",
64
64
  category: "code",
@@ -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 { logInfo, logWarn, logDebug } from "../../../_shared/lib-ts/base/logger.js";
8
- import { findExecutable, execFileAsync, getInternalSubprocessEnv } from "../../../_shared/lib-ts/base/subprocess-utils.js";
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: { cliName: "claude", buildArgs: claudePreflightArgs, input: CLAUDE_PREFLIGHT_INPUT },
28
- codex: { cliName: "codex", buildArgs: codexPreflightArgs, input: CODEX_PREFLIGHT_INPUT },
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 }) => checkProviderModel(provider, model, effectiveTimeout)),
67
+ checks.map(({ provider, model }) =>
68
+ checkProviderModel(provider, model, PREFLIGHT_COMMANDS[provider]!, effectiveTimeout, HOOK),
69
+ ),
136
70
  );
137
71
 
138
72
  // Build available map
@@ -43,7 +43,10 @@ import { loadSettings, loadModelsConfig, loadAgentLibrary, DEFAULT_ORCHESTRATOR
43
43
  import { DEFAULT_REVIEW_ITERATIONS, loadIterationState, saveIterationState } from "../../lib-ts/state.js";
44
44
  import type {
45
45
  AgentConfig,
46
+ AgentReviewSettings,
47
+ LoadedSettings,
46
48
  OrchestratorConfig,
49
+ PlanReviewSettings,
47
50
  ReviewerResult,
48
51
  CombinedReviewResult,
49
52
  OrchestratorResult,
@@ -66,7 +69,7 @@ function getActiveContextForReview(sessionId: string, projectRoot: string): Cont
66
69
  return ctx;
67
70
  }
68
71
  const allActive = getAllContexts("active", projectRoot);
69
- const planning = allActive.filter(c => c.mode === "active" || c.mode === "has_plan");
72
+ const planning = allActive.filter(c => c.mode === "active" || c.mode === "has_staged_work");
70
73
  if (planning.length === 1) {
71
74
  logInfo(HOOK, `Found single planning context: ${planning[0]!.id}`);
72
75
  return planning[0]!;
@@ -89,9 +92,9 @@ export async function runReviewPipeline(input: PipelineInput): Promise<PipelineR
89
92
  const { sessionId, base, aiwcliDir, transcriptPath, payload } = input;
90
93
 
91
94
  // 1. Load settings
92
- const settings = loadSettings(aiwcliDir);
93
- const planSettings = settings.planReview ?? {};
94
- const agentSettings = settings.agentReview ?? {};
95
+ const settings: LoadedSettings = loadSettings(aiwcliDir);
96
+ const planSettings: PlanReviewSettings = settings.planReview;
97
+ const agentSettings: AgentReviewSettings = settings.agentReview;
95
98
 
96
99
  const planReviewEnabled = planSettings.enabled ?? true;
97
100
  const agentReviewEnabled = agentSettings.enabled ?? true;
@@ -162,8 +165,8 @@ export async function runReviewPipeline(input: PipelineInput): Promise<PipelineR
162
165
  // 5. Questions gate
163
166
  if (!wasPlanQuestionsAgentAsked(sessionId, base)) {
164
167
  logInfo(HOOK, "Questions gate: plan-questions agent has not run yet, running now");
165
- const timeout = typeof agentSettings.timeout === "number" ? agentSettings.timeout : 120;
166
- const questionsResult = await runPlanQuestions(plan, aiwcliDir, timeout, undefined, sessionId);
168
+ const questionsTimeout = agentSettings.timeout ?? 120;
169
+ const questionsResult = await runPlanQuestions(plan, aiwcliDir, questionsTimeout, undefined, sessionId);
167
170
 
168
171
  markQuestionsAsked(sessionId, base, "agent");
169
172
 
@@ -227,12 +230,12 @@ export async function runReviewPipeline(input: PipelineInput): Promise<PipelineR
227
230
  let detectedComplexity = "medium";
228
231
 
229
232
  // Preflight: validate provider+model combos before committing agents or orchestrator
230
- const preflightEnabled = (agentSettings.preflight as Record<string, unknown>)?.enabled ?? true;
233
+ const preflightEnabled = agentSettings.preflight?.enabled ?? true;
231
234
  let preflightAvailable: Map<string, Set<string>> | undefined;
232
235
 
233
236
  if (preflightEnabled && agentReviewEnabled) {
234
237
  logInfo(HOOK, "=== PREFLIGHT: Checking provider availability ===");
235
- const preflightTimeoutMs = (agentSettings.preflight as Record<string, unknown>)?.timeoutMs as number | undefined;
238
+ const preflightTimeoutMs = agentSettings.preflight?.timeoutMs;
236
239
  const modelsConfig = loadModelsConfig(settings);
237
240
  const preflightReport = await runPreflight(modelsConfig, preflightTimeoutMs);
238
241
 
@@ -257,13 +260,14 @@ export async function runReviewPipeline(input: PipelineInput): Promise<PipelineR
257
260
  const agentLibrary = agentReviewEnabled ? loadAgentLibrary(aiwcliDir, agentSettings) : [];
258
261
  const originalAgentCount = agentLibrary.length;
259
262
  const enabledAgents = agentLibrary.filter(a => !graduatedSet.has(a.name));
260
- const timeout = typeof agentSettings.timeout === "number" ? agentSettings.timeout : 120;
263
+ const timeout = agentSettings.timeout ?? 120;
261
264
  const legacyMode = agentSettings.legacyMode === true;
262
265
 
263
266
  const orchSettings = agentSettings.orchestrator ?? DEFAULT_ORCHESTRATOR;
264
267
  const orchestratorConfig: OrchestratorConfig = {
265
268
  enabled: (orchSettings.enabled ?? true) && agentReviewEnabled,
266
269
  model: orchSettings.model ?? "haiku",
270
+ provider: orchSettings.provider,
267
271
  timeout: orchSettings.timeout ?? 30,
268
272
  };
269
273
 
@@ -278,7 +282,7 @@ export async function runReviewPipeline(input: PipelineInput): Promise<PipelineR
278
282
 
279
283
  if (orchestratorConfig.enabled && enabledAgents.length > 0 && !legacyMode) {
280
284
  // Guard orchestrator against preflight failures (always uses claude provider)
281
- const orchProvider = "claude";
285
+ const orchProvider = orchestratorConfig.provider ?? "claude";
282
286
  const orchModel = orchestratorConfig.model;
283
287
  const orchPassed = !preflightAvailable ||
284
288
  (preflightAvailable.has(orchProvider) && preflightAvailable.get(orchProvider)!.has(orchModel));
@@ -395,8 +399,7 @@ export async function runReviewPipeline(input: PipelineInput): Promise<PipelineR
395
399
  }
396
400
 
397
401
  // 10. Issue truncation + verdict override
398
- const maxIssuesPerAgent = typeof agentSettings.maxIssuesPerAgent === "number"
399
- ? agentSettings.maxIssuesPerAgent : 3;
402
+ const maxIssuesPerAgent = agentSettings.maxIssuesPerAgent ?? 3;
400
403
  truncateAgentIssues(agentResults, maxIssuesPerAgent);
401
404
 
402
405
  const passEligible = computePassEligible(agentResults);
@@ -404,7 +407,7 @@ export async function runReviewPipeline(input: PipelineInput): Promise<PipelineR
404
407
  logInfo(HOOK, `Pass-eligible agents this iteration: ${passEligible.join(", ")}`);
405
408
  }
406
409
 
407
- const highIssueThreshold = typeof agentSettings.highIssueThreshold === "number" ? agentSettings.highIssueThreshold : 3;
410
+ const highIssueThreshold = agentSettings.highIssueThreshold ?? 3;
408
411
  overrideVerdictsByThreshold(agentResults, highIssueThreshold);
409
412
 
410
413
  // PHASE 4: Generate Output
@@ -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(private agent: AgentConfig) {}
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
- let reviewer;
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(agent, schema, timeout, contextPath, sessionName);
67
+ reviewer = new CodexAgent(config, backend);
56
68
  break;
57
69
  }
58
70
  case "gemini": {
59
- reviewer = new GeminiAgent(agent, schema, timeout, contextPath, sessionName);
71
+ reviewer = new GeminiAgent(config, backend);
60
72
  break;
61
73
  }
62
74
  default: {
63
- reviewer = new ClaudeAgent(agent, schema, timeout, contextPath, sessionName);
75
+ reviewer = new ClaudeAgent(config, backend);
64
76
  break;
65
77
  }
66
78
  }