codeep 1.0.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.
- package/LICENSE +201 -0
- package/README.md +576 -0
- package/dist/api/index.d.ts +8 -0
- package/dist/api/index.js +421 -0
- package/dist/app.d.ts +2 -0
- package/dist/app.js +1406 -0
- package/dist/components/AgentProgress.d.ts +33 -0
- package/dist/components/AgentProgress.js +97 -0
- package/dist/components/Export.d.ts +8 -0
- package/dist/components/Export.js +27 -0
- package/dist/components/Help.d.ts +2 -0
- package/dist/components/Help.js +3 -0
- package/dist/components/Input.d.ts +9 -0
- package/dist/components/Input.js +89 -0
- package/dist/components/Loading.d.ts +9 -0
- package/dist/components/Loading.js +31 -0
- package/dist/components/Login.d.ts +7 -0
- package/dist/components/Login.js +77 -0
- package/dist/components/Logo.d.ts +8 -0
- package/dist/components/Logo.js +89 -0
- package/dist/components/LogoutPicker.d.ts +8 -0
- package/dist/components/LogoutPicker.js +61 -0
- package/dist/components/Message.d.ts +10 -0
- package/dist/components/Message.js +234 -0
- package/dist/components/MessageList.d.ts +10 -0
- package/dist/components/MessageList.js +8 -0
- package/dist/components/ProjectPermission.d.ts +7 -0
- package/dist/components/ProjectPermission.js +52 -0
- package/dist/components/Search.d.ts +10 -0
- package/dist/components/Search.js +30 -0
- package/dist/components/SessionPicker.d.ts +9 -0
- package/dist/components/SessionPicker.js +88 -0
- package/dist/components/Sessions.d.ts +12 -0
- package/dist/components/Sessions.js +102 -0
- package/dist/components/Settings.d.ts +7 -0
- package/dist/components/Settings.js +162 -0
- package/dist/components/Status.d.ts +2 -0
- package/dist/components/Status.js +12 -0
- package/dist/config/config.test.d.ts +1 -0
- package/dist/config/config.test.js +157 -0
- package/dist/config/index.d.ts +121 -0
- package/dist/config/index.js +555 -0
- package/dist/config/providers.d.ts +43 -0
- package/dist/config/providers.js +82 -0
- package/dist/config/providers.test.d.ts +1 -0
- package/dist/config/providers.test.js +132 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +38 -0
- package/dist/utils/agent.d.ts +37 -0
- package/dist/utils/agent.js +627 -0
- package/dist/utils/codeReview.d.ts +36 -0
- package/dist/utils/codeReview.js +390 -0
- package/dist/utils/context.d.ts +49 -0
- package/dist/utils/context.js +216 -0
- package/dist/utils/diffPreview.d.ts +57 -0
- package/dist/utils/diffPreview.js +335 -0
- package/dist/utils/export.d.ts +19 -0
- package/dist/utils/export.js +94 -0
- package/dist/utils/git.d.ts +85 -0
- package/dist/utils/git.js +399 -0
- package/dist/utils/git.test.d.ts +1 -0
- package/dist/utils/git.test.js +193 -0
- package/dist/utils/history.d.ts +93 -0
- package/dist/utils/history.js +348 -0
- package/dist/utils/interactive.d.ts +34 -0
- package/dist/utils/interactive.js +206 -0
- package/dist/utils/keychain.d.ts +17 -0
- package/dist/utils/keychain.js +160 -0
- package/dist/utils/learning.d.ts +89 -0
- package/dist/utils/learning.js +330 -0
- package/dist/utils/logger.d.ts +33 -0
- package/dist/utils/logger.js +130 -0
- package/dist/utils/project.d.ts +86 -0
- package/dist/utils/project.js +415 -0
- package/dist/utils/project.test.d.ts +1 -0
- package/dist/utils/project.test.js +212 -0
- package/dist/utils/ratelimit.d.ts +26 -0
- package/dist/utils/ratelimit.js +132 -0
- package/dist/utils/ratelimit.test.d.ts +1 -0
- package/dist/utils/ratelimit.test.js +131 -0
- package/dist/utils/retry.d.ts +28 -0
- package/dist/utils/retry.js +109 -0
- package/dist/utils/retry.test.d.ts +1 -0
- package/dist/utils/retry.test.js +163 -0
- package/dist/utils/search.d.ts +11 -0
- package/dist/utils/search.js +29 -0
- package/dist/utils/shell.d.ts +45 -0
- package/dist/utils/shell.js +242 -0
- package/dist/utils/skills.d.ts +144 -0
- package/dist/utils/skills.js +1137 -0
- package/dist/utils/smartContext.d.ts +29 -0
- package/dist/utils/smartContext.js +441 -0
- package/dist/utils/tools.d.ts +224 -0
- package/dist/utils/tools.js +731 -0
- package/dist/utils/update.d.ts +22 -0
- package/dist/utils/update.js +128 -0
- package/dist/utils/validation.d.ts +28 -0
- package/dist/utils/validation.js +141 -0
- package/dist/utils/validation.test.d.ts +1 -0
- package/dist/utils/validation.test.js +164 -0
- package/dist/utils/verify.d.ts +78 -0
- package/dist/utils/verify.js +464 -0
- package/package.json +68 -0
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent loop - autonomous task execution
|
|
3
|
+
*/
|
|
4
|
+
import { parseToolCalls, executeTool, createActionLog, formatToolDefinitions, getOpenAITools, getAnthropicTools, parseOpenAIToolCalls, parseAnthropicToolCalls } from './tools';
|
|
5
|
+
import { config, getApiKey } from '../config/index';
|
|
6
|
+
import { getProviderBaseUrl, getProviderAuthHeader, supportsNativeTools } from '../config/providers';
|
|
7
|
+
import { startSession, endSession, undoLastAction, undoAllActions, getCurrentSession, getRecentSessions, formatSession } from './history';
|
|
8
|
+
import { runAllVerifications, formatErrorsForAgent, hasVerificationErrors, getVerificationSummary } from './verify';
|
|
9
|
+
import { gatherSmartContext, formatSmartContext, extractTargetFile } from './smartContext';
|
|
10
|
+
const DEFAULT_OPTIONS = {
|
|
11
|
+
maxIterations: 20,
|
|
12
|
+
maxDuration: 5 * 60 * 1000, // 5 minutes
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Generate system prompt for agent mode (used with native tool calling)
|
|
16
|
+
*/
|
|
17
|
+
function getAgentSystemPrompt(projectContext) {
|
|
18
|
+
return `You are an AI coding agent with FULL autonomous access to this project.
|
|
19
|
+
|
|
20
|
+
## Your Capabilities
|
|
21
|
+
- Read, write, edit, and delete files and directories
|
|
22
|
+
- Create directories with create_directory tool
|
|
23
|
+
- Execute shell commands (npm, git, build tools, etc.)
|
|
24
|
+
- Search code in the project
|
|
25
|
+
- List directory contents
|
|
26
|
+
|
|
27
|
+
## IMPORTANT: Follow User Instructions Exactly
|
|
28
|
+
- Do EXACTLY what the user asks, nothing more
|
|
29
|
+
- If user says "create folder X" -> use create_directory tool to create folder X
|
|
30
|
+
- If user says "delete file X" -> use delete_file tool to delete file X
|
|
31
|
+
- Do NOT interpret or expand simple requests into complex tasks
|
|
32
|
+
- Simple tasks should be completed in 1-2 tool calls
|
|
33
|
+
- The user may write in any language - understand their request and execute it
|
|
34
|
+
- Tool names and parameters must ALWAYS be in English (e.g., "create_directory", not "kreiraj_direktorij")
|
|
35
|
+
|
|
36
|
+
## Rules
|
|
37
|
+
1. Always read files before editing them to understand the current content
|
|
38
|
+
2. Use edit_file for modifications to existing files (preserves other content)
|
|
39
|
+
3. Use write_file only for creating new files or complete overwrites
|
|
40
|
+
4. Use create_directory to create new folders/directories
|
|
41
|
+
5. When the task is complete, respond with a summary of what you did
|
|
42
|
+
|
|
43
|
+
## Self-Verification
|
|
44
|
+
After you make changes, the system will automatically run build and tests.
|
|
45
|
+
If there are errors, you will receive them and must fix them.
|
|
46
|
+
- Read error messages carefully
|
|
47
|
+
- Fix the specific files and lines mentioned
|
|
48
|
+
- Keep trying until verification passes
|
|
49
|
+
|
|
50
|
+
## Project Context
|
|
51
|
+
**Name:** ${projectContext.name}
|
|
52
|
+
**Type:** ${projectContext.type}
|
|
53
|
+
|
|
54
|
+
**Structure:**
|
|
55
|
+
\`\`\`
|
|
56
|
+
${projectContext.structure}
|
|
57
|
+
\`\`\`
|
|
58
|
+
|
|
59
|
+
**Key Files:** ${projectContext.keyFiles.join(', ')}
|
|
60
|
+
|
|
61
|
+
You have FULL READ AND WRITE access. Use the tools to complete tasks autonomously.`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Generate fallback system prompt (text-based tool calling)
|
|
65
|
+
*/
|
|
66
|
+
function getFallbackSystemPrompt(projectContext) {
|
|
67
|
+
return `You are an AI coding agent with FULL autonomous access to this project.
|
|
68
|
+
|
|
69
|
+
## IMPORTANT: Follow User Instructions Exactly
|
|
70
|
+
- Do EXACTLY what the user asks, nothing more
|
|
71
|
+
- If user says "create folder X" -> use create_directory tool
|
|
72
|
+
- If user says "delete file X" -> use delete_file tool
|
|
73
|
+
- Do NOT interpret or expand simple requests into complex tasks
|
|
74
|
+
- The user may write in any language - understand and execute
|
|
75
|
+
- Tool names and parameters must ALWAYS be in English
|
|
76
|
+
|
|
77
|
+
## Available Tools
|
|
78
|
+
${formatToolDefinitions()}
|
|
79
|
+
|
|
80
|
+
## Tool Call Format
|
|
81
|
+
When you need to use a tool, respond with:
|
|
82
|
+
<tool_call>
|
|
83
|
+
{"tool": "tool_name", "parameters": {"param1": "value1"}}
|
|
84
|
+
</tool_call>
|
|
85
|
+
|
|
86
|
+
## Examples
|
|
87
|
+
<tool_call>
|
|
88
|
+
{"tool": "create_directory", "parameters": {"path": "my-folder"}}
|
|
89
|
+
</tool_call>
|
|
90
|
+
|
|
91
|
+
<tool_call>
|
|
92
|
+
{"tool": "write_file", "parameters": {"path": "test/index.html", "content": "<!DOCTYPE html>..."}}
|
|
93
|
+
</tool_call>
|
|
94
|
+
|
|
95
|
+
## Rules
|
|
96
|
+
1. Use the exact format shown above
|
|
97
|
+
2. Always read files before editing
|
|
98
|
+
3. When done, respond WITHOUT tool calls
|
|
99
|
+
|
|
100
|
+
## Project: ${projectContext.name} (${projectContext.type})
|
|
101
|
+
${projectContext.structure}
|
|
102
|
+
|
|
103
|
+
You have FULL access. Execute tasks autonomously.`;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Make a chat API call for agent mode with native tool support
|
|
107
|
+
*/
|
|
108
|
+
async function agentChat(messages, systemPrompt, onChunk, abortSignal) {
|
|
109
|
+
const protocol = config.get('protocol');
|
|
110
|
+
const model = config.get('model');
|
|
111
|
+
const apiKey = getApiKey();
|
|
112
|
+
const providerId = config.get('provider');
|
|
113
|
+
const baseUrl = getProviderBaseUrl(providerId, protocol);
|
|
114
|
+
const authHeader = getProviderAuthHeader(providerId, protocol);
|
|
115
|
+
if (!baseUrl) {
|
|
116
|
+
throw new Error(`Provider ${providerId} does not support ${protocol} protocol`);
|
|
117
|
+
}
|
|
118
|
+
// Check if provider supports native tools - if not, use text-based fallback directly
|
|
119
|
+
if (!supportsNativeTools(providerId, protocol)) {
|
|
120
|
+
// Provider doesn't support native tools, use text-based fallback
|
|
121
|
+
return await agentChatFallback(messages, systemPrompt, onChunk, abortSignal);
|
|
122
|
+
}
|
|
123
|
+
const controller = new AbortController();
|
|
124
|
+
const timeout = setTimeout(() => controller.abort(), config.get('apiTimeout'));
|
|
125
|
+
if (abortSignal) {
|
|
126
|
+
abortSignal.addEventListener('abort', () => controller.abort());
|
|
127
|
+
}
|
|
128
|
+
const headers = {
|
|
129
|
+
'Content-Type': 'application/json',
|
|
130
|
+
};
|
|
131
|
+
if (authHeader === 'Bearer') {
|
|
132
|
+
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
headers['x-api-key'] = apiKey;
|
|
136
|
+
}
|
|
137
|
+
if (protocol === 'anthropic') {
|
|
138
|
+
headers['anthropic-version'] = '2023-06-01';
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
let endpoint;
|
|
142
|
+
let body;
|
|
143
|
+
if (protocol === 'openai') {
|
|
144
|
+
endpoint = `${baseUrl}/chat/completions`;
|
|
145
|
+
body = {
|
|
146
|
+
model,
|
|
147
|
+
messages: [
|
|
148
|
+
{ role: 'system', content: systemPrompt },
|
|
149
|
+
...messages,
|
|
150
|
+
],
|
|
151
|
+
tools: getOpenAITools(),
|
|
152
|
+
tool_choice: 'auto',
|
|
153
|
+
temperature: config.get('temperature'),
|
|
154
|
+
max_tokens: config.get('maxTokens'),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
endpoint = `${baseUrl}/v1/messages`;
|
|
159
|
+
body = {
|
|
160
|
+
model,
|
|
161
|
+
system: systemPrompt,
|
|
162
|
+
messages: messages,
|
|
163
|
+
tools: getAnthropicTools(),
|
|
164
|
+
temperature: config.get('temperature'),
|
|
165
|
+
max_tokens: config.get('maxTokens'),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
const response = await fetch(endpoint, {
|
|
169
|
+
method: 'POST',
|
|
170
|
+
headers,
|
|
171
|
+
body: JSON.stringify(body),
|
|
172
|
+
signal: controller.signal,
|
|
173
|
+
});
|
|
174
|
+
if (!response.ok) {
|
|
175
|
+
const errorText = await response.text();
|
|
176
|
+
// Check if error is due to tools not being supported - fallback to text mode
|
|
177
|
+
if (errorText.includes('tools') || errorText.includes('function') || response.status === 400) {
|
|
178
|
+
return await agentChatFallback(messages, systemPrompt, onChunk, abortSignal);
|
|
179
|
+
}
|
|
180
|
+
throw new Error(`API error: ${response.status} - ${errorText}`);
|
|
181
|
+
}
|
|
182
|
+
const data = await response.json();
|
|
183
|
+
if (protocol === 'openai') {
|
|
184
|
+
const message = data.choices?.[0]?.message;
|
|
185
|
+
const content = message?.content || '';
|
|
186
|
+
const toolCalls = parseOpenAIToolCalls(message?.tool_calls || []);
|
|
187
|
+
if (onChunk && content) {
|
|
188
|
+
onChunk(content);
|
|
189
|
+
}
|
|
190
|
+
return { content, toolCalls, usedNativeTools: true };
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
// Anthropic format
|
|
194
|
+
const contentBlocks = data.content || [];
|
|
195
|
+
let textContent = '';
|
|
196
|
+
for (const block of contentBlocks) {
|
|
197
|
+
if (block.type === 'text') {
|
|
198
|
+
textContent += block.text;
|
|
199
|
+
if (onChunk)
|
|
200
|
+
onChunk(block.text);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const toolCalls = parseAnthropicToolCalls(contentBlocks);
|
|
204
|
+
return { content: textContent, toolCalls, usedNativeTools: true };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
const err = error;
|
|
209
|
+
// If native tools failed, try fallback
|
|
210
|
+
if (err.message.includes('tools') || err.message.includes('function')) {
|
|
211
|
+
return await agentChatFallback(messages, systemPrompt, onChunk, abortSignal);
|
|
212
|
+
}
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
clearTimeout(timeout);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Fallback chat without native tools (text-based parsing)
|
|
221
|
+
*/
|
|
222
|
+
async function agentChatFallback(messages, systemPrompt, onChunk, abortSignal) {
|
|
223
|
+
const protocol = config.get('protocol');
|
|
224
|
+
const model = config.get('model');
|
|
225
|
+
const apiKey = getApiKey();
|
|
226
|
+
const providerId = config.get('provider');
|
|
227
|
+
const baseUrl = getProviderBaseUrl(providerId, protocol);
|
|
228
|
+
const authHeader = getProviderAuthHeader(providerId, protocol);
|
|
229
|
+
if (!baseUrl) {
|
|
230
|
+
throw new Error(`Provider ${providerId} does not support ${protocol} protocol`);
|
|
231
|
+
}
|
|
232
|
+
const controller = new AbortController();
|
|
233
|
+
const timeout = setTimeout(() => controller.abort(), config.get('apiTimeout'));
|
|
234
|
+
if (abortSignal) {
|
|
235
|
+
abortSignal.addEventListener('abort', () => controller.abort());
|
|
236
|
+
}
|
|
237
|
+
const headers = {
|
|
238
|
+
'Content-Type': 'application/json',
|
|
239
|
+
};
|
|
240
|
+
if (authHeader === 'Bearer') {
|
|
241
|
+
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
headers['x-api-key'] = apiKey;
|
|
245
|
+
}
|
|
246
|
+
if (protocol === 'anthropic') {
|
|
247
|
+
headers['anthropic-version'] = '2023-06-01';
|
|
248
|
+
}
|
|
249
|
+
// Use fallback system prompt with text-based tool format
|
|
250
|
+
const fallbackPrompt = systemPrompt.includes('## Available Tools')
|
|
251
|
+
? systemPrompt
|
|
252
|
+
: systemPrompt + '\n\n' + formatToolDefinitions();
|
|
253
|
+
try {
|
|
254
|
+
let endpoint;
|
|
255
|
+
let body;
|
|
256
|
+
if (protocol === 'openai') {
|
|
257
|
+
endpoint = `${baseUrl}/chat/completions`;
|
|
258
|
+
body = {
|
|
259
|
+
model,
|
|
260
|
+
messages: [
|
|
261
|
+
{ role: 'system', content: fallbackPrompt },
|
|
262
|
+
...messages,
|
|
263
|
+
],
|
|
264
|
+
stream: Boolean(onChunk),
|
|
265
|
+
temperature: config.get('temperature'),
|
|
266
|
+
max_tokens: config.get('maxTokens'),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
endpoint = `${baseUrl}/v1/messages`;
|
|
271
|
+
body = {
|
|
272
|
+
model,
|
|
273
|
+
messages: [
|
|
274
|
+
{ role: 'user', content: fallbackPrompt },
|
|
275
|
+
{ role: 'assistant', content: 'Understood. I will use the tools as specified.' },
|
|
276
|
+
...messages,
|
|
277
|
+
],
|
|
278
|
+
stream: Boolean(onChunk),
|
|
279
|
+
temperature: config.get('temperature'),
|
|
280
|
+
max_tokens: config.get('maxTokens'),
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
const response = await fetch(endpoint, {
|
|
284
|
+
method: 'POST',
|
|
285
|
+
headers,
|
|
286
|
+
body: JSON.stringify(body),
|
|
287
|
+
signal: controller.signal,
|
|
288
|
+
});
|
|
289
|
+
if (!response.ok) {
|
|
290
|
+
const error = await response.text();
|
|
291
|
+
throw new Error(`API error: ${response.status} - ${error}`);
|
|
292
|
+
}
|
|
293
|
+
let content;
|
|
294
|
+
if (onChunk && response.body) {
|
|
295
|
+
content = await handleStream(response.body, protocol, onChunk);
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
const data = await response.json();
|
|
299
|
+
if (protocol === 'openai') {
|
|
300
|
+
content = data.choices?.[0]?.message?.content || '';
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
content = data.content?.[0]?.text || '';
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// Parse tool calls from text response
|
|
307
|
+
const toolCalls = parseToolCalls(content);
|
|
308
|
+
return { content, toolCalls, usedNativeTools: false };
|
|
309
|
+
}
|
|
310
|
+
finally {
|
|
311
|
+
clearTimeout(timeout);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Handle streaming response
|
|
316
|
+
*/
|
|
317
|
+
async function handleStream(body, protocol, onChunk) {
|
|
318
|
+
const reader = body.getReader();
|
|
319
|
+
const decoder = new TextDecoder();
|
|
320
|
+
const chunks = [];
|
|
321
|
+
let buffer = '';
|
|
322
|
+
while (true) {
|
|
323
|
+
const { done, value } = await reader.read();
|
|
324
|
+
if (done)
|
|
325
|
+
break;
|
|
326
|
+
buffer += decoder.decode(value, { stream: true });
|
|
327
|
+
const lines = buffer.split('\n');
|
|
328
|
+
buffer = lines.pop() || '';
|
|
329
|
+
for (const line of lines) {
|
|
330
|
+
if (line.startsWith('data: ')) {
|
|
331
|
+
const data = line.slice(6);
|
|
332
|
+
if (data === '[DONE]')
|
|
333
|
+
continue;
|
|
334
|
+
try {
|
|
335
|
+
const parsed = JSON.parse(data);
|
|
336
|
+
let content;
|
|
337
|
+
if (protocol === 'openai') {
|
|
338
|
+
content = parsed.choices?.[0]?.delta?.content;
|
|
339
|
+
}
|
|
340
|
+
else if (parsed.type === 'content_block_delta') {
|
|
341
|
+
content = parsed.delta?.text;
|
|
342
|
+
}
|
|
343
|
+
if (content) {
|
|
344
|
+
chunks.push(content);
|
|
345
|
+
onChunk(content);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
catch {
|
|
349
|
+
// Skip parse errors
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return chunks.join('');
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Run the agent loop
|
|
358
|
+
*/
|
|
359
|
+
export async function runAgent(prompt, projectContext, options = {}) {
|
|
360
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
361
|
+
const startTime = Date.now();
|
|
362
|
+
const actions = [];
|
|
363
|
+
const messages = [];
|
|
364
|
+
// Start history session for undo support
|
|
365
|
+
const sessionId = startSession(prompt, projectContext.root || process.cwd());
|
|
366
|
+
// Gather smart context based on the task
|
|
367
|
+
const targetFile = extractTargetFile(prompt);
|
|
368
|
+
const smartContext = gatherSmartContext(targetFile, projectContext, prompt);
|
|
369
|
+
const smartContextStr = formatSmartContext(smartContext);
|
|
370
|
+
// Check if provider supports native tools
|
|
371
|
+
const protocol = config.get('protocol');
|
|
372
|
+
const providerId = config.get('provider');
|
|
373
|
+
const useNativeTools = supportsNativeTools(providerId, protocol);
|
|
374
|
+
// Build system prompt - use fallback format if native tools not supported
|
|
375
|
+
let systemPrompt = useNativeTools
|
|
376
|
+
? getAgentSystemPrompt(projectContext)
|
|
377
|
+
: getFallbackSystemPrompt(projectContext);
|
|
378
|
+
if (smartContextStr) {
|
|
379
|
+
systemPrompt += '\n\n' + smartContextStr;
|
|
380
|
+
}
|
|
381
|
+
// Initial user message
|
|
382
|
+
messages.push({ role: 'user', content: prompt });
|
|
383
|
+
let iteration = 0;
|
|
384
|
+
let finalResponse = '';
|
|
385
|
+
let result;
|
|
386
|
+
try {
|
|
387
|
+
while (iteration < opts.maxIterations) {
|
|
388
|
+
// Check timeout
|
|
389
|
+
if (Date.now() - startTime > opts.maxDuration) {
|
|
390
|
+
result = {
|
|
391
|
+
success: false,
|
|
392
|
+
iterations: iteration,
|
|
393
|
+
actions,
|
|
394
|
+
finalResponse: 'Agent timed out',
|
|
395
|
+
error: `Exceeded maximum duration of ${opts.maxDuration / 1000} seconds`,
|
|
396
|
+
};
|
|
397
|
+
return result;
|
|
398
|
+
}
|
|
399
|
+
// Check abort signal
|
|
400
|
+
if (opts.abortSignal?.aborted) {
|
|
401
|
+
result = {
|
|
402
|
+
success: false,
|
|
403
|
+
iterations: iteration,
|
|
404
|
+
actions,
|
|
405
|
+
finalResponse: 'Agent was stopped by user',
|
|
406
|
+
aborted: true,
|
|
407
|
+
};
|
|
408
|
+
return result;
|
|
409
|
+
}
|
|
410
|
+
iteration++;
|
|
411
|
+
opts.onIteration?.(iteration, `Iteration ${iteration}/${opts.maxIterations}`);
|
|
412
|
+
// Get AI response
|
|
413
|
+
let chatResponse;
|
|
414
|
+
try {
|
|
415
|
+
chatResponse = await agentChat(messages, systemPrompt, opts.onThinking, opts.abortSignal);
|
|
416
|
+
}
|
|
417
|
+
catch (error) {
|
|
418
|
+
const err = error;
|
|
419
|
+
if (err.name === 'AbortError') {
|
|
420
|
+
result = {
|
|
421
|
+
success: false,
|
|
422
|
+
iterations: iteration,
|
|
423
|
+
actions,
|
|
424
|
+
finalResponse: 'Agent was stopped',
|
|
425
|
+
aborted: true,
|
|
426
|
+
};
|
|
427
|
+
return result;
|
|
428
|
+
}
|
|
429
|
+
throw error;
|
|
430
|
+
}
|
|
431
|
+
let { content, toolCalls, usedNativeTools } = chatResponse;
|
|
432
|
+
// If native tools were used but no tool calls returned, try parsing text-based tool calls
|
|
433
|
+
// This handles models that accept tools parameter but respond with text anyway
|
|
434
|
+
if (usedNativeTools && toolCalls.length === 0 && iteration === 1) {
|
|
435
|
+
const textToolCalls = parseToolCalls(content);
|
|
436
|
+
if (textToolCalls.length > 0) {
|
|
437
|
+
toolCalls = textToolCalls;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
// If no tool calls, this is the final response
|
|
441
|
+
if (toolCalls.length === 0) {
|
|
442
|
+
// Remove <think>...</think> tags from response (some models include thinking)
|
|
443
|
+
finalResponse = content.replace(/<think>[\s\S]*?<\/think>/gi, '').trim();
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
// Add assistant response to history
|
|
447
|
+
messages.push({ role: 'assistant', content });
|
|
448
|
+
// Execute tool calls
|
|
449
|
+
const toolResults = [];
|
|
450
|
+
for (const toolCall of toolCalls) {
|
|
451
|
+
opts.onToolCall?.(toolCall);
|
|
452
|
+
let toolResult;
|
|
453
|
+
if (opts.dryRun) {
|
|
454
|
+
// In dry run mode, simulate success
|
|
455
|
+
toolResult = {
|
|
456
|
+
success: true,
|
|
457
|
+
output: `[DRY RUN] Would execute: ${toolCall.tool}`,
|
|
458
|
+
tool: toolCall.tool,
|
|
459
|
+
parameters: toolCall.parameters,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
// Actually execute the tool
|
|
464
|
+
toolResult = executeTool(toolCall, projectContext.root || process.cwd());
|
|
465
|
+
}
|
|
466
|
+
opts.onToolResult?.(toolResult);
|
|
467
|
+
// Log action
|
|
468
|
+
const actionLog = createActionLog(toolCall, toolResult);
|
|
469
|
+
actions.push(actionLog);
|
|
470
|
+
// Format result for AI
|
|
471
|
+
if (toolResult.success) {
|
|
472
|
+
toolResults.push(`Tool ${toolCall.tool} succeeded:\n${toolResult.output}`);
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
toolResults.push(`Tool ${toolCall.tool} failed:\n${toolResult.error || 'Unknown error'}`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
// Add tool results to messages
|
|
479
|
+
messages.push({
|
|
480
|
+
role: 'user',
|
|
481
|
+
content: `Tool results:\n\n${toolResults.join('\n\n')}\n\nContinue with the task. If done, provide a final summary without any tool calls.`,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
// Check if we hit max iterations
|
|
485
|
+
if (iteration >= opts.maxIterations && !finalResponse) {
|
|
486
|
+
result = {
|
|
487
|
+
success: false,
|
|
488
|
+
iterations: iteration,
|
|
489
|
+
actions,
|
|
490
|
+
finalResponse: 'Agent reached maximum iterations',
|
|
491
|
+
error: `Exceeded maximum of ${opts.maxIterations} iterations`,
|
|
492
|
+
};
|
|
493
|
+
return result;
|
|
494
|
+
}
|
|
495
|
+
// Self-verification: Run build/test and fix errors if needed
|
|
496
|
+
const autoVerify = opts.autoVerify ?? config.get('agentAutoVerify');
|
|
497
|
+
const maxFixAttempts = opts.maxFixAttempts ?? config.get('agentMaxFixAttempts');
|
|
498
|
+
if (autoVerify && !opts.dryRun) {
|
|
499
|
+
// Check if we made any file changes worth verifying
|
|
500
|
+
const hasFileChanges = actions.some(a => a.type === 'write' || a.type === 'edit' || a.type === 'delete');
|
|
501
|
+
if (hasFileChanges) {
|
|
502
|
+
let fixAttempt = 0;
|
|
503
|
+
while (fixAttempt < maxFixAttempts) {
|
|
504
|
+
// Check abort signal
|
|
505
|
+
if (opts.abortSignal?.aborted) {
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
opts.onIteration?.(iteration, `Verification attempt ${fixAttempt + 1}/${maxFixAttempts}`);
|
|
509
|
+
// Run verifications
|
|
510
|
+
const verifyResults = runAllVerifications(projectContext.root || process.cwd(), {
|
|
511
|
+
runBuild: true,
|
|
512
|
+
runTest: true,
|
|
513
|
+
runTypecheck: true,
|
|
514
|
+
runLint: false,
|
|
515
|
+
});
|
|
516
|
+
opts.onVerification?.(verifyResults);
|
|
517
|
+
// Check if all passed
|
|
518
|
+
if (!hasVerificationErrors(verifyResults)) {
|
|
519
|
+
const summary = getVerificationSummary(verifyResults);
|
|
520
|
+
finalResponse += `\n\n✓ Verification passed: ${summary.passed}/${summary.total} checks`;
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
fixAttempt++;
|
|
524
|
+
// If we've exceeded attempts, report the errors
|
|
525
|
+
if (fixAttempt >= maxFixAttempts) {
|
|
526
|
+
const summary = getVerificationSummary(verifyResults);
|
|
527
|
+
finalResponse += `\n\n✗ Verification failed after ${fixAttempt} fix attempts: ${summary.errors} errors remaining`;
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
// Ask agent to fix the errors
|
|
531
|
+
const errorMessage = formatErrorsForAgent(verifyResults);
|
|
532
|
+
messages.push({ role: 'assistant', content: finalResponse });
|
|
533
|
+
messages.push({
|
|
534
|
+
role: 'user',
|
|
535
|
+
content: `${errorMessage}\n\nFix these errors. After fixing, I will re-run verification.`
|
|
536
|
+
});
|
|
537
|
+
iteration++;
|
|
538
|
+
if (iteration >= opts.maxIterations) {
|
|
539
|
+
break;
|
|
540
|
+
}
|
|
541
|
+
// Get AI response to fix errors
|
|
542
|
+
try {
|
|
543
|
+
const fixResponse = await agentChat(messages, systemPrompt, opts.onThinking, opts.abortSignal);
|
|
544
|
+
const { content: fixContent, toolCalls: fixToolCalls } = fixResponse;
|
|
545
|
+
if (fixToolCalls.length === 0) {
|
|
546
|
+
// Agent gave up or thinks it's fixed
|
|
547
|
+
finalResponse = fixContent.replace(/<think>[\s\S]*?<\/think>/gi, '').trim();
|
|
548
|
+
continue; // Re-run verification
|
|
549
|
+
}
|
|
550
|
+
// Execute fix tool calls
|
|
551
|
+
messages.push({ role: 'assistant', content: fixContent });
|
|
552
|
+
const fixResults = [];
|
|
553
|
+
for (const toolCall of fixToolCalls) {
|
|
554
|
+
opts.onToolCall?.(toolCall);
|
|
555
|
+
const toolResult = executeTool(toolCall, projectContext.root || process.cwd());
|
|
556
|
+
opts.onToolResult?.(toolResult);
|
|
557
|
+
const actionLog = createActionLog(toolCall, toolResult);
|
|
558
|
+
actions.push(actionLog);
|
|
559
|
+
if (toolResult.success) {
|
|
560
|
+
fixResults.push(`Tool ${toolCall.tool} succeeded:\n${toolResult.output}`);
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
fixResults.push(`Tool ${toolCall.tool} failed:\n${toolResult.error || 'Unknown error'}`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
messages.push({
|
|
567
|
+
role: 'user',
|
|
568
|
+
content: `Fix results:\n\n${fixResults.join('\n\n')}\n\nContinue fixing if needed. Re-running verification...`,
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
catch (error) {
|
|
572
|
+
// If fix attempt failed, continue to next attempt
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
result = {
|
|
579
|
+
success: true,
|
|
580
|
+
iterations: iteration,
|
|
581
|
+
actions,
|
|
582
|
+
finalResponse,
|
|
583
|
+
};
|
|
584
|
+
return result;
|
|
585
|
+
}
|
|
586
|
+
catch (error) {
|
|
587
|
+
const err = error;
|
|
588
|
+
result = {
|
|
589
|
+
success: false,
|
|
590
|
+
iterations: iteration,
|
|
591
|
+
actions,
|
|
592
|
+
finalResponse: '',
|
|
593
|
+
error: err.message,
|
|
594
|
+
};
|
|
595
|
+
return result;
|
|
596
|
+
}
|
|
597
|
+
finally {
|
|
598
|
+
// End session and save history
|
|
599
|
+
endSession();
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Format agent result for display
|
|
604
|
+
*/
|
|
605
|
+
export function formatAgentResult(result) {
|
|
606
|
+
const lines = [];
|
|
607
|
+
if (result.success) {
|
|
608
|
+
lines.push(`Agent completed in ${result.iterations} iteration(s)`);
|
|
609
|
+
}
|
|
610
|
+
else if (result.aborted) {
|
|
611
|
+
lines.push('Agent was stopped by user');
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
lines.push(`Agent failed: ${result.error}`);
|
|
615
|
+
}
|
|
616
|
+
if (result.actions.length > 0) {
|
|
617
|
+
lines.push('');
|
|
618
|
+
lines.push('Actions performed:');
|
|
619
|
+
for (const action of result.actions) {
|
|
620
|
+
const status = action.result === 'success' ? '✓' : '✗';
|
|
621
|
+
lines.push(` ${status} ${action.type}: ${action.target}`);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
return lines.join('\n');
|
|
625
|
+
}
|
|
626
|
+
// Re-export history functions for undo support
|
|
627
|
+
export { undoLastAction, undoAllActions, getCurrentSession, getRecentSessions, formatSession };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Review Mode - AI-powered code review
|
|
3
|
+
*/
|
|
4
|
+
import { ProjectContext } from './project';
|
|
5
|
+
export interface ReviewIssue {
|
|
6
|
+
file: string;
|
|
7
|
+
line?: number;
|
|
8
|
+
severity: 'error' | 'warning' | 'info' | 'suggestion';
|
|
9
|
+
category: ReviewCategory;
|
|
10
|
+
message: string;
|
|
11
|
+
suggestion?: string;
|
|
12
|
+
}
|
|
13
|
+
export type ReviewCategory = 'security' | 'performance' | 'maintainability' | 'bug' | 'style' | 'types' | 'best-practice' | 'documentation';
|
|
14
|
+
export interface ReviewResult {
|
|
15
|
+
files: string[];
|
|
16
|
+
issues: ReviewIssue[];
|
|
17
|
+
summary: ReviewSummary;
|
|
18
|
+
score: number;
|
|
19
|
+
}
|
|
20
|
+
export interface ReviewSummary {
|
|
21
|
+
totalIssues: number;
|
|
22
|
+
byCategory: Record<ReviewCategory, number>;
|
|
23
|
+
bySeverity: Record<string, number>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Perform code review
|
|
27
|
+
*/
|
|
28
|
+
export declare function performCodeReview(projectContext: ProjectContext, specificFiles?: string[]): ReviewResult;
|
|
29
|
+
/**
|
|
30
|
+
* Format review result for display
|
|
31
|
+
*/
|
|
32
|
+
export declare function formatReviewResult(result: ReviewResult): string;
|
|
33
|
+
/**
|
|
34
|
+
* Get review prompt for AI-enhanced review
|
|
35
|
+
*/
|
|
36
|
+
export declare function getReviewSystemPrompt(result: ReviewResult): string;
|