@eminent337/aery 0.1.115 → 0.1.116

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 (270) hide show
  1. package/CHANGELOG.md +4172 -16
  2. package/README.md +621 -34
  3. package/dist/bun/cli.d.ts.map +1 -1
  4. package/dist/bun/cli.js +2 -1
  5. package/dist/bun/cli.js.map +1 -1
  6. package/dist/cli/args.d.ts.map +1 -1
  7. package/dist/cli/args.js +3 -0
  8. package/dist/cli/args.js.map +1 -1
  9. package/dist/cli/config-selector.d.ts +1 -1
  10. package/dist/cli/config-selector.d.ts.map +1 -1
  11. package/dist/cli/config-selector.js +1 -1
  12. package/dist/cli/config-selector.js.map +1 -1
  13. package/dist/cli.d.ts.map +1 -1
  14. package/dist/cli.js +4 -3
  15. package/dist/cli.js.map +1 -1
  16. package/dist/config.d.ts +11 -7
  17. package/dist/config.d.ts.map +1 -1
  18. package/dist/config.js +66 -46
  19. package/dist/config.js.map +1 -1
  20. package/dist/core/agent-session.d.ts +5 -5
  21. package/dist/core/agent-session.d.ts.map +1 -1
  22. package/dist/core/agent-session.js +37 -36
  23. package/dist/core/agent-session.js.map +1 -1
  24. package/dist/core/bash-executor.d.ts.map +1 -1
  25. package/dist/core/bash-executor.js +2 -2
  26. package/dist/core/bash-executor.js.map +1 -1
  27. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  28. package/dist/core/compaction/branch-summarization.js +1 -1
  29. package/dist/core/compaction/branch-summarization.js.map +1 -1
  30. package/dist/core/compaction/compaction.d.ts.map +1 -1
  31. package/dist/core/compaction/compaction.js +3 -3
  32. package/dist/core/compaction/compaction.js.map +1 -1
  33. package/dist/core/custom-openai-compatible.d.ts +1 -0
  34. package/dist/core/custom-openai-compatible.d.ts.map +1 -1
  35. package/dist/core/custom-openai-compatible.js +30 -4
  36. package/dist/core/custom-openai-compatible.js.map +1 -1
  37. package/dist/core/export-html/template.css +45 -1
  38. package/dist/core/export-html/template.js +68 -4
  39. package/dist/core/extensions/index.d.ts +1 -1
  40. package/dist/core/extensions/index.d.ts.map +1 -1
  41. package/dist/core/extensions/index.js.map +1 -1
  42. package/dist/core/extensions/runner.d.ts +3 -2
  43. package/dist/core/extensions/runner.d.ts.map +1 -1
  44. package/dist/core/extensions/runner.js +40 -0
  45. package/dist/core/extensions/runner.js.map +1 -1
  46. package/dist/core/extensions/types.d.ts +17 -3
  47. package/dist/core/extensions/types.d.ts.map +1 -1
  48. package/dist/core/extensions/types.js.map +1 -1
  49. package/dist/core/model-registry.d.ts +6 -0
  50. package/dist/core/model-registry.d.ts.map +1 -1
  51. package/dist/core/model-registry.js +59 -2
  52. package/dist/core/model-registry.js.map +1 -1
  53. package/dist/core/model-resolver.d.ts.map +1 -1
  54. package/dist/core/model-resolver.js +9 -1
  55. package/dist/core/model-resolver.js.map +1 -1
  56. package/dist/core/provider-display-names.d.ts +2 -0
  57. package/dist/core/provider-display-names.d.ts.map +1 -0
  58. package/dist/core/provider-display-names.js +35 -0
  59. package/dist/core/provider-display-names.js.map +1 -0
  60. package/dist/core/resource-loader.d.ts.map +1 -1
  61. package/dist/core/resource-loader.js +1 -1
  62. package/dist/core/resource-loader.js.map +1 -1
  63. package/dist/core/sdk.d.ts +3 -3
  64. package/dist/core/sdk.d.ts.map +1 -1
  65. package/dist/core/sdk.js +18 -10
  66. package/dist/core/sdk.js.map +1 -1
  67. package/dist/core/session-manager.d.ts +3 -3
  68. package/dist/core/session-manager.d.ts.map +1 -1
  69. package/dist/core/session-manager.js +1 -1
  70. package/dist/core/session-manager.js.map +1 -1
  71. package/dist/core/settings-manager.d.ts.map +1 -1
  72. package/dist/core/settings-manager.js +2 -2
  73. package/dist/core/settings-manager.js.map +1 -1
  74. package/dist/core/system-prompt.d.ts.map +1 -1
  75. package/dist/core/system-prompt.js +5 -5
  76. package/dist/core/system-prompt.js.map +1 -1
  77. package/dist/core/tools/bash.d.ts +2 -2
  78. package/dist/core/tools/bash.d.ts.map +1 -1
  79. package/dist/core/tools/bash.js +105 -125
  80. package/dist/core/tools/bash.js.map +1 -1
  81. package/dist/core/tools/find.d.ts.map +1 -1
  82. package/dist/core/tools/find.js +1 -1
  83. package/dist/core/tools/find.js.map +1 -1
  84. package/dist/core/tools/grep.d.ts.map +1 -1
  85. package/dist/core/tools/grep.js +1 -1
  86. package/dist/core/tools/grep.js.map +1 -1
  87. package/dist/core/tools/output-accumulator.d.ts +50 -0
  88. package/dist/core/tools/output-accumulator.d.ts.map +1 -0
  89. package/dist/core/tools/output-accumulator.js +178 -0
  90. package/dist/core/tools/output-accumulator.js.map +1 -0
  91. package/dist/core/tools/read.d.ts.map +1 -1
  92. package/dist/core/tools/read.js +70 -13
  93. package/dist/core/tools/read.js.map +1 -1
  94. package/dist/core/tools/render-utils.d.ts.map +1 -1
  95. package/dist/core/tools/render-utils.js +2 -2
  96. package/dist/core/tools/render-utils.js.map +1 -1
  97. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  98. package/dist/modes/interactive/components/bash-execution.js +1 -1
  99. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  100. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  101. package/dist/modes/interactive/components/config-selector.js +23 -1
  102. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  103. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
  104. package/dist/modes/interactive/components/earendil-announcement.js +2 -2
  105. package/dist/modes/interactive/components/earendil-announcement.js.map +1 -1
  106. package/dist/modes/interactive/components/extension-selector.d.ts +2 -0
  107. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  108. package/dist/modes/interactive/components/extension-selector.js +6 -1
  109. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  110. package/dist/modes/interactive/components/keybinding-hints.d.ts +5 -0
  111. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  112. package/dist/modes/interactive/components/keybinding-hints.js +19 -5
  113. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  114. package/dist/modes/interactive/components/login-dialog.d.ts +1 -3
  115. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  116. package/dist/modes/interactive/components/login-dialog.js +9 -17
  117. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  118. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  119. package/dist/modes/interactive/components/oauth-selector.js +24 -27
  120. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  121. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  122. package/dist/modes/interactive/components/settings-selector.js +4 -2
  123. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  124. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  125. package/dist/modes/interactive/components/tree-selector.js +2 -1
  126. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  127. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  128. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  129. package/dist/modes/interactive/interactive-mode.js +9 -1
  130. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  131. package/dist/modes/interactive/theme/dark.json +1 -1
  132. package/dist/modes/interactive/theme/light.json +1 -1
  133. package/dist/modes/interactive/theme/theme-schema.json +1 -1
  134. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  135. package/dist/modes/interactive/theme/theme.js +8 -10
  136. package/dist/modes/interactive/theme/theme.js.map +1 -1
  137. package/dist/modes/print-mode.d.ts +2 -2
  138. package/dist/modes/print-mode.d.ts.map +1 -1
  139. package/dist/modes/print-mode.js +2 -2
  140. package/dist/modes/print-mode.js.map +1 -1
  141. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  142. package/dist/modes/rpc/rpc-mode.js +4 -0
  143. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  144. package/dist/utils/ansi.d.ts +2 -0
  145. package/dist/utils/ansi.d.ts.map +1 -0
  146. package/dist/utils/ansi.js +52 -0
  147. package/dist/utils/ansi.js.map +1 -0
  148. package/dist/utils/clipboard-image.d.ts.map +1 -1
  149. package/dist/utils/clipboard-image.js +3 -3
  150. package/dist/utils/clipboard-image.js.map +1 -1
  151. package/dist/utils/clipboard.d.ts.map +1 -1
  152. package/dist/utils/clipboard.js +9 -2
  153. package/dist/utils/clipboard.js.map +1 -1
  154. package/dist/utils/html.d.ts +7 -0
  155. package/dist/utils/html.d.ts.map +1 -0
  156. package/dist/utils/html.js +40 -0
  157. package/dist/utils/html.js.map +1 -0
  158. package/dist/utils/mime.d.ts +1 -0
  159. package/dist/utils/mime.d.ts.map +1 -1
  160. package/dist/utils/mime.js +59 -16
  161. package/dist/utils/mime.js.map +1 -1
  162. package/dist/utils/paths.d.ts +2 -0
  163. package/dist/utils/paths.d.ts.map +1 -1
  164. package/dist/utils/paths.js +16 -0
  165. package/dist/utils/paths.js.map +1 -1
  166. package/dist/utils/pi-user-agent.d.ts +2 -0
  167. package/dist/utils/pi-user-agent.d.ts.map +1 -0
  168. package/dist/utils/pi-user-agent.js +5 -0
  169. package/dist/utils/pi-user-agent.js.map +1 -0
  170. package/dist/utils/syntax-highlight.d.ts +12 -0
  171. package/dist/utils/syntax-highlight.d.ts.map +1 -0
  172. package/dist/utils/syntax-highlight.js +118 -0
  173. package/dist/utils/syntax-highlight.js.map +1 -0
  174. package/dist/utils/tools-manager.d.ts.map +1 -1
  175. package/dist/utils/tools-manager.js +76 -7
  176. package/dist/utils/tools-manager.js.map +1 -1
  177. package/dist/utils/uuid.d.ts +2 -0
  178. package/dist/utils/uuid.d.ts.map +1 -0
  179. package/dist/utils/uuid.js +40 -0
  180. package/dist/utils/uuid.js.map +1 -0
  181. package/dist/utils/version-check.d.ts +7 -0
  182. package/dist/utils/version-check.d.ts.map +1 -1
  183. package/dist/utils/version-check.js +12 -5
  184. package/dist/utils/version-check.js.map +1 -1
  185. package/docs/compaction.md +16 -16
  186. package/docs/custom-provider.md +40 -32
  187. package/docs/development.md +4 -4
  188. package/docs/docs.json +20 -5
  189. package/docs/extensions.md +152 -102
  190. package/docs/index.md +16 -7
  191. package/docs/json.md +7 -7
  192. package/docs/keybindings.md +3 -3
  193. package/docs/models.md +48 -8
  194. package/docs/packages.md +41 -36
  195. package/docs/prompt-templates.md +2 -2
  196. package/docs/providers.md +52 -36
  197. package/docs/quickstart.md +20 -20
  198. package/docs/rpc.md +9 -9
  199. package/docs/sdk.md +31 -53
  200. package/docs/session-format.md +10 -10
  201. package/docs/sessions.md +9 -9
  202. package/docs/settings.md +12 -6
  203. package/docs/skills.md +4 -4
  204. package/docs/terminal-setup.md +6 -6
  205. package/docs/termux.md +6 -6
  206. package/docs/themes.md +7 -7
  207. package/docs/tmux.md +1 -1
  208. package/docs/tui.md +8 -8
  209. package/docs/usage.md +41 -39
  210. package/examples/extensions/README.md +3 -5
  211. package/examples/extensions/antigravity-image-gen.ts +9 -9
  212. package/examples/extensions/auto-commit-on-exit.ts +1 -1
  213. package/examples/extensions/bash-spawn-hook.ts +2 -2
  214. package/examples/extensions/built-in-tool-renderer.ts +1 -1
  215. package/examples/extensions/custom-compaction.ts +1 -1
  216. package/examples/extensions/custom-header.ts +2 -2
  217. package/examples/extensions/custom-provider-anthropic/index.ts +2 -2
  218. package/examples/extensions/custom-provider-anthropic/package-lock.json +4 -4
  219. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  220. package/examples/extensions/custom-provider-gitlab-duo/index.ts +2 -2
  221. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  222. package/examples/extensions/doom-overlay/README.md +2 -2
  223. package/examples/extensions/doom-overlay/doom/build.sh +2 -2
  224. package/examples/extensions/doom-overlay/index.ts +1 -1
  225. package/examples/extensions/dynamic-resources/dynamic.json +1 -1
  226. package/examples/extensions/handoff.ts +42 -5
  227. package/examples/extensions/hidden-thinking-label.ts +1 -1
  228. package/examples/extensions/inline-bash.ts +2 -2
  229. package/examples/extensions/input-transform.ts +3 -3
  230. package/examples/extensions/interactive-shell.ts +1 -1
  231. package/examples/extensions/mac-system-theme.ts +2 -2
  232. package/examples/extensions/minimal-mode.ts +1 -1
  233. package/examples/extensions/modal-editor.ts +1 -1
  234. package/examples/extensions/model-status.ts +1 -1
  235. package/examples/extensions/overlay-qa-tests.ts +6 -6
  236. package/examples/extensions/overlay-test.ts +1 -1
  237. package/examples/extensions/preset.ts +2 -2
  238. package/examples/extensions/provider-payload.ts +1 -1
  239. package/examples/extensions/rainbow-editor.ts +1 -1
  240. package/examples/extensions/rpc-demo.ts +1 -1
  241. package/examples/extensions/sandbox/index.ts +3 -3
  242. package/examples/extensions/sandbox/package-lock.json +7 -7
  243. package/examples/extensions/sandbox/package.json +1 -1
  244. package/examples/extensions/shutdown-command.ts +5 -5
  245. package/examples/extensions/ssh.ts +2 -2
  246. package/examples/extensions/subagent/README.md +2 -2
  247. package/examples/extensions/subagent/agents/aery-pods.md +1 -1
  248. package/examples/extensions/subagent/agents.ts +1 -1
  249. package/examples/extensions/subagent/index.ts +2 -2
  250. package/examples/extensions/titlebar-spinner.ts +1 -1
  251. package/examples/extensions/tool-override.ts +2 -2
  252. package/examples/extensions/truncated-tool.ts +1 -1
  253. package/examples/extensions/with-deps/package-lock.json +4 -4
  254. package/examples/extensions/with-deps/package.json +1 -1
  255. package/examples/extensions/working-indicator.ts +4 -4
  256. package/examples/extensions/working-message-test.ts +1 -1
  257. package/examples/sdk/01-minimal.ts +14 -10
  258. package/examples/sdk/02-custom-model.ts +12 -8
  259. package/examples/sdk/03-custom-prompt.ts +24 -16
  260. package/examples/sdk/04-skills.ts +2 -2
  261. package/examples/sdk/05-tools.ts +8 -4
  262. package/examples/sdk/06-extensions.ts +11 -7
  263. package/examples/sdk/07-context-files.ts +2 -2
  264. package/examples/sdk/08-prompt-templates.ts +2 -2
  265. package/examples/sdk/09-api-keys-and-oauth.ts +8 -4
  266. package/examples/sdk/10-settings.ts +4 -4
  267. package/examples/sdk/11-sessions.ts +4 -0
  268. package/examples/sdk/12-full-control.ts +11 -7
  269. package/examples/sdk/README.md +5 -8
  270. package/package.json +8 -14
