codeep 1.0.29 → 1.0.33

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
@@ -497,11 +497,14 @@ With write access enabled:
497
497
  | Setting | Default | Description |
498
498
  |---------|---------|-------------|
499
499
  | Temperature | 0.7 | Response creativity (0.0 - 2.0) |
500
- | Max Tokens | 4096 | Maximum response length |
501
- | API Timeout | 30000ms | Request timeout |
500
+ | Max Tokens | 8192 | Maximum response length |
501
+ | API Timeout | 60000ms | Request timeout |
502
502
  | API Rate Limit | 30/min | Max API calls per minute |
503
503
  | Command Rate Limit | 100/min | Max commands per minute |
504
504
  | Agent Mode | Auto | `Auto` = agent runs on every message, `Manual` = use /agent |
505
+ | Agent API Timeout | 180000ms | Timeout per agent API call (auto-adjusted for complexity) |
506
+ | Agent Max Duration | 20 min | Maximum time for agent to run (5-60 min) |
507
+ | Agent Max Iterations | 100 | Maximum agent iterations (10-200) |
505
508
  | Agent Confirmation | Dangerous | `Never`, `Dangerous` (default), or `Always` |
506
509
  | Agent Auto-Commit | Off | Automatically commit after agent completes |
507
510
  | Agent Branch | Off | Create new branch for agent commits |
