@mariozechner/pi-coding-agent 0.59.0 → 0.61.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 (200) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +15 -2
  3. package/dist/bun/cli.d.ts +3 -0
  4. package/dist/bun/cli.d.ts.map +1 -0
  5. package/dist/bun/cli.js +7 -0
  6. package/dist/bun/cli.js.map +1 -0
  7. package/dist/bun/register-bedrock.d.ts +2 -0
  8. package/dist/bun/register-bedrock.d.ts.map +1 -0
  9. package/dist/bun/register-bedrock.js +4 -0
  10. package/dist/bun/register-bedrock.js.map +1 -0
  11. package/dist/cli/args.d.ts +1 -0
  12. package/dist/cli/args.d.ts.map +1 -1
  13. package/dist/cli/args.js +4 -0
  14. package/dist/cli/args.js.map +1 -1
  15. package/dist/cli/initial-message.d.ts +18 -0
  16. package/dist/cli/initial-message.d.ts.map +1 -0
  17. package/dist/cli/initial-message.js +22 -0
  18. package/dist/cli/initial-message.js.map +1 -0
  19. package/dist/cli/session-picker.d.ts.map +1 -1
  20. package/dist/cli/session-picker.js +2 -1
  21. package/dist/cli/session-picker.js.map +1 -1
  22. package/dist/cli.d.ts.map +1 -1
  23. package/dist/cli.js +1 -3
  24. package/dist/cli.js.map +1 -1
  25. package/dist/core/agent-session.d.ts +18 -2
  26. package/dist/core/agent-session.d.ts.map +1 -1
  27. package/dist/core/agent-session.js +83 -8
  28. package/dist/core/agent-session.js.map +1 -1
  29. package/dist/core/bash-executor.d.ts +6 -7
  30. package/dist/core/bash-executor.d.ts.map +1 -1
  31. package/dist/core/bash-executor.js +8 -107
  32. package/dist/core/bash-executor.js.map +1 -1
  33. package/dist/core/exec.d.ts.map +1 -1
  34. package/dist/core/exec.js +7 -3
  35. package/dist/core/exec.js.map +1 -1
  36. package/dist/core/export-html/template.css +43 -13
  37. package/dist/core/export-html/template.html +1 -0
  38. package/dist/core/export-html/template.js +107 -0
  39. package/dist/core/extensions/index.d.ts +1 -1
  40. package/dist/core/extensions/index.d.ts.map +1 -1
  41. package/dist/core/extensions/index.js.map +1 -1
  42. package/dist/core/extensions/loader.d.ts.map +1 -1
  43. package/dist/core/extensions/loader.js +4 -4
  44. package/dist/core/extensions/loader.js.map +1 -1
  45. package/dist/core/extensions/runner.d.ts +6 -3
  46. package/dist/core/extensions/runner.d.ts.map +1 -1
  47. package/dist/core/extensions/runner.js +62 -33
  48. package/dist/core/extensions/runner.js.map +1 -1
  49. package/dist/core/extensions/types.d.ts +4 -3
  50. package/dist/core/extensions/types.d.ts.map +1 -1
  51. package/dist/core/extensions/types.js.map +1 -1
  52. package/dist/core/footer-data-provider.d.ts +9 -2
  53. package/dist/core/footer-data-provider.d.ts.map +1 -1
  54. package/dist/core/footer-data-provider.js +85 -13
  55. package/dist/core/footer-data-provider.js.map +1 -1
  56. package/dist/core/keybindings.d.ts +270 -50
  57. package/dist/core/keybindings.d.ts.map +1 -1
  58. package/dist/core/keybindings.js +222 -134
  59. package/dist/core/keybindings.js.map +1 -1
  60. package/dist/core/model-registry.d.ts +1 -0
  61. package/dist/core/model-registry.d.ts.map +1 -1
  62. package/dist/core/model-registry.js +23 -14
  63. package/dist/core/model-registry.js.map +1 -1
  64. package/dist/core/package-manager.d.ts +15 -1
  65. package/dist/core/package-manager.d.ts.map +1 -1
  66. package/dist/core/package-manager.js +194 -15
  67. package/dist/core/package-manager.js.map +1 -1
  68. package/dist/core/sdk.d.ts +2 -2
  69. package/dist/core/sdk.d.ts.map +1 -1
  70. package/dist/core/sdk.js +2 -2
  71. package/dist/core/sdk.js.map +1 -1
  72. package/dist/core/slash-commands.d.ts.map +1 -1
  73. package/dist/core/slash-commands.js +3 -2
  74. package/dist/core/slash-commands.js.map +1 -1
  75. package/dist/core/tools/bash.d.ts +8 -0
  76. package/dist/core/tools/bash.d.ts.map +1 -1
  77. package/dist/core/tools/bash.js +77 -69
  78. package/dist/core/tools/bash.js.map +1 -1
  79. package/dist/core/tools/edit.d.ts.map +1 -1
  80. package/dist/core/tools/edit.js +3 -2
  81. package/dist/core/tools/edit.js.map +1 -1
  82. package/dist/core/tools/file-mutation-queue.d.ts +6 -0
  83. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
  84. package/dist/core/tools/file-mutation-queue.js +37 -0
  85. package/dist/core/tools/file-mutation-queue.js.map +1 -0
  86. package/dist/core/tools/index.d.ts +2 -1
  87. package/dist/core/tools/index.d.ts.map +1 -1
  88. package/dist/core/tools/index.js +2 -1
  89. package/dist/core/tools/index.js.map +1 -1
  90. package/dist/core/tools/write.d.ts.map +1 -1
  91. package/dist/core/tools/write.js +6 -3
  92. package/dist/core/tools/write.js.map +1 -1
  93. package/dist/index.d.ts +3 -3
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +2 -2
  96. package/dist/index.js.map +1 -1
  97. package/dist/main.d.ts.map +1 -1
  98. package/dist/main.js +60 -24
  99. package/dist/main.js.map +1 -1
  100. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  101. package/dist/modes/interactive/components/bash-execution.js +4 -4
  102. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  103. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  104. package/dist/modes/interactive/components/bordered-loader.js +1 -1
  105. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  106. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  107. package/dist/modes/interactive/components/branch-summary-message.js +2 -2
  108. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  109. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  110. package/dist/modes/interactive/components/compaction-summary-message.js +2 -2
  111. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  112. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  113. package/dist/modes/interactive/components/config-selector.js +8 -8
  114. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  115. package/dist/modes/interactive/components/custom-editor.d.ts +3 -3
  116. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  117. package/dist/modes/interactive/components/custom-editor.js +6 -6
  118. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  119. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  120. package/dist/modes/interactive/components/extension-editor.js +9 -9
  121. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  122. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  123. package/dist/modes/interactive/components/extension-input.js +5 -5
  124. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  125. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  126. package/dist/modes/interactive/components/extension-selector.js +8 -8
  127. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  128. package/dist/modes/interactive/components/index.d.ts +1 -1
  129. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  130. package/dist/modes/interactive/components/index.js +1 -1
  131. package/dist/modes/interactive/components/index.js.map +1 -1
  132. package/dist/modes/interactive/components/keybinding-hints.d.ts +3 -36
  133. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  134. package/dist/modes/interactive/components/keybinding-hints.js +5 -44
  135. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  136. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  137. package/dist/modes/interactive/components/login-dialog.js +6 -6
  138. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  139. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  140. package/dist/modes/interactive/components/model-selector.js +12 -8
  141. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  142. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  143. package/dist/modes/interactive/components/oauth-selector.js +6 -6
  144. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  145. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  146. package/dist/modes/interactive/components/scoped-models-selector.js +4 -4
  147. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  148. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  149. package/dist/modes/interactive/components/session-selector.js +32 -35
  150. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  151. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  152. package/dist/modes/interactive/components/skill-invocation-message.js +2 -2
  153. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  154. package/dist/modes/interactive/components/tool-execution.d.ts +7 -0
  155. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  156. package/dist/modes/interactive/components/tool-execution.js +51 -6
  157. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  158. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  159. package/dist/modes/interactive/components/tree-selector.js +15 -15
  160. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  161. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  162. package/dist/modes/interactive/components/user-message-selector.js +6 -6
  163. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  164. package/dist/modes/interactive/interactive-mode.d.ts +3 -0
  165. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  166. package/dist/modes/interactive/interactive-mode.js +184 -87
  167. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  168. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  169. package/dist/modes/interactive/theme/theme.js +49 -37
  170. package/dist/modes/interactive/theme/theme.js.map +1 -1
  171. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  172. package/dist/modes/rpc/rpc-mode.js +4 -1
  173. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  174. package/dist/utils/child-process.d.ts +11 -0
  175. package/dist/utils/child-process.d.ts.map +1 -0
  176. package/dist/utils/child-process.js +78 -0
  177. package/dist/utils/child-process.js.map +1 -0
  178. package/dist/utils/clipboard-native.d.ts +1 -0
  179. package/dist/utils/clipboard-native.d.ts.map +1 -1
  180. package/dist/utils/clipboard-native.js.map +1 -1
  181. package/dist/utils/clipboard.d.ts +1 -1
  182. package/dist/utils/clipboard.d.ts.map +1 -1
  183. package/dist/utils/clipboard.js +11 -1
  184. package/dist/utils/clipboard.js.map +1 -1
  185. package/docs/extensions.md +59 -8
  186. package/docs/keybindings.md +103 -112
  187. package/docs/providers.md +7 -0
  188. package/docs/rpc.md +4 -4
  189. package/docs/sdk.md +2 -2
  190. package/examples/extensions/antigravity-image-gen.ts +5 -3
  191. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  192. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  193. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  194. package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
  195. package/examples/extensions/subagent/index.ts +7 -5
  196. package/examples/extensions/tool-override.ts +9 -7
  197. package/examples/extensions/truncated-tool.ts +6 -3
  198. package/examples/extensions/with-deps/package-lock.json +2 -2
  199. package/examples/extensions/with-deps/package.json +1 -1
  200. package/package.json +5 -5
