nova-terminal-assistant 0.1.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.

Potentially problematic release.


This version of nova-terminal-assistant might be problematic. Click here for more details.

Files changed (192) hide show
  1. package/README.md +358 -0
  2. package/bin/nova +38 -0
  3. package/bin/nova.js +12 -0
  4. package/package.json +67 -0
  5. package/src/cli/commands/SmartCompletion.ts +458 -0
  6. package/src/cli/index.ts +5 -0
  7. package/src/cli/startup/IFlowRepl.ts +212 -0
  8. package/src/cli/startup/InkBasedRepl.ts +1056 -0
  9. package/src/cli/startup/InteractiveRepl.ts +2833 -0
  10. package/src/cli/startup/NovaApp.ts +1861 -0
  11. package/src/cli/startup/index.ts +4 -0
  12. package/src/cli/startup/parseArgs.ts +293 -0
  13. package/src/cli/test-modules.ts +27 -0
  14. package/src/cli/ui/IFlowDropdown.ts +425 -0
  15. package/src/cli/ui/ModernReplUI.ts +276 -0
  16. package/src/cli/ui/SimpleSelector2.ts +215 -0
  17. package/src/cli/ui/components/ConfirmDialog.ts +176 -0
  18. package/src/cli/ui/components/ErrorPanel.ts +364 -0
  19. package/src/cli/ui/components/InkAppRunner.tsx +67 -0
  20. package/src/cli/ui/components/InkComponents.tsx +613 -0
  21. package/src/cli/ui/components/NovaInkApp.tsx +312 -0
  22. package/src/cli/ui/components/ProgressBar.ts +177 -0
  23. package/src/cli/ui/components/ProgressIndicator.ts +298 -0
  24. package/src/cli/ui/components/QuickActions.ts +396 -0
  25. package/src/cli/ui/components/SimpleErrorPanel.ts +231 -0
  26. package/src/cli/ui/components/StatusBar.ts +194 -0
  27. package/src/cli/ui/components/ThinkingBlockRenderer.ts +401 -0
  28. package/src/cli/ui/components/index.ts +27 -0
  29. package/src/cli/ui/ink-prototype.tsx +347 -0
  30. package/src/cli/utils/CliUI.ts +336 -0
  31. package/src/cli/utils/CompletionHelper.ts +388 -0
  32. package/src/cli/utils/EnhancedCompleter.test.ts +226 -0
  33. package/src/cli/utils/EnhancedCompleter.ts +513 -0
  34. package/src/cli/utils/ErrorEnhancer.ts +429 -0
  35. package/src/cli/utils/OutputFormatter.ts +193 -0
  36. package/src/cli/utils/index.ts +9 -0
  37. package/src/core/agents/AgentOrchestrator.ts +515 -0
  38. package/src/core/agents/index.ts +17 -0
  39. package/src/core/audit/AuditLogger.ts +509 -0
  40. package/src/core/audit/index.ts +11 -0
  41. package/src/core/auth/AuthManager.d.ts.map +1 -0
  42. package/src/core/auth/AuthManager.ts +138 -0
  43. package/src/core/auth/index.d.ts.map +1 -0
  44. package/src/core/auth/index.ts +2 -0
  45. package/src/core/config/ConfigManager.d.ts.map +1 -0
  46. package/src/core/config/ConfigManager.test.ts +183 -0
  47. package/src/core/config/ConfigManager.ts +1219 -0
  48. package/src/core/config/index.d.ts.map +1 -0
  49. package/src/core/config/index.ts +1 -0
  50. package/src/core/context/ContextBuilder.d.ts.map +1 -0
  51. package/src/core/context/ContextBuilder.ts +171 -0
  52. package/src/core/context/ContextCompressor.d.ts.map +1 -0
  53. package/src/core/context/ContextCompressor.ts +642 -0
  54. package/src/core/context/LayeredMemoryManager.ts +657 -0
  55. package/src/core/context/MemoryDiscovery.d.ts.map +1 -0
  56. package/src/core/context/MemoryDiscovery.ts +175 -0
  57. package/src/core/context/defaultSystemPrompt.d.ts.map +1 -0
  58. package/src/core/context/defaultSystemPrompt.ts +35 -0
  59. package/src/core/context/index.d.ts.map +1 -0
  60. package/src/core/context/index.ts +22 -0
  61. package/src/core/extensions/SkillGenerator.ts +421 -0
  62. package/src/core/extensions/SkillInstaller.d.ts.map +1 -0
  63. package/src/core/extensions/SkillInstaller.ts +257 -0
  64. package/src/core/extensions/SkillRegistry.d.ts.map +1 -0
  65. package/src/core/extensions/SkillRegistry.ts +361 -0
  66. package/src/core/extensions/SkillValidator.ts +525 -0
  67. package/src/core/extensions/index.ts +15 -0
  68. package/src/core/index.d.ts.map +1 -0
  69. package/src/core/index.ts +42 -0
  70. package/src/core/mcp/McpManager.d.ts.map +1 -0
  71. package/src/core/mcp/McpManager.ts +632 -0
  72. package/src/core/mcp/index.d.ts.map +1 -0
  73. package/src/core/mcp/index.ts +2 -0
  74. package/src/core/model/ModelClient.d.ts.map +1 -0
  75. package/src/core/model/ModelClient.ts +217 -0
  76. package/src/core/model/ModelConnectionTester.ts +363 -0
  77. package/src/core/model/ModelValidator.ts +348 -0
  78. package/src/core/model/index.d.ts.map +1 -0
  79. package/src/core/model/index.ts +6 -0
  80. package/src/core/model/providers/AnthropicProvider.d.ts.map +1 -0
  81. package/src/core/model/providers/AnthropicProvider.ts +279 -0
  82. package/src/core/model/providers/CodingPlanProvider.d.ts.map +1 -0
  83. package/src/core/model/providers/CodingPlanProvider.ts +210 -0
  84. package/src/core/model/providers/OllamaCloudProvider.d.ts.map +1 -0
  85. package/src/core/model/providers/OllamaCloudProvider.ts +405 -0
  86. package/src/core/model/providers/OllamaManager.d.ts.map +1 -0
  87. package/src/core/model/providers/OllamaManager.ts +201 -0
  88. package/src/core/model/providers/OllamaProvider.d.ts.map +1 -0
  89. package/src/core/model/providers/OllamaProvider.ts +73 -0
  90. package/src/core/model/providers/OpenAICompatibleProvider.d.ts.map +1 -0
  91. package/src/core/model/providers/OpenAICompatibleProvider.ts +327 -0
  92. package/src/core/model/providers/OpenAIProvider.d.ts.map +1 -0
  93. package/src/core/model/providers/OpenAIProvider.ts +29 -0
  94. package/src/core/model/providers/index.d.ts.map +1 -0
  95. package/src/core/model/providers/index.ts +12 -0
  96. package/src/core/model/types.d.ts.map +1 -0
  97. package/src/core/model/types.ts +77 -0
  98. package/src/core/security/ApprovalManager.d.ts.map +1 -0
  99. package/src/core/security/ApprovalManager.ts +174 -0
  100. package/src/core/security/FileFilter.d.ts.map +1 -0
  101. package/src/core/security/FileFilter.ts +141 -0
  102. package/src/core/security/HookExecutor.d.ts.map +1 -0
  103. package/src/core/security/HookExecutor.ts +178 -0
  104. package/src/core/security/SandboxExecutor.ts +447 -0
  105. package/src/core/security/index.d.ts.map +1 -0
  106. package/src/core/security/index.ts +8 -0
  107. package/src/core/session/AgentLoop.d.ts.map +1 -0
  108. package/src/core/session/AgentLoop.ts +501 -0
  109. package/src/core/session/SessionManager.d.ts.map +1 -0
  110. package/src/core/session/SessionManager.test.ts +183 -0
  111. package/src/core/session/SessionManager.ts +460 -0
  112. package/src/core/session/index.d.ts.map +1 -0
  113. package/src/core/session/index.ts +3 -0
  114. package/src/core/telemetry/Telemetry.d.ts.map +1 -0
  115. package/src/core/telemetry/Telemetry.ts +90 -0
  116. package/src/core/telemetry/TelemetryService.ts +531 -0
  117. package/src/core/telemetry/index.d.ts.map +1 -0
  118. package/src/core/telemetry/index.ts +12 -0
  119. package/src/core/testing/AutoFixer.ts +385 -0
  120. package/src/core/testing/ErrorAnalyzer.ts +499 -0
  121. package/src/core/testing/TestRunner.ts +265 -0
  122. package/src/core/testing/agent-cli-tests.ts +538 -0
  123. package/src/core/testing/index.ts +11 -0
  124. package/src/core/tools/ToolRegistry.d.ts.map +1 -0
  125. package/src/core/tools/ToolRegistry.test.ts +206 -0
  126. package/src/core/tools/ToolRegistry.ts +260 -0
  127. package/src/core/tools/impl/EditFileTool.d.ts.map +1 -0
  128. package/src/core/tools/impl/EditFileTool.ts +97 -0
  129. package/src/core/tools/impl/ListDirectoryTool.d.ts.map +1 -0
  130. package/src/core/tools/impl/ListDirectoryTool.ts +142 -0
  131. package/src/core/tools/impl/MemoryTool.d.ts.map +1 -0
  132. package/src/core/tools/impl/MemoryTool.ts +102 -0
  133. package/src/core/tools/impl/ReadFileTool.d.ts.map +1 -0
  134. package/src/core/tools/impl/ReadFileTool.ts +58 -0
  135. package/src/core/tools/impl/SearchContentTool.d.ts.map +1 -0
  136. package/src/core/tools/impl/SearchContentTool.ts +94 -0
  137. package/src/core/tools/impl/SearchFileTool.d.ts.map +1 -0
  138. package/src/core/tools/impl/SearchFileTool.ts +61 -0
  139. package/src/core/tools/impl/ShellTool.d.ts.map +1 -0
  140. package/src/core/tools/impl/ShellTool.ts +118 -0
  141. package/src/core/tools/impl/TaskTool.d.ts.map +1 -0
  142. package/src/core/tools/impl/TaskTool.ts +207 -0
  143. package/src/core/tools/impl/TodoTool.d.ts.map +1 -0
  144. package/src/core/tools/impl/TodoTool.ts +122 -0
  145. package/src/core/tools/impl/WebFetchTool.d.ts.map +1 -0
  146. package/src/core/tools/impl/WebFetchTool.ts +103 -0
  147. package/src/core/tools/impl/WebSearchTool.d.ts.map +1 -0
  148. package/src/core/tools/impl/WebSearchTool.ts +89 -0
  149. package/src/core/tools/impl/WriteFileTool.d.ts.map +1 -0
  150. package/src/core/tools/impl/WriteFileTool.ts +49 -0
  151. package/src/core/tools/impl/index.d.ts.map +1 -0
  152. package/src/core/tools/impl/index.ts +16 -0
  153. package/src/core/tools/index.d.ts.map +1 -0
  154. package/src/core/tools/index.ts +7 -0
  155. package/src/core/tools/schemas/execution.d.ts.map +1 -0
  156. package/src/core/tools/schemas/execution.ts +42 -0
  157. package/src/core/tools/schemas/file.d.ts.map +1 -0
  158. package/src/core/tools/schemas/file.ts +119 -0
  159. package/src/core/tools/schemas/index.d.ts.map +1 -0
  160. package/src/core/tools/schemas/index.ts +11 -0
  161. package/src/core/tools/schemas/memory.d.ts.map +1 -0
  162. package/src/core/tools/schemas/memory.ts +52 -0
  163. package/src/core/tools/schemas/orchestration.d.ts.map +1 -0
  164. package/src/core/tools/schemas/orchestration.ts +44 -0
  165. package/src/core/tools/schemas/search.d.ts.map +1 -0
  166. package/src/core/tools/schemas/search.ts +112 -0
  167. package/src/core/tools/schemas/todo.d.ts.map +1 -0
  168. package/src/core/tools/schemas/todo.ts +32 -0
  169. package/src/core/tools/schemas/web.d.ts.map +1 -0
  170. package/src/core/tools/schemas/web.ts +86 -0
  171. package/src/core/types/config.d.ts.map +1 -0
  172. package/src/core/types/config.ts +200 -0
  173. package/src/core/types/errors.d.ts.map +1 -0
  174. package/src/core/types/errors.ts +204 -0
  175. package/src/core/types/index.d.ts.map +1 -0
  176. package/src/core/types/index.ts +8 -0
  177. package/src/core/types/session.d.ts.map +1 -0
  178. package/src/core/types/session.ts +216 -0
  179. package/src/core/types/tools.d.ts.map +1 -0
  180. package/src/core/types/tools.ts +157 -0
  181. package/src/core/utils/CheckpointManager.d.ts.map +1 -0
  182. package/src/core/utils/CheckpointManager.ts +327 -0
  183. package/src/core/utils/Logger.d.ts.map +1 -0
  184. package/src/core/utils/Logger.ts +98 -0
  185. package/src/core/utils/RetryManager.ts +471 -0
  186. package/src/core/utils/TokenCounter.d.ts.map +1 -0
  187. package/src/core/utils/TokenCounter.ts +414 -0
  188. package/src/core/utils/VectorMemoryStore.ts +440 -0
  189. package/src/core/utils/helpers.d.ts.map +1 -0
  190. package/src/core/utils/helpers.ts +89 -0
  191. package/src/core/utils/index.d.ts.map +1 -0
  192. package/src/core/utils/index.ts +19 -0
