codeep 1.2.69 → 1.2.71
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/README.md +13 -9
- package/dist/api/index.js +2 -2
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +1 -1
- package/dist/renderer/agentExecution.js +19 -21
- package/dist/renderer/components/Settings.js +6 -3
- package/dist/utils/agent.d.ts +1 -1
- package/dist/utils/agent.js +61 -26
- package/dist/utils/agentChat.js +2 -2
- package/dist/utils/shell.js +2 -2
- package/dist/utils/toolExecution.js +2 -2
- package/dist/utils/verify.js +18 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -357,13 +357,17 @@ Main modules: src, components, hooks, utils
|
|
|
357
357
|
- Naming: camelCase
|
|
358
358
|
```
|
|
359
359
|
|
|
360
|
-
### Self-Verification
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
360
|
+
### Self-Verification (Optional)
|
|
361
|
+
When enabled in Settings, the agent can automatically verify its changes by running build, tests, or type checking after completing a task. Disabled by default — the agent works freely and you decide when to verify.
|
|
362
|
+
|
|
363
|
+
Configure in **Settings → Agent Auto-Verify**:
|
|
364
|
+
- **Off** (default) — no automatic verification
|
|
365
|
+
- **Build only** — checks compilation after changes
|
|
366
|
+
- **Typecheck only** — runs TypeScript/PHP type checking
|
|
367
|
+
- **Test only** — runs test suite after changes
|
|
368
|
+
- **Build + Typecheck + Test** — full verification
|
|
369
|
+
|
|
370
|
+
If errors are found, the agent tries to fix them automatically (up to 3 attempts, configurable).
|
|
367
371
|
|
|
368
372
|
**Supported project types:**
|
|
369
373
|
|
|
@@ -681,8 +685,8 @@ With write access enabled:
|
|
|
681
685
|
| Agent Confirmation | Dangerous | `Never`, `Dangerous` (default), or `Always` |
|
|
682
686
|
| Agent Auto-Commit | Off | Automatically commit after agent completes |
|
|
683
687
|
| Agent Branch | Off | Create new branch for agent commits |
|
|
684
|
-
| Agent Auto-Verify |
|
|
685
|
-
| Agent Max Fix Attempts | 3 | Max attempts to auto-fix errors |
|
|
688
|
+
| Agent Auto-Verify | Off | `Off`, `Build only`, `Typecheck only`, `Test only`, or `Build + Typecheck + Test` |
|
|
689
|
+
| Agent Max Fix Attempts | 3 | Max attempts to auto-fix errors (when Auto-Verify is enabled) |
|
|
686
690
|
|
|
687
691
|
## Usage Examples
|
|
688
692
|
|
package/dist/api/index.js
CHANGED
|
@@ -293,7 +293,7 @@ async function chatOpenAI(message, history, model, apiKey, onChunk, abortSignal)
|
|
|
293
293
|
const timeoutId = setTimeout(() => { timedOut = true; controller.abort(); }, timeout);
|
|
294
294
|
// Listen to external abort signal if provided (user cancel)
|
|
295
295
|
if (abortSignal) {
|
|
296
|
-
abortSignal.addEventListener('abort', () => controller.abort());
|
|
296
|
+
abortSignal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
297
297
|
}
|
|
298
298
|
// Build headers based on auth type
|
|
299
299
|
const headers = {
|
|
@@ -413,7 +413,7 @@ async function chatAnthropic(message, history, model, apiKey, onChunk, abortSign
|
|
|
413
413
|
const timeoutId = setTimeout(() => { timedOut = true; controller.abort(); }, timeout);
|
|
414
414
|
// Listen to external abort signal if provided (user cancel)
|
|
415
415
|
if (abortSignal) {
|
|
416
|
-
abortSignal.addEventListener('abort', () => controller.abort());
|
|
416
|
+
abortSignal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
417
417
|
}
|
|
418
418
|
// Build headers based on auth type
|
|
419
419
|
const headers = {
|
package/dist/config/index.d.ts
CHANGED
|
@@ -33,7 +33,7 @@ interface ConfigSchema {
|
|
|
33
33
|
agentConfirmation: 'always' | 'dangerous' | 'never';
|
|
34
34
|
agentAutoCommit: boolean;
|
|
35
35
|
agentAutoCommitBranch: boolean;
|
|
36
|
-
agentAutoVerify:
|
|
36
|
+
agentAutoVerify: 'off' | 'build' | 'typecheck' | 'test' | 'all';
|
|
37
37
|
agentMaxFixAttempts: number;
|
|
38
38
|
agentMaxIterations: number;
|
|
39
39
|
agentMaxDuration: number;
|
package/dist/config/index.js
CHANGED
|
@@ -146,7 +146,7 @@ function createConfig() {
|
|
|
146
146
|
agentConfirmation: 'dangerous',
|
|
147
147
|
agentAutoCommit: false,
|
|
148
148
|
agentAutoCommitBranch: false,
|
|
149
|
-
agentAutoVerify:
|
|
149
|
+
agentAutoVerify: 'off',
|
|
150
150
|
agentMaxFixAttempts: 3,
|
|
151
151
|
agentMaxIterations: 100,
|
|
152
152
|
agentMaxDuration: 20,
|
|
@@ -9,6 +9,16 @@ import { chat } from '../api/index.js';
|
|
|
9
9
|
import { runAgent } from '../utils/agent.js';
|
|
10
10
|
import { config, autoSaveSession } from '../config/index.js';
|
|
11
11
|
import { getGitStatus } from '../utils/git.js';
|
|
12
|
+
function getActionType(toolName) {
|
|
13
|
+
return toolName.includes('write') ? 'write' :
|
|
14
|
+
toolName.includes('edit') ? 'edit' :
|
|
15
|
+
toolName.includes('read') ? 'read' :
|
|
16
|
+
toolName.includes('delete') ? 'delete' :
|
|
17
|
+
toolName.includes('list') ? 'list' :
|
|
18
|
+
toolName.includes('search') || toolName.includes('grep') ? 'search' :
|
|
19
|
+
toolName.includes('mkdir') ? 'mkdir' :
|
|
20
|
+
toolName.includes('fetch') ? 'fetch' : 'command';
|
|
21
|
+
}
|
|
12
22
|
// ─── Dangerous tool detection ────────────────────────────────────────────────
|
|
13
23
|
const DANGEROUS_TOOLS = ['write', 'edit', 'delete', 'command', 'execute', 'shell', 'rm', 'mv'];
|
|
14
24
|
export function isDangerousTool(toolName, parameters) {
|
|
@@ -154,14 +164,7 @@ export async function executeAgentTask(task, dryRun, ctx) {
|
|
|
154
164
|
const target = tool.parameters.path ||
|
|
155
165
|
tool.parameters.command ||
|
|
156
166
|
tool.parameters.pattern || '';
|
|
157
|
-
const actionType = toolName
|
|
158
|
-
toolName.includes('edit') ? 'edit' :
|
|
159
|
-
toolName.includes('read') ? 'read' :
|
|
160
|
-
toolName.includes('delete') ? 'delete' :
|
|
161
|
-
toolName.includes('list') ? 'list' :
|
|
162
|
-
toolName.includes('search') || toolName.includes('grep') ? 'search' :
|
|
163
|
-
toolName.includes('mkdir') ? 'mkdir' :
|
|
164
|
-
toolName.includes('fetch') ? 'fetch' : 'command';
|
|
167
|
+
const actionType = getActionType(toolName);
|
|
165
168
|
const shortTarget = target.length > 50 ? '...' + target.slice(-47) : target;
|
|
166
169
|
app.setAgentThinking(`${actionType}: ${shortTarget}`);
|
|
167
170
|
if (actionType === 'write' && tool.parameters.content) {
|
|
@@ -247,14 +250,7 @@ export async function executeAgentTask(task, dryRun, ctx) {
|
|
|
247
250
|
onToolResult: (result, toolCall) => {
|
|
248
251
|
const toolName = toolCall.tool.toLowerCase();
|
|
249
252
|
const target = toolCall.parameters.path || toolCall.parameters.command || '';
|
|
250
|
-
const actionType = toolName
|
|
251
|
-
toolName.includes('edit') ? 'edit' :
|
|
252
|
-
toolName.includes('read') ? 'read' :
|
|
253
|
-
toolName.includes('delete') ? 'delete' :
|
|
254
|
-
toolName.includes('list') ? 'list' :
|
|
255
|
-
toolName.includes('search') || toolName.includes('grep') ? 'search' :
|
|
256
|
-
toolName.includes('mkdir') ? 'mkdir' :
|
|
257
|
-
toolName.includes('fetch') ? 'fetch' : 'command';
|
|
253
|
+
const actionType = getActionType(toolName);
|
|
258
254
|
app.updateAgentProgress(0, {
|
|
259
255
|
type: actionType,
|
|
260
256
|
target,
|
|
@@ -324,11 +320,13 @@ export async function executeAgentTask(task, dryRun, ctx) {
|
|
|
324
320
|
app.addMessage({ role: 'assistant', content: 'Agent stopped by user.' });
|
|
325
321
|
}
|
|
326
322
|
else {
|
|
327
|
-
|
|
328
|
-
if (result.finalResponse)
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
323
|
+
// Show the agent's summary if available, with error details below
|
|
324
|
+
if (result.finalResponse) {
|
|
325
|
+
app.addMessage({ role: 'assistant', content: result.finalResponse });
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
app.addMessage({ role: 'assistant', content: `Agent could not complete the task: ${result.error || 'Unknown error'}` });
|
|
329
|
+
}
|
|
332
330
|
}
|
|
333
331
|
autoSaveSession(app.getMessages(), ctx.projectPath);
|
|
334
332
|
}
|
|
@@ -115,11 +115,14 @@ export const SETTINGS = [
|
|
|
115
115
|
{
|
|
116
116
|
key: 'agentAutoVerify',
|
|
117
117
|
label: 'Agent Auto-Verify',
|
|
118
|
-
getValue: () => config.get('agentAutoVerify')
|
|
118
|
+
getValue: () => config.get('agentAutoVerify'),
|
|
119
119
|
type: 'select',
|
|
120
120
|
options: [
|
|
121
|
-
{ value:
|
|
122
|
-
{ value:
|
|
121
|
+
{ value: 'off', label: 'Off' },
|
|
122
|
+
{ value: 'build', label: 'Build only' },
|
|
123
|
+
{ value: 'typecheck', label: 'Typecheck only' },
|
|
124
|
+
{ value: 'test', label: 'Test only' },
|
|
125
|
+
{ value: 'all', label: 'Build + Typecheck + Test' },
|
|
123
126
|
],
|
|
124
127
|
},
|
|
125
128
|
{
|
package/dist/utils/agent.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface AgentOptions {
|
|
|
25
25
|
onTaskUpdate?: (task: SubTask) => void;
|
|
26
26
|
abortSignal?: AbortSignal;
|
|
27
27
|
dryRun?: boolean;
|
|
28
|
-
autoVerify?: boolean;
|
|
28
|
+
autoVerify?: 'off' | 'build' | 'typecheck' | 'test' | 'all' | boolean;
|
|
29
29
|
maxFixAttempts?: number;
|
|
30
30
|
usePlanning?: boolean;
|
|
31
31
|
chatHistory?: Array<{
|
package/dist/utils/agent.js
CHANGED
|
@@ -16,7 +16,7 @@ export { loadProjectRules, formatChatHistoryForAgent };
|
|
|
16
16
|
* Calculate dynamic timeout based on task complexity
|
|
17
17
|
* Complex tasks (creating pages, multiple files) need more time
|
|
18
18
|
*/
|
|
19
|
-
function calculateDynamicTimeout(
|
|
19
|
+
function calculateDynamicTimeout(iteration, baseTimeout) {
|
|
20
20
|
// Simple approach: just use base timeout with small multiplier for later iterations
|
|
21
21
|
// Complex calculations were causing more problems than they solved
|
|
22
22
|
let multiplier = 1.0;
|
|
@@ -224,11 +224,11 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
224
224
|
if (compressed !== messages) {
|
|
225
225
|
messages.length = 0;
|
|
226
226
|
messages.push(...compressed);
|
|
227
|
-
opts.onIteration?.(iteration, `Context compressed
|
|
227
|
+
opts.onIteration?.(iteration, `Context compressed to save memory — continuing with last ${compressed.length} messages`);
|
|
228
228
|
}
|
|
229
229
|
debug(`Starting iteration ${iteration}/${opts.maxIterations}, actions: ${actions.length}`);
|
|
230
230
|
// Calculate dynamic timeout based on task complexity
|
|
231
|
-
const dynamicTimeout = calculateDynamicTimeout(
|
|
231
|
+
const dynamicTimeout = calculateDynamicTimeout(iteration, baseTimeout);
|
|
232
232
|
debug(`Using timeout: ${dynamicTimeout}ms (base: ${baseTimeout}ms)`);
|
|
233
233
|
// Get AI response with retry logic for timeouts
|
|
234
234
|
let chatResponse = null;
|
|
@@ -283,23 +283,37 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
283
283
|
await new Promise(resolve => setTimeout(resolve, 1000 * retryCount));
|
|
284
284
|
continue;
|
|
285
285
|
}
|
|
286
|
-
//
|
|
286
|
+
// All non-abort errors are retryable — retry with backoff
|
|
287
|
+
retryCount++;
|
|
287
288
|
const isRateLimit = err.message.includes('429');
|
|
288
289
|
const isServerError = err.message.includes('500') || err.message.includes('502') || err.message.includes('503') || err.message.includes('529');
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
if (
|
|
297
|
-
|
|
290
|
+
const code = isRateLimit ? '429' : isServerError ? '5xx' : 'error';
|
|
291
|
+
const waitSec = Math.min(5 * retryCount, 30);
|
|
292
|
+
debug(`${code} (retry ${retryCount}/${maxTimeoutRetries}): ${err.message}`);
|
|
293
|
+
opts.onIteration?.(iteration, `API ${code}, retrying in ${waitSec}s... (${retryCount}/${maxTimeoutRetries})`);
|
|
294
|
+
if (retryCount >= maxTimeoutRetries) {
|
|
295
|
+
// Don't throw — skip this iteration like timeouts do
|
|
296
|
+
consecutiveTimeouts++;
|
|
297
|
+
if (consecutiveTimeouts >= maxConsecutiveTimeouts) {
|
|
298
|
+
result = {
|
|
299
|
+
success: false,
|
|
300
|
+
iterations: iteration,
|
|
301
|
+
actions,
|
|
302
|
+
finalResponse: actions.length > 0
|
|
303
|
+
? `Agent made progress (${actions.length} actions) but API errors prevented completion. You can continue by running the agent again.`
|
|
304
|
+
: 'Agent could not complete the task due to repeated API errors. Check your API key and network connection.',
|
|
305
|
+
error: `API failed after ${maxTimeoutRetries} retries: ${err.message}`,
|
|
306
|
+
};
|
|
307
|
+
return result;
|
|
298
308
|
}
|
|
299
|
-
|
|
300
|
-
|
|
309
|
+
messages.push({
|
|
310
|
+
role: 'user',
|
|
311
|
+
content: 'The previous request failed. Please continue with the task.'
|
|
312
|
+
});
|
|
313
|
+
break; // Break retry loop, continue main loop
|
|
301
314
|
}
|
|
302
|
-
|
|
315
|
+
await new Promise(resolve => setTimeout(resolve, waitSec * 1000));
|
|
316
|
+
continue;
|
|
303
317
|
}
|
|
304
318
|
}
|
|
305
319
|
// If we broke out due to max retries without a response, continue to next iteration
|
|
@@ -326,7 +340,7 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
326
340
|
.replace(/<arg_key>[\s\S]*?<\/arg_value>/gi, '')
|
|
327
341
|
.replace(/Tool parameters:[\s\S]*?(?=\n\n|$)/gi, '')
|
|
328
342
|
.replace(/\{'path'[\s\S]*?\}/g, '')
|
|
329
|
-
.replace(/```[\s\S]
|
|
343
|
+
.replace(/```(?:json|tool_call)?\s*\{[\s\S]*?\}\s*```/g, '') // Only strip tool-call-like code blocks
|
|
330
344
|
.trim();
|
|
331
345
|
// Check if model indicates it wants to continue (incomplete response)
|
|
332
346
|
const continueIndicators = [
|
|
@@ -426,11 +440,14 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
426
440
|
else {
|
|
427
441
|
toolResults.push(`Tool ${toolCall.tool} failed:\n${toolResult.error || 'Unknown error'}`);
|
|
428
442
|
}
|
|
429
|
-
// Invalidate read cache when
|
|
443
|
+
// Invalidate read cache when files may have changed
|
|
430
444
|
if ((toolCall.tool === 'write_file' || toolCall.tool === 'edit_file') && toolResult.success) {
|
|
431
445
|
const filePath = toolCall.parameters.path || '';
|
|
432
446
|
readCache.delete(filePath);
|
|
433
447
|
}
|
|
448
|
+
else if (toolCall.tool === 'execute_command' && toolResult.success) {
|
|
449
|
+
readCache.clear(); // Commands can modify arbitrary files
|
|
450
|
+
}
|
|
434
451
|
}
|
|
435
452
|
// Add tool results to messages
|
|
436
453
|
messages.push({
|
|
@@ -457,9 +474,11 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
457
474
|
return result;
|
|
458
475
|
}
|
|
459
476
|
// Self-verification: Run build/test and fix errors if needed
|
|
460
|
-
const
|
|
477
|
+
const autoVerifyRaw = opts.autoVerify ?? config.get('agentAutoVerify');
|
|
478
|
+
// Support legacy boolean values: true -> 'all', false -> 'off'
|
|
479
|
+
const autoVerify = autoVerifyRaw === true ? 'all' : autoVerifyRaw === false ? 'off' : autoVerifyRaw;
|
|
461
480
|
const maxFixAttempts = opts.maxFixAttempts ?? config.get('agentMaxFixAttempts');
|
|
462
|
-
if (autoVerify && !opts.dryRun) {
|
|
481
|
+
if (autoVerify !== 'off' && !opts.dryRun) {
|
|
463
482
|
// Check if we made any file changes worth verifying
|
|
464
483
|
const hasFileChanges = actions.some(a => a.type === 'write' || a.type === 'edit' || a.type === 'delete');
|
|
465
484
|
if (hasFileChanges) {
|
|
@@ -471,15 +490,30 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
471
490
|
break;
|
|
472
491
|
}
|
|
473
492
|
opts.onIteration?.(iteration, `Verification attempt ${fixAttempt + 1}/${maxFixAttempts}`);
|
|
474
|
-
// Run verifications
|
|
493
|
+
// Run verifications based on selected mode
|
|
475
494
|
const verifyResults = await runAllVerifications(projectContext.root || process.cwd(), {
|
|
476
|
-
runBuild:
|
|
477
|
-
runTest:
|
|
478
|
-
runTypecheck:
|
|
495
|
+
runBuild: autoVerify === 'all' || autoVerify === 'build',
|
|
496
|
+
runTest: autoVerify === 'all' || autoVerify === 'test',
|
|
497
|
+
runTypecheck: autoVerify === 'all' || autoVerify === 'typecheck',
|
|
479
498
|
runLint: false,
|
|
480
499
|
});
|
|
481
500
|
opts.onVerification?.(verifyResults);
|
|
482
|
-
//
|
|
501
|
+
// Filter errors: only keep those related to files the agent touched
|
|
502
|
+
const touchedFiles = new Set(actions
|
|
503
|
+
.filter(a => a.type === 'write' || a.type === 'edit')
|
|
504
|
+
.map(a => a.target));
|
|
505
|
+
for (const vr of verifyResults) {
|
|
506
|
+
vr.errors = vr.errors.filter(e => {
|
|
507
|
+
if (!e.file)
|
|
508
|
+
return true; // Keep errors without file info (build failures etc)
|
|
509
|
+
return touchedFiles.has(e.file) || [...touchedFiles].some(f => e.file.endsWith(f) || f.endsWith(e.file));
|
|
510
|
+
});
|
|
511
|
+
// Update success based on remaining errors
|
|
512
|
+
if (vr.errors.filter(e => e.severity === 'error').length === 0) {
|
|
513
|
+
vr.success = true;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
// Check if all passed (after filtering)
|
|
483
517
|
if (!hasVerificationErrors(verifyResults)) {
|
|
484
518
|
const summary = getVerificationSummary(verifyResults);
|
|
485
519
|
finalResponse += `\n\n✓ Verification passed: ${summary.passed}/${summary.total} checks`;
|
|
@@ -543,7 +577,8 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
543
577
|
const actionLog = createActionLog(toolCall, toolResult);
|
|
544
578
|
actions.push(actionLog);
|
|
545
579
|
if (toolResult.success) {
|
|
546
|
-
|
|
580
|
+
const truncated = truncateToolResult(toolResult.output, toolCall.tool);
|
|
581
|
+
fixResults.push(`Tool ${toolCall.tool} succeeded:\n${truncated}`);
|
|
547
582
|
}
|
|
548
583
|
else {
|
|
549
584
|
fixResults.push(`Tool ${toolCall.tool} failed:\n${toolResult.error || 'Unknown error'}`);
|
package/dist/utils/agentChat.js
CHANGED
|
@@ -154,7 +154,7 @@ export async function agentChat(messages, systemPrompt, onChunk, abortSignal, dy
|
|
|
154
154
|
let isTimeout = false;
|
|
155
155
|
const timeout = setTimeout(() => { isTimeout = true; controller.abort(); }, timeoutMs);
|
|
156
156
|
if (abortSignal) {
|
|
157
|
-
abortSignal.addEventListener('abort', () => { isTimeout = false; controller.abort(); });
|
|
157
|
+
abortSignal.addEventListener('abort', () => { isTimeout = false; controller.abort(); }, { once: true });
|
|
158
158
|
}
|
|
159
159
|
const headers = { 'Content-Type': 'application/json' };
|
|
160
160
|
if (authHeader === 'Bearer') {
|
|
@@ -268,7 +268,7 @@ export async function agentChatFallback(messages, systemPrompt, onChunk, abortSi
|
|
|
268
268
|
let isTimeout = false;
|
|
269
269
|
const timeout = setTimeout(() => { isTimeout = true; controller.abort(); }, timeoutMs);
|
|
270
270
|
if (abortSignal) {
|
|
271
|
-
abortSignal.addEventListener('abort', () => { isTimeout = false; controller.abort(); });
|
|
271
|
+
abortSignal.addEventListener('abort', () => { isTimeout = false; controller.abort(); }, { once: true });
|
|
272
272
|
}
|
|
273
273
|
const headers = { 'Content-Type': 'application/json' };
|
|
274
274
|
if (authHeader === 'Bearer') {
|
package/dist/utils/shell.js
CHANGED
|
@@ -112,8 +112,8 @@ export function validateCommand(command, args, options) {
|
|
|
112
112
|
}
|
|
113
113
|
// Special validation for rm command
|
|
114
114
|
if (command === 'rm') {
|
|
115
|
-
const hasRecursive = args.some(a => a.includes('r'));
|
|
116
|
-
const hasForce = args.some(a => a.includes('f'));
|
|
115
|
+
const hasRecursive = args.some(a => a.startsWith('-') && a.includes('r'));
|
|
116
|
+
const hasForce = args.some(a => a.startsWith('-') && a.includes('f'));
|
|
117
117
|
if (hasRecursive && hasForce) {
|
|
118
118
|
// rm -rf requires extra validation
|
|
119
119
|
const paths = args.filter(a => !a.startsWith('-'));
|
|
@@ -176,8 +176,8 @@ export async function executeTool(toolCall, projectRoot) {
|
|
|
176
176
|
return { success: false, output: '', error: 'Missing required parameter: path', tool, parameters };
|
|
177
177
|
}
|
|
178
178
|
if (content === undefined || content === null) {
|
|
179
|
-
debug('write_file: content was undefined
|
|
180
|
-
|
|
179
|
+
debug('write_file failed: content was undefined');
|
|
180
|
+
return { success: false, output: '', error: 'File content was empty or truncated by the API. Try writing a smaller file or splitting into multiple writes.', tool, parameters };
|
|
181
181
|
}
|
|
182
182
|
const validation = validatePath(path, projectRoot);
|
|
183
183
|
if (!validation.valid)
|
package/dist/utils/verify.js
CHANGED
|
@@ -134,14 +134,15 @@ async function runVerifyCommand(type, command, args, projectRoot, timeout) {
|
|
|
134
134
|
const output = result.stdout + '\n' + result.stderr;
|
|
135
135
|
// Parse errors from output
|
|
136
136
|
const errors = parseErrors(output, type);
|
|
137
|
-
// If command failed but no errors were parsed, surface the failure reason
|
|
137
|
+
// If command failed but no errors were parsed, surface the failure reason as warning
|
|
138
|
+
// (not error — could be pre-existing build issue unrelated to agent's changes)
|
|
138
139
|
if (!result.success && errors.length === 0) {
|
|
139
140
|
const reason = result.stderr?.includes('timed out')
|
|
140
|
-
? `Command timed out after ${Math.round(duration / 1000)}s. This build tool may be too slow for verification
|
|
141
|
+
? `Command timed out after ${Math.round(duration / 1000)}s. This build tool may be too slow for verification.`
|
|
141
142
|
: result.stderr?.includes('not in the allowed list') || result.stderr?.includes('not allowed')
|
|
142
143
|
? `Command '${command}' is not allowed. Check shell.ts ALLOWED_COMMANDS.`
|
|
143
144
|
: result.stderr?.trim() || result.stdout?.trim() || 'Command failed with no output';
|
|
144
|
-
errors.push({ severity: '
|
|
145
|
+
errors.push({ severity: 'warning', message: reason });
|
|
145
146
|
}
|
|
146
147
|
return {
|
|
147
148
|
success: result.success,
|
|
@@ -390,25 +391,26 @@ export async function runLintVerification(projectRoot, timeout = 60000) {
|
|
|
390
391
|
export async function runAllVerifications(projectRoot, options = {}) {
|
|
391
392
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
392
393
|
const results = [];
|
|
393
|
-
// Run typecheck
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
394
|
+
// Run typecheck and lint in parallel (independent checks)
|
|
395
|
+
const parallel = [];
|
|
396
|
+
if (opts.runTypecheck)
|
|
397
|
+
parallel.push(runTypecheckVerification(projectRoot, opts.timeout));
|
|
398
|
+
if (opts.runLint)
|
|
399
|
+
parallel.push(runLintVerification(projectRoot, opts.timeout));
|
|
400
|
+
if (parallel.length > 0) {
|
|
401
|
+
const parallelResults = await Promise.all(parallel);
|
|
402
|
+
for (const r of parallelResults) {
|
|
403
|
+
if (r)
|
|
404
|
+
results.push(r);
|
|
405
|
+
}
|
|
398
406
|
}
|
|
399
|
-
// Run build
|
|
407
|
+
// Run build after typecheck/lint (may depend on them)
|
|
400
408
|
if (opts.runBuild) {
|
|
401
409
|
const result = await runBuildVerification(projectRoot, opts.timeout);
|
|
402
410
|
if (result)
|
|
403
411
|
results.push(result);
|
|
404
412
|
}
|
|
405
|
-
// Run
|
|
406
|
-
if (opts.runLint) {
|
|
407
|
-
const result = await runLintVerification(projectRoot, opts.timeout);
|
|
408
|
-
if (result)
|
|
409
|
-
results.push(result);
|
|
410
|
-
}
|
|
411
|
-
// Run tests last (slowest)
|
|
413
|
+
// Run tests last (slowest, depends on build)
|
|
412
414
|
if (opts.runTest) {
|
|
413
415
|
const result = await runTestVerification(projectRoot, opts.timeout);
|
|
414
416
|
if (result)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeep",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.71",
|
|
4
4
|
"description": "AI-powered coding assistant built for the terminal. Multiple LLM providers, project-aware context, and a seamless development workflow.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|