agents-reverse-engineer 0.3.5 → 0.4.0

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 (162) hide show
  1. package/dist/ai/backends/claude.d.ts +92 -0
  2. package/dist/ai/backends/claude.d.ts.map +1 -0
  3. package/dist/ai/backends/claude.js +213 -0
  4. package/dist/ai/backends/claude.js.map +1 -0
  5. package/dist/ai/backends/gemini.d.ts +53 -0
  6. package/dist/ai/backends/gemini.d.ts.map +1 -0
  7. package/dist/ai/backends/gemini.js +66 -0
  8. package/dist/ai/backends/gemini.js.map +1 -0
  9. package/dist/ai/backends/opencode.d.ts +53 -0
  10. package/dist/ai/backends/opencode.d.ts.map +1 -0
  11. package/dist/ai/backends/opencode.js +66 -0
  12. package/dist/ai/backends/opencode.js.map +1 -0
  13. package/dist/ai/index.d.ts +39 -0
  14. package/dist/ai/index.d.ts.map +1 -0
  15. package/dist/ai/index.js +54 -0
  16. package/dist/ai/index.js.map +1 -0
  17. package/dist/ai/pricing.d.ts +84 -0
  18. package/dist/ai/pricing.d.ts.map +1 -0
  19. package/dist/ai/pricing.js +149 -0
  20. package/dist/ai/pricing.js.map +1 -0
  21. package/dist/ai/pricing.test.d.ts +2 -0
  22. package/dist/ai/pricing.test.d.ts.map +1 -0
  23. package/dist/ai/pricing.test.js +164 -0
  24. package/dist/ai/pricing.test.js.map +1 -0
  25. package/dist/ai/registry.d.ts +128 -0
  26. package/dist/ai/registry.d.ts.map +1 -0
  27. package/dist/ai/registry.js +192 -0
  28. package/dist/ai/registry.js.map +1 -0
  29. package/dist/ai/retry.d.ts +77 -0
  30. package/dist/ai/retry.d.ts.map +1 -0
  31. package/dist/ai/retry.js +100 -0
  32. package/dist/ai/retry.js.map +1 -0
  33. package/dist/ai/service.d.ts +124 -0
  34. package/dist/ai/service.d.ts.map +1 -0
  35. package/dist/ai/service.js +239 -0
  36. package/dist/ai/service.js.map +1 -0
  37. package/dist/ai/subprocess.d.ts +45 -0
  38. package/dist/ai/subprocess.d.ts.map +1 -0
  39. package/dist/ai/subprocess.js +90 -0
  40. package/dist/ai/subprocess.js.map +1 -0
  41. package/dist/ai/telemetry/cleanup.d.ts +30 -0
  42. package/dist/ai/telemetry/cleanup.d.ts.map +1 -0
  43. package/dist/ai/telemetry/cleanup.js +56 -0
  44. package/dist/ai/telemetry/cleanup.js.map +1 -0
  45. package/dist/ai/telemetry/logger.d.ts +76 -0
  46. package/dist/ai/telemetry/logger.d.ts.map +1 -0
  47. package/dist/ai/telemetry/logger.js +130 -0
  48. package/dist/ai/telemetry/logger.js.map +1 -0
  49. package/dist/ai/telemetry/run-log.d.ts +29 -0
  50. package/dist/ai/telemetry/run-log.d.ts.map +1 -0
  51. package/dist/ai/telemetry/run-log.js +43 -0
  52. package/dist/ai/telemetry/run-log.js.map +1 -0
  53. package/dist/ai/types.d.ts +235 -0
  54. package/dist/ai/types.d.ts.map +1 -0
  55. package/dist/ai/types.js +34 -0
  56. package/dist/ai/types.js.map +1 -0
  57. package/dist/cli/discover.d.ts.map +1 -1
  58. package/dist/cli/discover.js +0 -2
  59. package/dist/cli/discover.js.map +1 -1
  60. package/dist/cli/generate.d.ts +22 -14
  61. package/dist/cli/generate.d.ts.map +1 -1
  62. package/dist/cli/generate.js +91 -50
  63. package/dist/cli/generate.js.map +1 -1
  64. package/dist/cli/index.js +12 -3
  65. package/dist/cli/index.js.map +1 -1
  66. package/dist/cli/update.d.ts +13 -4
  67. package/dist/cli/update.d.ts.map +1 -1
  68. package/dist/cli/update.js +93 -131
  69. package/dist/cli/update.js.map +1 -1
  70. package/dist/config/defaults.d.ts +2 -2
  71. package/dist/config/defaults.d.ts.map +1 -1
  72. package/dist/config/defaults.js +2 -0
  73. package/dist/config/defaults.js.map +1 -1
  74. package/dist/config/schema.d.ts +175 -1
  75. package/dist/config/schema.d.ts.map +1 -1
  76. package/dist/config/schema.js +36 -1
  77. package/dist/config/schema.js.map +1 -1
  78. package/dist/generation/executor.d.ts.map +1 -1
  79. package/dist/generation/executor.js +2 -5
  80. package/dist/generation/executor.js.map +1 -1
  81. package/dist/generation/prompts/builder.d.ts +10 -0
  82. package/dist/generation/prompts/builder.d.ts.map +1 -1
  83. package/dist/generation/prompts/builder.js +77 -1
  84. package/dist/generation/prompts/builder.js.map +1 -1
  85. package/dist/generation/prompts/index.d.ts +1 -1
  86. package/dist/generation/prompts/index.d.ts.map +1 -1
  87. package/dist/generation/prompts/index.js +1 -1
  88. package/dist/generation/prompts/index.js.map +1 -1
  89. package/dist/generation/prompts/templates.d.ts +5 -0
  90. package/dist/generation/prompts/templates.d.ts.map +1 -1
  91. package/dist/generation/prompts/templates.js +94 -7
  92. package/dist/generation/prompts/templates.js.map +1 -1
  93. package/dist/generation/prompts/types.d.ts +2 -2
  94. package/dist/generation/prompts/types.js +1 -1
  95. package/dist/generation/writers/agents-md.d.ts +3 -59
  96. package/dist/generation/writers/agents-md.d.ts.map +1 -1
  97. package/dist/generation/writers/agents-md.js +11 -249
  98. package/dist/generation/writers/agents-md.js.map +1 -1
  99. package/dist/generation/writers/index.d.ts +1 -1
  100. package/dist/generation/writers/index.d.ts.map +1 -1
  101. package/dist/generation/writers/index.js +1 -1
  102. package/dist/generation/writers/index.js.map +1 -1
  103. package/dist/installer/uninstall.d.ts.map +1 -1
  104. package/dist/installer/uninstall.js +41 -1
  105. package/dist/installer/uninstall.js.map +1 -1
  106. package/dist/integration/templates.d.ts.map +1 -1
  107. package/dist/integration/templates.js +37 -4
  108. package/dist/integration/templates.js.map +1 -1
  109. package/dist/orchestration/index.d.ts +27 -0
  110. package/dist/orchestration/index.d.ts.map +1 -0
  111. package/dist/orchestration/index.js +34 -0
  112. package/dist/orchestration/index.js.map +1 -0
  113. package/dist/orchestration/pool.d.ts +62 -0
  114. package/dist/orchestration/pool.d.ts.map +1 -0
  115. package/dist/orchestration/pool.js +75 -0
  116. package/dist/orchestration/pool.js.map +1 -0
  117. package/dist/orchestration/progress.d.ts +124 -0
  118. package/dist/orchestration/progress.d.ts.map +1 -0
  119. package/dist/orchestration/progress.js +214 -0
  120. package/dist/orchestration/progress.js.map +1 -0
  121. package/dist/orchestration/runner.d.ts +76 -0
  122. package/dist/orchestration/runner.d.ts.map +1 -0
  123. package/dist/orchestration/runner.js +494 -0
  124. package/dist/orchestration/runner.js.map +1 -0
  125. package/dist/orchestration/types.d.ts +123 -0
  126. package/dist/orchestration/types.d.ts.map +1 -0
  127. package/dist/orchestration/types.js +11 -0
  128. package/dist/orchestration/types.js.map +1 -0
  129. package/dist/quality/density/validator.d.ts +38 -0
  130. package/dist/quality/density/validator.d.ts.map +1 -0
  131. package/dist/quality/density/validator.js +61 -0
  132. package/dist/quality/density/validator.js.map +1 -0
  133. package/dist/quality/inconsistency/code-vs-code.d.ts +26 -0
  134. package/dist/quality/inconsistency/code-vs-code.d.ts.map +1 -0
  135. package/dist/quality/inconsistency/code-vs-code.js +51 -0
  136. package/dist/quality/inconsistency/code-vs-code.js.map +1 -0
  137. package/dist/quality/inconsistency/code-vs-doc.d.ts +35 -0
  138. package/dist/quality/inconsistency/code-vs-doc.d.ts.map +1 -0
  139. package/dist/quality/inconsistency/code-vs-doc.js +59 -0
  140. package/dist/quality/inconsistency/code-vs-doc.js.map +1 -0
  141. package/dist/quality/inconsistency/code-vs-doc.test.d.ts +2 -0
  142. package/dist/quality/inconsistency/code-vs-doc.test.d.ts.map +1 -0
  143. package/dist/quality/inconsistency/code-vs-doc.test.js +196 -0
  144. package/dist/quality/inconsistency/code-vs-doc.test.js.map +1 -0
  145. package/dist/quality/inconsistency/reporter.d.ts +49 -0
  146. package/dist/quality/inconsistency/reporter.d.ts.map +1 -0
  147. package/dist/quality/inconsistency/reporter.js +99 -0
  148. package/dist/quality/inconsistency/reporter.js.map +1 -0
  149. package/dist/quality/index.d.ts +15 -0
  150. package/dist/quality/index.d.ts.map +1 -0
  151. package/dist/quality/index.js +25 -0
  152. package/dist/quality/index.js.map +1 -0
  153. package/dist/quality/types.d.ts +63 -0
  154. package/dist/quality/types.d.ts.map +1 -0
  155. package/dist/quality/types.js +5 -0
  156. package/dist/quality/types.js.map +1 -0
  157. package/dist/update/orchestrator.d.ts.map +1 -1
  158. package/dist/update/orchestrator.js +2 -1
  159. package/dist/update/orchestrator.js.map +1 -1
  160. package/dist/update/orphan-cleaner.js +1 -1
  161. package/dist/update/orphan-cleaner.js.map +1 -1
  162. package/package.json +1 -1
