@tianhuil/pi-orca
Pi extension that keeps Orca terminal tab titles in sync with the running pi session
Package details
Install @tianhuil/pi-orca from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@tianhuil/pi-orca- Package
@tianhuil/pi-orca- Version
1.0.1- Published
- Aug 11, 2026
- Downloads
- 105/mo · 12/wk
- Author
- tianhuil
- License
- MIT
- Types
- extension
- Size
- 25.1 KB
- Dependencies
- 1 dependency · 2 peers
Pi manifest JSON
{
"extensions": [
"./src/tab-title-sync/src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
Pi × Orca
A pi extension that keeps your Orca terminal tab titles in sync with the pi session running inside each one — and lets you spawn new pi tabs from within pi.
Install
pi install npm:@tianhuil/pi-orca
Then reload pi (/reload). The extension loads automatically via the pi.extensions field in package.json.
Prefer living on main? Install straight from the repo instead:
pi install git:github.com/tianhuil/pi-orca
# Clone the repo into your pi extensions directory
git clone https://github.com/tianhuil/pi-orca.git ~/.pi/extensions/pi-orca
cd ~/.pi/extensions/pi-orca
# Install dependencies (Node.js required — extensions run on Node, not Bun)
npm install
Then reload pi (/reload).
Requirements: Node.js 18+, the Orca app. No API keys needed — title summarization uses a free model endpoint out of the box.
How it works
Tab title sync
When pi starts inside an Orca tab, the extension looks up the session ID in a local SQLite title store:
| Situation | Tab title |
|---|---|
| Brand-new session | Pi |
| Resumed session with a stored title | The stored summary (≤ 50 chars) |
| Resumed session whose title was never written (crash recovery) | Re-summarized from the stored first message |
On the first user message of a fresh session, the extension:
- Persists the message text to the title store, keyed by session ID.
- Sends a truncated excerpt to a free summarizer model (Mimo v2.5), asking for a title under 50 characters.
- Writes the summarized title to the store and renames the Orca tab to match.
The title is also set as pi's internal session name, so pi's /resume picker shows the same summary as the tab. Once set, the title stays stable — Orca's terminal rename is sticky and overrides any later OSC title writes from pi.
If the summarizer is unreachable, the fallback is the first line of your message truncated to 50 characters.
/spawn command
Type /spawn inside pi to open a new Orca tab running a fresh pi session in the current worktree — without leaving pi or switching apps. The new tab opens already titled Pi and gets its own summary once you start typing.
Outside Orca
The extension degrades gracefully when pi runs outside Orca (no ORCA_TERMINAL_HANDLE environment variable): no tab renaming, but session names inside pi are still set. No crashes, no errors.
Architecture
src/
├── index.ts # Extension entry — registers provider, wires handlers & /spawn
├── orca.ts # Orca CLI arg builders + injectable executor
├── store.ts # SQLite title store (better-sqlite3 + Drizzle ORM, WAL mode)
├── summarizer.ts # Model-call abstraction with fallback
└── title.ts # Pure helpers: prompt builder, title normalizer
Key design decisions are documented in the docs/adr/ directory:
- ADR-0001 — Tab titles are set via
orca terminal rename, not pi's OSC writes - ADR-0002 — Summarization uses a self-registered free provider, not your configured model
- ADR-0003 — Extensions run on Node, so the store uses better-sqlite3 (not bun:sqlite)
Development
npm install
npm test # run unit tests (vitest)
npm test:watch # watch mode
Tests use in-memory SQLite and injectable model/executor fakes — no real Orca, no real API calls.
E2E smoke test (Orca-only)
An end-to-end smoke test validates that the extension loads inside pi without errors and that the pi-in-Orca integration (tab rename, summarization) works end-to-end. This requires the Orca app — it is not run in CI.
# From any Orca terminal whose worktree is this repo:
npm run smoke:e2e
# Or skip the Orca integration check (startup-error scan only):
npm run smoke:e2e -- --skip-orca
What it checks:
Startup errors — scans the most recent pi session log (
~/.pi/agent/sessions/) for this repo and flags unexpectederrorMessagefields, missingsession/model_changerecords, and suspicious tool errors. Best-effort heuristics; benign exit codes and user-initiated aborts are ignored.Pi-in-Orca integration — spawns a fresh pi tab in the current worktree, waits for pi to become ready, verifies the tab title is
Pi(confirming thesession_starthandler fired), sends a test message, and verifies the title changes (confirming the summarization pipeline ran). The test tab is cleaned up automatically.
Prerequisites: Orca must be running and this repo must be open as a worktree. The ORCA_TERMINAL_HANDLE environment variable must be set (Orca sets this automatically in its terminals).
Publishing
Published to npm and auto-listed on the pi.dev/packages gallery — the gallery is an npm keyword index (keywords:pi-package), not a registry with a submission step.
npm login # once per machine, with your npmjs.com account
npm publish # runs the tests (prepublishOnly), then publishes
npm run release # patch-bump + git tag + publish in one step
npm run preview # dry-run: inspect tarball contents before publishing
npm publish is gated by the prepublishOnly script (full test suite). For bigger bumps run npm version <minor|major> and then npm publish; npm version also creates the matching git tag, which pi install git:github.com/tianhuil/pi-orca@v1 can pin to.
After publishing, verify the listing (note the scoped name is URL-encoded as @tianhuil%2fpi-orca in the registry URL):
# 1. keyword on the published manifest (drives the card badge + preview)
curl -s https://registry.npmjs.org/@tianhuil%2fpi-orca/latest | jq -r '.keywords[]' | grep -qx pi-package
# 2. visible to the gallery's exact query
curl -s "https://registry.npmjs.org/-/v1/search?text=keywords:pi-package%20@tianhuil/pi-orca&size=20" \
| jq -r '.objects[].package.name' | grep -x "@tianhuil/pi-orca"
Both should exit 0; the search index can lag a publish by a few minutes. (The package is scoped, so publishConfig.access: "public" in package.json is what makes plain npm publish list it publicly — don't remove it.)
License
MIT © 2026 Tianhui Michael Li