pi-skill-cue
Skill router for pi. Matches every prompt to the right skill, injects it where the model will actually see it, and optionally blocks work until it is read.
Package details
Install pi-skill-cue from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-skill-cue- Package
pi-skill-cue- Version
0.1.1- Published
- Aug 28, 2026
- Downloads
- 363/mo · 15/wk
- Author
- sraavanchevireddy
- License
- MIT
- Types
- extension
- Size
- 52.3 KB
- Dependencies
- 0 dependencies · 1 peer
Pi manifest JSON
{
"extensions": [
"./extensions"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-skill-cue
Your skills only help if the model reads them. pi-skill-cue makes sure it does.
The problem
Pi loads skills through progressive disclosure: the system prompt lists every skill's name and description, and the model is expected to load the rest itself when a task matches. Pi's own documentation concedes the failure mode:
"the agent uses
readto load the full SKILL.md (models don't always do this; use prompting or/skill:nameto force it)"
A user installs thirty or forty skills, the descriptions sit in one undifferentiated block far from the request that actually needs them, and the model skims past the one that mattered. The user gets generic behaviour, blames the skill, and stops writing skills.
Install
pi install npm:pi-skill-cue
No API key, no network calls, no runtime dependencies, no per-turn cost. It works the moment it's installed, with zero configuration.
How it works
Route. On every prompt, a deterministic lexical scorer ranks installed skills against the prompt, the skill's own "use when …" phrases, any user-configured regex triggers, and signals from the working directory. If a match clears the threshold, a short directive naming the skill and its path is appended to that turn's system prompt — the one place attention is strongest.
Gate. For skills a user explicitly names in config, pi-skill-cue can block
write or edit until the model has actually read that skill's SKILL.md, either by
a read tool call on that path or a /skill:<name> invocation. Gates are advisory by
default and enforcement is opt-in per skill.
Report. A local, append-only ledger records injections, reads, blocks, and
outcomes, so /cue-report can show which skills fire, which never do, and
/skill-doctor can lint descriptions that are structurally incapable of ever
matching.
Benchmark
Numbers from npm run bench, against the synthetic corpus and labelled prompts in
bench/:
| Metric | Value |
|---|---|
| Cases | 49 |
| Precision@1 | 0.821 |
| Recall@3 | 0.821 |
| False-positive rate | 0 |
| Hard-subset precision@1 | 0 (6 cases) |
The hard subset is six prompts that share almost no vocabulary with their target skill's description — paraphrases like "this is acting weird" for a debugging skill. Lexical routing cannot reach them, and the number above is exactly zero: not rounded up, not excluded. An optional model-assisted escalation for ambiguous cases is deferred until the lexical approach alone is shown to be insufficient in practice.
Most routing packages assert that they select correctly. This one publishes a number
and enforces it: npm run bench recomputes these numbers against the committed
baseline in bench/baseline.json and exits non-zero on
regression, and tests/bench.test.ts asserts the same
baseline in CI.
Configuration
~/.pi/agent/skill-cue.json, merged with .pi/skill-cue.json where the project file
wins per top-level key. Absent config is valid and yields advisory-only behaviour.
{
"enabled": true,
"maxSkills": 3,
"threshold": 0.35,
"verbose": false,
"mute": ["some-noisy-skill"],
"triggers": { "ticket-intake": ["\\b[A-Z]{2,}-\\d{3,}\\b"] },
"gates": { "test-driven-development": { "tools": ["write", "edit"] } },
"escalate": { "enabled": false, "model": null }
}
triggers.ticket-intake above matches ticket keys shaped like ABC-123. Unknown
keys are ignored; invalid values fall back to defaults rather than throwing.
Commands
| Command | Behaviour |
|---|---|
/cue |
Current status: enabled, last match, and its reason. |
/cue off |
Disable routing and gating for this session. |
/cue on |
Re-enable for this session. |
/cue-report |
Per-skill table: injections, reads, blocks, never-fired flag. |
/cue-report --purge |
Delete the local ledger. |
/skill-doctor |
Lint installed skills for routability problems. |
Privacy
The ledger is a local, append-only file under ~/.pi/agent/skill-cue/.
It never leaves your machine.
/cue-report --purge deletes it. The extension makes no network calls in the
routing path, ever — there is nothing to call out to.
Failure behaviour
Fail-open by design. Every hook body is wrapped; on any error the extension does nothing and the turn proceeds exactly as vanilla pi. A router that occasionally misses is a mild loss. A router that can break a session is uninstalled the first time it does.
Gates carry an explicit anti-deadlock rule: if the same gate blocks the same tool twice, it releases permanently for the rest of the session rather than re-arming. No routing benefit justifies a block a user cannot escape.
Contributing
Before publishing, copy .leakpatterns.example to .leakpatterns.local (gitignored)
and add any identity- or employer-specific terms relevant to your environment. npm run check:leaks checks the packed tarball and git index against both the built-in generic
patterns and that local file.