aiwcli 0.14.0 → 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.
- package/dist/templates/_shared/.claude/skills/codex/prompt.md +25 -5
- package/dist/templates/_shared/lib-ts/agent-exec/index.ts +2 -0
- package/dist/templates/_shared/lib-ts/agent-exec/structured-output.ts +166 -0
- package/dist/templates/_shared/lib-ts/base/cli-args.ts +4 -0
- package/dist/templates/_shared/lib-ts/base/state-io.ts +1 -1
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +4 -3
- package/dist/templates/_shared/lib-ts/context/context-store.ts +3 -0
- package/dist/templates/_shared/scripts/status_line.ts +36 -19
- package/dist/templates/_shared/skills/prompt-codex/CLAUDE.md +29 -4
- package/dist/templates/_shared/skills/prompt-codex/scripts/launch-codex.ts +140 -7
- package/dist/templates/_shared/skills/prompt-codex/scripts/watch-codex.ts +257 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +9 -133
- package/dist/templates/cc-native/_cc-native/lib-ts/settings.ts +118 -42
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +1 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +61 -0
- package/dist/templates/cc-native/_cc-native/plan-review/lib/agent-selection.ts +5 -4
- package/dist/templates/cc-native/_cc-native/plan-review/lib/orchestrator.ts +4 -4
- package/dist/templates/cc-native/_cc-native/plan-review/lib/review-pipeline.ts +16 -13
- package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/orchestrator-claude-agent.ts +54 -23
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
|
@@ -11,8 +11,12 @@ import { loadConfig, getDisplaySettings } from "./config.js";
|
|
|
11
11
|
import { DEFAULT_REVIEW_ITERATIONS } from "./state.js";
|
|
12
12
|
import type {
|
|
13
13
|
AgentConfig,
|
|
14
|
-
|
|
14
|
+
AgentReviewSettings,
|
|
15
|
+
AgentSelectionConfig,
|
|
16
|
+
LoadedSettings,
|
|
15
17
|
ModelsConfig,
|
|
18
|
+
PlanReviewSettings,
|
|
19
|
+
ProviderConfig,
|
|
16
20
|
} from "./types.js";
|
|
17
21
|
import { DEFAULT_DISPLAY, DEFAULT_SANITIZATION } from "./types.js";
|
|
18
22
|
import { logInfo } from "../../_shared/lib-ts/base/logger.js";
|
|
@@ -57,10 +61,10 @@ export const DEFAULT_AGENTS: Array<{ name: string; model: string; provider: stri
|
|
|
57
61
|
{ ...AGENT_DEFAULTS, name: "constraint-validator", focus: "constraint identification and satisfaction", categories: ALL_CATEGORIES },
|
|
58
62
|
];
|
|
59
63
|
|
|
60
|
-
export const DEFAULT_ORCHESTRATOR
|
|
64
|
+
export const DEFAULT_ORCHESTRATOR = { enabled: true, model: CODEX_MODELS.codex, provider: "codex", timeout: 60 } as const;
|
|
61
65
|
export const DEFAULT_AGENT_MODEL = "sonnet";
|
|
62
66
|
|
|
63
|
-
export const DEFAULT_AGENT_SELECTION:
|
|
67
|
+
export const DEFAULT_AGENT_SELECTION: AgentSelectionConfig = {
|
|
64
68
|
simple: { min: 3, max: 3 },
|
|
65
69
|
medium: { min: 5, max: 5 },
|
|
66
70
|
high: { min: 7, max: 7 },
|
|
@@ -80,58 +84,131 @@ export const DEFAULT_MODELS_CONFIG: ModelsConfig = {
|
|
|
80
84
|
// Settings Loading
|
|
81
85
|
// ---------------------------------------------------------------------------
|
|
82
86
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
agentDefaults: { model: DEFAULT_AGENT_MODEL },
|
|
102
|
-
complexityCategories: [...DEFAULT_COMPLEXITY_CATEGORIES],
|
|
103
|
-
sanitization: { ...DEFAULT_SANITIZATION },
|
|
87
|
+
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
88
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
89
|
+
? value as Record<string, unknown>
|
|
90
|
+
: undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function asStringArray(value: unknown): string[] | undefined {
|
|
94
|
+
if (!Array.isArray(value)) return undefined;
|
|
95
|
+
const filtered = value.filter((item): item is string => typeof item === "string" && item.trim().length > 0);
|
|
96
|
+
return filtered.length > 0 ? filtered : undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function loadSettings(projDir: string): LoadedSettings {
|
|
100
|
+
const defaultPlan: PlanReviewSettings = {
|
|
101
|
+
enabled: true,
|
|
102
|
+
reviewers: {
|
|
103
|
+
codex: { enabled: true, model: "", timeout: 120 },
|
|
104
|
+
gemini: { enabled: false, model: "", timeout: 120 },
|
|
104
105
|
},
|
|
106
|
+
display: { ...DEFAULT_DISPLAY },
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const defaultAgent: AgentReviewSettings = {
|
|
110
|
+
enabled: true,
|
|
111
|
+
orchestrator: { ...DEFAULT_ORCHESTRATOR },
|
|
112
|
+
timeout: 180,
|
|
113
|
+
highIssueThreshold: 3,
|
|
114
|
+
legacyMode: false,
|
|
115
|
+
display: { ...DEFAULT_DISPLAY },
|
|
116
|
+
agentSelection: { ...DEFAULT_AGENT_SELECTION },
|
|
117
|
+
agentDefaults: { model: DEFAULT_AGENT_MODEL },
|
|
118
|
+
complexityCategories: [...DEFAULT_COMPLEXITY_CATEGORIES],
|
|
119
|
+
sanitization: { ...DEFAULT_SANITIZATION },
|
|
105
120
|
};
|
|
106
121
|
|
|
107
122
|
const config = loadConfig(projDir);
|
|
108
|
-
if (!config || Object.keys(config).length === 0)
|
|
123
|
+
if (!config || Object.keys(config).length === 0) {
|
|
124
|
+
return { planReview: defaultPlan, agentReview: defaultAgent, models: {} };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Cast raw config to access arbitrary keys from JSON
|
|
128
|
+
const raw = config as Record<string, unknown>;
|
|
109
129
|
|
|
110
130
|
// Merge planReview
|
|
111
|
-
const
|
|
112
|
-
const mergedPlan = { ...
|
|
113
|
-
if (
|
|
114
|
-
mergedPlan.reviewers = { ...
|
|
131
|
+
const planReviewRaw = (asRecord(raw.planReview) ?? {}) as Partial<PlanReviewSettings>;
|
|
132
|
+
const mergedPlan: PlanReviewSettings = { ...defaultPlan, ...planReviewRaw };
|
|
133
|
+
if (planReviewRaw.reviewers) {
|
|
134
|
+
mergedPlan.reviewers = { ...defaultPlan.reviewers, ...planReviewRaw.reviewers };
|
|
115
135
|
}
|
|
116
136
|
mergedPlan.display = getDisplaySettings(config, "planReview");
|
|
117
137
|
|
|
118
138
|
// Merge agentReview
|
|
119
|
-
const
|
|
120
|
-
const
|
|
139
|
+
const agentReviewRawRecord = asRecord(raw.agentReview);
|
|
140
|
+
const agentReviewRaw = (agentReviewRawRecord ?? {}) as Partial<AgentReviewSettings>;
|
|
141
|
+
const mergedAgent: AgentReviewSettings = { ...defaultAgent, ...agentReviewRaw };
|
|
121
142
|
if (!mergedAgent.orchestrator || typeof mergedAgent.orchestrator !== "object") {
|
|
122
143
|
mergedAgent.orchestrator = { ...DEFAULT_ORCHESTRATOR };
|
|
123
144
|
} else {
|
|
124
145
|
mergedAgent.orchestrator = { ...DEFAULT_ORCHESTRATOR, ...mergedAgent.orchestrator };
|
|
125
146
|
}
|
|
126
147
|
mergedAgent.display = getDisplaySettings(config, "agentReview");
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
mergedAgent.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
148
|
+
|
|
149
|
+
const nestedAgentSelection = asRecord(agentReviewRawRecord?.agentSelection) as AgentSelectionConfig | undefined;
|
|
150
|
+
const topLevelAgentSelection = asRecord(raw.agentSelection) as AgentSelectionConfig | undefined;
|
|
151
|
+
mergedAgent.agentSelection = {
|
|
152
|
+
...DEFAULT_AGENT_SELECTION,
|
|
153
|
+
...nestedAgentSelection,
|
|
154
|
+
...topLevelAgentSelection,
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const nestedAgentDefaults = asRecord(agentReviewRawRecord?.agentDefaults) as { model?: string } | undefined;
|
|
158
|
+
const topLevelAgentDefaults = asRecord(raw.agentDefaults) as { model?: string } | undefined;
|
|
159
|
+
mergedAgent.agentDefaults = {
|
|
160
|
+
model: DEFAULT_AGENT_MODEL,
|
|
161
|
+
...nestedAgentDefaults,
|
|
162
|
+
...topLevelAgentDefaults,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const nestedComplexityCategories = asStringArray(agentReviewRawRecord?.complexityCategories);
|
|
166
|
+
const topLevelComplexityCategories = asStringArray(raw.complexityCategories);
|
|
167
|
+
mergedAgent.complexityCategories = topLevelComplexityCategories
|
|
168
|
+
?? nestedComplexityCategories
|
|
169
|
+
?? [...DEFAULT_COMPLEXITY_CATEGORIES];
|
|
170
|
+
|
|
171
|
+
const nestedSanitization = asRecord(agentReviewRawRecord?.sanitization);
|
|
172
|
+
const topLevelSanitization = asRecord(raw.sanitization);
|
|
173
|
+
mergedAgent.sanitization = {
|
|
174
|
+
...DEFAULT_SANITIZATION,
|
|
175
|
+
...nestedSanitization,
|
|
176
|
+
...topLevelSanitization,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const nestedFallbackByComplexity = asRecord(agentReviewRawRecord?.fallbackByComplexity) as Record<string, number> | undefined;
|
|
180
|
+
const topLevelFallbackByComplexity = asRecord(raw.fallbackByComplexity) as Record<string, number> | undefined;
|
|
181
|
+
if (nestedFallbackByComplexity || topLevelFallbackByComplexity) {
|
|
182
|
+
mergedAgent.fallbackByComplexity = {
|
|
183
|
+
...(nestedFallbackByComplexity ?? {}),
|
|
184
|
+
...(topLevelFallbackByComplexity ?? {}),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const nestedMandatoryAgents = agentReviewRawRecord?.mandatoryAgents as AgentReviewSettings["mandatoryAgents"] | undefined;
|
|
189
|
+
const topLevelMandatoryAgents = raw.mandatoryAgents as AgentReviewSettings["mandatoryAgents"] | undefined;
|
|
190
|
+
if (nestedMandatoryAgents !== undefined || topLevelMandatoryAgents !== undefined) {
|
|
191
|
+
mergedAgent.mandatoryAgents = topLevelMandatoryAgents ?? nestedMandatoryAgents;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const nestedPreflight = asRecord(agentReviewRawRecord?.preflight);
|
|
195
|
+
const topLevelPreflight = asRecord(raw.preflight);
|
|
196
|
+
if (nestedPreflight || topLevelPreflight) {
|
|
197
|
+
mergedAgent.preflight = {
|
|
198
|
+
...(nestedPreflight ?? {}),
|
|
199
|
+
...(topLevelPreflight ?? {}),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const nestedReviewIterations = asRecord(agentReviewRawRecord?.reviewIterations) as Record<string, number> | undefined;
|
|
204
|
+
const topLevelReviewIterations = asRecord(raw.reviewIterations) as Record<string, number> | undefined;
|
|
205
|
+
mergedAgent.reviewIterations = {
|
|
206
|
+
...DEFAULT_REVIEW_ITERATIONS,
|
|
207
|
+
...(nestedReviewIterations ?? {}),
|
|
208
|
+
...(topLevelReviewIterations ?? {}),
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const modelsRaw = (raw.models ?? {}) as Record<string, unknown>;
|
|
135
212
|
return { planReview: mergedPlan, agentReview: mergedAgent, models: modelsRaw };
|
|
136
213
|
}
|
|
137
214
|
|
|
@@ -139,7 +216,7 @@ export function loadSettings(projDir: string): Record<string, unknown> {
|
|
|
139
216
|
// Models Config
|
|
140
217
|
// ---------------------------------------------------------------------------
|
|
141
218
|
|
|
142
|
-
export function loadModelsConfig(settings:
|
|
219
|
+
export function loadModelsConfig(settings: LoadedSettings): ModelsConfig {
|
|
143
220
|
const raw = settings.models as Record<string, unknown> | undefined;
|
|
144
221
|
if (!raw?.providers || typeof raw.providers !== "object") {
|
|
145
222
|
return DEFAULT_MODELS_CONFIG;
|
|
@@ -150,7 +227,6 @@ export function loadModelsConfig(settings: Record<string, unknown>): ModelsConfi
|
|
|
150
227
|
providers[name] = {
|
|
151
228
|
enabled: c.enabled !== false,
|
|
152
229
|
models: Array.isArray(c.models) ? (c.models as string[]).filter(Boolean) : [],
|
|
153
|
-
...(typeof c.reasoningEffort === "string" && { reasoningEffort: c.reasoningEffort }),
|
|
154
230
|
};
|
|
155
231
|
}
|
|
156
232
|
return { providers };
|
|
@@ -162,7 +238,7 @@ export function loadModelsConfig(settings: Record<string, unknown>): ModelsConfi
|
|
|
162
238
|
|
|
163
239
|
export function loadAgentLibrary(
|
|
164
240
|
projDir: string,
|
|
165
|
-
settings?:
|
|
241
|
+
settings?: AgentReviewSettings,
|
|
166
242
|
): AgentConfig[] {
|
|
167
243
|
const agentsData = aggregateAgents(path.join(projDir, "_cc-native", "plan-review", "agents", "plan-review"));
|
|
168
244
|
const defaultModel = settings?.agentDefaults?.model ?? DEFAULT_AGENT_MODEL;
|
|
@@ -128,6 +128,7 @@ export interface ReviewDecisionResult {
|
|
|
128
128
|
export interface OrchestratorConfig {
|
|
129
129
|
enabled: boolean;
|
|
130
130
|
model: string;
|
|
131
|
+
provider?: string;
|
|
131
132
|
timeout: number;
|
|
132
133
|
}
|
|
133
134
|
|
|
@@ -142,6 +143,66 @@ export interface ModelsConfig {
|
|
|
142
143
|
providers: Record<string, ProviderConfig>;
|
|
143
144
|
}
|
|
144
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
|
+
|
|
145
206
|
// ---------------------------------------------------------------------------
|
|
146
207
|
// State Interfaces
|
|
147
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[]
|
|
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
|
|
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:
|
|
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:
|
|
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 =
|
|
61
|
+
const fallbackCount = settings.agentSelection?.fallbackCount ?? 2;
|
|
62
62
|
return {
|
|
63
63
|
complexity: "medium",
|
|
64
64
|
category: "code",
|
|
@@ -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 === "
|
|
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
|
|
166
|
-
const questionsResult = await runPlanQuestions(plan, aiwcliDir,
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
410
|
+
const highIssueThreshold = agentSettings.highIssueThreshold ?? 3;
|
|
408
411
|
overrideVerdictsByThreshold(agentResults, highIssueThreshold);
|
|
409
412
|
|
|
410
413
|
// PHASE 4: Generate Output
|
|
@@ -3,13 +3,13 @@
|
|
|
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";
|
|
6
|
+
import { buildCliInvocation, reviewSpec, type CliProvider } from "../../../../../_shared/lib-ts/base/cli-args.js";
|
|
7
7
|
import type { ExecutionBackend } from "../../../../../_shared/lib-ts/agent-exec/execution-backend.js";
|
|
8
8
|
import type { ExecutionResult } from "../../../../../_shared/lib-ts/agent-exec/execution-backend.js";
|
|
9
|
+
import { parseStructuredOutput } from "../../../../../_shared/lib-ts/agent-exec/structured-output.js";
|
|
9
10
|
import { logDebug } from "../../../../../_shared/lib-ts/base/logger.js";
|
|
10
11
|
import { debugLog, debugRaw } from "../../../../lib-ts/debug.js";
|
|
11
|
-
import {
|
|
12
|
-
import type { AgentConfig, OrchestratorResult, ComplexityCategory } from "../../../../lib-ts/types.js";
|
|
12
|
+
import type { AgentConfig, AgentReviewSettings, AgentSelectionConfig, OrchestratorResult, ComplexityCategory } from "../../../../lib-ts/types.js";
|
|
13
13
|
import { BaseCliAgent } from "../base/base-agent.js";
|
|
14
14
|
import { buildOrchestratorSchema, ORCHESTRATOR_SCHEMA } from "../schemas.js";
|
|
15
15
|
|
|
@@ -27,7 +27,7 @@ const DEFAULT_COMPLEXITY_CATEGORIES = [
|
|
|
27
27
|
"research",
|
|
28
28
|
];
|
|
29
29
|
|
|
30
|
-
const DEFAULT_AGENT_SELECTION:
|
|
30
|
+
const DEFAULT_AGENT_SELECTION: AgentSelectionConfig = {
|
|
31
31
|
simple: { min: 3, max: 3 },
|
|
32
32
|
medium: { min: 8, max: 8 },
|
|
33
33
|
high: { min: 12, max: 12 },
|
|
@@ -43,14 +43,14 @@ export class OrchestratorClaudeAgent extends BaseCliAgent<OrchestratorResult> {
|
|
|
43
43
|
private fallbackCount: number;
|
|
44
44
|
private mandatoryCount: number;
|
|
45
45
|
private nonMandatory: AgentConfig[];
|
|
46
|
-
private settings:
|
|
46
|
+
private settings: AgentReviewSettings;
|
|
47
47
|
private validNames: string[];
|
|
48
48
|
|
|
49
49
|
constructor(
|
|
50
50
|
agent: AgentConfig,
|
|
51
51
|
agentLibrary: AgentConfig[],
|
|
52
52
|
mandatoryNames: Set<string>,
|
|
53
|
-
settings:
|
|
53
|
+
settings: AgentReviewSettings,
|
|
54
54
|
timeout: number,
|
|
55
55
|
contextPath?: string,
|
|
56
56
|
sessionName?: string,
|
|
@@ -61,7 +61,7 @@ export class OrchestratorClaudeAgent extends BaseCliAgent<OrchestratorResult> {
|
|
|
61
61
|
(a) => !mandatoryNames.has(a.name),
|
|
62
62
|
);
|
|
63
63
|
const validNames = nonMandatory.map((a) => a.name);
|
|
64
|
-
const categories =
|
|
64
|
+
const categories = settings.complexityCategories ?? DEFAULT_COMPLEXITY_CATEGORIES;
|
|
65
65
|
|
|
66
66
|
const schema = validNames.length > 0
|
|
67
67
|
? buildOrchestratorSchema(validNames, categories)
|
|
@@ -77,8 +77,8 @@ export class OrchestratorClaudeAgent extends BaseCliAgent<OrchestratorResult> {
|
|
|
77
77
|
this.categories = categories;
|
|
78
78
|
this.settings = settings;
|
|
79
79
|
|
|
80
|
-
const selection =
|
|
81
|
-
this.fallbackCount =
|
|
80
|
+
const selection = settings.agentSelection ?? DEFAULT_AGENT_SELECTION;
|
|
81
|
+
this.fallbackCount = selection.fallbackCount ?? DEFAULT_AGENT_SELECTION.fallbackCount;
|
|
82
82
|
this.mandatoryCount = agentLibrary.filter((a) => mandatoryNames.has(a.name)).length;
|
|
83
83
|
|
|
84
84
|
logDebug("orchestrator", `Mandatory agents (always run): ${[...mandatoryNames].sort().join(", ")}`);
|
|
@@ -97,12 +97,12 @@ When selecting agents:
|
|
|
97
97
|
- Fewer agents for simple plans, more for complex plans`;
|
|
98
98
|
|
|
99
99
|
return buildCliInvocation(
|
|
100
|
-
reviewSpec("claude", this.agent.model, this.schema, systemPrompt),
|
|
100
|
+
reviewSpec((this.agent.provider ?? "claude") as CliProvider, this.agent.model, this.schema, systemPrompt),
|
|
101
101
|
).args;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
protected buildPrompt(plan: string): string {
|
|
105
|
-
const selection =
|
|
105
|
+
const selection = this.settings.agentSelection ?? DEFAULT_AGENT_SELECTION;
|
|
106
106
|
|
|
107
107
|
const agentList = this.nonMandatory
|
|
108
108
|
.map(
|
|
@@ -112,9 +112,9 @@ When selecting agents:
|
|
|
112
112
|
.join("\n");
|
|
113
113
|
const categoryList = this.categories.join("/");
|
|
114
114
|
|
|
115
|
-
const simpleAdditional = Math.max(0, (
|
|
116
|
-
const mediumAdditional = Math.max(0, (
|
|
117
|
-
const highAdditional = Math.max(0, (
|
|
115
|
+
const simpleAdditional = Math.max(0, (selection.simple?.max ?? 3) - this.mandatoryCount);
|
|
116
|
+
const mediumAdditional = Math.max(0, (selection.medium?.max ?? 8) - this.mandatoryCount);
|
|
117
|
+
const highAdditional = Math.max(0, (selection.high?.max ?? 12) - this.mandatoryCount);
|
|
118
118
|
|
|
119
119
|
return `Analyze this plan and select appropriate reviewer agents.
|
|
120
120
|
|
|
@@ -149,26 +149,54 @@ Call StructuredOutput now with: complexity, category, selectedAgents, reasoning`
|
|
|
149
149
|
? rawComplexity
|
|
150
150
|
: "medium";
|
|
151
151
|
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
const defaultCategory = this.categories.includes("code")
|
|
153
|
+
? "code"
|
|
154
|
+
: (this.categories[0] ?? "code");
|
|
155
|
+
let category = defaultCategory;
|
|
156
|
+
if (typeof obj.category === "string" && this.categories.includes(obj.category)) {
|
|
157
|
+
category = obj.category;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const rawSelected = Array.isArray(obj.selectedAgents)
|
|
161
|
+
? obj.selectedAgents
|
|
162
|
+
: (Array.isArray(obj.selected_agents) ? obj.selected_agents : []);
|
|
163
|
+
|
|
164
|
+
const validNames = new Set(this.validNames);
|
|
165
|
+
const selectedAgents: string[] = [];
|
|
166
|
+
const unknownNames: string[] = [];
|
|
167
|
+
const seen = new Set<string>();
|
|
168
|
+
for (const value of rawSelected) {
|
|
169
|
+
if (typeof value !== "string") continue;
|
|
170
|
+
if (!validNames.has(value)) {
|
|
171
|
+
unknownNames.push(value);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (!seen.has(value)) {
|
|
175
|
+
selectedAgents.push(value);
|
|
176
|
+
seen.add(value);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
154
179
|
|
|
155
|
-
|
|
156
|
-
|
|
180
|
+
if (unknownNames.length > 0) {
|
|
181
|
+
logDebug("orchestrator", `Ignoring unknown orchestrator selections: ${unknownNames.join(", ")}`);
|
|
182
|
+
}
|
|
157
183
|
|
|
158
184
|
const reasoning = String(obj.reasoning ?? "").trim() || "No reasoning provided";
|
|
159
|
-
const
|
|
185
|
+
const skipReasonRaw = typeof obj.skipReason === "string"
|
|
186
|
+
? obj.skipReason
|
|
187
|
+
: (typeof obj.skip_reason === "string" ? obj.skip_reason : undefined);
|
|
160
188
|
|
|
161
189
|
return {
|
|
162
190
|
complexity,
|
|
163
191
|
category,
|
|
164
|
-
selected_agents: selectedAgents
|
|
192
|
+
selected_agents: selectedAgents,
|
|
165
193
|
reasoning,
|
|
166
|
-
skip_reason:
|
|
194
|
+
skip_reason: skipReasonRaw || undefined,
|
|
167
195
|
};
|
|
168
196
|
}
|
|
169
197
|
|
|
170
198
|
protected getCliName(): string {
|
|
171
|
-
return "claude";
|
|
199
|
+
return this.agent.provider ?? "claude";
|
|
172
200
|
}
|
|
173
201
|
|
|
174
202
|
protected makeErrorResult(type: "skip" | "error", message: string): OrchestratorResult {
|
|
@@ -179,7 +207,10 @@ Call StructuredOutput now with: complexity, category, selectedAgents, reasoning`
|
|
|
179
207
|
}
|
|
180
208
|
|
|
181
209
|
protected parseOutput(raw: string, _result: ExecutionResult): Record<string, unknown> | null {
|
|
182
|
-
return
|
|
210
|
+
return parseStructuredOutput(raw, {
|
|
211
|
+
requireFields: ["complexity", "category", "reasoning"],
|
|
212
|
+
loggerTag: "orchestrator_parser",
|
|
213
|
+
});
|
|
183
214
|
}
|
|
184
215
|
|
|
185
216
|
private makeFallback(reasoning: string, error: string): OrchestratorResult {
|
package/oclif.manifest.json
CHANGED