@oh-my-pi/pi-coding-agent 16.1.17 → 16.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/cli.js +17872 -17571
  3. package/dist/types/cli/usage-cli.d.ts +14 -0
  4. package/dist/types/commands/token.d.ts +9 -0
  5. package/dist/types/edit/hashline/filesystem.d.ts +1 -18
  6. package/dist/types/extensibility/plugins/legacy-pi-bundled-keys.d.ts +10 -0
  7. package/dist/types/extensibility/plugins/legacy-pi-bundled-registry.d.ts +10 -0
  8. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +43 -27
  9. package/dist/types/internal-urls/skill-protocol.d.ts +2 -2
  10. package/dist/types/internal-urls/types.d.ts +3 -0
  11. package/dist/types/modes/components/custom-editor.d.ts +0 -2
  12. package/dist/types/modes/components/extensions/extension-dashboard.d.ts +26 -10
  13. package/dist/types/modes/components/extensions/extension-list.d.ts +13 -0
  14. package/dist/types/modes/controllers/input-controller.d.ts +0 -1
  15. package/dist/types/tools/path-utils.d.ts +3 -0
  16. package/package.json +12 -12
  17. package/scripts/build-binary.ts +29 -16
  18. package/scripts/generate-legacy-pi-bundled-registry.ts +404 -0
  19. package/src/cli/usage-cli.ts +35 -4
  20. package/src/commands/token.ts +54 -0
  21. package/src/edit/hashline/filesystem.ts +14 -0
  22. package/src/extensibility/plugins/legacy-pi-bundled-keys.ts +987 -0
  23. package/src/extensibility/plugins/legacy-pi-bundled-registry.ts +3352 -0
  24. package/src/extensibility/plugins/legacy-pi-compat.ts +264 -131
  25. package/src/internal-urls/local-protocol.ts +116 -9
  26. package/src/internal-urls/skill-protocol.ts +3 -3
  27. package/src/internal-urls/types.ts +3 -0
  28. package/src/modes/components/custom-editor.test.ts +7 -5
  29. package/src/modes/components/custom-editor.ts +6 -16
  30. package/src/modes/components/extensions/extension-dashboard.ts +221 -165
  31. package/src/modes/components/extensions/extension-list.ts +66 -33
  32. package/src/modes/controllers/input-controller.ts +0 -71
  33. package/src/modes/controllers/selector-controller.ts +4 -0
  34. package/src/session/messages.ts +70 -47
  35. package/src/tools/ast-edit.ts +1 -0
  36. package/src/tools/ast-grep.ts +1 -0
  37. package/src/tools/find.ts +1 -0
  38. package/src/tools/path-utils.ts +4 -0
  39. package/src/tools/read.ts +43 -17
  40. package/src/tools/search.ts +4 -0
@@ -78,4 +78,18 @@ export declare function formatUsageBreakdown(reports: UsageReport[], accounts: U
78
78
  * peak-per-bucket sparkline per limit window plus latest/peak percentages.
79
79
  */
80
80
  export declare function formatUsageHistory(entries: UsageHistoryEntry[], sinceMs: number, nowMs: number, redaction?: Map<string, string>): string;
81
+ /**
82
+ * Keep only accounts worth a usage row: those whose provider has a usage
83
+ * provider, so a missing report is a real gap rather than the absence of any
84
+ * usage concept. Providers with no usage endpoint (web-search keys, local /
85
+ * keyless servers, inference providers without a usage API) would only ever
86
+ * render as noise, so they are dropped.
87
+ *
88
+ * `hasUsageProvider` is injected (in practice {@link AuthStorage.usageProviderFor})
89
+ * so custom/broker resolvers stay authoritative — no provider list is duplicated
90
+ * here. An explicit `--provider` request bypasses the cull, so
91
+ * `omp usage --provider xai` can still confirm the stored credential has no
92
+ * usage endpoint.
93
+ */
94
+ export declare function selectReportableAccounts(accounts: UsageAccountIdentity[], hasUsageProvider: (provider: string) => boolean, explicitProvider?: string): UsageAccountIdentity[];
81
95
  export declare function runUsageCommand(cmd: UsageCommandArgs): Promise<void>;
@@ -19,6 +19,15 @@ export default class Token extends Command {
19
19
  description: string;
20
20
  default: boolean;
21
21
  };
22
+ account: import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"integer"> & {
23
+ char: string;
24
+ description: string;
25
+ };
26
+ list: import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"boolean"> & {
27
+ char: string;
28
+ description: string;
29
+ default: boolean;
30
+ };
22
31
  };
