aiwcli 0.12.6 → 0.12.7

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 (124) hide show
  1. package/bin/dev.cmd +3 -3
  2. package/bin/dev.js +16 -16
  3. package/bin/run.cmd +3 -3
  4. package/bin/run.js +21 -21
  5. package/dist/commands/branch.js +7 -2
  6. package/dist/lib/bmad-installer.js +37 -37
  7. package/dist/lib/terminal.d.ts +2 -0
  8. package/dist/lib/terminal.js +57 -7
  9. package/dist/templates/CLAUDE.md +205 -205
  10. package/dist/templates/_shared/.claude/commands/handoff-resume.md +12 -12
  11. package/dist/templates/_shared/.claude/commands/handoff.md +12 -12
  12. package/dist/templates/_shared/.claude/settings.json +65 -65
  13. package/dist/templates/_shared/.codex/workflows/handoff.md +226 -226
  14. package/dist/templates/_shared/.windsurf/workflows/handoff.md +226 -226
  15. package/dist/templates/_shared/handoff-system/CLAUDE.md +421 -421
  16. package/dist/templates/_shared/handoff-system/lib/document-generator.ts +215 -215
  17. package/dist/templates/_shared/handoff-system/lib/handoff-reader.ts +158 -158
  18. package/dist/templates/_shared/handoff-system/scripts/resume_handoff.ts +373 -373
  19. package/dist/templates/_shared/handoff-system/scripts/save_handoff.ts +469 -469
  20. package/dist/templates/_shared/handoff-system/workflows/handoff-resume.md +66 -66
  21. package/dist/templates/_shared/handoff-system/workflows/handoff.md +254 -254
  22. package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +2 -2
  23. package/dist/templates/_shared/hooks-ts/archive_plan.ts +159 -159
  24. package/dist/templates/_shared/hooks-ts/context_monitor.ts +147 -147
  25. package/dist/templates/_shared/hooks-ts/file-suggestion.ts +128 -128
  26. package/dist/templates/_shared/hooks-ts/pre_compact.ts +49 -49
  27. package/dist/templates/_shared/hooks-ts/session_end.ts +196 -196
  28. package/dist/templates/_shared/hooks-ts/session_start.ts +163 -163
  29. package/dist/templates/_shared/hooks-ts/task_create_capture.ts +48 -48
  30. package/dist/templates/_shared/hooks-ts/task_update_capture.ts +74 -74
  31. package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +93 -93
  32. package/dist/templates/_shared/lib-ts/CLAUDE.md +367 -367
  33. package/dist/templates/_shared/lib-ts/base/atomic-write.ts +138 -138
  34. package/dist/templates/_shared/lib-ts/base/constants.ts +303 -303
  35. package/dist/templates/_shared/lib-ts/base/git-state.ts +58 -58
  36. package/dist/templates/_shared/lib-ts/base/hook-utils.ts +582 -582
  37. package/dist/templates/_shared/lib-ts/base/inference.ts +301 -301
  38. package/dist/templates/_shared/lib-ts/base/logger.ts +247 -247
  39. package/dist/templates/_shared/lib-ts/base/state-io.ts +202 -202
  40. package/dist/templates/_shared/lib-ts/base/stop-words.ts +184 -184
  41. package/dist/templates/_shared/lib-ts/base/utils.ts +184 -184
  42. package/dist/templates/_shared/lib-ts/context/context-formatter.ts +566 -566
  43. package/dist/templates/_shared/lib-ts/context/context-selector.ts +524 -524
  44. package/dist/templates/_shared/lib-ts/context/context-store.ts +712 -712
  45. package/dist/templates/_shared/lib-ts/context/plan-manager.ts +312 -312
  46. package/dist/templates/_shared/lib-ts/context/task-tracker.ts +185 -185
  47. package/dist/templates/_shared/lib-ts/package.json +20 -20
  48. package/dist/templates/_shared/lib-ts/templates/formatters.ts +102 -102
  49. package/dist/templates/_shared/lib-ts/templates/plan-context.ts +58 -58
  50. package/dist/templates/_shared/lib-ts/tsconfig.json +13 -13
  51. package/dist/templates/_shared/lib-ts/types.ts +186 -186
  52. package/dist/templates/_shared/scripts/resolve_context.ts +33 -33
  53. package/dist/templates/_shared/scripts/status_line.ts +690 -690
  54. package/dist/templates/cc-native/.claude/commands/cc-native/rlm/ask.md +136 -136
  55. package/dist/templates/cc-native/.claude/commands/cc-native/rlm/index.md +21 -21
  56. package/dist/templates/cc-native/.claude/commands/cc-native/rlm/overview.md +56 -56
  57. package/dist/templates/cc-native/.claude/commands/cc-native/specdev.md +10 -10
  58. package/dist/templates/cc-native/.windsurf/workflows/cc-native/fix.md +8 -8
  59. package/dist/templates/cc-native/.windsurf/workflows/cc-native/implement.md +8 -8
  60. package/dist/templates/cc-native/.windsurf/workflows/cc-native/research.md +8 -8
  61. package/dist/templates/cc-native/CC-NATIVE-README.md +189 -189
  62. package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +304 -304
  63. package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +143 -143
  64. package/dist/templates/cc-native/_cc-native/agents/PLAN-ORCHESTRATOR.md +213 -213
  65. package/dist/templates/cc-native/_cc-native/agents/plan-questions/PLAN-QUESTIONER.md +70 -70
  66. package/dist/templates/cc-native/_cc-native/cc-native.config.json +96 -96
  67. package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +247 -247
  68. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +76 -76
  69. package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_subagent.ts +54 -54
  70. package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_write.ts +51 -51
  71. package/dist/templates/cc-native/_cc-native/hooks/mark_questions_asked.ts +53 -53
  72. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -61
  73. package/dist/templates/cc-native/_cc-native/lib-ts/agent-selection.ts +163 -163
  74. package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +156 -156
  75. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts/format.ts +597 -597
  76. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts/index.ts +26 -26
  77. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts/tracker.ts +107 -107
  78. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts/write.ts +119 -119
  79. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +21 -21
  80. package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +319 -319
  81. package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +144 -144
  82. package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -57
  83. package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -83
  84. package/dist/templates/cc-native/_cc-native/lib-ts/corroboration.ts +119 -119
  85. package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +79 -79
  86. package/dist/templates/cc-native/_cc-native/lib-ts/graduation.ts +132 -132
  87. package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +116 -116
  88. package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +168 -168
  89. package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +70 -70
  90. package/dist/templates/cc-native/_cc-native/lib-ts/output-builder.ts +130 -130
  91. package/dist/templates/cc-native/_cc-native/lib-ts/plan-discovery.ts +80 -80
  92. package/dist/templates/cc-native/_cc-native/lib-ts/plan-enhancement.ts +41 -41
  93. package/dist/templates/cc-native/_cc-native/lib-ts/plan-questions.ts +101 -101
  94. package/dist/templates/cc-native/_cc-native/lib-ts/review-pipeline.ts +511 -511
  95. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +71 -71
  96. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/base/base-agent.ts +217 -217
  97. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +12 -12
  98. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/claude-agent.ts +66 -66
  99. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/codex-agent.ts +184 -184
  100. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/gemini-agent.ts +39 -39
  101. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/providers/orchestrator-claude-agent.ts +196 -196
  102. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/schemas.ts +201 -201
  103. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +21 -21
  104. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/CLAUDE.md +480 -480
  105. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/embedding-indexer.ts +287 -287
  106. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/hyde.ts +148 -148
  107. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/index.ts +54 -54
  108. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/logger.ts +58 -58
  109. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/ollama-client.ts +208 -208
  110. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/retrieval-pipeline.ts +460 -460
  111. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/transcript-indexer.ts +446 -446
  112. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/transcript-loader.ts +280 -280
  113. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/transcript-searcher.ts +274 -274
  114. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/types.ts +201 -201
  115. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/vector-store.ts +278 -278
  116. package/dist/templates/cc-native/_cc-native/lib-ts/settings.ts +184 -184
  117. package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +275 -275
  118. package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -18
  119. package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +329 -329
  120. package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -72
  121. package/dist/templates/cc-native/_cc-native/workflows/specdev.md +9 -9
  122. package/oclif.manifest.json +1 -1
  123. package/package.json +108 -108
  124. package/dist/templates/cc-native/_cc-native/lib-ts/nul +0 -3
