codeep 1.0.28 → 1.0.31
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 +5 -2
- package/dist/app.js +1 -2
- package/dist/components/Input.js +34 -11
- package/dist/components/Settings.js +18 -0
- package/dist/utils/agent.js +2 -2
- package/dist/utils/tools.js +43 -20
- package/package.json +1 -1
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 |
|
|
501
|
-
| API 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
|
-
|
|
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);
|
package/dist/components/Input.js
CHANGED
|
@@ -33,6 +33,7 @@ export const ChatInput = ({ onSubmit, disabled, history = [], clearTrigger = 0 }
|
|
|
33
33
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
34
34
|
const [isSelectingCommand, setIsSelectingCommand] = useState(false);
|
|
35
35
|
const [pasteInfo, setPasteInfo] = useState(null);
|
|
36
|
+
const [fullPasteText, setFullPasteText] = useState(null);
|
|
36
37
|
// Clear input when clearTrigger changes
|
|
37
38
|
useEffect(() => {
|
|
38
39
|
if (clearTrigger > 0) {
|
|
@@ -40,6 +41,7 @@ export const ChatInput = ({ onSubmit, disabled, history = [], clearTrigger = 0 }
|
|
|
40
41
|
setSelectedIndex(0);
|
|
41
42
|
setIsSelectingCommand(false);
|
|
42
43
|
setPasteInfo(null);
|
|
44
|
+
setFullPasteText(null);
|
|
43
45
|
}
|
|
44
46
|
}, [clearTrigger]);
|
|
45
47
|
// Filter commands based on input
|
|
@@ -66,15 +68,29 @@ export const ChatInput = ({ onSubmit, disabled, history = [], clearTrigger = 0 }
|
|
|
66
68
|
if (key.ctrl && input === 'v') {
|
|
67
69
|
try {
|
|
68
70
|
const clipboardText = await clipboard.read();
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
const trimmed = clipboardText.trim();
|
|
72
|
+
if (!trimmed)
|
|
73
|
+
return;
|
|
74
|
+
const lines = trimmed.split(/\r?\n/);
|
|
75
|
+
const lineCount = lines.length;
|
|
76
|
+
const charCount = trimmed.length;
|
|
77
|
+
// For multi-line or long pastes, show summary and store full text
|
|
78
|
+
if (lineCount > 1 || charCount > 200) {
|
|
79
|
+
// Store the full text for submission
|
|
80
|
+
setFullPasteText(trimmed);
|
|
81
|
+
// Create a short preview (first line, truncated)
|
|
82
|
+
const firstLine = lines[0].substring(0, 50);
|
|
83
|
+
const preview = firstLine + (lines[0].length > 50 ? '...' : '');
|
|
84
|
+
// Show compact indicator in input
|
|
85
|
+
setValue(prev => prev + `[paste: ${lineCount} lines, ${charCount} chars]`);
|
|
86
|
+
// Show detailed info below
|
|
87
|
+
setPasteInfo({ lines: lineCount, chars: charCount, preview });
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
// Short single-line paste - just insert directly
|
|
91
|
+
setValue(prev => prev + trimmed);
|
|
92
|
+
setFullPasteText(null);
|
|
93
|
+
setPasteInfo(null);
|
|
78
94
|
}
|
|
79
95
|
}
|
|
80
96
|
catch (error) {
|
|
@@ -105,11 +121,18 @@ export const ChatInput = ({ onSubmit, disabled, history = [], clearTrigger = 0 }
|
|
|
105
121
|
};
|
|
106
122
|
const handleSubmit = () => {
|
|
107
123
|
if (value.trim() && !disabled) {
|
|
108
|
-
|
|
124
|
+
// If we have stored paste text, replace the placeholder with actual content
|
|
125
|
+
let submitValue = value.trim();
|
|
126
|
+
if (fullPasteText && submitValue.includes('[paste:')) {
|
|
127
|
+
// Replace the paste placeholder with actual content
|
|
128
|
+
submitValue = submitValue.replace(/\[paste: \d+ lines, \d+ chars\]/, fullPasteText);
|
|
129
|
+
}
|
|
130
|
+
onSubmit(submitValue);
|
|
109
131
|
setValue('');
|
|
110
132
|
setPasteInfo(null);
|
|
133
|
+
setFullPasteText(null);
|
|
111
134
|
setIsSelectingCommand(false);
|
|
112
135
|
}
|
|
113
136
|
};
|
|
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 && (
|
|
137
|
+
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 && (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: [_jsxs(Text, { color: "cyan", children: ["\uD83D\uDCCB Pasted: ", _jsx(Text, { bold: true, children: pasteInfo.lines }), " ", pasteInfo.lines === 1 ? 'line' : 'lines', ", ", _jsx(Text, { bold: true, children: pasteInfo.chars }), " chars"] }), _jsxs(Text, { color: "gray", dimColor: true, children: ["Preview: ", pasteInfo.preview] })] }))] })] }));
|
|
115
138
|
};
|
|
@@ -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);
|
package/dist/utils/agent.js
CHANGED
|
@@ -343,7 +343,7 @@ async function agentChatFallback(messages, systemPrompt, onChunk, abortSignal, d
|
|
|
343
343
|
],
|
|
344
344
|
stream: Boolean(onChunk),
|
|
345
345
|
temperature: config.get('temperature'),
|
|
346
|
-
max_tokens: config.get('maxTokens'),
|
|
346
|
+
max_tokens: Math.max(config.get('maxTokens'), 16384), // Ensure enough tokens for large file generation
|
|
347
347
|
};
|
|
348
348
|
}
|
|
349
349
|
else {
|
|
@@ -357,7 +357,7 @@ async function agentChatFallback(messages, systemPrompt, onChunk, abortSignal, d
|
|
|
357
357
|
],
|
|
358
358
|
stream: Boolean(onChunk),
|
|
359
359
|
temperature: config.get('temperature'),
|
|
360
|
-
max_tokens: config.get('maxTokens'),
|
|
360
|
+
max_tokens: Math.max(config.get('maxTokens'), 16384), // Ensure enough tokens for large file generation
|
|
361
361
|
};
|
|
362
362
|
}
|
|
363
363
|
const response = await fetch(endpoint, {
|
package/dist/utils/tools.js
CHANGED
|
@@ -226,7 +226,12 @@ export function parseOpenAIToolCalls(toolCalls) {
|
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
228
|
// Validate required parameters for specific tools
|
|
229
|
-
|
|
229
|
+
// For write_file, we need at least a path (content can be empty string or placeholder)
|
|
230
|
+
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
|
+
}
|
|
230
235
|
continue;
|
|
231
236
|
}
|
|
232
237
|
if (toolName === 'read_file' && !parameters.path) {
|
|
@@ -252,21 +257,29 @@ function extractPartialToolParams(toolName, rawArgs) {
|
|
|
252
257
|
// For write_file, try to extract path and content
|
|
253
258
|
if (toolName === 'write_file') {
|
|
254
259
|
const pathMatch = rawArgs.match(/"path"\s*:\s*"([^"]+)"/);
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
content
|
|
268
|
-
|
|
269
|
-
|
|
260
|
+
if (pathMatch) {
|
|
261
|
+
// Try to extract content - it may be truncated
|
|
262
|
+
const contentMatch = rawArgs.match(/"content"\s*:\s*"([\s\S]*?)(?:"|$)/);
|
|
263
|
+
if (contentMatch) {
|
|
264
|
+
// Unescape the content
|
|
265
|
+
let content = contentMatch[1];
|
|
266
|
+
content = content
|
|
267
|
+
.replace(/\\n/g, '\n')
|
|
268
|
+
.replace(/\\t/g, '\t')
|
|
269
|
+
.replace(/\\r/g, '\r')
|
|
270
|
+
.replace(/\\"/g, '"')
|
|
271
|
+
.replace(/\\\\/g, '\\');
|
|
272
|
+
// If content appears truncated (doesn't end properly), add a comment
|
|
273
|
+
if (!content.endsWith('\n') && !content.endsWith('}') && !content.endsWith(';') && !content.endsWith('>')) {
|
|
274
|
+
content += '\n<!-- Content may be truncated -->\n';
|
|
275
|
+
}
|
|
276
|
+
return { path: pathMatch[1], content };
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
// Path found but content completely missing or malformed
|
|
280
|
+
// Return with empty content placeholder so the file is at least created
|
|
281
|
+
return { path: pathMatch[1], content: '<!-- Content was truncated by API -->\n' };
|
|
282
|
+
}
|
|
270
283
|
}
|
|
271
284
|
}
|
|
272
285
|
// For read_file, just need path
|
|
@@ -553,7 +566,16 @@ function tryParseToolCall(str) {
|
|
|
553
566
|
* Validate path is within project
|
|
554
567
|
*/
|
|
555
568
|
function validatePath(path, projectRoot) {
|
|
556
|
-
|
|
569
|
+
// If path is absolute but starts with projectRoot, convert to relative
|
|
570
|
+
let normalizedPath = path;
|
|
571
|
+
if (isAbsolute(path) && path.startsWith(projectRoot)) {
|
|
572
|
+
normalizedPath = relative(projectRoot, path);
|
|
573
|
+
}
|
|
574
|
+
// If still absolute and doesn't match projectRoot, reject it
|
|
575
|
+
if (isAbsolute(normalizedPath)) {
|
|
576
|
+
return { valid: false, absolutePath: normalizedPath, error: `Absolute path '${path}' not allowed. Use relative paths.` };
|
|
577
|
+
}
|
|
578
|
+
const absolutePath = resolve(projectRoot, normalizedPath);
|
|
557
579
|
const relativePath = relative(projectRoot, absolutePath);
|
|
558
580
|
if (relativePath.startsWith('..')) {
|
|
559
581
|
return { valid: false, absolutePath, error: `Path '${path}' is outside project directory` };
|
|
@@ -594,12 +616,13 @@ export function executeTool(toolCall, projectRoot) {
|
|
|
594
616
|
}
|
|
595
617
|
case 'write_file': {
|
|
596
618
|
const path = parameters.path;
|
|
597
|
-
|
|
619
|
+
let content = parameters.content;
|
|
598
620
|
if (!path) {
|
|
599
621
|
return { success: false, output: '', error: 'Missing required parameter: path', tool, parameters };
|
|
600
622
|
}
|
|
601
|
-
|
|
602
|
-
|
|
623
|
+
// Allow empty content or provide placeholder for truncated responses
|
|
624
|
+
if (content === undefined || content === null) {
|
|
625
|
+
content = '<!-- Content was not provided -->\n';
|
|
603
626
|
}
|
|
604
627
|
const validation = validatePath(path, projectRoot);
|
|
605
628
|
if (!validation.valid) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeep",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.31",
|
|
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",
|