Files
opencode/js/src/tool/ls.ts
Dax Raad 91a9e455e2 sync
2025-05-30 16:43:42 -04:00

97 lines
2.5 KiB
TypeScript

import { z } from "zod";
import { Tool } from "./tool";
import { App } from "../app/app";
import * as path from "path";
const IGNORE_PATTERNS = [
"node_modules/",
"__pycache__/",
".git/",
"dist/",
"build/",
"target/",
"vendor/",
"bin/",
"obj/",
".idea/",
".vscode/",
];
export const ls = Tool.define({
name: "opencode.ls",
description: "List directory contents",
parameters: z.object({
path: z.string().optional(),
ignore: z.array(z.string()).optional(),
}),
async execute(params) {
const app = await App.use();
const searchPath = path.resolve(app.root, params.path || ".");
const glob = new Bun.Glob("**/*");
const files = [];
for await (const file of glob.scan({ cwd: searchPath })) {
if (file.startsWith(".") || IGNORE_PATTERNS.some((p) => file.includes(p)))
continue;
if (params.ignore?.some((pattern) => new Bun.Glob(pattern).match(file)))
continue;
files.push(file);
if (files.length >= 1000) break;
}
// Build directory structure
const dirs = new Set<string>();
const filesByDir = new Map<string, string[]>();
for (const file of files) {
const dir = path.dirname(file);
const parts = dir === "." ? [] : dir.split("/");
// Add all parent directories
for (let i = 0; i <= parts.length; i++) {
const dirPath = i === 0 ? "." : parts.slice(0, i).join("/");
dirs.add(dirPath);
}
// Add file to its directory
if (!filesByDir.has(dir)) filesByDir.set(dir, []);
filesByDir.get(dir)!.push(path.basename(file));
}
function renderDir(dirPath: string, depth: number): string {
const indent = " ".repeat(depth);
let output = "";
if (depth > 0) {
output += `${indent}${path.basename(dirPath)}/\n`;
}
const childIndent = " ".repeat(depth + 1);
const children = Array.from(dirs)
.filter((d) => path.dirname(d) === dirPath && d !== dirPath)
.sort();
// Render subdirectories first
for (const child of children) {
output += renderDir(child, depth + 1);
}
// Render files
const files = filesByDir.get(dirPath) || [];
for (const file of files.sort()) {
output += `${childIndent}${file}\n`;
}
return output;
}
const output = `${searchPath}/\n` + renderDir(".", 0);
return {
metadata: { count: files.length, truncated: files.length >= 1000 },
output,
};
},
});