@@ -2,6 +2,12 @@
2
2
 
3
3
  All keyboard shortcuts can be customized via `~/.pi/agent/keybindings.json`. Each action can be bound to one or more keys.
4
4
 
5
+ The config file uses the same namespaced keybinding ids that pi uses internally and that extension authors use in `keyHint()` and injected `keybindings` managers.
6
+
7
+ Older configs using pre-namespaced ids such as `cursorUp` or `expandTools` are migrated automatically to the namespaced ids on startup.
8
+
9
+ After editing `keybindings.json`, run `/reload` in pi to apply the changes without restarting the session.
10
+
5
11
  ## Key Format
6
12
 
7
13
  `modifier+key` where modifiers are `ctrl`, `shift`, `alt` (combinable) and keys are:
@@ -16,127 +22,112 @@ Modifier combinations: `ctrl+shift+x`, `alt+ctrl+x`, `ctrl+shift+alt+x`, `ctrl+1
16
22
 
17
23
  ## All Actions
18
24
 
19
- ### Cursor Movement
25
+ ### TUI Editor Cursor Movement
20
26
 
21
- | Action | Default | Description |
27
+ | Keybinding id | Default | Description |
22
28
  |--------|---------|-------------|
23
- | `cursorUp` | `up` | Move cursor up |
24
- | `cursorDown` | `down` | Move cursor down |
25
- | `cursorLeft` | `left`, `ctrl+b` | Move cursor left |
26
- | `cursorRight` | `right`, `ctrl+f` | Move cursor right |
27
- | `cursorWordLeft` | `alt+left`, `ctrl+left`, `alt+b` | Move cursor word left |
28
- | `cursorWordRight` | `alt+right`, `ctrl+right`, `alt+f` | Move cursor word right |
29
- | `cursorLineStart` | `home`, `ctrl+a` | Move to line start |
30
- | `cursorLineEnd` | `end`, `ctrl+e` | Move to line end |
31
- | `jumpForward` | `ctrl+]` | Jump forward to character |
32
- | `jumpBackward` | `ctrl+alt+]` | Jump backward to character |
33
- | `pageUp` | `pageUp` | Scroll up by page |
34
- | `pageDown` | `pageDown` | Scroll down by page |
35
-
36
- ### Deletion
37
-
38
- | Action | Default | Description |
29
+ | `tui.editor.cursorUp` | `up` | Move cursor up |
30
+ | `tui.editor.cursorDown` | `down` | Move cursor down |
31
+ | `tui.editor.cursorLeft` | `left`, `ctrl+b` | Move cursor left |
32
+ | `tui.editor.cursorRight` | `right`, `ctrl+f` | Move cursor right |
33
+ | `tui.editor.cursorWordLeft` | `alt+left`, `ctrl+left`, `alt+b` | Move cursor word left |
34
+ | `tui.editor.cursorWordRight` | `alt+right`, `ctrl+right`, `alt+f` | Move cursor word right |
35
+ | `tui.editor.cursorLineStart` | `home`, `ctrl+a` | Move to line start |
36
+ | `tui.editor.cursorLineEnd` | `end`, `ctrl+e` | Move to line end |
37
+ | `tui.editor.jumpForward` | `ctrl+]` | Jump forward to character |
38
+ | `tui.editor.jumpBackward` | `ctrl+alt+]` | Jump backward to character |
39
+ | `tui.editor.pageUp` | `pageUp` | Scroll up by page |
40
+ | `tui.editor.pageDown` | `pageDown` | Scroll down by page |
41
+
42
+ ### TUI Editor Deletion
43
+
44
+ | Keybinding id | Default | Description |
39
45
  |--------|---------|-------------|
40
- | `deleteCharBackward` | `backspace` | Delete character backward |
41
- | `deleteCharForward` | `delete`, `ctrl+d` | Delete character forward |
42
- | `deleteWordBackward` | `ctrl+w`, `alt+backspace` | Delete word backward |
43
- | `deleteWordForward` | `alt+d`, `alt+delete` | Delete word forward |
44
- | `deleteToLineStart` | `ctrl+u` | Delete to line start |
45
- | `deleteToLineEnd` | `ctrl+k` | Delete to line end |
46
+ | `tui.editor.deleteCharBackward` | `backspace` | Delete character backward |
47
+ | `tui.editor.deleteCharForward` | `delete`, `ctrl+d` | Delete character forward |
48
+ | `tui.editor.deleteWordBackward` | `ctrl+w`, `alt+backspace` | Delete word backward |
49
+ | `tui.editor.deleteWordForward` | `alt+d`, `alt+delete` | Delete word forward |
50
+ | `tui.editor.deleteToLineStart` | `ctrl+u` | Delete to line start |
51
+ | `tui.editor.deleteToLineEnd` | `ctrl+k` | Delete to line end |
46
52
 
47
- ### Text Input
53
+ ### TUI Input
48
54
 
49
- | Action | Default | Description |
55
+ | Keybinding id | Default | Description |
50
56
  |--------|---------|-------------|
51
- | `newLine` | `shift+enter` | Insert new line |
52
- | `submit` | `enter` | Submit input |
53
- | `tab` | `tab` | Tab / autocomplete |
57
+ | `tui.input.newLine` | `shift+enter` | Insert new line |
58
+ | `tui.input.submit` | `enter` | Submit input |
59
+ | `tui.input.tab` | `tab` | Tab / autocomplete |
54
60
 
55
- ### Kill Ring
61
+ ### TUI Kill Ring
56
62
 
57
- | Action | Default | Description |
63
+ | Keybinding id | Default | Description |
58
64
  |--------|---------|-------------|
59
- | `yank` | `ctrl+y` | Paste most recently deleted text |
60
- | `yankPop` | `alt+y` | Cycle through deleted text after yank |
61
- | `undo` | `ctrl+-` | Undo last edit |
65
+ | `tui.editor.yank` | `ctrl+y` | Paste most recently deleted text |
66
+ | `tui.editor.yankPop` | `alt+y` | Cycle through deleted text after yank |
67
+ | `tui.editor.undo` | `ctrl+-` | Undo last edit |
62
68
 
63
- ### Clipboard
69
+ ### TUI Clipboard and Selection
64
70
 
65
- | Action | Default | Description |
71
+ | Keybinding id | Default | Description |
66
72
  |--------|---------|-------------|
67
- | `copy` | `ctrl+c` | Copy selection |
68
- | `pasteImage` | `ctrl+v` | Paste image from clipboard |
73
+ | `tui.input.copy` | `ctrl+c` | Copy selection |
74
+ | `tui.select.up` | `up` | Move selection up |
75
+ | `tui.select.down` | `down` | Move selection down |
76
+ | `tui.select.pageUp` | `pageUp` | Page up in list |
77
+ | `tui.select.pageDown` | `pageDown` | Page down in list |
78
+ | `tui.select.confirm` | `enter` | Confirm selection |
79
+ | `tui.select.cancel` | `escape`, `ctrl+c` | Cancel selection |
69
80
 
70
81
  ### Application
71
82
 
72
- | Action | Default | Description |
73
- |--------|---------|-------------|
74
- | `interrupt` | `escape` | Cancel / abort |
75
- | `clear` | `ctrl+c` | Clear editor |
76
- | `exit` | `ctrl+d` | Exit (when editor empty) |
77
- | `suspend` | `ctrl+z` | Suspend to background |
78
- | `externalEditor` | `ctrl+g` | Open in external editor (`$VISUAL` or `$EDITOR`) |
79
-
80
- ### Session
81
-
82
- | Action | Default | Description |
83
- |--------|---------|-------------|
84
- | `newSession` | *(none)* | Start a new session (`/new`) |
85
- | `tree` | *(none)* | Open session tree navigator (`/tree`) |
86
- | `fork` | *(none)* | Fork current session (`/fork`) |
87
- | `resume` | *(none)* | Open session resume picker (`/resume`) |
88
-
89
- ### Models & Thinking
90
-
91
- | Action | Default | Description |
83
+ | Keybinding id | Default | Description |
92
84
  |--------|---------|-------------|
93
- | `selectModel` | `ctrl+l` | Open model selector |
94
- | `cycleModelForward` | `ctrl+p` | Cycle to next model |
95
- | `cycleModelBackward` | `shift+ctrl+p` | Cycle to previous model |
96
- | `cycleThinkingLevel` | `shift+tab` | Cycle thinking level |
85
+ | `app.interrupt` | `escape` | Cancel / abort |
86
+ | `app.clear` | `ctrl+c` | Clear editor |
87
+ | `app.exit` | `ctrl+d` | Exit (when editor empty) |
88
+ | `app.suspend` | `ctrl+z` | Suspend to background |
89
+ | `app.editor.external` | `ctrl+g` | Open in external editor (`$VISUAL` or `$EDITOR`) |
90
+ | `app.clipboard.pasteImage` | `ctrl+v` (`alt+v` on Windows) | Paste image from clipboard |
97
91
 
98
- ### Display
92
+ ### Sessions
99
93
 
100
- | Action | Default | Description |
94
+ | Keybinding id | Default | Description |
101
95
  |--------|---------|-------------|
102
- | `expandTools` | `ctrl+o` | Collapse/expand tool output |
103
- | `toggleThinking` | `ctrl+t` | Collapse/expand thinking blocks |
104
-
105
- ### Message Queue
106
-
107
- | Action | Default | Description |
96
+ | `app.session.new` | *(none)* | Start a new session (`/new`) |
97
+ | `app.session.tree` | *(none)* | Open session tree navigator (`/tree`) |
98
+ | `app.session.fork` | *(none)* | Fork current session (`/fork`) |
99
+ | `app.session.resume` | *(none)* | Open session resume picker (`/resume`) |
100
+ | `app.session.togglePath` | `ctrl+p` | Toggle path display |
101
+ | `app.session.toggleSort` | `ctrl+s` | Toggle sort mode |
102
+ | `app.session.toggleNamedFilter` | `ctrl+n` | Toggle named-only filter |
103
+ | `app.session.rename` | `ctrl+r` | Rename session |
104
+ | `app.session.delete` | `ctrl+d` | Delete session |
105
+ | `app.session.deleteNoninvasive` | `ctrl+backspace` | Delete session when query is empty |
106
+
107
+ ### Models and Thinking
108
+
109
+ | Keybinding id | Default | Description |
108
110
  |--------|---------|-------------|
109
- | `followUp` | `alt+enter` | Queue follow-up message |
110
- | `dequeue` | `alt+up` | Restore queued messages to editor |
111
+ | `app.model.select` | `ctrl+l` | Open model selector |
112
+ | `app.model.cycleForward` | `ctrl+p` | Cycle to next model |
113
+ | `app.model.cycleBackward` | `shift+ctrl+p` | Cycle to previous model |
114
+ | `app.thinking.cycle` | `shift+tab` | Cycle thinking level |
115
+ | `app.thinking.toggle` | `ctrl+t` | Collapse or expand thinking blocks |
111
116
 
112
- ### Selection (Lists, Pickers)
117
+ ### Display and Message Queue
113
118
 
114
- | Action | Default | Description |
119
+ | Keybinding id | Default | Description |
115
120
  |--------|---------|-------------|
116
- | `selectUp` | `up` | Move selection up |
117
- | `selectDown` | `down` | Move selection down |
118
- | `selectPageUp` | `pageUp` | Page up in list |
119
- | `selectPageDown` | `pageDown` | Page down in list |
120
- | `selectConfirm` | `enter` | Confirm selection |
121
- | `selectCancel` | `escape`, `ctrl+c` | Cancel selection |
121
+ | `app.tools.expand` | `ctrl+o` | Collapse or expand tool output |
122
+ | `app.message.followUp` | `alt+enter` | Queue follow-up message |
123
+ | `app.message.dequeue` | `alt+up` | Restore queued messages to editor |
122
124
 
123
125
  ### Tree Navigation
124
126
 
125
- | Action | Default | Description |
126
- |--------|---------|-------------|
127
- | `treeFoldOrUp` | `ctrl+left`, `alt+left` | Fold current branch segment, or jump to the previous segment start |
128
- | `treeUnfoldOrDown` | `ctrl+right`, `alt+right` | Unfold current branch segment, or jump to the next segment start or branch end |
129
-
130
- ### Session Picker
131
-
132
- | Action | Default | Description |
127
+ | Keybinding id | Default | Description |
133
128
  |--------|---------|-------------|
134
- | `toggleSessionPath` | `ctrl+p` | Toggle path display |
135
- | `toggleSessionSort` | `ctrl+s` | Toggle sort mode |
136
- | `toggleSessionNamedFilter` | `ctrl+n` | Toggle named-only filter |
137
- | `renameSession` | `ctrl+r` | Rename session |
138
- | `deleteSession` | `ctrl+d` | Delete session |
139
- | `deleteSessionNoninvasive` | `ctrl+backspace` | Delete session (when query empty) |
129
+ | `app.tree.foldOrUp` | `ctrl+left`, `alt+left` | Fold current branch segment, or jump to the previous segment start |
130
+ | `app.tree.unfoldOrDown` | `ctrl+right`, `alt+right` | Unfold current branch segment, or jump to the next segment start or branch end |
140
131
 
141
132
  ## Custom Configuration
142
133
 
@@ -144,9 +135,9 @@ Create `~/.pi/agent/keybindings.json`:
144
135
 
145
136
  ```json
