@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
@@ -1,55 +1,275 @@
1
- import { type EditorAction, type KeyId } from "@mariozechner/pi-tui";
2
- /**
3
- * Application-level actions (coding agent specific).
4
- */
5
- export type AppAction = "interrupt" | "clear" | "exit" | "suspend" | "cycleThinkingLevel" | "cycleModelForward" | "cycleModelBackward" | "selectModel" | "expandTools" | "toggleThinking" | "toggleSessionNamedFilter" | "externalEditor" | "followUp" | "dequeue" | "pasteImage" | "newSession" | "tree" | "fork" | "resume";
6
- /**
7
- * All configurable actions.
8
- */
9
- export type KeyAction = AppAction | EditorAction;
10
- /**
11
- * Full keybindings configuration (app + editor actions).
12
- */
13
- export type KeybindingsConfig = {
14
- [K in KeyAction]?: KeyId | KeyId[];
1
+ import { type Keybinding, type KeybindingsConfig, type KeyId, KeybindingsManager as TuiKeybindingsManager } from "@mariozechner/pi-tui";
2
+ export interface AppKeybindings {
3
+ "app.interrupt": true;
4
+ "app.clear": true;
5
+ "app.exit": true;
6
+ "app.suspend": true;
7
+ "app.thinking.cycle": true;
8
+ "app.model.cycleForward": true;
9
+ "app.model.cycleBackward": true;
10
+ "app.model.select": true;
11
+ "app.tools.expand": true;
12
+ "app.thinking.toggle": true;
13
+ "app.session.toggleNamedFilter": true;
14
+ "app.editor.external": true;
15
+ "app.message.followUp": true;
16
+ "app.message.dequeue": true;
17
+ "app.clipboard.pasteImage": true;
18
+ "app.session.new": true;
19
+ "app.session.tree": true;
20
+ "app.session.fork": true;
21
+ "app.session.resume": true;
22
+ "app.tree.foldOrUp": true;
23
+ "app.tree.unfoldOrDown": true;
24
+ "app.session.togglePath": true;
25
+ "app.session.toggleSort": true;
26
+ "app.session.rename": true;
27
+ "app.session.delete": true;
28
+ "app.session.deleteNoninvasive": true;
29
+ }
30
+ export type AppKeybinding = keyof AppKeybindings;
31
+ declare module "@mariozechner/pi-tui" {
32
+ interface Keybindings extends AppKeybindings {
33
+ }
34
+ }
35
+ export declare const KEYBINDINGS: {
36
+ readonly "tui.editor.cursorUp": {
37
+ readonly defaultKeys: "up";
38
+ readonly description: "Move cursor up";
39
+ };
40
+ readonly "tui.editor.cursorDown": {
41
+ readonly defaultKeys: "down";
42
+ readonly description: "Move cursor down";
43
+ };
44
+ readonly "tui.editor.cursorLeft": {
45
+ readonly defaultKeys: ["left", "ctrl+b"];
46
+ readonly description: "Move cursor left";
47
+ };
48
+ readonly "tui.editor.cursorRight": {
49
+ readonly defaultKeys: ["right", "ctrl+f"];
50
+ readonly description: "Move cursor right";
51
+ };
52
+ readonly "tui.editor.cursorWordLeft": {
53
+ readonly defaultKeys: ["alt+left", "ctrl+left", "alt+b"];
54
+ readonly description: "Move cursor word left";
55
+ };
56
+ readonly "tui.editor.cursorWordRight": {
57
+ readonly defaultKeys: ["alt+right", "ctrl+right", "alt+f"];
58
+ readonly description: "Move cursor word right";
59
+ };
60
+ readonly "tui.editor.cursorLineStart": {
61
+ readonly defaultKeys: ["home", "ctrl+a"];
62
+ readonly description: "Move to line start";
63
+ };
64
+ readonly "tui.editor.cursorLineEnd": {
65
+ readonly defaultKeys: ["end", "ctrl+e"];
66
+ readonly description: "Move to line end";
67
+ };
68
+ readonly "tui.editor.jumpForward": {
69
+ readonly defaultKeys: "ctrl+]";
70
+ readonly description: "Jump forward to character";
71
+ };
72
+ readonly "tui.editor.jumpBackward": {
73
+ readonly defaultKeys: "ctrl+alt+]";
74
+ readonly description: "Jump backward to character";
75
+ };
76
+ readonly "tui.editor.pageUp": {
77
+ readonly defaultKeys: "pageUp";
78
+ readonly description: "Page up";
79
+ };
80
+ readonly "tui.editor.pageDown": {
81
+ readonly defaultKeys: "pageDown";
82
+ readonly description: "Page down";
83
+ };
84
+ readonly "tui.editor.deleteCharBackward": {
85
+ readonly defaultKeys: "backspace";
86
+ readonly description: "Delete character backward";
87
+ };
88
+ readonly "tui.editor.deleteCharForward": {
89
+ readonly defaultKeys: ["delete", "ctrl+d"];
90
+ readonly description: "Delete character forward";
91
+ };
92
+ readonly "tui.editor.deleteWordBackward": {
93
+ readonly defaultKeys: ["ctrl+w", "alt+backspace"];
94
+ readonly description: "Delete word backward";
95
+ };
96
+ readonly "tui.editor.deleteWordForward": {
97
+ readonly defaultKeys: ["alt+d", "alt+delete"];
98
+ readonly description: "Delete word forward";
99
+ };
100
+ readonly "tui.editor.deleteToLineStart": {
101
+ readonly defaultKeys: "ctrl+u";
102
+ readonly description: "Delete to line start";
103
+ };
104
+ readonly "tui.editor.deleteToLineEnd": {
105
+ readonly defaultKeys: "ctrl+k";
106
+ readonly description: "Delete to line end";
107
+ };
108
+ readonly "tui.editor.yank": {
109
+ readonly defaultKeys: "ctrl+y";
110
+ readonly description: "Yank";
111
+ };
112
+ readonly "tui.editor.yankPop": {
113
+ readonly defaultKeys: "alt+y";
114
+ readonly description: "Yank pop";
115
+ };
116
+ readonly "tui.editor.undo": {
117
+ readonly defaultKeys: "ctrl+-";
118
+ readonly description: "Undo";
119
+ };
120
+ readonly "tui.input.newLine": {
121
+ readonly defaultKeys: "shift+enter";
122
+ readonly description: "Insert newline";
123
+ };
124
+ readonly "tui.input.submit": {
125
+ readonly defaultKeys: "enter";
126
+ readonly description: "Submit input";
127
+ };
128
+ readonly "tui.input.tab": {
129
+ readonly defaultKeys: "tab";
130
+ readonly description: "Tab / autocomplete";
131
+ };
132
+ readonly "tui.input.copy": {
133
+ readonly defaultKeys: "ctrl+c";
134
+ readonly description: "Copy selection";
135
+ };
136
+ readonly "tui.select.up": {
137
+ readonly defaultKeys: "up";
138
+ readonly description: "Move selection up";
139
+ };
140
+ readonly "tui.select.down": {
141
+ readonly defaultKeys: "down";
142
+ readonly description: "Move selection down";
143
+ };
144
+ readonly "tui.select.pageUp": {
145
+ readonly defaultKeys: "pageUp";
146
+ readonly description: "Selection page up";
147
+ };
148
+ readonly "tui.select.pageDown": {
149
+ readonly defaultKeys: "pageDown";
150
+ readonly description: "Selection page down";
151
+ };
152
+ readonly "tui.select.confirm": {
153
+ readonly defaultKeys: "enter";
154
+ readonly description: "Confirm selection";
155
+ };
156
+ readonly "tui.select.cancel": {
157
+ readonly defaultKeys: ["escape", "ctrl+c"];
158
+ readonly description: "Cancel selection";
159
+ };
160
+ readonly "app.interrupt": {
161
+ readonly defaultKeys: "escape";
162
+ readonly description: "Cancel or abort";
163
+ };
164
+ readonly "app.clear": {
165
+ readonly defaultKeys: "ctrl+c";
166
+ readonly description: "Clear editor";
167
+ };
168
+ readonly "app.exit": {
169
+ readonly defaultKeys: "ctrl+d";
170
+ readonly description: "Exit when editor is empty";
171
+ };
172
+ readonly "app.suspend": {
173
+ readonly defaultKeys: "ctrl+z";
174
+ readonly description: "Suspend to background";
175
+ };
176
+ readonly "app.thinking.cycle": {
177
+ readonly defaultKeys: "shift+tab";
178
+ readonly description: "Cycle thinking level";
179
+ };
180
+ readonly "app.model.cycleForward": {
181
+ readonly defaultKeys: "ctrl+p";
182
+ readonly description: "Cycle to next model";
183
+ };
184
+ readonly "app.model.cycleBackward": {
185
+ readonly defaultKeys: "shift+ctrl+p";
186
+ readonly description: "Cycle to previous model";
187
+ };
188
+ readonly "app.model.select": {
189
+ readonly defaultKeys: "ctrl+l";
190
+ readonly description: "Open model selector";
191
+ };
192
+ readonly "app.tools.expand": {
193
+ readonly defaultKeys: "ctrl+o";
194
+ readonly description: "Toggle tool output";
195
+ };
196
+ readonly "app.thinking.toggle": {
197
+ readonly defaultKeys: "ctrl+t";
198
+ readonly description: "Toggle thinking blocks";
199
+ };
200
+ readonly "app.session.toggleNamedFilter": {
201
+ readonly defaultKeys: "ctrl+n";
202
+ readonly description: "Toggle named session filter";
203
+ };
204
+ readonly "app.editor.external": {
205
+ readonly defaultKeys: "ctrl+g";
206
+ readonly description: "Open external editor";
207
+ };
208
+ readonly "app.message.followUp": {
209
+ readonly defaultKeys: "alt+enter";
210
+ readonly description: "Queue follow-up message";
211
+ };
212
+ readonly "app.message.dequeue": {
213
+ readonly defaultKeys: "alt+up";
214
+ readonly description: "Restore queued messages";
215
+ };
216
+ readonly "app.clipboard.pasteImage": {
217
+ readonly defaultKeys: "alt+v" | "ctrl+v";
218
+ readonly description: "Paste image from clipboard";
219
+ };
220
+ readonly "app.session.new": {
221
+ readonly defaultKeys: [];
222
+ readonly description: "Start a new session";
223
+ };
224
+ readonly "app.session.tree": {
225
+ readonly defaultKeys: [];
226
+ readonly description: "Open session tree";
227
+ };
228
+ readonly "app.session.fork": {
229
+ readonly defaultKeys: [];
230
+ readonly description: "Fork current session";
231
+ };
232
+ readonly "app.session.resume": {
233
+ readonly defaultKeys: [];
234
+ readonly description: "Resume a session";
235
+ };
236
+ readonly "app.tree.foldOrUp": {
237
+ readonly defaultKeys: ["ctrl+left", "alt+left"];
238
+ readonly description: "Fold tree branch or move up";
239
+ };
240
+ readonly "app.tree.unfoldOrDown": {
241
+ readonly defaultKeys: ["ctrl+right", "alt+right"];
242
+ readonly description: "Unfold tree branch or move down";
243
+ };
244
+ readonly "app.session.togglePath": {
245
+ readonly defaultKeys: "ctrl+p";
246
+ readonly description: "Toggle session path display";
247
+ };
248
+ readonly "app.session.toggleSort": {
249
+ readonly defaultKeys: "ctrl+s";
250
+ readonly description: "Toggle session sort mode";
251
+ };
252
+ readonly "app.session.rename": {
253
+ readonly defaultKeys: "ctrl+r";
254
+ readonly description: "Rename session";
255
+ };
256
+ readonly "app.session.delete": {
257
+ readonly defaultKeys: "ctrl+d";
258
+ readonly description: "Delete session";
259
+ };
260
+ readonly "app.session.deleteNoninvasive": {
261
+ readonly defaultKeys: "ctrl+backspace";
262
+ readonly description: "Delete session when query is empty";
263
+ };
15
264
  };
16
- /**
17
- * Default application keybindings.
18
- */
19
- export declare const DEFAULT_APP_KEYBINDINGS: Record<AppAction, KeyId | KeyId[]>;
20
- /**
21
- * All default keybindings (app + editor).
22
- */
23
- export declare const DEFAULT_KEYBINDINGS: Required<KeybindingsConfig>;
24
- /**
25
- * Manages all keybindings (app + editor).
26
- */
27
- export declare class KeybindingsManager {
28
- private config;
29
- private appActionToKeys;
30
- private constructor();
31
- /**
32
- * Create from config file and set up editor keybindings.
33
- */
265
+ export declare function migrateKeybindingsConfigFile(agentDir?: string): boolean;
266
+ export declare class KeybindingsManager extends TuiKeybindingsManager {
267
+ private configPath;
268
+ constructor(userBindings?: KeybindingsConfig, configPath?: string);
34
269
  static create(agentDir?: string): KeybindingsManager;
35
- /**
36
- * Create in-memory.
37
- */
38
- static inMemory(config?: KeybindingsConfig): KeybindingsManager;
270
+ reload(): void;
271
+ getEffectiveConfig(): KeybindingsConfig;
39
272
  private static loadFromFile;
40
- private buildMaps;
41
- /**
42
- * Check if input matches an app action.
43
- */
44
- matches(data: string, action: AppAction): boolean;
45
- /**
46
- * Get keys bound to an app action.
47
- */
48
- getKeys(action: AppAction): KeyId[];
49
- /**
50
- * Get the full effective config.
51
- */
52
- getEffectiveConfig(): Required<KeybindingsConfig>;
53
273
  }
54
- export type { EditorAction, KeyId };
274
+ export type { Keybinding, KeyId, KeybindingsConfig };
55
275
  //# sourceMappingURL=keybindings.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"keybindings.d.ts","sourceRoot":"","sources":["../../src/core/keybindings.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,YAAY,EAGjB,KAAK,KAAK,EAGV,MAAM,sBAAsB,CAAC;AAK9B;;GAEG;AACH,MAAM,MAAM,SAAS,GAClB,WAAW,GACX,OAAO,GACP,MAAM,GACN,SAAS,GACT,oBAAoB,GACpB,mBAAmB,GACnB,oBAAoB,GACpB,aAAa,GACb,aAAa,GACb,gBAAgB,GAChB,0BAA0B,GAC1B,gBAAgB,GAChB,UAAU,GACV,SAAS,GACT,YAAY,GACZ,YAAY,GACZ,MAAM,GACN,MAAM,GACN,QAAQ,CAAC;AAEZ;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,YAAY,CAAC;AAEjD;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;KAC9B,CAAC,IAAI,SAAS,CAAC,CAAC,EAAE,KAAK,GAAG,KAAK,EAAE;CAClC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,SAAS,EAAE,KAAK,GAAG,KAAK,EAAE,CAoBtE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,iBAAiB,CAG3D,CAAC;AA6BF;;GAEG;AACH,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,eAAe,CAA0B;IAEjD,OAAO,eAIN;IAED;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAE,MAAsB,GAAG,kBAAkB,CAgBlE;IAED;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAE,iBAAsB,GAAG,kBAAkB,CAElE;IAED,OAAO,CAAC,MAAM,CAAC,YAAY;IAS3B,OAAO,CAAC,SAAS;IAiBjB;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAOhD;IAED;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG,KAAK,EAAE,CAElC;IAED;;OAEG;IACH,kBAAkB,IAAI,QAAQ,CAAC,iBAAiB,CAAC,CAQhD;CACD;AAGD,YAAY,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC","sourcesContent":["import {\n\tDEFAULT_EDITOR_KEYBINDINGS,\n\ttype EditorAction,\n\ttype EditorKeybindingsConfig,\n\tEditorKeybindingsManager,\n\ttype KeyId,\n\tmatchesKey,\n\tsetEditorKeybindings,\n} from \"@mariozechner/pi-tui\";\nimport { existsSync, readFileSync } from \"fs\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../config.js\";\n\n/**\n * Application-level actions (coding agent specific).\n */\nexport type AppAction =\n\t| \"interrupt\"\n\t| \"clear\"\n\t| \"exit\"\n\t| \"suspend\"\n\t| \"cycleThinkingLevel\"\n\t| \"cycleModelForward\"\n\t| \"cycleModelBackward\"\n\t| \"selectModel\"\n\t| \"expandTools\"\n\t| \"toggleThinking\"\n\t| \"toggleSessionNamedFilter\"\n\t| \"externalEditor\"\n\t| \"followUp\"\n\t| \"dequeue\"\n\t| \"pasteImage\"\n\t| \"newSession\"\n\t| \"tree\"\n\t| \"fork\"\n\t| \"resume\";\n\n/**\n * All configurable actions.\n */\nexport type KeyAction = AppAction | EditorAction;\n\n/**\n * Full keybindings configuration (app + editor actions).\n */\nexport type KeybindingsConfig = {\n\t[K in KeyAction]?: KeyId | KeyId[];\n};\n\n/**\n * Default application keybindings.\n */\nexport const DEFAULT_APP_KEYBINDINGS: Record<AppAction, KeyId | KeyId[]> = {\n\tinterrupt: \"escape\",\n\tclear: \"ctrl+c\",\n\texit: \"ctrl+d\",\n\tsuspend: \"ctrl+z\",\n\tcycleThinkingLevel: \"shift+tab\",\n\tcycleModelForward: \"ctrl+p\",\n\tcycleModelBackward: \"shift+ctrl+p\",\n\tselectModel: \"ctrl+l\",\n\texpandTools: \"ctrl+o\",\n\ttoggleThinking: \"ctrl+t\",\n\ttoggleSessionNamedFilter: \"ctrl+n\",\n\texternalEditor: \"ctrl+g\",\n\tfollowUp: \"alt+enter\",\n\tdequeue: \"alt+up\",\n\tpasteImage: process.platform === \"win32\" ? \"alt+v\" : \"ctrl+v\",\n\tnewSession: [],\n\ttree: [],\n\tfork: [],\n\tresume: [],\n};\n\n/**\n * All default keybindings (app + editor).\n */\nexport const DEFAULT_KEYBINDINGS: Required<KeybindingsConfig> = {\n\t...DEFAULT_EDITOR_KEYBINDINGS,\n\t...DEFAULT_APP_KEYBINDINGS,\n};\n\n// App actions list for type checking\nconst APP_ACTIONS: AppAction[] = [\n\t\"interrupt\",\n\t\"clear\",\n\t\"exit\",\n\t\"suspend\",\n\t\"cycleThinkingLevel\",\n\t\"cycleModelForward\",\n\t\"cycleModelBackward\",\n\t\"selectModel\",\n\t\"expandTools\",\n\t\"toggleThinking\",\n\t\"toggleSessionNamedFilter\",\n\t\"externalEditor\",\n\t\"followUp\",\n\t\"dequeue\",\n\t\"pasteImage\",\n\t\"newSession\",\n\t\"tree\",\n\t\"fork\",\n\t\"resume\",\n];\n\nfunction isAppAction(action: string): action is AppAction {\n\treturn APP_ACTIONS.includes(action as AppAction);\n}\n\n/**\n * Manages all keybindings (app + editor).\n */\nexport class KeybindingsManager {\n\tprivate config: KeybindingsConfig;\n\tprivate appActionToKeys: Map<AppAction, KeyId[]>;\n\n\tprivate constructor(config: KeybindingsConfig) {\n\t\tthis.config = config;\n\t\tthis.appActionToKeys = new Map();\n\t\tthis.buildMaps();\n\t}\n\n\t/**\n\t * Create from config file and set up editor keybindings.\n\t */\n\tstatic create(agentDir: string = getAgentDir()): KeybindingsManager {\n\t\tconst configPath = join(agentDir, \"keybindings.json\");\n\t\tconst config = KeybindingsManager.loadFromFile(configPath);\n\t\tconst manager = new KeybindingsManager(config);\n\n\t\t// Set up editor keybindings globally\n\t\t// Include both editor actions and expandTools (shared between app and editor)\n\t\tconst editorConfig: EditorKeybindingsConfig = {};\n\t\tfor (const [action, keys] of Object.entries(config)) {\n\t\t\tif (!isAppAction(action) || action === \"expandTools\") {\n\t\t\t\teditorConfig[action as EditorAction] = keys;\n\t\t\t}\n\t\t}\n\t\tsetEditorKeybindings(new EditorKeybindingsManager(editorConfig));\n\n\t\treturn manager;\n\t}\n\n\t/**\n\t * Create in-memory.\n\t */\n\tstatic inMemory(config: KeybindingsConfig = {}): KeybindingsManager {\n\t\treturn new KeybindingsManager(config);\n\t}\n\n\tprivate static loadFromFile(path: string): KeybindingsConfig {\n\t\tif (!existsSync(path)) return {};\n\t\ttry {\n\t\t\treturn JSON.parse(readFileSync(path, \"utf-8\"));\n\t\t} catch {\n\t\t\treturn {};\n\t\t}\n\t}\n\n\tprivate buildMaps(): void {\n\t\tthis.appActionToKeys.clear();\n\n\t\t// Set defaults for app actions\n\t\tfor (const [action, keys] of Object.entries(DEFAULT_APP_KEYBINDINGS)) {\n\t\t\tconst keyArray = Array.isArray(keys) ? keys : [keys];\n\t\t\tthis.appActionToKeys.set(action as AppAction, [...keyArray]);\n\t\t}\n\n\t\t// Override with user config (app actions only)\n\t\tfor (const [action, keys] of Object.entries(this.config)) {\n\t\t\tif (keys === undefined || !isAppAction(action)) continue;\n\t\t\tconst keyArray = Array.isArray(keys) ? keys : [keys];\n\t\t\tthis.appActionToKeys.set(action, keyArray);\n\t\t}\n\t}\n\n\t/**\n\t * Check if input matches an app action.\n\t */\n\tmatches(data: string, action: AppAction): boolean {\n\t\tconst keys = this.appActionToKeys.get(action);\n\t\tif (!keys) return false;\n\t\tfor (const key of keys) {\n\t\t\tif (matchesKey(data, key)) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Get keys bound to an app action.\n\t */\n\tgetKeys(action: AppAction): KeyId[] {\n\t\treturn this.appActionToKeys.get(action) ?? [];\n\t}\n\n\t/**\n\t * Get the full effective config.\n\t */\n\tgetEffectiveConfig(): Required<KeybindingsConfig> {\n\t\tconst result = { ...DEFAULT_KEYBINDINGS };\n\t\tfor (const [action, keys] of Object.entries(this.config)) {\n\t\t\tif (keys !== undefined) {\n\t\t\t\t(result as KeybindingsConfig)[action as KeyAction] = keys;\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n}\n\n// Re-export for convenience\nexport type { EditorAction, KeyId };\n"]}
1
+ {"version":3,"file":"keybindings.d.ts","sourceRoot":"","sources":["../../src/core/keybindings.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,UAAU,EAEf,KAAK,iBAAiB,EACtB,KAAK,KAAK,EAEV,kBAAkB,IAAI,qBAAqB,EAC3C,MAAM,sBAAsB,CAAC;AAK9B,MAAM,WAAW,cAAc;IAC9B,eAAe,EAAE,IAAI,CAAC;IACtB,WAAW,EAAE,IAAI,CAAC;IAClB,UAAU,EAAE,IAAI,CAAC;IACjB,aAAa,EAAE,IAAI,CAAC;IACpB,oBAAoB,EAAE,IAAI,CAAC;IAC3B,wBAAwB,EAAE,IAAI,CAAC;IAC/B,yBAAyB,EAAE,IAAI,CAAC;IAChC,kBAAkB,EAAE,IAAI,CAAC;IACzB,kBAAkB,EAAE,IAAI,CAAC;IACzB,qBAAqB,EAAE,IAAI,CAAC;IAC5B,+BAA+B,EAAE,IAAI,CAAC;IACtC,qBAAqB,EAAE,IAAI,CAAC;IAC5B,sBAAsB,EAAE,IAAI,CAAC;IAC7B,qBAAqB,EAAE,IAAI,CAAC;IAC5B,0BAA0B,EAAE,IAAI,CAAC;IACjC,iBAAiB,EAAE,IAAI,CAAC;IACxB,kBAAkB,EAAE,IAAI,CAAC;IACzB,kBAAkB,EAAE,IAAI,CAAC;IACzB,oBAAoB,EAAE,IAAI,CAAC;IAC3B,mBAAmB,EAAE,IAAI,CAAC;IAC1B,uBAAuB,EAAE,IAAI,CAAC;IAC9B,wBAAwB,EAAE,IAAI,CAAC;IAC/B,wBAAwB,EAAE,IAAI,CAAC;IAC/B,oBAAoB,EAAE,IAAI,CAAC;IAC3B,oBAAoB,EAAE,IAAI,CAAC;IAC3B,+BAA+B,EAAE,IAAI,CAAC;CACtC;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,cAAc,CAAC;AAEjD,OAAO,QAAQ,sBAAsB,CAAC,CAAC;IACtC,UAAU,WAAY,SAAQ,cAAc;KAAG;CAC/C;AAED,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4EkB,CAAC;AAwI3C,wBAAgB,4BAA4B,CAAC,QAAQ,GAAE,MAAsB,GAAG,OAAO,CAUtF;AAED,qBAAa,kBAAmB,SAAQ,qBAAqB;IAC5D,OAAO,CAAC,UAAU,CAAqB;IAEvC,YAAY,YAAY,GAAE,iBAAsB,EAAE,UAAU,CAAC,EAAE,MAAM,EAGpE;IAED,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAE,MAAsB,GAAG,kBAAkB,CAIlE;IAED,MAAM,IAAI,IAAI,CAGb;IAED,kBAAkB,IAAI,iBAAiB,CAEtC;IAED,OAAO,CAAC,MAAM,CAAC,YAAY;CAK3B;AAED,YAAY,EAAE,UAAU,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC","sourcesContent":["import {\n\ttype Keybinding,\n\ttype KeybindingDefinitions,\n\ttype KeybindingsConfig,\n\ttype KeyId,\n\tTUI_KEYBINDINGS,\n\tKeybindingsManager as TuiKeybindingsManager,\n} from \"@mariozechner/pi-tui\";\nimport { existsSync, readFileSync, writeFileSync } from \"fs\";\nimport { join } from \"path\";\nimport { getAgentDir } from \"../config.js\";\n\nexport interface AppKeybindings {\n\t\"app.interrupt\": true;\n\t\"app.clear\": true;\n\t\"app.exit\": true;\n\t\"app.suspend\": true;\n\t\"app.thinking.cycle\": true;\n\t\"app.model.cycleForward\": true;\n\t\"app.model.cycleBackward\": true;\n\t\"app.model.select\": true;\n\t\"app.tools.expand\": true;\n\t\"app.thinking.toggle\": true;\n\t\"app.session.toggleNamedFilter\": true;\n\t\"app.editor.external\": true;\n\t\"app.message.followUp\": true;\n\t\"app.message.dequeue\": true;\n\t\"app.clipboard.pasteImage\": true;\n\t\"app.session.new\": true;\n\t\"app.session.tree\": true;\n\t\"app.session.fork\": true;\n\t\"app.session.resume\": true;\n\t\"app.tree.foldOrUp\": true;\n\t\"app.tree.unfoldOrDown\": true;\n\t\"app.session.togglePath\": true;\n\t\"app.session.toggleSort\": true;\n\t\"app.session.rename\": true;\n\t\"app.session.delete\": true;\n\t\"app.session.deleteNoninvasive\": true;\n}\n\nexport type AppKeybinding = keyof AppKeybindings;\n\ndeclare module \"@mariozechner/pi-tui\" {\n\tinterface Keybindings extends AppKeybindings {}\n}\n\nexport const KEYBINDINGS = {\n\t...TUI_KEYBINDINGS,\n\t\"app.interrupt\": { defaultKeys: \"escape\", description: \"Cancel or abort\" },\n\t\"app.clear\": { defaultKeys: \"ctrl+c\", description: \"Clear editor\" },\n\t\"app.exit\": { defaultKeys: \"ctrl+d\", description: \"Exit when editor is empty\" },\n\t\"app.suspend\": { defaultKeys: \"ctrl+z\", description: \"Suspend to background\" },\n\t\"app.thinking.cycle\": {\n\t\tdefaultKeys: \"shift+tab\",\n\t\tdescription: \"Cycle thinking level\",\n\t},\n\t\"app.model.cycleForward\": {\n\t\tdefaultKeys: \"ctrl+p\",\n\t\tdescription: \"Cycle to next model\",\n\t},\n\t\"app.model.cycleBackward\": {\n\t\tdefaultKeys: \"shift+ctrl+p\",\n\t\tdescription: \"Cycle to previous model\",\n\t},\n\t\"app.model.select\": { defaultKeys: \"ctrl+l\", description: \"Open model selector\" },\n\t\"app.tools.expand\": { defaultKeys: \"ctrl+o\", description: \"Toggle tool output\" },\n\t\"app.thinking.toggle\": {\n\t\tdefaultKeys: \"ctrl+t\",\n\t\tdescription: \"Toggle thinking blocks\",\n\t},\n\t\"app.session.toggleNamedFilter\": {\n\t\tdefaultKeys: \"ctrl+n\",\n\t\tdescription: \"Toggle named session filter\",\n\t},\n\t\"app.editor.external\": {\n\t\tdefaultKeys: \"ctrl+g\",\n\t\tdescription: \"Open external editor\",\n\t},\n\t\"app.message.followUp\": {\n\t\tdefaultKeys: \"alt+enter\",\n\t\tdescription: \"Queue follow-up message\",\n\t},\n\t\"app.message.dequeue\": {\n\t\tdefaultKeys: \"alt+up\",\n\t\tdescription: \"Restore queued messages\",\n\t},\n\t\"app.clipboard.pasteImage\": {\n\t\tdefaultKeys: process.platform === \"win32\" ? \"alt+v\" : \"ctrl+v\",\n\t\tdescription: \"Paste image from clipboard\",\n\t},\n\t\"app.session.new\": { defaultKeys: [], description: \"Start a new session\" },\n\t\"app.session.tree\": { defaultKeys: [], description: \"Open session tree\" },\n\t\"app.session.fork\": { defaultKeys: [], description: \"Fork current session\" },\n\t\"app.session.resume\": { defaultKeys: [], description: \"Resume a session\" },\n\t\"app.tree.foldOrUp\": {\n\t\tdefaultKeys: [\"ctrl+left\", \"alt+left\"],\n\t\tdescription: \"Fold tree branch or move up\",\n\t},\n\t\"app.tree.unfoldOrDown\": {\n\t\tdefaultKeys: [\"ctrl+right\", \"alt+right\"],\n\t\tdescription: \"Unfold tree branch or move down\",\n\t},\n\t\"app.session.togglePath\": {\n\t\tdefaultKeys: \"ctrl+p\",\n\t\tdescription: \"Toggle session path display\",\n\t},\n\t\"app.session.toggleSort\": {\n\t\tdefaultKeys: \"ctrl+s\",\n\t\tdescription: \"Toggle session sort mode\",\n\t},\n\t\"app.session.rename\": {\n\t\tdefaultKeys: \"ctrl+r\",\n\t\tdescription: \"Rename session\",\n\t},\n\t\"app.session.delete\": {\n\t\tdefaultKeys: \"ctrl+d\",\n\t\tdescription: \"Delete session\",\n\t},\n\t\"app.session.deleteNoninvasive\": {\n\t\tdefaultKeys: \"ctrl+backspace\",\n\t\tdescription: \"Delete session when query is empty\",\n\t},\n} as const satisfies KeybindingDefinitions;\n\nconst KEYBINDING_NAME_MIGRATIONS = {\n\tcursorUp: \"tui.editor.cursorUp\",\n\tcursorDown: \"tui.editor.cursorDown\",\n\tcursorLeft: \"tui.editor.cursorLeft\",\n\tcursorRight: \"tui.editor.cursorRight\",\n\tcursorWordLeft: \"tui.editor.cursorWordLeft\",\n\tcursorWordRight: \"tui.editor.cursorWordRight\",\n\tcursorLineStart: \"tui.editor.cursorLineStart\",\n\tcursorLineEnd: \"tui.editor.cursorLineEnd\",\n\tjumpForward: \"tui.editor.jumpForward\",\n\tjumpBackward: \"tui.editor.jumpBackward\",\n\tpageUp: \"tui.editor.pageUp\",\n\tpageDown: \"tui.editor.pageDown\",\n\tdeleteCharBackward: \"tui.editor.deleteCharBackward\",\n\tdeleteCharForward: \"tui.editor.deleteCharForward\",\n\tdeleteWordBackward: \"tui.editor.deleteWordBackward\",\n\tdeleteWordForward: \"tui.editor.deleteWordForward\",\n\tdeleteToLineStart: \"tui.editor.deleteToLineStart\",\n\tdeleteToLineEnd: \"tui.editor.deleteToLineEnd\",\n\tyank: \"tui.editor.yank\",\n\tyankPop: \"tui.editor.yankPop\",\n\tundo: \"tui.editor.undo\",\n\tnewLine: \"tui.input.newLine\",\n\tsubmit: \"tui.input.submit\",\n\ttab: \"tui.input.tab\",\n\tcopy: \"tui.input.copy\",\n\tselectUp: \"tui.select.up\",\n\tselectDown: \"tui.select.down\",\n\tselectPageUp: \"tui.select.pageUp\",\n\tselectPageDown: \"tui.select.pageDown\",\n\tselectConfirm: \"tui.select.confirm\",\n\tselectCancel: \"tui.select.cancel\",\n\tinterrupt: \"app.interrupt\",\n\tclear: \"app.clear\",\n\texit: \"app.exit\",\n\tsuspend: \"app.suspend\",\n\tcycleThinkingLevel: \"app.thinking.cycle\",\n\tcycleModelForward: \"app.model.cycleForward\",\n\tcycleModelBackward: \"app.model.cycleBackward\",\n\tselectModel: \"app.model.select\",\n\texpandTools: \"app.tools.expand\",\n\ttoggleThinking: \"app.thinking.toggle\",\n\ttoggleSessionNamedFilter: \"app.session.toggleNamedFilter\",\n\texternalEditor: \"app.editor.external\",\n\tfollowUp: \"app.message.followUp\",\n\tdequeue: \"app.message.dequeue\",\n\tpasteImage: \"app.clipboard.pasteImage\",\n\tnewSession: \"app.session.new\",\n\ttree: \"app.session.tree\",\n\tfork: \"app.session.fork\",\n\tresume: \"app.session.resume\",\n\ttreeFoldOrUp: \"app.tree.foldOrUp\",\n\ttreeUnfoldOrDown: \"app.tree.unfoldOrDown\",\n\ttoggleSessionPath: \"app.session.togglePath\",\n\ttoggleSessionSort: \"app.session.toggleSort\",\n\trenameSession: \"app.session.rename\",\n\tdeleteSession: \"app.session.delete\",\n\tdeleteSessionNoninvasive: \"app.session.deleteNoninvasive\",\n} as const satisfies Record<string, Keybinding>;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isLegacyKeybindingName(key: string): key is keyof typeof KEYBINDING_NAME_MIGRATIONS {\n\treturn key in KEYBINDING_NAME_MIGRATIONS;\n}\n\nfunction toKeybindingsConfig(value: unknown): KeybindingsConfig {\n\tif (!isRecord(value)) return {};\n\n\tconst config: KeybindingsConfig = {};\n\tfor (const [key, binding] of Object.entries(value)) {\n\t\tif (typeof binding === \"string\") {\n\t\t\tconfig[key] = binding as KeyId;\n\t\t\tcontinue;\n\t\t}\n\t\tif (Array.isArray(binding) && binding.every((entry) => typeof entry === \"string\")) {\n\t\t\tconfig[key] = binding as KeyId[];\n\t\t}\n\t}\n\treturn config;\n}\n\nfunction migrateKeybindingNames(rawConfig: Record<string, unknown>): {\n\tconfig: Record<string, unknown>;\n\tmigrated: boolean;\n} {\n\tconst config: Record<string, unknown> = {};\n\tlet migrated = false;\n\n\tfor (const [key, value] of Object.entries(rawConfig)) {\n\t\tconst nextKey = isLegacyKeybindingName(key) ? KEYBINDING_NAME_MIGRATIONS[key] : key;\n\t\tif (nextKey !== key) {\n\t\t\tmigrated = true;\n\t\t}\n\t\tif (key !== nextKey && Object.hasOwn(rawConfig, nextKey)) {\n\t\t\tmigrated = true;\n\t\t\tcontinue;\n\t\t}\n\t\tconfig[nextKey] = value;\n\t}\n\n\treturn { config: orderKeybindingsConfig(config), migrated };\n}\n\nfunction orderKeybindingsConfig(config: Record<string, unknown>): Record<string, unknown> {\n\tconst ordered: Record<string, unknown> = {};\n\tfor (const keybinding of Object.keys(KEYBINDINGS)) {\n\t\tif (Object.hasOwn(config, keybinding)) {\n\t\t\tordered[keybinding] = config[keybinding];\n\t\t}\n\t}\n\n\tconst extras = Object.keys(config)\n\t\t.filter((key) => !Object.hasOwn(ordered, key))\n\t\t.sort();\n\tfor (const key of extras) {\n\t\tordered[key] = config[key];\n\t}\n\n\treturn ordered;\n}\n\nfunction loadRawConfig(path: string): Record<string, unknown> | undefined {\n\tif (!existsSync(path)) return undefined;\n\ttry {\n\t\tconst parsed = JSON.parse(readFileSync(path, \"utf-8\")) as unknown;\n\t\treturn isRecord(parsed) ? parsed : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function migrateKeybindingsConfigFile(agentDir: string = getAgentDir()): boolean {\n\tconst configPath = join(agentDir, \"keybindings.json\");\n\tconst rawConfig = loadRawConfig(configPath);\n\tif (!rawConfig) return false;\n\n\tconst { config, migrated } = migrateKeybindingNames(rawConfig);\n\tif (!migrated) return false;\n\n\twriteFileSync(configPath, `${JSON.stringify(config, null, 2)}\\n`, \"utf-8\");\n\treturn true;\n}\n\nexport class KeybindingsManager extends TuiKeybindingsManager {\n\tprivate configPath: string | undefined;\n\n\tconstructor(userBindings: KeybindingsConfig = {}, configPath?: string) {\n\t\tsuper(KEYBINDINGS, userBindings);\n\t\tthis.configPath = configPath;\n\t}\n\n\tstatic create(agentDir: string = getAgentDir()): KeybindingsManager {\n\t\tconst configPath = join(agentDir, \"keybindings.json\");\n\t\tconst userBindings = KeybindingsManager.loadFromFile(configPath);\n\t\treturn new KeybindingsManager(userBindings, configPath);\n\t}\n\n\treload(): void {\n\t\tif (!this.configPath) return;\n\t\tthis.setUserBindings(KeybindingsManager.loadFromFile(this.configPath));\n\t}\n\n\tgetEffectiveConfig(): KeybindingsConfig {\n\t\treturn this.getResolvedBindings();\n\t}\n\n\tprivate static loadFromFile(path: string): KeybindingsConfig {\n\t\tconst rawConfig = loadRawConfig(path);\n\t\tif (!rawConfig) return {};\n\t\treturn toKeybindingsConfig(migrateKeybindingNames(rawConfig).config);\n\t}\n}\n\nexport type { Keybinding, KeyId, KeybindingsConfig };\n"]}