@mariozechner/pi-coding-agent 0.30.2 → 0.31.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 (297) hide show
  1. package/CHANGELOG.md +244 -1
  2. package/README.md +105 -84
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +5 -1
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/cli/file-processor.d.ts +3 -3
  7. package/dist/cli/file-processor.d.ts.map +1 -1
  8. package/dist/cli/file-processor.js +7 -10
  9. package/dist/cli/file-processor.js.map +1 -1
  10. package/dist/config.d.ts +9 -0
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +18 -0
  13. package/dist/config.js.map +1 -1
  14. package/dist/core/agent-session.d.ts +73 -34
  15. package/dist/core/agent-session.d.ts.map +1 -1
  16. package/dist/core/agent-session.js +464 -210
  17. package/dist/core/agent-session.js.map +1 -1
  18. package/dist/core/auth-storage.d.ts +2 -2
  19. package/dist/core/auth-storage.d.ts.map +1 -1
  20. package/dist/core/auth-storage.js +2 -2
  21. package/dist/core/auth-storage.js.map +1 -1
  22. package/dist/core/bash-executor.d.ts +2 -2
  23. package/dist/core/bash-executor.d.ts.map +1 -1
  24. package/dist/core/bash-executor.js +2 -2
  25. package/dist/core/bash-executor.js.map +1 -1
  26. package/dist/core/compaction/branch-summarization.d.ts +84 -0
  27. package/dist/core/compaction/branch-summarization.d.ts.map +1 -0
  28. package/dist/core/compaction/branch-summarization.js +233 -0
  29. package/dist/core/compaction/branch-summarization.js.map +1 -0
  30. package/dist/core/{compaction.d.ts → compaction/compaction.d.ts} +38 -19
  31. package/dist/core/compaction/compaction.d.ts.map +1 -0
  32. package/dist/core/compaction/compaction.js +558 -0
  33. package/dist/core/compaction/compaction.js.map +1 -0
  34. package/dist/core/compaction/index.d.ts +7 -0
  35. package/dist/core/compaction/index.d.ts.map +1 -0
  36. package/dist/core/compaction/index.js +7 -0
  37. package/dist/core/compaction/index.js.map +1 -0
  38. package/dist/core/compaction/utils.d.ts +35 -0
  39. package/dist/core/compaction/utils.d.ts.map +1 -0
  40. package/dist/core/compaction/utils.js +138 -0
  41. package/dist/core/compaction/utils.js.map +1 -0
  42. package/dist/core/custom-tools/index.d.ts +2 -1
  43. package/dist/core/custom-tools/index.d.ts.map +1 -1
  44. package/dist/core/custom-tools/index.js +1 -0
  45. package/dist/core/custom-tools/index.js.map +1 -1
  46. package/dist/core/custom-tools/loader.d.ts.map +1 -1
  47. package/dist/core/custom-tools/loader.js +13 -80
  48. package/dist/core/custom-tools/loader.js.map +1 -1
  49. package/dist/core/custom-tools/types.d.ts +84 -59
  50. package/dist/core/custom-tools/types.d.ts.map +1 -1
  51. package/dist/core/custom-tools/types.js.map +1 -1
  52. package/dist/core/custom-tools/wrapper.d.ts +15 -0
  53. package/dist/core/custom-tools/wrapper.d.ts.map +1 -0
  54. package/dist/core/custom-tools/wrapper.js +23 -0
  55. package/dist/core/custom-tools/wrapper.js.map +1 -0
  56. package/dist/core/exec.d.ts +29 -0
  57. package/dist/core/exec.d.ts.map +1 -0
  58. package/dist/core/exec.js +71 -0
  59. package/dist/core/exec.js.map +1 -0
  60. package/dist/core/export-html/index.d.ts +17 -0
  61. package/dist/core/export-html/index.d.ts.map +1 -0
  62. package/dist/core/export-html/index.js +171 -0
  63. package/dist/core/export-html/index.js.map +1 -0
  64. package/dist/core/export-html/template.css +781 -0
  65. package/dist/core/export-html/template.html +54 -0
  66. package/dist/core/export-html/template.js +1185 -0
  67. package/dist/core/export-html/vendor/highlight.min.js +1213 -0
  68. package/dist/core/export-html/vendor/marked.min.js +6 -0
  69. package/dist/core/hooks/index.d.ts +4 -4
  70. package/dist/core/hooks/index.d.ts.map +1 -1
  71. package/dist/core/hooks/index.js +3 -3
  72. package/dist/core/hooks/index.js.map +1 -1
  73. package/dist/core/hooks/loader.d.ts +40 -5
  74. package/dist/core/hooks/loader.d.ts.map +1 -1
  75. package/dist/core/hooks/loader.js +43 -10
  76. package/dist/core/hooks/loader.js.map +1 -1
  77. package/dist/core/hooks/runner.d.ts +94 -18
  78. package/dist/core/hooks/runner.d.ts.map +1 -1
  79. package/dist/core/hooks/runner.js +199 -120
  80. package/dist/core/hooks/runner.js.map +1 -1
  81. package/dist/core/hooks/tool-wrapper.d.ts +1 -1
  82. package/dist/core/hooks/tool-wrapper.d.ts.map +1 -1
  83. package/dist/core/hooks/tool-wrapper.js +36 -19
  84. package/dist/core/hooks/tool-wrapper.js.map +1 -1
  85. package/dist/core/hooks/types.d.ts +407 -96
  86. package/dist/core/hooks/types.d.ts.map +1 -1
  87. package/dist/core/hooks/types.js.map +1 -1
  88. package/dist/core/index.d.ts +4 -3
  89. package/dist/core/index.d.ts.map +1 -1
  90. package/dist/core/index.js.map +1 -1
  91. package/dist/core/messages.d.ts +44 -12
  92. package/dist/core/messages.d.ts.map +1 -1
  93. package/dist/core/messages.js +82 -34
  94. package/dist/core/messages.js.map +1 -1
  95. package/dist/core/model-registry.d.ts +5 -5
  96. package/dist/core/model-registry.d.ts.map +1 -1
  97. package/dist/core/model-registry.js +7 -7
  98. package/dist/core/model-registry.js.map +1 -1
  99. package/dist/core/model-resolver.d.ts +7 -7
  100. package/dist/core/model-resolver.d.ts.map +1 -1
  101. package/dist/core/model-resolver.js +45 -14
  102. package/dist/core/model-resolver.js.map +1 -1
  103. package/dist/core/sdk.d.ts +7 -10
  104. package/dist/core/sdk.d.ts.map +1 -1
  105. package/dist/core/sdk.js +88 -32
  106. package/dist/core/sdk.js.map +1 -1
  107. package/dist/core/session-manager.d.ts +202 -36
  108. package/dist/core/session-manager.d.ts.map +1 -1
  109. package/dist/core/session-manager.js +565 -133
  110. package/dist/core/session-manager.js.map +1 -1
  111. package/dist/core/settings-manager.d.ts +9 -3
  112. package/dist/core/settings-manager.d.ts.map +1 -1
  113. package/dist/core/settings-manager.js +13 -12
  114. package/dist/core/settings-manager.js.map +1 -1
  115. package/dist/core/system-prompt.d.ts.map +1 -1
  116. package/dist/core/system-prompt.js +6 -3
  117. package/dist/core/system-prompt.js.map +1 -1
  118. package/dist/core/tools/bash.d.ts +1 -1
  119. package/dist/core/tools/bash.d.ts.map +1 -1
  120. package/dist/core/tools/bash.js.map +1 -1
  121. package/dist/core/tools/edit-diff.d.ts +33 -0
  122. package/dist/core/tools/edit-diff.d.ts.map +1 -0
  123. package/dist/core/tools/edit-diff.js +171 -0
  124. package/dist/core/tools/edit-diff.js.map +1 -0
  125. package/dist/core/tools/edit.d.ts +7 -1
  126. package/dist/core/tools/edit.d.ts.map +1 -1
  127. package/dist/core/tools/edit.js +20 -95
  128. package/dist/core/tools/edit.js.map +1 -1
  129. package/dist/core/tools/find.d.ts +1 -1
  130. package/dist/core/tools/find.d.ts.map +1 -1
  131. package/dist/core/tools/find.js.map +1 -1
  132. package/dist/core/tools/grep.d.ts +1 -1
  133. package/dist/core/tools/grep.d.ts.map +1 -1
  134. package/dist/core/tools/grep.js.map +1 -1
  135. package/dist/core/tools/index.d.ts +1 -1
  136. package/dist/core/tools/index.d.ts.map +1 -1
  137. package/dist/core/tools/index.js.map +1 -1
  138. package/dist/core/tools/ls.d.ts +1 -1
  139. package/dist/core/tools/ls.d.ts.map +1 -1
  140. package/dist/core/tools/ls.js.map +1 -1
  141. package/dist/core/tools/read.d.ts +1 -1
  142. package/dist/core/tools/read.d.ts.map +1 -1
  143. package/dist/core/tools/read.js.map +1 -1
  144. package/dist/core/tools/write.d.ts +1 -1
  145. package/dist/core/tools/write.d.ts.map +1 -1
  146. package/dist/core/tools/write.js.map +1 -1
  147. package/dist/index.d.ts +8 -7
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +5 -5
  150. package/dist/index.js.map +1 -1
  151. package/dist/main.d.ts.map +1 -1
  152. package/dist/main.js +22 -21
  153. package/dist/main.js.map +1 -1
  154. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  155. package/dist/modes/interactive/components/assistant-message.js +3 -4
  156. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  157. package/dist/modes/interactive/components/bash-execution.d.ts +1 -1
  158. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  159. package/dist/modes/interactive/components/bash-execution.js +6 -2
  160. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  161. package/dist/modes/interactive/components/bordered-loader.d.ts +12 -0
  162. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -0
  163. package/dist/modes/interactive/components/bordered-loader.js +30 -0
  164. package/dist/modes/interactive/components/bordered-loader.js.map +1 -0
  165. package/dist/modes/interactive/components/branch-summary-message.d.ts +14 -0
  166. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -0
  167. package/dist/modes/interactive/components/branch-summary-message.js +35 -0
  168. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -0
  169. package/dist/modes/interactive/components/compaction-summary-message.d.ts +14 -0
  170. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -0
  171. package/dist/modes/interactive/components/compaction-summary-message.js +36 -0
  172. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -0
  173. package/dist/modes/interactive/components/dynamic-border.d.ts +5 -1
  174. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  175. package/dist/modes/interactive/components/dynamic-border.js +5 -1
  176. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  177. package/dist/modes/interactive/components/footer.d.ts +12 -6
  178. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  179. package/dist/modes/interactive/components/footer.js +57 -25
  180. package/dist/modes/interactive/components/footer.js.map +1 -1
  181. package/dist/modes/interactive/components/hook-editor.d.ts +15 -0
  182. package/dist/modes/interactive/components/hook-editor.d.ts.map +1 -0
  183. package/dist/modes/interactive/components/hook-editor.js +95 -0
  184. package/dist/modes/interactive/components/hook-editor.js.map +1 -0
  185. package/dist/modes/interactive/components/hook-message.d.ts +18 -0
  186. package/dist/modes/interactive/components/hook-message.d.ts.map +1 -0
  187. package/dist/modes/interactive/components/hook-message.js +80 -0
  188. package/dist/modes/interactive/components/hook-message.js.map +1 -0
  189. package/dist/modes/interactive/components/model-selector.d.ts +3 -3
  190. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  191. package/dist/modes/interactive/components/model-selector.js +1 -1
  192. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  193. package/dist/modes/interactive/components/tool-execution.d.ts +15 -2
  194. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  195. package/dist/modes/interactive/components/tool-execution.js +70 -21
  196. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  197. package/dist/modes/interactive/components/tree-selector.d.ts +52 -0
  198. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -0
  199. package/dist/modes/interactive/components/tree-selector.js +745 -0
  200. package/dist/modes/interactive/components/tree-selector.js.map +1 -0
  201. package/dist/modes/interactive/components/user-message-selector.d.ts +3 -3
  202. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  203. package/dist/modes/interactive/components/user-message-selector.js +1 -1
  204. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  205. package/dist/modes/interactive/components/user-message.d.ts +1 -1
  206. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  207. package/dist/modes/interactive/components/user-message.js +2 -5
  208. package/dist/modes/interactive/components/user-message.js.map +1 -1
  209. package/dist/modes/interactive/interactive-mode.d.ts +29 -12
  210. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  211. package/dist/modes/interactive/interactive-mode.js +589 -208
  212. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  213. package/dist/modes/interactive/theme/dark.json +13 -1
  214. package/dist/modes/interactive/theme/light.json +13 -1
  215. package/dist/modes/interactive/theme/theme-schema.json +34 -0
  216. package/dist/modes/interactive/theme/theme.d.ts +20 -2
  217. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  218. package/dist/modes/interactive/theme/theme.js +135 -2
  219. package/dist/modes/interactive/theme/theme.js.map +1 -1
  220. package/dist/modes/print-mode.d.ts +3 -3
  221. package/dist/modes/print-mode.d.ts.map +1 -1
  222. package/dist/modes/print-mode.js +26 -20
  223. package/dist/modes/print-mode.js.map +1 -1
  224. package/dist/modes/rpc/rpc-client.d.ts +13 -10
  225. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  226. package/dist/modes/rpc/rpc-client.js +11 -10
  227. package/dist/modes/rpc/rpc-client.js.map +1 -1
  228. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  229. package/dist/modes/rpc/rpc-mode.js +88 -35
  230. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  231. package/dist/modes/rpc/rpc-types.d.ts +30 -11
  232. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  233. package/dist/modes/rpc/rpc-types.js.map +1 -1
  234. package/dist/utils/shell.d.ts +4 -2
  235. package/dist/utils/shell.d.ts.map +1 -1
  236. package/dist/utils/shell.js +36 -7
  237. package/dist/utils/shell.js.map +1 -1
  238. package/dist/utils/tools-manager.d.ts +1 -1
  239. package/dist/utils/tools-manager.d.ts.map +1 -1
  240. package/dist/utils/tools-manager.js +2 -2
  241. package/dist/utils/tools-manager.js.map +1 -1
  242. package/docs/compaction.md +388 -0
  243. package/docs/custom-tools.md +146 -43
  244. package/docs/extension-loading.md +1004 -0
  245. package/docs/hooks.md +562 -596
  246. package/docs/rpc.md +33 -19
  247. package/docs/sdk.md +93 -21
  248. package/docs/session-tree-plan.md +441 -0
  249. package/docs/session.md +172 -21
  250. package/docs/skills.md +2 -0
  251. package/docs/theme.md +31 -2
  252. package/docs/tree.md +197 -0
  253. package/docs/tui.md +343 -0
  254. package/examples/README.md +1 -9
  255. package/examples/custom-tools/hello/index.ts +4 -3
  256. package/examples/custom-tools/question/index.ts +4 -4
  257. package/examples/custom-tools/subagent/index.ts +7 -6
  258. package/examples/custom-tools/todo/index.ts +11 -5
  259. package/examples/hooks/README.md +29 -71
  260. package/examples/hooks/auto-commit-on-exit.ts +8 -9
  261. package/examples/hooks/confirm-destructive.ts +29 -30
  262. package/examples/hooks/custom-compaction.ts +20 -21
  263. package/examples/hooks/dirty-repo-guard.ts +41 -40
  264. package/examples/hooks/file-trigger.ts +10 -5
  265. package/examples/hooks/git-checkpoint.ts +16 -12
  266. package/examples/hooks/handoff.ts +150 -0
  267. package/examples/hooks/permission-gate.ts +1 -1
  268. package/examples/hooks/protected-paths.ts +1 -1
  269. package/examples/hooks/qna.ts +119 -0
  270. package/examples/hooks/snake.ts +343 -0
  271. package/examples/hooks/status-line.ts +40 -0
  272. package/examples/sdk/01-minimal.ts +1 -1
  273. package/examples/sdk/02-custom-model.ts +1 -1
  274. package/examples/sdk/03-custom-prompt.ts +1 -1
  275. package/examples/sdk/04-skills.ts +1 -1
  276. package/examples/sdk/05-tools.ts +4 -4
  277. package/examples/sdk/06-hooks.ts +1 -1
  278. package/examples/sdk/07-context-files.ts +1 -1
  279. package/examples/sdk/08-slash-commands.ts +6 -1
  280. package/examples/sdk/09-api-keys-and-oauth.ts +1 -1
  281. package/examples/sdk/10-settings.ts +1 -1
  282. package/examples/sdk/11-sessions.ts +1 -1
  283. package/examples/sdk/12-full-control.ts +4 -7
  284. package/package.json +6 -6
  285. package/dist/core/compaction.d.ts.map +0 -1
  286. package/dist/core/compaction.js +0 -412
  287. package/dist/core/compaction.js.map +0 -1
  288. package/dist/core/export-html.d.ts +0 -23
  289. package/dist/core/export-html.d.ts.map +0 -1
  290. package/dist/core/export-html.js +0 -1185
  291. package/dist/core/export-html.js.map +0 -1
  292. package/dist/modes/interactive/components/compaction.d.ts +0 -15
  293. package/dist/modes/interactive/components/compaction.d.ts.map +0 -1
  294. package/dist/modes/interactive/components/compaction.js +0 -41
  295. package/dist/modes/interactive/components/compaction.js.map +0 -1
  296. package/docs/hooks-v2.md +0 -385
  297. package/docs/session-tree.md +0 -452
