62 lines
2.3 KiB
TypeScript
62 lines
2.3 KiB
TypeScript
import { stat } from "node:fs/promises";
|
|
import { relative, sep } from "node:path";
|
|
import picomatch from "picomatch";
|
|
import type { Tool } from "@maia/core";
|
|
import { displayPath, optStr, resolvePath, str } from "./helpers";
|
|
import { walkFiles } from "./walk";
|
|
|
|
const MAX_RESULTS = 100;
|
|
|
|
export const globTool: Tool = {
|
|
spec: {
|
|
name: "Glob",
|
|
description:
|
|
'Trouve des fichiers par motif glob (ex. "**/*.ts", "src/**/*.test.ts", "*.json"). ' +
|
|
"Le motif est relatif au dossier de recherche. Résultats triés par date de modification " +
|
|
"(les plus récents d'abord), limités à 100. Ignore .git, node_modules et les fichiers exclus par .gitignore.",
|
|
inputSchema: {
|
|
type: "object",
|
|
properties: {
|
|
pattern: { type: "string", description: "Motif glob à rechercher" },
|
|
path: {
|
|
type: "string",
|
|
description: "Dossier de recherche : complet ou relatif au dossier de lancement (défaut : ce dossier).",
|
|
},
|
|
},
|
|
required: ["pattern"],
|
|
},
|
|
},
|
|
permission: "auto",
|
|
describe: (i) => `${String(i.pattern ?? "")}${i.path ? ` dans ${String(i.path)}` : ""}`,
|
|
async execute(input, ctx) {
|
|
const pattern = str(input, "pattern");
|
|
const root = resolvePath(ctx.cwd, optStr(input, "path") ?? ".");
|
|
|
|
try {
|
|
if (!(await stat(root)).isDirectory()) throw new Error();
|
|
} catch {
|
|
throw new Error(`Dossier de recherche introuvable : ${root}`);
|
|
}
|
|
|
|
const isMatch = picomatch(pattern, { dot: true });
|
|
const matched: string[] = [];
|
|
for await (const abs of walkFiles(root, ctx.signal)) {
|
|
if (isMatch(relative(root, abs).split(sep).join("/"))) matched.push(abs);
|
|
}
|
|
if (matched.length === 0) return `Aucun fichier ne correspond au motif "${pattern}"`;
|
|
|
|
const withTime = await Promise.all(
|
|
matched.map(async (abs) => ({
|
|
abs,
|
|
mtime: await stat(abs).then((s) => s.mtimeMs, () => 0),
|
|
})),
|
|
);
|
|
withTime.sort((a, b) => b.mtime - a.mtime);
|
|
|
|
const shown = withTime.slice(0, MAX_RESULTS).map((f) => displayPath(ctx.cwd, f.abs));
|
|
const extra = withTime.length - shown.length;
|
|
return extra > 0
|
|
? `${shown.join("\n")}\n(${extra} résultat(s) supplémentaire(s) non affiché(s) : affinez le motif)`
|
|
: shown.join("\n");
|
|
},
|
|
};
|