feat: harden into a type-safe, tested, CI-gated extension - #1
Open
mio-g wants to merge 1 commit into
Open
Conversation
The package worked but had no type checking, no tests, no CI gate, and
several silent-failure paths. This hardens it without changing its scope
or architecture.
Type safety:
- Remove `@ts-nocheck` from index.ts and ui.ts; both now pass `tsc --noEmit`
- Replace all 69 `any` occurrences with pi's exported types
- Add a pinned dev toolchain and strict tsconfig
Host compatibility:
- Narrow peerDependencies from `*` to `>=0.84.4`, and declare Node >=22.19.0
- Drop the dynamic import of pi's private `dist/core/model-resolver.js` in
favour of the exported `resolveModelScopeWithDiagnostics`
- Add a startup preflight over every consumed export; on mismatch the
extension warns via `ctx.ui.notify` naming the missing symbol instead of
registering half-working behavior
Session activity:
- Drop the monkey-patch of `InteractiveMode.prototype.setWorkingIndicator`,
which no longer exists in pi 0.84.4 and had been silently no-oping, so the
working/idle indicator was dead. Drive it from `agent_start`/`agent_end`
Path locks:
- Recognize mutating `git`, npm/pnpm/yarn/bun, `make`, `cargo`, `terraform`,
and `dbt` commands, which the previous regex whitelist missed entirely —
two sessions running `git checkout` in one repo were unguarded
- Reference-count same-session acquisitions so one tool call finishing does
not release a path another tool call still holds
- Tokenize shell commands so `sed -i 's|/usr/bin|/opt|'` no longer locks the
delimiter contents as paths
Correctness fixes found while testing:
- Child runtimes get a fresh resource loader, which dropped this extension
in sessions opened outside the parent cwd; re-register it explicitly
- Label the parent row `parent` rather than its cwd basename, which made the
protected row indistinguishable from a child
- Killing the active child deadlocked awaiting disposal of the runtime
executing the command; hand the terminal back first, then dispose
Also: no empty `catch {}` blocks remain (each reports via a debug channel or
carries a rationale), the kill key is configurable via
`~/.pi/agent/pi-sessions.json`, and the README states the real safety model —
locks are a per-tool-call race guard, not isolation, and are process-local.
Runtime dependencies remain zero.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hi — thanks for building this. It is the only pi multi-session tool that runs in-process, which makes it the only one that works in a plain terminal without tmux/Zellij/Ghostty. I have been using it daily and wanted to make it something I could depend on, so this PR adds the engineering scaffolding around the existing design.
No feature changes and no rewrite. The architecture and
LockManagerdesign are kept as-is; file names and the public surface are unchanged.Blast radius: 🟡 Medium
inferToolPathsnow recognizes mutatinggit, npm/pnpm/yarn/bun,make,cargo,terraform, anddbtcommands, so more tool calls take path locks than beforepeerDependenciesnarrows from*to>=0.84.4, andengines.nodeis declared as>=22.19.0(mirroring pi's own) — installs on older hosts now warn instead of failing at runtimedevDependencies)What this fixes
Silent drift against pi 0.84.4
The extension monkey-patched
InteractiveMode.prototype.setWorkingIndicator. That method no longer exists in pi 0.84.4, and the patch was guarded, so it silently no-oped — the working/idle indicator in the switcher was already dead. It is now driven from theagent_start/agent_endevents the extension already subscribes to.It also dynamically imported pi's compiled internals (
getPackageDir()/dist/core/model-resolver.js). That is replaced with the publicresolveModelScopeWithDiagnosticsexport. A startup preflight now checks every symbol the package consumes and, on mismatch, warns viactx.ui.notifynaming the missing symbol instead of registering half-working behavior.Lock coverage gap
The mutator whitelist did not include
git, so two sessions runninggit checkout/reset --hardin the same repo were completely unguarded. Same for package installs,make,cargo,terraform, anddbt. Two related fixes: same-session acquisitions are reference-counted (one tool call finishing no longer releases a path another still holds), and commands are tokenized sosed -i 's|/usr/bin|/opt|'no longer locks the delimiter contents as paths.Three bugs found while smoke-testing
Ctrl-Rstopped working after switching foldersType safety and validation
@ts-nocheckremoved from both files;tsc --noEmitpassesanyoccurrences replaced with pi's exported typesLockManager,inferToolPaths, the compatibility preflight, and the widgetcatch {}blocks now either report through a debug channel or carry a rationaleNotes
~/.pi/agent/pi-sessions.json({"killKey": "ctrl+x"}), since some terminals — kitty, for one — consumeCtrl-K. Default is unchanged.Happy to split this into smaller PRs, drop any part, or adjust the conventions to match your preferences — just say the word.