sam-coder-cli 1.0.68 → 2.0.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/bin/agi-cli.js +326 -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
|
|
|
@@ -430,9 +491,10 @@ const agentUtils = {
|
|
|
430
491
|
},
|
|
431
492
|
|
|
432
493
|
async writeFile(input, maybeContent) {
|
|
494
|
+
let filePath;
|
|
433
495
|
try {
|
|
434
|
-
|
|
435
|
-
const content = typeof input === 'string' ? maybeContent : input?.content;
|
|
496
|
+
filePath = typeof input === 'string' ? input : input?.path;
|
|
497
|
+
const content = typeof input === 'string' ? maybeContent : (input?.content ?? input?.contents ?? input?.data);
|
|
436
498
|
if (!filePath) throw new Error('writeFile: missing path');
|
|
437
499
|
if (typeof content !== 'string') throw new Error('writeFile: missing content');
|
|
438
500
|
const dir = path.dirname(filePath);
|
|
@@ -440,7 +502,8 @@ const agentUtils = {
|
|
|
440
502
|
await fs.writeFile(filePath, content, 'utf-8');
|
|
441
503
|
return `Successfully wrote to ${filePath}`;
|
|
442
504
|
} catch (error) {
|
|
443
|
-
|
|
505
|
+
const ctx = filePath || (typeof input === 'object' ? input?.path : undefined) || 'unknown path';
|
|
506
|
+
throw new Error(`Failed to write to file ${ctx}: ${error.message}`);
|
|
444
507
|
}
|
|
445
508
|
},
|
|
446
509
|
|
|
@@ -453,7 +516,7 @@ const agentUtils = {
|
|
|
453
516
|
// Read the current file content
|
|
454
517
|
let content = await fs.readFile(targetPath, 'utf-8');
|
|
455
518
|
const lines = content.split('\n');
|
|
456
|
-
|
|
519
|
+
|
|
457
520
|
// Process each edit operation
|
|
458
521
|
for (const op of edits.operations) {
|
|
459
522
|
switch (op.type) {
|
|
@@ -476,7 +539,7 @@ const agentUtils = {
|
|
|
476
539
|
lines.push(...content.split('\n'));
|
|
477
540
|
}
|
|
478
541
|
break;
|
|
479
|
-
|
|
542
|
+
|
|
480
543
|
case 'insert':
|
|
481
544
|
if (op.position === 'start') {
|
|
482
545
|
lines.unshift(...op.text.split('\n'));
|
|
@@ -490,23 +553,44 @@ const agentUtils = {
|
|
|
490
553
|
lines.splice(op.line - 1, 0, ...insertLines);
|
|
491
554
|
}
|
|
492
555
|
break;
|
|
493
|
-
|
|
556
|
+
|
|
494
557
|
case 'delete':
|
|
495
558
|
if (op.startLine < 1 || op.endLine > lines.length) {
|
|
496
559
|
throw new Error(`Line numbers out of range (1-${lines.length})`);
|
|
497
560
|
}
|
|
498
561
|
lines.splice(op.startLine - 1, op.endLine - op.startLine + 1);
|
|
499
562
|
break;
|
|
500
|
-
|
|
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
|
+
|
|
501
585
|
default:
|
|
502
586
|
throw new Error(`Unknown operation type: ${op.type}`);
|
|
503
587
|
}
|
|
504
588
|
}
|
|
505
|
-
|
|
589
|
+
|
|
506
590
|
// Write the modified content back to the file
|
|
507
591
|
await fs.writeFile(targetPath, lines.join('\n'), 'utf-8');
|
|
508
592
|
return `Successfully edited ${targetPath}`;
|
|
509
|
-
|
|
593
|
+
|
|
510
594
|
} catch (error) {
|
|
511
595
|
throw new Error(`Failed to edit file ${typeof inputPathOrObj === 'string' ? inputPathOrObj : inputPathOrObj?.path}: ${error.message}`);
|
|
512
596
|
}
|
|
@@ -514,9 +598,54 @@ const agentUtils = {
|
|
|
514
598
|
|
|
515
599
|
async runCommand(input) {
|
|
516
600
|
try {
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
const
|
|
601
|
+
const isObj = typeof input === 'object' && input !== null;
|
|
602
|
+
let command = !isObj ? input : (input.command ?? null);
|
|
603
|
+
const cmd = isObj ? (input.cmd ?? input.program ?? null) : null;
|
|
604
|
+
const args = isObj ? (input.args ?? input.params ?? null) : null;
|
|
605
|
+
const script = isObj ? (input.script ?? null) : null;
|
|
606
|
+
const shell = isObj ? (input.shell ?? (input.powershell ? 'powershell.exe' : (input.bash ? 'bash' : undefined))) : undefined;
|
|
607
|
+
const cwdRaw = isObj ? input.cwd : undefined;
|
|
608
|
+
const envRaw = isObj ? input.env : undefined;
|
|
609
|
+
const timeout = isObj && typeof input.timeout === 'number' ? input.timeout : undefined;
|
|
610
|
+
|
|
611
|
+
const quoteArg = (a) => {
|
|
612
|
+
if (a == null) return '';
|
|
613
|
+
const s = String(a);
|
|
614
|
+
return /\s|["']/g.test(s) ? '"' + s.replace(/"/g, '\\"') + '"' : s;
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
// Build command from arrays or fields if not provided directly
|
|
618
|
+
if (!command && cmd) {
|
|
619
|
+
if (Array.isArray(cmd)) {
|
|
620
|
+
command = cmd.map(quoteArg).join(' ');
|
|
621
|
+
} else if (typeof cmd === 'string') {
|
|
622
|
+
command = cmd;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (Array.isArray(command)) {
|
|
626
|
+
command = command.map(quoteArg).join(' ');
|
|
627
|
+
}
|
|
628
|
+
if (command && Array.isArray(args) && args.length) {
|
|
629
|
+
command = `${command} ${args.map(quoteArg).join(' ')}`;
|
|
630
|
+
}
|
|
631
|
+
if (script && !command) {
|
|
632
|
+
// If only a script is provided, run it directly under the selected shell
|
|
633
|
+
command = String(script);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (!command || typeof command !== 'string' || command.trim().length === 0) {
|
|
637
|
+
throw new Error('runCommand: missing command');
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// Resolve cwd
|
|
641
|
+
let cwd = process.cwd();
|
|
642
|
+
if (typeof cwdRaw === 'string' && cwdRaw.trim().length > 0) {
|
|
643
|
+
cwd = path.isAbsolute(cwdRaw) ? cwdRaw : path.join(process.cwd(), cwdRaw);
|
|
644
|
+
}
|
|
645
|
+
// Merge env
|
|
646
|
+
const env = envRaw && typeof envRaw === 'object' ? { ...process.env, ...envRaw } : undefined;
|
|
647
|
+
|
|
648
|
+
const { stdout, stderr } = await execAsync(command, { cwd, env, timeout, shell });
|
|
520
649
|
if (stderr) {
|
|
521
650
|
console.error('Command stderr:', stderr);
|
|
522
651
|
}
|
|
@@ -614,7 +743,7 @@ function extractJsonFromMarkdown(text) {
|
|
|
614
743
|
// Try to find a markdown code block with JSON content (case insensitive)
|
|
615
744
|
const codeBlockRegex = /```(?:json|JSON)\s*([\s\S]*?)\s*```/i;
|
|
616
745
|
const match = text.match(codeBlockRegex);
|
|
617
|
-
|
|
746
|
+
|
|
618
747
|
if (match && match[1]) {
|
|
619
748
|
try {
|
|
620
749
|
const jsonStr = match[1].trim();
|
|
@@ -626,7 +755,7 @@ function extractJsonFromMarkdown(text) {
|
|
|
626
755
|
// ignore
|
|
627
756
|
}
|
|
628
757
|
}
|
|
629
|
-
|
|
758
|
+
|
|
630
759
|
// If no code block found, look for JSON-like patterns in the text
|
|
631
760
|
const jsonPatterns = [
|
|
632
761
|
// Look for objects that start with { and end with }
|
|
@@ -634,7 +763,7 @@ function extractJsonFromMarkdown(text) {
|
|
|
634
763
|
// Look for arrays that start with [ and end with ]
|
|
635
764
|
/\[[\s\S]*?\]/g
|
|
636
765
|
];
|
|
637
|
-
|
|
766
|
+
|
|
638
767
|
for (const pattern of jsonPatterns) {
|
|
639
768
|
const matches = text.match(pattern);
|
|
640
769
|
if (matches) {
|
|
@@ -652,7 +781,7 @@ function extractJsonFromMarkdown(text) {
|
|
|
652
781
|
}
|
|
653
782
|
}
|
|
654
783
|
}
|
|
655
|
-
|
|
784
|
+
|
|
656
785
|
// If still no valid JSON found, try to parse the entire text as JSON
|
|
657
786
|
try {
|
|
658
787
|
const trimmed = text.trim();
|
|
@@ -662,7 +791,7 @@ function extractJsonFromMarkdown(text) {
|
|
|
662
791
|
} catch (error) {
|
|
663
792
|
// Last resort failed
|
|
664
793
|
}
|
|
665
|
-
|
|
794
|
+
|
|
666
795
|
return null;
|
|
667
796
|
}
|
|
668
797
|
|
|
@@ -928,24 +1057,24 @@ function extractArgsJson(text) {
|
|
|
928
1057
|
// Prefer fenced code block
|
|
929
1058
|
const fence = text.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
930
1059
|
if (fence && fence[1]) {
|
|
931
|
-
try { return JSON.parse(fence[1].trim()); } catch (_) {}
|
|
1060
|
+
try { return JSON.parse(fence[1].trim()); } catch (_) { }
|
|
932
1061
|
}
|
|
933
1062
|
// Try straightforward parse
|
|
934
1063
|
const trimmed = text.trim();
|
|
935
1064
|
if ((trimmed.startsWith('{') && trimmed.includes('}')) || (trimmed.startsWith('[') && trimmed.includes(']'))) {
|
|
936
|
-
try { return JSON.parse(trimmed); } catch (_) {}
|
|
1065
|
+
try { return JSON.parse(trimmed); } catch (_) { }
|
|
937
1066
|
}
|
|
938
1067
|
// Fallback: find first {...} minimally
|
|
939
1068
|
const braceRegex = /\{[\s\S]*?\}/g;
|
|
940
1069
|
const m = braceRegex.exec(text);
|
|
941
1070
|
if (m && m[0]) {
|
|
942
|
-
try { return JSON.parse(m[0]); } catch (_) {}
|
|
1071
|
+
try { return JSON.parse(m[0]); } catch (_) { }
|
|
943
1072
|
}
|
|
944
1073
|
// Fallback: find first [...] minimally
|
|
945
1074
|
const arrRegex = /\[[\s\S]*?\]/g;
|
|
946
1075
|
const a = arrRegex.exec(text);
|
|
947
1076
|
if (a && a[0]) {
|
|
948
|
-
try { return JSON.parse(a[0]); } catch (_) {}
|
|
1077
|
+
try { return JSON.parse(a[0]); } catch (_) { }
|
|
949
1078
|
}
|
|
950
1079
|
return {};
|
|
951
1080
|
}
|
|
@@ -1115,11 +1244,11 @@ async function processQueryWithTools(query, conversation = [], currentModel) {
|
|
|
1115
1244
|
|
|
1116
1245
|
async function handleToolCalls(toolCalls, messages) {
|
|
1117
1246
|
const results = [];
|
|
1118
|
-
|
|
1247
|
+
|
|
1119
1248
|
for (const toolCall of toolCalls) {
|
|
1120
1249
|
const functionName = toolCall.function.name;
|
|
1121
1250
|
let args;
|
|
1122
|
-
|
|
1251
|
+
|
|
1123
1252
|
try {
|
|
1124
1253
|
args = JSON.parse(toolCall.function.arguments);
|
|
1125
1254
|
} catch (error) {
|
|
@@ -1132,14 +1261,14 @@ async function handleToolCalls(toolCalls, messages) {
|
|
|
1132
1261
|
});
|
|
1133
1262
|
continue;
|
|
1134
1263
|
}
|
|
1135
|
-
|
|
1264
|
+
|
|
1136
1265
|
console.log(`🔧 Executing ${functionName} with args:`, args);
|
|
1137
|
-
|
|
1266
|
+
|
|
1138
1267
|
try {
|
|
1139
1268
|
if (!agentUtils[functionName]) {
|
|
1140
1269
|
throw new Error(`Tool '${functionName}' not found`);
|
|
1141
1270
|
}
|
|
1142
|
-
|
|
1271
|
+
|
|
1143
1272
|
let result;
|
|
1144
1273
|
if (Array.isArray(args)) {
|
|
1145
1274
|
result = await agentUtils[functionName](...args);
|
|
@@ -1151,9 +1280,9 @@ async function handleToolCalls(toolCalls, messages) {
|
|
|
1151
1280
|
result = await agentUtils[functionName]();
|
|
1152
1281
|
}
|
|
1153
1282
|
console.log('✅ Tool executed successfully');
|
|
1154
|
-
|
|
1283
|
+
|
|
1155
1284
|
const resultContent = typeof result === 'string' ? result : JSON.stringify(result);
|
|
1156
|
-
|
|
1285
|
+
|
|
1157
1286
|
results.push({
|
|
1158
1287
|
tool_call_id: toolCall.id,
|
|
1159
1288
|
role: 'tool',
|
|
@@ -1162,25 +1291,25 @@ async function handleToolCalls(toolCalls, messages) {
|
|
|
1162
1291
|
});
|
|
1163
1292
|
} catch (error) {
|
|
1164
1293
|
console.error('❌ Tool execution failed:', error);
|
|
1165
|
-
|
|
1294
|
+
|
|
1166
1295
|
results.push({
|
|
1167
1296
|
tool_call_id: toolCall.id,
|
|
1168
1297
|
role: 'tool',
|
|
1169
1298
|
name: functionName,
|
|
1170
|
-
content: JSON.stringify({
|
|
1299
|
+
content: JSON.stringify({
|
|
1171
1300
|
error: error.message,
|
|
1172
1301
|
stack: process.env.DEBUG ? error.stack : undefined
|
|
1173
1302
|
})
|
|
1174
1303
|
});
|
|
1175
1304
|
}
|
|
1176
1305
|
}
|
|
1177
|
-
|
|
1306
|
+
|
|
1178
1307
|
return results;
|
|
1179
1308
|
}
|
|
1180
1309
|
|
|
1181
1310
|
async function executeAction(action) {
|
|
1182
1311
|
const { type, data } = action;
|
|
1183
|
-
|
|
1312
|
+
|
|
1184
1313
|
switch (type) {
|
|
1185
1314
|
case 'read':
|
|
1186
1315
|
return await agentUtils.readFile(data.path);
|
|
@@ -1219,16 +1348,16 @@ async function executeAction(action) {
|
|
|
1219
1348
|
async function processQuery(query, conversation = [], currentModel) {
|
|
1220
1349
|
try {
|
|
1221
1350
|
const userMessage = { role: 'user', content: query };
|
|
1222
|
-
|
|
1351
|
+
|
|
1223
1352
|
// Use existing conversation (which should already have system prompt from chat function)
|
|
1224
1353
|
let messages = [...conversation, userMessage];
|
|
1225
|
-
|
|
1354
|
+
|
|
1226
1355
|
let actionCount = 0;
|
|
1227
1356
|
const MAX_ACTIONS = 10;
|
|
1228
1357
|
let finalResponse = '';
|
|
1229
1358
|
|
|
1230
1359
|
ui.startThinking();
|
|
1231
|
-
|
|
1360
|
+
|
|
1232
1361
|
while (actionCount < MAX_ACTIONS) {
|
|
1233
1362
|
const responseObj = await callOpenRouter(messages, currentModel, true);
|
|
1234
1363
|
const assistantMessage = responseObj.choices[0].message;
|
|
@@ -1248,55 +1377,55 @@ async function processQuery(query, conversation = [], currentModel) {
|
|
|
1248
1377
|
}
|
|
1249
1378
|
normalizeToolCallsFromMessage(assistantMessage);
|
|
1250
1379
|
messages.push(assistantMessage);
|
|
1251
|
-
|
|
1380
|
+
|
|
1252
1381
|
const actionData = extractJsonFromMarkdown(assistantMessage.content);
|
|
1253
|
-
|
|
1382
|
+
|
|
1254
1383
|
// If no valid action found, treat as final response
|
|
1255
1384
|
if (!actionData || !actionData.type) {
|
|
1256
1385
|
finalResponse = assistantMessage.content;
|
|
1257
1386
|
break;
|
|
1258
1387
|
}
|
|
1259
|
-
|
|
1388
|
+
|
|
1260
1389
|
// If stop action, break with the reasoning or content
|
|
1261
1390
|
if (actionData.type === 'stop') {
|
|
1262
1391
|
finalResponse = actionData.reasoning || assistantMessage.content;
|
|
1263
1392
|
break;
|
|
1264
1393
|
}
|
|
1265
|
-
|
|
1394
|
+
|
|
1266
1395
|
actionCount++;
|
|
1267
1396
|
console.log(`🔧 Executing action ${actionCount}: ${actionData.type}`);
|
|
1268
1397
|
if (actionData.reasoning) {
|
|
1269
1398
|
console.log(`📝 Reasoning: ${actionData.reasoning}`);
|
|
1270
1399
|
}
|
|
1271
|
-
|
|
1400
|
+
|
|
1272
1401
|
try {
|
|
1273
1402
|
const result = await executeAction(actionData);
|
|
1274
1403
|
console.log('✅ Action executed successfully');
|
|
1275
|
-
|
|
1404
|
+
|
|
1276
1405
|
// Add action result to conversation
|
|
1277
1406
|
const resultMessage = {
|
|
1278
|
-
role: 'user',
|
|
1407
|
+
role: 'user',
|
|
1279
1408
|
content: `Action result (${actionData.type}): ${result}`
|
|
1280
1409
|
};
|
|
1281
1410
|
messages.push(resultMessage);
|
|
1282
|
-
|
|
1411
|
+
|
|
1283
1412
|
} catch (error) {
|
|
1284
1413
|
console.error('❌ Action execution failed:', error.message);
|
|
1285
|
-
|
|
1414
|
+
|
|
1286
1415
|
// Add error result to conversation
|
|
1287
1416
|
const errorMessage = {
|
|
1288
|
-
role: 'user',
|
|
1417
|
+
role: 'user',
|
|
1289
1418
|
content: `Action failed (${actionData.type}): ${error.message}`
|
|
1290
1419
|
};
|
|
1291
1420
|
messages.push(errorMessage);
|
|
1292
1421
|
}
|
|
1293
1422
|
}
|
|
1294
|
-
|
|
1423
|
+
|
|
1295
1424
|
// If we hit max actions, get a final response
|
|
1296
1425
|
if (actionCount >= MAX_ACTIONS && !finalResponse) {
|
|
1297
1426
|
const finalMsg = { role: 'user', content: 'Please provide a final summary of what was accomplished.' };
|
|
1298
1427
|
messages.push(finalMsg);
|
|
1299
|
-
|
|
1428
|
+
|
|
1300
1429
|
const finalResponseObj = await callOpenRouter(messages, currentModel, true);
|
|
1301
1430
|
const finalAssistantMessage = finalResponseObj.choices[0].message;
|
|
1302
1431
|
if (finalAssistantMessage && typeof finalAssistantMessage.content === 'string') {
|
|
@@ -1326,22 +1455,27 @@ async function processQuery(query, conversation = [], currentModel) {
|
|
|
1326
1455
|
}
|
|
1327
1456
|
}
|
|
1328
1457
|
|
|
1329
|
-
async function chat(rl,
|
|
1458
|
+
async function chat(rl, mode, initialModel) {
|
|
1330
1459
|
let currentModel = initialModel;
|
|
1331
1460
|
const conversation = [];
|
|
1332
|
-
|
|
1333
|
-
// Initialize conversation with appropriate system prompt
|
|
1334
|
-
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') {
|
|
1335
1466
|
conversation.push({ role: 'system', content: TOOL_CALLING_PROMPT });
|
|
1336
1467
|
} else {
|
|
1337
1468
|
conversation.push({ role: 'system', content: FUNCTION_CALLING_PROMPT });
|
|
1338
1469
|
}
|
|
1339
|
-
|
|
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
|
+
|
|
1340
1474
|
console.log('Type your message, or "exit" to quit.');
|
|
1341
|
-
|
|
1475
|
+
|
|
1342
1476
|
rl.setPrompt('> ');
|
|
1343
1477
|
rl.prompt();
|
|
1344
|
-
|
|
1478
|
+
|
|
1345
1479
|
rl.on('line', async (input) => {
|
|
1346
1480
|
if (input.toLowerCase().startsWith('/model')) {
|
|
1347
1481
|
const newModel = input.split(' ')[1];
|
|
@@ -1394,11 +1528,99 @@ async function chat(rl, useToolCalling, initialModel) {
|
|
|
1394
1528
|
return;
|
|
1395
1529
|
}
|
|
1396
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
|
+
|
|
1397
1619
|
if (input.toLowerCase() === 'exit') {
|
|
1398
1620
|
rl.close();
|
|
1399
1621
|
return;
|
|
1400
1622
|
}
|
|
1401
|
-
|
|
1623
|
+
|
|
1402
1624
|
// Direct Harmony tool-call execution from user input (bypass model)
|
|
1403
1625
|
try {
|
|
1404
1626
|
const seg = parseSegmentedTranscript(input);
|
|
@@ -1429,16 +1651,16 @@ async function chat(rl, useToolCalling, initialModel) {
|
|
|
1429
1651
|
} catch (e) {
|
|
1430
1652
|
// Fall through to normal processing if parsing/execution fails
|
|
1431
1653
|
}
|
|
1432
|
-
|
|
1433
|
-
const result = useToolCalling
|
|
1654
|
+
|
|
1655
|
+
const result = useToolCalling
|
|
1434
1656
|
? await processQueryWithTools(input, conversation, currentModel)
|
|
1435
1657
|
: await processQuery(input, conversation, currentModel);
|
|
1436
1658
|
ui.stopThinking();
|
|
1437
1659
|
ui.showResponse(result.response);
|
|
1438
|
-
|
|
1660
|
+
|
|
1439
1661
|
conversation.length = 0;
|
|
1440
1662
|
result.conversation.forEach(msg => conversation.push(msg));
|
|
1441
|
-
|
|
1663
|
+
|
|
1442
1664
|
rl.prompt();
|
|
1443
1665
|
}).on('close', () => {
|
|
1444
1666
|
ui.showResponse('Goodbye!');
|
|
@@ -1448,8 +1670,15 @@ async function chat(rl, useToolCalling, initialModel) {
|
|
|
1448
1670
|
|
|
1449
1671
|
function askForMode(rl) {
|
|
1450
1672
|
return new Promise((resolve) => {
|
|
1451
|
-
rl.question('Select mode (1 for tool calling, 2 for function calling): ', (answer) => {
|
|
1452
|
-
|
|
1673
|
+
rl.question('Select mode (1 for tool calling, 2 for function calling, 3 for engineer): ', (answer) => {
|
|
1674
|
+
const mode = answer.trim();
|
|
1675
|
+
if (mode === '3') {
|
|
1676
|
+
resolve('engineer');
|
|
1677
|
+
} else if (mode === '2') {
|
|
1678
|
+
resolve('function');
|
|
1679
|
+
} else {
|
|
1680
|
+
resolve('tool');
|
|
1681
|
+
}
|
|
1453
1682
|
});
|
|
1454
1683
|
});
|
|
1455
1684
|
}
|
|
@@ -1532,11 +1761,11 @@ async function start() {
|
|
|
1532
1761
|
|
|
1533
1762
|
// Check if animation should be shown (can be disabled via config)
|
|
1534
1763
|
const showAnimation = config.showAnimation !== false; // Default to true
|
|
1535
|
-
|
|
1764
|
+
|
|
1536
1765
|
if (showAnimation && !process.env.SKIP_ANIMATION) {
|
|
1537
1766
|
// Run the epic AGI awakening animation
|
|
1538
1767
|
await runAGIAnimation();
|
|
1539
|
-
|
|
1768
|
+
|
|
1540
1769
|
// Small pause after animation
|
|
1541
1770
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
1542
1771
|
}
|
|
@@ -1545,11 +1774,13 @@ async function start() {
|
|
|
1545
1774
|
console.log('Select Mode:');
|
|
1546
1775
|
console.log('1. Tool Calling (for models that support it)');
|
|
1547
1776
|
console.log('2. Function Calling (legacy)');
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1777
|
+
console.log('3. Engineer Mode (autonomous engineering with Claude Code style editing)');
|
|
1778
|
+
|
|
1779
|
+
const selectedMode = await askForMode(rl);
|
|
1780
|
+
const modeNames = { 'tool': 'Tool Calling', 'function': 'Function Calling', 'engineer': 'Engineer' };
|
|
1781
|
+
ui.showResponse(`\nStarting in ${modeNames[selectedMode]} mode...\n`);
|
|
1782
|
+
|
|
1783
|
+
await chat(rl, selectedMode, MODEL);
|
|
1553
1784
|
} catch (error) {
|
|
1554
1785
|
ui.showError(error);
|
|
1555
1786
|
rl.close();
|