@@ -0,0 +1,239 @@
1
+ /**
2
+ * AI service orchestrator.
3
+ *
4
+ * The {@link AIService} class is the main entry point for making AI calls.
5
+ * It ties together the subprocess wrapper, retry logic, backend selection,
6
+ * and telemetry logging into a clean `call()` method.
7
+ *
8
+ * @module
9
+ */
10
+ import { AIServiceError } from './types.js';
11
+ import { runSubprocess } from './subprocess.js';
12
+ import { withRetry, DEFAULT_RETRY_OPTIONS } from './retry.js';
13
+ import { TelemetryLogger } from './telemetry/logger.js';
14
+ import { writeRunLog } from './telemetry/run-log.js';
15
+ import { cleanupOldLogs } from './telemetry/cleanup.js';
16
+ import { estimateCost } from './pricing.js';
17
+ // ---------------------------------------------------------------------------
18
+ // Rate-limit detection patterns
19
+ // ---------------------------------------------------------------------------
20
+ /** Patterns in stderr that indicate a transient rate-limit error */
21
+ const RATE_LIMIT_PATTERNS = [
22
+ 'rate limit',
23
+ '429',
24
+ 'too many requests',
25
+ 'overloaded',
26
+ ];
27
+ /**
28
+ * Check whether stderr text contains rate-limit indicators.
29
+ *
30
+ * @param stderr - Standard error output from the subprocess
31
+ * @returns `true` if any rate-limit pattern matches (case-insensitive)
32
+ */
33
+ function isRateLimitStderr(stderr) {
34
+ const lower = stderr.toLowerCase();
35
+ return RATE_LIMIT_PATTERNS.some((pattern) => lower.includes(pattern));
36
+ }
37
+ // ---------------------------------------------------------------------------
38
+ // AIService
39
+ // ---------------------------------------------------------------------------
40
+ /**
41
+ * Orchestrates AI CLI calls with retry, timeout, and telemetry.
42
+ *
43
+ * Create one instance per CLI run. Call {@link call} for each AI invocation.
44
+ * Call {@link finalize} at the end to write the run log and clean up old files.
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * import { AIService } from './service.js';
49
+ * import { resolveBackend, createBackendRegistry } from './registry.js';
50
+ *
51
+ * const registry = createBackendRegistry();
52
+ * const backend = await resolveBackend(registry, 'auto');
53
+ * const service = new AIService(backend, {
54
+ * timeoutMs: 120_000,
55
+ * maxRetries: 3,
56
+ * telemetry: { keepRuns: 10 },
57
+ * });
58
+ *
59
+ * const response = await service.call({ prompt: 'Summarize this codebase' });
60
+ * console.log(response.text);
61
+ *
62
+ * const { logPath, summary } = await service.finalize('/path/to/project');
63
+ * console.log(`Log written to ${logPath}, cost: $${summary.totalCostUsd}`);
64
+ * ```
65
+ */
66
+ export class AIService {
67
+ /** The backend adapter used for CLI invocations */
68
+ backend;
69
+ /** Service configuration */
70
+ options;
71
+ /** In-memory telemetry logger for this run */
72
+ logger;
73
+ /** Running count of calls made (used for entry tracking) */
74
+ callCount = 0;
75
+ /** Set of model IDs for which an unknown-pricing warning has already been emitted */
76
+ warnedModels = new Set();
77
+ /**
78
+ * Create a new AI service instance.
79
+ *
80
+ * @param backend - The resolved backend adapter
81
+ * @param options - Service configuration (timeout, retries, telemetry)
82
+ */
83
+ constructor(backend, options) {
84
+ this.backend = backend;
85
+ this.options = options;
86
+ this.logger = new TelemetryLogger(new Date().toISOString());
87
+ }
88
+ /**
89
+ * Make an AI call with retry logic and telemetry recording.
90
+ *
91
+ * The call flow:
92
+ * 1. Build CLI args via the backend adapter
93
+ * 2. Wrap the subprocess invocation in retry logic
94
+ * 3. On success: parse response via backend, record telemetry entry
95
+ * 4. On failure: record error telemetry entry, throw the error
96
+ *
97
+ * Retries are attempted for `RATE_LIMIT` and `TIMEOUT` errors only.
98
+ * All other errors are treated as permanent failures.
99
+ *
100
+ * @param options - The call options (prompt, model, timeout, etc.)
101
+ * @returns The normalized AI response
102
+ * @throws {AIServiceError} On timeout, rate limit exhaustion, parse error, or subprocess failure
103
+ */
104
+ async call(options) {
105
+ this.callCount++;
106
+ const callStart = Date.now();
107
+ const timestamp = new Date().toISOString();
108
+ const args = this.backend.buildArgs(options);
109
+ const timeoutMs = options.timeoutMs ?? this.options.timeoutMs;
110
+ let retryCount = 0;
111
+ try {
112
+ const response = await withRetry(async () => {
113
+ const result = await runSubprocess(this.backend.cliCommand, args, {
114
+ timeoutMs,
115
+ input: options.prompt,
116
+ });
117
+ if (result.timedOut) {
118
+ throw new AIServiceError('TIMEOUT', 'Subprocess timed out');
119
+ }
120
+ if (result.exitCode !== 0) {
121
+ if (isRateLimitStderr(result.stderr)) {
122
+ throw new AIServiceError('RATE_LIMIT', `Rate limited by ${this.backend.name}: ${result.stderr.slice(0, 200)}`);
123
+ }
124
+ throw new AIServiceError('SUBPROCESS_ERROR', `${this.backend.name} CLI exited with code ${result.exitCode}: ${result.stderr.slice(0, 500)}`);
125
+ }
126
+ // Parse the response -- wrap in try/catch for parse errors
127
+ try {
128
+ return this.backend.parseResponse(result.stdout, result.durationMs, result.exitCode);
129
+ }
130
+ catch (error) {
131
+ if (error instanceof AIServiceError) {
132
+ throw error;
133
+ }
134
+ const message = error instanceof Error ? error.message : String(error);
135
+ throw new AIServiceError('PARSE_ERROR', `Failed to parse response: ${message}`);
136
+ }
137
+ }, {
138
+ ...DEFAULT_RETRY_OPTIONS,
139
+ maxRetries: this.options.maxRetries,
140
+ isRetryable: (error) => {
141
+ return (error instanceof AIServiceError &&
142
+ (error.code === 'RATE_LIMIT' || error.code === 'TIMEOUT'));
143
+ },
144
+ onRetry: (_attempt, _error) => {
145
+ retryCount++;
146
+ },
147
+ });
148
+ // Compute cost via pricing engine
149
+ const costResult = estimateCost(response.model, response.inputTokens, response.outputTokens, response.costUsd > 0 ? response.costUsd : undefined, this.options.pricingOverrides);
150
+ // Warn once per unknown model (stderr to preserve JSON stdout)
151
+ if (costResult.source === 'unavailable' && !this.warnedModels.has(response.model)) {
152
+ this.warnedModels.add(response.model);
153
+ console.error(`Warning: No pricing data for model "${response.model}". Cost shown as N/A. Add pricing in config under ai.pricing.`);
154
+ }
155
+ // Record successful call
156
+ this.logger.addEntry({
157
+ timestamp,
158
+ prompt: options.prompt,
159
+ systemPrompt: options.systemPrompt,
160
+ response: response.text,
161
+ model: response.model,
162
+ inputTokens: response.inputTokens,
163
+ outputTokens: response.outputTokens,
164
+ cacheReadTokens: response.cacheReadTokens,
165
+ cacheCreationTokens: response.cacheCreationTokens,
166
+ costUsd: costResult.costUsd,
167
+ latencyMs: response.durationMs,
168
+ exitCode: response.exitCode,
169
+ retryCount,
170
+ thinking: 'not supported',
171
+ filesRead: [],
172
+ costSource: costResult.source,
173
+ });
174
+ return response;
175
+ }
176
+ catch (error) {
177
+ // Record failed call
178
+ const latencyMs = Date.now() - callStart;
179
+ const errorMessage = error instanceof Error ? error.message : String(error);
180
+ this.logger.addEntry({
181
+ timestamp,
182
+ prompt: options.prompt,
183
+ systemPrompt: options.systemPrompt,
184
+ response: '',
185
+ model: options.model ?? 'unknown',
186
+ inputTokens: 0,
187
+ outputTokens: 0,
188
+ cacheReadTokens: 0,
189
+ cacheCreationTokens: 0,
190
+ costUsd: 0,
191
+ latencyMs,
192
+ exitCode: 1,
193
+ error: errorMessage,
194
+ retryCount,
195
+ thinking: 'not supported',
196
+ filesRead: [],
197
+ costSource: 'unavailable',
198
+ });
199
+ throw error;
200
+ }
201
+ }
202
+ /**
203
+ * Finalize the run: write the run log to disk and clean up old files.
204
+ *
205
+ * Call this once at the end of a CLI invocation, after all `call()`
206
+ * invocations have completed (or failed).
207
+ *
208
+ * @param projectRoot - Absolute path to the project root directory
209
+ * @returns The log file path and the run summary
210
+ */
211
+ async finalize(projectRoot) {
212
+ const runLog = this.logger.toRunLog();
213
+ const logPath = await writeRunLog(projectRoot, runLog);
214
+ await cleanupOldLogs(projectRoot, this.options.telemetry.keepRuns);
215
+ return { logPath, summary: runLog.summary };
216
+ }
217
+ /**
218
+ * Attach file-read metadata to the most recent telemetry entry.
219
+ *
220
+ * Called by the command runner after an AI call completes, to record
221
+ * which source files were sent as context for that call.
222
+ *
223
+ * @param filesRead - Array of file-read records (path + size)
224
+ */
225
+ addFilesReadToLastEntry(filesRead) {
226
+ this.logger.setFilesReadOnLastEntry(filesRead);
227
+ }
228
+ /**
229
+ * Get the current run summary without finalizing.
230
+ *
231
+ * Useful for displaying progress during a run.
232
+ *
233
+ * @returns Current summary statistics
234
+ */
235
+ getSummary() {
236
+ return this.logger.getSummary();
237
+ }
238
+ }
239
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/ai/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Low-level subprocess wrapper for AI CLI invocations.
3
+ *
4
+ * This is the ONLY place in the codebase that spawns AI CLI processes.
5
+ * Centralizes timeout enforcement, stdin piping, zombie prevention,
6
+ * and exit code extraction.
7
+ *
8
+ * @module
9
+ */
10
+ import type { SubprocessResult } from './types.js';
11
+ /**
12
+ * Spawn a CLI subprocess with timeout enforcement and stdin piping.
13
+ *
14
+ * Always resolves -- never rejects. Errors are captured in the returned
15
+ * {@link SubprocessResult} fields (`exitCode`, `timedOut`, `stderr`) so
16
+ * that callers can decide how to handle failures.
17
+ *
18
+ * @param command - The CLI executable to run (e.g., "claude", "gemini")
19
+ * @param args - Argument array passed to the executable
20
+ * @param options - Timeout and optional stdin input
21
+ * @returns Resolved result with stdout, stderr, exit code, timing, and timeout flag
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * import { runSubprocess } from './subprocess.js';
26
+ *
27
+ * const result = await runSubprocess('claude', ['-p', '--output-format', 'json'], {
28
+ * timeoutMs: 120_000,
29
+ * input: 'Summarize this codebase',
30
+ * });
31
+ *
32
+ * if (result.timedOut) {
33
+ * console.error('CLI timed out after 120s');
34
+ * } else if (result.exitCode !== 0) {
35
+ * console.error('CLI failed:', result.stderr);
36
+ * } else {
37
+ * const response = JSON.parse(result.stdout);
38
+ * }
39
+ * ```
40
+ */
41
+ export declare function runSubprocess(command: string, args: string[], options: {
42
+ timeoutMs: number;
43
+ input?: string;
44
+ }): Promise<SubprocessResult>;
45
+ //# sourceMappingURL=subprocess.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subprocess.d.ts","sourceRoot":"","sources":["../../src/ai/subprocess.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C,OAAO,CAAC,gBAAgB,CAAC,CAuD3B"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Low-level subprocess wrapper for AI CLI invocations.
3
+ *
4
+ * This is the ONLY place in the codebase that spawns AI CLI processes.
5
+ * Centralizes timeout enforcement, stdin piping, zombie prevention,
6
+ * and exit code extraction.
7
+ *
8
+ * @module
9
+ */
10
+ import { execFile } from 'node:child_process';
11
+ /**
12
+ * Spawn a CLI subprocess with timeout enforcement and stdin piping.
13
+ *
14
+ * Always resolves -- never rejects. Errors are captured in the returned
15
+ * {@link SubprocessResult} fields (`exitCode`, `timedOut`, `stderr`) so
16
+ * that callers can decide how to handle failures.
17
+ *
18
+ * @param command - The CLI executable to run (e.g., "claude", "gemini")
19
+ * @param args - Argument array passed to the executable
20
+ * @param options - Timeout and optional stdin input
21
+ * @returns Resolved result with stdout, stderr, exit code, timing, and timeout flag
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * import { runSubprocess } from './subprocess.js';
26
+ *
27
+ * const result = await runSubprocess('claude', ['-p', '--output-format', 'json'], {
28
+ * timeoutMs: 120_000,
29
+ * input: 'Summarize this codebase',
30
+ * });
31
+ *
32
+ * if (result.timedOut) {
33
+ * console.error('CLI timed out after 120s');
34
+ * } else if (result.exitCode !== 0) {
35
+ * console.error('CLI failed:', result.stderr);
36
+ * } else {
37
+ * const response = JSON.parse(result.stdout);
38
+ * }
39
+ * ```
40
+ */
41
+ export function runSubprocess(command, args, options) {
42
+ return new Promise((resolve) => {
43
+ const startTime = Date.now();
44
+ const child = execFile(command, args, {
45
+ timeout: options.timeoutMs,
46
+ killSignal: 'SIGTERM',
47
+ maxBuffer: 10 * 1024 * 1024, // 10MB for large AI responses
48
+ encoding: 'utf-8',
49
+ }, (error, stdout, stderr) => {
50
+ const durationMs = Date.now() - startTime;
51
+ // Detect timeout: execFile sets `killed = true` when the process
52
+ // is terminated due to exceeding the timeout option.
53
+ const timedOut = error !== null && 'killed' in error && error.killed === true;
54
+ // Extract exit code from the error or child process.
55
+ // execFile puts the exit code in error.code when the process exits
56
+ // with a non-zero code, but error.code can also be a string like
57
+ // 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER'. Fall back to child.exitCode,
58
+ // then default to 1 for unknown failures and 0 for no error.
59
+ let exitCode;
60
+ if (error === null) {
61
+ exitCode = 0;
62
+ }
63
+ else if (typeof error.code === 'number') {
64
+ exitCode = error.code;
65
+ }
66
+ else if (child.exitCode !== null) {
67
+ exitCode = child.exitCode;
68
+ }
69
+ else {
70
+ exitCode = 1;
71
+ }
72
+ resolve({
73
+ stdout: stdout ?? '',
74
+ stderr: stderr ?? '',
75
+ exitCode,
76
+ signal: (error !== null && 'signal' in error ? error.signal : null) ?? null,
77
+ durationMs,
78
+ timedOut,
79
+ });
80
+ });
81
+ // Write prompt to stdin if provided, then close the stream.
82
+ // IMPORTANT: Always call .end() -- the child process blocks waiting
83
+ // for EOF on stdin otherwise (see RESEARCH.md Pitfall 1).
84
+ if (options.input !== undefined && child.stdin !== null) {
85
+ child.stdin.write(options.input);
86
+ child.stdin.end();
87
+ }
88
+ });
89
+ }
90
+ //# sourceMappingURL=subprocess.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subprocess.js","sourceRoot":"","sources":["../../src/ai/subprocess.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAG9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAe,EACf,IAAc,EACd,OAA8C;IAE9C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE7B,MAAM,KAAK,GAAG,QAAQ,CACpB,OAAO,EACP,IAAI,EACJ;YACE,OAAO,EAAE,OAAO,CAAC,SAAS;YAC1B,UAAU,EAAE,SAAS;YACrB,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,8BAA8B;YAC3D,QAAQ,EAAE,OAAO;SAClB,EACD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACxB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YAE1C,iEAAiE;YACjE,qDAAqD;YACrD,MAAM,QAAQ,GAAG,KAAK,KAAK,IAAI,IAAI,QAAQ,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC;YAE9E,qDAAqD;YACrD,mEAAmE;YACnE,iEAAiE;YACjE,oEAAoE;YACpE,6DAA6D;YAC7D,IAAI,QAAgB,CAAC;YACrB,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACnB,QAAQ,GAAG,CAAC,CAAC;YACf,CAAC;iBAAM,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC1C,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC;YACxB,CAAC;iBAAM,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;gBACnC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACN,QAAQ,GAAG,CAAC,CAAC;YACf,CAAC;YAED,OAAO,CAAC;gBACN,MAAM,EAAE,MAAM,IAAI,EAAE;gBACpB,MAAM,EAAE,MAAM,IAAI,EAAE;gBACpB,QAAQ;gBACR,MAAM,EAAE,CAAC,KAAK,KAAK,IAAI,IAAI,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI;gBACrF,UAAU;gBACV,QAAQ;aACT,CAAC,CAAC;QACL,CAAC,CACF,CAAC;QAEF,4DAA4D;QAC5D,oEAAoE;QACpE,0DAA0D;QAC1D,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YACxD,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACjC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACpB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Telemetry log cleanup utility.
3
+ *
4
+ * Removes old run log files from `.agents-reverse-engineer/logs/`,
5
+ * keeping only the N most recent. Files are sorted lexicographically
6
+ * by name, which works correctly because filenames contain ISO timestamps.
7
+ *
8
+ * @module
9
+ */
10
+ /**
11
+ * Remove old telemetry log files, keeping only the most recent ones.
12
+ *
13
+ * Reads the logs directory, filters for files matching `run-*.json`,
14
+ * sorts newest-first (lexicographic sort on ISO timestamp filenames),
15
+ * and deletes everything beyond `keepCount`.
16
+ *
17
+ * If the logs directory does not exist, returns 0 without error.
18
+ *
19
+ * @param projectRoot - Absolute path to the project root directory
20
+ * @param keepCount - Number of most recent log files to retain
21
+ * @returns Number of files deleted
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const deleted = await cleanupOldLogs('/home/user/project', 10);
26
+ * console.log(`Cleaned up ${deleted} old log files`);
27
+ * ```
28
+ */
29
+ export declare function cleanupOldLogs(projectRoot: string, keepCount: number): Promise<number>;
30
+ //# sourceMappingURL=cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../../src/ai/telemetry/cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA4B5F"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Telemetry log cleanup utility.
3
+ *
4
+ * Removes old run log files from `.agents-reverse-engineer/logs/`,
5
+ * keeping only the N most recent. Files are sorted lexicographically
6
+ * by name, which works correctly because filenames contain ISO timestamps.
7
+ *
8
+ * @module
9
+ */
10
+ import * as fs from 'node:fs/promises';
11
+ import * as path from 'node:path';
12
+ /** Directory name for telemetry log files (relative to project root) */
13
+ const LOGS_DIR = '.agents-reverse-engineer/logs';
14
+ /**
15
+ * Remove old telemetry log files, keeping only the most recent ones.
16
+ *
17
+ * Reads the logs directory, filters for files matching `run-*.json`,
18
+ * sorts newest-first (lexicographic sort on ISO timestamp filenames),
19
+ * and deletes everything beyond `keepCount`.
20
+ *
21
+ * If the logs directory does not exist, returns 0 without error.
22
+ *
23
+ * @param projectRoot - Absolute path to the project root directory
24
+ * @param keepCount - Number of most recent log files to retain
25
+ * @returns Number of files deleted
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const deleted = await cleanupOldLogs('/home/user/project', 10);
30
+ * console.log(`Cleaned up ${deleted} old log files`);
31
+ * ```
32
+ */
33
+ export async function cleanupOldLogs(projectRoot, keepCount) {
34
+ const logsDir = path.join(projectRoot, LOGS_DIR);
35
+ let entries;
36
+ try {
37
+ const allEntries = await fs.readdir(logsDir);
38
+ entries = allEntries.filter((name) => name.startsWith('run-') && name.endsWith('.json'));
39
+ }
40
+ catch (error) {
41
+ // Directory doesn't exist -- nothing to clean up
42
+ if (error.code === 'ENOENT') {
43
+ return 0;
44
+ }
45
+ throw error;
46
+ }
47
+ // Sort lexicographically (newest first) and find files to delete
48
+ entries.sort();
49
+ entries.reverse();
50
+ const toDelete = entries.slice(keepCount);
51
+ for (const filename of toDelete) {
52
+ await fs.unlink(path.join(logsDir, filename));
53
+ }
54
+ return toDelete.length;
55
+ }
56
+ //# sourceMappingURL=cleanup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../../../src/ai/telemetry/cleanup.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,wEAAwE;AACxE,MAAM,QAAQ,GAAG,+BAA+B,CAAC;AAEjD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,WAAmB,EAAE,SAAiB;IACzE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAEjD,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7C,OAAO,GAAG,UAAU,CAAC,MAAM,CACzB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAC5D,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,iDAAiD;QACjD,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvD,OAAO,CAAC,CAAC;QACX,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IAED,iEAAiE;IACjE,OAAO,CAAC,IAAI,EAAE,CAAC;IACf,OAAO,CAAC,OAAO,EAAE,CAAC;IAElB,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAE1C,KAAK,MAAM,QAAQ,IAAI,QAAQ,EAAE,CAAC;QAChC,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChD,CAAC;IAED,OAAO,QAAQ,CAAC,MAAM,CAAC;AACzB,CAAC"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * In-memory telemetry logger for AI service calls.
3
+ *
4
+ * Accumulates {@link TelemetryEntry} instances during a run and computes
5
+ * aggregate summaries. The logger is created once per CLI invocation and
6
+ * finalized when the run completes.
7
+ *
8
+ * @module
9
+ */
10
+ import type { TelemetryEntry, RunLog, FileRead } from '../types.js';
11
+ /**
12
+ * Accumulates per-call telemetry entries in memory and produces a
13
+ * complete {@link RunLog} when the run finishes.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const logger = new TelemetryLogger('2026-02-07T12:00:00.000Z');
18
+ * logger.addEntry(entry);
19
+ * const summary = logger.getSummary();
20
+ * const runLog = logger.toRunLog();
21
+ * ```
22
+ */
23
+ export declare class TelemetryLogger {
24
+ /** Unique identifier for this run (ISO timestamp-based) */
25
+ readonly runId: string;
26
+ /** ISO 8601 timestamp when the run started */
27
+ readonly startTime: string;
28
+ /** Accumulated telemetry entries */
29
+ private readonly entries;
30
+ /**
31
+ * Create a new telemetry logger for a run.
32
+ *
33
+ * @param runId - Unique run identifier (typically an ISO timestamp)
34
+ */
35
+ constructor(runId: string);
36
+ /**
37
+ * Record a telemetry entry for a completed AI call.
38
+ *
39
+ * @param entry - The telemetry entry to record
40
+ */
41
+ addEntry(entry: TelemetryEntry): void;
42
+ /**
43
+ * Get all recorded entries as a read-only array.
44
+ *
45
+ * @returns Immutable view of the accumulated entries
46
+ */
47
+ getEntries(): readonly TelemetryEntry[];
48
+ /**
49
+ * Update the most recent entry's filesRead array.
50
+ *
51
+ * Called by the AI service after the command runner attaches file
52
+ * metadata to the last call.
53
+ *
54
+ * @param filesRead - Array of file-read records to attach
55
+ */
56
+ setFilesReadOnLastEntry(filesRead: FileRead[]): void;
57
+ /**
58
+ * Compute aggregate summary statistics from all recorded entries.
59
+ *
60
+ * Totals are computed on every call (not cached) so the summary
61
+ * always reflects the current state of the entries array.
62
+ *
63
+ * @returns Summary with totals for calls, tokens, cost, duration, and errors
64
+ */
65
+ getSummary(): RunLog['summary'];
66
+ /**
67
+ * Assemble the complete {@link RunLog} for this run.
68
+ *
69
+ * Sets `endTime` to the current time, includes all entries, and
70
+ * computes the summary. Call this once when the run is finished.
71
+ *
72
+ * @returns Complete run log ready for serialization
73
+ */
74
+ toRunLog(): RunLog;
75
+ }
76
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/ai/telemetry/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEpE;;;;;;;;;;;GAWG;AACH,qBAAa,eAAe;IAC1B,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,oCAAoC;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAEhD;;;;OAIG;gBACS,KAAK,EAAE,MAAM;IAKzB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAIrC;;;;OAIG;IACH,UAAU,IAAI,SAAS,cAAc,EAAE;IAIvC;;;;;;;OAOG;IACH,uBAAuB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI;IAKpD;;;;;;;OAOG;IACH,UAAU,IAAI,MAAM,CAAC,SAAS,CAAC;IAwC/B;;;;;;;OAOG;IACH,QAAQ,IAAI,MAAM;CASnB"}