package/dist/app.js CHANGED
@@ -266,8 +266,7 @@ export const App = () => {
266
266
  setAbortController(controller);
267
267
  try {
268
268
  const result = await runAgent(prompt, projectContext, {
269
- maxIterations: 50, // Increased for complex tasks
270
- maxDuration: 5 * 60 * 1000, // 5 minutes
269
+ // Use config values - no hardcoded limits
271
270
  dryRun,
272
271
  onIteration: (iteration, message) => {
273
272
  setAgentIteration(iteration);
@@ -1,7 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useMemo, useEffect } from 'react';
2
+ import { useState, useMemo, useEffect, useRef } from 'react';
3
3
  import { Text, Box, useInput } from 'ink';
4
- import TextInput from 'ink-text-input';
5
4
  import clipboard from 'clipboardy';
6
5
  const COMMANDS = [
7
6
  { cmd: '/help', desc: 'Show help' },
@@ -30,16 +29,20 @@ const COMMANDS = [
30
29
  ];
31
30
  export const ChatInput = ({ onSubmit, disabled, history = [], clearTrigger = 0 }) => {
32
31
  const [value, setValue] = useState('');
32
+ const [cursorPos, setCursorPos] = useState(0);
33
33
  const [selectedIndex, setSelectedIndex] = useState(0);
34
- const [isSelectingCommand, setIsSelectingCommand] = useState(false);
35
34
  const [pasteInfo, setPasteInfo] = useState(null);
35
+ const [historyIndex, setHistoryIndex] = useState(-1);
36
+ const lastInputTime = useRef(0);
37
+ const inputBuffer = useRef('');
36
38
  // Clear input when clearTrigger changes
37
39
  useEffect(() => {
38
40
  if (clearTrigger > 0) {
39
41
  setValue('');
42
+ setCursorPos(0);
40
43
  setSelectedIndex(0);
41
- setIsSelectingCommand(false);
42
44
  setPasteInfo(null);
45
+ setHistoryIndex(-1);
43
46
  }
44
47
  }, [clearTrigger]);
45
48
  // Filter commands based on input
@@ -52,64 +55,227 @@ export const ChatInput = ({ onSubmit, disabled, history = [], clearTrigger = 0 }
52
55
  useEffect(() => {
53
56
  if (suggestions.length > 0) {
54
57
  setSelectedIndex(0);
55
- setIsSelectingCommand(true);
58
+ }
59
+ }, [suggestions.length]);
60
+ // Detect paste by checking for rapid input (multiple chars in < 50ms)
61
+ const detectPaste = (newChars) => {
62
+ const now = Date.now();
63
+ const timeDiff = now - lastInputTime.current;
64
+ // If multiple characters arrive very quickly, it's likely a paste
65
+ if (timeDiff < 50 && newChars.length > 0) {
66
+ inputBuffer.current += newChars;
67
+ return true;
68
+ }
69
+ // Process any buffered paste
70
+ if (inputBuffer.current.length > 5) {
71
+ // This was a paste that just finished
72
+ const pastedText = inputBuffer.current;
73
+ inputBuffer.current = '';
74
+ handlePastedText(pastedText);
75
+ lastInputTime.current = now;
76
+ return true;
77
+ }
78
+ inputBuffer.current = newChars;
79
+ lastInputTime.current = now;
80
+ return false;
81
+ };
82
+ const handlePastedText = (text) => {
83
+ const trimmed = text.trim();
84
+ if (!trimmed)
85
+ return;
86
+ const lines = trimmed.split(/\r?\n/);
87
+ const lineCount = lines.length;
88
+ const charCount = trimmed.length;
89
+ // For multi-line or long pastes, store info
90
+ if (lineCount > 1 || charCount > 100) {
91
+ const firstLine = lines[0].substring(0, 60);
92
+ const preview = firstLine + (lines[0].length > 60 ? '...' : '');
93
+ setPasteInfo({
94
+ lines: lineCount,
95
+ chars: charCount,
96
+ preview,
97
+ fullText: trimmed,
98
+ });
99
+ // Show indicator in input field
100
+ const indicator = `📋 [${lineCount} lines, ${charCount} chars]`;
101
+ setValue(prev => prev + indicator);
102
+ setCursorPos(prev => prev + indicator.length);
56
103
  }
57
104
  else {
58
- setIsSelectingCommand(false);
105
+ // Short paste - insert directly
106
+ setValue(prev => prev + trimmed);
107
+ setCursorPos(prev => prev + trimmed.length);
108
+ setPasteInfo(null);
59
109
  }
60
- }, [suggestions.length]);
61
- // Handle keyboard navigation for command suggestions and paste
62
- useInput(async (input, key) => {
110
+ };
111
+ // Main input handler
112
+ useInput((input, key) => {
63
113
  if (disabled)
64
114
  return;
65
- // Handle paste (Ctrl+V) - read from clipboard and insert
115
+ // Handle Ctrl+V paste
66
116
  if (key.ctrl && input === 'v') {
67
- try {
68
- const clipboardText = await clipboard.read();
69
- // Replace newlines with spaces to prevent multi-line issues
70
- const sanitized = clipboardText.replace(/\r?\n/g, ' ').trim();
71
- // Store full text
72
- setValue(prev => prev + sanitized);
73
- // Show paste info if long text
74
- if (sanitized.length > 100) {
75
- setPasteInfo(`[pasted ${sanitized.length} chars]`);
76
- // Clear info after 2 seconds
77
- setTimeout(() => setPasteInfo(null), 2000);
117
+ clipboard.read().then(text => {
118
+ if (text) {
119
+ handlePastedText(text);
120
+ }
121
+ }).catch(() => { });
122
+ return;
123
+ }
124
+ // Handle Enter - submit
125
+ if (key.return) {
126
+ if (value.trim()) {
127
+ let submitValue = value.trim();
128
+ // Replace paste indicator with actual content
129
+ if (pasteInfo && submitValue.includes('📋 [')) {
130
+ submitValue = submitValue.replace(/📋 \[\d+ lines, \d+ chars\]/, pasteInfo.fullText);
78
131
  }
132
+ onSubmit(submitValue);
133
+ setValue('');
134
+ setCursorPos(0);
135
+ setPasteInfo(null);
136
+ setHistoryIndex(-1);
79
137
  }
80
- catch (error) {
81
- // Clipboard read failed, ignore
138
+ return;
139
+ }
140
+ // Handle Escape - clear paste info or input
141
+ if (key.escape) {
142
+ if (pasteInfo) {
143
+ // Remove paste indicator from value
144
+ setValue(prev => prev.replace(/📋 \[\d+ lines, \d+ chars\]/, ''));
145
+ setPasteInfo(null);
146
+ }
147
+ else if (value) {
148
+ setValue('');
149
+ setCursorPos(0);
82
150
  }
83
151
  return;
84
152
  }
85
- if (suggestions.length === 0)
153
+ // Handle Backspace
154
+ if (key.backspace || key.delete) {
155
+ if (cursorPos > 0) {
156
+ setValue(prev => prev.slice(0, cursorPos - 1) + prev.slice(cursorPos));
157
+ setCursorPos(prev => prev - 1);
158
+ // Clear paste info if we deleted the indicator
159
+ if (pasteInfo && !value.includes('📋 [')) {
160
+ setPasteInfo(null);
161
+ }
162
+ }
86
163
  return;
87
- // Navigate suggestions with up/down arrows
164
+ }
165
+ // Handle Tab - autocomplete command
166
+ if (key.tab && suggestions.length > 0) {
167
+ setValue(suggestions[selectedIndex].cmd + ' ');
168
+ setCursorPos(suggestions[selectedIndex].cmd.length + 1);
169
+ return;
170
+ }
171
+ // Handle Up Arrow - navigate suggestions or history
88
172
  if (key.upArrow) {
89
- setSelectedIndex(i => Math.max(0, i - 1));
173
+ if (suggestions.length > 0) {
174
+ setSelectedIndex(i => Math.max(0, i - 1));
175
+ }
176
+ else if (history.length > 0) {
177
+ const newIndex = historyIndex < history.length - 1 ? historyIndex + 1 : historyIndex;
178
+ setHistoryIndex(newIndex);
179
+ if (newIndex >= 0 && history[history.length - 1 - newIndex]) {
180
+ const historyValue = history[history.length - 1 - newIndex];
181
+ setValue(historyValue);
182
+ setCursorPos(historyValue.length);
183
+ }
184
+ }
90
185
  return;
91
186
  }
187
+ // Handle Down Arrow - navigate suggestions or history
92
188
  if (key.downArrow) {
93
- setSelectedIndex(i => Math.min(suggestions.length - 1, i + 1));
189
+ if (suggestions.length > 0) {
190
+ setSelectedIndex(i => Math.min(suggestions.length - 1, i + 1));
191
+ }
192
+ else if (historyIndex > 0) {
193
+ const newIndex = historyIndex - 1;
194
+ setHistoryIndex(newIndex);
195
+ const historyValue = history[history.length - 1 - newIndex];
196
+ setValue(historyValue);
197
+ setCursorPos(historyValue.length);
198
+ }
199
+ else if (historyIndex === 0) {
200
+ setHistoryIndex(-1);
201
+ setValue('');
202
+ setCursorPos(0);
203
+ }
94
204
  return;
95
205
  }
96
- // Tab to autocomplete selected command
97
- if (key.tab) {
98
- setValue(suggestions[selectedIndex].cmd);
99
- setIsSelectingCommand(false);
206
+ // Handle Left Arrow
207
+ if (key.leftArrow) {
208
+ setCursorPos(prev => Math.max(0, prev - 1));
100
209
  return;
101
210
  }
102
- }, { isActive: !disabled });
103
- const handleChange = (newValue) => {
104
- setValue(newValue);
105
- };
106
- const handleSubmit = () => {
107
- if (value.trim() && !disabled) {
108
- onSubmit(value.trim());
211
+ // Handle Right Arrow
212
+ if (key.rightArrow) {
213
+ setCursorPos(prev => Math.min(value.length, prev + 1));
214
+ return;
215
+ }
216
+ // Handle Ctrl+A - go to beginning
217
+ if (key.ctrl && input === 'a') {
218
+ setCursorPos(0);
219
+ return;
220
+ }
221
+ // Handle Ctrl+E - go to end
222
+ if (key.ctrl && input === 'e') {
223
+ setCursorPos(value.length);
224
+ return;
225
+ }
226
+ // Handle Ctrl+U - clear line
227
+ if (key.ctrl && input === 'u') {
109
228
  setValue('');
229
+ setCursorPos(0);
110
230
  setPasteInfo(null);
111
- setIsSelectingCommand(false);
231
+ return;
232
+ }
233
+ // Handle Ctrl+W - delete word
234
+ if (key.ctrl && input === 'w') {
235
+ const beforeCursor = value.slice(0, cursorPos);
236
+ const afterCursor = value.slice(cursorPos);
237
+ const lastSpace = beforeCursor.trimEnd().lastIndexOf(' ');
238
+ const newBefore = lastSpace >= 0 ? beforeCursor.slice(0, lastSpace + 1) : '';
239
+ setValue(newBefore + afterCursor);
240
+ setCursorPos(newBefore.length);
241
+ return;
242
+ }
243
+ // Regular character input
244
+ if (input && !key.ctrl && !key.meta) {
245
+ // Check if this might be part of a paste
246
+ if (!detectPaste(input)) {
247
+ // Normal single character input
248
+ setValue(prev => prev.slice(0, cursorPos) + input + prev.slice(cursorPos));
249
+ setCursorPos(prev => prev + input.length);
250
+ }
251
+ }
252
+ }, { isActive: !disabled });
253
+ // Process any remaining buffered input after a delay
254
+ useEffect(() => {
255
+ const timer = setTimeout(() => {
256
+ if (inputBuffer.current.length > 0) {
257
+ const buffered = inputBuffer.current;
258
+ inputBuffer.current = '';
259
+ if (buffered.length > 5) {
260
+ handlePastedText(buffered);
261
+ }
262
+ else {
263
+ setValue(prev => prev + buffered);
264
+ setCursorPos(prev => prev + buffered.length);
265
+ }
266
+ }
267
+ }, 100);
268
+ return () => clearTimeout(timer);
269
+ }, [value]);
270
+ // Render input with cursor
271
+ const renderInput = () => {
272
+ if (!value) {
273
+ return _jsx(Text, { color: "gray", children: "Type a message or /command..." });
112
274
  }
275
+ const before = value.slice(0, cursorPos);
276
+ const cursor = value[cursorPos] || ' ';
277
+ const after = value.slice(cursorPos + 1);
278
+ return (_jsxs(Text, { children: [before, _jsx(Text, { backgroundColor: "white", color: "black", children: cursor }), after] }));
113
279
  };
114
- return (_jsxs(Box, { flexDirection: "column", children: [suggestions.length > 0 && (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [suggestions.map((s, i) => (_jsxs(Text, { children: [i === selectedIndex ? _jsx(Text, { color: "#f02a30", children: "\u25B8 " }) : ' ', _jsx(Text, { color: i === selectedIndex ? '#f02a30' : undefined, bold: i === selectedIndex, children: s.cmd }), _jsxs(Text, { color: i === selectedIndex ? undefined : 'gray', children: [" - ", s.desc] })] }, s.cmd))), _jsxs(Text, { color: "gray", children: ["\u2191\u2193 Navigate \u2022 Tab Complete \u2022 ", suggestions.length, " ", suggestions.length === 1 ? 'command' : 'commands'] })] })), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: "#f02a30", bold: true, children: '> ' }), disabled ? (_jsx(Text, { children: "..." })) : (_jsx(TextInput, { value: value, onChange: handleChange, onSubmit: handleSubmit, placeholder: "Type a message or /command..." }))] }), pasteInfo && (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "green", children: pasteInfo }) }))] })] }));
280
+ return (_jsxs(Box, { flexDirection: "column", children: [suggestions.length > 0 && (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [suggestions.slice(0, 8).map((s, i) => (_jsxs(Text, { children: [i === selectedIndex ? _jsx(Text, { color: "#f02a30", children: "\u25B8 " }) : ' ', _jsx(Text, { color: i === selectedIndex ? '#f02a30' : undefined, bold: i === selectedIndex, children: s.cmd }), _jsxs(Text, { color: i === selectedIndex ? undefined : 'gray', children: [" - ", s.desc] })] }, s.cmd))), _jsx(Text, { color: "gray", dimColor: true, children: "\u2191\u2193 navigate \u2022 Tab complete \u2022 Esc cancel" })] })), pasteInfo && (_jsxs(Box, { borderStyle: "round", borderColor: "cyan", paddingX: 1, marginBottom: 1, flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "\uD83D\uDCCB Pasted Content" }), _jsxs(Text, { children: [_jsx(Text, { color: "white", bold: true, children: pasteInfo.lines }), _jsxs(Text, { color: "gray", children: [" ", pasteInfo.lines === 1 ? 'line' : 'lines', " \u2022 "] }), _jsx(Text, { color: "white", bold: true, children: pasteInfo.chars }), _jsx(Text, { color: "gray", children: " characters" })] }), _jsx(Text, { color: "gray", dimColor: true, wrap: "truncate", children: pasteInfo.preview }), _jsx(Text, { color: "gray", dimColor: true, children: "Press Enter to send \u2022 Esc to remove" })] })), _jsxs(Box, { children: [_jsx(Text, { color: "#f02a30", bold: true, children: '> ' }), disabled ? (_jsx(Text, { color: "gray", children: "..." })) : (renderInput())] })] }));
115
281
  };
