@jmoyers/harness 0.1.11 → 0.1.20

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 (232) hide show
  1. package/README.md +31 -39
  2. package/package.json +31 -11
  3. package/packages/harness-ai/src/anthropic-protocol.ts +68 -68
  4. package/packages/harness-ai/src/stream-text.ts +13 -91
  5. package/packages/harness-ui/src/frame-primitives.ts +158 -0
  6. package/packages/harness-ui/src/index.ts +18 -0
  7. package/packages/harness-ui/src/interaction/conversation-input-forwarder.ts +221 -0
  8. package/packages/harness-ui/src/interaction/conversation-selection-input.ts +213 -0
  9. package/packages/harness-ui/src/interaction/global-shortcut-input.ts +172 -0
  10. package/{src/ui → packages/harness-ui/src/interaction}/input-preflight.ts +10 -12
  11. package/{src/ui → packages/harness-ui/src/interaction}/input-token-router.ts +120 -69
  12. package/packages/harness-ui/src/interaction/input.ts +420 -0
  13. package/packages/harness-ui/src/interaction/left-nav-input.ts +166 -0
  14. package/{src/ui → packages/harness-ui/src/interaction}/main-pane-pointer-input.ts +91 -23
  15. package/{src/ui → packages/harness-ui/src/interaction}/pointer-routing-input.ts +112 -48
  16. package/packages/harness-ui/src/interaction/rail-pointer-input.ts +62 -0
  17. package/packages/harness-ui/src/interaction/repository-fold-input.ts +118 -0
  18. package/packages/harness-ui/src/kit.ts +476 -0
  19. package/packages/harness-ui/src/layout.ts +238 -0
  20. package/packages/harness-ui/src/modal-manager.ts +222 -0
  21. package/{src/ui → packages/harness-ui/src}/screen.ts +53 -26
  22. package/packages/harness-ui/src/surface.ts +252 -0
  23. package/packages/harness-ui/src/text-layout.ts +210 -0
  24. package/packages/nim-core/src/contracts.ts +239 -0
  25. package/packages/nim-core/src/event-store.ts +299 -0
  26. package/packages/nim-core/src/events.ts +53 -0
  27. package/packages/nim-core/src/index.ts +9 -0
  28. package/packages/nim-core/src/provider-router.ts +129 -0
  29. package/packages/nim-core/src/providers/anthropic-driver.ts +291 -0
  30. package/packages/nim-core/src/runtime-factory.ts +49 -0
  31. package/packages/nim-core/src/runtime.ts +1797 -0
  32. package/packages/nim-core/src/session-store.ts +516 -0
  33. package/packages/nim-core/src/telemetry.ts +48 -0
  34. package/packages/nim-test-tui/src/index.ts +150 -0
  35. package/packages/nim-ui-core/src/index.ts +1 -0
  36. package/packages/nim-ui-core/src/projection.ts +87 -0
  37. package/scripts/codex-live-mux-runtime.ts +2 -3872
  38. package/scripts/control-plane-daemon.ts +11 -0
  39. package/scripts/harness-bin.js +5 -0
  40. package/scripts/harness-commands.ts +300 -0
  41. package/scripts/harness-runtime.ts +82 -0
  42. package/scripts/harness.ts +33 -3019
  43. package/scripts/nim-tui-smoke.ts +748 -0
  44. package/src/cli/auth/runtime.ts +948 -0
  45. package/src/cli/gateway/runtime.ts +1872 -0
  46. package/src/cli/parsing/flags.ts +23 -0
  47. package/src/cli/parsing/session.ts +42 -0
  48. package/src/cli/runtime/context.ts +193 -0
  49. package/src/cli/runtime-app/application.ts +392 -0
  50. package/src/cli/runtime-infra/gateway-control.ts +729 -0
  51. package/{scripts/harness-inspector.ts → src/cli/workflows/inspector.ts} +14 -11
  52. package/src/cli/workflows/runtime.ts +965 -0
  53. package/src/clients/tui/left-rail-interactions.ts +519 -0
  54. package/src/clients/tui/main-pane-interactions.ts +509 -0
  55. package/src/clients/tui/modal-input-routing.ts +71 -0
  56. package/src/clients/tui/render-snapshot-adapter.ts +88 -0
  57. package/src/clients/web/synced-selectors.ts +132 -0
  58. package/src/codex/live-session.ts +82 -29
  59. package/src/config/config-core.ts +348 -8
  60. package/src/config/harness.config.template.jsonc +33 -0
  61. package/src/control-plane/agent-realtime-api.ts +82 -427
  62. package/src/control-plane/session-summary.ts +10 -81
  63. package/src/control-plane/status/reducer-base.ts +12 -12
  64. package/src/control-plane/status/reducers/claude-status-reducer.ts +3 -3
  65. package/src/control-plane/status/reducers/codex-status-reducer.ts +4 -4
  66. package/src/control-plane/status/reducers/cursor-status-reducer.ts +3 -3
  67. package/src/control-plane/stream-client.ts +12 -2
  68. package/src/control-plane/stream-command-parser.ts +83 -143
  69. package/src/control-plane/stream-protocol.ts +53 -37
  70. package/src/control-plane/stream-server-command.ts +376 -69
  71. package/src/control-plane/stream-server-session-runtime.ts +3 -2
  72. package/src/control-plane/stream-server.ts +864 -70
  73. package/src/control-plane/stream-session-runtime-types.ts +41 -0
  74. package/src/{mux/live-mux/control-plane-records.ts → core/contracts/records.ts} +24 -97
  75. package/src/core/state/observed-stream-cursor.ts +43 -0
  76. package/src/core/state/synced-observed-state.ts +273 -0
  77. package/src/core/store/harness-synced-store.ts +81 -0
  78. package/src/diff/budget.ts +136 -0
  79. package/src/diff/build.ts +289 -0
  80. package/src/diff/chunker.ts +146 -0
  81. package/src/diff/git-invoke.ts +315 -0
  82. package/src/diff/git-parse.ts +472 -0
  83. package/src/diff/hash.ts +70 -0
  84. package/src/diff/index.ts +24 -0
  85. package/src/diff/normalize.ts +134 -0
  86. package/src/diff/types.ts +178 -0
  87. package/src/diff-ui/args.ts +346 -0
  88. package/src/diff-ui/commands.ts +123 -0
  89. package/src/diff-ui/finder.ts +94 -0
  90. package/src/diff-ui/highlight.ts +127 -0
  91. package/src/diff-ui/index.ts +2 -0
  92. package/src/diff-ui/model.ts +141 -0
  93. package/src/diff-ui/pager.ts +412 -0
  94. package/src/diff-ui/render.ts +337 -0
  95. package/src/diff-ui/runtime.ts +379 -0
  96. package/src/diff-ui/state.ts +224 -0
  97. package/src/diff-ui/types.ts +236 -0
  98. package/src/domain/workspace.ts +68 -5
  99. package/src/mux/control-plane-op-queue.ts +93 -7
  100. package/src/mux/conversation-rail.ts +28 -71
  101. package/src/mux/dual-pane-core.ts +13 -13
  102. package/src/mux/harness-core-ui.ts +313 -42
  103. package/src/mux/input-shortcuts.ts +13 -131
  104. package/src/mux/keybinding-catalog.ts +340 -0
  105. package/src/mux/keybinding-registry.ts +103 -0
  106. package/src/mux/live-mux/command-menu-open-in.ts +280 -0
  107. package/src/mux/live-mux/command-menu.ts +167 -4
  108. package/src/mux/live-mux/conversation-state.ts +13 -0
  109. package/src/mux/live-mux/directory-resolution.ts +1 -1
  110. package/src/mux/live-mux/git-snapshot.ts +33 -2
  111. package/src/mux/live-mux/global-shortcut-handlers.ts +6 -0
  112. package/src/mux/live-mux/home-pane-drop.ts +1 -1
  113. package/src/mux/live-mux/home-pane-pointer.ts +10 -0
  114. package/src/mux/live-mux/input-forwarding.ts +59 -2
  115. package/src/mux/live-mux/left-nav-activation.ts +124 -7
  116. package/src/mux/live-mux/left-nav.ts +35 -0
  117. package/src/mux/live-mux/link-click.ts +292 -0
  118. package/src/mux/live-mux/modal-command-menu-handler.ts +46 -9
  119. package/src/mux/live-mux/modal-conversation-handlers.ts +5 -1
  120. package/src/mux/live-mux/modal-input-reducers.ts +77 -12
  121. package/src/mux/live-mux/modal-overlays.ts +168 -34
  122. package/src/mux/live-mux/modal-pointer.ts +3 -7
  123. package/src/mux/live-mux/modal-prompt-handlers.ts +23 -2
  124. package/src/mux/live-mux/modal-release-notes-handler.ts +111 -0
  125. package/src/mux/live-mux/modal-task-editor-handler.ts +16 -11
  126. package/src/mux/live-mux/pointer-routing.ts +5 -2
  127. package/src/mux/live-mux/project-pane-pointer.ts +8 -0
  128. package/src/mux/live-mux/rail-layout.ts +33 -30
  129. package/src/mux/live-mux/release-notes.ts +383 -0
  130. package/src/mux/live-mux/render-trace-analysis.ts +52 -7
  131. package/src/mux/live-mux/repository-folding.ts +3 -0
  132. package/src/mux/live-mux/selection.ts +0 -4
  133. package/src/mux/live-mux/session-diagnostics-paths.ts +21 -0
  134. package/src/mux/project-pane-github-review.ts +271 -0
  135. package/src/mux/render-frame.ts +4 -0
  136. package/src/mux/runtime-app/codex-live-mux-runtime.ts +5191 -0
  137. package/src/mux/task-composer.ts +21 -14
  138. package/src/mux/task-focused-pane.ts +118 -117
  139. package/src/mux/task-screen-keybindings.ts +10 -101
  140. package/src/mux/workspace-rail-model.ts +270 -104
  141. package/src/mux/workspace-rail.ts +45 -22
  142. package/src/pty/session-broker.ts +1 -1
  143. package/{scripts → src/recording}/terminal-recording-gif-lib.ts +2 -2
  144. package/src/services/control-plane.ts +50 -32
  145. package/src/services/conversation-lifecycle.ts +118 -87
  146. package/src/services/conversation-startup-hydration.ts +20 -12
  147. package/src/services/directory-hydration.ts +21 -16
  148. package/src/services/event-persistence.ts +7 -0
  149. package/src/services/left-rail-pointer-handler.ts +329 -0
  150. package/src/services/mux-ui-state-persistence.ts +5 -1
  151. package/src/services/recording.ts +34 -26
  152. package/src/services/runtime-command-menu-agent-tools.ts +1 -1
  153. package/src/services/runtime-control-actions.ts +79 -61
  154. package/src/services/runtime-control-plane-ops.ts +122 -83
  155. package/src/services/runtime-conversation-actions.ts +40 -26
  156. package/src/services/runtime-conversation-activation.ts +73 -46
  157. package/src/services/runtime-conversation-starter.ts +53 -45
  158. package/src/services/runtime-conversation-title-edit.ts +91 -80
  159. package/src/services/runtime-envelope-handler.ts +107 -105
  160. package/src/services/runtime-git-state.ts +42 -29
  161. package/src/services/runtime-layout-resize.ts +3 -1
  162. package/src/services/runtime-left-rail-render.ts +99 -63
  163. package/src/services/runtime-nim-cli-session.ts +438 -0
  164. package/src/services/runtime-nim-session.ts +705 -0
  165. package/src/services/runtime-nim-tool-bridge.ts +141 -0
  166. package/src/services/runtime-observed-event-projection-pipeline.ts +45 -0
  167. package/src/services/runtime-process-wiring.ts +29 -36
  168. package/src/services/runtime-project-pane-github-review-cache.ts +164 -0
  169. package/src/services/runtime-render-flush.ts +63 -70
  170. package/src/services/runtime-render-lifecycle.ts +65 -64
  171. package/src/services/runtime-render-orchestrator.ts +55 -45
  172. package/src/services/runtime-render-pipeline.ts +106 -103
  173. package/src/services/runtime-render-state.ts +62 -49
  174. package/src/services/runtime-repository-actions.ts +97 -72
  175. package/src/services/runtime-right-pane-render.ts +80 -53
  176. package/src/services/runtime-shutdown.ts +38 -35
  177. package/src/services/runtime-stream-subscriptions.ts +35 -27
  178. package/src/services/runtime-task-composer-persistence.ts +71 -59
  179. package/src/services/runtime-task-composer-snapshot.ts +14 -0
  180. package/src/services/runtime-task-editor-actions.ts +46 -29
  181. package/src/services/runtime-task-pane-actions.ts +220 -134
  182. package/src/services/runtime-task-pane-shortcuts.ts +323 -123
  183. package/src/services/runtime-workspace-observed-effect-queue.ts +25 -0
  184. package/src/services/runtime-workspace-observed-events.ts +33 -184
  185. package/src/services/runtime-workspace-observed-transition-policy.ts +228 -0
  186. package/src/services/session-diagnostics-store.ts +217 -0
  187. package/src/services/startup-background-resume.ts +26 -21
  188. package/src/services/startup-orchestrator.ts +16 -13
  189. package/src/services/startup-paint-tracker.ts +29 -21
  190. package/src/services/startup-persisted-conversation-queue.ts +19 -13
  191. package/src/services/startup-settled-gate.ts +25 -15
  192. package/src/services/startup-shutdown.ts +18 -22
  193. package/src/services/startup-state-hydration.ts +44 -34
  194. package/src/services/startup-visibility.ts +12 -4
  195. package/src/services/task-pane-selection-actions.ts +89 -72
  196. package/src/services/task-planning-hydration.ts +24 -18
  197. package/src/services/task-planning-observed-events.ts +50 -52
  198. package/src/services/workspace-observed-events.ts +66 -63
  199. package/src/storage/storage-lifecycle-core.ts +438 -0
  200. package/src/store/control-plane-store-normalize.ts +33 -242
  201. package/src/store/control-plane-store-types.ts +1 -35
  202. package/src/store/control-plane-store.ts +360 -56
  203. package/src/store/event-store.ts +366 -8
  204. package/src/terminal/snapshot-oracle.ts +207 -94
  205. package/src/ui/mux-theme.ts +112 -8
  206. package/src/ui/panes/home-gridfire.ts +40 -31
  207. package/src/ui/panes/home.ts +10 -2
  208. package/src/ui/panes/nim.ts +315 -0
  209. package/src/mux/live-mux/actions-task.ts +0 -115
  210. package/src/mux/live-mux/left-rail-actions.ts +0 -118
  211. package/src/mux/live-mux/left-rail-conversation-click.ts +0 -85
  212. package/src/mux/live-mux/left-rail-pointer.ts +0 -74
  213. package/src/mux/live-mux/task-pane-shortcuts.ts +0 -206
  214. package/src/services/runtime-directory-actions.ts +0 -164
  215. package/src/services/runtime-input-pipeline.ts +0 -50
  216. package/src/services/runtime-input-router.ts +0 -195
  217. package/src/services/runtime-main-pane-input.ts +0 -230
  218. package/src/services/runtime-modal-input.ts +0 -137
  219. package/src/services/runtime-navigation-input.ts +0 -197
  220. package/src/services/runtime-rail-input.ts +0 -279
  221. package/src/services/runtime-task-pane.ts +0 -62
  222. package/src/services/runtime-workspace-actions.ts +0 -158
  223. package/src/ui/conversation-input-forwarder.ts +0 -114
  224. package/src/ui/conversation-selection-input.ts +0 -103
  225. package/src/ui/global-shortcut-input.ts +0 -89
  226. package/src/ui/input.ts +0 -269
  227. package/src/ui/kit.ts +0 -509
  228. package/src/ui/left-nav-input.ts +0 -80
  229. package/src/ui/left-rail-pointer-input.ts +0 -148
  230. package/src/ui/modals/manager.ts +0 -218
  231. package/src/ui/repository-fold-input.ts +0 -91
  232. package/src/ui/surface.ts +0 -224
