sam-coder-cli 1.0.66 → 1.0.68
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 +116 -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,77 @@ const agentUtils = {
|
|
|
515
526
|
}
|
|
516
527
|
},
|
|
517
528
|
|
|
518
|
-
async searchFiles(
|
|
529
|
+
async searchFiles(input) {
|
|
519
530
|
try {
|
|
520
|
-
|
|
521
|
-
|
|
531
|
+
const isString = typeof input === 'string';
|
|
532
|
+
let pattern = isString ? input : input?.pattern;
|
|
533
|
+
let basePathRaw = isString ? null : (input?.path || null);
|
|
534
|
+
const recursive = isString ? true : (input?.recursive !== false);
|
|
535
|
+
|
|
536
|
+
// Handle wildcards embedded in the path (e.g., C:\\foo\\bar\\*)
|
|
537
|
+
const hasWildcard = (p) => typeof p === 'string' && /[\*\?]/.test(p);
|
|
538
|
+
if (!pattern && hasWildcard(basePathRaw)) {
|
|
539
|
+
pattern = path.basename(basePathRaw);
|
|
540
|
+
basePathRaw = path.dirname(basePathRaw);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const basePath = basePathRaw
|
|
544
|
+
? (path.isAbsolute(basePathRaw) ? basePathRaw : path.join(process.cwd(), basePathRaw))
|
|
545
|
+
: process.cwd();
|
|
546
|
+
|
|
547
|
+
const results = [];
|
|
548
|
+
|
|
549
|
+
const matchByPattern = (name) => {
|
|
550
|
+
if (!pattern) return true; // if no pattern, match all
|
|
522
551
|
const escaped = pattern.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
523
|
-
|
|
552
|
+
const regex = new RegExp('^' + escaped.replace(/\*/g, '.*').replace(/\?/g, '.') + '$');
|
|
553
|
+
return regex.test(name);
|
|
524
554
|
};
|
|
525
555
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
556
|
+
// If base path is a file, test it directly
|
|
557
|
+
try {
|
|
558
|
+
const stat = await fs.stat(basePath);
|
|
559
|
+
if (stat.isFile()) {
|
|
560
|
+
if (matchByPattern(path.basename(basePath))) {
|
|
561
|
+
results.push(basePath);
|
|
562
|
+
}
|
|
563
|
+
return results.length > 0
|
|
564
|
+
? `Found ${results.length} files:\n${results.join('\n')}`
|
|
565
|
+
: 'No files found';
|
|
566
|
+
}
|
|
567
|
+
} catch (e) {
|
|
568
|
+
if (e && e.code === 'ENOENT') {
|
|
569
|
+
return 'No files found';
|
|
570
|
+
}
|
|
571
|
+
// Non-ENOENT errors are handled by traversal below
|
|
572
|
+
}
|
|
529
573
|
|
|
530
|
-
// Recursive directory search
|
|
531
574
|
const search = async (dir) => {
|
|
532
|
-
|
|
533
|
-
|
|
575
|
+
let entries;
|
|
576
|
+
try {
|
|
577
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
578
|
+
} catch (e) {
|
|
579
|
+
if (e && e.code === 'ENOENT') return; // directory missing
|
|
580
|
+
throw e;
|
|
581
|
+
}
|
|
534
582
|
for (const entry of entries) {
|
|
535
583
|
const fullPath = path.join(dir, entry.name);
|
|
536
|
-
|
|
537
584
|
try {
|
|
538
585
|
if (entry.isDirectory()) {
|
|
539
|
-
|
|
540
|
-
|
|
586
|
+
if (recursive) {
|
|
587
|
+
await search(fullPath);
|
|
588
|
+
}
|
|
589
|
+
} else if (matchByPattern(entry.name)) {
|
|
541
590
|
results.push(fullPath);
|
|
542
591
|
}
|
|
543
|
-
} catch (
|
|
544
|
-
// Skip files/directories we can't access
|
|
592
|
+
} catch (_) {
|
|
545
593
|
continue;
|
|
546
594
|
}
|
|
547
595
|
}
|
|
548
596
|
};
|
|
549
597
|
|
|
550
|
-
await search(
|
|
551
|
-
return results.length > 0
|
|
598
|
+
await search(basePath);
|
|
599
|
+
return results.length > 0
|
|
552
600
|
? `Found ${results.length} files:\n${results.join('\n')}`
|
|
553
601
|
: 'No files found';
|
|
554
602
|
} catch (error) {
|
|
@@ -789,8 +837,8 @@ function parseSegmentedTranscript(text) {
|
|
|
789
837
|
|
|
790
838
|
// If no blocks matched, return original
|
|
791
839
|
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
|
|
840
|
+
// Try channel-only segments: allow missing trailing stop tokens; stop at next token or end
|
|
841
|
+
const chanRegex = /\<\|channel\|>\s*([a-zA-Z]+)\s*(?:to=([^\s<]+))?\s*(?:\<\|constrain\|>(\w+))?\s*\<\|message\|>([\s\S]*?)(?=(?:\<\|start\|>|\<\|channel\|>|\<\|end\|>|\<\|call\|>|\<\|return\|>|$))/gi;
|
|
794
842
|
let anyChannel = false;
|
|
795
843
|
let commsWithRecipients = [];
|
|
796
844
|
while ((match = chanRegex.exec(text)) !== null) {
|
|
@@ -825,16 +873,8 @@ function parseSegmentedTranscript(text) {
|
|
|
825
873
|
if (funcName.startsWith('functions.')) {
|
|
826
874
|
funcName = funcName.slice('functions.'.length);
|
|
827
875
|
}
|
|
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
|
-
}
|
|
876
|
+
// parse args (robust: try code blocks, then first JSON object)
|
|
877
|
+
let args = extractArgsJson(item.body);
|
|
838
878
|
recoveredToolCalls.push({
|
|
839
879
|
id: `inline-${recoveredToolCalls.length + 1}`,
|
|
840
880
|
type: 'function',
|
|
@@ -882,6 +922,34 @@ function parseSegmentedTranscript(text) {
|
|
|
882
922
|
};
|
|
883
923
|
}
|
|
884
924
|
|
|
925
|
+
// Extract first JSON object/array from arbitrary text, including fenced code blocks
|
|
926
|
+
function extractArgsJson(text) {
|
|
927
|
+
if (!text || typeof text !== 'string') return {};
|
|
928
|
+
// Prefer fenced code block
|
|
929
|
+
const fence = text.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
930
|
+
if (fence && fence[1]) {
|
|
931
|
+
try { return JSON.parse(fence[1].trim()); } catch (_) {}
|
|
932
|
+
}
|
|
933
|
+
// Try straightforward parse
|
|
934
|
+
const trimmed = text.trim();
|
|
935
|
+
if ((trimmed.startsWith('{') && trimmed.includes('}')) || (trimmed.startsWith('[') && trimmed.includes(']'))) {
|
|
936
|
+
try { return JSON.parse(trimmed); } catch (_) {}
|
|
937
|
+
}
|
|
938
|
+
// Fallback: find first {...} minimally
|
|
939
|
+
const braceRegex = /\{[\s\S]*?\}/g;
|
|
940
|
+
const m = braceRegex.exec(text);
|
|
941
|
+
if (m && m[0]) {
|
|
942
|
+
try { return JSON.parse(m[0]); } catch (_) {}
|
|
943
|
+
}
|
|
944
|
+
// Fallback: find first [...] minimally
|
|
945
|
+
const arrRegex = /\[[\s\S]*?\]/g;
|
|
946
|
+
const a = arrRegex.exec(text);
|
|
947
|
+
if (a && a[0]) {
|
|
948
|
+
try { return JSON.parse(a[0]); } catch (_) {}
|
|
949
|
+
}
|
|
950
|
+
return {};
|
|
951
|
+
}
|
|
952
|
+
|
|
885
953
|
// Call OpenRouter API with tool calling
|
|
886
954
|
async function callOpenRouter(messages, currentModel, useJson = false) {
|
|
887
955
|
const apiKey = OPENROUTER_API_KEY;
|
|
@@ -1072,7 +1140,16 @@ async function handleToolCalls(toolCalls, messages) {
|
|
|
1072
1140
|
throw new Error(`Tool '${functionName}' not found`);
|
|
1073
1141
|
}
|
|
1074
1142
|
|
|
1075
|
-
|
|
1143
|
+
let result;
|
|
1144
|
+
if (Array.isArray(args)) {
|
|
1145
|
+
result = await agentUtils[functionName](...args);
|
|
1146
|
+
} else if (args && typeof args === 'object') {
|
|
1147
|
+
result = await agentUtils[functionName](args);
|
|
1148
|
+
} else if (args !== undefined) {
|
|
1149
|
+
result = await agentUtils[functionName](args);
|
|
1150
|
+
} else {
|
|
1151
|
+
result = await agentUtils[functionName]();
|
|
1152
|
+
}
|
|
1076
1153
|
console.log('✅ Tool executed successfully');
|
|
1077
1154
|
|
|
1078
1155
|
const resultContent = typeof result === 'string' ? result : JSON.stringify(result);
|