146
137
  {
147
- "cursorUp": ["up", "ctrl+p"],
148
- "cursorDown": ["down", "ctrl+n"],
149
- "deleteWordBackward": ["ctrl+w", "alt+backspace"]
138
+ "tui.editor.cursorUp": ["up", "ctrl+p"],
139
+ "tui.editor.cursorDown": ["down", "ctrl+n"],
140
+ "tui.editor.deleteWordBackward": ["ctrl+w", "alt+backspace"]
150
141
  }
151
142
  ```
152
143
 
@@ -156,15 +147,15 @@ Each action can have a single key or an array of keys. User config overrides def
156
147
 
157
148
  ```json
158
149
  {
159
- "cursorUp": ["up", "ctrl+p"],
160
- "cursorDown": ["down", "ctrl+n"],
161
- "cursorLeft": ["left", "ctrl+b"],
162
- "cursorRight": ["right", "ctrl+f"],
163
- "cursorWordLeft": ["alt+left", "alt+b"],
164
- "cursorWordRight": ["alt+right", "alt+f"],
165
- "deleteCharForward": ["delete", "ctrl+d"],
166
- "deleteCharBackward": ["backspace", "ctrl+h"],
167
- "newLine": ["shift+enter", "ctrl+j"]
150
+ "tui.editor.cursorUp": ["up", "ctrl+p"],
151
+ "tui.editor.cursorDown": ["down", "ctrl+n"],
152
+ "tui.editor.cursorLeft": ["left", "ctrl+b"],
153
+ "tui.editor.cursorRight": ["right", "ctrl+f"],
154
+ "tui.editor.cursorWordLeft": ["alt+left", "alt+b"],
155
+ "tui.editor.cursorWordRight": ["alt+right", "alt+f"],
156
+ "tui.editor.deleteCharForward": ["delete", "ctrl+d"],
157
+ "tui.editor.deleteCharBackward": ["backspace", "ctrl+h"],
158
+ "tui.input.newLine": ["shift+enter", "ctrl+j"]
168
159
  }