@@ -0,0 +1,340 @@
1
+ import {
2
+ MUX_GLOBAL_SHORTCUT_ACTION_ORDER,
3
+ TASK_SCREEN_KEYBINDING_ACTION_ORDER,
4
+ type MuxGlobalShortcutAction,
5
+ type TaskScreenKeybindingAction,
6
+ } from './keybinding-registry.ts';
7
+ import type { ResolvedMuxShortcutBindings } from './input-shortcuts.ts';
8
+ import type { ResolvedTaskScreenKeybindings } from './task-screen-keybindings.ts';
9
+
10
+ export const SHORTCUT_CATALOG_ACTION_ID_PREFIX = 'shortcut.binding.';
11
+ const SHOW_KEYBINDINGS_ACTION_ID = 'shortcuts.show';
12
+
13
+ interface KeybindingActionMetadata {
14
+ readonly title: string;
15
+ readonly screenLabel: string;
16
+ readonly sectionLabel: string;
17
+ readonly aliases?: readonly string[];
18
+ readonly keywords?: readonly string[];
19
+ }
20
+
21
+ interface KeybindingCatalogEntry {
22
+ readonly id: string;
23
+ readonly actionId: string;
24
+ readonly title: string;
25
+ readonly aliases: readonly string[];
26
+ readonly keywords: readonly string[];
27
+ readonly detail: string;
28
+ readonly screenLabel: string;
29
+ readonly sectionLabel: string;
30
+ readonly bindingHint: string;
31
+ }
32
+
33
+ export const SHOW_KEYBINDINGS_COMMAND_ACTION = {
34
+ id: SHOW_KEYBINDINGS_ACTION_ID,
35
+ title: 'Show Keybindings',
36
+ aliases: ['shortcuts', 'keybinds', 'keybindings'],
37
+ keywords: ['shortcut', 'shortcuts', 'keybind', 'keybinds', 'keybindings', 'hotkeys'],
38
+ detail: 'browse active keyboard shortcuts',
39
+ } as const;
40
+
41
+ const GLOBAL_ACTION_METADATA: Readonly<Record<MuxGlobalShortcutAction, KeybindingActionMetadata>> =
42
+ {
43
+ 'mux.app.quit': {
44
+ title: 'Quit',
45
+ screenLabel: 'Global',
46
+ sectionLabel: 'App',
47
+ },
48
+ 'mux.app.interrupt-all': {
49
+ title: 'Interrupt All',
50
+ screenLabel: 'Global',
51
+ sectionLabel: 'App',
52
+ },
53
+ 'mux.command-menu.toggle': {
54
+ title: 'Toggle Command Menu',
55
+ screenLabel: 'Global',
56
+ sectionLabel: 'Navigation',
57
+ aliases: ['command palette'],
58
+ },
59
+ 'mux.debug-bar.toggle': {
60
+ title: 'Toggle Debug Bar',
61
+ screenLabel: 'Global',
62
+ sectionLabel: 'Debug',
63
+ },
64
+ 'mux.gateway.profile.toggle': {
65
+ title: 'Toggle Gateway Profile',
66
+ screenLabel: 'Global',
67
+ sectionLabel: 'Gateway',
68
+ },
69
+ 'mux.gateway.status-timeline.toggle': {
70
+ title: 'Toggle Status Timeline',
71
+ screenLabel: 'Global',
72
+ sectionLabel: 'Gateway',
73
+ },
74
+ 'mux.gateway.render-trace.toggle': {
75
+ title: 'Toggle Render Trace',
76
+ screenLabel: 'Global',
77
+ sectionLabel: 'Gateway',
78
+ },
79
+ 'mux.conversation.new': {
80
+ title: 'New Thread',
81
+ screenLabel: 'Global',
82
+ sectionLabel: 'Conversations',
83
+ },
84
+ 'mux.conversation.critique.open-or-create': {
85
+ title: 'Open/Create Critique Thread',
86
+ screenLabel: 'Global',
87
+ sectionLabel: 'Conversations',
88
+ },
89
+ 'mux.conversation.next': {
90
+ title: 'Next Thread',
91
+ screenLabel: 'Global',
92
+ sectionLabel: 'Conversations',
93
+ },
94
+ 'mux.conversation.previous': {
95
+ title: 'Previous Thread',
96
+ screenLabel: 'Global',
97
+ sectionLabel: 'Conversations',
98
+ },
99
+ 'mux.conversation.titles.refresh-all': {
100
+ title: 'Refresh Thread Titles',
101
+ screenLabel: 'Global',
102
+ sectionLabel: 'Conversations',
103
+ },
104
+ 'mux.conversation.interrupt': {
105
+ title: 'Interrupt Active Thread',
106
+ screenLabel: 'Global',
107
+ sectionLabel: 'Conversations',
108
+ },
109
+ 'mux.conversation.archive': {
110
+ title: 'Archive Active Thread',
111
+ screenLabel: 'Global',
112
+ sectionLabel: 'Conversations',
113
+ },
114
+ 'mux.conversation.takeover': {
115
+ title: 'Take Over Active Thread',
116
+ screenLabel: 'Global',
117
+ sectionLabel: 'Conversations',
118
+ },
119
+ 'mux.conversation.delete': {
120
+ title: 'Delete Active Thread',
121
+ screenLabel: 'Global',
122
+ sectionLabel: 'Conversations',
123
+ },
124
+ 'mux.directory.add': {
125
+ title: 'Add Project',
126
+ screenLabel: 'Global',
127
+ sectionLabel: 'Projects',
128
+ },
129
+ 'mux.directory.close': {
130
+ title: 'Close Active Project',
131
+ screenLabel: 'Global',
132
+ sectionLabel: 'Projects',
133
+ },
134
+ };
135
+
136
+ const TASK_ACTION_METADATA: Readonly<Record<TaskScreenKeybindingAction, KeybindingActionMetadata>> =
137
+ {
138
+ 'mux.home.repo.dropdown.toggle': {
139
+ title: 'Toggle Repository Picker',
140
+ screenLabel: 'Home',
141
+ sectionLabel: 'Repositories',
142
+ },
143
+ 'mux.home.repo.next': {
144
+ title: 'Next Repository',
145
+ screenLabel: 'Home',
146
+ sectionLabel: 'Repositories',
147
+ },
148
+ 'mux.home.repo.previous': {
149
+ title: 'Previous Repository',
150
+ screenLabel: 'Home',
151
+ sectionLabel: 'Repositories',
152
+ },
153
+ 'mux.home.task.submit': {
154
+ title: 'Submit Task as Ready',
155
+ screenLabel: 'Home',
156
+ sectionLabel: 'Tasks',
157
+ },
158
+ 'mux.home.task.queue': {
159
+ title: 'Queue Task as Draft',
160
+ screenLabel: 'Home',
161
+ sectionLabel: 'Tasks',
162
+ },
163
+ 'mux.home.task.newline': {
164
+ title: 'Insert Newline',
165
+ screenLabel: 'Home',
166
+ sectionLabel: 'Editor',
167
+ },
168
+ 'mux.home.task.status.ready': {
169
+ title: 'Set Task Ready',
170
+ screenLabel: 'Home',
171
+ sectionLabel: 'Tasks',
172
+ },
173
+ 'mux.home.task.status.draft': {
174
+ title: 'Set Task Draft',
175
+ screenLabel: 'Home',
176
+ sectionLabel: 'Tasks',
177
+ },
178
+ 'mux.home.task.status.complete': {
179
+ title: 'Set Task Complete',
180
+ screenLabel: 'Home',
181
+ sectionLabel: 'Tasks',
182
+ },
183
+ 'mux.home.task.reorder.up': {
184
+ title: 'Move Task Up',
185
+ screenLabel: 'Home',
186
+ sectionLabel: 'Tasks',
187
+ },
188
+ 'mux.home.task.reorder.down': {
189
+ title: 'Move Task Down',
190
+ screenLabel: 'Home',
191
+ sectionLabel: 'Tasks',
192
+ },
193
+ 'mux.home.editor.cursor.left': {
194
+ title: 'Move Cursor Left',
195
+ screenLabel: 'Home',
196
+ sectionLabel: 'Editor',
197
+ },
198
+ 'mux.home.editor.cursor.right': {
199
+ title: 'Move Cursor Right',
200
+ screenLabel: 'Home',
201
+ sectionLabel: 'Editor',
202
+ },
203
+ 'mux.home.editor.cursor.up': {
204
+ title: 'Move Cursor Up',
205
+ screenLabel: 'Home',
206
+ sectionLabel: 'Editor',
207
+ },
208
+ 'mux.home.editor.cursor.down': {
209
+ title: 'Move Cursor Down',
210
+ screenLabel: 'Home',
211
+ sectionLabel: 'Editor',
212
+ },
213
+ 'mux.home.editor.line.start': {
214
+ title: 'Move to Line Start',
215
+ screenLabel: 'Home',
216
+ sectionLabel: 'Editor',
217
+ },
218
+ 'mux.home.editor.line.end': {
219
+ title: 'Move to Line End',
220
+ screenLabel: 'Home',
221
+ sectionLabel: 'Editor',
222
+ },
223
+ 'mux.home.editor.word.left': {
224
+ title: 'Move to Previous Word',
225
+ screenLabel: 'Home',
226
+ sectionLabel: 'Editor',
227
+ },
228
+ 'mux.home.editor.word.right': {
229
+ title: 'Move to Next Word',
230
+ screenLabel: 'Home',
231
+ sectionLabel: 'Editor',
232
+ },
233
+ 'mux.home.editor.delete.backward': {
234
+ title: 'Delete Backward',
235
+ screenLabel: 'Home',
236
+ sectionLabel: 'Editor',
237
+ },
238
+ 'mux.home.editor.delete.forward': {
239
+ title: 'Delete Forward',
240
+ screenLabel: 'Home',
241
+ sectionLabel: 'Editor',
242
+ },
243
+ 'mux.home.editor.delete.word.backward': {
244
+ title: 'Delete Previous Word',
245
+ screenLabel: 'Home',
246
+ sectionLabel: 'Editor',
247
+ },
248
+ 'mux.home.editor.delete.line.start': {
249
+ title: 'Delete to Line Start',
250
+ screenLabel: 'Home',
251
+ sectionLabel: 'Editor',
252
+ },
253
+ 'mux.home.editor.delete.line.end': {
254
+ title: 'Delete to Line End',
255
+ screenLabel: 'Home',
256
+ sectionLabel: 'Editor',
257
+ },
258
+ };
259
+
260
+ function bindingHint(bindings: readonly string[]): string {
261
+ return bindings.length === 0 ? '(unbound)' : bindings.join(', ');
262
+ }
263
+
264
+ function uniqueLower(values: readonly string[]): readonly string[] {
265
+ const seen = new Set<string>();
266
+ const out: string[] = [];
267
+ for (const value of values) {
268
+ const normalized = value.trim().toLowerCase();
269
+ if (normalized.length === 0 || seen.has(normalized)) {
270
+ continue;
271
+ }
272
+ seen.add(normalized);
273
+ out.push(normalized);
274
+ }
275
+ return out;
276
+ }
277
+
278
+ function keywordsFromActionId(actionId: string): readonly string[] {
279
+ return actionId
280
+ .split('.')
281
+ .map((part) => part.trim().toLowerCase())
282
+ .filter((part) => part.length > 0 && part !== 'mux' && part !== 'home');
283
+ }
284
+
285
+ function toCatalogEntry(
286
+ actionId: string,
287
+ bindings: readonly string[],
288
+ metadata: KeybindingActionMetadata,
289
+ ): KeybindingCatalogEntry {
290
+ const hint = bindingHint(bindings);
291
+ const aliases = uniqueLower([
292
+ actionId,
293
+ metadata.title,
294
+ metadata.sectionLabel,
295
+ ...(metadata.aliases ?? []),
296
+ ...bindings,
297
+ ]);
298
+ const keywords = uniqueLower([
299
+ 'shortcut',
300
+ 'keybinding',
301
+ 'keybind',
302
+ metadata.screenLabel,
303
+ metadata.sectionLabel,
304
+ ...(metadata.keywords ?? []),
305
+ ...keywordsFromActionId(actionId),
306
+ ...bindings,
307
+ ]);
308
+ return {
309
+ id: `${SHORTCUT_CATALOG_ACTION_ID_PREFIX}${actionId}`,
310
+ actionId,
311
+ title: metadata.title,
312
+ aliases,
313
+ keywords,
314
+ detail: actionId,
315
+ screenLabel: metadata.screenLabel,
316
+ sectionLabel: metadata.sectionLabel,
317
+ bindingHint: hint,
318
+ };
319
+ }
320
+
321
+ export function buildKeybindingCatalogEntries(input: {
322
+ readonly globalBindings: ResolvedMuxShortcutBindings;
323
+ readonly taskScreenKeybindings: ResolvedTaskScreenKeybindings;
324
+ }): readonly KeybindingCatalogEntry[] {
325
+ const global = MUX_GLOBAL_SHORTCUT_ACTION_ORDER.map((actionId) =>
326
+ toCatalogEntry(
327
+ actionId,
328
+ input.globalBindings.rawByAction[actionId],
329
+ GLOBAL_ACTION_METADATA[actionId],
330
+ ),
331
+ );
332
+ const taskScreen = TASK_SCREEN_KEYBINDING_ACTION_ORDER.map((actionId) =>
333
+ toCatalogEntry(
334
+ actionId,
335
+ input.taskScreenKeybindings.rawByAction[actionId],
336
+ TASK_ACTION_METADATA[actionId],
337
+ ),
338
+ );
339
+ return [...global, ...taskScreen];
340
+ }
@@ -0,0 +1,103 @@
1
+ export const MUX_GLOBAL_SHORTCUT_ACTION_ORDER = [
2
+ 'mux.app.quit',
3
+ 'mux.app.interrupt-all',
4
+ 'mux.command-menu.toggle',
5
+ 'mux.debug-bar.toggle',
6
+ 'mux.gateway.profile.toggle',
7
+ 'mux.gateway.status-timeline.toggle',
8
+ 'mux.gateway.render-trace.toggle',
9
+ 'mux.conversation.new',
10
+ 'mux.conversation.critique.open-or-create',
11
+ 'mux.conversation.next',
12
+ 'mux.conversation.previous',
13
+ 'mux.conversation.titles.refresh-all',
14
+ 'mux.conversation.interrupt',
15
+ 'mux.conversation.archive',
16
+ 'mux.conversation.takeover',
17
+ 'mux.conversation.delete',
18
+ 'mux.directory.add',
19
+ 'mux.directory.close',
20
+ ] as const;
21
+
22
+ export type MuxGlobalShortcutAction = (typeof MUX_GLOBAL_SHORTCUT_ACTION_ORDER)[number];
23
+
24
+ export const DEFAULT_MUX_SHORTCUT_BINDINGS_RAW: Readonly<
25
+ Record<MuxGlobalShortcutAction, readonly string[]>
26
+ > = {
27
+ 'mux.app.quit': [],
28
+ 'mux.app.interrupt-all': ['ctrl+c'],
29
+ 'mux.command-menu.toggle': ['ctrl+p', 'cmd+p'],
30
+ 'mux.debug-bar.toggle': [],
31
+ 'mux.gateway.profile.toggle': ['ctrl+shift+p'],
32
+ 'mux.gateway.status-timeline.toggle': ['alt+r'],
33
+ 'mux.gateway.render-trace.toggle': ['ctrl+]'],
34
+ 'mux.conversation.new': ['ctrl+t'],
35
+ 'mux.conversation.critique.open-or-create': ['ctrl+g'],
36
+ 'mux.conversation.next': ['ctrl+j'],
37
+ 'mux.conversation.previous': ['ctrl+k'],
38
+ 'mux.conversation.titles.refresh-all': ['ctrl+r'],
39
+ 'mux.conversation.interrupt': [],
40
+ 'mux.conversation.archive': [],
41
+ 'mux.conversation.takeover': ['ctrl+l'],
42
+ 'mux.conversation.delete': ['ctrl+x'],
43
+ 'mux.directory.add': ['ctrl+o'],
44
+ 'mux.directory.close': ['ctrl+w'],
45
+ };
46
+
47
+ export const TASK_SCREEN_KEYBINDING_ACTION_ORDER = [
48
+ 'mux.home.repo.dropdown.toggle',
49
+ 'mux.home.repo.next',
50
+ 'mux.home.repo.previous',
51
+ 'mux.home.task.submit',
52
+ 'mux.home.task.queue',
53
+ 'mux.home.task.newline',
54
+ 'mux.home.task.status.ready',
55
+ 'mux.home.task.status.draft',
56
+ 'mux.home.task.status.complete',
57
+ 'mux.home.task.reorder.up',
58
+ 'mux.home.task.reorder.down',
59
+ 'mux.home.editor.cursor.left',
60
+ 'mux.home.editor.cursor.right',
61
+ 'mux.home.editor.cursor.up',
62
+ 'mux.home.editor.cursor.down',
63
+ 'mux.home.editor.line.start',
64
+ 'mux.home.editor.line.end',
65
+ 'mux.home.editor.word.left',
66
+ 'mux.home.editor.word.right',
67
+ 'mux.home.editor.delete.backward',
68
+ 'mux.home.editor.delete.forward',
69
+ 'mux.home.editor.delete.word.backward',
70
+ 'mux.home.editor.delete.line.start',
71
+ 'mux.home.editor.delete.line.end',
72
+ ] as const;
73
+
74
+ export type TaskScreenKeybindingAction = (typeof TASK_SCREEN_KEYBINDING_ACTION_ORDER)[number];
75
+
76
+ export const DEFAULT_TASK_SCREEN_KEYBINDINGS_RAW: Readonly<
77
+ Record<TaskScreenKeybindingAction, readonly string[]>
78
+ > = {
79
+ 'mux.home.repo.dropdown.toggle': ['alt+g'],
80
+ 'mux.home.repo.next': ['ctrl+n'],
81
+ 'mux.home.repo.previous': ['ctrl+p'],
82
+ 'mux.home.task.submit': ['enter'],
83
+ 'mux.home.task.queue': ['tab'],
84
+ 'mux.home.task.newline': ['shift+enter'],
85
+ 'mux.home.task.status.ready': ['alt+r'],
86
+ 'mux.home.task.status.draft': ['alt+d'],
87
+ 'mux.home.task.status.complete': ['alt+c'],
88
+ 'mux.home.task.reorder.up': ['ctrl+up'],
89
+ 'mux.home.task.reorder.down': ['ctrl+down'],
90
+ 'mux.home.editor.cursor.left': ['left', 'ctrl+b'],
91
+ 'mux.home.editor.cursor.right': ['right', 'ctrl+f'],
92
+ 'mux.home.editor.cursor.up': ['up'],
93
+ 'mux.home.editor.cursor.down': ['down'],
94
+ 'mux.home.editor.line.start': ['ctrl+a', 'home'],
95
+ 'mux.home.editor.line.end': ['ctrl+e', 'end'],
96
+ 'mux.home.editor.word.left': ['alt+b'],
97
+ 'mux.home.editor.word.right': ['alt+f'],
98
+ 'mux.home.editor.delete.backward': ['backspace'],
99
+ 'mux.home.editor.delete.forward': ['delete'],
100
+ 'mux.home.editor.delete.word.backward': ['ctrl+w', 'alt+backspace'],
101
+ 'mux.home.editor.delete.line.start': ['ctrl+u'],
102
+ 'mux.home.editor.delete.line.end': ['ctrl+k'],
103
+ };