@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,111 @@
1
+ import type { ReleaseNotesPrompt } from './release-notes.ts';
2
+ import { RELEASE_NOTES_UPDATE_ACTION_ROW_OFFSET } from './modal-overlays.ts';
3
+
4
+ interface HandleReleaseNotesModalInputOptions {
5
+ readonly input: Buffer;
6
+ readonly prompt: ReleaseNotesPrompt | null;
7
+ readonly isQuitShortcut: (input: Buffer) => boolean;
8
+ readonly dismissOnOutsideClick: (
9
+ input: Buffer,
10
+ dismiss: () => void,
11
+ onInsidePointerPress?: (col: number, row: number) => boolean,
12
+ ) => boolean;
13
+ readonly buildReleaseNotesModalOverlay: () => { top: number } | null;
14
+ readonly setPrompt: (next: ReleaseNotesPrompt | null) => void;
15
+ readonly markDirty: () => void;
16
+ readonly onDismiss: (latestTag: string) => void;
17
+ readonly onNeverShowAgain: (latestTag: string) => void;
18
+ readonly onOpenLatest: (prompt: ReleaseNotesPrompt) => void;
19
+ readonly onUpdate: () => void;
20
+ }
21
+
22
+ function byteMatches(inputByte: number, lower: string): boolean {
23
+ const ascii = lower.charCodeAt(0);
24
+ return inputByte === ascii || inputByte === ascii - 32;
25
+ }
26
+
27
+ export function handleReleaseNotesModalInput(
28
+ options: HandleReleaseNotesModalInputOptions,
29
+ ): boolean {
30
+ const {
31
+ input,
32
+ prompt,
33
+ isQuitShortcut,
34
+ dismissOnOutsideClick,
35
+ buildReleaseNotesModalOverlay,
36
+ setPrompt,
37
+ markDirty,
38
+ onDismiss,
39
+ onNeverShowAgain,
40
+ onOpenLatest,
41
+ onUpdate,
42
+ } = options;
43
+ if (prompt === null) {
44
+ return false;
45
+ }
46
+ if (input.length === 1 && input[0] === 0x03) {
47
+ return false;
48
+ }
49
+ if (isQuitShortcut(input)) {
50
+ setPrompt(null);
51
+ onDismiss(prompt.latestTag);
52
+ markDirty();
53
+ return true;
54
+ }
55
+ if (
56
+ input.includes(0x3c) &&
57
+ dismissOnOutsideClick(
58
+ input,
59
+ () => {
60
+ setPrompt(null);
61
+ onDismiss(prompt.latestTag);
62
+ markDirty();
63
+ },
64
+ (_col, row) => {
65
+ const overlay = buildReleaseNotesModalOverlay();
66
+ if (overlay === null) {
67
+ return false;
68
+ }
69
+ if (row - 1 !== overlay.top + RELEASE_NOTES_UPDATE_ACTION_ROW_OFFSET) {
70
+ return false;
71
+ }
72
+ setPrompt(null);
73
+ onDismiss(prompt.latestTag);
74
+ onUpdate();
75
+ markDirty();
76
+ return true;
77
+ },
78
+ )
79
+ ) {
80
+ return true;
81
+ }
82
+
83
+ for (const byte of input) {
84
+ if (byte === 0x0d || byte === 0x0a) {
85
+ setPrompt(null);
86
+ onDismiss(prompt.latestTag);
87
+ markDirty();
88
+ return true;
89
+ }
90
+ if (byteMatches(byte, 'n')) {
91
+ setPrompt(null);
92
+ onNeverShowAgain(prompt.latestTag);
93
+ markDirty();
94
+ return true;
95
+ }
96
+ if (byteMatches(byte, 'u')) {
97
+ setPrompt(null);
98
+ onDismiss(prompt.latestTag);
99
+ onUpdate();
100
+ markDirty();
101
+ return true;
102
+ }
103
+ if (byteMatches(byte, 'o')) {
104
+ onOpenLatest(prompt);
105
+ markDirty();
106
+ return true;
107
+ }
108
+ }
109
+
110
+ return true;
111
+ }
@@ -12,9 +12,10 @@ interface TaskEditorPromptState extends TaskEditorPromptInputState {
12
12
  interface TaskEditorSubmitPayload {
13
13
  mode: 'create' | 'edit';
14
14
  taskId: string | null;
15
- repositoryId: string;
16
- title: string;
17
- description: string;
15
+ repositoryId: string | null;
16
+ projectId?: string | null;
17
+ title: string | null;
18
+ body: string;
18
19
  commandLabel: string;
19
20
  }
20
21
 
@@ -32,6 +33,8 @@ interface HandleTaskEditorPromptInputOptions {
32
33
  dismissOnOutsideClick: (input: Buffer, dismiss: () => void) => boolean;
33
34
  }
34
35
 
36
+ const MOUSE_EVENT_PREFIX = Buffer.from('\u001b[<', 'utf8');
37
+
35
38
  export function handleTaskEditorPromptInput(
36
39
  options: HandleTaskEditorPromptInputOptions,
37
40
  ): HandleTaskEditorPromptInputResult {
@@ -57,7 +60,9 @@ export function handleTaskEditorPromptInput(
57
60
  }
58
61
 
59
62
  let dismissed = false;
63
+ const maybeMouseSequence = input.includes(MOUSE_EVENT_PREFIX);
60
64
  if (
65
+ maybeMouseSequence &&
61
66
  dismissOnOutsideClick(input, () => {
62
67
  dismissed = true;
63
68
  })
@@ -77,13 +82,13 @@ export function handleTaskEditorPromptInput(
77
82
 
78
83
  const reduced = reduceTaskEditorPromptInput(prompt, input);
79
84
  const nextTitle = reduced.title;
80
- const nextDescription = reduced.description;
85
+ const nextBody = reduced.body;
81
86
  const nextFieldIndex = reduced.fieldIndex;
82
87
  const nextRepositoryIndex = reduced.repositoryIndex;
83
88
  const submit = reduced.submit;
84
89
  const changed =
85
90
  nextTitle !== prompt.title ||
86
- nextDescription !== prompt.description ||
91
+ nextBody !== prompt.body ||
87
92
  nextFieldIndex !== prompt.fieldIndex ||
88
93
  nextRepositoryIndex !== prompt.repositoryIndex;
89
94
 
@@ -91,7 +96,7 @@ export function handleTaskEditorPromptInput(
91
96
  ? {
92
97
  ...prompt,
93
98
  title: nextTitle,
94
- description: nextDescription,
99
+ body: nextBody,
95
100
  fieldIndex: nextFieldIndex,
96
101
  repositoryIndex: nextRepositoryIndex,
97
102
  error: null,
@@ -113,13 +118,12 @@ export function handleTaskEditorPromptInput(
113
118
  }
114
119
 
115
120
  const repositoryId = prompt.repositoryIds[nextRepositoryIndex] ?? null;
116
- const title = nextTitle.trim();
117
- if (title.length === 0) {
121
+ if (nextBody.trim().length === 0) {
118
122
  return {
119
123
  handled: true,
120
124
  nextPrompt: {
121
125
  ...changedPrompt,
122
- error: 'title required',
126
+ error: 'task body required',
123
127
  },
124
128
  markDirty: true,
125
129
  };
@@ -148,8 +152,9 @@ export function handleTaskEditorPromptInput(
148
152
  mode: prompt.mode,
149
153
  taskId: prompt.taskId,
150
154
  repositoryId,
151
- title,
152
- description: nextDescription,
155
+ projectId: null,
156
+ title: nextTitle.trim().length === 0 ? null : nextTitle.trim(),
157
+ body: nextBody,
153
158
  commandLabel: prompt.mode === 'create' ? 'tasks-create' : 'tasks-edit',
154
159
  },
155
160
  };
@@ -45,9 +45,10 @@ export function handleSeparatorPointerPress(options: HandleSeparatorPointerPress
45
45
  interface HandleMainPaneWheelInputOptions {
46
46
  target: string;
47
47
  wheelDelta: number | null;
48
- mainPaneMode: 'conversation' | 'project' | 'home';
48
+ mainPaneMode: 'conversation' | 'project' | 'home' | 'nim';
49
49
  onProjectWheel: (delta: number) => void;
50
50
  onHomeWheel: (delta: number) => void;
51
+ onNimWheel: (delta: number) => void;
51
52
  onConversationWheel: (delta: number) => void;
52
53
  markDirty: () => void;
53
54
  }
@@ -60,6 +61,8 @@ export function handleMainPaneWheelInput(options: HandleMainPaneWheelInputOption
60
61
  options.onProjectWheel(options.wheelDelta);
61
62
  } else if (options.mainPaneMode === 'home') {
62
63
  options.onHomeWheel(options.wheelDelta);
64
+ } else if (options.mainPaneMode === 'nim') {
65
+ options.onNimWheel(options.wheelDelta);
63
66
  } else {
64
67
  options.onConversationWheel(options.wheelDelta);
65
68
  }
@@ -77,7 +80,7 @@ interface HomePaneDragState {
77
80
 
78
81
  interface HandleHomePaneDragMoveOptions {
79
82
  homePaneDragState: HomePaneDragState | null;
80
- mainPaneMode: 'conversation' | 'project' | 'home';
83
+ mainPaneMode: 'conversation' | 'project' | 'home' | 'nim';
81
84
  target: string;
82
85
  isSelectionDrag: boolean;
83
86
  hasAltModifier: boolean;
@@ -14,6 +14,7 @@ interface HandleProjectPaneActionClickOptions<TSnapshot extends { directoryId: s
14
14
  ) => string | null;
15
15
  openNewThreadPrompt: (directoryId: string) => void;
16
16
  queueCloseDirectory: (directoryId: string) => void;
17
+ handleProjectPaneAction?: (action: string, directoryId: string) => boolean;
17
18
  markDirty: () => void;
18
19
  }
19
20
 
@@ -40,5 +41,12 @@ export function handleProjectPaneActionClick<TSnapshot extends { directoryId: st
40
41
  options.markDirty();
41
42
  return true;
42
43
  }
44
+ if (
45
+ action !== null &&
46
+ options.handleProjectPaneAction?.(action, options.snapshot.directoryId) === true
47
+ ) {
48
+ options.markDirty();
49
+ return true;
50
+ }
43
51
  return false;
44
52
  }
@@ -1,15 +1,13 @@
1
1
  import { basename } from 'node:path';
2
2
  import type { ConversationRailSessionSummary } from '../conversation-rail.ts';
3
- import { firstShortcutText } from '../input-shortcuts.ts';
4
- import type { resolveMuxShortcutBindings } from '../input-shortcuts.ts';
5
3
  import { buildWorkspaceRailViewRows } from '../workspace-rail-model.ts';
6
4
  import { renderWorkspaceRailAnsiRows } from '../workspace-rail.ts';
5
+ import type { ProjectPaneGitHubReviewSummary } from '../project-pane-github-review.ts';
7
6
  import type {
8
7
  StreamSessionController,
9
8
  StreamSessionStatusModel,
10
9
  } from '../../control-plane/stream-protocol.ts';
11
10
 
12
- type ResolvedMuxShortcutBindings = ReturnType<typeof resolveMuxShortcutBindings>;
13
11
  type WorkspaceRailModel = Parameters<typeof renderWorkspaceRailAnsiRows>[0];
14
12
 
15
13
  interface GitSummary {
@@ -71,12 +69,20 @@ interface BuildRailModelArgs {
71
69
  readonly projectSelectionEnabled: boolean;
72
70
  readonly repositorySelectionEnabled: boolean;
73
71
  readonly homeSelectionEnabled: boolean;
72
+ readonly nimSelectionEnabled?: boolean;
73
+ readonly tasksSelectionEnabled?: boolean;
74
+ readonly showNimEntry?: boolean;
75
+ readonly showTasksEntry?: boolean;
76
+ readonly showGitHubIntegration?: boolean;
77
+ readonly visibleGitHubDirectoryIds?: ReadonlySet<string>;
78
+ readonly expandedGitHubDirectoryIds?: ReadonlySet<string>;
79
+ readonly githubReviewByDirectoryId?: ReadonlyMap<string, ProjectPaneGitHubReviewSummary>;
80
+ readonly githubSelectionEnabled?: boolean;
81
+ readonly activeGitHubProjectId?: string | null;
74
82
  readonly repositoriesCollapsed: boolean;
75
83
  readonly collapsedRepositoryGroupIds: ReadonlySet<string>;
76
- readonly shortcutsCollapsed: boolean;
77
84
  readonly gitSummaryByDirectoryId: ReadonlyMap<string, GitSummary>;
78
85
  readonly processUsageBySessionId: ReadonlyMap<string, MuxRailProcessUsageSample>;
79
- readonly shortcutBindings: ResolvedMuxShortcutBindings;
80
86
  readonly loadingGitSummary: GitSummary;
81
87
  }
82
88
 
@@ -87,29 +93,6 @@ interface BuildRailRowsArgs extends BuildRailModelArgs {
87
93
  };
88
94
  }
89
95
 
90
- function shortcutHintText(bindings: ResolvedMuxShortcutBindings): string {
91
- const newConversation = firstShortcutText(bindings, 'mux.conversation.new') || 'ctrl+t';
92
- const critiqueConversation =
93
- firstShortcutText(bindings, 'mux.conversation.critique.open-or-create') || 'ctrl+g';
94
- const deleteConversation = firstShortcutText(bindings, 'mux.conversation.delete') || 'ctrl+x';
95
- const takeoverConversation = firstShortcutText(bindings, 'mux.conversation.takeover') || 'ctrl+l';
96
- const addProject = firstShortcutText(bindings, 'mux.directory.add') || 'ctrl+o';
97
- const closeProject = firstShortcutText(bindings, 'mux.directory.close') || 'ctrl+w';
98
- const next = firstShortcutText(bindings, 'mux.conversation.next') || 'ctrl+j';
99
- const previous = firstShortcutText(bindings, 'mux.conversation.previous') || 'ctrl+k';
100
- const interruptConversation = firstShortcutText(bindings, 'mux.conversation.interrupt');
101
- const quit = firstShortcutText(bindings, 'mux.app.interrupt-all') || 'ctrl+c';
102
- const commandMenu = firstShortcutText(bindings, 'mux.command-menu.toggle') || 'ctrl+p';
103
- const profile = firstShortcutText(bindings, 'mux.gateway.profile.toggle') || 'ctrl+shift+p';
104
- const statusTimeline =
105
- firstShortcutText(bindings, 'mux.gateway.status-timeline.toggle') || 'alt+r';
106
- const renderTrace = firstShortcutText(bindings, 'mux.gateway.render-trace.toggle') || 'ctrl+]';
107
- const switchHint = next === previous ? next : `${next}/${previous}`;
108
- const interruptHint =
109
- interruptConversation.length === 0 ? '' : ` ${interruptConversation} interrupt`;
110
- return `${commandMenu} menu ${newConversation} new ${critiqueConversation} critique ${deleteConversation} archive ${takeoverConversation} takeover ${addProject}/${closeProject} projects ${switchHint} switch nav ${profile} profile ${statusTimeline} status ${renderTrace} render${interruptHint} ←/→ collapse/expand ${quit} quit`;
111
- }
112
-
113
96
  function conversationSummary(
114
97
  conversation: MuxRailConversationRecord,
115
98
  ): ConversationRailSessionSummary {
@@ -219,14 +202,34 @@ export function buildRailModel(args: BuildRailModelArgs): WorkspaceRailModel {
219
202
  activeRepositoryId: args.activeRepositoryId,
220
203
  activeConversationId: args.activeConversationId,
221
204
  showTaskPlanningUi: true,
205
+ showNimEntry: args.showNimEntry ?? true,
206
+ showTasksEntry: args.showTasksEntry ?? true,
207
+ showGitHubIntegration: args.showGitHubIntegration ?? false,
208
+ ...(args.visibleGitHubDirectoryIds === undefined
209
+ ? {}
210
+ : {
211
+ visibleGitHubDirectoryKeys: [...args.visibleGitHubDirectoryIds],
212
+ }),
213
+ ...(args.expandedGitHubDirectoryIds === undefined
214
+ ? {}
215
+ : {
216
+ expandedGitHubDirectoryKeys: [...args.expandedGitHubDirectoryIds],
217
+ }),
218
+ ...(args.githubReviewByDirectoryId === undefined
219
+ ? {}
220
+ : {
221
+ githubReviewByDirectoryKey: args.githubReviewByDirectoryId,
222
+ }),
222
223
  projectSelectionEnabled: args.projectSelectionEnabled,
224
+ githubSelectionEnabled: args.githubSelectionEnabled ?? false,
225
+ activeGitHubProjectId: args.activeGitHubProjectId ?? null,
223
226
  repositorySelectionEnabled: args.repositorySelectionEnabled,
224
227
  homeSelectionEnabled: args.homeSelectionEnabled,
228
+ nimSelectionEnabled: args.nimSelectionEnabled ?? false,
229
+ tasksSelectionEnabled: args.tasksSelectionEnabled ?? false,
225
230
  repositoriesCollapsed: args.repositoriesCollapsed,
226
231
  collapsedRepositoryGroupIds: [...args.collapsedRepositoryGroupIds],
227
232
  processes: [],
228
- shortcutHint: shortcutHintText(args.shortcutBindings),
229
- shortcutsCollapsed: args.shortcutsCollapsed,
230
233
  nowMs: Date.now(),
231
234
  };
232
235
  }