23
32
  static examples: string[];
24
33
  run(): Promise<void>;
@@ -1,21 +1,3 @@
1
- /**
2
- * Coding-agent specific {@link Filesystem} adapter for the hashline patcher.
3
- *
4
- * Wires hashline's storage abstraction to the agent runtime:
5
- *
6
- * - Section paths are resolved through the plan-mode redirect so a bare
7
- * `PLAN.md` lands at the canonical session artifact location.
8
- * - Reads go through `readEditFileText` (notebook-aware) and the
9
- * auto-generated-file guard.
10
- * - Writes go through `serializeEditFileText` (notebook-aware) and the
11
- * LSP writethrough, with FS-scan cache invalidation on success. The
12
- * resulting `FileDiagnosticsResult` is captured per-path so the
13
- * orchestrator can attach it to the tool result.
14
- *
15
- * Construct one per `executeHashlineSingle` call: per-section state
16
- * (batch request, diagnostics) lives on the instance and isn't safe to
17
- * share across concurrent edit tools.
18
- */
19
1
  import { Filesystem, type WriteResult } from "@oh-my-pi/hashline";
20
2
  import type { FileDiagnosticsResult, WritethroughCallback, WritethroughDeferredHandle } from "../../lsp";
21
3
  import type { ToolSession } from "../../tools";