@@ -1,12 +1,10 @@
1
- > The agent can create extensions. Ask it to build one for your use case.
1
+ > pi can create extensions. Ask it to build one for your use case.
2
2
 
3
3
  # Extensions
4
4
 
5
- Extensions are TypeScript modules that extend the agent's behavior. They can subscribe to lifecycle events, register custom tools callable by the LLM, add commands, and more.
5
+ Extensions are TypeScript modules that extend pi's behavior. They can subscribe to lifecycle events, register custom tools callable by the LLM, add commands, and more.
6
6
 
7
- > This extension system is shared across the open-source AI coding agent ecosystem. Extensions written for [pi](https://github.com/badlogic/pi-mono) are compatible with [Aery](https://github.com/eminent337/aery). For more extension examples, see [openclaude](https://github.com/Gitlawb/openclaude) and [opencode](https://github.com/sst/opencode) which use similar plugin architectures.
8
-
9
- > **Placement for /reload:** Put extensions in `~/.aery/agent/extensions/` (global) or `.aery/extensions/` (project-local) for auto-discovery. Use `aery -e ./path.ts` only for quick tests. Extensions in auto-discovered locations can be hot-reloaded with `/reload`.
7
+ > **Placement for /reload:** Put extensions in `~/.aery/agent/extensions/` (global) or `.aery/extensions/` (project-local) for auto-discovery. Use `pi -e ./path.ts` only for quick tests. Extensions in auto-discovered locations can be hot-reloaded with `/reload`.
10
8
 
11
9
  **Key capabilities:**
12
10
  - **Custom tools** - Register tools the LLM can call via `pi.registerTool()`
@@ -14,7 +12,7 @@ Extensions are TypeScript modules that extend the agent's behavior. They can sub
14
12
  - **User interaction** - Prompt users via `ctx.ui` (select, confirm, input, notify)
15
13
  - **Custom UI components** - Full TUI components with keyboard input via `ctx.ui.custom()` for complex interactions
16
14
  - **Custom commands** - Register commands like `/mycommand` via `pi.registerCommand()`
17
- - **Session persistence** - Store state that survives restarts via `aery.appendEntry()`
15
+ - **Session persistence** - Store state that survives restarts via `pi.appendEntry()`
18
16
  - **Custom rendering** - Control how tool calls/results and messages appear in TUI
19
17
 
20
18
  **Example use cases:**
@@ -42,6 +40,7 @@ See [examples/extensions/](../examples/extensions/) for working implementations.
42
40
  - [Resource Events](#resource-events)
43
41
  - [Session Events](#session-events)
44
42
  - [Agent Events](#agent-events)
43
+ - [Model Events](#model-events)
45
44
  - [Tool Events](#tool-events)
46
45
  - [ExtensionContext](#extensioncontext)
47
46
  - [ExtensionCommandContext](#extensioncommandcontext)
@@ -59,9 +58,9 @@ Create `~/.aery/agent/extensions/my-extension.ts`:
59
58
 
60
59
  ```typescript
61
60
  import type { ExtensionAPI } from "@eminent337/aery";
62
- import { Type } from "@sinclair/typebox";
61
+ import { Type } from "typebox";
63
62
 
64
- export default function (aery: ExtensionAPI) {
63
+ export default function (pi: ExtensionAPI) {
65
64
  // React to events
66
65
  pi.on("session_start", async (_event, ctx) => {
67
66
  ctx.ui.notify("Extension loaded!", "info");
@@ -103,7 +102,7 @@ export default function (aery: ExtensionAPI) {
103
102
  Test with `--extension` (or `-e`) flag:
104
103
 
105
104
  ```bash
106
- aery -e ./my-extension.ts
105
+ pi -e ./my-extension.ts
107
106
  ```
108
107
 
109
108
  ## Extension Locations
@@ -134,20 +133,20 @@ Additional paths via `settings.json`:
134
133
  }
135
134
  ```
136
135
 
137
- To share extensions via npm or git as aery packages, see [packages.md](packages.md).
136
+ To share extensions via npm or git as pi packages, see [packages.md](packages.md).
138
137
 
139
138
  ## Available Imports
140
139
 
141
140
  | Package | Purpose |
142
141
  |---------|---------|
143
142
  | `@eminent337/aery` | Extension types (`ExtensionAPI`, `ExtensionContext`, events) |
144
- | `@sinclair/typebox` | Schema definitions for tool parameters |
143
+ | `typebox` | Schema definitions for tool parameters |
145
144
  | `@eminent337/aery-ai` | AI utilities (`StringEnum` for Google-compatible enums) |
146
145
  | `@eminent337/aery-tui` | TUI components for custom rendering |
147
146
 
148
147
  npm dependencies work too. Add a `package.json` next to your extension (or in a parent directory), run `npm install`, and imports from `node_modules/` are resolved automatically.
149
148
 
150
- For distributed aery packages installed with `aery install` (npm or git), runtime deps must be in `dependencies`. Package installation uses production installs (`npm install --omit=dev`), so `devDependencies` are not available at runtime.
149
+ For distributed pi packages installed with `pi install` (npm or git), runtime deps must be in `dependencies`. Package installation uses production installs (`npm install --omit=dev`) by default, so `devDependencies` are not available at runtime; when `npmCommand` is configured, git packages use plain `install` for compatibility with wrappers.
151
150
 
152
151
  Node.js built-ins (`node:fs`, `node:path`, etc.) are also available.
153
152
 
@@ -158,7 +157,7 @@ An extension exports a default factory function that receives `ExtensionAPI`. Th
158
157
  ```typescript
159
158
  import type { ExtensionAPI } from "@eminent337/aery";
160
159
 
161
- export default function (aery: ExtensionAPI) {
160
+ export default function (pi: ExtensionAPI) {
162
161
  // Subscribe to events
163
162
  pi.on("event_name", async (event, ctx) => {
164
163
  // ctx.ui for user interaction
@@ -171,14 +170,14 @@ export default function (aery: ExtensionAPI) {
171
170
  // Register tools, commands, shortcuts, flags
172
171
  pi.registerTool({ ... });
173
172
  pi.registerCommand("name", { ... });
174
- aery.registerShortcut("ctrl+x", { ... });
175
- aery.registerFlag("my-flag", { ... });
173
+ pi.registerShortcut("ctrl+x", { ... });
174
+ pi.registerFlag("my-flag", { ... });
176
175
  }
177
176
  ```
178
177
 
179
178
  Extensions are loaded via [jiti](https://github.com/unjs/jiti), so TypeScript works without compilation.
180
179
 
181
- If the factory returns a `Promise`, aery awaits it before continuing startup. That means async initialization completes before `session_start`, before `resources_discover`, and before provider registrations queued via `pi.registerProvider()` are flushed.
180
+ If the factory returns a `Promise`, pi awaits it before continuing startup. That means async initialization completes before `session_start`, before `resources_discover`, and before provider registrations queued via `pi.registerProvider()` are flushed.
182
181
 
183
182
  ### Async factory functions
184
183
 
@@ -187,7 +186,7 @@ Use an async factory for one-time startup work such as fetching remote configura
187
186
  ```typescript
188
187
  import type { ExtensionAPI } from "@eminent337/aery";
189
188
 
190
- export default async function (aery: ExtensionAPI) {
189
+ export default async function (pi: ExtensionAPI) {
191
190
  const response = await fetch("http://localhost:1234/v1/models");
192
191
  const payload = (await response.json()) as {
193
192
  data: Array<{
@@ -215,7 +214,7 @@ export default async function (aery: ExtensionAPI) {
215
214
  }
216
215
  ```
217
216
 
218
- This pattern makes the fetched models available during normal startup and to `aery --list-models`.
217
+ This pattern makes the fetched models available during normal startup and to `pi --list-models`.
219
218
 
220
219
  ### Extension Styles
221
220
 
@@ -256,7 +255,7 @@ This pattern makes the fetched models available during normal startup and to `ae
256
255
  "zod": "^3.0.0",
257
256
  "chalk": "^5.0.0"
258
257
  },
259
- "aery": {
258
+ "pi": {
260
259
  "extensions": ["./src/index.ts"]
261
260
  }
262
261
  }
@@ -269,7 +268,7 @@ Run `npm install` in the extension directory, then imports from `node_modules/`
269
268
  ### Lifecycle Overview
270
269
 
271
270
  ```
272
- aery starts
271
+ pi starts
273
272
 
274
273
  ├─► session_start { reason: "startup" }
275
274
  └─► resources_discover { reason: "startup" }
@@ -325,8 +324,12 @@ user sends another prompt ◄─────────────────
325
324
  └─► session_tree
326
325
 
327
326
  /model or Ctrl+P (model selection/cycling)
327
+ ├─► thinking_level_select (if model change changes/clamps thinking level)
328
328
  └─► model_select
329
329
 
330
+ thinking level changes (settings, keybinding, pi.setThinkingLevel())
331
+ └─► thinking_level_select
332
+
330
333
  exit (Ctrl+C, Ctrl+D, SIGHUP, SIGTERM)
331
334
  └─► session_shutdown
332
335
  ```
@@ -382,7 +385,7 @@ pi.on("session_before_switch", async (event, ctx) => {
382
385
  });
383
386
  ```
384
387
 
385
- After a successful switch or new-session action, aery emits `session_shutdown` for the old extension instance, reloads and rebinds extensions for the new session, then emits `session_start` with `reason: "new" | "resume"` and `previousSessionFile`.
388
+ After a successful switch or new-session action, pi emits `session_shutdown` for the old extension instance, reloads and rebinds extensions for the new session, then emits `session_start` with `reason: "new" | "resume"` and `previousSessionFile`.
386
389
  Do cleanup work in `session_shutdown`, then reestablish any in-memory state in `session_start`.
387
390
 
388
391
  #### session_before_fork
@@ -399,7 +402,7 @@ pi.on("session_before_fork", async (event, ctx) => {
399
402
  });
400
403
  ```
401
404
 
402
- After a successful fork or clone, aery emits `session_shutdown` for the old extension instance, reloads and rebinds extensions for the new session, then emits `session_start` with `reason: "fork"` and `previousSessionFile`.
405
+ After a successful fork or clone, pi emits `session_shutdown` for the old extension instance, reloads and rebinds extensions for the new session, then emits `session_start` with `reason: "fork"` and `previousSessionFile`.
403
406
  Do cleanup work in `session_shutdown`, then reestablish any in-memory state in `session_start`.
404
407
 
405
408
  #### session_before_compact / session_compact
@@ -468,7 +471,8 @@ Fired after user submits prompt, before agent loop. Can inject a message and/or
468
471
  pi.on("before_agent_start", async (event, ctx) => {
469
472
  // event.prompt - user's prompt text
470
473
  // event.images - attached images (if any)
471
- // event.systemPrompt - current system prompt
474
+ // event.systemPrompt - current chained system prompt for this handler
475
+ // (includes changes from earlier before_agent_start handlers)
472
476
  // event.systemPromptOptions - structured options used to build the system prompt
473
477
  // .customPrompt - any custom system prompt (from --system-prompt, SYSTEM.md, or custom templates)
474
478
  // .selectedTools - tools currently active in the prompt
@@ -494,6 +498,8 @@ pi.on("before_agent_start", async (event, ctx) => {
494
498
 
495
499
  The `systemPromptOptions` field gives extensions access to the same structured data Pi uses to build the system prompt. This lets you inspect what Pi has loaded — custom prompts, guidelines, tool snippets, context files, skills — without re-discovering resources or re-parsing flags. Use it when your extension needs to make deep, informed changes to the system prompt while respecting user-provided configuration.
496
500
 
501
+ Inside `before_agent_start`, `event.systemPrompt` and `ctx.getSystemPrompt()` both reflect the chained system prompt as of the current handler. Later `before_agent_start` handlers can still modify it again.
502
+
497
503
  #### agent_start / agent_end
498
504
 
499
505
  Fired once per user prompt.
@@ -526,6 +532,7 @@ Fired for message lifecycle updates.
526
532
 
527
533
  - `message_start` and `message_end` fire for user, assistant, and toolResult messages.
528
534
  - `message_update` fires for assistant streaming updates.
535
+ - `message_end` handlers can return `{ message }` to replace the finalized message. The replacement must keep the same `role`.
529
536
 
530
537
  ```typescript
531
538
  pi.on("message_start", async (event, ctx) => {
@@ -538,7 +545,20 @@ pi.on("message_update", async (event, ctx) => {
538
545
  });
539
546
 
540
547
  pi.on("message_end", async (event, ctx) => {
541
- // event.message
548
+ if (event.message.role !== "assistant") return;
549
+
550
+ return {
551
+ message: {
552
+ ...event.message,
553
+ usage: {
554
+ ...event.message.usage,
555
+ cost: {
556
+ ...event.message.usage.cost,
557
+ total: 0.123,
558
+ },
559
+ },
560
+ },
561
+ };
542
562
  });
543
563
  ```
544
564
 
@@ -634,13 +654,28 @@ pi.on("model_select", async (event, ctx) => {
634
654
 
635
655
  Use this to update UI elements (status bars, footers) or perform model-specific initialization when the active model changes.
636
656
 
657
+ #### thinking_level_select
658
+
659
+ Fired when the thinking level changes. This is notification-only; handler return values are ignored.
660
+
661
+ ```typescript
662
+ pi.on("thinking_level_select", async (event, ctx) => {
663
+ // event.level - newly selected thinking level
664
+ // event.previousLevel - previous thinking level
665
+
666
+ ctx.ui.setStatus("thinking", `thinking: ${event.level}`);
667
+ });
668
+ ```
669
+
670
+ Use this to update extension UI when `pi.setThinkingLevel()`, model changes, or built-in thinking-level controls change the active thinking level.
671
+
637
672
  ### Tool Events
638
673
 
639
674
  #### tool_call
640
675
 
641
676
  Fired after `tool_execution_start`, before the tool executes. **Can block.** Use `isToolCallEventType` to narrow and get typed inputs.
642
677
 
643
- Before `tool_call` runs, aery waits for previously emitted Agent events to finish draining through `AgentSession`. This means `ctx.sessionManager` is up to date through the current assistant tool-calling message.
678
+ Before `tool_call` runs, pi waits for previously emitted Agent events to finish draining through `AgentSession`. This means `ctx.sessionManager` is up to date through the current assistant tool-calling message.
644
679
 
645
680
  In the default parallel tool execution mode, sibling tool calls from the same assistant message are preflighted sequentially, then executed concurrently. `tool_call` is not guaranteed to see sibling tool results from that same assistant message in `ctx.sessionManager`.
646
681
 
@@ -751,7 +786,7 @@ pi.on("user_bash", (event, ctx) => {
751
786
  // Option 1: Provide custom operations (e.g., SSH)
752
787
  return { operations: remoteBashOps };
753
788
 
754
- // Option 2: Wrap aery's built-in local bash backend
789
+ // Option 2: Wrap pi's built-in local bash backend
755
790
  const local = createLocalBashOperations();
756
791
  return {
757
792
  operations: {
@@ -856,7 +891,7 @@ Use this for abort-aware nested work started by extension handlers, for example:
856
891
  - file or process helpers that accept `AbortSignal`
857
892
 
858
893
  `ctx.signal` is typically defined during active turn events such as `tool_call`, `tool_result`, `message_update`, and `turn_end`.
859
- It is usually `undefined` in idle or non-turn contexts such as session events, extension commands, and shortcuts fired while aery is idle.
894
+ It is usually `undefined` in idle or non-turn contexts such as session events, extension commands, and shortcuts fired while pi is idle.
860
895
 
861
896
  ```typescript
862
897
  pi.on("tool_result", async (event, ctx) => {
@@ -877,7 +912,7 @@ Control flow helpers.
877
912
 
878
913
  ### ctx.shutdown()
879
914
 
880
- Request a graceful shutdown of aery.
915
+ Request a graceful shutdown of pi.
881
916
 
882
917
  - **Interactive mode:** Deferred until the agent becomes idle (after processing all queued steering and follow-up messages).
883
918
  - **RPC mode:** Deferred until the next idle state (after completing the current command response, when waiting for the next command).
@@ -984,7 +1019,7 @@ if (result.cancelled) {
984
1019
  Options:
985
1020
  - `parentSession`: parent session file to record in the new session header
986
1021
  - `setup`: mutate the new session's `SessionManager` before `withSession` runs
987
- - `withSession`: run post-switch work against a fresh replacement-session context. Do not use captured old `aery` / command `ctx`; see [Session replacement lifecycle and footguns](#session-replacement-lifecycle-and-footguns).
1022
+ - `withSession`: run post-switch work against a fresh replacement-session context. Do not use captured old `pi` / command `ctx`; see [Session replacement lifecycle and footguns](#session-replacement-lifecycle-and-footguns).
988
1023
 
989
1024
  ### ctx.fork(entryId, options?)
990
1025
 
@@ -1010,7 +1045,7 @@ if (cloneResult.cancelled) {
1010
1045
  Options:
1011
1046
  - `position`: `"before"` (default) forks before the selected user message, restoring that prompt into the editor
1012
1047
  - `position`: `"at"` duplicates the active path through the selected entry without restoring editor text
1013
- - `withSession`: run post-switch work against a fresh replacement-session context. Do not use captured old `aery` / command `ctx`; see [Session replacement lifecycle and footguns](#session-replacement-lifecycle-and-footguns).
1048
+ - `withSession`: run post-switch work against a fresh replacement-session context. Do not use captured old `pi` / command `ctx`; see [Session replacement lifecycle and footguns](#session-replacement-lifecycle-and-footguns).
1014
1049
 
1015
1050
  ### ctx.navigateTree(targetId, options?)
1016
1051
 
@@ -1047,7 +1082,7 @@ if (result.cancelled) {
1047
1082
  ```
1048
1083
 
1049
1084
  Options:
1050
- - `withSession`: run post-switch work against a fresh replacement-session context. Do not use captured old `aery` / command `ctx`; see [Session replacement lifecycle and footguns](#session-replacement-lifecycle-and-footguns).
1085
+ - `withSession`: run post-switch work against a fresh replacement-session context. Do not use captured old `pi` / command `ctx`; see [Session replacement lifecycle and footguns](#session-replacement-lifecycle-and-footguns).
1051
1086
 
1052
1087
  To discover available sessions, use the static `SessionManager.list()` or `SessionManager.listAll()` methods:
1053
1088
 
@@ -1081,7 +1116,7 @@ pi.registerCommand("switch", {
1081
1116
  Lifecycle and footguns:
1082
1117
  - `withSession` runs only after the old session has emitted `session_shutdown`, the old runtime has been torn down, the replacement session has been rebound, and the new extension instance has already received `session_start`.
1083
1118
  - The callback still executes in the original closure, not inside the new extension instance. That means your old extension instance may already have run its shutdown cleanup before `withSession` starts.
1084
- - Captured old `aery` / old command `ctx` session-bound objects are stale after replacement and will throw if used. Use only the `ctx` passed to `withSession` for session-bound work.
1119
+ - Captured old `pi` / old command `ctx` session-bound objects are stale after replacement and will throw if used. Use only the `ctx` passed to `withSession` for session-bound work.
1085
1120
  - Previously extracted raw objects are still your responsibility. For example, if you capture `const sm = ctx.sessionManager` before replacement, `sm` is still the old `SessionManager` object. Do not reuse it after replacement.
1086
1121
  - Code in `withSession` should assume any state invalidated by your `session_shutdown` handler is already gone. Only capture plain data that survives shutdown cleanly, such as strings, ids, and serialized config.
1087
1122
 
@@ -1147,9 +1182,9 @@ Example tool the LLM can call to trigger reload:
1147
1182
 
1148
1183
  ```typescript
1149
1184
  import type { ExtensionAPI } from "@eminent337/aery";
1150
- import { Type } from "@sinclair/typebox";
1185
+ import { Type } from "typebox";
1151
1186
 
1152
- export default function (aery: ExtensionAPI) {
1187
+ export default function (pi: ExtensionAPI) {
1153
1188
  pi.registerCommand("reload-runtime", {
1154
1189
  description: "Reload extensions, skills, prompts, and themes",
1155
1190
  handler: async (_args, ctx) => {
@@ -1183,9 +1218,9 @@ Subscribe to events. See [Events](#events) for event types and return values.
1183
1218
 
1184
1219
  Register a custom tool callable by the LLM. See [Custom Tools](#custom-tools) for full details.
1185
1220
 
1186
- `pi.registerTool()` works both during extension load and after startup. You can call it inside `session_start`, command handlers, or other event handlers. New tools are refreshed immediately in the same session, so they appear in `aery.getAllTools()` and are callable by the LLM without `/reload`.
1221
+ `pi.registerTool()` works both during extension load and after startup. You can call it inside `session_start`, command handlers, or other event handlers. New tools are refreshed immediately in the same session, so they appear in `pi.getAllTools()` and are callable by the LLM without `/reload`.
1187
1222
 
1188
- Use `aery.setActiveTools()` to enable or disable tools (including dynamically added tools) at runtime.
1223
+ Use `pi.setActiveTools()` to enable or disable tools (including dynamically added tools) at runtime.
1189
1224
 
1190
1225
  Use `promptSnippet` to opt a custom tool into a one-line entry in `Available tools`, and `promptGuidelines` to append tool-specific bullets to the default `Guidelines` section when the tool is active.
1191
1226
 
@@ -1194,7 +1229,7 @@ Use `promptSnippet` to opt a custom tool into a one-line entry in `Available too
1194
1229
  See [dynamic-tools.ts](../examples/extensions/dynamic-tools.ts) for a full example.
1195
1230
 
1196
1231
  ```typescript
1197
- import { Type } from "@sinclair/typebox";
1232
+ import { Type } from "typebox";
1198
1233
  import { StringEnum } from "@eminent337/aery-ai";
1199
1234
 
1200
1235
  pi.registerTool({
@@ -1230,12 +1265,12 @@ pi.registerTool({
1230
1265
  });
1231
1266
  ```
1232
1267
 
1233
- ### aery.sendMessage(message, options?)
1268
+ ### pi.sendMessage(message, options?)
1234
1269
 
1235
1270
  Inject a custom message into the session.
1236
1271
 
1237
1272
  ```typescript
1238
- aery.sendMessage({
1273
+ pi.sendMessage({
1239
1274
  customType: "my-extension",
1240
1275
  content: "Message text",
1241
1276
  display: true,
@@ -1281,12 +1316,12 @@ When not streaming, the message is sent immediately and triggers a new turn. Whe
1281
1316
 
1282
1317
  See [send-user-message.ts](../examples/extensions/send-user-message.ts) for a complete example.
1283
1318
 
1284
- ### aery.appendEntry(customType, data?)
1319
+ ### pi.appendEntry(customType, data?)
1285
1320
 
1286
1321
  Persist extension state (does NOT participate in LLM context).
1287
1322
 
1288
1323
  ```typescript
1289
- aery.appendEntry("my-state", { count: 42 });
1324
+ pi.appendEntry("my-state", { count: 42 });
1290
1325
 
1291
1326
  // Restore on reload
1292
1327
  pi.on("session_start", async (_event, ctx) => {
@@ -1298,35 +1333,35 @@ pi.on("session_start", async (_event, ctx) => {
1298
1333
  });
1299
1334
  ```
1300
1335
 
1301
- ### aery.setSessionName(name)
1336
+ ### pi.setSessionName(name)
1302
1337
 
1303
1338
  Set the session display name (shown in session selector instead of first message).
1304
1339
 
1305
1340
  ```typescript
1306
- aery.setSessionName("Refactor auth module");
1341
+ pi.setSessionName("Refactor auth module");
1307
1342
  ```
1308
1343
 
1309
- ### aery.getSessionName()
1344
+ ### pi.getSessionName()
1310
1345
 
1311
1346
  Get the current session name, if set.
1312
1347
 
1313
1348
  ```typescript
1314
- const name = aery.getSessionName();
1349
+ const name = pi.getSessionName();
1315
1350
  if (name) {
1316
1351
  console.log(`Session: ${name}`);
1317
1352
  }
1318
1353
  ```
1319
1354
 
1320
- ### aery.setLabel(entryId, label)
1355
+ ### pi.setLabel(entryId, label)
1321
1356
 
1322
1357
  Set or clear a label on an entry. Labels are user-defined markers for bookmarking and navigation (shown in `/tree` selector).
1323
1358
 
1324
1359
  ```typescript
1325
1360
  // Set a label
1326
- aery.setLabel(entryId, "checkpoint-before-refactor");
1361
+ pi.setLabel(entryId, "checkpoint-before-refactor");
1327
1362
 
1328
1363
  // Clear a label
1329
- aery.setLabel(entryId, undefined);
1364
+ pi.setLabel(entryId, undefined);
1330
1365
 
1331
1366
  // Read labels via sessionManager
1332
1367
  const label = ctx.sessionManager.getLabel(entryId);
@@ -1338,7 +1373,7 @@ Labels persist in the session and survive restarts. Use them to mark important p
1338
1373
 
1339
1374
  Register a command.
1340
1375
 
1341
- If multiple extensions register the same command name, aery keeps them all and assigns numeric invocation suffixes in load order, for example `/review:1` and `/review:2`.
1376
+ If multiple extensions register the same command name, pi keeps them all and assigns numeric invocation suffixes in load order, for example `/review:1` and `/review:2`.
1342
1377
 
1343
1378
  ```typescript
1344
1379
  pi.registerCommand("stats", {
@@ -1369,13 +1404,13 @@ pi.registerCommand("deploy", {
1369
1404
  });
1370
1405
  ```
1371
1406
 
1372
- ### aery.getCommands()
1407
+ ### pi.getCommands()
1373
1408
 
1374
1409
  Get the slash commands available for invocation via `prompt` in the current session. Includes extension commands, prompt templates, and skill commands.
1375
1410
  The list matches the RPC `get_commands` ordering: extensions first, then templates, then skills.
1376
1411
 
1377
1412
  ```typescript
1378
- const commands = aery.getCommands();
1413
+ const commands = pi.getCommands();
1379
1414
  const bySource = commands.filter((command) => command.source === "extension");
1380
1415
  const userScoped = commands.filter((command) => command.sourceInfo.scope === "user");
1381
1416
  ```
@@ -1402,16 +1437,16 @@ Use `sourceInfo` as the canonical provenance field. Do not infer ownership from
1402
1437
  Built-in interactive commands (like `/model` and `/settings`) are not included here. They are handled only in interactive
1403
1438
  mode and would not execute if sent via `prompt`.
1404
1439
 
1405
- ### aery.registerMessageRenderer(customType, renderer)
1440
+ ### pi.registerMessageRenderer(customType, renderer)
1406
1441
 
1407
1442
  Register a custom TUI renderer for messages with your `customType`. See [Custom UI](#custom-ui).
1408
1443
 
1409
- ### aery.registerShortcut(shortcut, options)
1444
+ ### pi.registerShortcut(shortcut, options)
1410
1445
 
1411
1446
  Register a keyboard shortcut. See [keybindings.md](keybindings.md) for the shortcut format and built-in keybindings.
1412
1447
 
1413
1448
  ```typescript
1414
- aery.registerShortcut("ctrl+shift+p", {
1449
+ pi.registerShortcut("ctrl+shift+p", {
1415
1450
  description: "Toggle plan mode",
1416
1451
  handler: async (ctx) => {
1417
1452
  ctx.ui.notify("Toggled!");
@@ -1419,39 +1454,39 @@ aery.registerShortcut("ctrl+shift+p", {
1419
1454
  });
1420
1455
  ```
1421
1456
 
1422
- ### aery.registerFlag(name, options)
1457
+ ### pi.registerFlag(name, options)
1423
1458
 
1424
1459
  Register a CLI flag.
1425
1460
 
1426
1461
  ```typescript
1427
- aery.registerFlag("plan", {
1462
+ pi.registerFlag("plan", {
1428
1463
  description: "Start in plan mode",
1429
1464
  type: "boolean",
1430
1465
  default: false,
1431
1466
  });
1432
1467
 
1433
1468
  // Check value
1434
- if (aery.getFlag("--plan")) {
1469
+ if (pi.getFlag("plan")) {
1435
1470
  // Plan mode enabled
1436
1471
  }
1437
1472
  ```
1438
1473
 
1439
- ### aery.exec(command, args, options?)
1474
+ ### pi.exec(command, args, options?)
1440
1475
 
1441
1476
  Execute a shell command.
1442
1477
 
1443
1478
  ```typescript
1444
- const result = await aery.exec("git", ["status"], { signal, timeout: 5000 });
1479
+ const result = await pi.exec("git", ["status"], { signal, timeout: 5000 });
1445
1480
  // result.stdout, result.stderr, result.code, result.killed
1446
1481
  ```
1447
1482
 
1448
- ### aery.getActiveTools() / aery.getAllTools() / aery.setActiveTools(names)
1483
+ ### pi.getActiveTools() / pi.getAllTools() / pi.setActiveTools(names)
1449
1484
 
1450
1485
  Manage active tools. This works for both built-in tools and dynamically registered tools.
1451
1486
 
1452
1487
  ```typescript
1453
- const active = aery.getActiveTools();
1454
- const all = aery.getAllTools();
1488
+ const active = pi.getActiveTools();
1489
+ const all = pi.getAllTools();
1455
1490
  // [{
1456
1491
  // name: "read",
1457
1492
  // description: "Read file contents...",
@@ -1461,46 +1496,46 @@ const all = aery.getAllTools();
1461
1496
  const names = all.map(t => t.name);
1462
1497
  const builtinTools = all.filter((t) => t.sourceInfo.source === "builtin");
1463
1498
  const extensionTools = all.filter((t) => t.sourceInfo.source !== "builtin" && t.sourceInfo.source !== "sdk");
1464
- aery.setActiveTools(["read", "bash"]); // Switch to read-only
1499
+ pi.setActiveTools(["read", "bash"]); // Switch to read-only
1465
1500
  ```
1466
1501
 
1467
- `aery.getAllTools()` returns `name`, `description`, `parameters`, and `sourceInfo`.
1502
+ `pi.getAllTools()` returns `name`, `description`, `parameters`, and `sourceInfo`.
1468
1503
 
1469
1504
  Typical `sourceInfo.source` values:
1470
1505
  - `builtin` for built-in tools
1471
1506
  - `sdk` for tools passed via `createAgentSession({ customTools })`
1472
1507
  - extension source metadata for tools registered by extensions
1473
1508
 
1474
- ### aery.setModel(model)
1509
+ ### pi.setModel(model)
1475
1510
 
1476
1511
  Set the current model. Returns `false` if no API key is available for the model. See [models.md](models.md) for configuring custom models.
1477
1512
 
1478
1513
  ```typescript
1479
1514
  const model = ctx.modelRegistry.find("anthropic", "claude-sonnet-4-5");
1480
1515
  if (model) {
1481
- const success = await aery.setModel(model);
1516
+ const success = await pi.setModel(model);
1482
1517
  if (!success) {
1483
1518
  ctx.ui.notify("No API key for this model", "error");
1484
1519
  }
1485
1520
  }
1486
1521
  ```
1487
1522
 
1488
- ### aery.getThinkingLevel() / aery.setThinkingLevel(level)
1523
+ ### pi.getThinkingLevel() / pi.setThinkingLevel(level)
1489
1524
 
1490
- Get or set the thinking level. Level is clamped to model capabilities (non-reasoning models always use "off").
1525
+ Get or set the thinking level. Level is clamped to model capabilities (non-reasoning models always use "off"). Changes emit `thinking_level_select`.
1491
1526
 
1492
1527
  ```typescript
1493
- const current = aery.getThinkingLevel(); // "off" | "minimal" | "low" | "medium" | "high" | "xhigh"
1494
- aery.setThinkingLevel("high");
1528
+ const current = pi.getThinkingLevel(); // "off" | "minimal" | "low" | "medium" | "high" | "xhigh"
1529
+ pi.setThinkingLevel("high");
1495
1530
  ```
1496
1531
 
1497
- ### aery.events
1532
+ ### pi.events
1498
1533
 
1499
1534
  Shared event bus for communication between extensions:
1500
1535
 
1501
1536
  ```typescript
1502
- aery.events.on("my:event", (data) => { ... });
1503
- aery.events.emit("my:event", { ... });
1537
+ pi.events.on("my:event", (data) => { ... });
1538
+ pi.events.emit("my:event", { ... });
1504
1539
  ```
1505
1540
 
1506
1541
  ### pi.registerProvider(name, config)
@@ -1509,11 +1544,12 @@ Register or override a model provider dynamically. Useful for proxies, custom en
1509
1544
 
1510
1545
  Calls made during the extension factory function are queued and applied once the runner initialises. Calls made after that — for example from a command handler following a user setup flow — take effect immediately without requiring a `/reload`.
1511
1546
 
1512
- If you need to discover models from a remote endpoint, prefer an async extension factory over deferring the fetch to `session_start`. aery waits for the factory before startup continues, so the registered models are available immediately, including to `aery --list-models`.
1547
+ If you need to discover models from a remote endpoint, prefer an async extension factory over deferring the fetch to `session_start`. pi waits for the factory before startup continues, so the registered models are available immediately, including to `pi --list-models`.
1513
1548
 
1514
1549
  ```typescript
1515
1550
  // Register a new provider with custom models
1516
1551
  pi.registerProvider("my-proxy", {
1552
+ name: "My Proxy",
1517
1553
  baseUrl: "https://proxy.example.com",
1518
1554
  apiKey: "PROXY_API_KEY", // env var name or literal
1519
1555
  api: "anthropic-messages",
@@ -1560,18 +1596,19 @@ pi.registerProvider("corporate-ai", {
1560
1596
  ```
1561
1597
 
1562
1598
  **Config options:**
1599
+ - `name` - Display name for the provider in UI such as `/login`.
1563
1600
  - `baseUrl` - API endpoint URL. Required when defining models.
1564
1601
  - `apiKey` - API key or environment variable name. Required when defining models (unless `oauth` provided).
1565
1602
  - `api` - API type: `"anthropic-messages"`, `"openai-completions"`, `"openai-responses"`, etc.
1566
1603
  - `headers` - Custom headers to include in requests.
1567
1604
  - `authHeader` - If true, adds `Authorization: Bearer` header automatically.
1568
- - `models` - Array of model definitions. If provided, replaces all existing models for this provider.
1605
+ - `models` - Array of model definitions. If provided, replaces all existing models for this provider. Model definitions can set `baseUrl` to override the provider endpoint for that model.
1569
1606
  - `oauth` - OAuth provider config for `/login` support. When provided, the provider appears in the login menu.
1570
1607
  - `streamSimple` - Custom streaming implementation for non-standard APIs.
1571
1608
 
1572
1609
  See [custom-provider.md](custom-provider.md) for advanced topics: custom streaming APIs, OAuth details, model definition reference.
1573
1610
 
1574
- ### aery.unregisterProvider(name)
1611
+ ### pi.unregisterProvider(name)
1575
1612
 
1576
1613
  Remove a previously registered provider and its models. Built-in models that were overridden by the provider are restored. Has no effect if the provider was not registered.
1577
1614
 
@@ -1581,7 +1618,7 @@ Like `registerProvider`, this takes effect immediately when called after the ini
1581
1618
  pi.registerCommand("my-setup-teardown", {
1582
1619
  description: "Remove the custom proxy provider",
1583
1620
  handler: async (_args, _ctx) => {
1584
- aery.unregisterProvider("my-proxy");
1621
+ pi.unregisterProvider("my-proxy");
1585
1622
  },
1586
1623
  });
1587
1624
  ```
@@ -1591,7 +1628,7 @@ pi.registerCommand("my-setup-teardown", {
1591
1628
  Extensions with state should store it in tool result `details` for proper branching support:
1592
1629
 
1593
1630
  ```typescript
1594
- export default function (aery: ExtensionAPI) {
1631
+ export default function (pi: ExtensionAPI) {
1595
1632
  let items: string[] = [];
1596
1633
 
1597
1634
  // Reconstruct state from session
@@ -1626,7 +1663,7 @@ Register tools the LLM can call via `pi.registerTool()`. Tools appear in the sys
1626
1663
 
1627
1664
  Use `promptSnippet` for a short one-line entry in the `Available tools` section in the default system prompt. If omitted, custom tools are left out of that section.
1628
1665
 
1629
- Use `promptGuidelines` to add tool-specific bullets to the default system prompt `Guidelines` section. These bullets are included only while the tool is active (for example, after `aery.setActiveTools([...])`).
1666
+ Use `promptGuidelines` to add tool-specific bullets to the default system prompt `Guidelines` section. These bullets are included only while the tool is active (for example, after `pi.setActiveTools([...])`).
1630
1667
 
1631
1668
  **Important:** `promptGuidelines` bullets are appended flat to the `Guidelines` section with no tool name prefix or grouping. Each guideline must name the tool it refers to — avoid "Use this tool when..." because the LLM cannot tell which tool "this" means. Write "Use my_tool when..." instead.
1632
1669
 
@@ -1665,7 +1702,7 @@ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
1665
1702
  ### Tool Definition
1666
1703
 
1667
1704
  ```typescript
1668
- import { Type } from "@sinclair/typebox";
1705
+ import { Type } from "typebox";
1669
1706
  import { StringEnum } from "@eminent337/aery-ai";
1670
1707
  import { Text } from "@eminent337/aery-tui";
1671
1708
 
@@ -1702,8 +1739,8 @@ pi.registerTool({
1702
1739
  details: { progress: 50 },
1703
1740
  });
1704
1741
 
1705
- // Run commands via aery.exec (captured from extension closure)
1706
- const result = await aery.exec("some-command", [], { signal });
1742
+ // Run commands via pi.exec (captured from extension closure)
1743
+ const result = await pi.exec("some-command", [], { signal });
1707
1744
 
1708
1745
  // Return result
1709
1746
  return {
@@ -1737,7 +1774,7 @@ async execute(toolCallId, params) {
1737
1774
 
1738
1775
  **Important:** Use `StringEnum` from `@eminent337/aery-ai` for string enums. `Type.Union`/`Type.Literal` doesn't work with Google's API.
1739
1776
 
1740
- **Argument preparation:** `prepareArguments(args)` is optional. If defined, it runs before schema validation and before `execute()`. Use it to mimic an older accepted input shape when aery resumes an older session whose stored tool call arguments no longer match the current schema. Return the object you want validated against `parameters`. Keep the public schema strict. Do not add deprecated compatibility fields to `parameters` just to keep old resumed sessions working.
1777
+ **Argument preparation:** `prepareArguments(args)` is optional. If defined, it runs before schema validation and before `execute()`. Use it to mimic an older accepted input shape when pi resumes an older session whose stored tool call arguments no longer match the current schema. Return the object you want validated against `parameters`. Keep the public schema strict. Do not add deprecated compatibility fields to `parameters` just to keep old resumed sessions working.
1741
1778
 
1742
1779
  Example: an older session may contain an `edit` tool call with top-level `oldText` and `newText`, while the current schema only accepts `edits: [{ oldText, newText }]`.
1743
1780
 
@@ -1790,13 +1827,13 @@ Extensions can override built-in tools (`read`, `bash`, `edit`, `write`, `grep`,
1790
1827
 
1791
1828
  ```bash
1792
1829
  # Extension's read tool replaces built-in read
1793
- aery -e ./tool-override.ts
1830
+ pi -e ./tool-override.ts
1794
1831
  ```
1795
1832
 
1796
1833
  Alternatively, use `--no-builtin-tools` to start without any built-in tools while keeping extension tools enabled:
1797
1834
  ```bash
1798
1835
  # No built-in tools, only extension tools
1799
- aery --no-tools -e ./my-extension.ts
1836
+ pi --no-builtin-tools -e ./my-extension.ts
1800
1837
  ```
1801
1838
 
1802
1839
  See [examples/extensions/tool-override.ts](../examples/extensions/tool-override.ts) for a complete example that overrides `read` with logging and access control.
@@ -1808,13 +1845,13 @@ See [examples/extensions/tool-override.ts](../examples/extensions/tool-override.
1808
1845
  **Your implementation must match the exact result shape**, including the `details` type. The UI and session logic depend on these shapes for rendering and state tracking.
1809
1846
 
1810
1847
  Built-in tool implementations:
1811
- - [read.ts](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/core/tools/read.ts) - `ReadToolDetails`
1812
- - [bash.ts](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/core/tools/bash.ts) - `BashToolDetails`
1813
- - [edit.ts](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/core/tools/edit.ts)
1814
- - [write.ts](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/core/tools/write.ts)
1815
- - [grep.ts](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/core/tools/grep.ts) - `GrepToolDetails`
1816
- - [find.ts](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/core/tools/find.ts) - `FindToolDetails`
1817
- - [ls.ts](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/core/tools/ls.ts) - `LsToolDetails`
1848
+ - [read.ts](https://github.com/eminent337/aery/blob/main/packages/coding-agent/src/core/tools/read.ts) - `ReadToolDetails`
1849
+ - [bash.ts](https://github.com/eminent337/aery/blob/main/packages/coding-agent/src/core/tools/bash.ts) - `BashToolDetails`
1850
+ - [edit.ts](https://github.com/eminent337/aery/blob/main/packages/coding-agent/src/core/tools/edit.ts)
1851
+ - [write.ts](https://github.com/eminent337/aery/blob/main/packages/coding-agent/src/core/tools/write.ts)
1852
+ - [grep.ts](https://github.com/eminent337/aery/blob/main/packages/coding-agent/src/core/tools/grep.ts) - `GrepToolDetails`
1853
+ - [find.ts](https://github.com/eminent337/aery/blob/main/packages/coding-agent/src/core/tools/find.ts) - `FindToolDetails`
1854
+ - [ls.ts](https://github.com/eminent337/aery/blob/main/packages/coding-agent/src/core/tools/ls.ts) - `LsToolDetails`
1818
1855
 
1819
1856
  ### Remote Execution
1820
1857
 
@@ -1847,7 +1884,7 @@ pi.registerTool({
1847
1884
 
1848
1885
  **Operations interfaces:** `ReadOperations`, `WriteOperations`, `EditOperations`, `BashOperations`, `LsOperations`, `GrepOperations`, `FindOperations`
1849
1886
 
1850
- For `user_bash`, extensions can reuse aery's local shell backend via `createLocalBashOperations()` instead of reimplementing local process spawning, shell resolution, and process-tree termination.
1887
+ For `user_bash`, extensions can reuse pi's local shell backend via `createLocalBashOperations()` instead of reimplementing local process spawning, shell resolution, and process-tree termination.
1851
1888
 
1852
1889
  The bash tool also supports a spawn hook to adjust the command, cwd, or env before execution:
1853
1890
 
@@ -1922,7 +1959,7 @@ See [examples/extensions/truncated-tool.ts](../examples/extensions/truncated-too
1922
1959
  One extension can register multiple tools with shared state:
1923
1960
 
1924
1961
  ```typescript
1925
- export default function (aery: ExtensionAPI) {
1962
+ export default function (pi: ExtensionAPI) {
1926
1963
  let connection = null;
1927
1964
 
1928
1965
  pi.registerTool({ name: "db_connect", ... });
@@ -1937,7 +1974,7 @@ export default function (aery: ExtensionAPI) {
1937
1974
 
1938
1975
  ### Custom Rendering
1939
1976
 
1940
- Tools can provide `renderCall` and `renderResult` for custom TUI display. See [tui.md](tui.md) for the full component API and [tool-execution.ts](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/modes/interactive/components/tool-execution.ts) for how tool rows are composed.
1977
+ Tools can provide `renderCall` and `renderResult` for custom TUI display. See [tui.md](tui.md) for the full component API and [tool-execution.ts](https://github.com/eminent337/aery/blob/main/packages/coding-agent/src/modes/interactive/components/tool-execution.ts) for how tool rows are composed.
1941
1978
 
1942
1979
  By default, tool output is wrapped in a `Box` that handles padding and background. A defined `renderCall` or `renderResult` must return a `Component`. If a slot renderer is not defined, `tool-execution.ts` uses fallback rendering for that slot.
1943
1980
 
@@ -2186,7 +2223,7 @@ ctx.ui.setFooter((tui, theme) => ({
2186
2223
  ctx.ui.setFooter(undefined); // Restore built-in footer
2187
2224
 
2188
2225
  // Terminal title
2189
- ctx.ui.setTitle("aery - my-project");
2226
+ ctx.ui.setTitle("pi - my-project");
2190
2227
 
2191
2228
  // Editor text
2192
2229
  ctx.ui.setEditorText("Prefill text");
@@ -2224,6 +2261,10 @@ ctx.ui.setToolsExpanded(wasExpanded);
2224
2261
 
2225
2262
  // Custom editor (vim mode, emacs mode, etc.)
2226
2263
  ctx.ui.setEditorComponent((tui, theme, keybindings) => new VimEditor(tui, theme, keybindings));
2264
+ const currentEditor = ctx.ui.getEditorComponent();
2265
+ ctx.ui.setEditorComponent((tui, theme, keybindings) =>
2266
+ new WrappedEditor(tui, theme, keybindings, currentEditor?.(tui, theme, keybindings))
2267
+ );
2227
2268
  ctx.ui.setEditorComponent(undefined); // Restore default editor
2228
2269
 
2229
2270
  // Theme management (see themes.md for creating themes)
@@ -2365,7 +2406,7 @@ class VimEditor extends CustomEditor {
2365
2406
  }
2366
2407
  }
2367
2408
 
2368
- export default function (aery: ExtensionAPI) {
2409
+ export default function (pi: ExtensionAPI) {
2369
2410
  pi.on("session_start", (_event, ctx) => {
2370
2411
  ctx.ui.setEditorComponent((_tui, theme, keybindings) =>
2371
2412
  new VimEditor(theme, keybindings)
@@ -2378,8 +2419,18 @@ export default function (aery: ExtensionAPI) {
2378
2419
  - Extend `CustomEditor` (not base `Editor`) to get app keybindings (escape to abort, ctrl+d, model switching)
2379
2420
  - Call `super.handleInput(data)` for keys you don't handle
2380
2421
  - Factory receives `theme` and `keybindings` from the app
2422
+ - Use `ctx.ui.getEditorComponent()` before `setEditorComponent()` to wrap the previously configured custom editor
2381
2423
  - Pass `undefined` to restore default: `ctx.ui.setEditorComponent(undefined)`
2382
2424
 
2425
+ To compose with another extension that already replaced the editor, capture the previous factory before setting yours:
2426
+
2427
+ ```typescript
2428
+ const previous = ctx.ui.getEditorComponent();
2429
+ ctx.ui.setEditorComponent((tui, theme, keybindings) =>
2430
+ new MyEditor(tui, theme, keybindings, { base: previous?.(tui, theme, keybindings) })
2431
+ );
2432
+ ```
2433
+
2383
2434
  See [tui.md](tui.md) Pattern 7 for a complete example with mode indicator.
2384
2435
 
2385
2436
  ### Message Rendering
@@ -2389,7 +2440,7 @@ Register a custom renderer for messages with your `customType`:
2389
2440
  ```typescript
2390
2441
  import { Text } from "@eminent337/aery-tui";
2391
2442
 
2392
- aery.registerMessageRenderer("my-extension", (message, options, theme) => {
2443
+ pi.registerMessageRenderer("my-extension", (message, options, theme) => {
2393
2444
  const { expanded } = options;
2394
2445
  let text = theme.fg("accent", `[${message.customType}] `);
2395
2446
  text += message.content;
@@ -2402,10 +2453,10 @@ aery.registerMessageRenderer("my-extension", (message, options, theme) => {
2402
2453
  });
2403
2454
  ```
2404
2455
 
2405
- Messages are sent via `aery.sendMessage()`:
2456
+ Messages are sent via `pi.sendMessage()`:
2406
2457
 
2407
2458
  ```typescript
2408
- aery.sendMessage({
2459
+ pi.sendMessage({
2409
2460
  customType: "my-extension", // Matches registerMessageRenderer
2410
2461
  content: "Status update",
2411
2462
  display: true, // Show in TUI
@@ -2535,12 +2586,11 @@ All examples in [examples/extensions/](../examples/extensions/).
2535
2586
  | `custom-provider-gitlab-duo/` | GitLab Duo integration | `registerProvider` with OAuth |
2536
2587
  | **Messages & Communication** |||
2537
2588
  | `message-renderer.ts` | Custom message rendering | `registerMessageRenderer`, `sendMessage` |
2538
- | `event-bus.ts` | Inter-extension events | `aery.events` |
2589
+ | `event-bus.ts` | Inter-extension events | `pi.events` |
2539
2590
  | **Session Metadata** |||
2540
2591
  | `session-name.ts` | Name sessions for selector | `setSessionName`, `getSessionName` |
2541
2592
  | `bookmark.ts` | Bookmark entries for /tree | `setLabel` |
2542
2593
  | **Misc** |||
2543
- | `antigravity-image-gen.ts` | Image generation tool | `registerTool`, Google Antigravity |
2544
2594
  | `inline-bash.ts` | Inline bash in tool calls | `on("tool_call")` |
2545
2595
  | `bash-spawn-hook.ts` | Adjust bash command, cwd, and env before execution | `createBashTool`, `spawnHook` |
2546
2596
  | `with-deps/` | Extension with npm dependencies | Package structure with `package.json` |