sam-coder-cli 2.0.7 → 2.0.9
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 +61 -13
- package/bin/ui.js +12 -5
- package/package.json +1 -1
package/bin/agi-cli.js
CHANGED
|
@@ -157,17 +157,17 @@ const tools = [
|
|
|
157
157
|
properties: {
|
|
158
158
|
type: { const: 'insert' },
|
|
159
159
|
position: { type: 'string', enum: ['start', 'end'] },
|
|
160
|
-
|
|
160
|
+
newText: { type: 'string' }
|
|
161
161
|
},
|
|
162
|
-
required: ['position', '
|
|
162
|
+
required: ['position', 'newText']
|
|
163
163
|
},
|
|
164
164
|
{
|
|
165
165
|
properties: {
|
|
166
166
|
type: { const: 'insert' },
|
|
167
167
|
line: { type: 'number' },
|
|
168
|
-
|
|
168
|
+
newText: { type: 'string' }
|
|
169
169
|
},
|
|
170
|
-
required: ['line', '
|
|
170
|
+
required: ['line', 'newText']
|
|
171
171
|
},
|
|
172
172
|
{
|
|
173
173
|
properties: {
|
|
@@ -188,6 +188,23 @@ const tools = [
|
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
},
|
|
191
|
+
{
|
|
192
|
+
type: 'function',
|
|
193
|
+
function: {
|
|
194
|
+
name: 'replace_lines',
|
|
195
|
+
description: 'Replace a range of lines in a file with new content',
|
|
196
|
+
parameters: {
|
|
197
|
+
type: 'object',
|
|
198
|
+
properties: {
|
|
199
|
+
path: { type: 'string', description: 'Path to the file to edit' },
|
|
200
|
+
startLine: { type: 'number', description: 'Starting line number (1-based)' },
|
|
201
|
+
endLine: { type: 'number', description: 'Ending line number (1-based, inclusive)' },
|
|
202
|
+
newContent: { type: 'string', description: 'The new content to replace the lines with' }
|
|
203
|
+
},
|
|
204
|
+
required: ['path', 'startLine', 'endLine', 'newContent']
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
191
208
|
{
|
|
192
209
|
type: 'function',
|
|
193
210
|
function: {
|
|
@@ -495,12 +512,13 @@ const agentUtils = {
|
|
|
495
512
|
if (op.startLine < 1 || op.endLine > lines.length) {
|
|
496
513
|
throw new Error(`Line numbers out of range (1-${lines.length})`);
|
|
497
514
|
}
|
|
498
|
-
|
|
515
|
+
const newText = op.newText ?? op.text;
|
|
516
|
+
if (typeof newText !== 'string') {
|
|
499
517
|
throw new Error(`'newText' must be a string for replace operation`);
|
|
500
518
|
}
|
|
501
519
|
const before = lines.slice(0, op.startLine - 1);
|
|
502
520
|
const after = lines.slice(op.endLine);
|
|
503
|
-
const newLines =
|
|
521
|
+
const newLines = newText.split('\n');
|
|
504
522
|
lines = [...before, ...newLines, ...after];
|
|
505
523
|
} else if (op.pattern) {
|
|
506
524
|
// Pattern-based replacement
|
|
@@ -517,7 +535,7 @@ const agentUtils = {
|
|
|
517
535
|
break;
|
|
518
536
|
|
|
519
537
|
case 'replace_block':
|
|
520
|
-
if (typeof op.oldText !== 'string' || typeof op.replacementContent !== 'string') {
|
|
538
|
+
if (typeof op.oldText !== 'string' || typeof (op.replacementContent ?? op.newText) !== 'string') {
|
|
521
539
|
throw new Error('replace_block requires both oldText and replacementContent as strings');
|
|
522
540
|
}
|
|
523
541
|
content = lines.join('\n');
|
|
@@ -529,23 +547,24 @@ const agentUtils = {
|
|
|
529
547
|
if (occurrences > 1) {
|
|
530
548
|
throw new Error(`Found ${occurrences} occurrences of oldText. Please be more specific to ensure a unique match.`);
|
|
531
549
|
}
|
|
532
|
-
content = content.replace(op.oldText, op.replacementContent);
|
|
550
|
+
content = content.replace(op.oldText, op.replacementContent ?? op.newText);
|
|
533
551
|
lines = content.split('\n');
|
|
534
552
|
break;
|
|
535
553
|
|
|
536
554
|
case 'insert':
|
|
537
|
-
|
|
538
|
-
|
|
555
|
+
const insertText = op.newText ?? op.text;
|
|
556
|
+
if (typeof insertText !== 'string') {
|
|
557
|
+
throw new Error(`'newText' must be a string for insert operation`);
|
|
539
558
|
}
|
|
540
559
|
if (op.position === 'start') {
|
|
541
|
-
lines.unshift(...
|
|
560
|
+
lines.unshift(...insertText.split('\n'));
|
|
542
561
|
} else if (op.position === 'end') {
|
|
543
|
-
lines.push(...
|
|
562
|
+
lines.push(...insertText.split('\n'));
|
|
544
563
|
} else if (op.line !== undefined) {
|
|
545
564
|
if (op.line < 1 || op.line > lines.length + 1) {
|
|
546
565
|
throw new Error(`Line number out of range (1-${lines.length + 1})`);
|
|
547
566
|
}
|
|
548
|
-
const insertLines =
|
|
567
|
+
const insertLines = insertText.split('\n');
|
|
549
568
|
lines.splice(op.line - 1, 0, ...insertLines);
|
|
550
569
|
} else {
|
|
551
570
|
throw new Error('Insert operation requires either position (start/end) or line number');
|
|
@@ -580,6 +599,33 @@ const agentUtils = {
|
|
|
580
599
|
}
|
|
581
600
|
},
|
|
582
601
|
|
|
602
|
+
async replace_lines(input) {
|
|
603
|
+
try {
|
|
604
|
+
const { path: filePath, startLine, endLine, newContent } = input;
|
|
605
|
+
if (!filePath) throw new Error('replace_lines: missing path');
|
|
606
|
+
if (startLine === undefined || endLine === undefined) throw new Error('replace_lines: missing line range');
|
|
607
|
+
if (typeof newContent !== 'string') throw new Error('replace_lines: missing newContent');
|
|
608
|
+
|
|
609
|
+
let content = await fs.readFile(filePath, 'utf-8');
|
|
610
|
+
const lines = content.split('\n');
|
|
611
|
+
|
|
612
|
+
if (startLine < 1 || endLine > lines.length || startLine > endLine) {
|
|
613
|
+
throw new Error(`Line numbers out of range (1-${lines.length}) or invalid range`);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const before = lines.slice(0, startLine - 1);
|
|
617
|
+
const after = lines.slice(endLine);
|
|
618
|
+
const newLines = newContent.split('\n');
|
|
619
|
+
|
|
620
|
+
const updatedLines = [...before, ...newLines, ...after];
|
|
621
|
+
await fs.writeFile(filePath, updatedLines.join('\n'), 'utf-8');
|
|
622
|
+
|
|
623
|
+
return `Successfully replaced lines ${startLine}-${endLine} in ${filePath}`;
|
|
624
|
+
} catch (error) {
|
|
625
|
+
throw new Error(`replace_lines failed: ${error.message}`);
|
|
626
|
+
}
|
|
627
|
+
},
|
|
628
|
+
|
|
583
629
|
async runCommand(input) {
|
|
584
630
|
try {
|
|
585
631
|
const isObj = typeof input === 'object' && input !== null;
|
|
@@ -1301,6 +1347,8 @@ async function executeAction(action) {
|
|
|
1301
1347
|
return await agentUtils.writeFile(data.path, data.content);
|
|
1302
1348
|
case 'edit':
|
|
1303
1349
|
return await agentUtils.editFile(data.path, data.edits);
|
|
1350
|
+
case 'replace_lines':
|
|
1351
|
+
return await agentUtils.replace_lines(data);
|
|
1304
1352
|
case 'command':
|
|
1305
1353
|
return await agentUtils.runCommand(data.command);
|
|
1306
1354
|
case 'search':
|
package/bin/ui.js
CHANGED
|
@@ -10,17 +10,24 @@ const spinner = ora({
|
|
|
10
10
|
discardStdin: false // Critical: prevents stdin interference
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
// ASCII Art for AGI header
|
|
13
|
+
// ASCII Art for AGI header - Shoggoth mascot
|
|
14
|
+
const SHOGGOTH = `
|
|
15
|
+
▄▄▄▄▄▄▄
|
|
16
|
+
█ ● ● █
|
|
17
|
+
█░░░░░░░█
|
|
18
|
+
▀▀▀▀▀▀▀
|
|
19
|
+
║ ║ ║
|
|
20
|
+
`;
|
|
21
|
+
|
|
14
22
|
const AGI_HEADER = `
|
|
15
23
|
╔═══════════════════════════════════════╗
|
|
16
|
-
║ A
|
|
17
|
-
║ Artificial General Intelligence ║
|
|
18
|
-
║ Command Line Interface ║
|
|
24
|
+
║ S A M - C O D E R ║
|
|
19
25
|
╚═══════════════════════════════════════╝
|
|
20
26
|
`;
|
|
21
27
|
|
|
22
28
|
function showHeader() {
|
|
23
|
-
console.log(chalk.
|
|
29
|
+
console.log(chalk.redBright(SHOGGOTH));
|
|
30
|
+
console.log(chalk.red.bold(AGI_HEADER));
|
|
24
31
|
console.log(chalk.gray('─'.repeat(41)));
|
|
25
32
|
console.log();
|
|
26
33
|
}
|