sam-coder-cli 1.0.67 → 1.0.69

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 (2) hide show
  1. package/bin/agi-cli.js +88 -10
  2. package/package.json +1 -1
package/bin/agi-cli.js CHANGED
@@ -430,9 +430,10 @@ const agentUtils = {
430
430
  },
431
431
 
432
432
  async writeFile(input, maybeContent) {
433
+ let filePath;
433
434
  try {
434
- const filePath = typeof input === 'string' ? input : input?.path;
435
- const content = typeof input === 'string' ? maybeContent : input?.content;
435
+ filePath = typeof input === 'string' ? input : input?.path;
436
+ const content = typeof input === 'string' ? maybeContent : (input?.content ?? input?.contents ?? input?.data);
436
437
  if (!filePath) throw new Error('writeFile: missing path');
437
438
  if (typeof content !== 'string') throw new Error('writeFile: missing content');
438
439
  const dir = path.dirname(filePath);
@@ -440,7 +441,8 @@ const agentUtils = {
440
441
  await fs.writeFile(filePath, content, 'utf-8');
441
442
  return `Successfully wrote to ${filePath}`;
442
443
  } catch (error) {
443
- throw new Error(`Failed to write to file ${filePath}: ${error.message}`);
444
+ const ctx = filePath || (typeof input === 'object' ? input?.path : undefined) || 'unknown path';
445
+ throw new Error(`Failed to write to file ${ctx}: ${error.message}`);
444
446
  }
445
447
  },
446
448
 
@@ -514,9 +516,54 @@ const agentUtils = {
514
516
 
515
517
  async runCommand(input) {
516
518
  try {
517
- const command = typeof input === 'string' ? input : input?.command;
518
- if (!command) throw new Error('runCommand: missing command');
519
- const { stdout, stderr } = await execAsync(command, { cwd: process.cwd() });
519
+ const isObj = typeof input === 'object' && input !== null;
520
+ let command = !isObj ? input : (input.command ?? null);
521
+ const cmd = isObj ? (input.cmd ?? input.program ?? null) : null;
522
+ const args = isObj ? (input.args ?? input.params ?? null) : null;
523
+ const script = isObj ? (input.script ?? null) : null;
524
+ const shell = isObj ? (input.shell ?? (input.powershell ? 'powershell.exe' : (input.bash ? 'bash' : undefined))) : undefined;
525
+ const cwdRaw = isObj ? input.cwd : undefined;
526
+ const envRaw = isObj ? input.env : undefined;
527
+ const timeout = isObj && typeof input.timeout === 'number' ? input.timeout : undefined;
528
+
529
+ const quoteArg = (a) => {
530
+ if (a == null) return '';
531
+ const s = String(a);
532
+ return /\s|["']/g.test(s) ? '"' + s.replace(/"/g, '\\"') + '"' : s;
533
+ };
534
+
535
+ // Build command from arrays or fields if not provided directly
536
+ if (!command && cmd) {
537
+ if (Array.isArray(cmd)) {
538
+ command = cmd.map(quoteArg).join(' ');
539
+ } else if (typeof cmd === 'string') {
540
+ command = cmd;
541
+ }
542
+ }
543
+ if (Array.isArray(command)) {
544
+ command = command.map(quoteArg).join(' ');
545
+ }
546
+ if (command && Array.isArray(args) && args.length) {
547
+ command = `${command} ${args.map(quoteArg).join(' ')}`;
548
+ }
549
+ if (script && !command) {
550
+ // If only a script is provided, run it directly under the selected shell
551
+ command = String(script);
552
+ }
553
+
554
+ if (!command || typeof command !== 'string' || command.trim().length === 0) {
555
+ throw new Error('runCommand: missing command');
556
+ }
557
+
558
+ // Resolve cwd
559
+ let cwd = process.cwd();
560
+ if (typeof cwdRaw === 'string' && cwdRaw.trim().length > 0) {
561
+ cwd = path.isAbsolute(cwdRaw) ? cwdRaw : path.join(process.cwd(), cwdRaw);
562
+ }
563
+ // Merge env
564
+ const env = envRaw && typeof envRaw === 'object' ? { ...process.env, ...envRaw } : undefined;
565
+
566
+ const { stdout, stderr } = await execAsync(command, { cwd, env, timeout, shell });
520
567
  if (stderr) {
521
568
  console.error('Command stderr:', stderr);
522
569
  }
@@ -529,10 +576,17 @@ const agentUtils = {
529
576
  async searchFiles(input) {
530
577
  try {
531
578
  const isString = typeof input === 'string';
532
- const pattern = isString ? input : input?.pattern;
533
- const basePathRaw = isString ? null : (input?.path || null);
579
+ let pattern = isString ? input : input?.pattern;
580
+ let basePathRaw = isString ? null : (input?.path || null);
534
581
  const recursive = isString ? true : (input?.recursive !== false);
535
582
 
583
+ // Handle wildcards embedded in the path (e.g., C:\\foo\\bar\\*)
584
+ const hasWildcard = (p) => typeof p === 'string' && /[\*\?]/.test(p);
585
+ if (!pattern && hasWildcard(basePathRaw)) {
586
+ pattern = path.basename(basePathRaw);
587
+ basePathRaw = path.dirname(basePathRaw);
588
+ }
589
+
536
590
  const basePath = basePathRaw
537
591
  ? (path.isAbsolute(basePathRaw) ? basePathRaw : path.join(process.cwd(), basePathRaw))
538
592
  : process.cwd();
@@ -542,12 +596,36 @@ const agentUtils = {
542
596
  const matchByPattern = (name) => {
543
597
  if (!pattern) return true; // if no pattern, match all
544
598
  const escaped = pattern.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&');
545
- const regex = new RegExp('^' + escaped.replace(/\*/g, '.*') + '$');
599
+ const regex = new RegExp('^' + escaped.replace(/\*/g, '.*').replace(/\?/g, '.') + '$');
546
600
  return regex.test(name);
547
601
  };
548
602
 
603
+ // If base path is a file, test it directly
604
+ try {
605
+ const stat = await fs.stat(basePath);
606
+ if (stat.isFile()) {
607
+ if (matchByPattern(path.basename(basePath))) {
608
+ results.push(basePath);
609
+ }
610
+ return results.length > 0
611
+ ? `Found ${results.length} files:\n${results.join('\n')}`
612
+ : 'No files found';
613
+ }
614
+ } catch (e) {
615
+ if (e && e.code === 'ENOENT') {
616
+ return 'No files found';
617
+ }
618
+ // Non-ENOENT errors are handled by traversal below
619
+ }
620
+
549
621
  const search = async (dir) => {
550
- const entries = await fs.readdir(dir, { withFileTypes: true });
622
+ let entries;
623
+ try {
624
+ entries = await fs.readdir(dir, { withFileTypes: true });
625
+ } catch (e) {
626
+ if (e && e.code === 'ENOENT') return; // directory missing
627
+ throw e;
628
+ }
551
629
  for (const entry of entries) {
552
630
  const fullPath = path.join(dir, entry.name);
553
631
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sam-coder-cli",
3
- "version": "1.0.67",
3
+ "version": "1.0.69",
4
4
  "description": "SAM-CODER: An animated command-line AI assistant with agency capabilities.",
5
5
  "main": "bin/agi-cli.js",
6
6
  "bin": {