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.
Files changed (117) hide show
  1. package/dist/commands/clear.d.ts +8 -0
  2. package/dist/commands/clear.js +86 -0
  3. package/dist/lib/bmad-installer.d.ts +2 -27
  4. package/dist/lib/bmad-installer.js +3 -43
  5. package/dist/lib/claude-settings-types.d.ts +2 -1
  6. package/dist/lib/env-compat.d.ts +0 -8
  7. package/dist/lib/env-compat.js +0 -12
  8. package/dist/lib/git/index.d.ts +0 -1
  9. package/dist/lib/gitignore-manager.d.ts +0 -2
  10. package/dist/lib/gitignore-manager.js +1 -1
  11. package/dist/lib/hooks-merger.d.ts +1 -15
  12. package/dist/lib/hooks-merger.js +1 -1
  13. package/dist/lib/index.d.ts +3 -7
  14. package/dist/lib/index.js +3 -11
  15. package/dist/lib/output.d.ts +2 -1
  16. package/dist/lib/settings-hierarchy.d.ts +1 -13
  17. package/dist/lib/settings-hierarchy.js +1 -1
  18. package/dist/lib/template-installer.d.ts +5 -9
  19. package/dist/lib/template-installer.js +3 -13
  20. package/dist/lib/template-linter.d.ts +3 -10
  21. package/dist/lib/template-linter.js +2 -2
  22. package/dist/lib/template-resolver.d.ts +6 -0
  23. package/dist/lib/template-resolver.js +10 -0
  24. package/dist/lib/template-settings-reconstructor.d.ts +1 -1
  25. package/dist/lib/template-settings-reconstructor.js +17 -24
  26. package/dist/lib/terminal.d.ts +3 -14
  27. package/dist/lib/terminal.js +0 -4
  28. package/dist/lib/version.d.ts +2 -11
  29. package/dist/lib/version.js +3 -3
  30. package/dist/lib/windsurf-hooks-merger.d.ts +1 -15
  31. package/dist/lib/windsurf-hooks-merger.js +1 -1
  32. package/dist/templates/_shared/.codex/workflows/handoff.md +1 -1
  33. package/dist/templates/_shared/.windsurf/workflows/handoff.md +1 -1
  34. package/dist/templates/_shared/hooks-ts/session_end.ts +75 -4
  35. package/dist/templates/_shared/hooks-ts/session_start.ts +11 -13
  36. package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +6 -8
  37. package/dist/templates/_shared/lib-ts/CLAUDE.md +56 -7
  38. package/dist/templates/_shared/lib-ts/base/hook-utils.ts +176 -29
  39. package/dist/templates/_shared/lib-ts/base/logger.ts +1 -1
  40. package/dist/templates/_shared/lib-ts/base/state-io.ts +11 -2
  41. package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +181 -165
  42. package/dist/templates/_shared/lib-ts/context/plan-manager.ts +14 -13
  43. package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +3 -2
  44. package/dist/templates/_shared/lib-ts/package.json +1 -2
  45. package/dist/templates/_shared/lib-ts/templates/plan-context.ts +27 -34
  46. package/dist/templates/_shared/lib-ts/types.ts +17 -2
  47. package/dist/templates/_shared/scripts/resume_handoff.ts +4 -4
  48. package/dist/templates/_shared/scripts/save_handoff.ts +7 -7
  49. package/dist/templates/_shared/scripts/status_line.ts +104 -71
  50. package/dist/templates/_shared/workflows/handoff.md +1 -1
  51. package/dist/templates/cc-native/.claude/settings.json +182 -175
  52. package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +23 -1
  53. package/dist/templates/cc-native/_cc-native/agents/plan-questions/PLAN-QUESTIONER.md +70 -0
  54. package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +6 -1
  55. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +142 -111
  56. package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_subagent.ts +54 -0
  57. package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_write.ts +52 -0
  58. package/dist/templates/cc-native/_cc-native/hooks/mark_questions_asked.ts +53 -0
  59. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +19 -19
  60. package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +6 -5
  61. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +114 -83
  62. package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +107 -10
  63. package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +1 -1
  64. package/dist/templates/cc-native/_cc-native/lib-ts/corroboration.ts +6 -2
  65. package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +0 -4
  66. package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +40 -219
  67. package/dist/templates/cc-native/_cc-native/lib-ts/plan-enhancement.ts +41 -0
  68. package/dist/templates/cc-native/_cc-native/lib-ts/plan-questions.ts +102 -0
  69. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +26 -227
  70. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/base/base-agent.ts +217 -0
  71. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +4 -2
  72. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/claude-agent.ts +65 -0
  73. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/codex-agent.ts +185 -0
  74. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/gemini-agent.ts +39 -0
  75. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/orchestrator-claude-agent.ts +195 -0
  76. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/schemas.ts +201 -0
  77. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +2 -2
  78. package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +17 -16
  79. package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +13 -108
  80. package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +3 -3
  81. package/dist/templates/cc-native/_cc-native/plan-review.config.json +2 -14
  82. package/oclif.manifest.json +1 -1
  83. package/package.json +1 -2
  84. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +0 -119
  85. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +0 -130
  86. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +0 -107
  87. /package/dist/templates/cc-native/_cc-native/agents/{ARCH-EVOLUTION.md → plan-review/ARCH-EVOLUTION.md} +0 -0
  88. /package/dist/templates/cc-native/_cc-native/agents/{ARCH-PATTERNS.md → plan-review/ARCH-PATTERNS.md} +0 -0
  89. /package/dist/templates/cc-native/_cc-native/agents/{ARCH-STRUCTURE.md → plan-review/ARCH-STRUCTURE.md} +0 -0
  90. /package/dist/templates/cc-native/_cc-native/agents/{ASSUMPTION-TRACER.md → plan-review/ASSUMPTION-TRACER.md} +0 -0
  91. /package/dist/templates/cc-native/_cc-native/agents/{CLARITY-AUDITOR.md → plan-review/CLARITY-AUDITOR.md} +0 -0
  92. /package/dist/templates/cc-native/_cc-native/agents/{COMPLETENESS-FEASIBILITY.md → plan-review/COMPLETENESS-FEASIBILITY.md} +0 -0
  93. /package/dist/templates/cc-native/_cc-native/agents/{COMPLETENESS-GAPS.md → plan-review/COMPLETENESS-GAPS.md} +0 -0
  94. /package/dist/templates/cc-native/_cc-native/agents/{COMPLETENESS-ORDERING.md → plan-review/COMPLETENESS-ORDERING.md} +0 -0
  95. /package/dist/templates/cc-native/_cc-native/agents/{CONSTRAINT-VALIDATOR.md → plan-review/CONSTRAINT-VALIDATOR.md} +0 -0
  96. /package/dist/templates/cc-native/_cc-native/agents/{DESIGN-ADR-VALIDATOR.md → plan-review/DESIGN-ADR-VALIDATOR.md} +0 -0
  97. /package/dist/templates/cc-native/_cc-native/agents/{DESIGN-SCALE-MATCHER.md → plan-review/DESIGN-SCALE-MATCHER.md} +0 -0
  98. /package/dist/templates/cc-native/_cc-native/agents/{DEVILS-ADVOCATE.md → plan-review/DEVILS-ADVOCATE.md} +0 -0
  99. /package/dist/templates/cc-native/_cc-native/agents/{DOCUMENTATION-PHILOSOPHY.md → plan-review/DOCUMENTATION-PHILOSOPHY.md} +0 -0
  100. /package/dist/templates/cc-native/_cc-native/agents/{HANDOFF-READINESS.md → plan-review/HANDOFF-READINESS.md} +0 -0
  101. /package/dist/templates/cc-native/_cc-native/agents/{HIDDEN-COMPLEXITY.md → plan-review/HIDDEN-COMPLEXITY.md} +0 -0
  102. /package/dist/templates/cc-native/_cc-native/agents/{INCREMENTAL-DELIVERY.md → plan-review/INCREMENTAL-DELIVERY.md} +0 -0
  103. /package/dist/templates/cc-native/_cc-native/agents/{RISK-DEPENDENCY.md → plan-review/RISK-DEPENDENCY.md} +0 -0
  104. /package/dist/templates/cc-native/_cc-native/agents/{RISK-FMEA.md → plan-review/RISK-FMEA.md} +0 -0
  105. /package/dist/templates/cc-native/_cc-native/agents/{RISK-PREMORTEM.md → plan-review/RISK-PREMORTEM.md} +0 -0
  106. /package/dist/templates/cc-native/_cc-native/agents/{RISK-REVERSIBILITY.md → plan-review/RISK-REVERSIBILITY.md} +0 -0
  107. /package/dist/templates/cc-native/_cc-native/agents/{SCOPE-BOUNDARY.md → plan-review/SCOPE-BOUNDARY.md} +0 -0
  108. /package/dist/templates/cc-native/_cc-native/agents/{SIMPLICITY-GUARDIAN.md → plan-review/SIMPLICITY-GUARDIAN.md} +0 -0
  109. /package/dist/templates/cc-native/_cc-native/agents/{SKEPTIC.md → plan-review/SKEPTIC.md} +0 -0
  110. /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-BEHAVIOR-AUDITOR.md → plan-review/TESTDRIVEN-BEHAVIOR-AUDITOR.md} +0 -0
  111. /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-CHARACTERIZATION.md → plan-review/TESTDRIVEN-CHARACTERIZATION.md} +0 -0
  112. /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-FIRST-VALIDATOR.md → plan-review/TESTDRIVEN-FIRST-VALIDATOR.md} +0 -0
  113. /package/dist/templates/cc-native/_cc-native/agents/{TESTDRIVEN-PYRAMID-ANALYZER.md → plan-review/TESTDRIVEN-PYRAMID-ANALYZER.md} +0 -0
  114. /package/dist/templates/cc-native/_cc-native/agents/{TRADEOFF-COSTS.md → plan-review/TRADEOFF-COSTS.md} +0 -0
  115. /package/dist/templates/cc-native/_cc-native/agents/{TRADEOFF-STAKEHOLDERS.md → plan-review/TRADEOFF-STAKEHOLDERS.md} +0 -0
  116. /package/dist/templates/cc-native/_cc-native/agents/{VERIFY-COVERAGE.md → plan-review/VERIFY-COVERAGE.md} +0 -0
  117. /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
+ }
@@ -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 { ReviewData, ReviewerResult, Verdict } from "../types.js";
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> | ReviewData,
16
+ data: ReviewData | Record<string, unknown>,
17
17
  raw: string,
18
18
  err: string,
19
19
  ): ReviewerResult {