@@ -78,6 +78,24 @@ const SETTINGS = [
78
78
  max: 300000,
79
79
  step: 10000,
80
80
  },
81
+ {
82
+ key: 'agentMaxDuration',
83
+ label: 'Agent Max Duration (min)',
84
+ value: () => config.get('agentMaxDuration'),
85
+ type: 'number',
86
+ min: 5,
87
+ max: 60,
88
+ step: 5,
89
+ },
90
+ {
91
+ key: 'agentMaxIterations',
92
+ label: 'Agent Max Iterations',
93
+ value: () => config.get('agentMaxIterations'),
94
+ type: 'number',
95
+ min: 10,
96
+ max: 200,
97
+ step: 10,
98
+ },
81
99
  ];
82
100
  export const Settings = ({ onClose, notify }) => {
83
101
  const [selected, setSelected] = useState(0);
@@ -1,6 +1,12 @@
1
1
  /**
2
2
  * Agent loop - autonomous task execution
3
3
  */
4
+ // Debug logging helper - only logs when CODEEP_DEBUG=1
5
+ const debug = (...args) => {
6
+ if (process.env.CODEEP_DEBUG === '1') {
7
+ console.error('[DEBUG]', ...args);
8
+ }
9
+ };
4
10
  /**
5
11
  * Custom error class for timeout - allows distinguishing from user abort
6
12
  */
@@ -236,15 +242,21 @@ async function agentChat(messages, systemPrompt, onChunk, abortSignal, dynamicTi
236
242
  throw new Error(`API error: ${response.status} - ${errorText}`);
237
243
  }
238
244
  const data = await response.json();
245
+ debug('Raw API response:', JSON.stringify(data, null, 2).substring(0, 1500));
239
246
  if (protocol === 'openai') {
240
247
  const message = data.choices?.[0]?.message;
241
248
  const content = message?.content || '';
242
249
  const rawToolCalls = message?.tool_calls || [];
250
+ debug('Raw tool_calls:', JSON.stringify(rawToolCalls, null, 2));
243
251
  const toolCalls = parseOpenAIToolCalls(rawToolCalls);
252
+ debug('Parsed tool calls:', toolCalls.length, toolCalls.map(t => t.tool));
244
253
  // If no native tool calls, try parsing from content (some models return text-based)
245
254
  if (toolCalls.length === 0 && content) {
255
+ debug('No native tool calls, checking content for text-based calls...');
256
+ debug('Content preview:', content.substring(0, 300));
246
257
  const textToolCalls = parseToolCalls(content);
247
258
  if (textToolCalls.length > 0) {
259
+ debug('Found text-based tool calls:', textToolCalls.length);
248
260
  return { content, toolCalls: textToolCalls, usedNativeTools: false };
249
261
  }
250
262
  }
@@ -534,6 +546,7 @@ export async function runAgent(prompt, projectContext, options = {}) {
534
546
  }
535
547
  // Check abort signal
536
548
  if (opts.abortSignal?.aborted) {
549
+ debug('Agent aborted at iteration', iteration);
537
550
  result = {
538
551
  success: false,
539
552
  iterations: iteration,
@@ -545,8 +558,10 @@ export async function runAgent(prompt, projectContext, options = {}) {
545
558
  }
546
559
  iteration++;
547
560
  opts.onIteration?.(iteration, `Iteration ${iteration}/${opts.maxIterations}`);
561
+ debug(`Starting iteration ${iteration}/${opts.maxIterations}, actions: ${actions.length}`);
548
562
  // Calculate dynamic timeout based on task complexity
549
563
  const dynamicTimeout = calculateDynamicTimeout(prompt, iteration, baseTimeout);
564
+ debug(`Using timeout: ${dynamicTimeout}ms (base: ${baseTimeout}ms)`);
550
565
  // Get AI response with retry logic for timeouts
551
566
  let chatResponse;
552
567
  let retryCount = 0;
@@ -574,6 +589,7 @@ export async function runAgent(prompt, projectContext, options = {}) {
574
589
  if (err.name === 'TimeoutError') {
575
590
  retryCount++;
576
591
  consecutiveTimeouts++;
592
+ debug(`Timeout occurred (retry ${retryCount}/${maxTimeoutRetries}, consecutive: ${consecutiveTimeouts})`);
577
593
  opts.onIteration?.(iteration, `API timeout, retrying (${retryCount}/${maxTimeoutRetries})...`);
578
594
  if (retryCount >= maxTimeoutRetries) {
579
595
  // Too many retries for this iteration
@@ -617,6 +633,7 @@ export async function runAgent(prompt, projectContext, options = {}) {
617
633
  }
618
634
  // If no tool calls, check if model wants to continue or is really done
619
635
  if (toolCalls.length === 0) {
636
+ debug(`No tool calls at iteration ${iteration}, content length: ${content.length}`);
620
637
  // Remove <think>...</think> tags from response (some models include thinking)
621
638
  finalResponse = content.replace(/<think>[\s\S]*?<\/think>/gi, '').trim();
622
639
  // Check if model indicates it wants to continue (incomplete response)
@@ -631,6 +648,7 @@ export async function runAgent(prompt, projectContext, options = {}) {
631
648
  // by looking for incomplete actions (e.g., write_file without content)
632
649
  const hasIncompleteWork = iteration < 10 && wantsToContinue && finalResponse.length < 500;
633
650
  if (hasIncompleteWork) {
651
+ debug('Model wants to continue, prompting for next action');
634
652
  messages.push({ role: 'assistant', content });
635
653
  messages.push({
636
654
  role: 'user',
@@ -639,6 +657,7 @@ export async function runAgent(prompt, projectContext, options = {}) {
639
657
  continue;
640
658
  }
641
659
  // Model is done
660
+ debug(`Agent finished at iteration ${iteration}`);
642
661
  break;
643
662
  }
644
663
  // Add assistant response to history
@@ -2,6 +2,12 @@
2
2
  * Agent tools - definitions and execution
3
3
  */
4
4
  import { existsSync, readdirSync, statSync, readFileSync, writeFileSync, unlinkSync, mkdirSync, rmSync } from 'fs';
5
+ // Debug logging helper - only logs when CODEEP_DEBUG=1
6
+ const debug = (...args) => {
7
+ if (process.env.CODEEP_DEBUG === '1') {
8
+ console.error('[DEBUG]', ...args);
9
+ }
10
+ };
5
11
  import { join, dirname, relative, resolve, isAbsolute } from 'path';
6
12
  import { executeCommand } from './shell.js';
7
13
  import { recordWrite, recordEdit, recordDelete, recordMkdir, recordCommand } from './history.js';
@@ -217,27 +223,30 @@ export function parseOpenAIToolCalls(toolCalls) {
217
223
  catch (e) {
218
224
  // JSON parsing failed - likely truncated response
219
225
  // Try to extract what we can from partial JSON
226
+ debug(`Failed to parse tool arguments for ${toolName}, attempting partial extraction...`);
227
+ debug('Raw args preview:', rawArgs.substring(0, 200));
220
228
  const partialParams = extractPartialToolParams(toolName, rawArgs);
221
229
  if (partialParams) {
230
+ debug(`Successfully extracted partial params for ${toolName}:`, Object.keys(partialParams));
222
231
  parameters = partialParams;
223
232
  }
224
233
  else {
234
+ debug(`Could not extract params, skipping ${toolName}`);
225
235
  continue;
226
236
  }
227
237
  }
228
238
  // Validate required parameters for specific tools
229
239
  // For write_file, we need at least a path (content can be empty string or placeholder)
230
240
  if (toolName === 'write_file' && !parameters.path) {
231
- // Log for debugging write_file issues
232
- if (process.env.CODEEP_DEBUG) {
233
- console.error(`[WARN] write_file missing path, raw args: ${rawArgs.substring(0, 200)}`);
234
- }
241
+ debug(`Skipping write_file - missing path. Raw args:`, rawArgs.substring(0, 200));
235
242
  continue;
236
243
  }
237
244
  if (toolName === 'read_file' && !parameters.path) {
245
+ debug(`Skipping read_file - missing path`);
238
246
  continue;
239
247
  }
240
248
  if (toolName === 'edit_file' && (!parameters.path || parameters.old_text === undefined || parameters.new_text === undefined)) {
249
+ debug(`Skipping edit_file - missing required params`);
241
250
  continue;
242
251
  }
243
252
  parsed.push({
@@ -334,6 +343,7 @@ function extractPartialToolParams(toolName, rawArgs) {
334
343
  return null;
335
344
  }
336
345
  catch (e) {
346
+ debug('Error in extractPartialToolParams:', e);
337
347
  return null;
338
348
  }
339
349
  }
@@ -589,6 +599,7 @@ export function executeTool(toolCall, projectRoot) {
589
599
  // Normalize tool name to handle case variations (WRITE_FILE -> write_file)
590
600
  const tool = normalizeToolName(toolCall.tool);
591
601
  const parameters = toolCall.parameters;
602
+ debug(`Executing tool: ${tool}`, parameters.path || parameters.command || '');
592
603
  try {
593
604
  switch (tool) {
594
605
  case 'read_file': {
@@ -618,10 +629,12 @@ export function executeTool(toolCall, projectRoot) {
618
629
  const path = parameters.path;
619
630
  let content = parameters.content;
620
631
  if (!path) {
632
+ debug('write_file failed: missing path');
621
633
  return { success: false, output: '', error: 'Missing required parameter: path', tool, parameters };
622
634
  }
623
635
  // Allow empty content or provide placeholder for truncated responses
624
636
  if (content === undefined || content === null) {
637
+ debug('write_file: content was undefined, using placeholder');
625
638
  content = '<!-- Content was not provided -->\n';
626
639
  }
627
640
  const validation = validatePath(path, projectRoot);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeep",
3
- "version": "1.0.29",
3
+ "version": "1.0.33",
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",