@@ -1,74 +1,74 @@
1
- /**
2
- * Agent-based plan reviewer with multi-provider support.
3
- * Routes to provider-specific implementations (Claude, Codex, Gemini).
4
- * See cc-native-plan-review-spec.md §4.10
5
- */
6
-
7
- import { logWarn } from "../../../_shared/lib-ts/base/logger.js";
8
- import type { AgentConfig, ReviewerResult, ReviewOptions } from "../types.js";
9
- import { ClaudeAgent } from "./providers/claude-agent.js";
10
- import { CodexAgent } from "./providers/codex-agent.js";
11
- import { GeminiAgent } from "./providers/gemini-agent.js";
12
- import type { Reviewer } from "./types.js";
13
- import { makeResult } from "./types.js";
14
-
15
- /**
16
- * Agent reviewer — runs a CLI instance with a custom persona.
17
- */
18
- export class AgentReviewer implements Reviewer {
19
- constructor(private agent: AgentConfig) {}
20
-
21
- async review(
22
- plan: string,
23
- schema: Record<string, unknown>,
24
- options: ReviewOptions,
25
- ): Promise<ReviewerResult> {
26
- return runAgentReview(
27
- plan,
28
- this.agent,
29
- schema,
30
- options.timeout,
31
- options.context_path,
32
- options.session_name ?? "unknown",
33
- );
34
- }
35
- }
36
-
37
- /**
38
- * Run a single agent to review the plan.
39
- * Routes to provider-specific implementation based on agent.provider.
40
- * Never throws — returns error ReviewerResult on failure.
41
- */
42
- export async function runAgentReview(
43
- plan: string,
44
- agent: AgentConfig,
45
- schema: Record<string, unknown>,
46
- timeout: number,
47
- contextPath?: string,
48
- sessionName = "unknown",
49
- ): Promise<ReviewerResult> {
50
- try {
51
- let reviewer;
52
-
53
- switch (agent.provider) {
54
- case "codex": {
55
- reviewer = new CodexAgent(agent, schema, timeout, contextPath, sessionName);
1
+ /**
2
+ * Agent-based plan reviewer with multi-provider support.
3
+ * Routes to provider-specific implementations (Claude, Codex, Gemini).
4
+ * See cc-native-plan-review-spec.md §4.10
5
+ */
6
+
7
+ import { logWarn } from "../../../_shared/lib-ts/base/logger.js";
8
+ import type { AgentConfig, ReviewerResult, ReviewOptions } from "../types.js";
9
+ import { ClaudeAgent } from "./providers/claude-agent.js";
10
+ import { CodexAgent } from "./providers/codex-agent.js";
11
+ import { GeminiAgent } from "./providers/gemini-agent.js";
12
+ import type { Reviewer } from "./types.js";
13
+ import { makeResult } from "./types.js";
14
+
15
+ /**
16
+ * Agent reviewer — runs a CLI instance with a custom persona.
17
+ */
18
+ export class AgentReviewer implements Reviewer {
19
+ constructor(private agent: AgentConfig) {}
20
+
21
+ async review(
22
+ plan: string,
23
+ schema: Record<string, unknown>,
24
+ options: ReviewOptions,
25
+ ): Promise<ReviewerResult> {
26
+ return runAgentReview(
27
+ plan,
28
+ this.agent,
29
+ schema,
30
+ options.timeout,
31
+ options.context_path,
32
+ options.session_name ?? "unknown",
33
+ );
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Run a single agent to review the plan.
39
+ * Routes to provider-specific implementation based on agent.provider.
40
+ * Never throws — returns error ReviewerResult on failure.
41
+ */
42
+ export async function runAgentReview(
43
+ plan: string,
44
+ agent: AgentConfig,
45
+ schema: Record<string, unknown>,
46
+ timeout: number,
47
+ contextPath?: string,
48
+ sessionName = "unknown",
49
+ ): Promise<ReviewerResult> {
50
+ try {
51
+ let reviewer;
52
+
53
+ switch (agent.provider) {
54
+ case "codex": {
55
+ reviewer = new CodexAgent(agent, schema, timeout, contextPath, sessionName);
56
56
  break;
57
- }
58
- case "gemini": {
59
- reviewer = new GeminiAgent(agent, schema, timeout, contextPath, sessionName);
57
+ }
58
+ case "gemini": {
59
+ reviewer = new GeminiAgent(agent, schema, timeout, contextPath, sessionName);
60
60
  break;
61
- }
62
- case "claude":
63
- default: {
64
- reviewer = new ClaudeAgent(agent, schema, timeout, contextPath, sessionName);
61
+ }
62
+ case "claude":
63
+ default: {
64
+ reviewer = new ClaudeAgent(agent, schema, timeout, contextPath, sessionName);
65
65
  break;
66
- }
67
- }
68
-
69
- return await reviewer.review(plan);
70
- } catch (error) {
71
- logWarn(agent.name, `Unexpected error creating reviewer: ${error}`);
72
- return makeResult(agent.name, false, "error", {}, "", `Failed: ${error}`);
73
- }
74
- }
66
+ }
67
+ }
68
+
69
+ return await reviewer.review(plan);
70
+ } catch (error) {
71
+ logWarn(agent.name, `Unexpected error creating reviewer: ${error}`);
72
+ return makeResult(agent.name, false, "error", {}, "", `Failed: ${error}`);
73
+ }
74
+ }
@@ -1,217 +1,217 @@
1
- /**
2
- * Abstract base class for CLI-based agent reviewers.
3
- * Implements template method pattern for subprocess execution flow.
4
- * Provider-specific implementations (Claude, Codex, Gemini) extend this class.
5
- */
6
-
7
- import { logDebug, logInfo, logWarn, logError } from "../../../../_shared/lib-ts/base/logger.js";
8
- import { getInternalSubprocessEnv, findExecutable, execFileAsync } from "../../../../_shared/lib-ts/base/subprocess-utils.js";
9
- import { debugLog, debugRaw } from "../../debug.js";
10
- import type { AgentConfig } from "../../types.js";
11
-
12
- /** Result from execFileAsync */
13
- export interface ExecResult {
14
- stdout: string;
15
- stderr: string;
16
- exitCode: number | null;
17
- signal: string | null;
18
- killed: boolean;
19
- }
20
-
21
- /**
22
- * Abstract base class for all CLI agent subprocess invocations.
23
- * Parameterized over return type T — ReviewerResult for reviewers,
24
- * OrchestratorResult for the orchestrator.
25
- * Subclasses implement provider-specific details.
26
- */
27
- export abstract class BaseCliAgent<T> {
28
- protected agent: AgentConfig;
29
- protected contextPath?: string;
30
- protected schema: Record<string, unknown>;
31
- protected sessionName: string;
32
- protected timeout: number;
33
-
34
- constructor(
35
- agent: AgentConfig,
36
- schema: Record<string, unknown>,
37
- timeout: number,
38
- contextPath?: string,
39
- sessionName = "unknown",
40
- ) {
41
- this.agent = agent;
42
- this.schema = schema;
43
- this.timeout = timeout;
44
- this.contextPath = contextPath;
45
- this.sessionName = sessionName;
46
- }
47
-
48
- /** Build the command-line arguments for the CLI */
49
- protected abstract buildCliArgs(): string[];
50
-
51
- // ─── Abstract Methods (Subclass Implements) ────────────────────────────
52
-
53
- /** Build the stdin prompt for the CLI */
54
- protected abstract buildPrompt(plan: string): string;
55
-
56
- /** Optional cleanup after subprocess execution */
57
- protected async cleanup(): Promise<void> {
58
- // Default: no-op. Subclasses override if needed (e.g., Codex temp files).
59
- }
60
-
61
- /** Coerce parsed JSON into the result type T */
62
- protected abstract coerceResult(obj: Record<string, unknown> | null, raw: string, err: string): T;
63
-
64
- /** Extract stdout/stderr from subprocess result. Override for file-based output (Codex). */
65
- protected extractOutput(result: ExecResult): { raw: string; err: string } {
66
- return {
67
- raw: result.stdout.trim(),
68
- err: result.stderr.trim(),
69
- };
70
- }
71
-
72
- /** Find the CLI executable. Override for custom search logic. */
73
- protected findCli(): string | null {
74
- return findExecutable(this.getCliName());
75
- }
76
-
77
- // ─── Template Methods (Subclass Can Override) ──────────────────────────
78
-
79
- /** Get the CLI executable name (e.g., "claude", "codex") */
80
- protected abstract getCliName(): string;
81
-
82
- /** Get default error message for coerceToReview */
83
- protected getDefaultErrorMessage(): string {
84
- return `Retry or check ${this.getCliName()} configuration.`;
85
- }
86
-
87
- /** Handle non-zero exit with no output */
88
- protected handleExitError(result: ExecResult): T {
89
- const msg = `${this.agent.name} failed to run (exit ${result.exitCode})`;
90
- logError(this.agent.name, `Process exited with code ${result.exitCode} and no output`);
91
- return this.makeErrorResult("error", msg);
92
- }
93
-
94
- /** Handle timeout scenario */
95
- protected handleTimeout(): T {
96
- const msg = `${this.getCliName()} TIMEOUT after ${this.timeout}s`;
97
- logWarn(this.agent.name, msg);
98
- return this.makeErrorResult("error", `${this.agent.name} timed out after ${this.timeout}s`);
99
- }
100
-
101
- // ─── Shared Infrastructure ──────────────────────────────────────────────
102
-
103
- /** Log parsed JSON result */
104
- protected logParsedResult(obj: Record<string, unknown> | null): void {
105
- if (this.contextPath && obj) {
106
- debugLog(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "parsed_result", {
107
- parsed_keys: Object.keys(obj),
108
- verdict: obj.verdict ?? null,
109
- has_summary: Boolean(obj.summary),
110
- issues_count: Array.isArray(obj.issues) ? (obj.issues as unknown[]).length : 0,
111
- });
112
- }
113
-
114
- if (obj) {
115
- logInfo(this.agent.name, `Parsed JSON successfully, verdict: ${obj.verdict ?? "N/A"}`);
116
- } else {
117
- logWarn(this.agent.name, "Failed to parse JSON from output");
118
- }
119
- }
120
-
121
- /** Log subprocess execution results */
122
- protected logSubprocessResult(result: ExecResult, raw: string, err: string): void {
123
- logDebug(this.agent.name, `Exit code: ${result.exitCode}`);
124
- logDebug(this.agent.name, `stdout length: ${raw.length} chars`);
125
- if (err) logDebug(this.agent.name, `stderr: ${err.slice(0, 500)}`);
126
-
127
- // Debug logging
128
- if (this.contextPath) {
129
- debugRaw(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "stdout", raw);
130
- if (err) {
131
- debugRaw(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "stderr", err);
132
- }
133
- debugLog(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "subprocess_info", {
134
- exit_code: result.exitCode,
135
- stdout_len: raw.length,
136
- stderr_len: err.length,
137
- model: this.agent.model,
138
- provider: this.agent.provider,
139
- timeout: this.timeout,
140
- });
141
- }
142
-
143
- if (raw) logDebug(this.agent.name, `stdout preview: ${raw.slice(0, 500)}`);
144
- }
145
-
146
- /** Construct a T for error/skip/timeout scenarios. Subclasses define shape. */
147
- protected abstract makeErrorResult(type: "skip" | "error", message: string): T;
148
-
149
- /** Create skip result when CLI not found */
150
- protected makeSkipResult(reason: string): T {
151
- logWarn(this.agent.name, reason);
152
- return this.makeErrorResult("skip", reason);
153
- }
154
-
155
- /** Parse JSON from CLI output */
156
- protected abstract parseOutput(raw: string, result: ExecResult): Record<string, unknown> | null;
157
-
158
- /**
159
- * Template method - orchestrates the review flow.
160
- * Subclasses override abstract methods to customize behavior.
161
- */
162
- async review(plan: string): Promise<T> {
163
- // 1. Find CLI executable
164
- const cliPath = this.findCli();
165
- if (!cliPath) {
166
- return this.makeSkipResult(`${this.getCliName()} CLI not found on PATH`);
167
- }
168
-
169
- logDebug(this.agent.name, `Found ${this.getCliName()} CLI at: ${cliPath}`);
170
-
171
- // 2. Build prompt and args (provider-specific)
172
- const prompt = this.buildPrompt(plan);
173
- const args = this.buildCliArgs();
174
-
175
- logInfo(this.agent.name, `Running ${this.getCliName()} with model: ${this.agent.model}, timeout: ${this.timeout}s`);
176
-
177
- // 3. Execute subprocess
178
- const env = getInternalSubprocessEnv();
179
- const result = await execFileAsync(cliPath, args, {
180
- input: prompt,
181
- timeout: this.timeout * 1000,
182
- env: env as Record<string, string>,
183
- maxBuffer: 10 * 1024 * 1024,
184
- shell: process.platform === "win32",
185
- });
186
-
187
- // 4. Handle timeout
188
- if (result.killed || result.signal === "SIGTERM") {
189
- return this.handleTimeout();
190
- }
191
-
192
- // 5. Extract output (provider-specific)
193
- const { raw, err } = this.extractOutput(result);
194
-
195
- // 6. Handle exit errors
196
- if (!raw && !err && result.exitCode !== 0) {
197
- return this.handleExitError(result);
198
- }
199
-
200
- // 7. Log subprocess results
201
- this.logSubprocessResult(result, raw, err);
202
-
203
- // 8. Parse JSON output (provider-specific)
204
- const obj = this.parseOutput(raw, result);
205
-
206
- // 9. Log parsed result
207
- this.logParsedResult(obj);
208
-
209
- // 10. Coerce to result type T (provider-specific)
210
- const coerced = this.coerceResult(obj, raw, err);
211
-
212
- // 11. Cleanup (optional override)
213
- await this.cleanup();
214
-
215
- return coerced;
216
- }
217
- }
1
+ /**
2
+ * Abstract base class for CLI-based agent reviewers.
3
+ * Implements template method pattern for subprocess execution flow.
4
+ * Provider-specific implementations (Claude, Codex, Gemini) extend this class.
5
+ */
6
+
7
+ import { logDebug, logInfo, logWarn, logError } from "../../../../_shared/lib-ts/base/logger.js";
8
+ import { getInternalSubprocessEnv, findExecutable, execFileAsync } from "../../../../_shared/lib-ts/base/subprocess-utils.js";
9
+ import { debugLog, debugRaw } from "../../debug.js";
10
+ import type { AgentConfig } from "../../types.js";
11
+
12
+ /** Result from execFileAsync */
13
+ export interface ExecResult {
14
+ stdout: string;
15
+ stderr: string;
16
+ exitCode: number | null;
17
+ signal: string | null;
18
+ killed: boolean;
19
+ }
20
+
21
+ /**
22
+ * Abstract base class for all CLI agent subprocess invocations.
23
+ * Parameterized over return type T — ReviewerResult for reviewers,
24
+ * OrchestratorResult for the orchestrator.
25
+ * Subclasses implement provider-specific details.
26
+ */
27
+ export abstract class BaseCliAgent<T> {
28
+ protected agent: AgentConfig;
29
+ protected contextPath?: string;
30
+ protected schema: Record<string, unknown>;
31
+ protected sessionName: string;
32
+ protected timeout: number;
33
+
34
+ constructor(
35
+ agent: AgentConfig,
36
+ schema: Record<string, unknown>,
37
+ timeout: number,
38
+ contextPath?: string,
39
+ sessionName = "unknown",
40
+ ) {
41
+ this.agent = agent;
42
+ this.schema = schema;
43
+ this.timeout = timeout;
44
+ this.contextPath = contextPath;
45
+ this.sessionName = sessionName;
46
+ }
47
+
48
+ /** Build the command-line arguments for the CLI */
49
+ protected abstract buildCliArgs(): string[];
50
+
51
+ // ─── Abstract Methods (Subclass Implements) ────────────────────────────
52
+
53
+ /** Build the stdin prompt for the CLI */
54
+ protected abstract buildPrompt(plan: string): string;
55
+
56
+ /** Optional cleanup after subprocess execution */
57
+ protected async cleanup(): Promise<void> {
58
+ // Default: no-op. Subclasses override if needed (e.g., Codex temp files).
59
+ }
60
+
61
+ /** Coerce parsed JSON into the result type T */
62
+ protected abstract coerceResult(obj: Record<string, unknown> | null, raw: string, err: string): T;
63
+
64
+ /** Extract stdout/stderr from subprocess result. Override for file-based output (Codex). */
65
+ protected extractOutput(result: ExecResult): { raw: string; err: string } {
66
+ return {
67
+ raw: result.stdout.trim(),
68
+ err: result.stderr.trim(),
69
+ };
70
+ }
71
+
72
+ /** Find the CLI executable. Override for custom search logic. */
73
+ protected findCli(): string | null {
74
+ return findExecutable(this.getCliName());
75
+ }
76
+
77
+ // ─── Template Methods (Subclass Can Override) ──────────────────────────
78
+
79
+ /** Get the CLI executable name (e.g., "claude", "codex") */
80
+ protected abstract getCliName(): string;
81
+
82
+ /** Get default error message for coerceToReview */
83
+ protected getDefaultErrorMessage(): string {
84
+ return `Retry or check ${this.getCliName()} configuration.`;
85
+ }
86
+
87
+ /** Handle non-zero exit with no output */
88
+ protected handleExitError(result: ExecResult): T {
89
+ const msg = `${this.agent.name} failed to run (exit ${result.exitCode})`;
90
+ logError(this.agent.name, `Process exited with code ${result.exitCode} and no output`);
91
+ return this.makeErrorResult("error", msg);
92
+ }
93
+
94
+ /** Handle timeout scenario */
95
+ protected handleTimeout(): T {
96
+ const msg = `${this.getCliName()} TIMEOUT after ${this.timeout}s`;
97
+ logWarn(this.agent.name, msg);
98
+ return this.makeErrorResult("error", `${this.agent.name} timed out after ${this.timeout}s`);
99
+ }
100
+
101
+ // ─── Shared Infrastructure ──────────────────────────────────────────────
102
+
103
+ /** Log parsed JSON result */
104
+ protected logParsedResult(obj: Record<string, unknown> | null): void {
105
+ if (this.contextPath && obj) {
106
+ debugLog(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "parsed_result", {
107
+ parsed_keys: Object.keys(obj),
108
+ verdict: obj.verdict ?? null,
109
+ has_summary: Boolean(obj.summary),
110
+ issues_count: Array.isArray(obj.issues) ? (obj.issues as unknown[]).length : 0,
111
+ });
112
+ }
113
+
114
+ if (obj) {
115
+ logInfo(this.agent.name, `Parsed JSON successfully, verdict: ${obj.verdict ?? "N/A"}`);
116
+ } else {
117
+ logWarn(this.agent.name, "Failed to parse JSON from output");
118
+ }
119
+ }
120
+
121
+ /** Log subprocess execution results */
122
+ protected logSubprocessResult(result: ExecResult, raw: string, err: string): void {
123
+ logDebug(this.agent.name, `Exit code: ${result.exitCode}`);
124
+ logDebug(this.agent.name, `stdout length: ${raw.length} chars`);
125
+ if (err) logDebug(this.agent.name, `stderr: ${err.slice(0, 500)}`);
126
+
127
+ // Debug logging
128
+ if (this.contextPath) {
129
+ debugRaw(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "stdout", raw);
130
+ if (err) {
131
+ debugRaw(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "stderr", err);
132
+ }
133
+ debugLog(this.contextPath, this.sessionName, `agent:${this.agent.name}`, "subprocess_info", {
134
+ exit_code: result.exitCode,
135
+ stdout_len: raw.length,
136
+ stderr_len: err.length,
137
+ model: this.agent.model,
138
+ provider: this.agent.provider,
139
+ timeout: this.timeout,
140
+ });
141
+ }
142
+
143
+ if (raw) logDebug(this.agent.name, `stdout preview: ${raw.slice(0, 500)}`);
144
+ }
145
+
146
+ /** Construct a T for error/skip/timeout scenarios. Subclasses define shape. */
147
+ protected abstract makeErrorResult(type: "skip" | "error", message: string): T;
148
+
149
+ /** Create skip result when CLI not found */
150
+ protected makeSkipResult(reason: string): T {
151
+ logWarn(this.agent.name, reason);
152
+ return this.makeErrorResult("skip", reason);
153
+ }
154
+
155
+ /** Parse JSON from CLI output */
156
+ protected abstract parseOutput(raw: string, result: ExecResult): Record<string, unknown> | null;
157
+
158
+ /**
159
+ * Template method - orchestrates the review flow.
160
+ * Subclasses override abstract methods to customize behavior.
161
+ */
162
+ async review(plan: string): Promise<T> {
163
+ // 1. Find CLI executable
164
+ const cliPath = this.findCli();
165
+ if (!cliPath) {
166
+ return this.makeSkipResult(`${this.getCliName()} CLI not found on PATH`);
167
+ }
168
+
169
+ logDebug(this.agent.name, `Found ${this.getCliName()} CLI at: ${cliPath}`);
170
+
171
+ // 2. Build prompt and args (provider-specific)
172
+ const prompt = this.buildPrompt(plan);
173
+ const args = this.buildCliArgs();
174
+
175
+ logInfo(this.agent.name, `Running ${this.getCliName()} with model: ${this.agent.model}, timeout: ${this.timeout}s`);
176
+
177
+ // 3. Execute subprocess
178
+ const env = getInternalSubprocessEnv();
179
+ const result = await execFileAsync(cliPath, args, {
180
+ input: prompt,
181
+ timeout: this.timeout * 1000,
182
+ env: env as Record<string, string>,
183
+ maxBuffer: 10 * 1024 * 1024,
184
+ shell: process.platform === "win32",
185
+ });
186
+
187
+ // 4. Handle timeout
188
+ if (result.killed || result.signal === "SIGTERM") {
189
+ return this.handleTimeout();
190
+ }
191
+
192
+ // 5. Extract output (provider-specific)
193
+ const { raw, err } = this.extractOutput(result);
194
+
195
+ // 6. Handle exit errors
196
+ if (!raw && !err && result.exitCode !== 0) {
197
+ return this.handleExitError(result);
198
+ }
199
+
200
+ // 7. Log subprocess results
201
+ this.logSubprocessResult(result, raw, err);
202
+
203
+ // 8. Parse JSON output (provider-specific)
204
+ const obj = this.parseOutput(raw, result);
205
+
206
+ // 9. Log parsed result
207
+ this.logParsedResult(obj);
208
+
209
+ // 10. Coerce to result type T (provider-specific)
210
+ const coerced = this.coerceResult(obj, raw, err);
211
+
212
+ // 11. Cleanup (optional override)
213
+ await this.cleanup();
214
+
215
+ return coerced;
216
+ }
217
+ }
@@ -1,12 +1,12 @@
1
- /**
2
- * Reviewers package — re-exports all reviewer implementations.
3
- * See cc-native-plan-review-spec.md §4.9
4
- */
5
-
6
- export { AgentReviewer, runAgentReview } from "./agent.js";
7
- export { BaseCliAgent } from "./base/base-agent.js";
8
- export { ClaudeAgent } from "./providers/claude-agent.js";
9
- export { CodexAgent } from "./providers/codex-agent.js";
10
- export { GeminiAgent } from "./providers/gemini-agent.js";
11
- export type { Reviewer, ReviewerResult, ReviewOptions } from "./types.js";
12
- export { makeResult } from "./types.js";
1
+ /**
2
+ * Reviewers package — re-exports all reviewer implementations.
3
+ * See cc-native-plan-review-spec.md §4.9
4
+ */
5
+
6
+ export { AgentReviewer, runAgentReview } from "./agent.js";
7
+ export { BaseCliAgent } from "./base/base-agent.js";
8
+ export { ClaudeAgent } from "./providers/claude-agent.js";
9
+ export { CodexAgent } from "./providers/codex-agent.js";
10
+ export { GeminiAgent } from "./providers/gemini-agent.js";
11
+ export type { Reviewer, ReviewerResult, ReviewOptions } from "./types.js";
12
+ export { makeResult } from "./types.js";