169
160
  ```
170
161
 
@@ -172,11 +163,11 @@ Each action can have a single key or an array of keys. User config overrides def
172
163
 
173
164
  ```json
174
165
  {
175
- "cursorUp": ["up", "alt+k"],
176
- "cursorDown": ["down", "alt+j"],
177
- "cursorLeft": ["left", "alt+h"],
178
- "cursorRight": ["right", "alt+l"],
179
- "cursorWordLeft": ["alt+left", "alt+b"],
180
- "cursorWordRight": ["alt+right", "alt+w"]
166
+ "tui.editor.cursorUp": ["up", "alt+k"],
167
+ "tui.editor.cursorDown": ["down", "alt+j"],
168
+ "tui.editor.cursorLeft": ["left", "alt+h"],
169
+ "tui.editor.cursorRight": ["right", "alt+l"],
170
+ "tui.editor.cursorWordLeft": ["alt+left", "alt+b"],
171
+ "tui.editor.cursorWordRight": ["alt+right", "alt+w"]
181
172
  }
182
173
  ```
package/docs/providers.md CHANGED
@@ -147,6 +147,13 @@ Also supports ECS task roles (`AWS_CONTAINER_CREDENTIALS_*`) and IRSA (`AWS_WEB_
147
147
  pi --provider amazon-bedrock --model us.anthropic.claude-sonnet-4-20250514-v1:0
