@mariozechner/pi-coding-agent 0.34.2 → 0.36.0

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 (260) hide show
  1. package/CHANGELOG.md +210 -0
  2. package/README.md +246 -107
  3. package/dist/cli/args.d.ts +3 -4
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +13 -18
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/config.d.ts +2 -2
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +3 -3
  10. package/dist/config.js.map +1 -1
  11. package/dist/core/agent-session.d.ts +39 -50
  12. package/dist/core/agent-session.d.ts.map +1 -1
  13. package/dist/core/agent-session.js +166 -197
  14. package/dist/core/agent-session.js.map +1 -1
  15. package/dist/core/auth-storage.d.ts.map +1 -1
  16. package/dist/core/auth-storage.js +4 -1
  17. package/dist/core/auth-storage.js.map +1 -1
  18. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  19. package/dist/core/compaction/branch-summarization.js +3 -3
  20. package/dist/core/compaction/branch-summarization.js.map +1 -1
  21. package/dist/core/compaction/compaction.d.ts +1 -1
  22. package/dist/core/compaction/compaction.d.ts.map +1 -1
  23. package/dist/core/compaction/compaction.js +6 -5
  24. package/dist/core/compaction/compaction.js.map +1 -1
  25. package/dist/core/event-bus.d.ts +9 -0
  26. package/dist/core/event-bus.d.ts.map +1 -0
  27. package/dist/core/event-bus.js +25 -0
  28. package/dist/core/event-bus.js.map +1 -0
  29. package/dist/core/exec.d.ts +1 -1
  30. package/dist/core/exec.d.ts.map +1 -1
  31. package/dist/core/exec.js +1 -1
  32. package/dist/core/exec.js.map +1 -1
  33. package/dist/core/extensions/index.d.ts +10 -0
  34. package/dist/core/extensions/index.d.ts.map +1 -0
  35. package/dist/core/extensions/index.js +9 -0
  36. package/dist/core/extensions/index.js.map +1 -0
  37. package/dist/core/extensions/loader.d.ts +21 -0
  38. package/dist/core/extensions/loader.d.ts.map +1 -0
  39. package/dist/core/extensions/loader.js +400 -0
  40. package/dist/core/extensions/loader.js.map +1 -0
  41. package/dist/core/extensions/runner.d.ts +88 -0
  42. package/dist/core/extensions/runner.d.ts.map +1 -0
  43. package/dist/core/{hooks → extensions}/runner.js +52 -141
  44. package/dist/core/extensions/runner.js.map +1 -0
  45. package/dist/core/extensions/types.d.ts +461 -0
  46. package/dist/core/extensions/types.d.ts.map +1 -0
  47. package/dist/core/{hooks → extensions}/types.js +7 -4
  48. package/dist/core/extensions/types.js.map +1 -0
  49. package/dist/core/extensions/wrapper.d.ts +25 -0
  50. package/dist/core/extensions/wrapper.d.ts.map +1 -0
  51. package/dist/core/{hooks/tool-wrapper.js → extensions/wrapper.js} +39 -24
  52. package/dist/core/extensions/wrapper.js.map +1 -0
  53. package/dist/core/index.d.ts +2 -2
  54. package/dist/core/index.d.ts.map +1 -1
  55. package/dist/core/index.js +3 -2
  56. package/dist/core/index.js.map +1 -1
  57. package/dist/core/messages.d.ts +7 -7
  58. package/dist/core/messages.d.ts.map +1 -1
  59. package/dist/core/messages.js +4 -4
  60. package/dist/core/messages.js.map +1 -1
  61. package/dist/core/model-registry.d.ts.map +1 -1
  62. package/dist/core/model-registry.js +2 -0
  63. package/dist/core/model-registry.js.map +1 -1
  64. package/dist/core/model-resolver.d.ts.map +1 -1
  65. package/dist/core/model-resolver.js +1 -0
  66. package/dist/core/model-resolver.js.map +1 -1
  67. package/dist/core/prompt-templates.d.ts +40 -0
  68. package/dist/core/prompt-templates.d.ts.map +1 -0
  69. package/dist/core/{slash-commands.js → prompt-templates.js} +31 -31
  70. package/dist/core/prompt-templates.js.map +1 -0
  71. package/dist/core/sdk.d.ts +29 -52
  72. package/dist/core/sdk.d.ts.map +1 -1
  73. package/dist/core/sdk.js +111 -211
  74. package/dist/core/sdk.js.map +1 -1
  75. package/dist/core/session-manager.d.ts +17 -17
  76. package/dist/core/session-manager.d.ts.map +1 -1
  77. package/dist/core/session-manager.js +25 -10
  78. package/dist/core/session-manager.js.map +1 -1
  79. package/dist/core/settings-manager.d.ts +3 -6
  80. package/dist/core/settings-manager.d.ts.map +1 -1
  81. package/dist/core/settings-manager.js +4 -11
  82. package/dist/core/settings-manager.js.map +1 -1
  83. package/dist/core/system-prompt.d.ts.map +1 -1
  84. package/dist/core/system-prompt.js +4 -2
  85. package/dist/core/system-prompt.js.map +1 -1
  86. package/dist/index.d.ts +4 -5
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +5 -6
  89. package/dist/index.js.map +1 -1
  90. package/dist/main.d.ts.map +1 -1
  91. package/dist/main.js +36 -33
  92. package/dist/main.js.map +1 -1
  93. package/dist/migrations.d.ts +7 -2
  94. package/dist/migrations.d.ts.map +1 -1
  95. package/dist/migrations.js +93 -4
  96. package/dist/migrations.js.map +1 -1
  97. package/dist/modes/interactive/components/bordered-loader.d.ts +1 -1
  98. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  99. package/dist/modes/interactive/components/bordered-loader.js +1 -1
  100. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  101. package/dist/modes/interactive/components/branch-summary-message.d.ts +1 -1
  102. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  103. package/dist/modes/interactive/components/branch-summary-message.js +1 -1
  104. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  105. package/dist/modes/interactive/components/compaction-summary-message.d.ts +1 -1
  106. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  107. package/dist/modes/interactive/components/compaction-summary-message.js +1 -1
  108. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  109. package/dist/modes/interactive/components/custom-editor.d.ts +2 -2
  110. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  111. package/dist/modes/interactive/components/custom-editor.js +4 -4
  112. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  113. package/dist/modes/interactive/components/custom-message.d.ts +18 -0
  114. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -0
  115. package/dist/modes/interactive/components/{hook-message.js → custom-message.js} +3 -3
  116. package/dist/modes/interactive/components/custom-message.js.map +1 -0
  117. package/dist/modes/interactive/components/dynamic-border.d.ts +2 -2
  118. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  119. package/dist/modes/interactive/components/dynamic-border.js +2 -2
  120. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  121. package/dist/modes/interactive/components/{hook-editor.d.ts → extension-editor.d.ts} +3 -3
  122. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -0
  123. package/dist/modes/interactive/components/{hook-editor.js → extension-editor.js} +4 -4
  124. package/dist/modes/interactive/components/extension-editor.js.map +1 -0
  125. package/dist/modes/interactive/components/{hook-input.d.ts → extension-input.d.ts} +3 -3
  126. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -0
  127. package/dist/modes/interactive/components/{hook-input.js → extension-input.js} +3 -3
  128. package/dist/modes/interactive/components/extension-input.js.map +1 -0
  129. package/dist/modes/interactive/components/{hook-selector.d.ts → extension-selector.d.ts} +3 -3
  130. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -0
  131. package/dist/modes/interactive/components/{hook-selector.js → extension-selector.js} +3 -3
  132. package/dist/modes/interactive/components/extension-selector.js.map +1 -0
  133. package/dist/modes/interactive/components/footer.d.ts +3 -3
  134. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  135. package/dist/modes/interactive/components/footer.js +8 -8
  136. package/dist/modes/interactive/components/footer.js.map +1 -1
  137. package/dist/modes/interactive/components/tool-execution.d.ts +3 -3
  138. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  139. package/dist/modes/interactive/components/tool-execution.js +9 -9
  140. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  141. package/dist/modes/interactive/interactive-mode.d.ts +37 -44
  142. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  143. package/dist/modes/interactive/interactive-mode.js +143 -189
  144. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  145. package/dist/modes/print-mode.d.ts.map +1 -1
  146. package/dist/modes/print-mode.js +10 -33
  147. package/dist/modes/print-mode.js.map +1 -1
  148. package/dist/modes/rpc/rpc-client.d.ts +3 -3
  149. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  150. package/dist/modes/rpc/rpc-client.js +3 -3
  151. package/dist/modes/rpc/rpc-client.js.map +1 -1
  152. package/dist/modes/rpc/rpc-mode.d.ts +2 -2
  153. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  154. package/dist/modes/rpc/rpc-mode.js +33 -57
  155. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  156. package/dist/modes/rpc/rpc-types.d.ts +16 -16
  157. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  158. package/dist/modes/rpc/rpc-types.js.map +1 -1
  159. package/docs/extensions.md +1053 -0
  160. package/docs/rpc.md +4 -4
  161. package/docs/sdk.md +62 -93
  162. package/docs/session.md +22 -19
  163. package/docs/skills.md +1 -1
  164. package/docs/tui.md +1 -1
  165. package/examples/README.md +9 -15
  166. package/examples/extensions/README.md +141 -0
  167. package/examples/{hooks → extensions}/auto-commit-on-exit.ts +3 -3
  168. package/examples/extensions/chalk-logger.ts +26 -0
  169. package/examples/{hooks → extensions}/confirm-destructive.ts +3 -3
  170. package/examples/{hooks → extensions}/custom-compaction.ts +6 -6
  171. package/examples/{hooks → extensions}/dirty-repo-guard.ts +8 -4
  172. package/examples/{hooks → extensions}/file-trigger.ts +3 -3
  173. package/examples/{hooks → extensions}/git-checkpoint.ts +3 -3
  174. package/examples/{hooks → extensions}/handoff.ts +3 -3
  175. package/examples/extensions/hello.ts +25 -0
  176. package/examples/{hooks → extensions}/permission-gate.ts +3 -3
  177. package/examples/{hooks → extensions}/pirate.ts +5 -5
  178. package/examples/{hooks → extensions}/plan-mode.ts +6 -6
  179. package/examples/{hooks → extensions}/protected-paths.ts +3 -3
  180. package/examples/{hooks → extensions}/qna.ts +3 -3
  181. package/examples/{custom-tools/question/index.ts → extensions/question.ts} +13 -17
  182. package/examples/{hooks → extensions}/snake.ts +3 -3
  183. package/examples/{hooks → extensions}/status-line.ts +3 -3
  184. package/examples/{custom-tools → extensions}/subagent/README.md +15 -15
  185. package/examples/{custom-tools → extensions}/subagent/index.ts +22 -43
  186. package/examples/{custom-tools/todo/index.ts → extensions/todo.ts} +122 -39
  187. package/examples/{hooks → extensions}/tools.ts +5 -5
  188. package/examples/extensions/with-deps/index.ts +36 -0
  189. package/examples/extensions/with-deps/package-lock.json +31 -0
  190. package/examples/extensions/with-deps/package.json +16 -0
  191. package/examples/sdk/01-minimal.ts +1 -1
  192. package/examples/sdk/05-tools.ts +7 -41
  193. package/examples/sdk/06-extensions.ts +81 -0
  194. package/examples/sdk/08-prompt-templates.ts +42 -0
  195. package/examples/sdk/12-full-control.ts +10 -29
  196. package/examples/sdk/README.md +5 -5
  197. package/package.json +4 -4
  198. package/dist/core/custom-tools/index.d.ts +0 -7
  199. package/dist/core/custom-tools/index.d.ts.map +0 -1
  200. package/dist/core/custom-tools/index.js +0 -6
  201. package/dist/core/custom-tools/index.js.map +0 -1
  202. package/dist/core/custom-tools/loader.d.ts +0 -30
  203. package/dist/core/custom-tools/loader.d.ts.map +0 -1
  204. package/dist/core/custom-tools/loader.js +0 -276
  205. package/dist/core/custom-tools/loader.js.map +0 -1
  206. package/dist/core/custom-tools/types.d.ts +0 -144
  207. package/dist/core/custom-tools/types.d.ts.map +0 -1
  208. package/dist/core/custom-tools/types.js +0 -8
  209. package/dist/core/custom-tools/types.js.map +0 -1
  210. package/dist/core/custom-tools/wrapper.d.ts +0 -15
  211. package/dist/core/custom-tools/wrapper.d.ts.map +0 -1
  212. package/dist/core/custom-tools/wrapper.js +0 -23
  213. package/dist/core/custom-tools/wrapper.js.map +0 -1
  214. package/dist/core/hooks/index.d.ts +0 -6
  215. package/dist/core/hooks/index.d.ts.map +0 -1
  216. package/dist/core/hooks/index.js +0 -6
  217. package/dist/core/hooks/index.js.map +0 -1
  218. package/dist/core/hooks/loader.d.ts +0 -146
  219. package/dist/core/hooks/loader.d.ts.map +0 -1
  220. package/dist/core/hooks/loader.js +0 -275
  221. package/dist/core/hooks/loader.js.map +0 -1
  222. package/dist/core/hooks/runner.d.ts +0 -173
  223. package/dist/core/hooks/runner.d.ts.map +0 -1
  224. package/dist/core/hooks/runner.js.map +0 -1
  225. package/dist/core/hooks/tool-wrapper.d.ts +0 -17
  226. package/dist/core/hooks/tool-wrapper.d.ts.map +0 -1
  227. package/dist/core/hooks/tool-wrapper.js.map +0 -1
  228. package/dist/core/hooks/types.d.ts +0 -767
  229. package/dist/core/hooks/types.d.ts.map +0 -1
  230. package/dist/core/hooks/types.js.map +0 -1
  231. package/dist/core/slash-commands.d.ts +0 -40
  232. package/dist/core/slash-commands.d.ts.map +0 -1
  233. package/dist/core/slash-commands.js.map +0 -1
  234. package/dist/modes/interactive/components/hook-editor.d.ts.map +0 -1
  235. package/dist/modes/interactive/components/hook-editor.js.map +0 -1
  236. package/dist/modes/interactive/components/hook-input.d.ts.map +0 -1
  237. package/dist/modes/interactive/components/hook-input.js.map +0 -1
  238. package/dist/modes/interactive/components/hook-message.d.ts +0 -18
  239. package/dist/modes/interactive/components/hook-message.d.ts.map +0 -1
  240. package/dist/modes/interactive/components/hook-message.js.map +0 -1
  241. package/dist/modes/interactive/components/hook-selector.d.ts.map +0 -1
  242. package/dist/modes/interactive/components/hook-selector.js.map +0 -1
  243. package/docs/custom-tools.md +0 -514
  244. package/docs/extension-loading.md +0 -1004
  245. package/docs/hooks.md +0 -979
  246. package/docs/session-tree-plan.md +0 -441
  247. package/examples/custom-tools/README.md +0 -114
  248. package/examples/custom-tools/hello/index.ts +0 -21
  249. package/examples/hooks/README.md +0 -60
  250. package/examples/hooks/todo/index.ts +0 -134
  251. package/examples/sdk/06-hooks.ts +0 -61
  252. package/examples/sdk/08-slash-commands.ts +0 -42
  253. /package/examples/{custom-tools → extensions}/subagent/agents/planner.md +0 -0
  254. /package/examples/{custom-tools → extensions}/subagent/agents/reviewer.md +0 -0
  255. /package/examples/{custom-tools → extensions}/subagent/agents/scout.md +0 -0
  256. /package/examples/{custom-tools → extensions}/subagent/agents/worker.md +0 -0
  257. /package/examples/{custom-tools → extensions}/subagent/agents.ts +0 -0
  258. /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/implement-and-review.md +0 -0
  259. /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/implement.md +0 -0
  260. /package/examples/{custom-tools/subagent/commands → extensions/subagent/prompts}/scout-and-plan.md +0 -0
