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.
- package/README.md +358 -0
- package/bin/nova +38 -0
- package/bin/nova.js +12 -0
- package/package.json +67 -0
- package/src/cli/commands/SmartCompletion.ts +458 -0
- package/src/cli/index.ts +5 -0
- package/src/cli/startup/IFlowRepl.ts +212 -0
- package/src/cli/startup/InkBasedRepl.ts +1056 -0
- package/src/cli/startup/InteractiveRepl.ts +2833 -0
- package/src/cli/startup/NovaApp.ts +1861 -0
- package/src/cli/startup/index.ts +4 -0
- package/src/cli/startup/parseArgs.ts +293 -0
- package/src/cli/test-modules.ts +27 -0
- package/src/cli/ui/IFlowDropdown.ts +425 -0
- package/src/cli/ui/ModernReplUI.ts +276 -0
- package/src/cli/ui/SimpleSelector2.ts +215 -0
- package/src/cli/ui/components/ConfirmDialog.ts +176 -0
- package/src/cli/ui/components/ErrorPanel.ts +364 -0
- package/src/cli/ui/components/InkAppRunner.tsx +67 -0
- package/src/cli/ui/components/InkComponents.tsx +613 -0
- package/src/cli/ui/components/NovaInkApp.tsx +312 -0
- package/src/cli/ui/components/ProgressBar.ts +177 -0
- package/src/cli/ui/components/ProgressIndicator.ts +298 -0
- package/src/cli/ui/components/QuickActions.ts +396 -0
- package/src/cli/ui/components/SimpleErrorPanel.ts +231 -0
- package/src/cli/ui/components/StatusBar.ts +194 -0
- package/src/cli/ui/components/ThinkingBlockRenderer.ts +401 -0
- package/src/cli/ui/components/index.ts +27 -0
- package/src/cli/ui/ink-prototype.tsx +347 -0
- package/src/cli/utils/CliUI.ts +336 -0
- package/src/cli/utils/CompletionHelper.ts +388 -0
- package/src/cli/utils/EnhancedCompleter.test.ts +226 -0
- package/src/cli/utils/EnhancedCompleter.ts +513 -0
- package/src/cli/utils/ErrorEnhancer.ts +429 -0
- package/src/cli/utils/OutputFormatter.ts +193 -0
- package/src/cli/utils/index.ts +9 -0
- package/src/core/agents/AgentOrchestrator.ts +515 -0
- package/src/core/agents/index.ts +17 -0
- package/src/core/audit/AuditLogger.ts +509 -0
- package/src/core/audit/index.ts +11 -0
- package/src/core/auth/AuthManager.d.ts.map +1 -0
- package/src/core/auth/AuthManager.ts +138 -0
- package/src/core/auth/index.d.ts.map +1 -0
- package/src/core/auth/index.ts +2 -0
- package/src/core/config/ConfigManager.d.ts.map +1 -0
- package/src/core/config/ConfigManager.test.ts +183 -0
- package/src/core/config/ConfigManager.ts +1219 -0
- package/src/core/config/index.d.ts.map +1 -0
- package/src/core/config/index.ts +1 -0
- package/src/core/context/ContextBuilder.d.ts.map +1 -0
- package/src/core/context/ContextBuilder.ts +171 -0
- package/src/core/context/ContextCompressor.d.ts.map +1 -0
- package/src/core/context/ContextCompressor.ts +642 -0
- package/src/core/context/LayeredMemoryManager.ts +657 -0
- package/src/core/context/MemoryDiscovery.d.ts.map +1 -0
- package/src/core/context/MemoryDiscovery.ts +175 -0
- package/src/core/context/defaultSystemPrompt.d.ts.map +1 -0
- package/src/core/context/defaultSystemPrompt.ts +35 -0
- package/src/core/context/index.d.ts.map +1 -0
- package/src/core/context/index.ts +22 -0
- package/src/core/extensions/SkillGenerator.ts +421 -0
- package/src/core/extensions/SkillInstaller.d.ts.map +1 -0
- package/src/core/extensions/SkillInstaller.ts +257 -0
- package/src/core/extensions/SkillRegistry.d.ts.map +1 -0
- package/src/core/extensions/SkillRegistry.ts +361 -0
- package/src/core/extensions/SkillValidator.ts +525 -0
- package/src/core/extensions/index.ts +15 -0
- package/src/core/index.d.ts.map +1 -0
- package/src/core/index.ts +42 -0
- package/src/core/mcp/McpManager.d.ts.map +1 -0
- package/src/core/mcp/McpManager.ts +632 -0
- package/src/core/mcp/index.d.ts.map +1 -0
- package/src/core/mcp/index.ts +2 -0
- package/src/core/model/ModelClient.d.ts.map +1 -0
- package/src/core/model/ModelClient.ts +217 -0
- package/src/core/model/ModelConnectionTester.ts +363 -0
- package/src/core/model/ModelValidator.ts +348 -0
- package/src/core/model/index.d.ts.map +1 -0
- package/src/core/model/index.ts +6 -0
- package/src/core/model/providers/AnthropicProvider.d.ts.map +1 -0
- package/src/core/model/providers/AnthropicProvider.ts +279 -0
- package/src/core/model/providers/CodingPlanProvider.d.ts.map +1 -0
- package/src/core/model/providers/CodingPlanProvider.ts +210 -0
- package/src/core/model/providers/OllamaCloudProvider.d.ts.map +1 -0
- package/src/core/model/providers/OllamaCloudProvider.ts +405 -0
- package/src/core/model/providers/OllamaManager.d.ts.map +1 -0
- package/src/core/model/providers/OllamaManager.ts +201 -0
- package/src/core/model/providers/OllamaProvider.d.ts.map +1 -0
- package/src/core/model/providers/OllamaProvider.ts +73 -0
- package/src/core/model/providers/OpenAICompatibleProvider.d.ts.map +1 -0
- package/src/core/model/providers/OpenAICompatibleProvider.ts +327 -0
- package/src/core/model/providers/OpenAIProvider.d.ts.map +1 -0
- package/src/core/model/providers/OpenAIProvider.ts +29 -0
- package/src/core/model/providers/index.d.ts.map +1 -0
- package/src/core/model/providers/index.ts +12 -0
- package/src/core/model/types.d.ts.map +1 -0
- package/src/core/model/types.ts +77 -0
- package/src/core/security/ApprovalManager.d.ts.map +1 -0
- package/src/core/security/ApprovalManager.ts +174 -0
- package/src/core/security/FileFilter.d.ts.map +1 -0
- package/src/core/security/FileFilter.ts +141 -0
- package/src/core/security/HookExecutor.d.ts.map +1 -0
- package/src/core/security/HookExecutor.ts +178 -0
- package/src/core/security/SandboxExecutor.ts +447 -0
- package/src/core/security/index.d.ts.map +1 -0
- package/src/core/security/index.ts +8 -0
- package/src/core/session/AgentLoop.d.ts.map +1 -0
- package/src/core/session/AgentLoop.ts +501 -0
- package/src/core/session/SessionManager.d.ts.map +1 -0
- package/src/core/session/SessionManager.test.ts +183 -0
- package/src/core/session/SessionManager.ts +460 -0
- package/src/core/session/index.d.ts.map +1 -0
- package/src/core/session/index.ts +3 -0
- package/src/core/telemetry/Telemetry.d.ts.map +1 -0
- package/src/core/telemetry/Telemetry.ts +90 -0
- package/src/core/telemetry/TelemetryService.ts +531 -0
- package/src/core/telemetry/index.d.ts.map +1 -0
- package/src/core/telemetry/index.ts +12 -0
- package/src/core/testing/AutoFixer.ts +385 -0
- package/src/core/testing/ErrorAnalyzer.ts +499 -0
- package/src/core/testing/TestRunner.ts +265 -0
- package/src/core/testing/agent-cli-tests.ts +538 -0
- package/src/core/testing/index.ts +11 -0
- package/src/core/tools/ToolRegistry.d.ts.map +1 -0
- package/src/core/tools/ToolRegistry.test.ts +206 -0
- package/src/core/tools/ToolRegistry.ts +260 -0
- package/src/core/tools/impl/EditFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/EditFileTool.ts +97 -0
- package/src/core/tools/impl/ListDirectoryTool.d.ts.map +1 -0
- package/src/core/tools/impl/ListDirectoryTool.ts +142 -0
- package/src/core/tools/impl/MemoryTool.d.ts.map +1 -0
- package/src/core/tools/impl/MemoryTool.ts +102 -0
- package/src/core/tools/impl/ReadFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/ReadFileTool.ts +58 -0
- package/src/core/tools/impl/SearchContentTool.d.ts.map +1 -0
- package/src/core/tools/impl/SearchContentTool.ts +94 -0
- package/src/core/tools/impl/SearchFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/SearchFileTool.ts +61 -0
- package/src/core/tools/impl/ShellTool.d.ts.map +1 -0
- package/src/core/tools/impl/ShellTool.ts +118 -0
- package/src/core/tools/impl/TaskTool.d.ts.map +1 -0
- package/src/core/tools/impl/TaskTool.ts +207 -0
- package/src/core/tools/impl/TodoTool.d.ts.map +1 -0
- package/src/core/tools/impl/TodoTool.ts +122 -0
- package/src/core/tools/impl/WebFetchTool.d.ts.map +1 -0
- package/src/core/tools/impl/WebFetchTool.ts +103 -0
- package/src/core/tools/impl/WebSearchTool.d.ts.map +1 -0
- package/src/core/tools/impl/WebSearchTool.ts +89 -0
- package/src/core/tools/impl/WriteFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/WriteFileTool.ts +49 -0
- package/src/core/tools/impl/index.d.ts.map +1 -0
- package/src/core/tools/impl/index.ts +16 -0
- package/src/core/tools/index.d.ts.map +1 -0
- package/src/core/tools/index.ts +7 -0
- package/src/core/tools/schemas/execution.d.ts.map +1 -0
- package/src/core/tools/schemas/execution.ts +42 -0
- package/src/core/tools/schemas/file.d.ts.map +1 -0
- package/src/core/tools/schemas/file.ts +119 -0
- package/src/core/tools/schemas/index.d.ts.map +1 -0
- package/src/core/tools/schemas/index.ts +11 -0
- package/src/core/tools/schemas/memory.d.ts.map +1 -0
- package/src/core/tools/schemas/memory.ts +52 -0
- package/src/core/tools/schemas/orchestration.d.ts.map +1 -0
- package/src/core/tools/schemas/orchestration.ts +44 -0
- package/src/core/tools/schemas/search.d.ts.map +1 -0
- package/src/core/tools/schemas/search.ts +112 -0
- package/src/core/tools/schemas/todo.d.ts.map +1 -0
- package/src/core/tools/schemas/todo.ts +32 -0
- package/src/core/tools/schemas/web.d.ts.map +1 -0
- package/src/core/tools/schemas/web.ts +86 -0
- package/src/core/types/config.d.ts.map +1 -0
- package/src/core/types/config.ts +200 -0
- package/src/core/types/errors.d.ts.map +1 -0
- package/src/core/types/errors.ts +204 -0
- package/src/core/types/index.d.ts.map +1 -0
- package/src/core/types/index.ts +8 -0
- package/src/core/types/session.d.ts.map +1 -0
- package/src/core/types/session.ts +216 -0
- package/src/core/types/tools.d.ts.map +1 -0
- package/src/core/types/tools.ts +157 -0
- package/src/core/utils/CheckpointManager.d.ts.map +1 -0
- package/src/core/utils/CheckpointManager.ts +327 -0
- package/src/core/utils/Logger.d.ts.map +1 -0
- package/src/core/utils/Logger.ts +98 -0
- package/src/core/utils/RetryManager.ts +471 -0
- package/src/core/utils/TokenCounter.d.ts.map +1 -0
- package/src/core/utils/TokenCounter.ts +414 -0
- package/src/core/utils/VectorMemoryStore.ts +440 -0
- package/src/core/utils/helpers.d.ts.map +1 -0
- package/src/core/utils/helpers.ts +89 -0
- package/src/core/utils/index.d.ts.map +1 -0
- 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
|
+
}
|