Summary
Pi natively discovers global skills from both ~/.pi/agent/skills/ and ~/.agents/skills/.
When a HyperFrames skill update performs an install, HyperFrames writes the selected skill bundles to Claude Code's global store and the universal ~/.agents/skills store. It then mirrors all globally installed HyperFrames skills attributed to its source into detected agent-specific directories. Because Pi is in that mirror table, HyperFrames creates matching entries under ~/.pi/agent/skills even though Pi already loads the universal copies.
Pi consequently discovers each mirrored skill twice, reports a name collision, keeps the ~/.pi/agent/skills entry, and skips the ~/.agents/skills entry.
Clarification about .claude: Pi does not load ~/.claude/skills by default. That path appears here only because HyperFrames currently uses Claude Code's global skill store as the source for its fan-out. On Unix, the redundant Pi entries are symlinks back to that source. The two locations Pi actually discovers in this reproduction are ~/.pi/agent/skills and ~/.agents/skills.
This is deterministic duplicate discovery, not the unrelated-skill deletion reported in #3111. It is also narrower than the silent-update and overwrite concerns in #2613.
Environment
- HyperFrames CLI
0.7.109
- Pi
0.84.2
- macOS
- Default
CLAUDE_CONFIG_DIR (therefore Claude's store is ~/.claude/skills)
Steps to reproduce
-
Have Pi installed, so ~/.pi/agent/ exists.
-
Have HyperFrames skills installed globally, including copies under ~/.agents/skills.
-
Remove or otherwise ensure the matching HyperFrames entries are absent from ~/.pi/agent/skills.
-
Make at least one HyperFrames skill stale or missing so the next update performs an install rather than a no-op.
-
Run:
npx hyperframes skills update product-launch-video
-
Observe output similar to:
Linked skills into 12 other agent directories.
-
Inspect the Pi directory. With the default Claude configuration, entries look like:
~/.pi/agent/skills/hyperframes -> ../../../.claude/skills/hyperframes
-
Start Pi. It reports collisions such as:
"hyperframes" collision:
✓ auto (user) ~/.pi/agent/skills/hyperframes/SKILL.md
✗ ~/.agents/skills/hyperframes/SKILL.md (skipped)
In the observed reproduction, HyperFrames created 12 Pi symlinks and all 12 corresponding SKILL.md files were byte-identical to the universal copies. They still collide because Pi discovers them through two configured locations.
Root cause in the current implementation
packages/cli/src/commands/skills.ts invokes the upstream installer globally with --agent claude-code universal --copy.
packages/cli/src/utils/skillsMirror.ts then chooses Claude's global store ($CLAUDE_CONFIG_DIR/skills, defaulting to ~/.claude/skills) as the fan-out source.
packages/cli/src/utils/agentDirs.generated.ts includes Pi at .pi/agent/skills.
- The mirror skips the Claude source and
~/.agents/skills themselves, but Pi's agent-specific path is different, so it is mirrored despite Pi already consuming the universal store.
- The fan-out runs after an actual install. A freshness check that is a complete no-op does not recreate the links.
On Unix the fan-out uses relative symlinks; on Windows it copies directories. Either form can create duplicate Pi discovery when the universal copy is also present.
Expected behavior
HyperFrames should not create multiple discoverable copies of the same skill for one agent by default.
A defensible design would provide:
- Capability-aware
auto behavior by default — do not mirror into an agent-specific directory when that agent already consumes the universal store.
- A persistent opt-out — users should be able to disable global fan-out entirely.
- Optional target controls — advanced users should be able to include or exclude specific agents.
- Non-interactive determinism — agent-triggered updates should honor saved or default behavior without prompting on every run.
Possible CLI shapes, offered only as examples:
hyperframes skills update --no-mirror
hyperframes skills update --mirror-agents cursor,goose
The exact interface is open for maintainers to choose. A Pi-specific exclusion would fix this reproduction. A broader target-control design would also support users who deliberately manage several agent skill stores differently.
If explicit Pi mirroring is supported, the install targets must be coordinated: installing the same skill names into both Pi's agent-specific directory and ~/.agents/skills preserves the collision.
Acceptance criteria
- The default update path does not create Pi skill collisions when the same HyperFrames skills are installed under
~/.agents/skills.
- Users have a documented way to prevent or scope mirroring without patching generated files or an npm cache.
- Existing non-redundant agent mirroring continues to work.
- A regression test covers an installed Pi plus the universal store.
Summary
Pi natively discovers global skills from both
~/.pi/agent/skills/and~/.agents/skills/.When a HyperFrames skill update performs an install, HyperFrames writes the selected skill bundles to Claude Code's global store and the universal
~/.agents/skillsstore. It then mirrors all globally installed HyperFrames skills attributed to its source into detected agent-specific directories. Because Pi is in that mirror table, HyperFrames creates matching entries under~/.pi/agent/skillseven though Pi already loads the universal copies.Pi consequently discovers each mirrored skill twice, reports a name collision, keeps the
~/.pi/agent/skillsentry, and skips the~/.agents/skillsentry.Clarification about
.claude: Pi does not load~/.claude/skillsby default. That path appears here only because HyperFrames currently uses Claude Code's global skill store as the source for its fan-out. On Unix, the redundant Pi entries are symlinks back to that source. The two locations Pi actually discovers in this reproduction are~/.pi/agent/skillsand~/.agents/skills.This is deterministic duplicate discovery, not the unrelated-skill deletion reported in #3111. It is also narrower than the silent-update and overwrite concerns in #2613.
Environment
0.7.1090.84.2CLAUDE_CONFIG_DIR(therefore Claude's store is~/.claude/skills)Steps to reproduce
Have Pi installed, so
~/.pi/agent/exists.Have HyperFrames skills installed globally, including copies under
~/.agents/skills.Remove or otherwise ensure the matching HyperFrames entries are absent from
~/.pi/agent/skills.Make at least one HyperFrames skill stale or missing so the next update performs an install rather than a no-op.
Run:
Observe output similar to:
Inspect the Pi directory. With the default Claude configuration, entries look like:
Start Pi. It reports collisions such as:
In the observed reproduction, HyperFrames created 12 Pi symlinks and all 12 corresponding
SKILL.mdfiles were byte-identical to the universal copies. They still collide because Pi discovers them through two configured locations.Root cause in the current implementation
packages/cli/src/commands/skills.tsinvokes the upstream installer globally with--agent claude-code universal --copy.packages/cli/src/utils/skillsMirror.tsthen chooses Claude's global store ($CLAUDE_CONFIG_DIR/skills, defaulting to~/.claude/skills) as the fan-out source.packages/cli/src/utils/agentDirs.generated.tsincludes Pi at.pi/agent/skills.~/.agents/skillsthemselves, but Pi's agent-specific path is different, so it is mirrored despite Pi already consuming the universal store.On Unix the fan-out uses relative symlinks; on Windows it copies directories. Either form can create duplicate Pi discovery when the universal copy is also present.
Expected behavior
HyperFrames should not create multiple discoverable copies of the same skill for one agent by default.
A defensible design would provide:
autobehavior by default — do not mirror into an agent-specific directory when that agent already consumes the universal store.Possible CLI shapes, offered only as examples:
The exact interface is open for maintainers to choose. A Pi-specific exclusion would fix this reproduction. A broader target-control design would also support users who deliberately manage several agent skill stores differently.
If explicit Pi mirroring is supported, the install targets must be coordinated: installing the same skill names into both Pi's agent-specific directory and
~/.agents/skillspreserves the collision.Acceptance criteria
~/.agents/skills.