refactor(skills): stabilize watcher targets and include agents skills
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
@@ -22,10 +23,15 @@ describe("ensureSkillsWatcher", () => {
|
||||
const opts = watchMock.mock.calls[0]?.[1] as { ignored?: unknown };
|
||||
|
||||
expect(opts.ignored).toBe(mod.DEFAULT_SKILLS_WATCH_IGNORED);
|
||||
const posix = (p: string) => p.replaceAll("\\", "/");
|
||||
expect(targets).toEqual(
|
||||
expect.arrayContaining([
|
||||
path.join("/tmp/workspace", "skills", "SKILL.md"),
|
||||
path.join("/tmp/workspace", "skills", "*", "SKILL.md"),
|
||||
posix(path.join("/tmp/workspace", "skills", "SKILL.md")),
|
||||
posix(path.join("/tmp/workspace", "skills", "*", "SKILL.md")),
|
||||
posix(path.join("/tmp/workspace", ".agents", "skills", "SKILL.md")),
|
||||
posix(path.join("/tmp/workspace", ".agents", "skills", "*", "SKILL.md")),
|
||||
posix(path.join(os.homedir(), ".agents", "skills", "SKILL.md")),
|
||||
posix(path.join(os.homedir(), ".agents", "skills", "*", "SKILL.md")),
|
||||
]),
|
||||
);
|
||||
expect(targets.every((target) => target.includes("SKILL.md"))).toBe(true);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import chokidar, { type FSWatcher } from "chokidar";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||
@@ -59,8 +60,10 @@ function resolveWatchPaths(workspaceDir: string, config?: OpenClawConfig): strin
|
||||
const paths: string[] = [];
|
||||
if (workspaceDir.trim()) {
|
||||
paths.push(path.join(workspaceDir, "skills"));
|
||||
paths.push(path.join(workspaceDir, ".agents", "skills"));
|
||||
}
|
||||
paths.push(path.join(CONFIG_DIR, "skills"));
|
||||
paths.push(path.join(os.homedir(), ".agents", "skills"));
|
||||
const extraDirsRaw = config?.skills?.load?.extraDirs ?? [];
|
||||
const extraDirs = extraDirsRaw
|
||||
.map((d) => (typeof d === "string" ? d.trim() : ""))
|
||||
@@ -72,17 +75,24 @@ function resolveWatchPaths(workspaceDir: string, config?: OpenClawConfig): strin
|
||||
return paths;
|
||||
}
|
||||
|
||||
function toWatchGlobRoot(raw: string): string {
|
||||
// Chokidar treats globs as POSIX-ish patterns. Normalize Windows separators
|
||||
// so `*` works consistently across platforms.
|
||||
return raw.replaceAll("\\", "/").replace(/\/+$/, "");
|
||||
}
|
||||
|
||||
function resolveWatchTargets(workspaceDir: string, config?: OpenClawConfig): string[] {
|
||||
// Skills are defined by SKILL.md; watch only those files to avoid traversing
|
||||
// or watching unrelated large trees (e.g. datasets) that can exhaust FDs.
|
||||
const targets = new Set<string>();
|
||||
for (const root of resolveWatchPaths(workspaceDir, config)) {
|
||||
const globRoot = toWatchGlobRoot(root);
|
||||
// Some configs point directly at a skill folder.
|
||||
targets.add(path.join(root, "SKILL.md"));
|
||||
targets.add(`${globRoot}/SKILL.md`);
|
||||
// Standard layout: <skillsRoot>/<skillName>/SKILL.md
|
||||
targets.add(path.join(root, "*", "SKILL.md"));
|
||||
targets.add(`${globRoot}/*/SKILL.md`);
|
||||
}
|
||||
return Array.from(targets);
|
||||
return Array.from(targets).toSorted();
|
||||
}
|
||||
|
||||
export function registerSkillsChangeListener(listener: (event: SkillsChangeEvent) => void) {
|
||||
|
||||
Reference in New Issue
Block a user