@@ -50,6 +32,7 @@ export declare class HashlineFilesystem extends Filesystem {
50
32
  consumeDiagnostics(path: string): FileDiagnosticsResult | undefined;
51
33
  resolveAbsolute(relativePath: string): string;
52
34
  canonicalPath(relativePath: string): string;
35
+ allowTagPathRecovery(authoredPath: string, resolvedPath: string): boolean;
53
36
  readText(relativePath: string): Promise<string>;
54
37
  preflightWrite(relativePath: string): Promise<void>;
55
38
  writeText(relativePath: string, content: string): Promise<WriteResult>;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Canonical keys served by the `omp-legacy-pi-bundled:` virtual namespace.
3
+ * Mirrors `Object.keys(BUNDLED_PI_REGISTRY)` from
4
+ * `legacy-pi-bundled-keys.ts`'s sibling registry file. `legacy-pi-compat.ts`
5
+ * statically imports this set to seed `LEGACY_PI_PACKAGE_ROOT_OVERRIDES` in
6
+ * compiled-binary mode without dragging the heavy registry's transitive
7
+ * graph into dev/test runs (the registry itself stays behind a dynamic
8
+ * import — see `ensureBundledRegistryLoaded` in `legacy-pi-compat.ts`).
9
+ */
10
+ export declare const BUNDLED_PI_REGISTRY_KEYS: ReadonlySet<string>;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Canonical specifier → live module namespace. Keys MUST match the right-hand
3
+ * side of `bundledRegistryVirtualSpecifier(...)` calls in
4
+ * `legacy-pi-compat.ts`; the synthesizer enumerates each namespace's own
5
+ * enumerable exports at extension load time. The companion
6
+ * `legacy-pi-bundled-keys.ts` mirrors `Object.keys(BUNDLED_PI_REGISTRY)` and
7
+ * is statically imported by `legacy-pi-compat.ts` to seed the override map
8
+ * without paying the cascade above.
9
+ */
10
+ export declare const BUNDLED_PI_REGISTRY: Readonly<Record<string, Readonly<Record<string, unknown>>>>;
@@ -1,26 +1,16 @@
1
1
  import * as path from "node:path";
2
2
  /**
3
- * Compute the bunfs package root from the compiled binary's `import.meta.dir`
4
- * (or any stand-in supplied by tests). Bun compiled binaries report one of:
5
- *
6
- * - the bunfs mount root itself — `/$bunfs/root` or `<drive>:\~BUN\root` (Bun
7
- * 1.2.x and early 1.3.x). Append `packages` for the canonical layout.
8
- * - the bunfs mount root followed by the binary's basename — `//root/<bin>`
9
- * on POSIX or `<drive>:\~BUN\root\<bin>.exe` on Windows (observed on Bun
10
- * 1.3.14 with the cross-compiled `omp-darwin-arm64` release asset issue
11
- * #3329). The trailing segment is stripped so the result still lands on
12
- * `<root>/packages`.
13
- * - the module's own source directory if a future Bun release switches to
14
- * module-specific `import.meta.dir` values:
15
- * `<bunfs>/packages/coding-agent/src/extensibility/plugins`.
16
- * The bunfs-root-with-binary branch slices the original `metaDir`, and
17
- * `bunfsPath` uses a matching double-slash-preserving join, so the bunfs-native
18
- * prefix is preserved verbatim — `path.posix.join` collapses `//root` to
19
- * `/root`, but Bun's bunfs lookup is keyed on the exact `//root` form.
20
- *
21
- * Exported for tests; production callers use `BUNFS_PACKAGE_ROOT` below.
3
+ * Test seam: builds the synthetic ES module source for a virtual specifier
4
+ * against an explicit registry. Pure (no globalThis read); the emitted source
5
+ * still routes runtime lookups through `globalThis[BUNDLED_REGISTRY_GLOBAL]`.
6
+ */
7
+ export declare function __synthesizeLegacyPiBundledSourceWithRegistry(registryKey: string, registry: Readonly<Record<string, Readonly<Record<string, unknown>>>>): string;
8
+ /**
9
+ * Test seam: returns the globalThis key the synthetic loader reads from. Tests
10
+ * assert that the emitted source addresses the exact stash key the install
11
+ * function writes to, so a rename can't break extension loads silently.
22
12
  */
23
- export declare function __computeBunfsPackageRoot(metaDir: string, pathImpl?: typeof path): string;
13
+ export declare function __getLegacyPiBundledRegistryGlobal(): string;
24
14
  /**
25
15
  * Compute the package root for the npm prebuilt `dist/cli.js` bundle.
26
16
  *
@@ -31,20 +21,46 @@ export declare function __computeBunfsPackageRoot(metaDir: string, pathImpl?: ty
31
21
  */
32
22
  export declare function __computeBundledSelfPackageRoot(metaDir: string, pathImpl?: typeof path): string;
33
23
  /**
34
- * Join a computed bunfs package root with descendants without collapsing
35
- * Bun's POSIX `//root` mount prefix.
24
+ * Resolve the path the TypeBox compatibility shim ships at, then drop it when
25
+ * the source file is missing.
36
26
  *
37
- * Exported for tests; production callers use `bunfsPath` below.
27
+ * In compiled-binary mode the shim is served through the
28
+ * `omp-legacy-pi-bundled:` virtual namespace (issue #3423) — bunfs paths are
29
+ * unreachable on Bun 1.3.14+, so the virtual specifier is always available and
30
+ * needs no filesystem probe. In dev / source-link / installed-package mode the
31
+ * shim is an on-disk source file; validation mirrors
32
+ * `__validateLegacyPiPackageRootOverrides` (#2168): if the computed candidate
33
+ * doesn't exist (e.g. an install that dropped the source — issue #3414),
34
+ * `resolveTypeBoxSpecifier` returns `undefined` and
35
+ * `rewriteLegacyExtensionSource` leaves bare `typebox` / `@sinclair/typebox`
36
+ * specifiers alone, so Bun falls through to native resolution against the
37
+ * extension's own `node_modules`.
38
+ *
39
+ * Exported for tests; production callers use `TYPEBOX_SHIM_PATH`.
38
40
  */
39
- export declare function __joinBunfsPath(root: string, segments: readonly string[], pathImpl?: typeof path): string;
41
+ export declare function __resolveTypeBoxShimPath(isCompiled: boolean, sourcePath: string, pathExistsSync?: (p: string) => boolean): string | null;
40
42
  /**
41
- * Drop overrides whose targets are missing on disk so they can fall through to
42
- * the canonical-resolution path. Exported for the test seam in #2168.
43
+ * Drop overrides whose filesystem targets are missing so they can fall
44
+ * through to the canonical-resolution path. Virtual `omp-legacy-pi-bundled:`
45
+ * entries always pass — the bundled registry is the source of truth in
46
+ * compiled-binary mode where bunfs paths are unreachable (issue #3423).
43
47
  *
44
- * `pathExistsSync` defaults to `fs.existsSync`; the tests inject a stub to
48
+ * `pathExistsSync` defaults to `fs.existsSync`; tests inject a stub to
45
49
  * simulate the missing-entrypoint failure mode without touching the real FS.
46
50
  */
47
51
  export declare function __validateLegacyPiPackageRootOverrides(candidates: Record<string, string>, pathExistsSync?: (p: string) => boolean): Record<string, string>;
52
+ /**
53
+ * Compute the override map keyed by every canonical specifier the host serves
54
+ * directly: the pi-ai / pi-coding-agent roots (compat shims that re-attach
55
+ * legacy helpers) plus, in compiled-binary mode, every other canonical pi-*
56
+ * package root AND every non-wildcard subpath registered in the bundled
57
+ * registry (see `legacy-pi-bundled-keys.ts`). Subpath coverage is what stops
58
+ * `@(scope)/pi-ai/oauth` and friends from falling through to the extension's
59
+ * own — possibly absent — peer install when bunfs filesystem walks fail
60
+ * (issue #3442 follow-up to #3423). Exported as a test seam so the
61
+ * compiled-binary branch is verifiable from dev tests.
62
+ */
63
+ export declare function __buildLegacyPiPackageRootOverrides(isCompiled: boolean): Record<string, string>;
48
64
  /**
49
65
  * Load a legacy Pi extension module from its real on-disk location.
50
66
  *
@@ -1,4 +1,4 @@
1
- import type { InternalResource, InternalUrl, ProtocolHandler, UrlCompletion } from "./types";
1
+ import type { InternalResource, InternalUrl, ProtocolHandler, ResolveContext, UrlCompletion } from "./types";
2
2
  /**
3
3
  * Validate that a path is safe (no traversal, no absolute paths).
4
4
  */
@@ -9,6 +9,6 @@ export declare function validateRelativePath(relativePath: string): void;
9
9
  export declare class SkillProtocolHandler implements ProtocolHandler {
10
10
  readonly scheme = "skill";
11
11
  readonly immutable = true;
12
- resolve(url: InternalUrl): Promise<InternalResource>;
12
+ resolve(url: InternalUrl, context?: ResolveContext): Promise<InternalResource>;
13
13
  complete(): Promise<UrlCompletion[]>;
14
14
  }
@@ -4,6 +4,7 @@
4
4
  * Internal URLs (`agent://`, `artifact://`, `history://`, `issue://`, `local://`, `mcp://`, `memory://`, `omp://`, `pr://`, `rule://`, `skill://`, and `vault://`) are resolved by tools like read,
5
5
  * providing access to agent outputs and server resources without exposing filesystem paths.
6
6
  */
7
+ import type { Skill } from "../extensibility/skills";
7
8
  import type { LocalProtocolOptions } from "./local-protocol";
8
9
  /**
9
10
  * Raw resource payload returned by protocol handlers. The `immutable` flag is
@@ -85,6 +86,8 @@ export interface ResolveContext {
85
86
  * [#1608](https://github.com/can1357/oh-my-pi/issues/1608).
86
87
  */
87
88
  localProtocolOptions?: LocalProtocolOptions;
89
+ /** Calling session's loaded skills. Prefer this over process-global skill state. */
90
+ skills?: readonly Skill[];
88
91
  }
