vigiles 27.1.6 → 27.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/eval.js +34 -3
  2. package/package.json +1 -1
package/dist/eval.js CHANGED
@@ -1412,6 +1412,37 @@ exports.claudeEvalDriver = {
1412
1412
  parse: parseClaudeRun,
1413
1413
  harness: "claude-code",
1414
1414
  };
1415
+ /**
1416
+ * Is this directory entry a skill DIRECTORY — following a symlink to one?
1417
+ *
1418
+ * 🔴 `Dirent.isDirectory()` describes the ENTRY, not its target: for a symlink
1419
+ * pointing at a directory it is FALSE. Skipping on it therefore skips symlinked
1420
+ * skills entirely, and the failure is silent and misreads as a finding — the skill
1421
+ * never enters the temp install, so nothing fires on any prompt and the run reports
1422
+ * 0% recall plus a competing-skill count short by however many were linked. The
1423
+ * tool then says "usually SETUP, not the description", which is true and useless,
1424
+ * because the setup it means is its own.
1425
+ *
1426
+ * A symlinked skills tree is not exotic: it is what a consumer gets when skills are
1427
+ * distributed as an npm package and linked into `.claude/skills/`, which is exactly
1428
+ * how a package publishes them.
1429
+ *
1430
+ * `statSync` follows the link, so it answers about the TARGET. A dangling link
1431
+ * throws, and that is not-a-skill-dir — the same answer the `SKILL.md` check
1432
+ * downstream would give it, one step earlier and without an exception escaping.
1433
+ */
1434
+ function isSkillDirEntry(parent, entry) {
1435
+ if (entry.isDirectory())
1436
+ return true;
1437
+ if (!entry.isSymbolicLink())
1438
+ return false;
1439
+ try {
1440
+ return (0, node_fs_1.statSync)((0, node_path_1.join)(parent, entry.name)).isDirectory();
1441
+ }
1442
+ catch {
1443
+ return false;
1444
+ }
1445
+ }
1415
1446
  /**
1416
1447
  * Package loose `<skillsDir>/<name>/SKILL.md` skills into a throwaway plugin dir
1417
1448
  * that `claude --plugin-dir` accepts — so repo-local skills (e.g. `.claude/skills`)
@@ -1432,7 +1463,7 @@ function packageSkillsDir(skillsDir, opts = {}) {
1432
1463
  (0, node_fs_1.mkdirSync)(skillsOut, { recursive: true });
1433
1464
  let copied = 0;
1434
1465
  for (const entry of (0, node_fs_1.readdirSync)(abs, { withFileTypes: true })) {
1435
- if (!entry.isDirectory())
1466
+ if (!isSkillDirEntry(abs, entry))
1436
1467
  continue;
1437
1468
  const srcSkill = (0, node_path_1.join)(abs, entry.name, "SKILL.md");
1438
1469
  if (!(0, node_fs_1.existsSync)(srcSkill))
@@ -1589,7 +1620,7 @@ function copySkillsInto(src, skillsOut, stub, present) {
1589
1620
  throw new Error(`installSet source not found: ${src} (resolved ${abs})`);
1590
1621
  let copied = 0;
1591
1622
  for (const entry of (0, node_fs_1.readdirSync)(abs, { withFileTypes: true })) {
1592
- if (!entry.isDirectory() || present.has(entry.name))
1623
+ if (!isSkillDirEntry(abs, entry) || present.has(entry.name))
1593
1624
  continue;
1594
1625
  const srcSkill = (0, node_path_1.join)(abs, entry.name, "SKILL.md");
1595
1626
  if (!(0, node_fs_1.existsSync)(srcSkill))
@@ -1656,7 +1687,7 @@ function countSkills(pluginDir) {
1656
1687
  return 0;
1657
1688
  let n = 0;
1658
1689
  for (const e of (0, node_fs_1.readdirSync)(dir, { withFileTypes: true }))
1659
- if (e.isDirectory() && (0, node_fs_1.existsSync)((0, node_path_1.join)(dir, e.name, "SKILL.md")))
1690
+ if (isSkillDirEntry(dir, e) && (0, node_fs_1.existsSync)((0, node_path_1.join)(dir, e.name, "SKILL.md")))
1660
1691
  n++;
1661
1692
  return n;
1662
1693
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigiles",
3
- "version": "27.1.6",
3
+ "version": "27.1.7",
4
4
  "description": "Audit, test and measure the harness your AI agent runs on — grade your CLAUDE.md / AGENTS.md, skills, subagents and hooks, run them against a scripted model, and measure whether they actually fire.",
5
5
  "keywords": [
6
6
  "claude-code",