diff --git a/package-lock.json b/package-lock.json index ba95e861..e0ca1ff1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "packages/coding-agent/examples/extensions/with-deps" ], "dependencies": { + "@mariozechner/jiti": "^2.6.5", "@mariozechner/pi-coding-agent": "^0.30.2", "get-east-asian-width": "^1.4.0" }, @@ -1781,9 +1782,9 @@ } }, "node_modules/@mariozechner/jiti": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@mariozechner/jiti/-/jiti-2.6.2.tgz", - "integrity": "sha512-CcFowm/fDWcEMH/F47DQcdawpLQb0nw+WR+hZOv8mgAeACFJxE9uo3cXjUk/5Cl3j23t/oxvtxxUtlBCUIGeQg==", + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@mariozechner/jiti/-/jiti-2.6.5.tgz", + "integrity": "sha512-faGUlTcXka5l7rv0lP3K3vGW/ejRuOS24RR2aSFWREUQqzjgdsuWNo/IiPqL3kWRGt6Ahl2+qcDAwtdeWeuGUw==", "license": "MIT", "dependencies": { "std-env": "^3.10.0", diff --git a/package.json b/package.json index ad5d33be..33ec59ce 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ }, "version": "0.0.3", "dependencies": { + "@mariozechner/jiti": "^2.6.5", "@mariozechner/pi-coding-agent": "^0.30.2", "get-east-asian-width": "^1.4.0" } diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 72c8157c..ac5cc7cc 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -19,6 +19,7 @@ - Fix API key resolution after model switches by using provider argument ([#691](https://github.com/badlogic/pi-mono/pull/691) by [@joshp123](https://github.com/joshp123)) - Fixed z.ai thinking/reasoning: thinking toggle now correctly enables/disables thinking for z.ai models ([#688](https://github.com/badlogic/pi-mono/issues/688)) +- Fixed extension loading in compiled Bun binary: extensions with local file imports now work correctly. Updated `@mariozechner/jiti` to v2.6.5 which bundles babel for Bun binary compatibility. ([#681](https://github.com/badlogic/pi-mono/issues/681)) ## [0.45.3] - 2026-01-13 diff --git a/packages/coding-agent/docs/sdk.md b/packages/coding-agent/docs/sdk.md index 2cfb00b2..d231b291 100644 --- a/packages/coding-agent/docs/sdk.md +++ b/packages/coding-agent/docs/sdk.md @@ -735,12 +735,12 @@ import { discoverAuthStorage, discoverModels, discoverSkills, - discoverHooks, - discoverCustomTools, + discoverExtensions, discoverContextFiles, discoverPromptTemplates, loadSettings, buildSystemPrompt, + createEventBus, } from "@mariozechner/pi-coding-agent"; // Auth and Models @@ -754,19 +754,16 @@ const builtIn = getModel("anthropic", "claude-opus-4-5"); // Built-in only // Skills const { skills, warnings } = discoverSkills(cwd, agentDir, skillsSettings); -// Hooks (async - loads TypeScript) -// Pass eventBus to share pi.events across hooks/tools +// Extensions (async - loads TypeScript) +// Pass eventBus to share pi.events across extensions const eventBus = createEventBus(); -const hooks = await discoverHooks(eventBus, cwd, agentDir); - -// Custom tools (async - loads TypeScript) -const tools = await discoverCustomTools(eventBus, cwd, agentDir); +const { extensions, errors } = await discoverExtensions(eventBus, cwd, agentDir); // Context files const contextFiles = discoverContextFiles(cwd, agentDir); // Prompt templates -const commands = discoverPromptTemplates(cwd, agentDir); +const templates = discoverPromptTemplates(cwd, agentDir); // Settings (global + project merged) const settings = loadSettings(cwd, agentDir); @@ -816,8 +813,8 @@ import { SettingsManager, readTool, bashTool, - type HookFactory, - type CustomTool, + type ExtensionFactory, + type ToolDefinition, } from "@mariozechner/pi-coding-agent"; // Set up auth storage (custom location) @@ -831,16 +828,16 @@ if (process.env.MY_KEY) { // Model registry (no custom models.json) const modelRegistry = new ModelRegistry(authStorage); -// Inline hook -const auditHook: HookFactory = (api) => { - api.on("tool_call", async (event) => { +// Inline extension +const auditExtension: ExtensionFactory = (pi) => { + pi.on("tool_call", async (event) => { console.log(`[Audit] ${event.toolName}`); return undefined; }); }; // Inline tool -const statusTool: CustomTool = { +const statusTool: ToolDefinition = { name: "status", label: "Status", description: "Get system status", @@ -872,8 +869,8 @@ const { session } = await createAgentSession({ systemPrompt: "You are a minimal assistant. Be concise.", tools: [readTool, bashTool], - customTools: [{ tool: statusTool }], - hooks: [{ factory: auditHook }], + customTools: [statusTool], + extensions: [auditExtension], skills: [], contextFiles: [], promptTemplates: [], @@ -961,7 +958,7 @@ The SDK is preferred when: - You want type safety - You're in the same Node.js process - You need direct access to agent state -- You want to customize tools/hooks programmatically +- You want to customize tools/extensions programmatically RPC mode is preferred when: - You're integrating from another language @@ -984,12 +981,11 @@ discoverModels // Discovery discoverSkills -discoverHooks -discoverCustomTools +discoverExtensions discoverContextFiles discoverPromptTemplates -// Event Bus (for shared hook/tool communication) +// Event Bus (for shared extension communication) createEventBus // Helpers @@ -1015,8 +1011,9 @@ createGrepTool, createFindTool, createLsTool // Types type CreateAgentSessionOptions type CreateAgentSessionResult -type CustomTool -type HookFactory +type ExtensionFactory +type ExtensionAPI +type ToolDefinition type Skill type PromptTemplate type Settings @@ -1024,28 +1021,4 @@ type SkillsSettings type Tool ``` -For hook types, import from the hooks subpath: - -```typescript -import type { - HookAPI, - HookMessage, - HookFactory, - HookEventContext, - HookCommandContext, - ToolCallEvent, - ToolResultEvent, -} from "@mariozechner/pi-coding-agent/hooks"; -``` - -For message utilities: - -```typescript -import { isHookMessage, createHookMessage } from "@mariozechner/pi-coding-agent"; -``` - -For config utilities: - -```typescript -import { getAgentDir } from "@mariozechner/pi-coding-agent/config"; -``` +For extension types, see [extensions.md](extensions.md) for the full API. diff --git a/packages/coding-agent/examples/sdk/README.md b/packages/coding-agent/examples/sdk/README.md index 9096c0bc..43872409 100644 --- a/packages/coding-agent/examples/sdk/README.md +++ b/packages/coding-agent/examples/sdk/README.md @@ -37,9 +37,8 @@ import { discoverModels, discoverSkills, discoverExtensions, - discoverCustomTools, discoverContextFiles, - discoverSlashCommands, + discoverPromptTemplates, loadSettings, buildSystemPrompt, ModelRegistry, @@ -92,7 +91,7 @@ const { session } = await createAgentSession({ extensions: [{ factory: myExtension }], skills: [], contextFiles: [], - slashCommands: [], + promptTemplates: [], sessionManager: SessionManager.inMemory(), }); @@ -123,7 +122,7 @@ await session.prompt("Hello"); | `additionalExtensionPaths` | `[]` | Merge with discovery | | `skills` | Discovered | Skills for prompt | | `contextFiles` | Discovered | AGENTS.md files | -| `slashCommands` | Discovered | File commands | +| `promptTemplates` | Discovered | Prompt templates (slash commands) | | `sessionManager` | `SessionManager.create(cwd)` | Persistence | | `settingsManager` | From agentDir | Settings overrides | diff --git a/packages/coding-agent/src/core/extensions/loader.ts b/packages/coding-agent/src/core/extensions/loader.ts index cb79e0a1..d581b528 100644 --- a/packages/coding-agent/src/core/extensions/loader.ts +++ b/packages/coding-agent/src/core/extensions/loader.ts @@ -10,6 +10,7 @@ import * as os from "node:os"; import * as path from "node:path"; import { fileURLToPath } from "node:url"; import { createJiti } from "@mariozechner/jiti"; +import * as _bundledPiAgentCore from "@mariozechner/pi-agent-core"; import * as _bundledPiAi from "@mariozechner/pi-ai"; import type { KeyId } from "@mariozechner/pi-tui"; import * as _bundledPiTui from "@mariozechner/pi-tui"; @@ -18,6 +19,9 @@ import * as _bundledPiTui from "@mariozechner/pi-tui"; // The virtualModules option then makes them available to extensions. import * as _bundledTypebox from "@sinclair/typebox"; import { getAgentDir, isBunBinary } from "../../config.js"; +// NOTE: This import works because loader.ts exports are NOT re-exported from index.ts, +// avoiding a circular dependency. Extensions can import from @mariozechner/pi-coding-agent. +import * as _bundledPiCodingAgent from "../../index.js"; import { createEventBus, type EventBus } from "../event-bus.js"; import type { ExecOptions } from "../exec.js"; import { execCommand } from "../exec.js"; @@ -33,19 +37,12 @@ import type { } from "./types.js"; /** Modules available to extensions via virtualModules (for compiled Bun binary) */ -let _lazyPiCodingAgent: unknown; const VIRTUAL_MODULES: Record = { "@sinclair/typebox": _bundledTypebox, + "@mariozechner/pi-agent-core": _bundledPiAgentCore, "@mariozechner/pi-tui": _bundledPiTui, "@mariozechner/pi-ai": _bundledPiAi, - // Lazy-loaded to avoid circular dependency (loader.ts is part of pi-coding-agent) - get "@mariozechner/pi-coding-agent"() { - if (!_lazyPiCodingAgent) { - // Dynamic require after module initialization completes - _lazyPiCodingAgent = require("../../index.js"); - } - return _lazyPiCodingAgent; - }, + "@mariozechner/pi-coding-agent": _bundledPiCodingAgent, }; const require = createRequire(import.meta.url); @@ -66,6 +63,7 @@ function getAliases(): Record { _aliases = { "@mariozechner/pi-coding-agent": packageIndex, + "@mariozechner/pi-agent-core": require.resolve("@mariozechner/pi-agent-core"), "@mariozechner/pi-tui": require.resolve("@mariozechner/pi-tui"), "@mariozechner/pi-ai": require.resolve("@mariozechner/pi-ai"), "@sinclair/typebox": typeboxRoot, diff --git a/packages/coding-agent/src/index.ts b/packages/coding-agent/src/index.ts index 4335da4f..545d306a 100644 --- a/packages/coding-agent/src/index.ts +++ b/packages/coding-agent/src/index.ts @@ -88,8 +88,6 @@ export type { UserBashEventResult, } from "./core/extensions/index.js"; export { - createExtensionRuntime, - discoverAndLoadExtensions, ExtensionRunner, isBashToolResult, isEditToolResult, @@ -98,7 +96,6 @@ export { isLsToolResult, isReadToolResult, isWriteToolResult, - loadExtensions, wrapRegisteredTool, wrapRegisteredTools, wrapToolsWithExtensions,