diff --git a/bun.lock b/bun.lock index 57a4fc4..c707eca 100644 --- a/bun.lock +++ b/bun.lock @@ -3,22 +3,23 @@ "workspaces": { "": { "name": "letta-code", - "dependencies": { + "devDependencies": { "@letta-ai/letta-client": "1.0.0-alpha.2", + "@types/bun": "latest", + "@types/diff": "^8.0.0", "diff": "^8.0.2", + "husky": "9.1.7", "ink": "^5.0.0", "ink-spinner": "^5.0.0", "ink-text-input": "^5.0.0", + "lint-staged": "16.2.4", "minimatch": "^10.0.3", "react": "18.2.0", - }, - "devDependencies": { - "@types/bun": "latest", - "@types/diff": "^8.0.0", - "husky": "9.1.7", - "lint-staged": "16.2.4", "typescript": "^5.0.0", }, + "optionalDependencies": { + "@vscode/ripgrep": "^1.17.0", + }, }, }, "packages": { @@ -38,8 +39,12 @@ "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, ""], + "@vscode/ripgrep": ["@vscode/ripgrep@1.17.0", "", { "dependencies": { "https-proxy-agent": "^7.0.2", "proxy-from-env": "^1.1.0", "yauzl": "^2.9.2" } }, "sha512-mBRKm+ASPkUcw4o9aAgfbusIu6H4Sdhw09bjeP1YOBFTJEZAnrnk6WZwzv8NEjgC82f7ILvhmb1WIElSugea6g=="], + "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, ""], + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + "ansi-escapes": ["ansi-escapes@7.1.1", "", { "dependencies": { "environment": "^1.0.0" } }, ""], "ansi-regex": ["ansi-regex@6.2.2", "", {}, ""], @@ -56,6 +61,8 @@ "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, ""], + "buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="], + "bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, ""], "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, ""], @@ -84,6 +91,8 @@ "csstype": ["csstype@3.1.3", "", {}, ""], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "delayed-stream": ["delayed-stream@1.0.0", "", {}, ""], "diff": ["diff@8.0.2", "", {}, ""], @@ -112,6 +121,8 @@ "events": ["events@3.3.0", "", {}, ""], + "fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="], + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, ""], "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, ""], @@ -136,6 +147,8 @@ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, ""], + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + "husky": ["husky@9.1.7", "", { "bin": "bin.js" }, ""], "ieee754": ["ieee754@1.2.1", "", {}, ""], @@ -178,6 +191,8 @@ "minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "nano-spawn": ["nano-spawn@2.0.0", "", {}, ""], "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, ""], @@ -188,12 +203,16 @@ "patch-console": ["patch-console@2.0.0", "", {}, ""], + "pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="], + "picomatch": ["picomatch@2.3.1", "", {}, ""], "pidtree": ["pidtree@0.6.0", "", { "bin": "bin/pidtree.js" }, ""], "process": ["process@0.11.10", "", {}, ""], + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, ""], "react": ["react@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ=="], @@ -256,6 +275,8 @@ "yaml": ["yaml@2.8.1", "", { "bin": "bin.mjs" }, ""], + "yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="], + "yoga-layout": ["yoga-layout@3.2.1", "", {}, ""], "cli-truncate/slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, ""], diff --git a/package.json b/package.json index e855262..8e015ea 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,9 @@ "publishConfig": { "access": "public" }, - "dependencies": {}, + "dependencies": { + "@vscode/ripgrep": "^1.17.0" + }, "devDependencies": { "@types/bun": "latest", "@types/diff": "^8.0.0", diff --git a/src/tools/impl/Read.ts b/src/tools/impl/Read.ts index 728e57f..f9817a3 100644 --- a/src/tools/impl/Read.ts +++ b/src/tools/impl/Read.ts @@ -15,19 +15,37 @@ async function isBinaryFile(filePath: string): Promise { const fd = await fs.open(filePath, "r"); try { const stats = await fd.stat(); - const bufferSize = Math.min(4096, stats.size); + const bufferSize = Math.min(8192, stats.size); if (bufferSize === 0) return false; const buffer = Buffer.alloc(bufferSize); const { bytesRead } = await fd.read(buffer, 0, bufferSize, 0); if (bytesRead === 0) return false; - for (let i = 0; i < bytesRead; i++) if (buffer[i] === 0) return true; - let nonPrintableCount = 0; + + // Check for null bytes (definite binary indicator) for (let i = 0; i < bytesRead; i++) { - const byte = buffer[i]; - if (byte < 9 || (byte > 13 && byte < 32) || byte > 126) - nonPrintableCount++; + if (buffer[i] === 0) return true; + } + + // Try to decode as UTF-8 and check if valid + try { + const text = buffer.slice(0, bytesRead).toString("utf-8"); + // Check for replacement characters (indicates invalid UTF-8) + if (text.includes("\uFFFD")) return true; + + // Count control characters (excluding whitespace) + let controlCharCount = 0; + for (let i = 0; i < text.length; i++) { + const code = text.charCodeAt(i); + // Allow tab(9), newline(10), carriage return(13) + if (code < 9 || (code > 13 && code < 32)) { + controlCharCount++; + } + } + return controlCharCount / text.length > 0.3; + } catch { + // Invalid UTF-8 = binary + return true; } - return nonPrintableCount / bytesRead > 0.3; } finally { await fd.close(); }