package/CHANGELOG.md CHANGED
@@ -1,6 +1,249 @@
1
1
  # Changelog
2
2
 
3
- ## [Unreleased]
3
+ ## [0.31.0] - 2026-01-02
4
+
5
+ This release introduces session trees for in-place branching, major API changes to hooks and custom tools, and structured compaction with file tracking.
6
+
7
+ ### Session Tree
8
+
9
+ Sessions now use a tree structure with `id`/`parentId` fields. This enables in-place branching: navigate to any previous point with `/tree`, continue from there, and switch between branches while preserving all history in a single file.
10
+
11
+ **Existing sessions are automatically migrated** (v1 → v2) on first load. No manual action required.
12
+
13
+ New entry types: `BranchSummaryEntry` (context from abandoned branches), `CustomEntry` (hook state), `CustomMessageEntry` (hook-injected messages), `LabelEntry` (bookmarks).
14
+
15
+ See [docs/session.md](docs/session.md) for the file format and `SessionManager` API.
16
+
17
+ ### Hooks Migration
18
+
19
+ The hooks API has been restructured with more granular events and better session access.
20
+
21
+ **Type renames:**
22
+ - `HookEventContext` → `HookContext`
23
+ - `HookCommandContext` is now a new interface extending `HookContext` with session control methods
24
+
25
+ **Event changes:**
26
+ - The monolithic `session` event is now split into granular events: `session_start`, `session_before_switch`, `session_switch`, `session_before_branch`, `session_branch`, `session_before_compact`, `session_compact`, `session_shutdown`
27
+ - `session_before_switch` and `session_switch` events now include `reason: "new" | "resume"` to distinguish between `/new` and `/resume`
28
+ - New `session_before_tree` and `session_tree` events for `/tree` navigation (hook can provide custom branch summary)
29
+ - New `before_agent_start` event: inject messages before the agent loop starts
30
+ - New `context` event: modify messages non-destructively before each LLM call
31
+ - Session entries are no longer passed in events. Use `ctx.sessionManager.getEntries()` or `ctx.sessionManager.getBranch()` instead
32
+
33
+ **API changes:**
34
+ - `pi.send(text, attachments?)` → `pi.sendMessage(message, triggerTurn?)` (creates `CustomMessageEntry`)
35
+ - New `pi.appendEntry(customType, data?)` for hook state persistence (not in LLM context)
36
+ - New `pi.registerCommand(name, options)` for custom slash commands (handler receives `HookCommandContext`)
37
+ - New `pi.registerMessageRenderer(customType, renderer)` for custom TUI rendering
38
+ - New `ctx.isIdle()`, `ctx.abort()`, `ctx.hasQueuedMessages()` for agent state (available in all events)
39
+ - New `ctx.ui.editor(title, prefill?)` for multi-line text editing with Ctrl+G external editor support
40
+ - New `ctx.ui.custom(component)` for full TUI component rendering with keyboard focus
41
+ - New `ctx.ui.setStatus(key, text)` for persistent status text in footer (multiple hooks can set their own)
42
+ - New `ctx.ui.theme` getter for styling text with theme colors
43
+ - `ctx.exec()` moved to `pi.exec()`
44
+ - `ctx.sessionFile` → `ctx.sessionManager.getSessionFile()`
45
+ - New `ctx.modelRegistry` and `ctx.model` for API key resolution
46
+
47
+ **HookCommandContext (slash commands only):**
48
+ - `ctx.waitForIdle()` - wait for agent to finish streaming
49
+ - `ctx.newSession(options?)` - create new sessions with optional setup callback
50
+ - `ctx.branch(entryId)` - branch from a specific entry
51
+ - `ctx.navigateTree(targetId, options?)` - navigate the session tree
52
+
53
+ These methods are only on `HookCommandContext` (not `HookContext`) because they can deadlock if called from event handlers that run inside the agent loop.
54
+
55
+ **Removed:**
56
+ - `hookTimeout` setting (hooks no longer have timeouts; use Ctrl+C to abort)
57
+ - `resolveApiKey` parameter (use `ctx.modelRegistry.getApiKey(model)`)
58
+
59
+ See [docs/hooks.md](docs/hooks.md) and [examples/hooks/](examples/hooks/) for the current API.
60
+
61
+ ### Custom Tools Migration
62
+
63
+ The custom tools API has been restructured to mirror the hooks pattern with a context object.
64
+
65
+ **Type renames:**
66
+ - `CustomAgentTool` → `CustomTool`
67
+ - `ToolAPI` → `CustomToolAPI`
68
+ - `ToolContext` → `CustomToolContext`
69
+ - `ToolSessionEvent` → `CustomToolSessionEvent`
70
+
71
+ **Execute signature changed:**
72
+ ```typescript
73
+ // Before (v0.30.2)
74
+ execute(toolCallId, params, signal, onUpdate)
75
+
76
+ // After
77
+ execute(toolCallId, params, onUpdate, ctx, signal?)
78
+ ```
79
+
80
+ The new `ctx: CustomToolContext` provides `sessionManager`, `modelRegistry`, `model`, and agent state methods:
81
+ - `ctx.isIdle()` - check if agent is streaming
82
+ - `ctx.hasQueuedMessages()` - check if user has queued messages (skip interactive prompts)
83
+ - `ctx.abort()` - abort current operation (fire-and-forget)
84
+
85
+ **Session event changes:**
86
+ - `CustomToolSessionEvent` now only has `reason` and `previousSessionFile`
87
+ - Session entries are no longer in the event. Use `ctx.sessionManager.getBranch()` or `ctx.sessionManager.getEntries()` to reconstruct state
88
+ - Reasons: `"start" | "switch" | "branch" | "tree" | "shutdown"` (no separate `"new"` reason; `/new` triggers `"switch"`)
89
+ - `dispose()` method removed. Use `onSession` with `reason: "shutdown"` for cleanup
90
+
91
+ See [docs/custom-tools.md](docs/custom-tools.md) and [examples/custom-tools/](examples/custom-tools/) for the current API.
92
+
93
+ ### SDK Migration
94
+
95
+ **Type changes:**
96
+ - `CustomAgentTool` → `CustomTool`
97
+ - `AppMessage` → `AgentMessage`
98
+ - `sessionFile` returns `string | undefined` (was `string | null`)
99
+ - `model` returns `Model | undefined` (was `Model | null`)
100
+ - `Attachment` type removed. Use `ImageContent` from `@mariozechner/pi-ai` instead. Add images directly to message content arrays.
101
+
102
+ **AgentSession API:**
103
+ - `branch(entryIndex: number)` → `branch(entryId: string)`
104
+ - `getUserMessagesForBranching()` returns `{ entryId, text }` instead of `{ entryIndex, text }`
105
+ - `reset()` → `newSession(options?)` where options has optional `parentSession` for lineage tracking
106
+ - `newSession()` and `switchSession()` now return `Promise<boolean>` (false if cancelled by hook)
107
+ - New `navigateTree(targetId, options?)` for in-place tree navigation
108
+
109
+ **Hook integration:**
110
+ - New `sendHookMessage(message, triggerTurn?)` for hook message injection
111
+
112
+ **SessionManager API:**
113
+ - Method renames: `saveXXX()` → `appendXXX()` (e.g., `appendMessage`, `appendCompaction`)
114
+ - `branchInPlace()` → `branch()`
115
+ - `reset()` → `newSession(options?)` with optional `parentSession` for lineage tracking
116
+ - `createBranchedSessionFromEntries(entries, index)` → `createBranchedSession(leafId)`
117
+ - `SessionHeader.branchedFrom` → `SessionHeader.parentSession`
118
+ - `saveCompaction(entry)` → `appendCompaction(summary, firstKeptEntryId, tokensBefore, details?)`
119
+ - `getEntries()` now excludes the session header (use `getHeader()` separately)
120
+ - `getSessionFile()` returns `string | undefined` (undefined for in-memory sessions)
121
+ - New tree methods: `getTree()`, `getBranch()`, `getLeafId()`, `getLeafEntry()`, `getEntry()`, `getChildren()`, `getLabel()`
122
+ - New append methods: `appendCustomEntry()`, `appendCustomMessageEntry()`, `appendLabelChange()`
123
+ - New branch methods: `branch(entryId)`, `branchWithSummary()`
124
+
125
+ **ModelRegistry (new):**
126
+
127
+ `ModelRegistry` is a new class that manages model discovery and API key resolution. It combines built-in models with custom models from `models.json` and resolves API keys via `AuthStorage`.
128
+
129
+ ```typescript
130
+ import { discoverAuthStorage, discoverModels } from "@mariozechner/pi-coding-agent";
131
+
132
+ const authStorage = discoverAuthStorage(); // ~/.pi/agent/auth.json
133
+ const modelRegistry = discoverModels(authStorage); // + ~/.pi/agent/models.json
134
+
135
+ // Get all models (built-in + custom)
136
+ const allModels = modelRegistry.getAll();
137
+
138
+ // Get only models with valid API keys
139
+ const available = await modelRegistry.getAvailable();
140
+
141
+ // Find specific model
142
+ const model = modelRegistry.find("anthropic", "claude-sonnet-4-20250514");
143
+
144
+ // Get API key for a model
145
+ const apiKey = await modelRegistry.getApiKey(model);
146
+ ```
147
+
148
+ This replaces the old `resolveApiKey` callback pattern. Hooks and custom tools access it via `ctx.modelRegistry`.
149
+
150
+ **Renamed exports:**
151
+ - `messageTransformer` → `convertToLlm`
152
+ - `SessionContext` alias `LoadedSession` removed
153
+
154
+ See [docs/sdk.md](docs/sdk.md) and [examples/sdk/](examples/sdk/) for the current API.
155
+
156
+ ### RPC Migration
157
+
158
+ **Session commands:**
159
+ - `reset` command → `new_session` command with optional `parentSession` field
160
+
161
+ **Branching commands:**
162
+ - `branch` command: `entryIndex` → `entryId`
163
+ - `get_branch_messages` response: `entryIndex` → `entryId`
164
+
165
+ **Type changes:**
166
+ - Messages are now `AgentMessage` (was `AppMessage`)
167
+ - `prompt` command: `attachments` field replaced with `images` field using `ImageContent` format
168
+
169
+ **Compaction events:**
170
+ - `auto_compaction_start` now includes `reason` field (`"threshold"` or `"overflow"`)
171
+ - `auto_compaction_end` now includes `willRetry` field
172
+ - `compact` response includes full `CompactionResult` (`summary`, `firstKeptEntryId`, `tokensBefore`, `details`)
173
+
174
+ See [docs/rpc.md](docs/rpc.md) for the current protocol.
175
+
176
+ ### Structured Compaction
177
+
178
+ Compaction and branch summarization now use a structured output format:
179
+ - Clear sections: Goal, Progress, Key Information, File Operations
180
+ - File tracking: `readFiles` and `modifiedFiles` arrays in `details`, accumulated across compactions
181
+ - Conversations are serialized to text before summarization to prevent the model from "continuing" them
182
+
183
+ The `before_compact` and `before_tree` hook events allow custom compaction implementations. See [docs/compaction.md](docs/compaction.md).
184
+
185
+ ### Interactive Mode
186
+
187
+ **`/tree` command:**
188
+ - Navigate the full session tree in-place
189
+ - Search by typing, page with ←/→
190
+ - Filter modes (Ctrl+O): default → no-tools → user-only → labeled-only → all
191
+ - Press `l` to label entries as bookmarks
192
+ - Selecting a branch switches context and optionally injects a summary of the abandoned branch
193
+
194
+ **Entry labels:**
195
+ - Bookmark any entry via `/tree` → select → `l`
196
+ - Labels appear in tree view and persist as `LabelEntry`
197
+
198
+ **Theme changes (breaking for custom themes):**
199
+
200
+ Custom themes must add these new color tokens or they will fail to load:
201
+ - `selectedBg`: background for selected/highlighted items in tree selector and other components
202
+ - `customMessageBg`: background for hook-injected messages (`CustomMessageEntry`)
203
+ - `customMessageText`: text color for hook messages
204
+ - `customMessageLabel`: label color for hook messages (the `[customType]` prefix)
205
+
206
+ Total color count increased from 46 to 50. See [docs/theme.md](docs/theme.md) for the full color list and copy values from the built-in dark/light themes.
207
+
208
+ **Settings:**
209
+ - `enabledModels`: allowlist models in `settings.json` (same format as `--models` CLI)
210
+
211
+ ### Added
212
+
213
+ - `ctx.ui.setStatus(key, text)` for hooks to display persistent status text in the footer ([#385](https://github.com/badlogic/pi-mono/pull/385) by [@prateekmedia](https://github.com/prateekmedia))
214
+ - `ctx.ui.theme` getter for styling status text and other output with theme colors
215
+ - `/share` command to upload session as a secret GitHub gist and get a shareable URL via shittycodingagent.ai ([#380](https://github.com/badlogic/pi-mono/issues/380))
216
+ - HTML export now includes a tree visualization sidebar for navigating session branches ([#375](https://github.com/badlogic/pi-mono/issues/375))
217
+ - HTML export supports keyboard shortcuts: Ctrl+T to toggle thinking blocks, Ctrl+O to toggle tool outputs
218
+ - HTML export supports theme-configurable background colors via optional `export` section in theme JSON ([#387](https://github.com/badlogic/pi-mono/pull/387) by [@mitsuhiko](https://github.com/mitsuhiko))
219
+ - HTML export syntax highlighting now uses theme colors and matches TUI rendering
220
+ - **Snake game example hook**: Demonstrates `ui.custom()`, `registerCommand()`, and session persistence. See [examples/hooks/snake.ts](examples/hooks/snake.ts).
221
+ - **`thinkingText` theme token**: Configurable color for thinking block text. ([#366](https://github.com/badlogic/pi-mono/pull/366) by [@paulbettner](https://github.com/paulbettner))
222
+
223
+ ### Changed
224
+
225
+ - **Entry IDs**: Session entries now use short 8-character hex IDs instead of full UUIDs
226
+ - **API key priority**: `ANTHROPIC_OAUTH_TOKEN` now takes precedence over `ANTHROPIC_API_KEY`
227
+ - HTML export template split into separate files (template.html, template.css, template.js) for easier maintenance
228
+
229
+ ### Fixed
230
+
231
+ - HTML export now properly sanitizes user messages containing HTML tags like `<style>` that could break DOM rendering
232
+ - Crash when displaying bash output containing Unicode format characters like U+0600-U+0604 ([#372](https://github.com/badlogic/pi-mono/pull/372) by [@HACKE-RC](https://github.com/HACKE-RC))
233
+ - **Footer shows full session stats**: Token usage and cost now include all messages, not just those after compaction. ([#322](https://github.com/badlogic/pi-mono/issues/322))
234
+ - **Status messages spam chat log**: Rapidly changing settings (e.g., thinking level via Shift+Tab) would add multiple status lines. Sequential status updates now coalesce into a single line. ([#365](https://github.com/badlogic/pi-mono/pull/365) by [@paulbettner](https://github.com/paulbettner))
235
+ - **Toggling thinking blocks during streaming shows nothing**: Pressing Ctrl+T while streaming would hide the current message until streaming completed.
236
+ - **Resuming session resets thinking level to off**: Initial model and thinking level were not saved to session file, causing `--resume`/`--continue` to default to `off`. ([#342](https://github.com/badlogic/pi-mono/issues/342) by [@aliou](https://github.com/aliou))
237
+ - **Hook `tool_result` event ignores errors from custom tools**: The `tool_result` hook event was never emitted when tools threw errors, and always had `isError: false` for successful executions. Now emits the event with correct `isError` value in both success and error cases. ([#374](https://github.com/badlogic/pi-mono/issues/374) by [@nicobailon](https://github.com/nicobailon))
238
+ - **Edit tool fails on Windows due to CRLF line endings**: Files with CRLF line endings now match correctly when LLMs send LF-only text. Line endings are normalized before matching and restored to original style on write. ([#355](https://github.com/badlogic/pi-mono/issues/355) by [@Pratham-Dubey](https://github.com/Pratham-Dubey))
239
+ - **Edit tool fails on files with UTF-8 BOM**: Files with UTF-8 BOM marker could cause "text not found" errors since the LLM doesn't include the invisible BOM character. BOM is now stripped before matching and restored on write. ([#394](https://github.com/badlogic/pi-mono/pull/394) by [@prathamdby](https://github.com/prathamdby))
240
+ - **Use bash instead of sh on Unix**: Fixed shell commands using `/bin/sh` instead of `/bin/bash` on Unix systems. ([#328](https://github.com/badlogic/pi-mono/pull/328) by [@dnouri](https://github.com/dnouri))
241
+ - **OAuth login URL clickable**: Made OAuth login URLs clickable in terminal. ([#349](https://github.com/badlogic/pi-mono/pull/349) by [@Cursivez](https://github.com/Cursivez))
242
+ - **Improved error messages**: Better error messages when `apiKey` or `model` are missing. ([#346](https://github.com/badlogic/pi-mono/pull/346) by [@ronyrus](https://github.com/ronyrus))
243
+ - **Session file validation**: `findMostRecentSession()` now validates session headers before returning, preventing non-session JSONL files from being loaded
244
+ - **Compaction error handling**: `generateSummary()` and `generateTurnPrefixSummary()` now throw on LLM errors instead of returning empty strings
245
+ - **Compaction with branched sessions**: Fixed compaction incorrectly including entries from abandoned branches, causing token overflow errors. Compaction now uses `sessionManager.getPath()` to work only on the current branch path, eliminating 80+ lines of duplicate entry collection logic between `prepareCompaction()` and `compact()`
246
+ - **enabledModels glob patterns**: `--models` and `enabledModels` now support glob patterns like `github-copilot/*` or `*sonnet*`. Previously, patterns were only matched literally or via substring search. ([#337](https://github.com/badlogic/pi-mono/issues/337))
4
247
 
5
248
  ## [0.30.2] - 2025-12-26
6
249
 
package/README.md CHANGED
@@ -25,12 +25,13 @@ Works on Linux, macOS, and Windows (requires bash; see [Windows Setup](#windows-
25
25
  - [Project Context Files](#project-context-files)
26
26
  - [Custom System Prompt](#custom-system-prompt)
27
27
  - [Custom Models and Providers](#custom-models-and-providers)
28
+ - [Settings File](#settings-file)
29
+ - [Extensions](#extensions)
28
30
  - [Themes](#themes)
29
31
  - [Custom Slash Commands](#custom-slash-commands)
30
32
  - [Skills](#skills)
31
33
  - [Hooks](#hooks)
32
34
  - [Custom Tools](#custom-tools)
33
- - [Settings File](#settings-file)
34
35
  - [CLI Reference](#cli-reference)
35
36
  - [Tools](#tools)
36
37
  - [Programmatic Usage](#programmatic-usage)
@@ -190,9 +191,11 @@ The agent reads, writes, and edits files, and executes commands via bash.
190
191
  | `/settings` | Open settings menu (thinking, theme, queue mode, toggles) |
191
192
  | `/model` | Switch models mid-session (fuzzy search, arrow keys, Enter to select) |
192
193
  | `/export [file]` | Export session to self-contained HTML |
194
+ | `/share` | Upload session as secret GitHub gist, get shareable URL (requires `gh` CLI) |
193
195
  | `/session` | Show session info: path, message counts, token usage, cost |
194
196
  | `/hotkeys` | Show all keyboard shortcuts |
195
197
  | `/changelog` | Display full version history |
198
+ | `/tree` | Navigate session tree in-place (search, filter, label entries) |
196
199
  | `/branch` | Create new conversation branch from a previous message |
197
200
  | `/resume` | Switch to a different session (interactive selector) |
198
201
  | `/login` | OAuth login for subscription-based models |
@@ -291,6 +294,10 @@ Toggle inline images via `/settings` or set `terminal.showImages: false` in sett
291
294
 
292
295
  ## Sessions
293
296
 
297
+ Sessions are stored as JSONL files with a **tree structure**. Each entry has an `id` and `parentId`, enabling in-place branching: navigate to any previous point with `/tree`, continue from there, and switch between branches while preserving all history in a single file.
298
+
299
+ See [docs/session.md](docs/session.md) for the file format and programmatic API.
300
+
294
301
  ### Session Management
295
302
 
296
303
  Sessions auto-save to `~/.pi/agent/sessions/` organized by working directory.
@@ -319,14 +326,6 @@ Long sessions can exhaust context windows. Compaction summarizes older messages
319
326
 
320
327
  When disabled, neither case triggers automatic compaction (use `/compact` manually if needed).
321
328
 
322
- **How it works:**
323
- 1. Cut point calculated to keep ~20k tokens of recent messages
324
- 2. Messages before cut point are summarized
325
- 3. Summary replaces old messages as "context handoff"
326
- 4. Previous compaction summaries chain into new ones
327
-
328
- Compaction does not create a new session, but continues the existing one, with a marker in the `.jsonl` file that encodes the compaction point.
329
-
330
329
  **Configuration** (`~/.pi/agent/settings.json`):
331
330
 
332
331
  ```json
@@ -339,11 +338,20 @@ Compaction does not create a new session, but continues the existing one, with a
339
338
  }
340
339
  ```
341
340
 
342
- > **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 `/branch` to revisit any previous point.
341
+ > **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.
342
+
343
+ See [docs/compaction.md](docs/compaction.md) for how compaction works internally and how to customize it via hooks.
343
344
 
344
345
  ### Branching
345
346
 
346
- Use `/branch` to explore alternative conversation paths:
347
+ **In-place navigation (`/tree`):** Navigate the session tree without creating new files. Select any previous point, continue from there, and switch between branches while preserving all history.
348
+
349
+ - Search by typing, page with ←/→
350
+ - Filter modes (Ctrl+O): default → no-tools → user-only → labeled-only → all
351
+ - Press `l` to label entries as bookmarks
352
+ - When switching branches, you're prompted whether to generate a summary of the abandoned branch (messages up to the common ancestor)
353
+
354
+ **Create new session (`/branch`):** Branch to a new session file:
347
355
 
348
356
  1. Opens selector showing all your user messages
349
357
  2. Select a message to branch from
@@ -473,6 +481,75 @@ Add custom models (Ollama, vLLM, LM Studio, etc.) via `~/.pi/agent/models.json`:
473
481
 
474
482
  > pi can help you create custom provider and model configurations.
475
483
 
484
+ ### Settings File
485
+
486
+ Settings are loaded from two locations and merged:
487
+
488
+ 1. **Global:** `~/.pi/agent/settings.json` - user preferences
489
+ 2. **Project:** `<cwd>/.pi/settings.json` - project-specific overrides (version control friendly)
490
+
491
+ Project settings override global settings. For nested objects, individual keys merge. Settings changed via TUI (model, thinking level, etc.) are saved to global preferences only.
492
+
493
+ Global `~/.pi/agent/settings.json` stores persistent preferences:
494
+
495
+ ```json
496
+ {
497
+ "theme": "dark",
498
+ "defaultProvider": "anthropic",
499
+ "defaultModel": "claude-sonnet-4-20250514",
500
+ "defaultThinkingLevel": "medium",
501
+ "enabledModels": ["anthropic/*", "*gpt*", "gemini-2.5-pro:high"],
502
+ "queueMode": "one-at-a-time",
503
+ "shellPath": "C:\\path\\to\\bash.exe",
504
+ "hideThinkingBlock": false,
505
+ "collapseChangelog": false,
506
+ "compaction": {
507
+ "enabled": true,
508
+ "reserveTokens": 16384,
509
+ "keepRecentTokens": 20000
510
+ },
511
+ "skills": {
512
+ "enabled": true
513
+ },
514
+ "retry": {
515
+ "enabled": true,
516
+ "maxRetries": 3,
517
+ "baseDelayMs": 2000
518
+ },
519
+ "terminal": {
520
+ "showImages": true
521
+ },
522
+ "hooks": ["/path/to/hook.ts"],
523
+ "customTools": ["/path/to/tool.ts"]
524
+ }
525
+ ```
526
+
527
+ | Setting | Description | Default |
528
+ |---------|-------------|---------|
529
+ | `theme` | Color theme name | auto-detected |
530
+ | `defaultProvider` | Default model provider | - |
531
+ | `defaultModel` | Default model ID | - |
532
+ | `defaultThinkingLevel` | Thinking level: `off`, `minimal`, `low`, `medium`, `high`, `xhigh` | - |
533
+ | `enabledModels` | Model patterns for cycling. Supports glob patterns (`github-copilot/*`, `*sonnet*`) and fuzzy matching. Same as `--models` CLI flag | - |
534
+ | `queueMode` | Message queue mode: `all` or `one-at-a-time` | `one-at-a-time` |
535
+ | `shellPath` | Custom bash path (Windows) | auto-detected |
536
+ | `hideThinkingBlock` | Hide thinking blocks in output (Ctrl+T to toggle) | `false` |
537
+ | `collapseChangelog` | Show condensed changelog after update | `false` |
538
+ | `compaction.enabled` | Enable auto-compaction | `true` |
539
+ | `compaction.reserveTokens` | Tokens to reserve before compaction triggers | `16384` |
540
+ | `compaction.keepRecentTokens` | Recent tokens to keep after compaction | `20000` |
541
+ | `skills.enabled` | Enable skills discovery | `true` |
542
+ | `retry.enabled` | Auto-retry on transient errors | `true` |
543
+ | `retry.maxRetries` | Maximum retry attempts | `3` |
544
+ | `retry.baseDelayMs` | Base delay for exponential backoff | `2000` |
545
+ | `terminal.showImages` | Render images inline (supported terminals) | `true` |
546
+ | `hooks` | Additional hook file paths | `[]` |
547
+ | `customTools` | Additional custom tool file paths | `[]` |
548
+
549
+ ---
550
+
551
+ ## Extensions
552
+
476
553
  ### Themes
477
554
 
478
555
  Built-in themes: `dark` (default), `light`. Auto-detected on first run.
@@ -612,18 +689,23 @@ export default function (pi: HookAPI) {
612
689
 
613
690
  **Sending messages from hooks:**
614
691
 
615
- Use `pi.send(text, attachments?)` to inject messages into the session. If the agent is streaming, the message is queued; otherwise a new agent loop starts immediately.
692
+ Use `pi.sendMessage(message, triggerTurn?)` to inject messages into the session. Messages are persisted as `CustomMessageEntry` and sent to the LLM. If the agent is streaming, the message is queued; otherwise a new agent loop starts if `triggerTurn` is true.
616
693
 
617
694
  ```typescript
618
695
  import * as fs from "node:fs";
619
696
  import type { HookAPI } from "@mariozechner/pi-coding-agent/hooks";
620
697
 
621
698
  export default function (pi: HookAPI) {
622
- pi.on("session", async (event) => {
623
- if (event.reason !== "start") return;
699
+ pi.on("session_start", async () => {
624
700
  fs.watch("/tmp/trigger.txt", () => {
625
701
  const content = fs.readFileSync("/tmp/trigger.txt", "utf-8").trim();
626
- if (content) pi.send(content);
702
+ if (content) {
703
+ pi.sendMessage({
704
+ customType: "file-trigger",
705
+ content,
706
+ display: true,
707
+ }, true); // triggerTurn: start agent loop
708
+ }
627
709
  });
628
710
  });
629
711
  }
@@ -659,10 +741,11 @@ const factory: CustomToolFactory = (pi) => ({
659
741
  name: Type.String({ description: "Name to greet" }),
660
742
  }),
661
743
 
662
- async execute(toolCallId, params) {
744
+ async execute(toolCallId, params, onUpdate, ctx, signal) {
745
+ const { name } = params as { name: string };
663
746
  return {
664
- content: [{ type: "text", text: `Hello, ${params.name}!` }],
665
- details: { greeted: params.name },
747
+ content: [{ type: "text", text: `Hello, ${name}!` }],
748
+ details: { greeted: name },
666
749
  };
667
750
  },
668
751
  });
@@ -682,71 +765,6 @@ export default factory;
682
765
 
683
766
  > 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.
684
767
 
685
- ### Settings File
686
-
687
- Settings are loaded from two locations and merged:
688
-
689
- 1. **Global:** `~/.pi/agent/settings.json` - user preferences
690
- 2. **Project:** `<cwd>/.pi/settings.json` - project-specific overrides (version control friendly)
691
-
692
- Project settings override global settings. For nested objects, individual keys merge. Settings changed via TUI (model, thinking level, etc.) are saved to global preferences only.
693
-
694
- Global `~/.pi/agent/settings.json` stores persistent preferences:
695
-
696
- ```json
697
- {
698
- "theme": "dark",
699
- "defaultProvider": "anthropic",
700
- "defaultModel": "claude-sonnet-4-20250514",
701
- "defaultThinkingLevel": "medium",
702
- "queueMode": "one-at-a-time",
703
- "shellPath": "C:\\path\\to\\bash.exe",
704
- "hideThinkingBlock": false,
705
- "collapseChangelog": false,
706
- "compaction": {
707
- "enabled": true,
708
- "reserveTokens": 16384,
709
- "keepRecentTokens": 20000
710
- },
711
- "skills": {
712
- "enabled": true
713
- },
714
- "retry": {
715
- "enabled": true,
716
- "maxRetries": 3,
717
- "baseDelayMs": 2000
718
- },
719
- "terminal": {
720
- "showImages": true
721
- },
722
- "hooks": ["/path/to/hook.ts"],
723
- "hookTimeout": 30000,
724
- "customTools": ["/path/to/tool.ts"]
725
- }
726
- ```
727
-
728
- | Setting | Description | Default |
729
- |---------|-------------|---------|
730
- | `theme` | Color theme name | auto-detected |
731
- | `defaultProvider` | Default model provider | - |
732
- | `defaultModel` | Default model ID | - |
733
- | `defaultThinkingLevel` | Thinking level: `off`, `minimal`, `low`, `medium`, `high`, `xhigh` | - |
734
- | `queueMode` | Message queue mode: `all` or `one-at-a-time` | `one-at-a-time` |
735
- | `shellPath` | Custom bash path (Windows) | auto-detected |
736
- | `hideThinkingBlock` | Hide thinking blocks in output (Ctrl+T to toggle) | `false` |
737
- | `collapseChangelog` | Show condensed changelog after update | `false` |
738
- | `compaction.enabled` | Enable auto-compaction | `true` |
739
- | `compaction.reserveTokens` | Tokens to reserve before compaction triggers | `16384` |
740
- | `compaction.keepRecentTokens` | Recent tokens to keep after compaction | `20000` |
741
- | `skills.enabled` | Enable skills discovery | `true` |
742
- | `retry.enabled` | Auto-retry on transient errors | `true` |
743
- | `retry.maxRetries` | Maximum retry attempts | `3` |
744
- | `retry.baseDelayMs` | Base delay for exponential backoff | `2000` |
745
- | `terminal.showImages` | Render images inline (supported terminals) | `true` |
746
- | `hooks` | Additional hook file paths | `[]` |
747
- | `hookTimeout` | Timeout for hook operations (ms) | `30000` |
748
- | `customTools` | Additional custom tool file paths | `[]` |
749
-
750
768
  ---
751
769
 
752
770
  ## CLI Reference
@@ -771,7 +789,7 @@ pi [options] [@files...] [messages...]
771
789
  | `--session-dir <dir>` | Directory for session storage and lookup |
772
790
  | `--continue`, `-c` | Continue most recent session |
773
791
  | `--resume`, `-r` | Select session to resume |
774
- | `--models <patterns>` | Comma-separated patterns for Ctrl+P cycling (e.g., `sonnet:high,haiku:low`) |
792
+ | `--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`) |
775
793
  | `--tools <tools>` | Comma-separated tool list (default: `read,bash,edit,write`) |
776
794
  | `--thinking <level>` | Thinking level: `off`, `minimal`, `low`, `medium`, `high` |
777
795
  | `--hook <path>` | Load a hook file (can be used multiple times) |
@@ -823,6 +841,9 @@ pi --provider openai --model gpt-4o "Help me refactor"
823
841
  # Model cycling with thinking levels
824
842
  pi --models sonnet:high,haiku:low
825
843
 
844
+ # Limit to specific provider with glob pattern
845
+ pi --models "github-copilot/*"
846
+
826
847
  # Read-only mode
827
848
  pi --tools read,grep,find,ls -p "Review the architecture"
828
849
 
@@ -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;CACnB;AAID,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,aAAa,CAE1E;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA8F9C;AAED,wBAAgB,SAAS,IAAI,IAAI,CA2FhC","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}\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[]): Args {\n\tconst result: Args = {\n\t\tmessages: [],\n\t\tfileArgs: [],\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(\"-\")) {\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 quick cycling with Ctrl+P\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\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 # 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,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;CACnB;AAID,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,aAAa,CAE1E;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA8F9C;AAED,wBAAgB,SAAS,IAAI,IAAI,CA+FhC","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}\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[]): Args {\n\tconst result: Args = {\n\t\tmessages: [],\n\t\tfileArgs: [],\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(\"-\")) {\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\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"]}
package/dist/cli/args.js CHANGED
@@ -140,7 +140,8 @@ ${chalk.bold("Options:")}
140
140
  --session <path> Use specific session file
141
141
  --session-dir <dir> Directory for session storage and lookup
142
142
  --no-session Don't save session (ephemeral)
143
- --models <patterns> Comma-separated model patterns for quick cycling with Ctrl+P
143
+ --models <patterns> Comma-separated model patterns for Ctrl+P cycling
144
+ Supports globs (anthropic/*, *sonnet*) and fuzzy matching
144
145
  --tools <tools> Comma-separated list of tools to enable (default: read,bash,edit,write)
145
146
  Available: read, bash, edit, write, grep, find, ls
146
147
  --thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh
@@ -178,6 +179,9 @@ ${chalk.bold("Examples:")}
178
179
  # Limit model cycling to specific models
179
180
  ${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o
180
181
 
182
+ # Limit to a specific provider with glob pattern
183
+ ${APP_NAME} --models "github-copilot/*"
184
+
181
185
  # Cycle models with fixed thinking levels
182
186
  ${APP_NAME} --models sonnet:high,haiku:low
183
187