fix(pi-ext): stop importing @earendil-works/pi-ai at runtime
Some checks failed
CI/CD / typecheck (pull_request) Successful in 1m19s
CI/CD / test (pull_request) Successful in 1m23s
CI/CD / lint (pull_request) Successful in 3m10s
CI/CD / smoke (pull_request) Failing after 10m42s
CI/CD / build (pull_request) Failing after 13m32s
CI/CD / publish (pull_request) Has been skipped
Some checks failed
CI/CD / typecheck (pull_request) Successful in 1m19s
CI/CD / test (pull_request) Successful in 1m23s
CI/CD / lint (pull_request) Successful in 3m10s
CI/CD / smoke (pull_request) Failing after 10m42s
CI/CD / build (pull_request) Failing after 13m32s
CI/CD / publish (pull_request) Has been skipped
The extension failed to load outright on older pi installs: Failed to load extension ".../mcpctl-pi.ts": Cannot find module '@earendil-works/pi-ai' pi doesn't resolve an extension's bare specifiers the ordinary way — it hands jiti a hard-coded alias table built from its own dependencies, and that table differs between pi distributions. `@earendil-works/pi-coding- agent` (0.84.1) aliases both the `@earendil-works/*` and legacy `@mariozechner/*` names; `@mariozechner/pi-coding-agent` (0.73.1) aliases only the old ones. Neither resolves the other's namespace, so a single import outside the intersection takes the whole extension down: every tool, the /mcpctl command, and the status line, all gone. The only thing we used from pi-ai was `StringEnum`, a six-line wrapper over `Type.Unsafe`. Inlined as a local `stringEnum` with byte-identical output, so `typebox` — aliased by every published pi — is now the sole bare runtime import. The call site also passes `description` through, which the pi-ai version was silently dropping. Guarded in tests/config/pi-extension-embed.test.ts: any runtime import in the embedded sources that isn't `node:`, relative, or typebox now fails. Verified against both installs with the same active project: 0.73.1 reproduced the error verbatim before the change and loads cleanly after, and 0.84.1 keeps registering the gate tool exactly as before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014tsRTqhEC7YYYYaP3cBqo8
This commit is contained in:
@@ -101,9 +101,34 @@ src/pi-ext/
|
||||
mcp-http.ts # vendored Streamable-HTTP JSON-RPC client (no deps)
|
||||
```
|
||||
|
||||
The extension imports only from pi-bundled packages
|
||||
(`@earendil-works/pi-coding-agent`, `@earendil-works/pi-ai`, `typebox`), so it
|
||||
loads standalone.
|
||||
The extension imports only from pi-bundled packages, so it loads standalone.
|
||||
|
||||
### `typebox` is the only bare runtime import
|
||||
|
||||
pi does not let an extension resolve modules the ordinary way: it hands jiti a
|
||||
hard-coded alias table built from its *own* dependencies, and that table is not
|
||||
the same across pi distributions. The newer `@earendil-works/pi-coding-agent`
|
||||
aliases both the `@earendil-works/*` and the legacy `@mariozechner/*` names;
|
||||
older `@mariozechner/pi-coding-agent` installs (0.73.x and earlier) alias only
|
||||
the `@mariozechner/*` ones. Neither resolves the other's namespace.
|
||||
|
||||
So an import of anything outside the intersection kills the *whole* extension on
|
||||
someone else's pi — every tool, the `/mcpctl` command, the status line — with:
|
||||
|
||||
```
|
||||
Failed to load extension ".../mcpctl-pi.ts": Cannot find module '@earendil-works/pi-ai'
|
||||
```
|
||||
|
||||
which is exactly what `import { StringEnum } from "@earendil-works/pi-ai"` did.
|
||||
`typebox` is aliased by every published pi, so it is the only bare specifier
|
||||
allowed at runtime. Everything else must be a `node:` builtin, a relative path,
|
||||
an `import type` (erased before jiti resolves anything), or inlined — pi-ai's
|
||||
`StringEnum` is now a six-line local `stringEnum`. The
|
||||
`tests/config/pi-extension-embed.test.ts` guard fails the build on a reintroduced
|
||||
runtime import.
|
||||
|
||||
If a user does hit this error, check `type -a pi`: two installs on `$PATH` is the
|
||||
usual cause, and the extension has to load under whichever one wins.
|
||||
|
||||
## Typechecking
|
||||
|
||||
|
||||
Reference in New Issue
Block a user