@tanstack/ai-skills
Version:
Portable Agent Skills (SKILL.md) as a first-class chat() middleware for TanStack AI.
49 lines (48 loc) • 1.69 kB
JavaScript
//#region src/walk.ts
var SKILL_FILE = "SKILL.md";
var MAX_SKILL_WALK_DEPTH = 6;
var SKIP_DIR_NAMES = /* @__PURE__ */ new Set([".git", "node_modules"]);
function basenameOf(path) {
const segments = path.replace(/\\/g, "/").split("/").filter((segment) => segment !== "");
return segments[segments.length - 1] ?? path;
}
/**
* Find every skill folder under `root`. A skill folder is a directory that
* directly contains `SKILL.md`; the walk stops descending once found. Skips
* dot-directories, `.git`, and `node_modules`. Bounded by `maxDepth`. Errors
* from `list` are swallowed (an unreadable directory yields nothing).
*
* Unlike `ai-sandbox`'s `discoverSkillDirs`, this returns `[]` when nothing is
* found — the "fall back to the clone dir" behavior is a harness-projection
* concern and stays at that call site (it is wrong for a catalog).
*/
async function walkSkillDirs(list, root, opts = {}) {
const maxDepth = opts.maxDepth ?? 6;
const found = [];
await walk(list, root, found, 0, maxDepth);
return found;
}
async function walk(list, dir, found, depth, maxDepth) {
if (depth > maxDepth) return;
let entries;
try {
entries = await list(dir);
} catch {
return;
}
if (entries.some((entry) => entry.type === "file" && entry.name.toLowerCase() === "SKILL.md".toLowerCase())) {
found.push({
name: basenameOf(dir),
dir
});
return;
}
for (const entry of entries) {
if (entry.type !== "dir") continue;
if (entry.name.startsWith(".") || SKIP_DIR_NAMES.has(entry.name)) continue;
await walk(list, entry.path, found, depth + 1, maxDepth);
}
}
//#endregion
export { MAX_SKILL_WALK_DEPTH, SKILL_FILE, walkSkillDirs };
//# sourceMappingURL=walk.js.map