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 CHANGED
@@ -357,13 +357,17 @@ Main modules: src, components, hooks, utils
357
357
  - Naming: camelCase
358
358
  ```
359
359
 
360
- ### Self-Verification
361
- After making changes, the agent automatically:
362
- 1. Runs **build** to check for compilation errors
363
- 2. Runs **tests** to ensure nothing is broken
364
- 3. Runs **type checking** for TypeScript/PHP projects
365
- 4. If errors found automatically tries to fix them (up to 3 attempts)
366
- 5. Reports final verification status
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 | On | Automatically run build/test after changes |
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 = {
@@ -33,7 +33,7 @@ interface ConfigSchema {
33
33
  agentConfirmation: 'always' | 'dangerous' | 'never';
34
34
  agentAutoCommit: boolean;
35
35
  agentAutoCommitBranch: boolean;
36
- agentAutoVerify: boolean;
36
+ agentAutoVerify: 'off' | 'build' | 'typecheck' | 'test' | 'all';
37
37
  agentMaxFixAttempts: number;
38
38
  agentMaxIterations: number;
39
39
  agentMaxDuration: number;
@@ -146,7 +146,7 @@ function createConfig() {
146
146
  agentConfirmation: 'dangerous',
147
147
  agentAutoCommit: false,
148
148
  agentAutoCommitBranch: false,
149
- agentAutoVerify: true,
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.includes('write') ? 'write' :
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.includes('write') ? 'write' :
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
- const failLines = [];
328
- if (result.finalResponse)
329
- failLines.push(result.finalResponse);
330
- failLines.push(`**Agent stopped**: ${result.error || 'Unknown error'}`);
331
- app.addMessage({ role: 'assistant', content: failLines.join('\n\n') });
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') !== false,
118
+ getValue: () => config.get('agentAutoVerify'),
119
119
  type: 'select',
120
120
  options: [
121
- { value: true, label: 'On' },
122
- { value: false, label: 'Off' },
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
  {
@@ -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<{
@@ -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(prompt, iteration, baseTimeout) {
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 (${compressed.length} messages kept)`);
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(prompt, iteration, baseTimeout);
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
- // Retry on transient errors: rate-limit, server errors, network failures
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 isNetworkError = ['ECONNRESET', 'ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND', 'EPIPE', 'EAI_AGAIN'].some(code => err.message.includes(code));
290
- if (isRateLimit || isServerError || isNetworkError) {
291
- retryCount++;
292
- const waitSec = Math.min(5 * retryCount, 30); // 5s, 10s, 15s max 30s
293
- const code = isRateLimit ? '429' : isServerError ? '5xx' : 'network';
294
- debug(`${code} error (retry ${retryCount}/${maxTimeoutRetries}), waiting ${waitSec}s`);
295
- opts.onIteration?.(iteration, `Server error (${code}), retrying in ${waitSec}s... (${retryCount}/${maxTimeoutRetries})`);
296
- if (retryCount >= maxTimeoutRetries) {
297
- throw error; // Give up after max retries
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
- await new Promise(resolve => setTimeout(resolve, waitSec * 1000));
300
- continue;
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
- throw error;
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]*?```/g, '')
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 a file is written/edited
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 autoVerify = opts.autoVerify ?? config.get('agentAutoVerify');
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: true,
477
- runTest: true,
478
- runTypecheck: true,
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
- // Check if all passed
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
- fixResults.push(`Tool ${toolCall.tool} succeeded:\n${toolResult.output}`);
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'}`);
@@ -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') {
@@ -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, using placeholder');
180
- content = '<!-- Content was not provided -->\n';
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)
@@ -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 explicitly
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. Consider adding a faster typecheck script to package.json.`
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: 'error', message: reason });
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 first (fastest feedback)
394
- if (opts.runTypecheck) {
395
- const result = await runTypecheckVerification(projectRoot, opts.timeout);
396
- if (result)
397
- results.push(result);
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 lint
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.69",
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",