sam-coder-cli 1.0.69 → 2.0.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/bin/agi-cli.js +287 -95
- package/bin/agi-cli.js.bak +352 -0
- package/bin/core/brainstorm.js +198 -0
- package/bin/core/edit_finished_brainstorm.js +294 -0
- package/bin/core/finish_brainstorm.js +217 -0
- package/bin/core/index.js +37 -0
- package/bin/core/models.js +290 -0
- package/bin/core/templates.js +567 -0
- package/package.json +14 -4
- package/ANIMATION_ENHANCEMENTS.md +0 -86
- package/media/ai-icon.png +0 -0
- package/media/ai-icon.svg +0 -5
- package/media/infinity-icon.svg +0 -4
package/bin/agi-cli.js
CHANGED
|
@@ -12,6 +12,9 @@ const execAsync = util.promisify(exec);
|
|
|
12
12
|
// Import AGI Animation module
|
|
13
13
|
const { runAGIAnimation } = require('./agi-animation.js');
|
|
14
14
|
|
|
15
|
+
// Import Brainstorm Core module
|
|
16
|
+
const brainstormCore = require('./core');
|
|
17
|
+
|
|
15
18
|
// Configuration
|
|
16
19
|
const CONFIG_PATH = path.join(os.homedir(), '.sam-coder-config.json');
|
|
17
20
|
let OPENROUTER_API_KEY;
|
|
@@ -67,43 +70,51 @@ const tools = [
|
|
|
67
70
|
items: {
|
|
68
71
|
type: 'object',
|
|
69
72
|
properties: {
|
|
70
|
-
type: {
|
|
71
|
-
type: 'string',
|
|
72
|
-
enum: ['replace', 'insert', 'delete'],
|
|
73
|
+
type: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
enum: ['replace', 'insert', 'delete', 'search_replace'],
|
|
73
76
|
description: 'Type of edit operation'
|
|
74
77
|
},
|
|
75
|
-
|
|
76
|
-
type: '
|
|
77
|
-
description: '
|
|
78
|
+
old_string: {
|
|
79
|
+
type: 'string',
|
|
80
|
+
description: 'Exact string to search for and replace (for search_replace operations)'
|
|
81
|
+
},
|
|
82
|
+
new_string: {
|
|
83
|
+
type: 'string',
|
|
84
|
+
description: 'String to replace old_string with (for search_replace operations)'
|
|
78
85
|
},
|
|
79
|
-
|
|
80
|
-
type: 'number',
|
|
81
|
-
description: '
|
|
86
|
+
startLine: {
|
|
87
|
+
type: 'number',
|
|
88
|
+
description: 'Starting line number (1-based)'
|
|
82
89
|
},
|
|
83
|
-
|
|
84
|
-
type: '
|
|
85
|
-
description: '
|
|
90
|
+
endLine: {
|
|
91
|
+
type: 'number',
|
|
92
|
+
description: 'Ending line number (1-based, inclusive)'
|
|
86
93
|
},
|
|
87
|
-
|
|
88
|
-
type: 'string',
|
|
89
|
-
description: '
|
|
94
|
+
newText: {
|
|
95
|
+
type: 'string',
|
|
96
|
+
description: 'New text to insert or replace with'
|
|
90
97
|
},
|
|
91
|
-
|
|
92
|
-
type: 'string',
|
|
93
|
-
description: '
|
|
98
|
+
pattern: {
|
|
99
|
+
type: 'string',
|
|
100
|
+
description: 'Pattern to search for (for replace operations)'
|
|
94
101
|
},
|
|
95
|
-
|
|
96
|
-
type: 'string',
|
|
97
|
-
description: '
|
|
102
|
+
replacement: {
|
|
103
|
+
type: 'string',
|
|
104
|
+
description: 'Replacement text (for pattern-based replace)'
|
|
98
105
|
},
|
|
99
|
-
|
|
100
|
-
type: 'string',
|
|
106
|
+
flags: {
|
|
107
|
+
type: 'string',
|
|
108
|
+
description: 'Regex flags (e.g., "g" for global)'
|
|
109
|
+
},
|
|
110
|
+
position: {
|
|
111
|
+
type: 'string',
|
|
101
112
|
enum: ['start', 'end'],
|
|
102
|
-
description: 'Where to insert (only for insert operations)'
|
|
113
|
+
description: 'Where to insert (only for insert operations)'
|
|
103
114
|
},
|
|
104
|
-
line: {
|
|
105
|
-
type: 'number',
|
|
106
|
-
description: 'Line number to insert at (for line-based insert)'
|
|
115
|
+
line: {
|
|
116
|
+
type: 'number',
|
|
117
|
+
description: 'Line number to insert at (for line-based insert)'
|
|
107
118
|
}
|
|
108
119
|
},
|
|
109
120
|
required: ['type'],
|
|
@@ -149,6 +160,14 @@ const tools = [
|
|
|
149
160
|
endLine: { type: 'number' }
|
|
150
161
|
},
|
|
151
162
|
required: ['startLine', 'endLine']
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
properties: {
|
|
166
|
+
type: { const: 'search_replace' },
|
|
167
|
+
old_string: { type: 'string' },
|
|
168
|
+
new_string: { type: 'string' }
|
|
169
|
+
},
|
|
170
|
+
required: ['old_string', 'new_string']
|
|
152
171
|
}
|
|
153
172
|
]
|
|
154
173
|
}
|
|
@@ -214,6 +233,48 @@ INSTRUCTIONS:
|
|
|
214
233
|
|
|
215
234
|
Always think step by step and explain your reasoning before taking actions that could affect the system.`;
|
|
216
235
|
|
|
236
|
+
// System prompt for the AI Assistant when using Engineer Mode
|
|
237
|
+
const ENGINEER_PROMPT = `You are a Senior Software Engineer with 15+ years of experience. You have deep expertise in:
|
|
238
|
+
- Software architecture and design patterns
|
|
239
|
+
- Clean code principles and best practices
|
|
240
|
+
- Test-driven development
|
|
241
|
+
- Performance optimization
|
|
242
|
+
- Security best practices
|
|
243
|
+
- Code review and mentoring
|
|
244
|
+
|
|
245
|
+
TOOLS AVAILABLE:
|
|
246
|
+
1. readFile - Read the contents of a file
|
|
247
|
+
2. writeFile - Write content to a file
|
|
248
|
+
3. editFile - Edit specific parts of a file (use search_replace with old_string/new_string for precise edits)
|
|
249
|
+
4. runCommand - Execute a shell command
|
|
250
|
+
5. searchFiles - Search for files using a glob pattern
|
|
251
|
+
|
|
252
|
+
ENGINEER PRINCIPLES:
|
|
253
|
+
1. **Code Quality First**: Write clean, maintainable, well-documented code
|
|
254
|
+
2. **Think Before Acting**: Analyze the problem thoroughly before making changes
|
|
255
|
+
3. **Small, Focused Changes**: Make incremental changes that are easy to review
|
|
256
|
+
4. **Test Your Work**: Verify changes work as expected before moving on
|
|
257
|
+
5. **Explain Your Reasoning**: Document why you made certain technical decisions
|
|
258
|
+
|
|
259
|
+
WHEN EDITING FILES:
|
|
260
|
+
- ALWAYS use editFile with search_replace operations
|
|
261
|
+
- Use { "type": "search_replace", "old_string": "exact text to find", "new_string": "replacement text" }
|
|
262
|
+
- The old_string must match EXACTLY including whitespace
|
|
263
|
+
- Make focused, minimal changes
|
|
264
|
+
|
|
265
|
+
WORKFLOW:
|
|
266
|
+
1. Read and understand the existing code
|
|
267
|
+
2. Identify the minimal changes needed
|
|
268
|
+
3. Make changes using precise search_replace operations
|
|
269
|
+
4. Verify the changes compile/run correctly
|
|
270
|
+
5. Summarize what was done
|
|
271
|
+
|
|
272
|
+
ENVIRONMENT:
|
|
273
|
+
- OS: ${process.platform}
|
|
274
|
+
- Current directory: ${process.cwd()}
|
|
275
|
+
|
|
276
|
+
You are autonomous - continue working until the task is complete. Use the 'stop' action when finished.`;
|
|
277
|
+
|
|
217
278
|
// System prompt for the AI Assistant when using legacy function calling (JSON actions)
|
|
218
279
|
const FUNCTION_CALLING_PROMPT = `You are an autonomous AI agent with advanced problem-solving capabilities. You operate through strategic action sequences to accomplish complex tasks on the user's system. Think like an expert developer and system administrator combined.
|
|
219
280
|
|
|
@@ -455,7 +516,7 @@ const agentUtils = {
|
|
|
455
516
|
// Read the current file content
|
|
456
517
|
let content = await fs.readFile(targetPath, 'utf-8');
|
|
457
518
|
const lines = content.split('\n');
|
|
458
|
-
|
|
519
|
+
|
|
459
520
|
// Process each edit operation
|
|
460
521
|
for (const op of edits.operations) {
|
|
461
522
|
switch (op.type) {
|
|
@@ -478,7 +539,7 @@ const agentUtils = {
|
|
|
478
539
|
lines.push(...content.split('\n'));
|
|
479
540
|
}
|
|
480
541
|
break;
|
|
481
|
-
|
|
542
|
+
|
|
482
543
|
case 'insert':
|
|
483
544
|
if (op.position === 'start') {
|
|
484
545
|
lines.unshift(...op.text.split('\n'));
|
|
@@ -492,23 +553,44 @@ const agentUtils = {
|
|
|
492
553
|
lines.splice(op.line - 1, 0, ...insertLines);
|
|
493
554
|
}
|
|
494
555
|
break;
|
|
495
|
-
|
|
556
|
+
|
|
496
557
|
case 'delete':
|
|
497
558
|
if (op.startLine < 1 || op.endLine > lines.length) {
|
|
498
559
|
throw new Error(`Line numbers out of range (1-${lines.length})`);
|
|
499
560
|
}
|
|
500
561
|
lines.splice(op.startLine - 1, op.endLine - op.startLine + 1);
|
|
501
562
|
break;
|
|
502
|
-
|
|
563
|
+
|
|
564
|
+
case 'search_replace':
|
|
565
|
+
// Claude Code style: exact string search and replace
|
|
566
|
+
if (op.old_string === undefined || op.new_string === undefined) {
|
|
567
|
+
throw new Error('search_replace requires old_string and new_string');
|
|
568
|
+
}
|
|
569
|
+
content = lines.join('\n');
|
|
570
|
+
if (!content.includes(op.old_string)) {
|
|
571
|
+
throw new Error(`Could not find exact match for old_string: "${op.old_string.substring(0, 50)}${op.old_string.length > 50 ? '...' : ''}"`);
|
|
572
|
+
}
|
|
573
|
+
// Count occurrences
|
|
574
|
+
const occurrences = content.split(op.old_string).length - 1;
|
|
575
|
+
if (occurrences > 1) {
|
|
576
|
+
console.log(`Warning: Found ${occurrences} occurrences of old_string. Replacing first occurrence only.`);
|
|
577
|
+
}
|
|
578
|
+
// Replace first occurrence (to be safe and predictable like Claude Code)
|
|
579
|
+
content = content.replace(op.old_string, op.new_string);
|
|
580
|
+
// Update lines array
|
|
581
|
+
lines.length = 0;
|
|
582
|
+
lines.push(...content.split('\n'));
|
|
583
|
+
break;
|
|
584
|
+
|
|
503
585
|
default:
|
|
504
586
|
throw new Error(`Unknown operation type: ${op.type}`);
|
|
505
587
|
}
|
|
506
588
|
}
|
|
507
|
-
|
|
589
|
+
|
|
508
590
|
// Write the modified content back to the file
|
|
509
591
|
await fs.writeFile(targetPath, lines.join('\n'), 'utf-8');
|
|
510
592
|
return `Successfully edited ${targetPath}`;
|
|
511
|
-
|
|
593
|
+
|
|
512
594
|
} catch (error) {
|
|
513
595
|
throw new Error(`Failed to edit file ${typeof inputPathOrObj === 'string' ? inputPathOrObj : inputPathOrObj?.path}: ${error.message}`);
|
|
514
596
|
}
|
|
@@ -661,7 +743,7 @@ function extractJsonFromMarkdown(text) {
|
|
|
661
743
|
// Try to find a markdown code block with JSON content (case insensitive)
|
|
662
744
|
const codeBlockRegex = /```(?:json|JSON)\s*([\s\S]*?)\s*```/i;
|
|
663
745
|
const match = text.match(codeBlockRegex);
|
|
664
|
-
|
|
746
|
+
|
|
665
747
|
if (match && match[1]) {
|
|
666
748
|
try {
|
|
667
749
|
const jsonStr = match[1].trim();
|
|
@@ -673,7 +755,7 @@ function extractJsonFromMarkdown(text) {
|
|
|
673
755
|
// ignore
|
|
674
756
|
}
|
|
675
757
|
}
|
|
676
|
-
|
|
758
|
+
|
|
677
759
|
// If no code block found, look for JSON-like patterns in the text
|
|
678
760
|
const jsonPatterns = [
|
|
679
761
|
// Look for objects that start with { and end with }
|
|
@@ -681,7 +763,7 @@ function extractJsonFromMarkdown(text) {
|
|
|
681
763
|
// Look for arrays that start with [ and end with ]
|
|
682
764
|
/\[[\s\S]*?\]/g
|
|
683
765
|
];
|
|
684
|
-
|
|
766
|
+
|
|
685
767
|
for (const pattern of jsonPatterns) {
|
|
686
768
|
const matches = text.match(pattern);
|
|
687
769
|
if (matches) {
|
|
@@ -699,7 +781,7 @@ function extractJsonFromMarkdown(text) {
|
|
|
699
781
|
}
|
|
700
782
|
}
|
|
701
783
|
}
|
|
702
|
-
|
|
784
|
+
|
|
703
785
|
// If still no valid JSON found, try to parse the entire text as JSON
|
|
704
786
|
try {
|
|
705
787
|
const trimmed = text.trim();
|
|
@@ -709,7 +791,7 @@ function extractJsonFromMarkdown(text) {
|
|
|
709
791
|
} catch (error) {
|
|
710
792
|
// Last resort failed
|
|
711
793
|
}
|
|
712
|
-
|
|
794
|
+
|
|
713
795
|
return null;
|
|
714
796
|
}
|
|
715
797
|
|
|
@@ -975,24 +1057,24 @@ function extractArgsJson(text) {
|
|
|
975
1057
|
// Prefer fenced code block
|
|
976
1058
|
const fence = text.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
977
1059
|
if (fence && fence[1]) {
|
|
978
|
-
try { return JSON.parse(fence[1].trim()); } catch (_) {}
|
|
1060
|
+
try { return JSON.parse(fence[1].trim()); } catch (_) { }
|
|
979
1061
|
}
|
|
980
1062
|
// Try straightforward parse
|
|
981
1063
|
const trimmed = text.trim();
|
|
982
1064
|
if ((trimmed.startsWith('{') && trimmed.includes('}')) || (trimmed.startsWith('[') && trimmed.includes(']'))) {
|
|
983
|
-
try { return JSON.parse(trimmed); } catch (_) {}
|
|
1065
|
+
try { return JSON.parse(trimmed); } catch (_) { }
|
|
984
1066
|
}
|
|
985
1067
|
// Fallback: find first {...} minimally
|
|
986
1068
|
const braceRegex = /\{[\s\S]*?\}/g;
|
|
987
1069
|
const m = braceRegex.exec(text);
|
|
988
1070
|
if (m && m[0]) {
|
|
989
|
-
try { return JSON.parse(m[0]); } catch (_) {}
|
|
1071
|
+
try { return JSON.parse(m[0]); } catch (_) { }
|
|
990
1072
|
}
|
|
991
1073
|
// Fallback: find first [...] minimally
|
|
992
1074
|
const arrRegex = /\[[\s\S]*?\]/g;
|
|
993
1075
|
const a = arrRegex.exec(text);
|
|
994
1076
|
if (a && a[0]) {
|
|
995
|
-
try { return JSON.parse(a[0]); } catch (_) {}
|
|
1077
|
+
try { return JSON.parse(a[0]); } catch (_) { }
|
|
996
1078
|
}
|
|
997
1079
|
return {};
|
|
998
1080
|
}
|
|
@@ -1162,11 +1244,11 @@ async function processQueryWithTools(query, conversation = [], currentModel) {
|
|
|
1162
1244
|
|
|
1163
1245
|
async function handleToolCalls(toolCalls, messages) {
|
|
1164
1246
|
const results = [];
|
|
1165
|
-
|
|
1247
|
+
|
|
1166
1248
|
for (const toolCall of toolCalls) {
|
|
1167
1249
|
const functionName = toolCall.function.name;
|
|
1168
1250
|
let args;
|
|
1169
|
-
|
|
1251
|
+
|
|
1170
1252
|
try {
|
|
1171
1253
|
args = JSON.parse(toolCall.function.arguments);
|
|
1172
1254
|
} catch (error) {
|
|
@@ -1179,14 +1261,14 @@ async function handleToolCalls(toolCalls, messages) {
|
|
|
1179
1261
|
});
|
|
1180
1262
|
continue;
|
|
1181
1263
|
}
|
|
1182
|
-
|
|
1264
|
+
|
|
1183
1265
|
console.log(`🔧 Executing ${functionName} with args:`, args);
|
|
1184
|
-
|
|
1266
|
+
|
|
1185
1267
|
try {
|
|
1186
1268
|
if (!agentUtils[functionName]) {
|
|
1187
1269
|
throw new Error(`Tool '${functionName}' not found`);
|
|
1188
1270
|
}
|
|
1189
|
-
|
|
1271
|
+
|
|
1190
1272
|
let result;
|
|
1191
1273
|
if (Array.isArray(args)) {
|
|
1192
1274
|
result = await agentUtils[functionName](...args);
|
|
@@ -1198,9 +1280,9 @@ async function handleToolCalls(toolCalls, messages) {
|
|
|
1198
1280
|
result = await agentUtils[functionName]();
|
|
1199
1281
|
}
|
|
1200
1282
|
console.log('✅ Tool executed successfully');
|
|
1201
|
-
|
|
1283
|
+
|
|
1202
1284
|
const resultContent = typeof result === 'string' ? result : JSON.stringify(result);
|
|
1203
|
-
|
|
1285
|
+
|
|
1204
1286
|
results.push({
|
|
1205
1287
|
tool_call_id: toolCall.id,
|
|
1206
1288
|
role: 'tool',
|
|
@@ -1209,25 +1291,25 @@ async function handleToolCalls(toolCalls, messages) {
|
|
|
1209
1291
|
});
|
|
1210
1292
|
} catch (error) {
|
|
1211
1293
|
console.error('❌ Tool execution failed:', error);
|
|
1212
|
-
|
|
1294
|
+
|
|
1213
1295
|
results.push({
|
|
1214
1296
|
tool_call_id: toolCall.id,
|
|
1215
1297
|
role: 'tool',
|
|
1216
1298
|
name: functionName,
|
|
1217
|
-
content: JSON.stringify({
|
|
1299
|
+
content: JSON.stringify({
|
|
1218
1300
|
error: error.message,
|
|
1219
1301
|
stack: process.env.DEBUG ? error.stack : undefined
|
|
1220
1302
|
})
|
|
1221
1303
|
});
|
|
1222
1304
|
}
|
|
1223
1305
|
}
|
|
1224
|
-
|
|
1306
|
+
|
|
1225
1307
|
return results;
|
|
1226
1308
|
}
|
|
1227
1309
|
|
|
1228
1310
|
async function executeAction(action) {
|
|
1229
1311
|
const { type, data } = action;
|
|
1230
|
-
|
|
1312
|
+
|
|
1231
1313
|
switch (type) {
|
|
1232
1314
|
case 'read':
|
|
1233
1315
|
return await agentUtils.readFile(data.path);
|
|
@@ -1266,16 +1348,16 @@ async function executeAction(action) {
|
|
|
1266
1348
|
async function processQuery(query, conversation = [], currentModel) {
|
|
1267
1349
|
try {
|
|
1268
1350
|
const userMessage = { role: 'user', content: query };
|
|
1269
|
-
|
|
1351
|
+
|
|
1270
1352
|
// Use existing conversation (which should already have system prompt from chat function)
|
|
1271
1353
|
let messages = [...conversation, userMessage];
|
|
1272
|
-
|
|
1354
|
+
|
|
1273
1355
|
let actionCount = 0;
|
|
1274
1356
|
const MAX_ACTIONS = 10;
|
|
1275
1357
|
let finalResponse = '';
|
|
1276
1358
|
|
|
1277
1359
|
ui.startThinking();
|
|
1278
|
-
|
|
1360
|
+
|
|
1279
1361
|
while (actionCount < MAX_ACTIONS) {
|
|
1280
1362
|
const responseObj = await callOpenRouter(messages, currentModel, true);
|
|
1281
1363
|
const assistantMessage = responseObj.choices[0].message;
|
|
@@ -1295,55 +1377,55 @@ async function processQuery(query, conversation = [], currentModel) {
|
|
|
1295
1377
|
}
|
|
1296
1378
|
normalizeToolCallsFromMessage(assistantMessage);
|
|
1297
1379
|
messages.push(assistantMessage);
|
|
1298
|
-
|
|
1380
|
+
|
|
1299
1381
|
const actionData = extractJsonFromMarkdown(assistantMessage.content);
|
|
1300
|
-
|
|
1382
|
+
|
|
1301
1383
|
// If no valid action found, treat as final response
|
|
1302
1384
|
if (!actionData || !actionData.type) {
|
|
1303
1385
|
finalResponse = assistantMessage.content;
|
|
1304
1386
|
break;
|
|
1305
1387
|
}
|
|
1306
|
-
|
|
1388
|
+
|
|
1307
1389
|
// If stop action, break with the reasoning or content
|
|
1308
1390
|
if (actionData.type === 'stop') {
|
|
1309
1391
|
finalResponse = actionData.reasoning || assistantMessage.content;
|
|
1310
1392
|
break;
|
|
1311
1393
|
}
|
|
1312
|
-
|
|
1394
|
+
|
|
1313
1395
|
actionCount++;
|
|
1314
1396
|
console.log(`🔧 Executing action ${actionCount}: ${actionData.type}`);
|
|
1315
1397
|
if (actionData.reasoning) {
|
|
1316
1398
|
console.log(`📝 Reasoning: ${actionData.reasoning}`);
|
|
1317
1399
|
}
|
|
1318
|
-
|
|
1400
|
+
|
|
1319
1401
|
try {
|
|
1320
1402
|
const result = await executeAction(actionData);
|
|
1321
1403
|
console.log('✅ Action executed successfully');
|
|
1322
|
-
|
|
1404
|
+
|
|
1323
1405
|
// Add action result to conversation
|
|
1324
1406
|
const resultMessage = {
|
|
1325
|
-
role: 'user',
|
|
1407
|
+
role: 'user',
|
|
1326
1408
|
content: `Action result (${actionData.type}): ${result}`
|
|
1327
1409
|
};
|
|
1328
1410
|
messages.push(resultMessage);
|
|
1329
|
-
|
|
1411
|
+
|
|
1330
1412
|
} catch (error) {
|
|
1331
1413
|
console.error('❌ Action execution failed:', error.message);
|
|
1332
|
-
|
|
1414
|
+
|
|
1333
1415
|
// Add error result to conversation
|
|
1334
1416
|
const errorMessage = {
|
|
1335
|
-
role: 'user',
|
|
1417
|
+
role: 'user',
|
|
1336
1418
|
content: `Action failed (${actionData.type}): ${error.message}`
|
|
1337
1419
|
};
|
|
1338
1420
|
messages.push(errorMessage);
|
|
1339
1421
|
}
|
|
1340
1422
|
}
|
|
1341
|
-
|
|
1423
|
+
|
|
1342
1424
|
// If we hit max actions, get a final response
|
|
1343
1425
|
if (actionCount >= MAX_ACTIONS && !finalResponse) {
|
|
1344
1426
|
const finalMsg = { role: 'user', content: 'Please provide a final summary of what was accomplished.' };
|
|
1345
1427
|
messages.push(finalMsg);
|
|
1346
|
-
|
|
1428
|
+
|
|
1347
1429
|
const finalResponseObj = await callOpenRouter(messages, currentModel, true);
|
|
1348
1430
|
const finalAssistantMessage = finalResponseObj.choices[0].message;
|
|
1349
1431
|
if (finalAssistantMessage && typeof finalAssistantMessage.content === 'string') {
|
|
@@ -1373,22 +1455,27 @@ async function processQuery(query, conversation = [], currentModel) {
|
|
|
1373
1455
|
}
|
|
1374
1456
|
}
|
|
1375
1457
|
|
|
1376
|
-
async function chat(rl,
|
|
1458
|
+
async function chat(rl, mode, initialModel) {
|
|
1377
1459
|
let currentModel = initialModel;
|
|
1378
1460
|
const conversation = [];
|
|
1379
|
-
|
|
1380
|
-
// Initialize conversation with appropriate system prompt
|
|
1381
|
-
if (
|
|
1461
|
+
|
|
1462
|
+
// Initialize conversation with appropriate system prompt based on mode
|
|
1463
|
+
if (mode === 'engineer') {
|
|
1464
|
+
conversation.push({ role: 'system', content: ENGINEER_PROMPT });
|
|
1465
|
+
} else if (mode === 'tool') {
|
|
1382
1466
|
conversation.push({ role: 'system', content: TOOL_CALLING_PROMPT });
|
|
1383
1467
|
} else {
|
|
1384
1468
|
conversation.push({ role: 'system', content: FUNCTION_CALLING_PROMPT });
|
|
1385
1469
|
}
|
|
1386
|
-
|
|
1470
|
+
|
|
1471
|
+
// Determine if we should use tool calling API (both 'tool' and 'engineer' modes use tools)
|
|
1472
|
+
const useToolCalling = (mode === 'tool' || mode === 'engineer');
|
|
1473
|
+
|
|
1387
1474
|
console.log('Type your message, or "exit" to quit.');
|
|
1388
|
-
|
|
1475
|
+
|
|
1389
1476
|
rl.setPrompt('> ');
|
|
1390
1477
|
rl.prompt();
|
|
1391
|
-
|
|
1478
|
+
|
|
1392
1479
|
rl.on('line', async (input) => {
|
|
1393
1480
|
if (input.toLowerCase().startsWith('/model')) {
|
|
1394
1481
|
const newModel = input.split(' ')[1];
|
|
@@ -1441,11 +1528,99 @@ async function chat(rl, useToolCalling, initialModel) {
|
|
|
1441
1528
|
return;
|
|
1442
1529
|
}
|
|
1443
1530
|
|
|
1531
|
+
// Brainstorm command: /brainstorm <project-name> <description>
|
|
1532
|
+
if (input.toLowerCase().startsWith('/brainstorm')) {
|
|
1533
|
+
const parts = input.substring('/brainstorm'.length).trim();
|
|
1534
|
+
if (!parts) {
|
|
1535
|
+
console.log('Usage: /brainstorm <project-name> "<description>"');
|
|
1536
|
+
console.log('Example: /brainstorm "My Project" "A cool project that does things"');
|
|
1537
|
+
rl.prompt();
|
|
1538
|
+
return;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
// Parse project name and description
|
|
1542
|
+
const match = parts.match(/^"?([^"]+)"?\s+"?([^"]+)"?$/) || parts.match(/^(\S+)\s+(.+)$/);
|
|
1543
|
+
if (!match) {
|
|
1544
|
+
console.log('Usage: /brainstorm <project-name> "<description>"');
|
|
1545
|
+
rl.prompt();
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
const projectName = match[1].trim();
|
|
1550
|
+
const projectDescription = match[2].trim();
|
|
1551
|
+
const outputDir = path.join(process.cwd(), projectName.replace(/\s+/g, '-').toLowerCase());
|
|
1552
|
+
|
|
1553
|
+
try {
|
|
1554
|
+
ui.showInfo(`Creating brainstorm session for "${projectName}"...`);
|
|
1555
|
+
const session = await brainstormCore.quickStart(
|
|
1556
|
+
projectName,
|
|
1557
|
+
projectDescription,
|
|
1558
|
+
outputDir,
|
|
1559
|
+
['CLAUDE-1'] // Single agent by default
|
|
1560
|
+
);
|
|
1561
|
+
|
|
1562
|
+
ui.showSuccess(`✅ Brainstorm session created!`);
|
|
1563
|
+
console.log(` Session ID: ${session.id}`);
|
|
1564
|
+
console.log(` Output: ${outputDir}`);
|
|
1565
|
+
console.log(` Files generated: ${Object.keys(session.fileVersions).length}`);
|
|
1566
|
+
console.log(`\n Generated files:`);
|
|
1567
|
+
Object.keys(session.fileVersions).forEach(f => console.log(` - ${f}`));
|
|
1568
|
+
console.log(`\n Use "/finish <summary>" when done to complete the session.`);
|
|
1569
|
+
|
|
1570
|
+
// Store current session path for /finish command
|
|
1571
|
+
global.currentSessionDir = outputDir;
|
|
1572
|
+
} catch (error) {
|
|
1573
|
+
ui.showError(`Failed to create brainstorm: ${error.message}`);
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
rl.prompt();
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
// Finish command: /finish <summary>
|
|
1581
|
+
if (input.toLowerCase().startsWith('/finish')) {
|
|
1582
|
+
const summary = input.substring('/finish'.length).trim();
|
|
1583
|
+
|
|
1584
|
+
if (!global.currentSessionDir) {
|
|
1585
|
+
console.log('No active brainstorm session. Use /brainstorm first.');
|
|
1586
|
+
rl.prompt();
|
|
1587
|
+
return;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
if (!summary) {
|
|
1591
|
+
console.log('Usage: /finish "<summary of what was accomplished>"');
|
|
1592
|
+
rl.prompt();
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
try {
|
|
1597
|
+
ui.showInfo('Completing brainstorm session...');
|
|
1598
|
+
const result = await brainstormCore.finishBrainstorm({
|
|
1599
|
+
sessionDir: global.currentSessionDir,
|
|
1600
|
+
summary,
|
|
1601
|
+
actor: 'CLAUDE-1'
|
|
1602
|
+
});
|
|
1603
|
+
|
|
1604
|
+
if (result.success) {
|
|
1605
|
+
ui.showSuccess(`✅ Session completed!`);
|
|
1606
|
+
console.log(` Summary: ${summary}`);
|
|
1607
|
+
global.currentSessionDir = null;
|
|
1608
|
+
} else {
|
|
1609
|
+
ui.showError(`Failed: ${result.errors.join(', ')}`);
|
|
1610
|
+
}
|
|
1611
|
+
} catch (error) {
|
|
1612
|
+
ui.showError(`Failed to finish session: ${error.message}`);
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
rl.prompt();
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1444
1619
|
if (input.toLowerCase() === 'exit') {
|
|
1445
1620
|
rl.close();
|
|
1446
1621
|
return;
|
|
1447
1622
|
}
|
|
1448
|
-
|
|
1623
|
+
|
|
1449
1624
|
// Direct Harmony tool-call execution from user input (bypass model)
|
|
1450
1625
|
try {
|
|
1451
1626
|
const seg = parseSegmentedTranscript(input);
|
|
@@ -1476,16 +1651,24 @@ async function chat(rl, useToolCalling, initialModel) {
|
|
|
1476
1651
|
} catch (e) {
|
|
1477
1652
|
// Fall through to normal processing if parsing/execution fails
|
|
1478
1653
|
}
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1654
|
+
|
|
1655
|
+
try {
|
|
1656
|
+
const result = useToolCalling
|
|
1657
|
+
? await processQueryWithTools(input, conversation, currentModel)
|
|
1658
|
+
: await processQuery(input, conversation, currentModel);
|
|
1659
|
+
ui.stopThinking();
|
|
1660
|
+
ui.showResponse(result.response);
|
|
1661
|
+
|
|
1662
|
+
conversation.length = 0;
|
|
1663
|
+
result.conversation.forEach(msg => conversation.push(msg));
|
|
1664
|
+
} catch (error) {
|
|
1665
|
+
ui.stopThinking();
|
|
1666
|
+
console.error('❌ Error processing request:', error.message);
|
|
1667
|
+
if (process.env.DEBUG) {
|
|
1668
|
+
console.error(error.stack);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1489
1672
|
rl.prompt();
|
|
1490
1673
|
}).on('close', () => {
|
|
1491
1674
|
ui.showResponse('Goodbye!');
|
|
@@ -1495,8 +1678,15 @@ async function chat(rl, useToolCalling, initialModel) {
|
|
|
1495
1678
|
|
|
1496
1679
|
function askForMode(rl) {
|
|
1497
1680
|
return new Promise((resolve) => {
|
|
1498
|
-
rl.question('Select mode (1 for tool calling, 2 for function calling): ', (answer) => {
|
|
1499
|
-
|
|
1681
|
+
rl.question('Select mode (1 for tool calling, 2 for function calling, 3 for engineer): ', (answer) => {
|
|
1682
|
+
const mode = answer.trim();
|
|
1683
|
+
if (mode === '3') {
|
|
1684
|
+
resolve('engineer');
|
|
1685
|
+
} else if (mode === '2') {
|
|
1686
|
+
resolve('function');
|
|
1687
|
+
} else {
|
|
1688
|
+
resolve('tool');
|
|
1689
|
+
}
|
|
1500
1690
|
});
|
|
1501
1691
|
});
|
|
1502
1692
|
}
|
|
@@ -1579,11 +1769,11 @@ async function start() {
|
|
|
1579
1769
|
|
|
1580
1770
|
// Check if animation should be shown (can be disabled via config)
|
|
1581
1771
|
const showAnimation = config.showAnimation !== false; // Default to true
|
|
1582
|
-
|
|
1772
|
+
|
|
1583
1773
|
if (showAnimation && !process.env.SKIP_ANIMATION) {
|
|
1584
1774
|
// Run the epic AGI awakening animation
|
|
1585
1775
|
await runAGIAnimation();
|
|
1586
|
-
|
|
1776
|
+
|
|
1587
1777
|
// Small pause after animation
|
|
1588
1778
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
1589
1779
|
}
|
|
@@ -1592,11 +1782,13 @@ async function start() {
|
|
|
1592
1782
|
console.log('Select Mode:');
|
|
1593
1783
|
console.log('1. Tool Calling (for models that support it)');
|
|
1594
1784
|
console.log('2. Function Calling (legacy)');
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1785
|
+
console.log('3. Engineer Mode (autonomous engineering with Claude Code style editing)');
|
|
1786
|
+
|
|
1787
|
+
const selectedMode = await askForMode(rl);
|
|
1788
|
+
const modeNames = { 'tool': 'Tool Calling', 'function': 'Function Calling', 'engineer': 'Engineer' };
|
|
1789
|
+
ui.showResponse(`\nStarting in ${modeNames[selectedMode]} mode...\n`);
|
|
1790
|
+
|
|
1791
|
+
await chat(rl, selectedMode, MODEL);
|
|
1600
1792
|
} catch (error) {
|
|
1601
1793
|
ui.showError(error);
|
|
1602
1794
|
rl.close();
|