deepagents 1.7.2 → 1.7.3

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/dist/index.cjs CHANGED
@@ -2021,7 +2021,8 @@ function parseSkillMetadataFromContent(content, skillPath, directoryName) {
2021
2021
  */
2022
2022
  async function listSkillsFromBackend(backend, sourcePath) {
2023
2023
  const skills = [];
2024
- const normalizedPath = sourcePath.endsWith("/") || sourcePath.endsWith("\\") ? sourcePath : `${sourcePath}/`;
2024
+ const pathSep = sourcePath.includes("\\") ? "\\" : "/";
2025
+ const normalizedPath = sourcePath.endsWith("/") || sourcePath.endsWith("\\") ? sourcePath : `${sourcePath}${pathSep}`;
2025
2026
  let fileInfos;
2026
2027
  try {
2027
2028
  fileInfos = await backend.lsInfo(normalizedPath);
@@ -2034,7 +2035,7 @@ async function listSkillsFromBackend(backend, sourcePath) {
2034
2035
  }));
2035
2036
  for (const entry of entries) {
2036
2037
  if (entry.type !== "directory") continue;
2037
- const skillMdPath = `${normalizedPath}${entry.name}/SKILL.md`;
2038
+ const skillMdPath = `${normalizedPath}${entry.name}${pathSep}SKILL.md`;
2038
2039
  let content;
2039
2040
  if (backend.downloadFiles) {
2040
2041
  const results = await backend.downloadFiles([skillMdPath]);
@@ -3335,11 +3336,18 @@ function globToPathRegex(pattern) {
3335
3336
  return new RegExp(regex);
3336
3337
  }
3337
3338
  /**
3338
- * Parse a single line of stat output in the format: size\tmtime\ttype\tpath
3339
+ * Parse a single line of stat/find output in the format: size\tmtime\ttype\tpath
3339
3340
  *
3340
3341
  * The first three tab-delimited fields are always fixed (number, number, string),
3341
3342
  * so we safely take everything after the third tab as the file path — even if the
3342
3343
  * path itself contains tabs.
3344
+ *
3345
+ * The type field varies by platform / tool:
3346
+ * - GNU find -printf %y: single letter "d", "f", "l"
3347
+ * - BSD stat -f %Sp: permission strings like "drwxr-xr-x", "-rw-r--r--"
3348
+ *
3349
+ * The mtime field may be a float (GNU find %T@ → "1234567890.0000000000")
3350
+ * or an integer (BSD stat %m → "1234567890"); parseInt handles both.
3343
3351
  */
3344
3352
  function parseStatLine(line) {
3345
3353
  const firstTab = line.indexOf(" ");
@@ -3356,29 +3364,43 @@ function parseStatLine(line) {
3356
3364
  return {
3357
3365
  size,
3358
3366
  mtime,
3359
- isDir: fileType === "directory",
3367
+ isDir: fileType === "d" || fileType === "directory" || fileType.startsWith("d"),
3360
3368
  fullPath
3361
3369
  };
3362
3370
  }
3363
3371
  /**
3364
- * Pure POSIX shell command for listing directory contents with metadata.
3365
- * Uses find -maxdepth 1 + stat — works on any Linux including Alpine (busybox).
3372
+ * BusyBox/Alpine fallback script for stat -c.
3373
+ *
3374
+ * Determines file type with POSIX test builtins, then uses stat -c
3375
+ * (supported by both GNU coreutils and BusyBox) for size and mtime.
3376
+ * printf handles tab-delimited output formatting.
3377
+ */
3378
+ const STAT_C_SCRIPT = "for f; do if [ -d \"$f\" ]; then t=d; elif [ -L \"$f\" ]; then t=l; else t=f; fi; sz=$(stat -c %s \"$f\" 2>/dev/null) || continue; mt=$(stat -c %Y \"$f\" 2>/dev/null) || continue; printf \"%s\\t%s\\t%s\\t%s\\n\" \"$sz\" \"$mt\" \"$t\" \"$f\"; done";
3379
+ /**
3380
+ * Shell command for listing directory contents with metadata.
3381
+ *
3382
+ * Detects the environment at runtime with three-way probing:
3383
+ * 1. GNU find (full Linux): uses built-in `-printf` (most efficient)
3384
+ * 2. BusyBox / Alpine: uses `find -exec sh -c` with `stat -c` fallback
3385
+ * 3. BSD / macOS: uses `find -exec stat -f`
3366
3386
  *
3367
3387
  * Output format per line: size\tmtime\ttype\tpath
3368
3388
  */
3369
3389
  function buildLsCommand(dirPath) {
3370
3390
  const quotedPath = shellQuote(dirPath);
3371
- return `find ${quotedPath} -maxdepth 1 -not -path ${quotedPath} -exec stat -c '%s\\t%Y\\t%F\\t%n' {} + 2>/dev/null || true`;
3391
+ const findBase = `find ${quotedPath} -maxdepth 1 -not -path ${quotedPath}`;
3392
+ return `if find /dev/null -maxdepth 0 -printf '' 2>/dev/null; then ${findBase} -printf '%s\\t%T@\\t%y\\t%p\\n' 2>/dev/null; elif stat -c %s /dev/null >/dev/null 2>&1; then ${findBase} -exec sh -c '${STAT_C_SCRIPT}' _ {} +; else ${findBase} -exec stat -f '%z\t%m\t%Sp\t%N' {} + 2>/dev/null; fi || true`;
3372
3393
  }
3373
3394
  /**
3374
- * Pure POSIX shell command for listing files recursively with metadata.
3375
- * Uses find + stat works on any Linux including Alpine (busybox).
3395
+ * Shell command for listing files recursively with metadata.
3396
+ * Same three-way detection as buildLsCommand (GNU -printf / stat -c / BSD stat -f).
3376
3397
  *
3377
3398
  * Output format per line: size\tmtime\ttype\tpath
3378
3399
  */
3379
3400
  function buildFindCommand(searchPath) {
3380
3401
  const quotedPath = shellQuote(searchPath);
3381
- return `find ${quotedPath} -not -path ${quotedPath} -exec stat -c '%s\\t%Y\\t%F\\t%n' {} + 2>/dev/null || true`;
3402
+ const findBase = `find ${quotedPath} -not -path ${quotedPath}`;
3403
+ return `if find /dev/null -maxdepth 0 -printf '' 2>/dev/null; then ${findBase} -printf '%s\\t%T@\\t%y\\t%p\\n' 2>/dev/null; elif stat -c %s /dev/null >/dev/null 2>&1; then ${findBase} -exec sh -c '${STAT_C_SCRIPT}' _ {} +; else ${findBase} -exec stat -f '%z\t%m\t%Sp\t%N' {} + 2>/dev/null; fi || true`;
3382
3404
  }
3383
3405
  /**
3384
3406
  * Pure POSIX shell command for reading files with line numbers.
@@ -3399,7 +3421,9 @@ function buildReadCommand(filePath, offset, limit) {
3399
3421
  /**
3400
3422
  * Build a grep command for literal (fixed-string) search.
3401
3423
  * Uses grep -rHnF for recursive, with-filename, with-line-number, fixed-string search.
3402
- * Pure POSIX — works on any Linux including Alpine.
3424
+ *
3425
+ * When a glob pattern is provided, uses `find -name GLOB -exec grep` instead of
3426
+ * `grep --include=GLOB` for universal compatibility (BusyBox grep lacks --include).
3403
3427
  *
3404
3428
  * @param pattern - Literal string to search for (NOT regex).
3405
3429
  * @param searchPath - Base path to search in.
@@ -3408,7 +3432,8 @@ function buildReadCommand(filePath, offset, limit) {
3408
3432
  function buildGrepCommand(pattern, searchPath, globPattern) {
3409
3433
  const patternEscaped = shellQuote(pattern);
3410
3434
  const searchPathQuoted = shellQuote(searchPath);
3411
- return `grep -rHnF ${globPattern ? `--include=${shellQuote(globPattern)}` : ""} -e ${patternEscaped} ${searchPathQuoted} 2>/dev/null || true`;
3435
+ if (globPattern) return `find ${searchPathQuoted} -type f -name ${shellQuote(globPattern)} -exec grep -HnF -e ${patternEscaped} {} + 2>/dev/null || true`;
3436
+ return `grep -rHnF -e ${patternEscaped} ${searchPathQuoted} 2>/dev/null || true`;
3412
3437
  }
3413
3438
  /**
3414
3439
  * Base sandbox implementation with execute() as the only abstract method.
@@ -3461,6 +3486,7 @@ var BaseSandbox = class {
3461
3486
  * @returns Formatted file content with line numbers, or error message
3462
3487
  */
3463
3488
  async read(filePath, offset = 0, limit = 500) {
3489
+ if (limit === 0) return "";
3464
3490
  const command = buildReadCommand(filePath, offset, limit);
3465
3491
  const result = await this.execute(command);
3466
3492
  if (result.exitCode !== 0) return `Error: File '${filePath}' not found`;