sam-coder-cli 2.0.4 → 2.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -43
- package/bin/agi-cli.js +230 -337
- package/bin/ui.js +8 -1
- package/package.json +11 -20
- package/bin/core/brainstorm.js +0 -198
- package/bin/core/edit_finished_brainstorm.js +0 -294
- package/bin/core/finish_brainstorm.js +0 -217
- package/bin/core/index.js +0 -37
- package/bin/core/models.js +0 -290
- package/bin/core/templates.js +0 -567
package/bin/agi-cli.js
CHANGED
|
@@ -9,12 +9,20 @@ const { exec } = require('child_process');
|
|
|
9
9
|
const util = require('util');
|
|
10
10
|
const execAsync = util.promisify(exec);
|
|
11
11
|
|
|
12
|
+
// Global error handlers to prevent silent crashes
|
|
13
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
14
|
+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
15
|
+
// Don't exit, just log the error
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
process.on('uncaughtException', (error) => {
|
|
19
|
+
console.error('Uncaught Exception:', error);
|
|
20
|
+
// Don't exit, just log the error
|
|
21
|
+
});
|
|
22
|
+
|
|
12
23
|
// Import AGI Animation module
|
|
13
24
|
const { runAGIAnimation } = require('./agi-animation.js');
|
|
14
25
|
|
|
15
|
-
// Import Brainstorm Core module
|
|
16
|
-
const brainstormCore = require('./core');
|
|
17
|
-
|
|
18
26
|
// Configuration
|
|
19
27
|
const CONFIG_PATH = path.join(os.homedir(), '.sam-coder-config.json');
|
|
20
28
|
let OPENROUTER_API_KEY;
|
|
@@ -72,17 +80,9 @@ const tools = [
|
|
|
72
80
|
properties: {
|
|
73
81
|
type: {
|
|
74
82
|
type: 'string',
|
|
75
|
-
enum: ['replace', 'insert', 'delete', '
|
|
83
|
+
enum: ['replace', 'insert', 'delete', 'replace_block'],
|
|
76
84
|
description: 'Type of edit operation'
|
|
77
85
|
},
|
|
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)'
|
|
85
|
-
},
|
|
86
86
|
startLine: {
|
|
87
87
|
type: 'number',
|
|
88
88
|
description: 'Starting line number (1-based)'
|
|
@@ -95,6 +95,14 @@ const tools = [
|
|
|
95
95
|
type: 'string',
|
|
96
96
|
description: 'New text to insert or replace with'
|
|
97
97
|
},
|
|
98
|
+
oldText: {
|
|
99
|
+
type: 'string',
|
|
100
|
+
description: 'Exact text to search for and replace (for replace_block)'
|
|
101
|
+
},
|
|
102
|
+
replacementContent: {
|
|
103
|
+
type: 'string',
|
|
104
|
+
description: 'New text to replace oldText with (for replace_block)'
|
|
105
|
+
},
|
|
98
106
|
pattern: {
|
|
99
107
|
type: 'string',
|
|
100
108
|
description: 'Pattern to search for (for replace operations)'
|
|
@@ -137,6 +145,14 @@ const tools = [
|
|
|
137
145
|
},
|
|
138
146
|
required: ['pattern', 'replacement']
|
|
139
147
|
},
|
|
148
|
+
{
|
|
149
|
+
properties: {
|
|
150
|
+
type: { const: 'replace_block' },
|
|
151
|
+
oldText: { type: 'string' },
|
|
152
|
+
replacementContent: { type: 'string' }
|
|
153
|
+
},
|
|
154
|
+
required: ['oldText', 'replacementContent']
|
|
155
|
+
},
|
|
140
156
|
{
|
|
141
157
|
properties: {
|
|
142
158
|
type: { const: 'insert' },
|
|
@@ -160,14 +176,6 @@ const tools = [
|
|
|
160
176
|
endLine: { type: 'number' }
|
|
161
177
|
},
|
|
162
178
|
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']
|
|
171
179
|
}
|
|
172
180
|
]
|
|
173
181
|
}
|
|
@@ -233,48 +241,6 @@ INSTRUCTIONS:
|
|
|
233
241
|
|
|
234
242
|
Always think step by step and explain your reasoning before taking actions that could affect the system.`;
|
|
235
243
|
|
|
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
|
-
|
|
278
244
|
// System prompt for the AI Assistant when using legacy function calling (JSON actions)
|
|
279
245
|
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.
|
|
280
246
|
|
|
@@ -513,77 +479,94 @@ const agentUtils = {
|
|
|
513
479
|
const edits = typeof inputPathOrObj === 'string' ? maybeEdits : inputPathOrObj?.edits;
|
|
514
480
|
if (!targetPath) throw new Error('editFile: missing path');
|
|
515
481
|
if (!edits) throw new Error('editFile: missing edits');
|
|
482
|
+
|
|
516
483
|
// Read the current file content
|
|
517
484
|
let content = await fs.readFile(targetPath, 'utf-8');
|
|
518
|
-
|
|
485
|
+
let lines = content.split('\n');
|
|
519
486
|
|
|
520
487
|
// Process each edit operation
|
|
521
|
-
for (
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
if (op.startLine
|
|
527
|
-
|
|
488
|
+
for (let i = 0; i < edits.operations.length; i++) {
|
|
489
|
+
const op = edits.operations[i];
|
|
490
|
+
try {
|
|
491
|
+
switch (op.type) {
|
|
492
|
+
case 'replace':
|
|
493
|
+
if (op.startLine !== undefined && op.endLine !== undefined) {
|
|
494
|
+
// Line-based replacement
|
|
495
|
+
if (op.startLine < 1 || op.endLine > lines.length) {
|
|
496
|
+
throw new Error(`Line numbers out of range (1-${lines.length})`);
|
|
497
|
+
}
|
|
498
|
+
if (typeof op.newText !== 'string') {
|
|
499
|
+
throw new Error(`'newText' must be a string for replace operation`);
|
|
500
|
+
}
|
|
501
|
+
const before = lines.slice(0, op.startLine - 1);
|
|
502
|
+
const after = lines.slice(op.endLine);
|
|
503
|
+
const newLines = op.newText.split('\n');
|
|
504
|
+
lines = [...before, ...newLines, ...after];
|
|
505
|
+
} else if (op.pattern) {
|
|
506
|
+
// Pattern-based replacement
|
|
507
|
+
if (typeof op.replacement !== 'string') {
|
|
508
|
+
throw new Error(`'replacement' must be a string for pattern-based replace`);
|
|
509
|
+
}
|
|
510
|
+
const regex = new RegExp(op.pattern, op.flags || '');
|
|
511
|
+
content = lines.join('\n');
|
|
512
|
+
content = content.replace(regex, op.replacement);
|
|
513
|
+
lines = content.split('\n');
|
|
514
|
+
} else {
|
|
515
|
+
throw new Error('Replace operation requires either startLine/endLine/newText or pattern/replacement');
|
|
528
516
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
// Pattern-based replacement
|
|
535
|
-
const regex = new RegExp(op.pattern, op.flags || '');
|
|
536
|
-
content = content.replace(regex, op.replacement);
|
|
537
|
-
// Update lines array for subsequent operations
|
|
538
|
-
lines.length = 0;
|
|
539
|
-
lines.push(...content.split('\n'));
|
|
540
|
-
}
|
|
541
|
-
break;
|
|
542
|
-
|
|
543
|
-
case 'insert':
|
|
544
|
-
if (op.position === 'start') {
|
|
545
|
-
lines.unshift(...op.text.split('\n'));
|
|
546
|
-
} else if (op.position === 'end') {
|
|
547
|
-
lines.push(...op.text.split('\n'));
|
|
548
|
-
} else if (op.line !== undefined) {
|
|
549
|
-
if (op.line < 1 || op.line > lines.length + 1) {
|
|
550
|
-
throw new Error(`Line number out of range (1-${lines.length + 1})`);
|
|
517
|
+
break;
|
|
518
|
+
|
|
519
|
+
case 'replace_block':
|
|
520
|
+
if (typeof op.oldText !== 'string' || typeof op.replacementContent !== 'string') {
|
|
521
|
+
throw new Error('replace_block requires both oldText and replacementContent as strings');
|
|
551
522
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
523
|
+
content = lines.join('\n');
|
|
524
|
+
if (!content.includes(op.oldText)) {
|
|
525
|
+
throw new Error('Could not find exact match for oldText in file');
|
|
526
|
+
}
|
|
527
|
+
// Check for uniqueness
|
|
528
|
+
const occurrences = content.split(op.oldText).length - 1;
|
|
529
|
+
if (occurrences > 1) {
|
|
530
|
+
throw new Error(`Found ${occurrences} occurrences of oldText. Please be more specific to ensure a unique match.`);
|
|
531
|
+
}
|
|
532
|
+
content = content.replace(op.oldText, op.replacementContent);
|
|
533
|
+
lines = content.split('\n');
|
|
534
|
+
break;
|
|
556
535
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
536
|
+
case 'insert':
|
|
537
|
+
if (typeof op.text !== 'string') {
|
|
538
|
+
throw new Error(`'text' must be a string for insert operation`);
|
|
539
|
+
}
|
|
540
|
+
if (op.position === 'start') {
|
|
541
|
+
lines.unshift(...op.text.split('\n'));
|
|
542
|
+
} else if (op.position === 'end') {
|
|
543
|
+
lines.push(...op.text.split('\n'));
|
|
544
|
+
} else if (op.line !== undefined) {
|
|
545
|
+
if (op.line < 1 || op.line > lines.length + 1) {
|
|
546
|
+
throw new Error(`Line number out of range (1-${lines.length + 1})`);
|
|
547
|
+
}
|
|
548
|
+
const insertLines = op.text.split('\n');
|
|
549
|
+
lines.splice(op.line - 1, 0, ...insertLines);
|
|
550
|
+
} else {
|
|
551
|
+
throw new Error('Insert operation requires either position (start/end) or line number');
|
|
552
|
+
}
|
|
553
|
+
break;
|
|
563
554
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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
|
-
|
|
585
|
-
default:
|
|
586
|
-
throw new Error(`Unknown operation type: ${op.type}`);
|
|
555
|
+
case 'delete':
|
|
556
|
+
if (op.startLine === undefined || op.endLine === undefined) {
|
|
557
|
+
throw new Error('Delete operation requires both startLine and endLine');
|
|
558
|
+
}
|
|
559
|
+
if (op.startLine < 1 || op.endLine > lines.length) {
|
|
560
|
+
throw new Error(`Line numbers out of range (1-${lines.length})`);
|
|
561
|
+
}
|
|
562
|
+
lines.splice(op.startLine - 1, op.endLine - op.startLine + 1);
|
|
563
|
+
break;
|
|
564
|
+
|
|
565
|
+
default:
|
|
566
|
+
throw new Error(`Unknown operation type: ${op.type}`);
|
|
567
|
+
}
|
|
568
|
+
} catch (opError) {
|
|
569
|
+
throw new Error(`Operation #${i + 1} (${op.type}) failed: ${opError.message}`);
|
|
587
570
|
}
|
|
588
571
|
}
|
|
589
572
|
|
|
@@ -592,7 +575,8 @@ const agentUtils = {
|
|
|
592
575
|
return `Successfully edited ${targetPath}`;
|
|
593
576
|
|
|
594
577
|
} catch (error) {
|
|
595
|
-
|
|
578
|
+
const pathInfo = typeof inputPathOrObj === 'string' ? inputPathOrObj : inputPathOrObj?.path;
|
|
579
|
+
throw new Error(`Failed to edit file ${pathInfo}: ${error.message}`);
|
|
596
580
|
}
|
|
597
581
|
},
|
|
598
582
|
|
|
@@ -1455,225 +1439,145 @@ async function processQuery(query, conversation = [], currentModel) {
|
|
|
1455
1439
|
}
|
|
1456
1440
|
}
|
|
1457
1441
|
|
|
1458
|
-
async function chat(rl,
|
|
1459
|
-
|
|
1460
|
-
|
|
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') {
|
|
1466
|
-
conversation.push({ role: 'system', content: TOOL_CALLING_PROMPT });
|
|
1467
|
-
} else {
|
|
1468
|
-
conversation.push({ role: 'system', content: FUNCTION_CALLING_PROMPT });
|
|
1469
|
-
}
|
|
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
|
-
|
|
1474
|
-
console.log('Type your message, or "exit" to quit.');
|
|
1475
|
-
|
|
1476
|
-
// Use async iterator pattern instead of event-based 'on' to avoid async callback issues
|
|
1477
|
-
while (true) {
|
|
1478
|
-
const input = await new Promise((resolve) => {
|
|
1479
|
-
rl.question('> ', resolve);
|
|
1480
|
-
});
|
|
1481
|
-
|
|
1482
|
-
// Handle commands
|
|
1483
|
-
if (input.toLowerCase().startsWith('/model')) {
|
|
1484
|
-
const newModel = input.split(' ')[1];
|
|
1485
|
-
if (newModel) {
|
|
1486
|
-
currentModel = newModel;
|
|
1487
|
-
let config = await readConfig() || {};
|
|
1488
|
-
config.MODEL = currentModel;
|
|
1489
|
-
await writeConfig(config);
|
|
1490
|
-
console.log(`Model changed to: ${currentModel}`);
|
|
1491
|
-
} else {
|
|
1492
|
-
console.log('Please specify a model. Usage: /model <model_name>');
|
|
1493
|
-
}
|
|
1494
|
-
continue;
|
|
1495
|
-
}
|
|
1496
|
-
|
|
1497
|
-
if (input.toLowerCase().startsWith('/thoughts')) {
|
|
1498
|
-
const parts = input.trim().split(/\s+/);
|
|
1499
|
-
const arg = parts[1] ? parts[1].toLowerCase() : '';
|
|
1500
|
-
if (arg !== 'on' && arg !== 'off') {
|
|
1501
|
-
const state = SHOW_THOUGHTS ? 'on' : 'off';
|
|
1502
|
-
ui.showInfo(`Usage: /thoughts on|off (currently ${state})`);
|
|
1503
|
-
continue;
|
|
1504
|
-
}
|
|
1505
|
-
const enable = arg === 'on';
|
|
1506
|
-
SHOW_THOUGHTS = enable;
|
|
1507
|
-
let config = await readConfig() || {};
|
|
1508
|
-
config.showThoughts = enable;
|
|
1509
|
-
await writeConfig(config);
|
|
1510
|
-
ui.showResponse(`Hidden thoughts ${enable ? 'enabled' : 'disabled'}.`);
|
|
1511
|
-
continue;
|
|
1512
|
-
}
|
|
1513
|
-
|
|
1514
|
-
if (input.toLowerCase() === '/default-model') {
|
|
1515
|
-
currentModel = 'deepseek/deepseek-chat-v3-0324:free';
|
|
1516
|
-
let config = await readConfig() || {};
|
|
1517
|
-
config.MODEL = currentModel;
|
|
1518
|
-
await writeConfig(config);
|
|
1519
|
-
console.log(`Model reset to default: ${currentModel}`);
|
|
1520
|
-
continue;
|
|
1521
|
-
}
|
|
1442
|
+
async function chat(rl, useToolCalling, initialModel) {
|
|
1443
|
+
return new Promise((resolve) => {
|
|
1444
|
+
let currentModel = initialModel;
|
|
1445
|
+
const conversation = [];
|
|
1522
1446
|
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1447
|
+
// Initialize conversation with appropriate system prompt
|
|
1448
|
+
if (useToolCalling) {
|
|
1449
|
+
conversation.push({ role: 'system', content: TOOL_CALLING_PROMPT });
|
|
1450
|
+
} else {
|
|
1451
|
+
conversation.push({ role: 'system', content: FUNCTION_CALLING_PROMPT });
|
|
1528
1452
|
}
|
|
1529
1453
|
|
|
1530
|
-
|
|
1531
|
-
if (input.toLowerCase().startsWith('/brainstorm')) {
|
|
1532
|
-
const parts = input.substring('/brainstorm'.length).trim();
|
|
1533
|
-
if (!parts) {
|
|
1534
|
-
console.log('Usage: /brainstorm <project-name> "<description>"');
|
|
1535
|
-
console.log('Example: /brainstorm "My Project" "A cool project that does things"');
|
|
1536
|
-
continue;
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
// Parse project name and description
|
|
1540
|
-
const match = parts.match(/^"?([^"]+)"?\s+"?([^"]+)"?$/) || parts.match(/^(\S+)\s+(.+)$/);
|
|
1541
|
-
if (!match) {
|
|
1542
|
-
console.log('Usage: /brainstorm <project-name> "<description>"');
|
|
1543
|
-
continue;
|
|
1544
|
-
}
|
|
1454
|
+
console.log('Type your message, or "exit" to quit.');
|
|
1545
1455
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
const outputDir = path.join(process.cwd(), projectName.replace(/\s+/g, '-').toLowerCase());
|
|
1456
|
+
rl.setPrompt('> ');
|
|
1457
|
+
rl.prompt();
|
|
1549
1458
|
|
|
1459
|
+
rl.on('line', async (input) => {
|
|
1550
1460
|
try {
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
['CLAUDE-1'] // Single agent by default
|
|
1557
|
-
);
|
|
1558
|
-
|
|
1559
|
-
ui.showSuccess(`✅ Brainstorm session created!`);
|
|
1560
|
-
console.log(` Session ID: ${session.id}`);
|
|
1561
|
-
console.log(` Output: ${outputDir}`);
|
|
1562
|
-
console.log(` Files generated: ${Object.keys(session.fileVersions).length}`);
|
|
1563
|
-
console.log(`\n Generated files:`);
|
|
1564
|
-
Object.keys(session.fileVersions).forEach(f => console.log(` - ${f}`));
|
|
1565
|
-
console.log(`\n Use "/finish <summary>" when done to complete the session.`);
|
|
1566
|
-
|
|
1567
|
-
// Store current session path for /finish command
|
|
1568
|
-
global.currentSessionDir = outputDir;
|
|
1569
|
-
} catch (error) {
|
|
1570
|
-
ui.showError(`Failed to create brainstorm: ${error.message}`);
|
|
1571
|
-
}
|
|
1572
|
-
continue;
|
|
1573
|
-
}
|
|
1574
|
-
|
|
1575
|
-
// Finish command: /finish <summary>
|
|
1576
|
-
if (input.toLowerCase().startsWith('/finish')) {
|
|
1577
|
-
const summary = input.substring('/finish'.length).trim();
|
|
1461
|
+
// Handle empty input - just reprompt
|
|
1462
|
+
if (!input || !input.trim()) {
|
|
1463
|
+
rl.prompt();
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1578
1466
|
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1467
|
+
if (input.toLowerCase().startsWith('/model')) {
|
|
1468
|
+
const newModel = input.split(' ')[1];
|
|
1469
|
+
if (newModel) {
|
|
1470
|
+
currentModel = newModel;
|
|
1471
|
+
let config = await readConfig() || {};
|
|
1472
|
+
config.MODEL = currentModel;
|
|
1473
|
+
await writeConfig(config);
|
|
1474
|
+
console.log(`Model changed to: ${currentModel}`);
|
|
1475
|
+
} else {
|
|
1476
|
+
console.log('Please specify a model. Usage: /model <model_name>');
|
|
1477
|
+
}
|
|
1478
|
+
rl.prompt();
|
|
1479
|
+
return;
|
|
1480
|
+
}
|
|
1583
1481
|
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1482
|
+
if (input.toLowerCase().startsWith('/thoughts')) {
|
|
1483
|
+
const parts = input.trim().split(/\s+/);
|
|
1484
|
+
const arg = parts[1] ? parts[1].toLowerCase() : '';
|
|
1485
|
+
if (arg !== 'on' && arg !== 'off') {
|
|
1486
|
+
const state = SHOW_THOUGHTS ? 'on' : 'off';
|
|
1487
|
+
ui.showInfo(`Usage: /thoughts on|off (currently ${state})`);
|
|
1488
|
+
rl.prompt();
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
const enable = arg === 'on';
|
|
1492
|
+
SHOW_THOUGHTS = enable;
|
|
1493
|
+
let config = await readConfig() || {};
|
|
1494
|
+
config.showThoughts = enable;
|
|
1495
|
+
await writeConfig(config);
|
|
1496
|
+
ui.showResponse(`Hidden thoughts ${enable ? 'enabled' : 'disabled'}.`);
|
|
1497
|
+
rl.prompt();
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1588
1500
|
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1501
|
+
if (input.toLowerCase() === '/default-model') {
|
|
1502
|
+
currentModel = 'deepseek/deepseek-chat-v3-0324:free';
|
|
1503
|
+
let config = await readConfig() || {};
|
|
1504
|
+
config.MODEL = currentModel;
|
|
1505
|
+
await writeConfig(config);
|
|
1506
|
+
console.log(`Model reset to default: ${currentModel}`);
|
|
1507
|
+
rl.prompt();
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1596
1510
|
|
|
1597
|
-
if (
|
|
1598
|
-
|
|
1599
|
-
console.log(
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
ui.showError(`Failed: ${result.errors.join(', ')}`);
|
|
1511
|
+
if (input.toLowerCase() === '/setup') {
|
|
1512
|
+
await runSetup(rl, true);
|
|
1513
|
+
console.log('\nSetup complete. Please restart the application to apply changes.');
|
|
1514
|
+
rl.close();
|
|
1515
|
+
return;
|
|
1603
1516
|
}
|
|
1604
|
-
} catch (error) {
|
|
1605
|
-
ui.showError(`Failed to finish session: ${error.message}`);
|
|
1606
|
-
}
|
|
1607
|
-
continue;
|
|
1608
|
-
}
|
|
1609
1517
|
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
}
|
|
1518
|
+
if (input.toLowerCase() === 'exit') {
|
|
1519
|
+
rl.close();
|
|
1520
|
+
return;
|
|
1521
|
+
}
|
|
1615
1522
|
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1523
|
+
// Direct Harmony tool-call execution from user input (bypass model)
|
|
1524
|
+
try {
|
|
1525
|
+
const seg = parseSegmentedTranscript(input);
|
|
1526
|
+
if (seg && seg.segmented && seg.recoveredToolCalls && seg.recoveredToolCalls.length) {
|
|
1527
|
+
const messages = [];
|
|
1528
|
+
if (useToolCalling) {
|
|
1529
|
+
messages.push({ role: 'system', content: TOOL_CALLING_PROMPT });
|
|
1530
|
+
} else {
|
|
1531
|
+
messages.push({ role: 'system', content: FUNCTION_CALLING_PROMPT });
|
|
1532
|
+
}
|
|
1533
|
+
messages.push({ role: 'user', content: input });
|
|
1534
|
+
ui.startThinking();
|
|
1535
|
+
const results = await handleToolCalls(seg.recoveredToolCalls, messages);
|
|
1536
|
+
ui.stopThinking();
|
|
1537
|
+
if (results.length === 1) {
|
|
1538
|
+
ui.showSuccess(`Tool '${results[0].name}' executed.`);
|
|
1539
|
+
} else {
|
|
1540
|
+
ui.showSuccess(`Executed ${results.length} tool calls.`);
|
|
1541
|
+
}
|
|
1542
|
+
// Show concise outputs
|
|
1543
|
+
results.forEach(r => {
|
|
1544
|
+
const preview = typeof r.content === 'string' ? r.content : JSON.stringify(r.content);
|
|
1545
|
+
ui.showInfo(`${r.name}: ${preview.length > 300 ? preview.slice(0, 300) + '...' : preview}`);
|
|
1546
|
+
});
|
|
1547
|
+
rl.prompt();
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
} catch (e) {
|
|
1551
|
+
// Fall through to normal processing if parsing/execution fails
|
|
1625
1552
|
}
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1553
|
+
|
|
1554
|
+
const result = useToolCalling
|
|
1555
|
+
? await processQueryWithTools(input, conversation, currentModel)
|
|
1556
|
+
: await processQuery(input, conversation, currentModel);
|
|
1629
1557
|
ui.stopThinking();
|
|
1630
|
-
|
|
1631
|
-
ui.showSuccess(`Tool '${results[0].name}' executed.`);
|
|
1632
|
-
} else {
|
|
1633
|
-
ui.showSuccess(`Executed ${results.length} tool calls.`);
|
|
1634
|
-
}
|
|
1635
|
-
// Show concise outputs
|
|
1636
|
-
results.forEach(r => {
|
|
1637
|
-
const preview = typeof r.content === 'string' ? r.content : JSON.stringify(r.content);
|
|
1638
|
-
ui.showInfo(`${r.name}: ${preview.length > 300 ? preview.slice(0, 300) + '...' : preview}`);
|
|
1639
|
-
});
|
|
1640
|
-
continue;
|
|
1641
|
-
}
|
|
1642
|
-
} catch (e) {
|
|
1643
|
-
// Fall through to normal processing if parsing/execution fails
|
|
1644
|
-
}
|
|
1558
|
+
ui.showResponse(result.response);
|
|
1645
1559
|
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
const result = useToolCalling
|
|
1649
|
-
? await processQueryWithTools(input, conversation, currentModel)
|
|
1650
|
-
: await processQuery(input, conversation, currentModel);
|
|
1651
|
-
ui.stopThinking();
|
|
1652
|
-
ui.showResponse(result.response);
|
|
1560
|
+
conversation.length = 0;
|
|
1561
|
+
result.conversation.forEach(msg => conversation.push(msg));
|
|
1653
1562
|
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
console.error(error.stack);
|
|
1563
|
+
rl.prompt();
|
|
1564
|
+
} catch (error) {
|
|
1565
|
+
ui.stopThinking();
|
|
1566
|
+
ui.showError(`Unexpected error: ${error.message}`);
|
|
1567
|
+
console.error(error);
|
|
1568
|
+
rl.prompt();
|
|
1661
1569
|
}
|
|
1662
|
-
}
|
|
1663
|
-
|
|
1570
|
+
}).on('close', () => {
|
|
1571
|
+
ui.showResponse('Goodbye!');
|
|
1572
|
+
resolve();
|
|
1573
|
+
});
|
|
1574
|
+
});
|
|
1664
1575
|
}
|
|
1665
1576
|
|
|
1666
1577
|
function askForMode(rl) {
|
|
1667
1578
|
return new Promise((resolve) => {
|
|
1668
|
-
rl.question('Select mode (1 for tool calling, 2 for function calling
|
|
1669
|
-
|
|
1670
|
-
if (mode === '3') {
|
|
1671
|
-
resolve('engineer');
|
|
1672
|
-
} else if (mode === '2') {
|
|
1673
|
-
resolve('function');
|
|
1674
|
-
} else {
|
|
1675
|
-
resolve('tool');
|
|
1676
|
-
}
|
|
1579
|
+
rl.question('Select mode (1 for tool calling, 2 for function calling): ', (answer) => {
|
|
1580
|
+
resolve(answer.trim() === '1');
|
|
1677
1581
|
});
|
|
1678
1582
|
});
|
|
1679
1583
|
}
|
|
@@ -1769,14 +1673,11 @@ async function start() {
|
|
|
1769
1673
|
console.log('Select Mode:');
|
|
1770
1674
|
console.log('1. Tool Calling (for models that support it)');
|
|
1771
1675
|
console.log('2. Function Calling (legacy)');
|
|
1772
|
-
console.log('3. Engineer Mode (autonomous engineering with Claude Code style editing)');
|
|
1773
1676
|
|
|
1774
|
-
const
|
|
1775
|
-
|
|
1776
|
-
ui.showResponse(`\nStarting in ${modeNames[selectedMode]} mode...\n`);
|
|
1677
|
+
const useToolCalling = await askForMode(rl);
|
|
1678
|
+
ui.showResponse(`\nStarting in ${useToolCalling ? 'Tool Calling' : 'Function Calling'} mode...\n`);
|
|
1777
1679
|
|
|
1778
|
-
await chat(rl,
|
|
1779
|
-
process.exit(0);
|
|
1680
|
+
await chat(rl, useToolCalling, MODEL);
|
|
1780
1681
|
} catch (error) {
|
|
1781
1682
|
ui.showError(error);
|
|
1782
1683
|
rl.close();
|
|
@@ -1784,12 +1685,4 @@ async function start() {
|
|
|
1784
1685
|
}
|
|
1785
1686
|
}
|
|
1786
1687
|
|
|
1787
|
-
// Keep the process alive and handle uncaught errors
|
|
1788
|
-
process.on('uncaughtException', (err) => {
|
|
1789
|
-
console.error('Uncaught Exception:', err.message);
|
|
1790
|
-
});
|
|
1791
|
-
process.on('unhandledRejection', (reason) => {
|
|
1792
|
-
console.error('Unhandled Rejection:', reason);
|
|
1793
|
-
});
|
|
1794
|
-
|
|
1795
1688
|
start().catch(console.error);
|