148
148
  ```
149
149
 
150
+ Prompt caching is enabled automatically for Claude models whose ID contains a recognizable model name (base models and system-defined inference profiles). For application inference profiles (whose ARNs don't contain the model name), set `AWS_BEDROCK_FORCE_CACHE=1` to enable cache points:
151
+
152
+ ```bash
153
+ export AWS_BEDROCK_FORCE_CACHE=1
154
+ pi --provider amazon-bedrock --model arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/abc123
155
+ ```
156
+
150
157
  If you are connecting to a Bedrock API proxy, the following environment variables can be used:
151
158
 
152
159
  ```bash
package/docs/rpc.md CHANGED
@@ -58,7 +58,7 @@ With images:
58
58
  {"type": "prompt", "message": "New instruction", "streamingBehavior": "steer"}
59
59
  ```
60
60
 
61
- - `"steer"`: Interrupt the agent mid-run. Message is delivered after current tool execution, remaining tools are skipped.
61
+ - `"steer"`: Queue the message while the agent is running. It is delivered after the current assistant turn finishes executing its tool calls, before the next LLM call.
62
62
  - `"followUp"`: Wait until the agent finishes. Message is delivered only when agent stops.
63
63
 
64
64
  If the agent is streaming and no `streamingBehavior` is specified, the command returns an error.
@@ -76,7 +76,7 @@ The `images` field is optional. Each image uses `ImageContent` format: `{"type":
76
76
 
77
77
  #### steer
78
78
 
79
- Queue a steering message to interrupt the agent mid-run. Delivered after current tool execution, remaining tools are skipped. Skill commands and prompt templates are expanded. Extension commands are not allowed (use `prompt` instead).
79
+ Queue a steering message while the agent is running. It is delivered after the current assistant turn finishes executing its tool calls, before the next LLM call. Skill commands and prompt templates are expanded. Extension commands are not allowed (use `prompt` instead).
80
80
 
81
81
  ```json
82
82
  {"type": "steer", "message": "Stop and do this instead"}
@@ -321,8 +321,8 @@ Control how steering messages (from `steer`) are delivered.
321
321
  ```
322
322
 
323
323
  Modes:
324
- - `"all"`: Deliver all steering messages at the next interruption point
325
- - `"one-at-a-time"`: Deliver one steering message per interruption (default)
324
+ - `"all"`: Deliver all steering messages after the current assistant turn finishes executing its tool calls
325
+ - `"one-at-a-time"`: Deliver one steering message per completed assistant turn (default)
326
326
 
327
327
  Response:
328
328
  ```json
package/docs/sdk.md CHANGED
@@ -78,7 +78,7 @@ interface AgentSession {
78
78
  prompt(text: string, options?: PromptOptions): Promise<void>;
79
79
 
80
80
  // Queue messages during streaming
81
- steer(text: string): Promise<void>; // Interrupt: delivered after current tool, skips remaining
81
+ steer(text: string): Promise<void>; // Queue for delivery after the current assistant turn finishes its tool calls
82
82
  followUp(text: string): Promise<void>; // Wait: delivered only when agent finishes
83
83
 
84
84
  // Subscribe to events (returns unsubscribe function)
@@ -150,7 +150,7 @@ await session.prompt("After you're done, also check X", { streamingBehavior: "fo
150
150
  For explicit queueing during streaming:
151
151
 
152
152
  ```typescript
153
- // Interrupt the agent (delivered after current tool, skips remaining tools)
153
+ // Queue a steering message for delivery after the current assistant turn finishes its tool calls
154
154
  await session.steer("New instruction");
155
155
 
156
156
  // Wait for agent to finish (delivered only when agent stops)
@@ -30,7 +30,7 @@ import { existsSync, readFileSync } from "node:fs";
30
30
  import { mkdir, writeFile } from "node:fs/promises";
31
31
  import { join } from "node:path";
32
32
  import { StringEnum } from "@mariozechner/pi-ai";
33
- import { type ExtensionAPI, getAgentDir } from "@mariozechner/pi-coding-agent";
33
+ import { type ExtensionAPI, getAgentDir, withFileMutationQueue } from "@mariozechner/pi-coding-agent";
34
34
  import { type Static, Type } from "@sinclair/typebox";
35
35
 
36
36
  const PROVIDER = "google-antigravity";
@@ -228,12 +228,14 @@ function imageExtension(mimeType: string): string {
228
228
  }
229
229
 
230
230
  async function saveImage(base64Data: string, mimeType: string, outputDir: string): Promise<string> {
231
- await mkdir(outputDir, { recursive: true });
232
231
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
233
232
  const ext = imageExtension(mimeType);
234
233
  const filename = `image-${timestamp}-${randomUUID().slice(0, 8)}.${ext}`;
235
234
  const filePath = join(outputDir, filename);
236
- await writeFile(filePath, Buffer.from(base64Data, "base64"));
235
+ await withFileMutationQueue(filePath, async () => {
236
+ await mkdir(outputDir, { recursive: true });
237
+ await writeFile(filePath, Buffer.from(base64Data, "base64"));
238
+ });
237
239
  return filePath;
238
240
  }
239
241
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider",
3
- "version": "1.10.0",
3
+ "version": "1.12.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-custom-provider",
9
- "version": "1.10.0",
9
+ "version": "1.12.0",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.52.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-anthropic",
3
3
  "private": true,
4
- "version": "1.10.0",
4
+ "version": "1.12.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "1.10.0",
4
+ "version": "1.12.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-qwen-cli",
3
3
  "private": true,
4
- "version": "1.9.0",
4
+ "version": "1.11.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -19,7 +19,7 @@ import * as path from "node:path";
19
19
  import type { AgentToolResult } from "@mariozechner/pi-agent-core";
20
20
  import type { Message } from "@mariozechner/pi-ai";
21
21
  import { StringEnum } from "@mariozechner/pi-ai";
22
- import { type ExtensionAPI, getMarkdownTheme } from "@mariozechner/pi-coding-agent";
22
+ import { type ExtensionAPI, getMarkdownTheme, withFileMutationQueue } from "@mariozechner/pi-coding-agent";
23
23
  import { Container, Markdown, Spacer, Text } from "@mariozechner/pi-tui";
24
24
  import { Type } from "@sinclair/typebox";
25
25
  import { type AgentConfig, type AgentScope, discoverAgents } from "./agents.js";
@@ -207,11 +207,13 @@ async function mapWithConcurrencyLimit<TIn, TOut>(
207
207
  return results;
208
208
  }
209
209
 
210
- function writePromptToTempFile(agentName: string, prompt: string): { dir: string; filePath: string } {
211
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
210
+ async function writePromptToTempFile(agentName: string, prompt: string): Promise<{ dir: string; filePath: string }> {
211
+ const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "pi-subagent-"));
212
212
  const safeName = agentName.replace(/[^\w.-]+/g, "_");
213
213
  const filePath = path.join(tmpDir, `prompt-${safeName}.md`);
214
- fs.writeFileSync(filePath, prompt, { encoding: "utf-8", mode: 0o600 });
214
+ await withFileMutationQueue(filePath, async () => {
215
+ await fs.promises.writeFile(filePath, prompt, { encoding: "utf-8", mode: 0o600 });
216
+ });
215
217
  return { dir: tmpDir, filePath };
216
218
  }
