omp packages

@sagmans/pi-prefix0.2.0

Configurable prefix key that maps a second key to Pi app actions, slash commands, or extension events

GitHub Actions Published 6 days ago MIT ~468 downloads/mo

Untested on omp

This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.

Did it work for you?

Result

One report per person. A new report replaces your old one.

Reports 0

No reports yet.

Readme

@sagmans/pi-prefix

A prefix key for Pi: press one configurable prefix key, then a second key, to trigger a Pi app action, a slash command, or an extension event.

  • Zero-keymap by default. No prefix and no bindings ship with the package. Without configuration the extension is completely inert.
  • Your keybindings stay authoritative. A prefix that collides with an active main-editor binding disables pi-prefix and reports the exact keybinding ids. pi-prefix never edits keybindings.json.
  • No arbitrary code. Targets are app actions, slash commands, and events. There is no executable target.

Security warning

Pi packages execute with your user privileges. Review this repository before installing. Report vulnerabilities privately (see SECURITY.md).

Install

pi install npm:@sagmans/pi-prefix

Unpinned on purpose: pinned specs are skipped by pi update, so pinning hides future upgrades. Then restart Pi (or run /reload).

Ask your agent

Ask your coding agent to configure pi-prefix for you, for example:

Set up @sagmans/pi-prefix for me: prefix ctrl+, with m for model picker, h for /hotkeys submitted, and a binding for the open event of the extension I use.

The package ships a skill that guides agents through the same steps.

Configuration

Two layers, merged in order defaults → global → project:

Layer Path When loaded
Global ~/.pi/agent/pi-prefix.json always
Project <project>/.pi/pi-prefix.json only when you approved it and Pi trusts the project (see Project overlays)

prefix and timeoutMs come from the global layer. A project overlay can only add command bindings. Activation needs an effective prefix and at least one effective binding. Any parse or validation error in the global layer fails the whole effective config with the source path and reason.

The prefix is configurable: pick any key that no Pi or terminal binding already uses. pi-prefix disables itself when the prefix conflicts with a Pi binding. For example, ctrl+x is bound to app.message.copy in Pi 1.1.0, so the examples use ctrl+\ (written "ctrl+\\" in JSON), which is free.

~/.pi/agent/pi-prefix.json example:

{
  "$schema": "https://unpkg.com/@sagmans/pi-prefix/schema.json",
  "prefix": "ctrl+\\",
  "timeoutMs": 2000,
  "bindings": {
    "m": { "action": "app.model.select" },
    "h": { "command": "/hotkeys", "submit": true },
    "a": { "command": "/third-party-command", "submit": false },
    "o": { "event": "example-extension:open", "payload": { "source": "pi-prefix" } }
  }
}

Project overlay example (<project>/.pi/pi-prefix.json):

{
  "bindings": {
    "w": { "command": "/project-command", "submit": true }
  }
}

Project overlays

A project overlay is opt-in per project. pi-prefix loads it only when both are true:

  1. Pi trusts the project.
  2. Your global config approves this project path and the exact overlay content.

To approve, open Pi's interactive terminal UI in the project and run /pi-prefix allow-project. Pi shows the project path, the overlay's sha256, and each key with the command it runs and the extension that owns it. The answer defaults to No; choose Yes, approve to accept. pi-prefix then adds an entry to projectOverlays.allow in your global config. Run /reload to apply it. If the overlay file changes later, pi-prefix ignores it and asks you to approve it again. To revoke, remove the entry from your global config.

Approval rewrites the whole global config as formatted JSON (2-space indent). Key order can change, and the file mode is kept.

pi-prefix reads an overlay file only after Pi trusts the project. An untrusted project's overlay is never opened. A trusted overlay must be a regular file (a symlink to one is fine) of at most 64 KiB; devices, FIFOs, directories, and larger files are refused.

{
  "projectOverlays": {
    "allow": [
      { "path": "/home/me/src/app", "sha256": "<64 hex characters>" }
    ]
  }
}

An overlay can only add new keys that run commands registered by an extension (the extension source in /commands). Each command must be exactly /name, with lowercase letters, digits, :, _ or - and no arguments. It cannot set prefix or timeoutMs, change or remove (null) your global bindings, or bind actions, events, built-in commands, or plain text. /share, /export, /login, /trust, /quit, and /pi-prefix are always refused, and only registered extension commands run. A project key whose command is not a registered extension command is refused with a warning when pressed.

