banana-code 1.3.0 → 1.4.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/banana.js +85 -11
- package/lib/agenticRunner.js +240 -14
- package/lib/claudeCodeProvider.js +540 -0
- package/lib/config.js +49 -15
- package/lib/contextBuilder.js +11 -4
- package/lib/fileManager.js +9 -11
- package/lib/fsUtils.js +30 -0
- package/lib/historyManager.js +3 -5
- package/lib/modelRegistry.js +2 -1
- package/lib/providerManager.js +7 -1
- package/lib/providerStore.js +38 -4
- package/lib/streamHandler.js +25 -4
- package/lib/subAgentManager.js +1 -1
- package/package.json +48 -43
- package/prompts/code-agent-qwen.md +1 -0
- package/prompts/code-agent.md +1 -0
package/lib/contextBuilder.js
CHANGED
|
@@ -40,10 +40,15 @@ class ContextBuilder {
|
|
|
40
40
|
constructor(fileManager, ignorePatterns = []) {
|
|
41
41
|
this.fileManager = fileManager;
|
|
42
42
|
this.projectDir = fileManager.projectDir;
|
|
43
|
-
this.customIgnores = ignorePatterns;
|
|
43
|
+
this.customIgnores = [...ignorePatterns];
|
|
44
|
+
this.activeIgnores = [...this.customIgnores];
|
|
44
45
|
this.loadedFiles = new Map(); // path -> content
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
refreshIgnorePatterns() {
|
|
49
|
+
this.activeIgnores = [...new Set([...this.customIgnores, ...this.loadGitignore()])];
|
|
50
|
+
}
|
|
51
|
+
|
|
47
52
|
/**
|
|
48
53
|
* Load .gitignore patterns
|
|
49
54
|
*/
|
|
@@ -79,7 +84,7 @@ class ContextBuilder {
|
|
|
79
84
|
if (IGNORED_FILES.includes(basename)) return true;
|
|
80
85
|
|
|
81
86
|
// Check custom patterns (simple matching)
|
|
82
|
-
for (const pattern of this.
|
|
87
|
+
for (const pattern of this.activeIgnores) {
|
|
83
88
|
if (pattern.endsWith('/') || pattern.endsWith('\\')) {
|
|
84
89
|
// Directory pattern
|
|
85
90
|
const dirName = pattern.slice(0, -1);
|
|
@@ -105,6 +110,9 @@ class ContextBuilder {
|
|
|
105
110
|
* @param {object} counter - Shared counter across recursive calls
|
|
106
111
|
*/
|
|
107
112
|
scanDirectory(dir = this.projectDir, depth = 0, maxDepth = 6, maxFiles = 5000, counter = { count: 0 }) {
|
|
113
|
+
if (depth === 0) {
|
|
114
|
+
this.refreshIgnorePatterns();
|
|
115
|
+
}
|
|
108
116
|
if (depth > maxDepth) return [];
|
|
109
117
|
if (counter.count >= maxFiles) return [];
|
|
110
118
|
|
|
@@ -262,8 +270,7 @@ class ContextBuilder {
|
|
|
262
270
|
* Build the full context string for AI
|
|
263
271
|
*/
|
|
264
272
|
buildContext() {
|
|
265
|
-
|
|
266
|
-
this.customIgnores = [...this.customIgnores, ...this.loadGitignore()];
|
|
273
|
+
this.refreshIgnorePatterns();
|
|
267
274
|
|
|
268
275
|
// Load priority files first
|
|
269
276
|
this.loadPriorityFiles();
|
package/lib/fileManager.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
|
+
const { ensureDirSync, atomicWriteFileSync } = require('./fsUtils');
|
|
7
8
|
|
|
8
9
|
// File extensions to always skip
|
|
9
10
|
const BINARY_EXTENSIONS = [
|
|
@@ -29,9 +30,7 @@ class FileManager {
|
|
|
29
30
|
* Ensure backup directory exists
|
|
30
31
|
*/
|
|
31
32
|
ensureBackupDir() {
|
|
32
|
-
|
|
33
|
-
fs.mkdirSync(this.backupDir, { recursive: true });
|
|
34
|
-
}
|
|
33
|
+
ensureDirSync(this.backupDir);
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
/**
|
|
@@ -85,8 +84,9 @@ class FileManager {
|
|
|
85
84
|
: path.join(this.projectDir, filePath);
|
|
86
85
|
|
|
87
86
|
try {
|
|
87
|
+
const existed = fs.existsSync(fullPath);
|
|
88
88
|
// Create backup if file exists
|
|
89
|
-
if (
|
|
89
|
+
if (existed) {
|
|
90
90
|
this.ensureBackupDir();
|
|
91
91
|
const timestamp = Date.now();
|
|
92
92
|
const relativePath = path.relative(this.projectDir, fullPath);
|
|
@@ -97,15 +97,12 @@ class FileManager {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
// Ensure directory exists
|
|
100
|
-
|
|
101
|
-
if (!fs.existsSync(dir)) {
|
|
102
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
103
|
-
}
|
|
100
|
+
ensureDirSync(path.dirname(fullPath));
|
|
104
101
|
|
|
105
102
|
// Write the file
|
|
106
|
-
|
|
103
|
+
atomicWriteFileSync(fullPath, content);
|
|
107
104
|
|
|
108
|
-
return { success: true, path: fullPath, isNew: !
|
|
105
|
+
return { success: true, path: fullPath, isNew: !existed };
|
|
109
106
|
} catch (error) {
|
|
110
107
|
return { success: false, error: error.message };
|
|
111
108
|
}
|
|
@@ -187,8 +184,9 @@ class FileManager {
|
|
|
187
184
|
const targetPath = path.isAbsolute(filePath)
|
|
188
185
|
? filePath
|
|
189
186
|
: path.join(this.projectDir, filePath);
|
|
187
|
+
ensureDirSync(path.dirname(targetPath));
|
|
190
188
|
|
|
191
|
-
|
|
189
|
+
atomicWriteFileSync(targetPath, content);
|
|
192
190
|
|
|
193
191
|
return { success: true, restored: latestBackup.name };
|
|
194
192
|
} catch (error) {
|
package/lib/fsUtils.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
function ensureDirSync(dirPath) {
|
|
6
|
+
if (!fs.existsSync(dirPath)) {
|
|
7
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function atomicWriteFileSync(targetPath, content, encoding = 'utf-8') {
|
|
12
|
+
const dir = path.dirname(targetPath);
|
|
13
|
+
ensureDirSync(dir);
|
|
14
|
+
const tempPath = path.join(
|
|
15
|
+
dir,
|
|
16
|
+
`.${path.basename(targetPath)}.${process.pid}.${Date.now()}.${crypto.randomBytes(4).toString('hex')}.tmp`
|
|
17
|
+
);
|
|
18
|
+
fs.writeFileSync(tempPath, content, encoding);
|
|
19
|
+
fs.renameSync(tempPath, targetPath);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function atomicWriteJsonSync(targetPath, value) {
|
|
23
|
+
atomicWriteFileSync(targetPath, JSON.stringify(value, null, 2));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
ensureDirSync,
|
|
28
|
+
atomicWriteFileSync,
|
|
29
|
+
atomicWriteJsonSync
|
|
30
|
+
};
|
package/lib/historyManager.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
|
+
const { ensureDirSync, atomicWriteFileSync } = require('./fsUtils');
|
|
7
8
|
|
|
8
9
|
class HistoryManager {
|
|
9
10
|
constructor(bananaDir, maxHistory = 100) {
|
|
@@ -44,11 +45,8 @@ class HistoryManager {
|
|
|
44
45
|
|
|
45
46
|
save() {
|
|
46
47
|
try {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
50
|
-
}
|
|
51
|
-
fs.writeFileSync(this.historyFile, this.history.join('\n'));
|
|
48
|
+
ensureDirSync(path.dirname(this.historyFile));
|
|
49
|
+
atomicWriteFileSync(this.historyFile, this.history.join('\n'));
|
|
52
50
|
} catch {
|
|
53
51
|
// Ignore save errors
|
|
54
52
|
}
|
package/lib/modelRegistry.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
7
|
+
const { atomicWriteJsonSync } = require('./fsUtils');
|
|
7
8
|
|
|
8
9
|
class ModelRegistry {
|
|
9
10
|
constructor(registryPath, lmStudio, providerStore = null) {
|
|
@@ -34,7 +35,7 @@ class ModelRegistry {
|
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
save() {
|
|
37
|
-
|
|
38
|
+
atomicWriteJsonSync(this.registryPath, this.localRegistry);
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
_normalizeLocalModels() {
|
package/lib/providerManager.js
CHANGED
|
@@ -18,7 +18,8 @@ const PROVIDER_LABELS = {
|
|
|
18
18
|
anthropic: 'Anthropic',
|
|
19
19
|
openai: 'OpenAI',
|
|
20
20
|
openrouter: 'OpenRouter',
|
|
21
|
-
monkey: 'Monkey Models'
|
|
21
|
+
monkey: 'Monkey Models',
|
|
22
|
+
'claude-code': 'Claude Code'
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
class ProviderManager {
|
|
@@ -115,6 +116,11 @@ class ProviderManager {
|
|
|
115
116
|
});
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
if (provider === 'claude-code') {
|
|
120
|
+
const { ClaudeCodeClient } = require('./claudeCodeProvider');
|
|
121
|
+
return new ClaudeCodeClient();
|
|
122
|
+
}
|
|
123
|
+
|
|
118
124
|
throw new Error(`Unknown provider: ${provider}`);
|
|
119
125
|
}
|
|
120
126
|
|
package/lib/providerStore.js
CHANGED
|
@@ -4,7 +4,7 @@ const path = require('path');
|
|
|
4
4
|
|
|
5
5
|
const DEFAULT_PATH = path.join(os.homedir(), '.banana', 'providers.json');
|
|
6
6
|
|
|
7
|
-
const PROVIDERS = ['monkey', 'anthropic', 'openai', 'openrouter'];
|
|
7
|
+
const PROVIDERS = ['monkey', 'anthropic', 'openai', 'openrouter', 'claude-code'];
|
|
8
8
|
|
|
9
9
|
const DEFAULT_PROVIDER_MODELS = {
|
|
10
10
|
monkey: {},
|
|
@@ -42,6 +42,29 @@ const DEFAULT_PROVIDER_MODELS = {
|
|
|
42
42
|
prompt: 'code-agent'
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
|
+
'claude-code': {
|
|
46
|
+
'opus': {
|
|
47
|
+
name: 'Claude Opus (Claude Code)',
|
|
48
|
+
id: 'opus',
|
|
49
|
+
contextLimit: 200000,
|
|
50
|
+
supportsThinking: false,
|
|
51
|
+
prompt: 'code-agent'
|
|
52
|
+
},
|
|
53
|
+
'sonnet': {
|
|
54
|
+
name: 'Claude Sonnet (Claude Code)',
|
|
55
|
+
id: 'sonnet',
|
|
56
|
+
contextLimit: 200000,
|
|
57
|
+
supportsThinking: false,
|
|
58
|
+
prompt: 'code-agent'
|
|
59
|
+
},
|
|
60
|
+
'haiku': {
|
|
61
|
+
name: 'Claude Haiku (Claude Code)',
|
|
62
|
+
id: 'haiku',
|
|
63
|
+
contextLimit: 200000,
|
|
64
|
+
supportsThinking: false,
|
|
65
|
+
prompt: 'code-agent'
|
|
66
|
+
}
|
|
67
|
+
},
|
|
45
68
|
openrouter: {
|
|
46
69
|
'claude-opus-4.6': {
|
|
47
70
|
name: 'Claude Opus 4.6 (OpenRouter)',
|
|
@@ -91,7 +114,8 @@ function createDefaultData() {
|
|
|
91
114
|
providers: {
|
|
92
115
|
anthropic: defaultProviderRecord('anthropic'),
|
|
93
116
|
openai: defaultProviderRecord('openai'),
|
|
94
|
-
openrouter: defaultProviderRecord('openrouter')
|
|
117
|
+
openrouter: defaultProviderRecord('openrouter'),
|
|
118
|
+
'claude-code': defaultProviderRecord('claude-code')
|
|
95
119
|
}
|
|
96
120
|
};
|
|
97
121
|
}
|
|
@@ -328,6 +352,16 @@ class ProviderStore {
|
|
|
328
352
|
this.save();
|
|
329
353
|
}
|
|
330
354
|
|
|
355
|
+
/**
|
|
356
|
+
* Connect Claude Code provider (no API key needed, uses CLI auth).
|
|
357
|
+
*/
|
|
358
|
+
connectClaudeCode() {
|
|
359
|
+
this._touch('claude-code');
|
|
360
|
+
this.data.providers['claude-code'].connected = true;
|
|
361
|
+
this.data.providers['claude-code'].auth = { type: 'cli' };
|
|
362
|
+
this.save();
|
|
363
|
+
}
|
|
364
|
+
|
|
331
365
|
connectOpenAI(auth) {
|
|
332
366
|
this._touch('openai');
|
|
333
367
|
this.data.providers.openai.connected = true;
|
|
@@ -396,8 +430,8 @@ class ProviderStore {
|
|
|
396
430
|
contextLimit: model.contextLimit || 128000,
|
|
397
431
|
supportsThinking: model.supportsThinking !== false,
|
|
398
432
|
prompt: model.prompt || 'code-agent',
|
|
399
|
-
tags: provider === 'openrouter'
|
|
400
|
-
? ['remote', provider
|
|
433
|
+
tags: (provider === 'openrouter' || provider === 'claude-code')
|
|
434
|
+
? ['remote', provider]
|
|
401
435
|
: ['remote', provider, 'tool-calling', 'vision']
|
|
402
436
|
});
|
|
403
437
|
}
|
package/lib/streamHandler.js
CHANGED
|
@@ -36,6 +36,9 @@ class StreamHandler {
|
|
|
36
36
|
// Buffer output until we confirm there's no orphan </think> coming
|
|
37
37
|
this.orphanBuffer = '';
|
|
38
38
|
this.orphanCheckComplete = false; // Once true, stop buffering for orphan check
|
|
39
|
+
this.completionStatus = 'completed';
|
|
40
|
+
this.completionWarning = null;
|
|
41
|
+
this.doneSignalReceived = false;
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
/**
|
|
@@ -176,7 +179,7 @@ class StreamHandler {
|
|
|
176
179
|
async handleStream(response) {
|
|
177
180
|
const reader = response.body.getReader();
|
|
178
181
|
const decoder = new TextDecoder();
|
|
179
|
-
const IDLE_TIMEOUT = 30000;
|
|
182
|
+
const IDLE_TIMEOUT = 30000;
|
|
180
183
|
|
|
181
184
|
try {
|
|
182
185
|
while (true) {
|
|
@@ -193,7 +196,8 @@ class StreamHandler {
|
|
|
193
196
|
value = result.value;
|
|
194
197
|
} catch (e) {
|
|
195
198
|
if (e.message === 'IDLE_TIMEOUT') {
|
|
196
|
-
|
|
199
|
+
this.completionStatus = 'incomplete_stream';
|
|
200
|
+
this.completionWarning = `Stream stalled for ${IDLE_TIMEOUT}ms before an explicit completion signal.`;
|
|
197
201
|
break;
|
|
198
202
|
}
|
|
199
203
|
throw e;
|
|
@@ -218,7 +222,9 @@ class StreamHandler {
|
|
|
218
222
|
|
|
219
223
|
if (data === '[DONE]') {
|
|
220
224
|
// Stream complete signal - exit the loop
|
|
221
|
-
this.
|
|
225
|
+
this.doneSignalReceived = true;
|
|
226
|
+
this.completionStatus = 'completed';
|
|
227
|
+
this.onComplete(this.fullResponse, this.getResult());
|
|
222
228
|
return this.fullResponse;
|
|
223
229
|
}
|
|
224
230
|
|
|
@@ -251,6 +257,11 @@ class StreamHandler {
|
|
|
251
257
|
}
|
|
252
258
|
}
|
|
253
259
|
|
|
260
|
+
if (!this.doneSignalReceived && this.completionStatus === 'completed') {
|
|
261
|
+
this.completionStatus = 'incomplete_stream';
|
|
262
|
+
this.completionWarning = 'Stream ended without an explicit completion signal.';
|
|
263
|
+
}
|
|
264
|
+
|
|
254
265
|
// Process any remaining buffer
|
|
255
266
|
if (this.buffer) {
|
|
256
267
|
const lines = this.buffer.split('\n');
|
|
@@ -280,7 +291,7 @@ class StreamHandler {
|
|
|
280
291
|
}
|
|
281
292
|
}
|
|
282
293
|
|
|
283
|
-
this.onComplete(this.fullResponse);
|
|
294
|
+
this.onComplete(this.fullResponse, this.getResult());
|
|
284
295
|
return this.fullResponse;
|
|
285
296
|
|
|
286
297
|
} catch (error) {
|
|
@@ -292,6 +303,16 @@ class StreamHandler {
|
|
|
292
303
|
getFullResponse() {
|
|
293
304
|
return this.fullResponse;
|
|
294
305
|
}
|
|
306
|
+
|
|
307
|
+
getResult() {
|
|
308
|
+
return {
|
|
309
|
+
content: this.fullResponse,
|
|
310
|
+
completed: this.completionStatus === 'completed',
|
|
311
|
+
status: this.completionStatus,
|
|
312
|
+
warning: this.completionWarning,
|
|
313
|
+
doneSignalReceived: this.doneSignalReceived
|
|
314
|
+
};
|
|
315
|
+
}
|
|
295
316
|
}
|
|
296
317
|
|
|
297
318
|
// Non-streaming fallback with simulated streaming effect
|
package/lib/subAgentManager.js
CHANGED
|
@@ -8,7 +8,7 @@ const { AgenticRunner, TOOLS, READ_ONLY_TOOLS } = require('./agenticRunner');
|
|
|
8
8
|
const crypto = require('crypto');
|
|
9
9
|
|
|
10
10
|
const MAX_DEPTH = 2;
|
|
11
|
-
const DEFAULT_MAX_ITERATIONS_SUBAGENT =
|
|
11
|
+
const DEFAULT_MAX_ITERATIONS_SUBAGENT = 40;
|
|
12
12
|
const DEFAULT_TIMEOUT_CLOUD_MS = 120_000;
|
|
13
13
|
const DEFAULT_TIMEOUT_LOCAL_MS = 300_000;
|
|
14
14
|
const DEFAULT_MAX_TOKENS_SUBAGENT = 16384;
|
package/package.json
CHANGED
|
@@ -1,43 +1,48 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "banana-code",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "AI coding agent CLI powered by Monkey Models and remote providers (Anthropic, OpenAI, OpenRouter)",
|
|
5
|
-
"main": "banana.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"banana": "banana.js"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"banana.js",
|
|
11
|
-
"lib/",
|
|
12
|
-
"prompts/",
|
|
13
|
-
"models.json",
|
|
14
|
-
"package.json"
|
|
15
|
-
],
|
|
16
|
-
"scripts": {
|
|
17
|
-
"postinstall": "node scripts/createBananaDir.js || echo banana: skipped dir creation"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
}
|
|
43
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "banana-code",
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "AI coding agent CLI powered by Monkey Models and remote providers (Anthropic, OpenAI, OpenRouter)",
|
|
5
|
+
"main": "banana.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"banana": "banana.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"banana.js",
|
|
11
|
+
"lib/",
|
|
12
|
+
"prompts/",
|
|
13
|
+
"models.json",
|
|
14
|
+
"package.json"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"postinstall": "node scripts/createBananaDir.js || echo banana: skipped dir creation",
|
|
18
|
+
"test:tool-batch": "node scripts/regression-tool-batch-sanitizer.js",
|
|
19
|
+
"test:command-verification": "node scripts/regression-command-verification.js",
|
|
20
|
+
"test:stream-handler": "node scripts/regression-stream-handler.js",
|
|
21
|
+
"test:persistence-context": "node scripts/regression-persistence-context.js",
|
|
22
|
+
"test:reliability": "npm run test:tool-batch && npm run test:command-verification && npm run test:stream-handler && npm run test:persistence-context"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"ai",
|
|
26
|
+
"coding",
|
|
27
|
+
"agent",
|
|
28
|
+
"cli",
|
|
29
|
+
"monkey-models",
|
|
30
|
+
"banana-code",
|
|
31
|
+
"code-assistant"
|
|
32
|
+
],
|
|
33
|
+
"author": "Matt Johnston",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/mrchevyceleb/banana-code.git"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://github.com/mrchevyceleb/banana-code",
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"diff": "^8.0.3",
|
|
45
|
+
"glob": "^13.0.6",
|
|
46
|
+
"update-notifier": "^7.3.1"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
- Always provide complete, valid JSON in tool call arguments. Never use partial or malformed JSON.
|
|
6
6
|
- When calling tools, provide ALL required arguments. Missing arguments cause failures.
|
|
7
7
|
- After receiving a tool result, analyze it before deciding your next step.
|
|
8
|
+
- If a command changes git state or the filesystem, verify the effect with a separate read-only check before you tell the user it is done.
|
|
8
9
|
- Do not call the same tool with the same arguments twice in a row.
|
|
9
10
|
- If a tool returns an error, try a different approach or explain the issue.
|
|
10
11
|
- `run_command` uses cmd.exe on Windows, NOT PowerShell. Use cmd syntax (findstr, type, dir). For PowerShell, wrap it: `powershell -Command "..."`.
|
package/prompts/code-agent.md
CHANGED
|
@@ -20,6 +20,7 @@ For simple conversational messages (greetings, questions about general knowledge
|
|
|
20
20
|
- **Act fast. Do not over-research.** Read only what you need to start writing code. For most tasks, a few reads are enough. Start writing when you have enough context, not when you have complete context. If you have been reading files for several iterations without making any changes, you are probably over-reading.
|
|
21
21
|
- Use `create_file` to write files and `edit_file` to modify existing ones. Never put file content in your text response.
|
|
22
22
|
- Use `run_command` for shell commands (cmd.exe syntax on Windows; for PowerShell wrap with `powershell -Command "..."`).
|
|
23
|
+
- If you use `run_command` to change git state, install things, or modify the filesystem, do not claim success until you verify it with a separate read-only check.
|
|
23
24
|
- Use `ask_human` when you need clarification, a decision, or confirmation from the user before proceeding. Don't guess when you can ask.
|
|
24
25
|
- Only call `read_file` or `list_files` if you genuinely need to see existing code before writing.
|
|
25
26
|
- `call_mcp` is a GENERIC WRAPPER that can call ANY MCP tool by name. Pass `{"tool":"tool_name","args":{...}}`.
|