sam-coder-cli 1.0.66 → 1.0.67
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 +85 -39
- package/package.json +1 -1
package/bin/agi-cli.js
CHANGED
|
@@ -418,8 +418,10 @@ You are not just executing commands - you are solving problems intelligently. Ev
|
|
|
418
418
|
|
|
419
419
|
// Agent utilities
|
|
420
420
|
const agentUtils = {
|
|
421
|
-
async readFile(
|
|
421
|
+
async readFile(input) {
|
|
422
422
|
try {
|
|
423
|
+
const filePath = typeof input === 'string' ? input : input?.path;
|
|
424
|
+
if (!filePath) throw new Error('readFile: missing path');
|
|
423
425
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
424
426
|
return content;
|
|
425
427
|
} catch (error) {
|
|
@@ -427,9 +429,12 @@ const agentUtils = {
|
|
|
427
429
|
}
|
|
428
430
|
},
|
|
429
431
|
|
|
430
|
-
async writeFile(
|
|
432
|
+
async writeFile(input, maybeContent) {
|
|
431
433
|
try {
|
|
432
|
-
|
|
434
|
+
const filePath = typeof input === 'string' ? input : input?.path;
|
|
435
|
+
const content = typeof input === 'string' ? maybeContent : input?.content;
|
|
436
|
+
if (!filePath) throw new Error('writeFile: missing path');
|
|
437
|
+
if (typeof content !== 'string') throw new Error('writeFile: missing content');
|
|
433
438
|
const dir = path.dirname(filePath);
|
|
434
439
|
await fs.mkdir(dir, { recursive: true });
|
|
435
440
|
await fs.writeFile(filePath, content, 'utf-8');
|
|
@@ -439,10 +444,14 @@ const agentUtils = {
|
|
|
439
444
|
}
|
|
440
445
|
},
|
|
441
446
|
|
|
442
|
-
async editFile(
|
|
447
|
+
async editFile(inputPathOrObj, maybeEdits) {
|
|
443
448
|
try {
|
|
449
|
+
const targetPath = typeof inputPathOrObj === 'string' ? inputPathOrObj : inputPathOrObj?.path;
|
|
450
|
+
const edits = typeof inputPathOrObj === 'string' ? maybeEdits : inputPathOrObj?.edits;
|
|
451
|
+
if (!targetPath) throw new Error('editFile: missing path');
|
|
452
|
+
if (!edits) throw new Error('editFile: missing edits');
|
|
444
453
|
// Read the current file content
|
|
445
|
-
let content = await fs.readFile(
|
|
454
|
+
let content = await fs.readFile(targetPath, 'utf-8');
|
|
446
455
|
const lines = content.split('\n');
|
|
447
456
|
|
|
448
457
|
// Process each edit operation
|
|
@@ -495,16 +504,18 @@ const agentUtils = {
|
|
|
495
504
|
}
|
|
496
505
|
|
|
497
506
|
// Write the modified content back to the file
|
|
498
|
-
await fs.writeFile(
|
|
499
|
-
return `Successfully edited ${
|
|
507
|
+
await fs.writeFile(targetPath, lines.join('\n'), 'utf-8');
|
|
508
|
+
return `Successfully edited ${targetPath}`;
|
|
500
509
|
|
|
501
510
|
} catch (error) {
|
|
502
|
-
throw new Error(`Failed to edit file ${path}: ${error.message}`);
|
|
511
|
+
throw new Error(`Failed to edit file ${typeof inputPathOrObj === 'string' ? inputPathOrObj : inputPathOrObj?.path}: ${error.message}`);
|
|
503
512
|
}
|
|
504
513
|
},
|
|
505
514
|
|
|
506
|
-
async runCommand(
|
|
515
|
+
async runCommand(input) {
|
|
507
516
|
try {
|
|
517
|
+
const command = typeof input === 'string' ? input : input?.command;
|
|
518
|
+
if (!command) throw new Error('runCommand: missing command');
|
|
508
519
|
const { stdout, stderr } = await execAsync(command, { cwd: process.cwd() });
|
|
509
520
|
if (stderr) {
|
|
510
521
|
console.error('Command stderr:', stderr);
|
|
@@ -515,40 +526,46 @@ const agentUtils = {
|
|
|
515
526
|
}
|
|
516
527
|
},
|
|
517
528
|
|
|
518
|
-
async searchFiles(
|
|
529
|
+
async searchFiles(input) {
|
|
519
530
|
try {
|
|
520
|
-
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
531
|
+
const isString = typeof input === 'string';
|
|
532
|
+
const pattern = isString ? input : input?.pattern;
|
|
533
|
+
const basePathRaw = isString ? null : (input?.path || null);
|
|
534
|
+
const recursive = isString ? true : (input?.recursive !== false);
|
|
535
|
+
|
|
536
|
+
const basePath = basePathRaw
|
|
537
|
+
? (path.isAbsolute(basePathRaw) ? basePathRaw : path.join(process.cwd(), basePathRaw))
|
|
538
|
+
: process.cwd();
|
|
525
539
|
|
|
526
|
-
const regex = globToRegex(pattern);
|
|
527
|
-
const searchDir = process.cwd();
|
|
528
540
|
const results = [];
|
|
529
541
|
|
|
530
|
-
|
|
542
|
+
const matchByPattern = (name) => {
|
|
543
|
+
if (!pattern) return true; // if no pattern, match all
|
|
544
|
+
const escaped = pattern.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
545
|
+
const regex = new RegExp('^' + escaped.replace(/\*/g, '.*') + '$');
|
|
546
|
+
return regex.test(name);
|
|
547
|
+
};
|
|
548
|
+
|
|
531
549
|
const search = async (dir) => {
|
|
532
550
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
533
|
-
|
|
534
551
|
for (const entry of entries) {
|
|
535
552
|
const fullPath = path.join(dir, entry.name);
|
|
536
|
-
|
|
537
553
|
try {
|
|
538
554
|
if (entry.isDirectory()) {
|
|
539
|
-
|
|
540
|
-
|
|
555
|
+
if (recursive) {
|
|
556
|
+
await search(fullPath);
|
|
557
|
+
}
|
|
558
|
+
} else if (matchByPattern(entry.name)) {
|
|
541
559
|
results.push(fullPath);
|
|
542
560
|
}
|
|
543
|
-
} catch (
|
|
544
|
-
// Skip files/directories we can't access
|
|
561
|
+
} catch (_) {
|
|
545
562
|
continue;
|
|
546
563
|
}
|
|
547
564
|
}
|
|
548
565
|
};
|
|
549
566
|
|
|
550
|
-
await search(
|
|
551
|
-
return results.length > 0
|
|
567
|
+
await search(basePath);
|
|
568
|
+
return results.length > 0
|
|
552
569
|
? `Found ${results.length} files:\n${results.join('\n')}`
|
|
553
570
|
: 'No files found';
|
|
554
571
|
} catch (error) {
|
|
@@ -789,8 +806,8 @@ function parseSegmentedTranscript(text) {
|
|
|
789
806
|
|
|
790
807
|
// If no blocks matched, return original
|
|
791
808
|
if (visibleParts.length === 0 && thoughts.length === 0 && commentaryParts.length === 0) {
|
|
792
|
-
// Try channel-only segments:
|
|
793
|
-
const chanRegex = /\<\|channel\|>\s*([a-zA-Z]+)\s*(?:to=([^\s<]+))?\s*(?:\<\|constrain\|>(\w+))?\s*\<\|message\|>([\s\S]*?)(?:\<\|end\|>|\<\|call\|>|\<\|return
|
|
809
|
+
// Try channel-only segments: allow missing trailing stop tokens; stop at next token or end
|
|
810
|
+
const chanRegex = /\<\|channel\|>\s*([a-zA-Z]+)\s*(?:to=([^\s<]+))?\s*(?:\<\|constrain\|>(\w+))?\s*\<\|message\|>([\s\S]*?)(?=(?:\<\|start\|>|\<\|channel\|>|\<\|end\|>|\<\|call\|>|\<\|return\|>|$))/gi;
|
|
794
811
|
let anyChannel = false;
|
|
795
812
|
let commsWithRecipients = [];
|
|
796
813
|
while ((match = chanRegex.exec(text)) !== null) {
|
|
@@ -825,16 +842,8 @@ function parseSegmentedTranscript(text) {
|
|
|
825
842
|
if (funcName.startsWith('functions.')) {
|
|
826
843
|
funcName = funcName.slice('functions.'.length);
|
|
827
844
|
}
|
|
828
|
-
// parse args
|
|
829
|
-
let args =
|
|
830
|
-
if (item.constraint === 'json' || item.body.startsWith('{') || item.body.startsWith('[')) {
|
|
831
|
-
try {
|
|
832
|
-
const parsed = JSON.parse(item.body);
|
|
833
|
-
args = parsed;
|
|
834
|
-
} catch (_) {
|
|
835
|
-
// ignore parse error; leave empty
|
|
836
|
-
}
|
|
837
|
-
}
|
|
845
|
+
// parse args (robust: try code blocks, then first JSON object)
|
|
846
|
+
let args = extractArgsJson(item.body);
|
|
838
847
|
recoveredToolCalls.push({
|
|
839
848
|
id: `inline-${recoveredToolCalls.length + 1}`,
|
|
840
849
|
type: 'function',
|
|
@@ -882,6 +891,34 @@ function parseSegmentedTranscript(text) {
|
|
|
882
891
|
};
|
|
883
892
|
}
|
|
884
893
|
|
|
894
|
+
// Extract first JSON object/array from arbitrary text, including fenced code blocks
|
|
895
|
+
function extractArgsJson(text) {
|
|
896
|
+
if (!text || typeof text !== 'string') return {};
|
|
897
|
+
// Prefer fenced code block
|
|
898
|
+
const fence = text.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
899
|
+
if (fence && fence[1]) {
|
|
900
|
+
try { return JSON.parse(fence[1].trim()); } catch (_) {}
|
|
901
|
+
}
|
|
902
|
+
// Try straightforward parse
|
|
903
|
+
const trimmed = text.trim();
|
|
904
|
+
if ((trimmed.startsWith('{') && trimmed.includes('}')) || (trimmed.startsWith('[') && trimmed.includes(']'))) {
|
|
905
|
+
try { return JSON.parse(trimmed); } catch (_) {}
|
|
906
|
+
}
|
|
907
|
+
// Fallback: find first {...} minimally
|
|
908
|
+
const braceRegex = /\{[\s\S]*?\}/g;
|
|
909
|
+
const m = braceRegex.exec(text);
|
|
910
|
+
if (m && m[0]) {
|
|
911
|
+
try { return JSON.parse(m[0]); } catch (_) {}
|
|
912
|
+
}
|
|
913
|
+
// Fallback: find first [...] minimally
|
|
914
|
+
const arrRegex = /\[[\s\S]*?\]/g;
|
|
915
|
+
const a = arrRegex.exec(text);
|
|
916
|
+
if (a && a[0]) {
|
|
917
|
+
try { return JSON.parse(a[0]); } catch (_) {}
|
|
918
|
+
}
|
|
919
|
+
return {};
|
|
920
|
+
}
|
|
921
|
+
|
|
885
922
|
// Call OpenRouter API with tool calling
|
|
886
923
|
async function callOpenRouter(messages, currentModel, useJson = false) {
|
|
887
924
|
const apiKey = OPENROUTER_API_KEY;
|
|
@@ -1072,7 +1109,16 @@ async function handleToolCalls(toolCalls, messages) {
|
|
|
1072
1109
|
throw new Error(`Tool '${functionName}' not found`);
|
|
1073
1110
|
}
|
|
1074
1111
|
|
|
1075
|
-
|
|
1112
|
+
let result;
|
|
1113
|
+
if (Array.isArray(args)) {
|
|
1114
|
+
result = await agentUtils[functionName](...args);
|
|
1115
|
+
} else if (args && typeof args === 'object') {
|
|
1116
|
+
result = await agentUtils[functionName](args);
|
|
1117
|
+
} else if (args !== undefined) {
|
|
1118
|
+
result = await agentUtils[functionName](args);
|
|
1119
|
+
} else {
|
|
1120
|
+
result = await agentUtils[functionName]();
|
|
1121
|
+
}
|
|
1076
1122
|
console.log('✅ Tool executed successfully');
|
|
1077
1123
|
|
|
1078
1124
|
const resultContent = typeof result === 'string' ? result : JSON.stringify(result);
|