pi-toggle-skills 0.1.3 → 0.1.4

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-toggle-skills",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Toggle skill visibility in pi's system prompt — flip disable-model-invocation on skills via an interactive TUI",
5
5
  "type": "module",
6
6
  "author": "Tom X Nguyen",
@@ -14,7 +14,7 @@ import {
14
14
  } from "./index.js";
15
15
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
16
16
  import matter from "gray-matter";
17
- import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from "node:fs";
17
+ import { readFileSync, writeFileSync, existsSync, readdirSync, statSync, realpathSync } from "node:fs";
18
18
  import { homedir } from "node:os";
19
19
  import { join, basename, dirname } from "node:path";
20
20
 
@@ -52,11 +52,14 @@ function getAllSkillDirs(cwd: string): SkillDir[] {
52
52
  for (const path of getProjectSkillDirs(cwd)) {
53
53
  dirs.push({ path, source: "project-pi" });
54
54
  }
55
- // Deduplicate by path
55
+ // Deduplicate by resolved (real) path so that symlinked directories
56
+ // (e.g. ~/.agents/skills → ~/.pi/agent/skills) are not scanned twice.
56
57
  const seen = new Set<string>();
57
58
  return dirs.filter((d) => {
58
- if (seen.has(d.path)) return false;
59
- seen.add(d.path);
59
+ let resolved = d.path;
60
+ try { resolved = realpathSync(d.path); } catch { /* path doesn't exist yet */ }
61
+ if (seen.has(resolved)) return false;
62
+ seen.add(resolved);
60
63
  return true;
61
64
  });
62
65
  }
@@ -172,11 +175,18 @@ function loadSkillFromFile(filePath: string): ToggleSkill | null {
172
175
  // Skills without description are not loaded by pi — skip them
173
176
  if (!description || description.trim() === "") return null;
174
177
 
178
+ // Resolve symlinks so that the same physical SKILL.md reached via
179
+ // different alias paths (e.g. ~/.agents/skills/x vs ~/.pi/agent/skills/x)
180
+ // gets the same filePath and is correctly deduplicated.
181
+ let realFilePath = filePath;
182
+ try { realFilePath = realpathSync(filePath); } catch { /* keep as-is */ }
183
+ const realBaseDir = dirname(realFilePath);
184
+
175
185
  return {
176
186
  name,
177
187
  description,
178
- filePath,
179
- baseDir: skillDir,
188
+ filePath: realFilePath,
189
+ baseDir: realBaseDir,
180
190
  disabled: isDisabled(frontmatter),
181
191
  };
182
192
  } catch {
@@ -297,7 +297,7 @@ export class ToggleSkillSelectorComponent implements Component {
297
297
  ? this.theme.fg("accent", item.name)
298
298
  : item.name;
299
299
  const status = item.disabled
300
- ? this.theme.fg("warning", " ✗")
300
+ ? this.theme.fg("dim", " ✗")
301
301
  : this.theme.fg("success", " ✓");
302
302
 
303
303
  this.listContainer.addChild(