@async23/pi-skill-control
Control Skill availability in Pi while showing native invocation routes
Package details
Install @async23/pi-skill-control from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@async23/pi-skill-control- Package
@async23/pi-skill-control- Version
0.5.0- Published
- Aug 4, 2026
- Downloads
- 222/mo · 222/wk
- Author
- async23
- License
- MIT
- Types
- extension
- Size
- 90.6 KB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./extensions/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@async23/pi-skill-control
A Pi extension for marking every Skill Pi has discovered as Unblocked or Blocked, while showing Pi's native invocation routes.
Pi remains responsible for discovery and invocation semantics: default directories, settings.json, packages, --skill, other extensions, and each Skill's disable-model-invocation frontmatter determine the candidate Skill set and its native availability. Unblocked means Skill Control adds no restriction and preserves Pi's native behavior; Blocked means Skill Control blocks both observable invocation routes. It does not add scan paths, edit SKILL.md, or override disable-model-invocation.
Install
pi install npm:@async23/pi-skill-control
Restart Pi after installation, or run /reload in an existing session to load the extension code.
For local development from this repository:
pi --no-extensions -e ./packages/skill-control
Usage
Run:
/skills
The inspector lists the Skills Pi discovered for the current session, groups them by their actual source, and previews each SKILL.md with theme-aware YAML frontmatter and Markdown highlighting. The selected file path remains visible in both wide and narrow layouts. A symlinked Skill shows both its lexical Scanned entry and canonical Target; a regular Skill shows a single Path.
The Sources summary recognizes .agents, Pi, Claude, Codex, OpenCode, Gemini, Antigravity, Cursor, Trae, Grok, and Kimi Code. Zed is also shown because it consumes .agents rather than owning a separate Skill root. Sources with discovered Skills are placed first in their original order; known tools with no distinct candidate follow with a dimmed 0. A zero commonly means the tool reuses .agents, has no Skill in its independent root, points to the same file through a symlink, or lost a same-name collision; it does not imply that Pi skipped the configured directory.
In ALL, the Skill list remains grouped by its actual source. In a specific Source filter, the same list is grouped by Pi's configuration scope: Global (user), Project, then Temporary. Group headings stay at the top level while their Skill rows are indented beneath them. Groups start expanded, show ▾ or ▸, and can be selected and folded for the lifetime of the inspector. Filtering temporarily expands groups that contain matches without changing their saved fold state, and a long scrolled list keeps the current group heading visible.
- Up/Down or
j/kselects a group heading or Skill while Skills is focused and scrolls while Preview is focused. - Space or Enter folds or unfolds a selected group heading. On narrow terminals, Enter opens Preview for a selected Skill.
- On a selected group heading, Left or
hcollapses the group and Right orlexpands it. On a selected Skill, Left/Right orh/lcycles focus between Skills and Preview. Moving past either end wraps to the other pane; Tab remains an alternative on wide terminals. [/]cycles backward/forward through source filters that contain discovered Skills. Empty known-tool entries remain visible in the summary but are skipped while cycling. Moving past either end wraps around./enters filter input from either pane; it is a trigger, not part of the displayed query. Filtering mirrors Codex CLI: it matches only Skill names with a case-insensitive subsequence fuzzy match, ranks tighter and leading matches first, and does not search descriptions, sources, or paths.- Filter input uses Pi's native single-line editor, including Unicode/IME cursor positioning, paste, Left/Right, Home/End, and customized keybindings. Defaults include Backspace or Ctrl+H to delete a character, Ctrl+W to delete a word, and Ctrl+U to delete to the start.
- While filter input is active, printable characters—including
h,j,k,l,[,], Space, and?—edit the query. Up/Down navigates results, Enter keeps the filter, and Escape cancels the edit and restores the previous filter. - Outside filter input, Escape returns from the narrow Preview first, then clears a kept filter before closing the inspector. Pending policy changes still require explicit discard confirmation.
- On a selected Skill, Space toggles between Unblocked and Blocked.
runblocks the selected Skill, anduundoes the latest policy change. ?opens a read-only guide to native availability and the blocking policy.- Ctrl+S writes and applies all pending changes without closing the inspector; the saved state becomes the new editing baseline, so you can continue adjusting other Skills. No Pi reload is needed.
Native availability and policy
Pi defines the native invocation routes of every discovered Skill:
| Native availability | Model sees the Skill | /skill:name is shown and accepted |
|---|---|---|
| Model + /skill | Yes | Yes |
| /skill only | No | Yes |
disable-model-invocation: true produces /skill only; otherwise Pi uses Model + /skill. Skill Control displays this native information but does not edit or override it.
Skill Control adds one binary policy:
| Policy | Effective behavior |
|---|---|
| Unblocked | Add no restriction; preserve Pi's native Model + /skill or /skill only availability |
| Blocked | Hide the Skill from the model and block direct /skill:name invocation |
Skill rows keep the two dimensions visually independent. A normal Model + /skill Skill has no native marker; a native /skill only Skill shows a user marker. The policy column is also empty by default and uses ⊘ instead of the word Blocked; unsaved edits continue to show Pending. A Skill can therefore show both the user marker and ⊘. The preview still spells out native availability, effective availability, and policy. Changes remain pending until Ctrl+S; saving keeps the inspector open and starts a fresh undo history from the new baseline. Policy shortcuts do nothing while Preview is focused.
The inspector header follows the same convention. Native shows the normal count without an icon and prefixes only the /skill only count with the user marker. Policy displays ⊘ plus the blocked count and adds a Pending count while edits are staged; the default unblocked count is intentionally omitted.
The user marker is Font Awesome user (U+F007) when Nerd Font support is detected, otherwise ⓤ (U+24E4). Detection checks PI_SKILL_CONTROL_NERD_FONT, Ghostty's effective font configuration, then fontconfig support for U+F007. Set PI_SKILL_CONTROL_NERD_FONT=1 or 0 before starting Pi to override automatic detection.
Configuration
User policy:
~/.pi/agent/skill-control.json
Version 5 stores blocked canonical SKILL.md paths:
{
"version": 5,
"blockedPaths": [
"/absolute/path/to/example/SKILL.md"
]
}
Project-local .pi/skill-control.json files are not read or modified. Existing files are left untouched.
Version 1, 2, and 4 disabledPaths entries migrate directly to blockedPaths. Version 3 invocation overrides that removed either route migrate conservatively to Blocked; fully open overrides migrate to Unblocked. Migration is written immediately in version 5 format. The old discover setting is no longer used; configure additional Skill paths through Pi's native skills setting instead. For the supported tool roots on macOS/Linux:
{
"skills": [
"~/.pi/agent/skills",
"~/.agents/skills",
"~/.codex/skills",
"~/.claude/skills",
"~/.config/opencode/skills",
"~/.gemini/config/skills",
"~/.cursor/skills",
"~/.trae/skills",
"~/.grok/skills",
"~/.kimi-code/skills"
]
}
Pi already scans ~/.pi/agent/skills and ~/.agents/skills; the example repeats them to make their precedence explicit. In this layout, Gemini CLI and Zed use the shared .agents root, while Antigravity uses ~/.gemini/config/skills. Kimi Code reads both .agents/skills and its independent global ~/.kimi-code/skills / project .kimi-code/skills roots; shared Skills remain labeled .agents, while independent Skills are labeled Kimi Code. Add ~/.gemini/skills only when that separate Gemini directory exists. The source classifier preserves the lexical discovery path, so a Skill loaded through a tool-specific symlink is labeled with that tool while policy continues to use the canonical file path.
When source ownership matters, explicitly placing ~/.pi/agent/skills and ~/.agents/skills first in the array keeps their Skills ahead of tool-specific symlink aliases and same-name copies. Pi still deduplicates by canonical file path and keeps the first Skill for a name collision.
Enforcement
Pi does not expose a per-Skill enable/disable API. Pi's resource configuration remains the discovery authority, and Unblocked is not an active enable operation—it only means this extension is not blocking the Skill. For temporary --skill entries and Skills injected by other extensions, stock Pi also does not expose a resource-removal hook. This package therefore enforces Blocked through extension hooks at the observable entry points:
- Blocked Skills are removed from
<available_skills>when Pi would normally show them. - Blocked Skills are removed from slash-command autocomplete.
- A manually typed
/skill:nameinvocation for a blocked Skill is intercepted before expansion.
The underlying Skill file is never modified or deleted.
License
MIT