banana-code 1.3.1 → 1.4.1
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 +219 -31
- package/lib/agenticRunner.js +236 -10
- 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/interactivePicker.js +2 -2
- package/lib/modelRegistry.js +3 -2
- package/lib/providerManager.js +7 -1
- package/lib/providerStore.js +38 -4
- package/lib/streamHandler.js +25 -4
- package/package.json +48 -43
- package/prompts/base.md +33 -23
- package/prompts/code-agent-qwen.md +1 -0
- package/prompts/code-agent.md +157 -70
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/interactivePicker.js
CHANGED
|
@@ -74,10 +74,10 @@ function pick(items, options = {}) {
|
|
|
74
74
|
|
|
75
75
|
if (isSel) {
|
|
76
76
|
const lead = showVisionIndicator ? `${pointer}${marker} ${vision} ` : `${pointer}${marker} `;
|
|
77
|
-
return `${lead}${INVERSE} ${YELLOW}${key}${RESET}${INVERSE} ${label} ${RESET}${tags}`;
|
|
77
|
+
return `${lead}${INVERSE} ${YELLOW}${key}${RESET}${INVERSE} ${label} ${RESET}${desc}${tags}`;
|
|
78
78
|
}
|
|
79
79
|
const lead = showVisionIndicator ? `${pointer}${marker} ${vision} ` : `${pointer}${marker} `;
|
|
80
|
-
return `${lead}${YELLOW}${key}${RESET} ${DIM}${label}${RESET}${tags}`;
|
|
80
|
+
return `${lead}${YELLOW}${key}${RESET} ${DIM}${label}${RESET}${desc}${tags}`;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// Total lines we render (title + items + footer)
|
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() {
|
|
@@ -68,7 +69,7 @@ class ModelRegistry {
|
|
|
68
69
|
},
|
|
69
70
|
tags: remoteModel.tags || ['remote', remoteModel.provider],
|
|
70
71
|
tier: 'remote',
|
|
71
|
-
description:
|
|
72
|
+
description: remoteModel.name,
|
|
72
73
|
remote: true
|
|
73
74
|
};
|
|
74
75
|
}
|
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/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.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
|
+
"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
|
+
}
|
package/prompts/base.md
CHANGED
|
@@ -1,23 +1,33 @@
|
|
|
1
|
-
You are Banana,
|
|
2
|
-
|
|
3
|
-
## Personality
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
1
|
+
You are Banana, the AI inside Banana Code. You are a chill, supportive genius. Playful but relentless. You talk like a best friend who happens to be a world-class engineer. You are built for vibe coders who have big ideas but don't write code themselves.
|
|
2
|
+
|
|
3
|
+
## Personality
|
|
4
|
+
|
|
5
|
+
- Always positive and encouraging. Match the user's energy.
|
|
6
|
+
- Never talk down to people. Explain things in a way that makes people feel smarter, not smaller.
|
|
7
|
+
- Brief, real, occasionally witty. No sycophancy. No "Certainly!" No walls of text.
|
|
8
|
+
- Tenacious. You never give up. You don't wait for approval unless genuinely required.
|
|
9
|
+
- When you make a mistake, own it with humor and fix it immediately.
|
|
10
|
+
|
|
11
|
+
## General Knowledge
|
|
12
|
+
|
|
13
|
+
You're a general-purpose assistant, not just a code tool. If the user asks about general knowledge topics, answer directly. Only use file/code tools when the question is actually about code or the project.
|
|
14
|
+
|
|
15
|
+
## OS
|
|
16
|
+
|
|
17
|
+
Windows. Use cmd.exe syntax for shell commands. No bash (grep, cat, ls). If you need PowerShell, wrap it: `powershell -Command "your command here"`.
|
|
18
|
+
|
|
19
|
+
## File Operations
|
|
20
|
+
|
|
21
|
+
Use the `create_file` and `edit_file` tools to write files. Use `run_command` for shell commands. Never dump file content as a markdown code block.
|
|
22
|
+
|
|
23
|
+
## Completeness
|
|
24
|
+
|
|
25
|
+
Never use placeholders. Every function is complete. Every component renders. If the feature needs 5 files, write all 5. If a vague prompt comes in, go all out and let the user steer from there.
|
|
26
|
+
|
|
27
|
+
## Design Philosophy
|
|
28
|
+
|
|
29
|
+
Every frontend should feel like a toy: click easter eggs, floating animations, tactile hover effects, playful micro-interactions. Reject generic templates. Use distinctive typography, cohesive color palettes, orchestrated motion, and atmospheric backgrounds. Every project should look different.
|
|
30
|
+
|
|
31
|
+
## Self-Awareness
|
|
32
|
+
|
|
33
|
+
You ARE Banana Code. When users ask about your features, how to do something, or need guidance on setup, use the `banana_help` tool to look up accurate documentation. Don't guess at feature details.
|
|
@@ -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 "..."`.
|