While an overlay is active, pi-prefix shows a notice at startup and after /reload that lists each project key and its command, and the status line shows prefix [project].

Your global config (~/.pi/agent/pi-prefix.json) is the trust anchor for project overlays. Anything that can write that file can approve an overlay, including an agent session that has file-write or shell tools. Do not let an agent edit this file on behalf of a repository you do not trust.

Schema reference

All properties are optional at layer level.

  • prefix — one key id, e.g. ctrl+\, f9, alt+shift+p. See Pi's keybinding format (modifier+key, modifiers ctrl, shift, alt, super; aliases esc/return are normalized to escape/enter).
  • timeoutMs — positive integer; prefix mode clears after this idle time. Default 2000.
  • bindings — map of second key id to exactly one target. In the global layer, null binds nothing and reserves the key, so a project overlay cannot claim it:
    • { "action": "app.model.select" } — a Pi app action. Unknown or context-only actions notify when pressed instead of throwing.
    • { "command": "/hotkeys", "submit": true } — a slash command. submit: true runs the command immediately; extension commands run without touching the editor draft, anything else replaces the draft and submits. submit: false replaces the draft with the command and stops.
    • { "event": "example-extension:open", "payload": {} } — emits the event name and exact JSON payload through pi.events. The extension that owns the event defines valid names and payloads.

Escape is reserved for canceling prefix mode; a binding on escape loads with a warning and can never dispatch. Enter, tab, and arrows are valid second keys.

Command behavior

With submit: true, extension commands (those listed by /commands with the extension source, e.g. a mode toggle installed from another package) are dispatched directly through Pi's command pipeline and the current editor draft is preserved untouched. Any other command — built-ins like /model or third-party commands Pi reports without the extension source — replaces the draft and submits it through the editor, matching manual typing. With submit: false the command text replaces the current draft and sits in the editor until you press Enter, which is the right form for commands that take an argument.

Conflicts and /pi-prefix

On startup pi-prefix scans Pi's main-editor bindings. A colliding prefix disables pi-prefix with an error naming the exact keybinding ids. Existing bindings are never changed. Fix by choosing another prefix or rebinding the conflicting action yourself in ~/.pi/agent/keybindings.json, then /reload.

Third-party extension shortcuts cannot be inventoried publicly: Pi reports those collisions under [Extension issues] and resolves them by load order. That limitation is Pi's, not pi-prefix's.

/pi-prefix prints active/inactive state, loaded config paths, trust state, the project overlay state (absent, untrusted, not-allowed, changed, rejected, or loaded), effective prefix, timeout, a binding summary, config errors and warnings, and any Pi keybinding conflicts.

Compatibility

Supports Pi hosts >=0.84.4 <0.88.0 || >=0.99.1 <0.100.0 || >=1.0.0 <2.0.0. The existing baseline covers 0.84.4, 0.86.1, 0.87.0, and 0.87.1. A separate CI job checks Pi 1.1.0 without changing the baseline lockfile. Other intervening minor versions are not claimed as supported. Pi 0.99.1 passed combined stash, history, and prefix smoke checks in regular and fullscreen modes, including /reload. Pi 1.1.0 passed a live TUI smoke check of prefix activation, dispatch, and cancel.

pi-prefix builds only on public extension surface: CustomEditor wrapping, the editor-component factory, native shortcut registration, command dispatch, and the extension event bus. pi-prefix does not opt into embedded working-status rendering. Pi controls the fallback status placement.

What pi-prefix does not do

  • No shipped prefix, keymap, or bindings.
  • No self-registration protocol for other extensions.
  • No arbitrary-code targets and no private Pi APIs.
  • No automatic conflict edits; reports only.
  • No raw global terminal input interception; activation goes through Pi's native shortcut registration.

Development

The locked baseline retains Pi 0.84.4. A development-only npm override patches its pinned Undici dependency without changing the host API under test. Consumers supply Pi's runtime modules. npm does not apply this package's overrides when it is installed as a dependency.

npm install
npm run typecheck
npm test
npm pack --dry-run   # package-content gate

Before each release, run the live checks in DOGFOOD.md.