217
219
 
@@ -274,7 +276,7 @@ async function runSingleAgent(
274
276
 
275
277
  try {
276
278
  if (agent.systemPrompt.trim()) {
277
- const tmp = writePromptToTempFile(agent.name, agent.systemPrompt);
279
+ const tmp = await writePromptToTempFile(agent.name, agent.systemPrompt);
278
280
  tmpPromptDir = tmp.dir;
279
281
  tmpPromptPath = tmp.filePath;
280
282
  args.push("--append-system-prompt", tmpPromptPath);
@@ -21,10 +21,10 @@
21
21
  */
22
22
 
23
23
  import type { TextContent } from "@mariozechner/pi-ai";
24
- import { type ExtensionAPI, getAgentDir } from "@mariozechner/pi-coding-agent";
24
+ import { type ExtensionAPI, getAgentDir, withFileMutationQueue } from "@mariozechner/pi-coding-agent";
25
25
  import { Type } from "@sinclair/typebox";
26
- import { appendFileSync, constants, readFileSync } from "fs";
27
- import { access, readFile } from "fs/promises";
26
+ import { constants, readFileSync } from "fs";
27
+ import { access, appendFile, readFile } from "fs/promises";
28
28
  import { join, resolve } from "path";
29
29
 
30
30
  const LOG_FILE = join(getAgentDir(), "read-access.log");
@@ -44,14 +44,16 @@ function isBlockedPath(path: string): boolean {
44
44
  return BLOCKED_PATTERNS.some((pattern) => pattern.test(path));
45
45
  }
46
46
 
47
- function logAccess(path: string, allowed: boolean, reason?: string) {
47
+ async function logAccess(path: string, allowed: boolean, reason?: string) {
48
48
  const timestamp = new Date().toISOString();
49
49
  const status = allowed ? "ALLOWED" : "BLOCKED";
50
50
  const msg = reason ? ` (${reason})` : "";
51
51
  const line = `[${timestamp}] ${status}: ${path}${msg}\n`;
52
52
 
53
53
  try {
54
- appendFileSync(LOG_FILE, line);
54
+ await withFileMutationQueue(LOG_FILE, async () => {
55
+ await appendFile(LOG_FILE, line);
56
+ });
55
57
  } catch {
56
58
  // Ignore logging errors
57
59
  }
@@ -77,7 +79,7 @@ export default function (pi: ExtensionAPI) {
77
79
 
78
80
  // Check if path is blocked
79
81
  if (isBlockedPath(absolutePath)) {
80
- logAccess(absolutePath, false, "matches blocked pattern");
82
+ await logAccess(absolutePath, false, "matches blocked pattern");
81
83
  return {
82
84
  content: [
83
85
  {
@@ -90,7 +92,7 @@ export default function (pi: ExtensionAPI) {
90
92
  }
91
93
 
92
94
  // Log allowed access
93
- logAccess(absolutePath, true);
95
+ await logAccess(absolutePath, true);
94
96
 
95
97
  // Perform the actual read (simplified implementation)
96
98
  try {
@@ -14,6 +14,7 @@
14
14
  * built-in `grep` tool in src/core/tools/grep.ts for a more complete implementation.
15
15
  */
16
16
 
17
+ import { mkdtemp, writeFile } from "node:fs/promises";
17
18
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
18
19
  import {
19
20
  DEFAULT_MAX_BYTES,
@@ -21,11 +22,11 @@ import {
21
22
  formatSize,
22
23
  type TruncationResult,
23
24
  truncateHead,
25
+ withFileMutationQueue,
24
26
  } from "@mariozechner/pi-coding-agent";
25
27
  import { Text } from "@mariozechner/pi-tui";
26
28
  import { Type } from "@sinclair/typebox";
27
29
  import { execSync } from "child_process";
28
- import { mkdtempSync, writeFileSync } from "fs";
29
30
  import { tmpdir } from "os";
30
31
  import { join } from "path";
31
32
 
@@ -108,9 +109,11 @@ export default function (pi: ExtensionAPI) {
108
109
 
109
110
  if (truncation.truncated) {
110
111
  // Save full output to a temp file so LLM can access it if needed
111
- const tempDir = mkdtempSync(join(tmpdir(), "pi-rg-"));
112
+ const tempDir = await mkdtemp(join(tmpdir(), "pi-rg-"));
112
113
  const tempFile = join(tempDir, "output.txt");
113
- writeFileSync(tempFile, output);
114
+ await withFileMutationQueue(tempFile, async () => {
115
+ await writeFile(tempFile, output, "utf8");
116
+ });
114
117
 
115
118
  details.truncation = truncation;
116
119
  details.fullOutputPath = tempFile;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
- "version": "1.23.0",
3
+ "version": "1.25.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-with-deps",
9
- "version": "1.23.0",
9
+ "version": "1.25.0",
10
10
  "dependencies": {
11
11
  "ms": "^2.1.3"
12
12
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
3
  "private": true,
4
- "version": "1.23.0",
4
+ "version": "1.25.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mariozechner/pi-coding-agent",
3
- "version": "0.59.0",
3
+ "version": "0.61.0",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -32,7 +32,7 @@
32
32
  "clean": "shx rm -rf dist",
33
33
  "dev": "tsgo -p tsconfig.build.json --watch --preserveWatchOutput",
34
34
  "build": "tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js && npm run copy-assets",
35
- "build:binary": "npm --prefix ../tui run build && npm --prefix ../ai run build && npm --prefix ../agent run build && npm run build && bun build --compile ./dist/cli.js --outfile dist/pi && npm run copy-binary-assets",
35
+ "build:binary": "npm --prefix ../tui run build && npm --prefix ../ai run build && npm --prefix ../agent run build && npm run build && bun build --compile ./dist/bun/cli.js --outfile dist/pi && npm run copy-binary-assets",
36
36
  "copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/core/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && shx cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/",
37
37
  "copy-binary-assets": "shx cp package.json dist/ && shx cp README.md dist/ && shx cp CHANGELOG.md dist/ && shx mkdir -p dist/theme && shx cp src/modes/interactive/theme/*.json dist/theme/ && shx mkdir -p dist/export-html/vendor && shx cp src/core/export-html/template.html dist/export-html/ && shx cp src/core/export-html/vendor/*.js dist/export-html/vendor/ && shx cp -r docs dist/ && shx cp -r examples dist/ && shx cp ../../node_modules/@silvia-odwyer/photon-node/photon_rs_bg.wasm dist/",
38
38
  "test": "vitest --run",
@@ -40,9 +40,9 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@mariozechner/jiti": "^2.6.2",
43
- "@mariozechner/pi-agent-core": "^0.59.0",
44
- "@mariozechner/pi-ai": "^0.59.0",
45
- "@mariozechner/pi-tui": "^0.59.0",
43
+ "@mariozechner/pi-agent-core": "^0.61.0",
44
+ "@mariozechner/pi-ai": "^0.61.0",
45
+ "@mariozechner/pi-tui": "^0.61.0",
46
46
  "@silvia-odwyer/photon-node": "^0.3.4",
47
47
  "chalk": "^5.5.0",
48
48
  "cli-highlight": "^2.1.11",