89
92
  /**
90
93
  * Caller context for write operations dispatched to host-owned URI handlers.
@@ -87,8 +87,6 @@ export declare class CustomEditor extends Editor {
87
87
  onPasteImage?: () => Promise<boolean>;
88
88
  /** Called when a bracketed paste contains one or more image-file paths. */
89
89
  onPasteImagePath?: (path: string) => void | Promise<void>;
90
- /** Called when a bracketed paste contains one or more non-image file paths. */
91
- onPasteFilePath?: (path: string) => void | Promise<void>;
92
90
  /** Called when the configured raw text-paste shortcut is pressed. */
93
91
  onPasteTextRaw?: () => void;
94
92
  /** Called when the configured dequeue shortcut is pressed. */
@@ -1,19 +1,29 @@
1
1
  /**
2
- * ExtensionDashboard - Tabbed layout for the Extension Control Center.
2
+ * ExtensionDashboard - Fullscreen alternate-screen control center for extensions.
3
3
  *
4
- * Layout:
5
- * - Top: Horizontal tab bar for provider selection
6
- * - Body: 2-column grid (inventory list | preview panel)
4
+ * Chrome mirrors the `/settings` overlay: a titled rounded box, a shared
5
+ * {@link TabBar} for provider selection, and a two-column body (inventory list |
6
+ * inspector). Both panes are mouse-aware wheel scrolls, hover highlights, and
7
+ * clicks select/activate — routed from a single SGR-mouse handler.
7
8
  *
8
9
  * Navigation:
9
- * - TAB/Shift+TAB: Cycle through provider tabs
10
- * - Up/Down/j/k: Navigate list
11
- * - Space: Toggle selected item (or master switch)
12
- * - Esc: Close dashboard (clears search first if active)
10
+ * - Tab/Shift+Tab or ←/→: switch provider tab
11
+ * - Up/Down/j/k or wheel: move list selection
12
+ * - Space/Enter or click: toggle selected item (or provider master switch)
13
+ * - Wheel over the inspector: scroll the detail pane
14
+ * - Esc: clear search (if active) then close
13
15
  */
