Merge pull request #734 from prakhar1605/fix/skills-watcher-cold-start
Some checks failed
rowboat / apps/x Vitest suites (push) Has been cancelled
rowboat / apps/x Electron package smoke test (push) Has been cancelled

fix: skills load without restart on first install
This commit is contained in:
PRAKHAR PANDEY 2026-07-11 21:46:05 +05:30 committed by GitHub
commit 2e45bc0213
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 18 additions and 19 deletions

View file

@ -72,8 +72,18 @@ export function loadDiskSkills(): DiskSkill[] {
}
for (const entry of entries) {
if (!entry.isDirectory()) continue;
const dir = path.join(root, entry.name);
// entry.isDirectory() is false for a symlink to a directory, so stat the
// full path (statSync follows symlinks) to accept symlinked skill folders.
try {
if (!fs.statSync(dir).isDirectory()) {
console.warn(`[disk-skills] Skipping '${entry.name}': not a directory at ${dir}`);
continue;
}
} catch (err) {
console.warn(`[disk-skills] Skipping '${entry.name}': cannot stat ${dir}:`, err);
continue;
}
const skillFile = path.join(dir, "SKILL.md");
let raw: string;

View file

@ -1,5 +1,4 @@
import chokidar, { type FSWatcher } from "chokidar";
import fs from "node:fs";
import { SKILL_ROOTS } from "./disk-loader.js";
import { refreshDiskSkills } from "./index.js";
import { invalidateCopilotInstructionsCache } from "../copilot/instructions.js";
@ -28,28 +27,18 @@ function scheduleReload(): void {
/**
* Start watching the disk skill roots (~/.rowboat/skills, ~/.agents/skills) for
* changes and live-reload the skill catalog. Idempotent. Missing directories
* are skipped so watching ~/.agents when it doesn't exist never errors.
* changes and live-reload the skill catalog. Idempotent. Roots that don't exist
* yet (e.g. ~/.agents/skills before the first install) are watched anyway
* chokidar handles absent paths and emits events once they are created.
*/
export function startSkillsWatcher(): void {
if (watcher) return;
const roots = SKILL_ROOTS.filter((root) => {
try {
return fs.statSync(root).isDirectory();
} catch {
return false; // Directory absent (e.g. ~/.agents/skills) — nothing to watch.
}
});
if (roots.length === 0) {
console.log("[disk-skills] no skill directories present; watcher idle");
return;
}
try {
watcher = chokidar.watch(roots, {
watcher = chokidar.watch(SKILL_ROOTS, {
ignoreInitial: true,
// Only <root>/<skill>/ one level deep — don't recurse into large skill repos.
depth: 1,
awaitWriteFinish: {
stabilityThreshold: 150,
pollInterval: 50,
@ -67,7 +56,7 @@ export function startSkillsWatcher(): void {
console.error("[disk-skills] watcher error:", error);
});
console.log(`[disk-skills] watching ${roots.length} skill director${roots.length === 1 ? "y" : "ies"} for changes`);
console.log(`[disk-skills] watching ${SKILL_ROOTS.length} skill director${SKILL_ROOTS.length === 1 ? "y" : "ies"} for changes`);
} catch (err) {
console.error("[disk-skills] failed to start watcher:", err);
watcher = null;