package/README.md CHANGED
@@ -36,12 +36,11 @@ Works on Linux, macOS, and Windows (requires bash; see [Windows Setup](#windows-
36
36
  - [Custom System Prompt](#custom-system-prompt)
37
37
  - [Custom Models and Providers](#custom-models-and-providers)
38
38
  - [Settings File](#settings-file)
39
- - [Extensions](#extensions)
39
+ - [Customization](#customization)
40
40
  - [Themes](#themes)
41
- - [Custom Slash Commands](#custom-slash-commands)
41
+ - [Prompt Templates](#prompt-templates)
42
42
  - [Skills](#skills)
43
- - [Hooks](#hooks)
44
- - [Custom Tools](#custom-tools)
43
+ - [Extensions](#extensions)
45
44
  - [CLI Reference](#cli-reference)
46
45
  - [Tools](#tools)
47
46
  - [Programmatic Usage](#programmatic-usage)
@@ -156,6 +155,7 @@ Use `/login` to authenticate with subscription-based or free-tier providers:
156
155
  | GitHub Copilot | GPT-4o, Claude, Gemini via Copilot subscription | Subscription |
157
156
  | Google Gemini CLI | Gemini 2.0/2.5 models | Free (Google account) |
158
157
  | Google Antigravity | Gemini 3, Claude, GPT-OSS | Free (Google account) |
158
+ | OpenAI Codex (ChatGPT Plus/Pro) | Codex models via ChatGPT subscription | Subscription |
159
159
 
160
160
  ```bash
161
161
  pi
@@ -173,8 +173,18 @@ pi
173
173
  - Antigravity uses a sandbox endpoint with access to Gemini 3, Claude (sonnet/opus thinking), and GPT-OSS models
174
174
  - Both are free with any Google account, subject to rate limits
175
175
 
176
+ **OpenAI Codex notes:**
177
+ - Requires ChatGPT Plus/Pro OAuth (`/login openai-codex`)
178
+ - Prompt cache stored under `~/.pi/agent/cache/openai-codex/`
179
+ - Intended for personal use with your own subscription; not for resale or multi-user services. For production, use the OpenAI Platform API.
180
+
176
181
  Credentials stored in `~/.pi/agent/auth.json`. Use `/logout` to clear.
177
182
 
183
+ **Troubleshooting (OAuth):**
184
+ - **Port 1455 in use:** Close the conflicting process or paste the auth code/URL when prompted.
185
+ - **Token expired / refresh failed:** Run `/login` again for the provider to refresh credentials.
186
+ - **Usage limits (429):** Wait for the reset window; pi will surface a friendly message with the approximate retry time.
187
+
178
188
  ### Quick Start
179
189
 
180
190
  ```bash
@@ -374,10 +384,9 @@ The output becomes part of your next prompt, formatted as:
374
384
 
375
385
  ```
376
386
  Ran `ls -la`
377
- ```
387
+
378
388
  <output here>
379
389
  ```
380
- ```
381
390
 
382
391
  Run multiple commands before prompting; all outputs are included together.
383
392
 
@@ -453,7 +462,7 @@ When disabled, neither case triggers automatic compaction (use `/compact` manual
453
462
 
454
463
  > **Note:** Compaction is lossy. The agent loses full conversation access afterward. Size tasks to avoid context limits when possible. For critical context, ask the agent to write a summary to a file, iterate on it until it covers everything, then start a new session with that file. The full session history is preserved in the JSONL file; use `/tree` to revisit any previous point.
455
464
 
456
- See [docs/compaction.md](docs/compaction.md) for how compaction works internally and how to customize it via hooks.
465
+ See [docs/compaction.md](docs/compaction.md) for how compaction works internally and how to customize it via extensions.
457
466
 
458
467
  ### Branching
459
468
 
@@ -547,7 +556,7 @@ Add custom models (Ollama, vLLM, LM Studio, etc.) via `~/.pi/agent/models.json`:
547
556
  }
548
557
  ```
549
558
 
550
- **Supported APIs:** `openai-completions`, `openai-responses`, `anthropic-messages`, `google-generative-ai`
559
+ **Supported APIs:** `openai-completions`, `openai-responses`, `openai-codex-responses`, `anthropic-messages`, `google-generative-ai`
551
560
 
552
561
  **API key resolution:** The `apiKey` field is checked as environment variable name first, then used as literal value.
553
562
 
@@ -667,8 +676,7 @@ Global `~/.pi/agent/settings.json` stores persistent preferences:
667
676
  "images": {
668
677
  "autoResize": true
669
678
  },
670
- "hooks": ["/path/to/hook.ts"],
671
- "customTools": ["/path/to/tool.ts"]
679
+ "extensions": ["/path/to/extension.ts"]
672
680
  }
673
681
  ```
674
682
 
@@ -694,12 +702,11 @@ Global `~/.pi/agent/settings.json` stores persistent preferences:
694
702
  | `terminal.showImages` | Render images inline (supported terminals) | `true` |
695
703
  | `images.autoResize` | Auto-resize images to 2000x2000 max for better model compatibility | `true` |
696
704
  | `doubleEscapeAction` | Action for double-escape with empty editor: `tree` or `branch` | `tree` |
697
- | `hooks` | Additional hook file paths | `[]` |
698
- | `customTools` | Additional custom tool file paths | `[]` |
705
+ | `extensions` | Additional extension file paths | `[]` |
699
706
 
700
707
  ---
701
708
 
702
- ## Extensions
709
+ ## Customization
703
710
 
704
711
  ### Themes
705
712
 
@@ -720,13 +727,13 @@ Select with `/settings`, then edit the file. Changes apply on save.
720
727
 
721
728
  **VS Code terminal fix:** Set `terminal.integrated.minimumContrastRatio` to `1` for accurate colors.
722
729
 
723
- ### Custom Slash Commands
730
+ ### Prompt Templates
724
731
 
725
732
  Define reusable prompts as Markdown files:
726
733
 
727
734
  **Locations:**
728
- - Global: `~/.pi/agent/commands/*.md`
729
- - Project: `.pi/commands/*.md`
735
+ - Global: `~/.pi/agent/prompts/*.md`
736
+ - Project: `.pi/prompts/*.md`
730
737
 
731
738
  **Format:**
732
739
 
@@ -755,7 +762,7 @@ Usage: `/component Button "onClick handler" "disabled support"`
755
762
  - `$1` = `Button`
756
763
  - `$@` or `$ARGUMENTS` = all arguments joined (`Button onClick handler disabled support`)
757
764
 
758
- **Namespacing:** Subdirectories create prefixes. `.pi/commands/frontend/component.md` → `/component (project:frontend)`
765
+ **Namespacing:** Subdirectories create prefixes. `.pi/prompts/frontend/component.md` → `/component (project:frontend)`
759
766
 
760
767
 
761
768
  ### Skills
@@ -807,120 +814,251 @@ cd /path/to/brave-search && npm install
807
814
 
808
815
  > See [docs/skills.md](docs/skills.md) for details, examples, and links to skill repositories. pi can help you create new skills.
809
816
 
810
- ### Hooks
817
+ ### Extensions
818
+
819
+ Extensions are TypeScript modules that extend pi's behavior.
820
+
821
+ **Use cases:**
822
+ - **Custom tools** - Register tools callable by the LLM with custom UI and rendering
823
+ - **Custom commands** - Add `/commands` for users (e.g., `/deploy`, `/stats`)
824
+ - **Event interception** - Block tool calls, modify results, customize compaction
825
+ - **State persistence** - Store data in session, reconstruct on reload/branch
826
+ - **External integrations** - File watchers, webhooks, git checkpointing
827
+ - **Custom UI** - Full TUI control from tools, commands, or event handlers
811
828
 
812
- Hooks are TypeScript modules that extend pi's behavior by subscribing to lifecycle events. Use them to:
829
+ **Locations:**
830
+ - Global: `~/.pi/agent/extensions/*.ts` or `~/.pi/agent/extensions/*/index.ts`
831
+ - Project: `.pi/extensions/*.ts` or `.pi/extensions/*/index.ts`
832
+ - CLI: `--extension <path>` or `-e <path>`
813
833
 
814
- - **Block dangerous commands** (permission gates for `rm -rf`, `sudo`, etc.)
815
- - **Checkpoint code state** (git stash at each turn, restore on `/branch`)
816
- - **Protect paths** (block writes to `.env`, `node_modules/`, etc.)
817
- - **Modify tool output** (filter or transform results before the LLM sees them)
818
- - **Inject messages from external sources to wake up the agent** (file watchers, webhooks, CI systems)
834
+ **Dependencies:** Extensions can have their own dependencies. Place a `package.json` next to the extension (or in a parent directory), run `npm install`, and imports are resolved via [jiti](https://github.com/unjs/jiti). See [examples/extensions/with-deps/](examples/extensions/with-deps/).
819
835
 
820
- **Hook locations:**
821
- - Global: `~/.pi/agent/hooks/*.ts`
822
- - Project: `.pi/hooks/*.ts`
823
- - CLI: `--hook <path>` (for debugging)
836
+ #### Custom Tools
824
837
 
825
- **Quick example** (permission gate):
838
+ Tools are functions the LLM can call. They appear in the system prompt and can have custom rendering.
826
839
 
827
840
  ```typescript
828
- import type { HookAPI } from "@mariozechner/pi-coding-agent/hooks";
841
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
842
+ import { Type } from "@sinclair/typebox";
843
+ import { Text } from "@mariozechner/pi-tui";
844
+
845
+ export default function (pi: ExtensionAPI) {
846
+ pi.registerTool({
847
+ name: "deploy",
848
+ label: "Deploy",
849
+ description: "Deploy the application to production",
850
+ parameters: Type.Object({
851
+ environment: Type.String({ description: "Target environment" }),
852
+ }),
853
+
854
+ async execute(toolCallId, params, onUpdate, ctx, signal) {
855
+ // Show progress via onUpdate
856
+ onUpdate({ status: "Deploying..." });
857
+
858
+ // Ask user for confirmation
859
+ const ok = await ctx.ui.confirm("Deploy?", `Deploy to ${params.environment}?`);
860
+ if (!ok) {
861
+ return { content: [{ type: "text", text: "Cancelled" }], details: { cancelled: true } };
862
+ }
863
+
864
+ // Run shell commands
865
+ const result = await ctx.exec("./deploy.sh", [params.environment], { signal });
866
+
867
+ return {
868
+ content: [{ type: "text", text: result.stdout }],
869
+ details: { environment: params.environment, exitCode: result.exitCode },
870
+ };
871
+ },
872
+
873
+ // Custom TUI rendering (optional)
874
+ renderCall(args, theme) {
875
+ return new Text(theme.bold("deploy ") + theme.fg("accent", args.environment), 0, 0);
876
+ },
877
+ renderResult(result, options, theme) {
878
+ const ok = result.details?.exitCode === 0;
879
+ return new Text(ok ? theme.fg("success", "✓ Deployed") : theme.fg("error", "✗ Failed"), 0, 0);
880
+ },
881
+ });
882
+ }
883
+ ```
829
884
 
830
- export default function (pi: HookAPI) {
885
+ #### Custom Commands
886
+
887
+ Commands are user-invoked via `/name`. They can show custom UI, modify state, or trigger agent turns.
888
+
889
+ ```typescript
890
+ export default function (pi: ExtensionAPI) {
891
+ pi.registerCommand("stats", {
892
+ description: "Show session statistics",
893
+ handler: async (args, ctx) => {
894
+ // Simple notification
895
+ ctx.ui.notify(`${ctx.sessionManager.getEntries().length} entries`, "info");
896
+ },
897
+ });
898
+
899
+ pi.registerCommand("todos", {
900
+ description: "Interactive todo viewer",
901
+ handler: async (args, ctx) => {
902
+ // Full custom UI with keyboard handling
903
+ await ctx.ui.custom((tui, theme, done) => {
904
+ return {
905
+ render(width) {
906
+ return [
907
+ theme.bold("Todos"),
908
+ "- [ ] Item 1",
909
+ "- [x] Item 2",
910
+ "",
911
+ theme.fg("dim", "Press Escape to close"),
912
+ ];
913
+ },
914
+ handleInput(data) {
915
+ if (matchesKey(data, "escape")) done();
916
+ },
917
+ };
918
+ });
919
+ },
920
+ });
921
+ }
922
+ ```
923
+
924
+ #### Event Interception
925
+
926
+ Subscribe to lifecycle events to block, modify, or observe agent behavior.
927
+
928
+ ```typescript
929
+ export default function (pi: ExtensionAPI) {
930
+ // Block dangerous commands
831
931
  pi.on("tool_call", async (event, ctx) => {
832
- if (event.toolName === "bash" && /sudo/.test(event.input.command as string)) {
833
- const ok = await ctx.ui.confirm("Allow sudo?", event.input.command as string);
932
+ if (event.toolName === "bash" && /rm -rf/.test(event.input.command as string)) {
933
+ const ok = await ctx.ui.confirm("Dangerous!", "Allow rm -rf?");
834
934
  if (!ok) return { block: true, reason: "Blocked by user" };
835
935
  }
836
- return undefined;
936
+ });
937
+
938
+ // Modify tool results
939
+ pi.on("tool_result", async (event, ctx) => {
940
+ if (event.toolName === "read") {
941
+ // Redact secrets from file contents
942
+ return { modifiedResult: event.result.replace(/API_KEY=\w+/g, "API_KEY=***") };
943
+ }
944
+ });
945
+
946
+ // Custom compaction
947
+ pi.on("session_before_compact", async (event, ctx) => {
948
+ return { customSummary: "My custom summary of the conversation so far..." };
949
+ });
950
+
951
+ // Git checkpoint on each turn
952
+ pi.on("turn_end", async (event, ctx) => {
953
+ await ctx.exec("git", ["stash", "push", "-m", `pi-checkpoint-${Date.now()}`]);
837
954
  });
838
955
  }
839
956
  ```
840
957
 
841
- **Sending messages from hooks:**
842
-
843
- Use `pi.sendMessage(message, options?)` to inject messages into the session. Messages are persisted as `CustomMessageEntry` and sent to the LLM.
958
+ #### State Persistence
844
959
 
845
- Options:
846
- - `triggerTurn`: If true and agent is idle, starts a new agent turn. Default: false.
847
- - `deliverAs`: When agent is streaming, controls delivery timing:
848
- - `"steer"` (default): Delivered after current tool execution, interrupts remaining tools.
849
- - `"followUp"`: Delivered only after agent finishes all work.
960
+ Store state in session entries that survive reload and work correctly with branching.
850
961
 
851
962
  ```typescript
852
- import * as fs from "node:fs";
853
- import type { HookAPI } from "@mariozechner/pi-coding-agent/hooks";
854
-
855
- export default function (pi: HookAPI) {
856
- pi.on("session_start", async () => {
857
- fs.watch("/tmp/trigger.txt", () => {
858
- const content = fs.readFileSync("/tmp/trigger.txt", "utf-8").trim();
859
- if (content) {
860
- pi.sendMessage({
861
- customType: "file-trigger",
862
- content,
863
- display: true,
864
- }, true); // triggerTurn: start agent loop
963
+ export default function (pi: ExtensionAPI) {
964
+ let counter = 0;
965
+
966
+ // Reconstruct state from session history
967
+ const reconstruct = (ctx) => {
968
+ counter = 0;
969
+ for (const entry of ctx.sessionManager.getBranch()) {
970
+ if (entry.type === "custom" && entry.customType === "my_counter") {
971
+ counter = entry.data.value;
865
972
  }
866
- });
973
+ }
974
+ };
975
+
976
+ pi.on("session_start", async (e, ctx) => reconstruct(ctx));
977
+ pi.on("session_branch", async (e, ctx) => reconstruct(ctx));
978
+ pi.on("session_tree", async (e, ctx) => reconstruct(ctx));
979
+
980
+ pi.registerCommand("increment", {
981
+ handler: async (args, ctx) => {
982
+ counter++;
983
+ ctx.appendEntry("my_counter", { value: counter }); // Persisted in session
984
+ ctx.ui.notify(`Counter: ${counter}`, "info");
985
+ },
867
986
  });
868
987
  }
869
988
  ```
870
989
 
871
- > See [Hooks Documentation](docs/hooks.md) for full API reference. pi can help you create new hooks
872
-
873
- > See [examples/hooks/](examples/hooks/) for working examples including permission gates, git checkpointing, and path protection.
990
+ #### Keyboard Shortcuts
874
991
 
875
- ### Custom Tools
992
+ Register custom keyboard shortcuts (shown in `/hotkeys`):
876
993
 
877
- Custom tools let you extend the built-in toolset (read, write, edit, bash, ...) and are called by the LLM directly. They are TypeScript modules that define tools with optional custom TUI integration for getting user input and custom tool call and result rendering.
878
-
879
- **Tool locations (auto-discovered):**
880
- - Global: `~/.pi/agent/tools/*/index.ts`
881
- - Project: `.pi/tools/*/index.ts`
994
+ ```typescript
995
+ export default function (pi: ExtensionAPI) {
996
+ pi.registerShortcut("ctrl+shift+d", {
997
+ description: "Deploy to production",
998
+ handler: async (ctx) => {
999
+ ctx.ui.notify("Deploying...", "info");
1000
+ await ctx.exec("./deploy.sh", []);
1001
+ },
1002
+ });
1003
+ }
1004
+ ```
882
1005
 
883
- **Explicit paths:**
884
- - CLI: `--tool <path>` (any .ts file)
885
- - Settings: `customTools` array in `settings.json`
1006
+ #### CLI Flags
886
1007
 
887
- **Quick example:**
1008
+ Register custom CLI flags (parsed automatically, shown in `--help`):
888
1009
 
889
1010
  ```typescript
890
- import { Type } from "@sinclair/typebox";
891
- import type { CustomToolFactory } from "@mariozechner/pi-coding-agent";
892
-
893
- const factory: CustomToolFactory = (pi) => ({
894
- name: "greet",
895
- label: "Greeting",
896
- description: "Generate a greeting",
897
- parameters: Type.Object({
898
- name: Type.String({ description: "Name to greet" }),
899
- }),
900
-
901
- async execute(toolCallId, params, onUpdate, ctx, signal) {
902
- const { name } = params as { name: string };
903
- return {
904
- content: [{ type: "text", text: `Hello, ${name}!` }],
905
- details: { greeted: name },
906
- };
907
- },
908
- });
1011
+ export default function (pi: ExtensionAPI) {
1012
+ pi.registerFlag("--dry-run", {
1013
+ description: "Run without making changes",
1014
+ type: "boolean",
1015
+ });
909
1016
 
910
- export default factory;
1017
+ pi.on("tool_call", async (event, ctx) => {
1018
+ if (pi.getFlag("dry-run") && event.toolName === "write") {
1019
+ return { block: true, reason: "Dry run mode" };
1020
+ }
1021
+ });
1022
+ }
911
1023
  ```
912
1024
 
913
- **Features:**
914
- - Access to `pi.cwd`, `pi.exec()`, `pi.ui` (select/confirm/input dialogs)
915
- - Session lifecycle via `onSession` callback (for state reconstruction)
916
- - Custom rendering via `renderCall()` and `renderResult()` methods
917
- - Streaming results via `onUpdate` callback
918
- - Abort handling via `signal` parameter
919
- - Multiple tools from one factory (return an array)
1025
+ #### Custom UI
1026
+
1027
+ Extensions have full TUI access via `ctx.ui`:
920
1028
 
921
- > See [Custom Tools Documentation](docs/custom-tools.md) for the full API reference, TUI component guide, and examples. pi can help you create custom tools.
1029
+ ```typescript
1030
+ // Simple prompts
1031
+ const confirmed = await ctx.ui.confirm("Title", "Are you sure?");
1032
+ const choice = await ctx.ui.select("Pick one", ["Option A", "Option B"]);
1033
+ const text = await ctx.ui.input("Enter value");
1034
+
1035
+ // Notifications
1036
+ ctx.ui.notify("Done!", "success"); // success, info, warning, error
1037
+
1038
+ // Status line (persistent in footer, multiple extensions can set their own)
1039
+ ctx.ui.setStatus("my-ext", "Processing...");
1040
+ ctx.ui.setStatus("my-ext", null); // Clear
1041
+
1042
+ // Widgets (above editor)
1043
+ ctx.ui.setWidget("my-ext", ["Line 1", "Line 2"]);
1044
+
1045
+ // Full custom component with keyboard handling
1046
+ await ctx.ui.custom((tui, theme, done) => ({
1047
+ render(width) {
1048
+ return [
1049
+ theme.bold("My Component"),
1050
+ theme.fg("dim", "Press Escape to close"),
1051
+ ];
1052
+ },
1053
+ handleInput(data) {
1054
+ if (matchesKey(data, "escape")) done();
1055
+ },
1056
+ }));
1057
+ ```
922
1058
 
923
- > See [examples/custom-tools/](examples/custom-tools/) for working examples including a todo list with session state management and a question tool with UI interaction.
1059
+ > See [docs/extensions.md](docs/extensions.md) for full API reference.
1060
+ > See [docs/tui.md](docs/tui.md) for TUI components and custom rendering.
1061
+ > See [examples/extensions/](examples/extensions/) for working examples.
924
1062
 
925
1063
  ---
926
1064
 
@@ -934,7 +1072,7 @@ pi [options] [@files...] [messages...]
934
1072
 
935
1073
  | Option | Description |
936
1074
  |--------|-------------|
937
- | `--provider <name>` | Provider: `anthropic`, `openai`, `google`, `mistral`, `xai`, `groq`, `cerebras`, `openrouter`, `zai`, `github-copilot`, `google-gemini-cli`, `google-antigravity`, or custom |
1075
+ | `--provider <name>` | Provider: `anthropic`, `openai`, `openai-codex`, `google`, `mistral`, `xai`, `groq`, `cerebras`, `openrouter`, `zai`, `github-copilot`, `google-gemini-cli`, `google-antigravity`, or custom |
938
1076
  | `--model <id>` | Model ID |
939
1077
  | `--api-key <key>` | API key (overrides environment) |
940
1078
  | `--system-prompt <text\|file>` | Custom system prompt (text or file path) |
@@ -949,7 +1087,7 @@ pi [options] [@files...] [messages...]
949
1087
  | `--models <patterns>` | Comma-separated patterns for Ctrl+P cycling. Supports glob patterns (e.g., `anthropic/*`, `*sonnet*:high`) and fuzzy matching (e.g., `sonnet,haiku:low`) |
950
1088
  | `--tools <tools>` | Comma-separated tool list (default: `read,bash,edit,write`) |
951
1089
  | `--thinking <level>` | Thinking level: `off`, `minimal`, `low`, `medium`, `high` |
952
- | `--hook <path>` | Load a hook file (can be used multiple times) |
1090
+ | `--extension <path>`, `-e` | Load an extension file (can be used multiple times) |
953
1091
  | `--no-skills` | Disable skills discovery and loading |
954
1092
  | `--skills <patterns>` | Comma-separated glob patterns to filter skills (e.g., `git-*,docker`) |
955
1093
  | `--export <file> [output]` | Export session to HTML |
@@ -1033,7 +1171,7 @@ Available via `--tools` flag:
1033
1171
 
1034
1172
  Example: `--tools read,grep,find,ls` for code review without modification.
1035
1173
 
1036
- For adding new tools, see [Custom Tools](#custom-tools) in the Configuration section.
1174
+ For adding new tools, see [Extensions](#extensions) in the Customization section.
1037
1175
 
1038
1176
  ---
1039
1177
 
@@ -1068,8 +1206,8 @@ The SDK provides full control over:
1068
1206
  - Model selection and thinking level
1069
1207
  - System prompt (replace or modify)
1070
1208
  - Tools (built-in subsets, custom tools)
1071
- - Hooks (inline or discovered)
1072
- - Skills, context files, slash commands
1209
+ - Extensions (discovered or via paths)
1210
+ - Skills, context files, prompt templates
1073
1211
  - Session persistence (`SessionManager`)
1074
1212
  - Settings (`SettingsManager`)
1075
1213
  - API key resolution and OAuth
@@ -1101,7 +1239,7 @@ pi --export session.jsonl # Auto-generated filename
1101
1239
  pi --export session.jsonl output.html # Custom filename
1102
1240
  ```
1103
1241
 
1104
- Works with both session files and streaming event logs from `--mode json`.
1242
+ Works with session files.
1105
1243
 
1106
1244
  ---
1107
1245
 
@@ -1111,13 +1249,13 @@ Pi is opinionated about what it won't do. These are intentional design decisions
1111
1249
 
1112
1250
  **No MCP.** Build CLI tools with READMEs (see [Skills](#skills)). The agent reads them on demand. [Would you like to know more?](https://mariozechner.at/posts/2025-11-02-what-if-you-dont-need-mcp/)
1113
1251
 
1114
- **No sub-agents.** Spawn pi instances via tmux, or [build your own sub-agent tool](examples/custom-tools/subagent/) with [custom tools](#custom-tools). Full observability and steerability.
1252
+ **No sub-agents.** Spawn pi instances via tmux, or [build your own sub-agent tool](examples/extensions/subagent/) with [Extensions](#extensions). Full observability and steerability.
1115
1253
 
1116
- **No permission popups.** Security theater. Run in a container or build your own with [Hooks](#hooks).
1254
+ **No permission popups.** Security theater. Run in a container or build your own with [Extensions](#extensions).
1117
1255
 
1118
1256
  **No plan mode.** Gather context in one session, write plans to file, start fresh for implementation.
1119
1257
 
1120
- **No built-in to-dos.** They confuse models. Use a TODO.md file, or [build your own](examples/custom-tools/todo/) with [custom tools](#custom-tools).
1258
+ **No built-in to-dos.** They confuse models. Use a TODO.md file, or [build your own](examples/extensions/todo.ts) with [Extensions](#extensions).
1121
1259
 
1122
1260
  **No background bash.** Use tmux. Full observability, direct interaction.
1123
1261
 
@@ -1168,3 +1306,4 @@ MIT
1168
1306
 
1169
1307
  - [@mariozechner/pi-ai](https://www.npmjs.com/package/@mariozechner/pi-ai): Core LLM toolkit
1170
1308
  - [@mariozechner/pi-agent](https://www.npmjs.com/package/@mariozechner/pi-agent): Agent framework
1309
+ - [@mariozechner/pi-tui](https://www.npmjs.com/package/@mariozechner/pi-tui): Terminal UI components
@@ -21,8 +21,7 @@ export interface Args {
21
21
  sessionDir?: string;
22
22
  models?: string[];
23
23
  tools?: ToolName[];
24
- hooks?: string[];
25
- customTools?: string[];
24
+ extensions?: string[];
26
25
  print?: boolean;
27
26
  export?: string;
28
27
  noSkills?: boolean;
@@ -30,11 +29,11 @@ export interface Args {
30
29
  listModels?: string | true;
31
30
  messages: string[];
32
31
  fileArgs: string[];
33
- /** Unknown flags (potentially hook flags) - map of flag name to value */
32
+ /** Unknown flags (potentially extension flags) - map of flag name to value */
34
33
  unknownFlags: Map<string, boolean | string>;
35
34
  }
36
35
  export declare function isValidThinkingLevel(level: string): level is ThinkingLevel;
37
- export declare function parseArgs(args: string[], hookFlags?: Map<string, {
36
+ export declare function parseArgs(args: string[], extensionFlags?: Map<string, {
38
37
  type: "boolean" | "string";
39
38
  }>): Args;
40
39
  export declare function printHelp(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAGjE,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEjE,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE3C,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,yEAAyE;IACzE,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;CAC5C;AAID,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,aAAa,CAE1E;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAA;CAAE,CAAC,GAAG,IAAI,CA2GvG;AAED,wBAAgB,SAAS,IAAI,IAAI,CAiGhC","sourcesContent":["/**\n * CLI argument parsing and help display\n */\n\nimport type { ThinkingLevel } from \"@mariozechner/pi-agent-core\";\nimport chalk from \"chalk\";\nimport { APP_NAME, CONFIG_DIR_NAME, ENV_AGENT_DIR } from \"../config.js\";\nimport { allTools, type ToolName } from \"../core/tools/index.js\";\n\nexport type Mode = \"text\" | \"json\" | \"rpc\";\n\nexport interface Args {\n\tprovider?: string;\n\tmodel?: string;\n\tapiKey?: string;\n\tsystemPrompt?: string;\n\tappendSystemPrompt?: string;\n\tthinking?: ThinkingLevel;\n\tcontinue?: boolean;\n\tresume?: boolean;\n\thelp?: boolean;\n\tversion?: boolean;\n\tmode?: Mode;\n\tnoSession?: boolean;\n\tsession?: string;\n\tsessionDir?: string;\n\tmodels?: string[];\n\ttools?: ToolName[];\n\thooks?: string[];\n\tcustomTools?: string[];\n\tprint?: boolean;\n\texport?: string;\n\tnoSkills?: boolean;\n\tskills?: string[];\n\tlistModels?: string | true;\n\tmessages: string[];\n\tfileArgs: string[];\n\t/** Unknown flags (potentially hook flags) - map of flag name to value */\n\tunknownFlags: Map<string, boolean | string>;\n}\n\nconst VALID_THINKING_LEVELS = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"] as const;\n\nexport function isValidThinkingLevel(level: string): level is ThinkingLevel {\n\treturn VALID_THINKING_LEVELS.includes(level as ThinkingLevel);\n}\n\nexport function parseArgs(args: string[], hookFlags?: Map<string, { type: \"boolean\" | \"string\" }>): Args {\n\tconst result: Args = {\n\t\tmessages: [],\n\t\tfileArgs: [],\n\t\tunknownFlags: new Map(),\n\t};\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\n\t\tif (arg === \"--help\" || arg === \"-h\") {\n\t\t\tresult.help = true;\n\t\t} else if (arg === \"--version\" || arg === \"-v\") {\n\t\t\tresult.version = true;\n\t\t} else if (arg === \"--mode\" && i + 1 < args.length) {\n\t\t\tconst mode = args[++i];\n\t\t\tif (mode === \"text\" || mode === \"json\" || mode === \"rpc\") {\n\t\t\t\tresult.mode = mode;\n\t\t\t}\n\t\t} else if (arg === \"--continue\" || arg === \"-c\") {\n\t\t\tresult.continue = true;\n\t\t} else if (arg === \"--resume\" || arg === \"-r\") {\n\t\t\tresult.resume = true;\n\t\t} else if (arg === \"--provider\" && i + 1 < args.length) {\n\t\t\tresult.provider = args[++i];\n\t\t} else if (arg === \"--model\" && i + 1 < args.length) {\n\t\t\tresult.model = args[++i];\n\t\t} else if (arg === \"--api-key\" && i + 1 < args.length) {\n\t\t\tresult.apiKey = args[++i];\n\t\t} else if (arg === \"--system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.systemPrompt = args[++i];\n\t\t} else if (arg === \"--append-system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.appendSystemPrompt = args[++i];\n\t\t} else if (arg === \"--no-session\") {\n\t\t\tresult.noSession = true;\n\t\t} else if (arg === \"--session\" && i + 1 < args.length) {\n\t\t\tresult.session = args[++i];\n\t\t} else if (arg === \"--session-dir\" && i + 1 < args.length) {\n\t\t\tresult.sessionDir = args[++i];\n\t\t} else if (arg === \"--models\" && i + 1 < args.length) {\n\t\t\tresult.models = args[++i].split(\",\").map((s) => s.trim());\n\t\t} else if (arg === \"--tools\" && i + 1 < args.length) {\n\t\t\tconst toolNames = args[++i].split(\",\").map((s) => s.trim());\n\t\t\tconst validTools: ToolName[] = [];\n\t\t\tfor (const name of toolNames) {\n\t\t\t\tif (name in allTools) {\n\t\t\t\t\tvalidTools.push(name as ToolName);\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\tchalk.yellow(`Warning: Unknown tool \"${name}\". Valid tools: ${Object.keys(allTools).join(\", \")}`),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tresult.tools = validTools;\n\t\t} else if (arg === \"--thinking\" && i + 1 < args.length) {\n\t\t\tconst level = args[++i];\n\t\t\tif (isValidThinkingLevel(level)) {\n\t\t\t\tresult.thinking = level;\n\t\t\t} else {\n\t\t\t\tconsole.error(\n\t\t\t\t\tchalk.yellow(\n\t\t\t\t\t\t`Warning: Invalid thinking level \"${level}\". Valid values: ${VALID_THINKING_LEVELS.join(\", \")}`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (arg === \"--print\" || arg === \"-p\") {\n\t\t\tresult.print = true;\n\t\t} else if (arg === \"--export\" && i + 1 < args.length) {\n\t\t\tresult.export = args[++i];\n\t\t} else if (arg === \"--hook\" && i + 1 < args.length) {\n\t\t\tresult.hooks = result.hooks ?? [];\n\t\t\tresult.hooks.push(args[++i]);\n\t\t} else if (arg === \"--tool\" && i + 1 < args.length) {\n\t\t\tresult.customTools = result.customTools ?? [];\n\t\t\tresult.customTools.push(args[++i]);\n\t\t} else if (arg === \"--no-skills\") {\n\t\t\tresult.noSkills = true;\n\t\t} else if (arg === \"--skills\" && i + 1 < args.length) {\n\t\t\t// Comma-separated glob patterns for skill filtering\n\t\t\tresult.skills = args[++i].split(\",\").map((s) => s.trim());\n\t\t} else if (arg === \"--list-models\") {\n\t\t\t// Check if next arg is a search pattern (not a flag or file arg)\n\t\t\tif (i + 1 < args.length && !args[i + 1].startsWith(\"-\") && !args[i + 1].startsWith(\"@\")) {\n\t\t\t\tresult.listModels = args[++i];\n\t\t\t} else {\n\t\t\t\tresult.listModels = true;\n\t\t\t}\n\t\t} else if (arg.startsWith(\"@\")) {\n\t\t\tresult.fileArgs.push(arg.slice(1)); // Remove @ prefix\n\t\t} else if (arg.startsWith(\"--\") && hookFlags) {\n\t\t\t// Check if it's a hook-registered flag\n\t\t\tconst flagName = arg.slice(2);\n\t\t\tconst hookFlag = hookFlags.get(flagName);\n\t\t\tif (hookFlag) {\n\t\t\t\tif (hookFlag.type === \"boolean\") {\n\t\t\t\t\tresult.unknownFlags.set(flagName, true);\n\t\t\t\t} else if (hookFlag.type === \"string\" && i + 1 < args.length) {\n\t\t\t\t\tresult.unknownFlags.set(flagName, args[++i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Unknown flags without hookFlags are silently ignored (first pass)\n\t\t} else if (!arg.startsWith(\"-\")) {\n\t\t\tresult.messages.push(arg);\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport function printHelp(): void {\n\tconsole.log(`${chalk.bold(APP_NAME)} - AI coding assistant with read, bash, edit, write tools\n\n${chalk.bold(\"Usage:\")}\n ${APP_NAME} [options] [@files...] [messages...]\n\n${chalk.bold(\"Options:\")}\n --provider <name> Provider name (default: google)\n --model <id> Model ID (default: gemini-2.5-flash)\n --api-key <key> API key (defaults to env vars)\n --system-prompt <text> System prompt (default: coding assistant prompt)\n --append-system-prompt <text> Append text or file contents to the system prompt\n --mode <mode> Output mode: text (default), json, or rpc\n --print, -p Non-interactive mode: process prompt and exit\n --continue, -c Continue previous session\n --resume, -r Select a session to resume\n --session <path> Use specific session file\n --session-dir <dir> Directory for session storage and lookup\n --no-session Don't save session (ephemeral)\n --models <patterns> Comma-separated model patterns for Ctrl+P cycling\n Supports globs (anthropic/*, *sonnet*) and fuzzy matching\n --tools <tools> Comma-separated list of tools to enable (default: read,bash,edit,write)\n Available: read, bash, edit, write, grep, find, ls\n --thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh\n --hook <path> Load a hook file (can be used multiple times)\n --tool <path> Load a custom tool file (can be used multiple times)\n --no-skills Disable skills discovery and loading\n --skills <patterns> Comma-separated glob patterns to filter skills (e.g., git-*,docker)\n --export <file> Export session file to HTML and exit\n --list-models [search] List available models (with optional fuzzy search)\n --help, -h Show this help\n --version, -v Show version number\n\nHooks can register additional flags (e.g., --plan from plan-mode hook).\n\n${chalk.bold(\"Examples:\")}\n # Interactive mode\n ${APP_NAME}\n\n # Interactive mode with initial prompt\n ${APP_NAME} \"List all .ts files in src/\"\n\n # Include files in initial message\n ${APP_NAME} @prompt.md @image.png \"What color is the sky?\"\n\n # Non-interactive mode (process and exit)\n ${APP_NAME} -p \"List all .ts files in src/\"\n\n # Multiple messages (interactive)\n ${APP_NAME} \"Read package.json\" \"What dependencies do we have?\"\n\n # Continue previous session\n ${APP_NAME} --continue \"What did we discuss?\"\n\n # Use different model\n ${APP_NAME} --provider openai --model gpt-4o-mini \"Help me refactor this code\"\n\n # Limit model cycling to specific models\n ${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o\n\n # Limit to a specific provider with glob pattern\n ${APP_NAME} --models \"github-copilot/*\"\n\n # Cycle models with fixed thinking levels\n ${APP_NAME} --models sonnet:high,haiku:low\n\n # Start with a specific thinking level\n ${APP_NAME} --thinking high \"Solve this complex problem\"\n\n # Read-only mode (no file modifications possible)\n ${APP_NAME} --tools read,grep,find,ls -p \"Review the code in src/\"\n\n # Export a session file to HTML\n ${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent/sessions/--path--/session.jsonl\n ${APP_NAME} --export session.jsonl output.html\n\n${chalk.bold(\"Environment Variables:\")}\n ANTHROPIC_API_KEY - Anthropic Claude API key\n ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth token (alternative to API key)\n OPENAI_API_KEY - OpenAI GPT API key\n GEMINI_API_KEY - Google Gemini API key\n GROQ_API_KEY - Groq API key\n CEREBRAS_API_KEY - Cerebras API key\n XAI_API_KEY - xAI Grok API key\n OPENROUTER_API_KEY - OpenRouter API key\n ZAI_API_KEY - ZAI API key\n ${ENV_AGENT_DIR.padEnd(23)} - Session storage directory (default: ~/${CONFIG_DIR_NAME}/agent)\n\n${chalk.bold(\"Available Tools (default: read, bash, edit, write):\")}\n read - Read file contents\n bash - Execute bash commands\n edit - Edit files with find/replace\n write - Write files (creates/overwrites)\n grep - Search file contents (read-only, off by default)\n find - Find files by glob pattern (read-only, off by default)\n ls - List directory contents (read-only, off by default)\n`);\n}\n"]}
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAGjE,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEjE,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE3C,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,8EAA8E;IAC9E,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;CAC5C;AAID,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,aAAa,CAE1E;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAA;CAAE,CAAC,GAAG,IAAI,CAwG5G;AAED,wBAAgB,SAAS,IAAI,IAAI,CAgGhC","sourcesContent":["/**\n * CLI argument parsing and help display\n */\n\nimport type { ThinkingLevel } from \"@mariozechner/pi-agent-core\";\nimport chalk from \"chalk\";\nimport { APP_NAME, CONFIG_DIR_NAME, ENV_AGENT_DIR } from \"../config.js\";\nimport { allTools, type ToolName } from \"../core/tools/index.js\";\n\nexport type Mode = \"text\" | \"json\" | \"rpc\";\n\nexport interface Args {\n\tprovider?: string;\n\tmodel?: string;\n\tapiKey?: string;\n\tsystemPrompt?: string;\n\tappendSystemPrompt?: string;\n\tthinking?: ThinkingLevel;\n\tcontinue?: boolean;\n\tresume?: boolean;\n\thelp?: boolean;\n\tversion?: boolean;\n\tmode?: Mode;\n\tnoSession?: boolean;\n\tsession?: string;\n\tsessionDir?: string;\n\tmodels?: string[];\n\ttools?: ToolName[];\n\textensions?: string[];\n\tprint?: boolean;\n\texport?: string;\n\tnoSkills?: boolean;\n\tskills?: string[];\n\tlistModels?: string | true;\n\tmessages: string[];\n\tfileArgs: string[];\n\t/** Unknown flags (potentially extension flags) - map of flag name to value */\n\tunknownFlags: Map<string, boolean | string>;\n}\n\nconst VALID_THINKING_LEVELS = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"] as const;\n\nexport function isValidThinkingLevel(level: string): level is ThinkingLevel {\n\treturn VALID_THINKING_LEVELS.includes(level as ThinkingLevel);\n}\n\nexport function parseArgs(args: string[], extensionFlags?: Map<string, { type: \"boolean\" | \"string\" }>): Args {\n\tconst result: Args = {\n\t\tmessages: [],\n\t\tfileArgs: [],\n\t\tunknownFlags: new Map(),\n\t};\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\n\t\tif (arg === \"--help\" || arg === \"-h\") {\n\t\t\tresult.help = true;\n\t\t} else if (arg === \"--version\" || arg === \"-v\") {\n\t\t\tresult.version = true;\n\t\t} else if (arg === \"--mode\" && i + 1 < args.length) {\n\t\t\tconst mode = args[++i];\n\t\t\tif (mode === \"text\" || mode === \"json\" || mode === \"rpc\") {\n\t\t\t\tresult.mode = mode;\n\t\t\t}\n\t\t} else if (arg === \"--continue\" || arg === \"-c\") {\n\t\t\tresult.continue = true;\n\t\t} else if (arg === \"--resume\" || arg === \"-r\") {\n\t\t\tresult.resume = true;\n\t\t} else if (arg === \"--provider\" && i + 1 < args.length) {\n\t\t\tresult.provider = args[++i];\n\t\t} else if (arg === \"--model\" && i + 1 < args.length) {\n\t\t\tresult.model = args[++i];\n\t\t} else if (arg === \"--api-key\" && i + 1 < args.length) {\n\t\t\tresult.apiKey = args[++i];\n\t\t} else if (arg === \"--system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.systemPrompt = args[++i];\n\t\t} else if (arg === \"--append-system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.appendSystemPrompt = args[++i];\n\t\t} else if (arg === \"--no-session\") {\n\t\t\tresult.noSession = true;\n\t\t} else if (arg === \"--session\" && i + 1 < args.length) {\n\t\t\tresult.session = args[++i];\n\t\t} else if (arg === \"--session-dir\" && i + 1 < args.length) {\n\t\t\tresult.sessionDir = args[++i];\n\t\t} else if (arg === \"--models\" && i + 1 < args.length) {\n\t\t\tresult.models = args[++i].split(\",\").map((s) => s.trim());\n\t\t} else if (arg === \"--tools\" && i + 1 < args.length) {\n\t\t\tconst toolNames = args[++i].split(\",\").map((s) => s.trim());\n\t\t\tconst validTools: ToolName[] = [];\n\t\t\tfor (const name of toolNames) {\n\t\t\t\tif (name in allTools) {\n\t\t\t\t\tvalidTools.push(name as ToolName);\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\tchalk.yellow(`Warning: Unknown tool \"${name}\". Valid tools: ${Object.keys(allTools).join(\", \")}`),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tresult.tools = validTools;\n\t\t} else if (arg === \"--thinking\" && i + 1 < args.length) {\n\t\t\tconst level = args[++i];\n\t\t\tif (isValidThinkingLevel(level)) {\n\t\t\t\tresult.thinking = level;\n\t\t\t} else {\n\t\t\t\tconsole.error(\n\t\t\t\t\tchalk.yellow(\n\t\t\t\t\t\t`Warning: Invalid thinking level \"${level}\". Valid values: ${VALID_THINKING_LEVELS.join(\", \")}`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (arg === \"--print\" || arg === \"-p\") {\n\t\t\tresult.print = true;\n\t\t} else if (arg === \"--export\" && i + 1 < args.length) {\n\t\t\tresult.export = args[++i];\n\t\t} else if ((arg === \"--extension\" || arg === \"-e\") && i + 1 < args.length) {\n\t\t\tresult.extensions = result.extensions ?? [];\n\t\t\tresult.extensions.push(args[++i]);\n\t\t} else if (arg === \"--no-skills\") {\n\t\t\tresult.noSkills = true;\n\t\t} else if (arg === \"--skills\" && i + 1 < args.length) {\n\t\t\t// Comma-separated glob patterns for skill filtering\n\t\t\tresult.skills = args[++i].split(\",\").map((s) => s.trim());\n\t\t} else if (arg === \"--list-models\") {\n\t\t\t// Check if next arg is a search pattern (not a flag or file arg)\n\t\t\tif (i + 1 < args.length && !args[i + 1].startsWith(\"-\") && !args[i + 1].startsWith(\"@\")) {\n\t\t\t\tresult.listModels = args[++i];\n\t\t\t} else {\n\t\t\t\tresult.listModels = true;\n\t\t\t}\n\t\t} else if (arg.startsWith(\"@\")) {\n\t\t\tresult.fileArgs.push(arg.slice(1)); // Remove @ prefix\n\t\t} else if (arg.startsWith(\"--\") && extensionFlags) {\n\t\t\t// Check if it's an extension-registered flag\n\t\t\tconst flagName = arg.slice(2);\n\t\t\tconst extFlag = extensionFlags.get(flagName);\n\t\t\tif (extFlag) {\n\t\t\t\tif (extFlag.type === \"boolean\") {\n\t\t\t\t\tresult.unknownFlags.set(flagName, true);\n\t\t\t\t} else if (extFlag.type === \"string\" && i + 1 < args.length) {\n\t\t\t\t\tresult.unknownFlags.set(flagName, args[++i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Unknown flags without extensionFlags are silently ignored (first pass)\n\t\t} else if (!arg.startsWith(\"-\")) {\n\t\t\tresult.messages.push(arg);\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport function printHelp(): void {\n\tconsole.log(`${chalk.bold(APP_NAME)} - AI coding assistant with read, bash, edit, write tools\n\n${chalk.bold(\"Usage:\")}\n ${APP_NAME} [options] [@files...] [messages...]\n\n${chalk.bold(\"Options:\")}\n --provider <name> Provider name (default: google)\n --model <id> Model ID (default: gemini-2.5-flash)\n --api-key <key> API key (defaults to env vars)\n --system-prompt <text> System prompt (default: coding assistant prompt)\n --append-system-prompt <text> Append text or file contents to the system prompt\n --mode <mode> Output mode: text (default), json, or rpc\n --print, -p Non-interactive mode: process prompt and exit\n --continue, -c Continue previous session\n --resume, -r Select a session to resume\n --session <path> Use specific session file\n --session-dir <dir> Directory for session storage and lookup\n --no-session Don't save session (ephemeral)\n --models <patterns> Comma-separated model patterns for Ctrl+P cycling\n Supports globs (anthropic/*, *sonnet*) and fuzzy matching\n --tools <tools> Comma-separated list of tools to enable (default: read,bash,edit,write)\n Available: read, bash, edit, write, grep, find, ls\n --thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh\n --extension, -e <path> Load an extension file (can be used multiple times)\n --no-skills Disable skills discovery and loading\n --skills <patterns> Comma-separated glob patterns to filter skills (e.g., git-*,docker)\n --export <file> Export session file to HTML and exit\n --list-models [search] List available models (with optional fuzzy search)\n --help, -h Show this help\n --version, -v Show version number\n\nExtensions can register additional flags (e.g., --plan from plan-mode extension).\n\n${chalk.bold(\"Examples:\")}\n # Interactive mode\n ${APP_NAME}\n\n # Interactive mode with initial prompt\n ${APP_NAME} \"List all .ts files in src/\"\n\n # Include files in initial message\n ${APP_NAME} @prompt.md @image.png \"What color is the sky?\"\n\n # Non-interactive mode (process and exit)\n ${APP_NAME} -p \"List all .ts files in src/\"\n\n # Multiple messages (interactive)\n ${APP_NAME} \"Read package.json\" \"What dependencies do we have?\"\n\n # Continue previous session\n ${APP_NAME} --continue \"What did we discuss?\"\n\n # Use different model\n ${APP_NAME} --provider openai --model gpt-4o-mini \"Help me refactor this code\"\n\n # Limit model cycling to specific models\n ${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o\n\n # Limit to a specific provider with glob pattern\n ${APP_NAME} --models \"github-copilot/*\"\n\n # Cycle models with fixed thinking levels\n ${APP_NAME} --models sonnet:high,haiku:low\n\n # Start with a specific thinking level\n ${APP_NAME} --thinking high \"Solve this complex problem\"\n\n # Read-only mode (no file modifications possible)\n ${APP_NAME} --tools read,grep,find,ls -p \"Review the code in src/\"\n\n # Export a session file to HTML\n ${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent/sessions/--path--/session.jsonl\n ${APP_NAME} --export session.jsonl output.html\n\n${chalk.bold(\"Environment Variables:\")}\n ANTHROPIC_API_KEY - Anthropic Claude API key\n ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth token (alternative to API key)\n OPENAI_API_KEY - OpenAI GPT API key\n GEMINI_API_KEY - Google Gemini API key\n GROQ_API_KEY - Groq API key\n CEREBRAS_API_KEY - Cerebras API key\n XAI_API_KEY - xAI Grok API key\n OPENROUTER_API_KEY - OpenRouter API key\n ZAI_API_KEY - ZAI API key\n ${ENV_AGENT_DIR.padEnd(23)} - Session storage directory (default: ~/${CONFIG_DIR_NAME}/agent)\n\n${chalk.bold(\"Available Tools (default: read, bash, edit, write):\")}\n read - Read file contents\n bash - Execute bash commands\n edit - Edit files with find/replace\n write - Write files (creates/overwrites)\n grep - Search file contents (read-only, off by default)\n find - Find files by glob pattern (read-only, off by default)\n ls - List directory contents (read-only, off by default)\n`);\n}\n"]}