@@ -0,0 +1,385 @@
1
+ // ============================================================================
2
+ // AutoFixer - Self-healing test-fix loop engine
3
+ // Reference: Aider-style automatic code repair cycle
4
+ // ============================================================================
5
+
6
+ import { createMessageId, createSessionId } from '../types/session.js';
7
+ import type { ModelClient } from '../model/ModelClient.js';
8
+ import type { TestRunner } from './TestRunner.js';
9
+ import type { ErrorAnalyzer, FixSuggestion } from './ErrorAnalyzer.js';
10
+ import type { Message } from '../types/session.js';
11
+
12
+ export interface FixIteration {
13
+ /** Iteration number (1-based) */
14
+ iteration: number;
15
+ /** Errors being addressed */
16
+ errors: string[];
17
+ /** Fix prompt sent to the model */
18
+ fixPrompt: string;
19
+ /** Raw model response */
20
+ modelResponse: string;
21
+ /** Files modified in this iteration */
22
+ filesModified: string[];
23
+ /** Duration in ms */
24
+ duration: number;
25
+ /** Whether this iteration succeeded */
26
+ success: boolean;
27
+ }
28
+
29
+ export interface FixResult {
30
+ /** Whether all errors were fixed */
31
+ success: boolean;
32
+ /** Total iterations attempted */
33
+ iterations: FixIteration[];
34
+ /** Final test result (if available) */
35
+ finalTestSuccess?: boolean;
36
+ /** Total time spent fixing */
37
+ totalDuration: number;
38
+ /** Summary of what was fixed */
39
+ summary: string;
40
+ }
41
+
42
+ export interface AutoFixerOptions {
43
+ /** Model client for generating fixes */
44
+ modelClient: ModelClient;
45
+ /** Test runner for executing tests */
46
+ testRunner: TestRunner;
47
+ /** Error analyzer for classifying errors */
48
+ errorAnalyzer: ErrorAnalyzer;
49
+ /** Maximum fix iterations (default: 3) */
50
+ maxIterations?: number;
51
+ /** Test command to run */
52
+ testCommand: string;
53
+ /** Maximum time per fix iteration in ms (default: 120000) */
54
+ iterationTimeout?: number;
55
+ /** Working directory */
56
+ cwd?: string;
57
+ /** Additional context for the model */
58
+ context?: string;
59
+ /** Called when a fix iteration starts */
60
+ onIterationStart?: (iteration: number, errors: string[]) => void;
61
+ /** Called when a fix iteration completes */
62
+ onIterationComplete?: (iteration: FixIteration) => void;
63
+ /** Called when the fix process completes */
64
+ onComplete?: (result: FixResult) => void;
65
+ }
66
+
67
+ export class AutoFixer {
68
+ private options: Required<
69
+ Pick<
70
+ AutoFixerOptions,
71
+ | 'modelClient'
72
+ | 'testRunner'
73
+ | 'errorAnalyzer'
74
+ | 'maxIterations'
75
+ | 'testCommand'
76
+ | 'iterationTimeout'
77
+ | 'cwd'
78
+ | 'context'
79
+ >
80
+ > & Omit<AutoFixerOptions, keyof Pick<AutoFixerOptions, 'modelClient' | 'testRunner' | 'errorAnalyzer' | 'maxIterations' | 'testCommand' | 'iterationTimeout' | 'cwd' | 'context'>>;
81
+
82
+ constructor(options: AutoFixerOptions) {
83
+ this.options = {
84
+ modelClient: options.modelClient,
85
+ testRunner: options.testRunner,
86
+ errorAnalyzer: options.errorAnalyzer,
87
+ maxIterations: options.maxIterations || 3,
88
+ testCommand: options.testCommand,
89
+ iterationTimeout: options.iterationTimeout || 120000,
90
+ cwd: options.cwd || process.cwd(),
91
+ context: options.context || '',
92
+ onIterationStart: options.onIterationStart,
93
+ onIterationComplete: options.onIterationComplete,
94
+ onComplete: options.onComplete,
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Run the full self-healing fix loop.
100
+ * 1. Run tests
101
+ * 2. If failures, analyze errors
102
+ * 3. Generate fixes
103
+ * 4. Apply fixes
104
+ * 5. Re-run tests
105
+ * 6. Repeat until fixed or max iterations
106
+ */
107
+ async fix(testOutput?: string): Promise<FixResult> {
108
+ const startTime = Date.now();
109
+ const iterations: FixIteration[] = [];
110
+
111
+ // Step 1: Run tests (if no output provided)
112
+ const testResult = testOutput
113
+ ? null
114
+ : await this.runTests();
115
+
116
+ const combinedOutput = testOutput || (testResult ? `${testResult.output}\n${testResult.errors}` : '');
117
+
118
+ // Step 2: Analyze errors
119
+ const analysis = this.options.errorAnalyzer.analyze(combinedOutput);
120
+
121
+ if (analysis.suggestions.length === 0) {
122
+ const result: FixResult = {
123
+ success: true,
124
+ iterations: [],
125
+ finalTestSuccess: true,
126
+ totalDuration: Date.now() - startTime,
127
+ summary: 'No errors detected. Tests appear to be passing.',
128
+ };
129
+ this.options.onComplete?.(result);
130
+ return result;
131
+ }
132
+
133
+ // Step 3-6: Fix loop
134
+ let currentOutput = combinedOutput;
135
+ let currentErrors = analysis.suggestions;
136
+ let fixSuccess = false;
137
+
138
+ for (let i = 1; i <= this.options.maxIterations; i++) {
139
+ this.options.onIterationStart?.(i, currentErrors.map((e) => e.description));
140
+
141
+ const iteration = await this.runFixIteration(i, currentErrors, currentOutput);
142
+ iterations.push(iteration);
143
+
144
+ this.options.onIterationComplete?.(iteration);
145
+
146
+ if (iteration.success) {
147
+ // Re-run tests to verify
148
+ const verifyResult = await this.runTests();
149
+ if (verifyResult.success) {
150
+ fixSuccess = true;
151
+ break;
152
+ }
153
+
154
+ // Tests still failing - analyze remaining errors
155
+ currentOutput = `${verifyResult.output}\n${verifyResult.errors}`;
156
+ const newAnalysis = this.options.errorAnalyzer.analyze(currentOutput);
157
+
158
+ // Check if we made progress (fewer errors)
159
+ if (newAnalysis.suggestions.length >= currentErrors.length) {
160
+ // No progress - stop to avoid infinite loop
161
+ break;
162
+ }
163
+
164
+ currentErrors = newAnalysis.suggestions;
165
+ }
166
+ }
167
+
168
+ const totalTime = Date.now() - startTime;
169
+ const result: FixResult = {
170
+ success: fixSuccess,
171
+ iterations,
172
+ totalDuration: totalTime,
173
+ summary: this.generateSummary(fixSuccess, iterations),
174
+ };
175
+
176
+ this.options.onComplete?.(result);
177
+ return result;
178
+ }
179
+
180
+ /**
181
+ * Run a single fix iteration.
182
+ */
183
+ private async runFixIteration(
184
+ iteration: number,
185
+ errors: FixSuggestion[],
186
+ testOutput: string
187
+ ): Promise<FixIteration> {
188
+ const startTime = Date.now();
189
+
190
+ // Build the fix prompt for the model
191
+ const fixPrompt = this.buildFixPrompt(errors, testOutput);
192
+
193
+ // Call the model to generate fixes
194
+ let modelResponse: string;
195
+ try {
196
+ const messages: Message[] = [
197
+ {
198
+ id: createMessageId(`fix-system-${iteration}`),
199
+ role: 'user',
200
+ content: [
201
+ {
202
+ type: 'text',
203
+ text: fixPrompt,
204
+ },
205
+ ],
206
+ timestamp: Date.now(),
207
+ createdAt: new Date(),
208
+ },
209
+ ];
210
+
211
+ const response = await this.options.modelClient.complete(
212
+ messages,
213
+ [],
214
+ createSessionId(`session-${Date.now()}`),
215
+ { systemPrompt: this.getSystemPrompt() }
216
+ );
217
+
218
+ // Extract text from response
219
+ modelResponse =
220
+ response.content
221
+ ?.filter((b) => b.type === 'text')
222
+ .map((b) => b.text)
223
+ .join('\n') || '';
224
+ } catch (err: any) {
225
+ return {
226
+ iteration,
227
+ errors: errors.map((e) => e.description),
228
+ fixPrompt,
229
+ modelResponse: `Model error: ${err.message}`,
230
+ filesModified: [],
231
+ duration: Date.now() - startTime,
232
+ success: false,
233
+ };
234
+ }
235
+
236
+ // Extract modified files from model response
237
+ const filesModified = this.extractFilesFromResponse(modelResponse);
238
+
239
+ return {
240
+ iteration,
241
+ errors: errors.map((e) => e.description),
242
+ fixPrompt,
243
+ modelResponse,
244
+ filesModified,
245
+ duration: Date.now() - startTime,
246
+ success: filesModified.length > 0 && modelResponse.length > 100,
247
+ };
248
+ }
249
+
250
+ /**
251
+ * Build a detailed fix prompt for the model.
252
+ */
253
+ private buildFixPrompt(errors: FixSuggestion[], testOutput: string): string {
254
+ const prioritized = this.options.errorAnalyzer.prioritizeErrors(errors);
255
+ const autoFixable = prioritized.filter((e) => e.autoFixable);
256
+
257
+ let prompt = `## Fix Request - Iteration Context\n\n`;
258
+ prompt += `The following errors need to be fixed:\n\n`;
259
+
260
+ for (let i = 0; i < prioritized.length; i++) {
261
+ const error = prioritized[i];
262
+ prompt += `### Error ${i + 1}: ${error.errorType} (confidence: ${Math.round(error.confidence * 100)}%)\n`;
263
+ prompt += `- **File**: ${error.file}${error.line ? `:${error.line}` : ''}\n`;
264
+ prompt += `- **Description**: ${error.description}\n`;
265
+ prompt += `- **Suggested approach**: ${error.fixApproach}\n`;
266
+ if (error.searchPatterns.length > 0) {
267
+ prompt += `- **Search patterns**: ${error.searchPatterns.join(', ')}\n`;
268
+ }
269
+ if (error.autoFixable) {
270
+ prompt += `- **Auto-fixable**: Yes\n`;
271
+ }
272
+ prompt += '\n';
273
+ }
274
+
275
+ if (autoFixable.length > 0) {
276
+ prompt += `\n**Priority**: Fix these auto-fixable errors first:\n`;
277
+ for (const e of autoFixable) {
278
+ prompt += `- ${e.file}: ${e.description}\n`;
279
+ }
280
+ prompt += '\n';
281
+ }
282
+
283
+ prompt += `\n## Relevant Test Output\n\n\`\`\`\n${this.truncateOutput(testOutput, 3000)}\n\`\`\`\n\n`;
284
+
285
+ if (this.options.context) {
286
+ prompt += `\n## Project Context\n\n${this.options.context}\n\n`;
287
+ }
288
+
289
+ prompt += `## Instructions\n\n`;
290
+ prompt += `1. For each error, show the exact file that needs to be modified\n`;
291
+ prompt += `2. Show the complete fixed code block with file path\n`;
292
+ prompt += `3. Explain what changed and why\n`;
293
+ prompt += `4. Format: use \`\`\`filepath\n// code here\n\`\`\` for each file change\n\n`;
294
+ prompt += `Focus ONLY on fixing the listed errors. Do not refactor or change unrelated code.`;
295
+
296
+ return prompt;
297
+ }
298
+
299
+ /**
300
+ * Get the system prompt for the fix model.
301
+ */
302
+ private getSystemPrompt(): string {
303
+ return `You are an expert code repair assistant. Your job is to fix specific errors identified by automated testing.
304
+
305
+ Rules:
306
+ - Only modify code that directly relates to the reported errors
307
+ - Preserve existing code style, formatting, and conventions
308
+ - Show complete file contents for modified files (not just diffs)
309
+ - Explain each fix briefly
310
+ - If an error is unclear, make the minimal change most likely to fix it
311
+ - Never introduce new imports or dependencies unless the error requires it
312
+ - Never delete tests or test assertions
313
+ - Use the same language as the existing code for comments and strings`;
314
+ }
315
+
316
+ /**
317
+ * Extract file paths mentioned in the model response.
318
+ */
319
+ private extractFilesFromResponse(response: string): string[] {
320
+ const files: string[] = [];
321
+ const filePattern = /```(?:typescript|javascript|python|go|rust|java|ruby)?\s*\n\/\/\s*(?:file:\s*)?([^\n]+\.(?:ts|tsx|js|jsx|py|go|rs|java|rb))/gi;
322
+
323
+ let match: RegExpExecArray | null;
324
+ while ((match = filePattern.exec(response)) !== null) {
325
+ const file = match[1].trim();
326
+ if (file && !files.includes(file)) {
327
+ files.push(file);
328
+ }
329
+ }
330
+
331
+ // Also try simple code block patterns
332
+ const simplePattern = /```(?:\w+)?\s*\n([\s\S]*?)```/g;
333
+ while ((match = simplePattern.exec(response)) !== null) {
334
+ const content = match[1];
335
+ const filePathMatch = content.match(/^(?:\/\/|#)\s*(?:file:\s*)?([^\n]+\.(?:ts|tsx|js|jsx|py|go|rs|java|rb))/m);
336
+ if (filePathMatch) {
337
+ const file = filePathMatch[1].trim();
338
+ if (file && !files.includes(file)) {
339
+ files.push(file);
340
+ }
341
+ }
342
+ }
343
+
344
+ return files;
345
+ }
346
+
347
+ /**
348
+ * Run the test command.
349
+ */
350
+ private async runTests() {
351
+ return this.options.testRunner.run(this.options.testCommand);
352
+ }
353
+
354
+ /**
355
+ * Truncate output to prevent prompt overflow.
356
+ */
357
+ private truncateOutput(output: string, maxChars: number): string {
358
+ if (output.length <= maxChars) return output;
359
+
360
+ // Keep the beginning (context) and end (most recent errors)
361
+ const headSize = Math.floor(maxChars * 0.3);
362
+ const tailSize = maxChars - headSize;
363
+
364
+ const head = output.substring(0, headSize);
365
+ const tail = output.substring(output.length - tailSize);
366
+
367
+ return `${head}\n\n... [truncated ${output.length - maxChars} characters] ...\n\n${tail}`;
368
+ }
369
+
370
+ /**
371
+ * Generate a summary of the fix process.
372
+ */
373
+ private generateSummary(success: boolean, iterations: FixIteration[]): string {
374
+ if (success) {
375
+ return `All errors fixed in ${iterations.length} iteration(s). ${iterations.reduce((sum, i) => sum + i.filesModified.length, 0)} file(s) modified.`;
376
+ }
377
+
378
+ if (iterations.length === 0) {
379
+ return 'No fix iterations were attempted.';
380
+ }
381
+
382
+ const lastIteration = iterations[iterations.length - 1];
383
+ return `Fix attempt concluded after ${iterations.length} iteration(s) without resolving all errors. ${lastIteration.filesModified.length} file(s) were modified in the last iteration.`;
384
+ }
385
+ }