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.
Files changed (3) hide show
  1. package/bin/agi-cli.js +61 -13
  2. package/bin/ui.js +12 -5
  3. 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
- text: { type: 'string' }
160
+ newText: { type: 'string' }
161
161
  },
162
- required: ['position', 'text']
162
+ required: ['position', 'newText']
163
163
  },
164
164
  {
165
165
  properties: {
166
166
  type: { const: 'insert' },
167
167
  line: { type: 'number' },
168
- text: { type: 'string' }
168
+ newText: { type: 'string' }
169
169
  },
170
- required: ['line', 'text']
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
- if (typeof op.newText !== 'string') {
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 = op.newText.split('\n');
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
- if (typeof op.text !== 'string') {
538
- throw new Error(`'text' must be a string for insert operation`);
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(...op.text.split('\n'));
560
+ lines.unshift(...insertText.split('\n'));
542
561
  } else if (op.position === 'end') {
543
- lines.push(...op.text.split('\n'));
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 = op.text.split('\n');
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 G I - C L I
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.cyan.bold(AGI_HEADER));
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sam-coder-cli",
3
- "version": "2.0.7",
3
+ "version": "2.0.9",
4
4
  "description": "SAM-CODER: An animated command-line AI assistant with agency capabilities.",
5
5
  "main": "bin/agi-cli.js",
6
6
  "bin": {