pi-opener
extensionmaintainedPi extension: open files in $EDITOR via herdr, with per-extension openers (pdf, images, …)
by — · v0.1.0 · published 1w ago
$ pi install npm:pi-openerSignals
Download trend
145 downloads · last 12 weeks (weekly)
README
pi-opener
Pi extension that opens files in your real editor.
- Terminal editors (
$EDITOR/$VISUAL/nvim) open in a herdr split pane by default - Pane reuse — later
/opens target the same editor pane (or drive nvim/vim/hx in-place) - Per-extension openers send PDFs, images, etc. to the OS handler (
openon macOS,xdg-openon Linux) - Configurable wrapper — herdr-first, or tmux/wezterm/direct via
wrapperCommand - Slash commands
/open//o(project files) and/recent//rec(chat-mentioned paths) - LLM tool
open_file
Install
From npm (recommended)
pi install npm:pi-opener
Or pin a version:
pi install npm:pi-opener@0.1.0
Project-local install (writes .pi/settings.json):
pi install -l npm:pi-opener
From git
pi install git:github.com/yzuyr/pi-opener
# or
pi install https://github.com/yzuyr/pi-opener
From a local checkout
pi install /absolute/path/to/pi-opener
# or, from this repo:
pi install .
Then /reload (or restart pi).
Note: Terminal splits work best inside herdr (HERDR_PANE_ID). Without herdr, set wrapperCommand or use external openers / direct launch.
Usage
/open # pick from project files (cwd, ignore-aware)
/o # alias for /open
/open src/ind<Tab> # filesystem path completions
/open src/index.ts
/open src/index.ts:42 # jump to line when the editor supports it
/recent # pick from paths mentioned in this chat
/rec # alias for /recent
/recent src/foo.ts:10 # open a chat-mentioned path directly
| Command | Source |
|---|---|
/open, /o | Files under cwd (skips node_modules, .gitignore, etc.) |
/recent, /rec | Paths scraped from this session that still exist on disk (including ignored paths if the chat referenced them) |
Pi has no built-in command-alias API, so short names are registered as separate commands sharing handlers.
The agent can also call open_file.
Config
Primary configuration is loaded from ~/.config/pi-opener/config.yaml (or config.yml).
~/.config/pi-opener/config.yaml
wrapperCommand: herdr
opener:
code: druk %p:%l # files (line optional → 0)
dir: druk %p # directories — no %l
pdf: xdg-open %p # extension keys: with or without leading dot
md: glow -p %p
See config.example.yaml.
| Key | Meaning |
|---|---|
opener.code | Default code editor (default / * / editor also accepted) |
opener.dir | Directory opener (directory / folder aliases). Default: platform opener |
opener.<ext> | Opener for that extension (pdf, .pdf, md, …) |
wrapperCommand | How terminal openers launch (see below). Aliases: wrapper, wrapper_command |
Directories are detected via stat and never go through opener.code, so a file template like %p:%l will not produce dirpath:0 unless you put %l in opener.dir yourself.
wrapperCommand (herdr-first, not herdr-only)
Terminal openers (nvim, bunx druk, …) are launched through a wrapper. Default is herdr when HERDR_PANE_ID is set; otherwise the command runs directly.
Herdr pane reuse and right-split direction are built-in. If you want to change the split direction or force a new pane every time, set a custom wrapperCommand.
# default behavior (can omit)
wrapperCommand: herdr
# never use a wrapper — spawn the opener directly
wrapperCommand: none
# custom split / terminal launcher (e.g. tmux down split)
wrapperCommand: "tmux split-window -v %c"
wrapperCommand: ["wezterm", "cli", "split-pane", "--", "%@"]
| Value | Effect |
|---|---|
herdr (default) | herdr pane split + run; falls back to direct if herdr is unavailable |
none / direct / false | run opener argv directly (no split) |
| string | shell-ish command; %c = full opener command (quoted) |
| string array | argv prefix; %@ or %c splices opener argv, else argv is appended |
External openers (xdg-open, opener.dir default, media) never use the wrapper.
Placeholders
| Token | Value |
|---|---|
%p | Absolute file path |
%l | Line number, or 0 if none |
%c | Column, or 0 if none |
If %p is omitted, the path is appended automatically (with editor-specific line jumps when possible).
Platform default openers
| OS | Command |
|---|---|
| macOS | open |
| Linux / other | xdg-open |
| Windows | cmd /c start "" |
Applied to common media extensions (pdf, images, audio, video). Override per-extension under opener:.
mode is auto-detected for xdg-open, open, start, gio, wslview, cmd → external (detached). Everything else defaults to terminal (launched via wrapper).
Line numbers
| Editor | Jump (when no %l in template) |
|---|---|
| nvim / vim / vi / nano / emacs | +LINE |
| helix | file:LINE |
| vscode / cursor / zed | --goto file:LINE |
| sublime | file:LINE |
| kate | --line=LINE |
Prefer explicit templates when you care: code: nvim +%l %p.
Development
bun install # or: npm install
bun test
bun run typecheck
npm pack --dry-run # verify publish contents
Pure logic lives in src/core.ts; the Pi extension entrypoint is src/index.ts.
Publish (maintainers)
# 1. ensure clean tree, tests green
bun test && bun run typecheck
# 2. bump version in package.json if needed
# 3. publish
npm publish --access public
# 4. users install with:
# pi install npm:pi-opener
Pi installs packages with production deps only (npm install --omit=dev), so runtime deps must stay in dependencies (ignore, yaml). Pi-provided packages stay in peerDependencies (@earendil-works/pi-coding-agent, @earendil-works/pi-tui, typebox).