14
- import { Container } from "@oh-my-pi/pi-tui";
16
+ import { type Component, type Tab } from "@oh-my-pi/pi-tui";
15
17
  import { Settings } from "../../../config/settings";
16
- export declare class ExtensionDashboard extends Container {
18
+ import type { ProviderTab } from "./types";
19
+ /**
20
+ * Map dashboard provider tabs to {@link TabBar} tabs. Empty *enabled* providers
21
+ * are muted — skipped by keyboard nav and unclickable; disabled providers stay
22
+ * selectable (with a leading disabled glyph) so their master switch can be
23
+ * re-enabled from the list. The "all" tab is never muted or marked.
24
+ */
25
+ export declare function buildTabBarTabs(tabs: ProviderTab[]): Tab[];
26
+ export declare class ExtensionDashboard implements Component {
17
27
  #private;
18
28
  private readonly cwd;
19
29
  private readonly settings;
@@ -22,6 +32,12 @@ export declare class ExtensionDashboard extends Container {
22
32
  onRequestRender?: () => void;
23
33
  private constructor();
24
34
  static create(cwd: string, settings?: Settings | null, terminalHeight?: number): Promise<ExtensionDashboard>;
35
+ /**
36
+ * Fullscreen frame: titled top border, the tab row(s), a divider, the
37
+ * two-column body sized to fill the viewport, a divider, the footer hint, and
38
+ * the bottom border. Records row geometry for mouse hit-testing.
39
+ */
25
40
  render(width: number): readonly string[];
41
+ invalidate(): void;
26
42
  handleInput(data: string): void;
27
43
  }
@@ -35,5 +35,18 @@ export declare class ExtensionList implements Component {
35
35
  clearSearch(): void;
36
36
  invalidate(): void;
37
37
  render(width: number): readonly string[];
38
+ /** Highlight the row under the pointer (null clears). */
39
+ setHoverIndex(index: number | null): void;
40
+ /**
41
+ * Map a 0-based line within this component's render to the absolute list-item
42
+ * index, or null when the line is the search banner, a padding row, or outside
43
+ * the visible window. The first two lines are the search banner and a blank
44
+ * separator; item rows follow, windowed at the current scroll offset.
45
+ */
46
+ hitTest(line: number): number | null;
47
+ /** Wheel notch: move the selection (and the inspector) one row. */
48
+ handleWheel(delta: -1 | 1): void;
49
+ /** Click: select the row under the pointer, or activate it when already selected. */
50
+ handleClick(line: number): void;
38
51
  handleInput(data: string): void;
39
52
  }
@@ -38,7 +38,6 @@ export declare class InputController {
38
38
  abort?: boolean;
39
39
  currentText?: string;
40
40
  }): number;
41
- handleFilePathPaste(filePath: string): Promise<void>;
42
41
  handleImagePathPaste(path: string): Promise<void>;
43
42
  handleImagePaste(): Promise<boolean>;
44
43
  handleClipboardTextRawPaste(): Promise<void>;
@@ -1,3 +1,4 @@
1
+ import type { Skill } from "../extensibility/skills";
1
2
  import { type LocalProtocolOptions } from "../internal-urls";
2
3
  export declare function expandTilde(filePath: string, home?: string): string;
3
4
  export declare function expandPath(filePath: string): string;
@@ -189,6 +190,8 @@ export interface ToolScopeOptions {
189
190
  signal?: AbortSignal;
190
191
  /** Calling session's `local://` root mapping — pins resolutions to the calling session. */
191
192
  localProtocolOptions?: LocalProtocolOptions;
193
+ /** Calling session's loaded skills — lets skill:// resolve without process-global state. */
194
+ skills?: readonly Skill[];
192
195
  }
193
196
  export interface ToolScopeResolution {
194
197
  searchPath: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-coding-agent",
4
- "version": "16.1.17",
4
+ "version": "16.1.19",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -48,17 +48,17 @@
48
48
  "@agentclientprotocol/sdk": "0.25.0",
49
49
  "@babel/parser": "^7.29.7",
50
50
  "@mozilla/readability": "^0.6.0",
51
- "@oh-my-pi/hashline": "16.1.17",
52
- "@oh-my-pi/omp-stats": "16.1.17",
53
- "@oh-my-pi/pi-agent-core": "16.1.17",
54
- "@oh-my-pi/pi-ai": "16.1.17",
55
- "@oh-my-pi/pi-catalog": "16.1.17",
56
- "@oh-my-pi/pi-mnemopi": "16.1.17",
57
- "@oh-my-pi/pi-natives": "16.1.17",
58
- "@oh-my-pi/pi-tui": "16.1.17",
59
- "@oh-my-pi/pi-utils": "16.1.17",
60
- "@oh-my-pi/pi-wire": "16.1.17",
61
- "@oh-my-pi/snapcompact": "16.1.17",
51
+ "@oh-my-pi/hashline": "16.1.19",
52
+ "@oh-my-pi/omp-stats": "16.1.19",
53
+ "@oh-my-pi/pi-agent-core": "16.1.19",
54
+ "@oh-my-pi/pi-ai": "16.1.19",
55
+ "@oh-my-pi/pi-catalog": "16.1.19",
56
+ "@oh-my-pi/pi-mnemopi": "16.1.19",
57
+ "@oh-my-pi/pi-natives": "16.1.19",
58
+ "@oh-my-pi/pi-tui": "16.1.19",
59
+ "@oh-my-pi/pi-utils": "16.1.19",
60
+ "@oh-my-pi/pi-wire": "16.1.19",
61
+ "@oh-my-pi/snapcompact": "16.1.19",
62
62
  "@opentelemetry/api": "^1.9.1",
63
63
  "@opentelemetry/context-async-hooks": "^2.7.1",
64
64
  "@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
@@ -51,6 +51,17 @@ async function main(): Promise<void> {
51
51
  try {
52
52
  await runCommand(["bun", "--cwd=../stats", "scripts/generate-client-bundle.ts", "--generate"]);
53
53
  await runCommand(["bun", "scripts/generate-docs-index.ts", "--generate"]);
54
+ // `legacy-pi-bundled-registry.ts` static-imports
55
+ // `@oh-my-pi/pi-coding-agent/export/html` (one of pi-coding-agent's
56
+ // named subpath exports, see scripts/generate-legacy-pi-bundled-registry.ts),
57
+ // whose source pulls in `tool-views.generated.js`. The root
58
+ // `package.json` "prepare" lifecycle hook builds that file on
59
+ // `bun install`, but a clean binary build that skips install hooks
60
+ // would `bun build --compile` against the registry entry and fail
61
+ // resolving the missing generated bundle. Rebuilding the tool views
62
+ // here makes the compile self-contained and matches what `prepack`
63
+ // does for the npm bundle.
64
+ await runCommand(["bun", "--cwd=../collab-web", "run", "build:tool-views"]);
54
65
  await runCommand(
55
66
  ["bun", "--cwd=../natives", "run", "embed:native"],
56
67
  crossTarget
@@ -58,6 +69,15 @@ async function main(): Promise<void> {
58
69
  : Bun.env,
59
70
  );
60
71
  await runCommand(["bun", "scripts/embed-mupdf-wasm.ts", "--generate"]);
72
+ // Regenerate the bundled-pi registry + key set before the compile so any
73
+ // new pi-* subpath export added under `packages/*/package.json` is served
74
+ // from the host's in-process copy. Without this, `bun build --compile`
75
+ // would freeze whatever the committed registry happened to enumerate at
76
+ // the time of the last manual `--generate`, and a new subpath added
77
+ // since then would crash extension validation with `Cannot find module`
78
+ // (issue #3442). The generator also normalizes formatting, so the diff
79
+ // against the committed copy stays clean.
80
+ await runCommand(["bun", "scripts/generate-legacy-pi-bundled-registry.ts", "--generate"]);
61
81
  try {
62
82
  const buildEnv = shouldAdhocSignDarwinBinary() ? { ...Bun.env, BUN_NO_CODESIGN_MACHO_BINARY: "1" } : Bun.env;
63
83
  await runCommand(
@@ -82,22 +102,15 @@ async function main(): Promise<void> {
82
102
  "--root",
83
103
  ".",
84
104
  "./packages/coding-agent/src/cli.ts",
85
- // Legacy pi-* extension compat entrypoints served by
86
- // `legacy-pi-compat.ts`. These are reached via computed bunfs paths
87
- // (which `--compile`'s static analyzer cannot trace), so each must be
88
- // listed here to land in bunfs at
89
- // `/$bunfs/root/packages/<pkg>/<entry>.js`. The coding-agent's own
90
- // `./src/index.ts` is intentionally NOT listed: bun --compile silently
91
- // breaks the CLI entry when the same package's barrel appears as an
92
- // extra entrypoint (issue #1474), so legacy `pi-coding-agent` imports
93
- // resolve through `legacy-pi-coding-agent-shim.ts` instead.
94
- "./packages/agent/src/index.ts",
95
- "./packages/natives/native/index.js",
96
- "./packages/tui/src/index.ts",
97
- "./packages/utils/src/index.ts",
98
- "./packages/coding-agent/src/extensibility/typebox.ts",
99
- "./packages/coding-agent/src/extensibility/legacy-pi-ai-shim.ts",
100
- "./packages/coding-agent/src/extensibility/legacy-pi-coding-agent-shim.ts",
105
+ // Legacy pi-* extension compat surfaces (host packages + shims)
106
+ // were previously listed as explicit `--compile` entries so the
107
+ // rewrite path could emit `/$bunfs/root/...` URLs against them.
108
+ // Bun 1.3.14 made bunfs files unreachable at runtime (issue
109
+ // #3423), so `legacy-pi-compat.ts` now serves them through a
110
+ // virtual namespace backed by `legacy-pi-bundled-registry.ts`,
111
+ // which static-imports each surface the bundler already
112
+ // includes them via the main module graph, so no `--compile`
113
+ // extras are required.
101
114
  "--outfile",
102
115
  `packages/coding-agent/dist/${outName}`,
103
116
  ],