@brimveyn/aimux 1.20.4 → 1.22.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 (233) hide show
  1. package/README.md +27 -25
  2. package/package.json +2 -2
  3. package/src/app-runtime/auto-commit-driver.ts +22 -27
  4. package/src/app-runtime/backend-attach-runtime.ts +43 -36
  5. package/src/app-runtime/backend-runtime-events.ts +107 -88
  6. package/src/app-runtime/editor-actions.ts +201 -0
  7. package/src/app-runtime/git-actions.ts +197 -0
  8. package/src/app-runtime/measure-ref.ts +19 -0
  9. package/src/app-runtime/navigation-actions.ts +213 -0
  10. package/src/app-runtime/project-actions.ts +188 -0
  11. package/src/app-runtime/prompt-injection.ts +123 -0
  12. package/src/app-runtime/selection.ts +29 -0
  13. package/src/app-runtime/settings-actions.ts +156 -0
  14. package/src/app-runtime/setup-actions.ts +237 -0
  15. package/src/app-runtime/side-effect-context.ts +33 -0
  16. package/src/app-runtime/side-effects.ts +305 -1589
  17. package/src/app-runtime/snippet-actions.ts +3 -2
  18. package/src/app-runtime/split-drag-controller.ts +4 -8
  19. package/src/app-runtime/tab-actions.ts +218 -0
  20. package/src/app-runtime/tab-runtime-timeouts.ts +1 -1
  21. package/src/app-runtime/use-auto-commit-driver.ts +14 -13
  22. package/src/app-runtime/use-backend-runtime.ts +12 -11
  23. package/src/app-runtime/use-directory-search.ts +5 -4
  24. package/src/app-runtime/use-mouse-handlers.ts +28 -42
  25. package/src/app-runtime/{use-workspace-autosave.ts → use-project-autosave.ts} +4 -4
  26. package/src/app-runtime/use-renderer-bindings.ts +2 -1
  27. package/src/app-runtime/use-setup-runner.ts +96 -0
  28. package/src/app-runtime/use-terminal-resize.ts +28 -33
  29. package/src/app-runtime/workspace-actions.ts +366 -0
  30. package/src/app-runtime/workspace-activity.ts +138 -0
  31. package/src/app-runtime/workspace-naming.ts +90 -0
  32. package/src/app.tsx +102 -85
  33. package/src/assets/sounds/bell.wav +0 -0
  34. package/src/assets/sounds/ding.wav +0 -0
  35. package/src/assets/sounds/plane.wav +0 -0
  36. package/src/cli/client/daemon-client.ts +2 -2
  37. package/src/cli/client/project-resolver.ts +112 -0
  38. package/src/cli/commands/project/close.ts +32 -0
  39. package/src/cli/commands/project/create.ts +93 -0
  40. package/src/cli/commands/project/list.ts +25 -0
  41. package/src/cli/commands/project/show.ts +32 -0
  42. package/src/cli/commands/{workspace → project}/switch.ts +19 -19
  43. package/src/cli/commands/tab/await.ts +2 -2
  44. package/src/cli/commands/tab/close.ts +3 -3
  45. package/src/cli/commands/tab/create.ts +74 -73
  46. package/src/cli/commands/tab/focus.ts +3 -3
  47. package/src/cli/commands/tab/list.ts +6 -6
  48. package/src/cli/commands/tab/run.ts +2 -2
  49. package/src/cli/commands/tab/send.ts +2 -2
  50. package/src/cli/commands/tab/snapshot.ts +2 -2
  51. package/src/cli/commands/tab/tail.ts +2 -2
  52. package/src/cli/commands/tab/wait.ts +2 -2
  53. package/src/cli/commands/worker/await.ts +3 -3
  54. package/src/cli/commands/worker/doctor.ts +30 -30
  55. package/src/cli/commands/worker/list.ts +15 -15
  56. package/src/cli/commands/worker/prompt.ts +3 -3
  57. package/src/cli/commands/worker/run.ts +23 -23
  58. package/src/cli/commands/worker/shared.ts +53 -59
  59. package/src/cli/commands/worker/stop.ts +29 -29
  60. package/src/cli/commands/worker/submit.ts +3 -3
  61. package/src/cli/commands/{worktree → workspace}/create-core.ts +26 -26
  62. package/src/cli/commands/workspace/create.ts +30 -68
  63. package/src/cli/commands/workspace/list.ts +52 -9
  64. package/src/cli/commands/workspace/remove.ts +81 -0
  65. package/src/cli/completion/plan.ts +2 -2
  66. package/src/cli/completion/sources.ts +18 -18
  67. package/src/cli/context.ts +13 -13
  68. package/src/cli/flags.ts +30 -10
  69. package/src/cli/index.ts +26 -16
  70. package/src/cli/output.ts +1 -1
  71. package/src/cli/registry.ts +12 -12
  72. package/src/config/loader.ts +16 -5
  73. package/src/config.ts +149 -101
  74. package/src/daemon/catalog-writer.ts +67 -67
  75. package/src/daemon/daemon.ts +205 -201
  76. package/src/daemon/session-manager.ts +39 -39
  77. package/src/daemon/session-registry.ts +14 -14
  78. package/src/git/divergence.ts +33 -2
  79. package/src/git/git-poller.ts +1 -1
  80. package/src/git/git-status.ts +2 -2
  81. package/src/git/{move-worktree.ts → move-workspace.ts} +5 -5
  82. package/src/git/pr-merge.ts +64 -0
  83. package/src/git/pr-status-poller.ts +57 -0
  84. package/src/git/pr-status.ts +227 -0
  85. package/src/git/repo-discovery.ts +1 -1
  86. package/src/git/use-repo-discovery.ts +1 -1
  87. package/src/git/workspace-branch-poller.ts +54 -0
  88. package/src/git/workspace-divergence-poller.ts +65 -0
  89. package/src/git/worktree.ts +29 -0
  90. package/src/index.tsx +11 -6
  91. package/src/input/keymap/help-entries.ts +7 -5
  92. package/src/input/modes/bridge.ts +13 -12
  93. package/src/input/modes/handlers/shared.ts +1 -1
  94. package/src/input/modes/transitions.ts +34 -21
  95. package/src/input/modes/types.ts +48 -31
  96. package/src/ipc/manager-protocol.ts +44 -44
  97. package/src/ipc/protocol.ts +192 -166
  98. package/src/platform/open-url.ts +39 -0
  99. package/src/platform/play-sound.ts +184 -0
  100. package/src/platform/project-search.ts +8 -8
  101. package/src/platform/worktree-paths.ts +8 -6
  102. package/src/pty/assistant-question-extractor.ts +1 -1
  103. package/src/pty/assistant-status-detection-loop.ts +79 -55
  104. package/src/pty/assistant-status-detector.ts +40 -15
  105. package/src/pty/command-registry.ts +81 -1
  106. package/src/restart-terminal-manager.ts +1 -1
  107. package/src/services/ai-usage/provider.ts +9 -2
  108. package/src/services/ai-usage/spawn.ts +3 -1
  109. package/src/session-backend/bootstrap.ts +4 -4
  110. package/src/session-backend/local-session-backend.ts +78 -75
  111. package/src/session-backend/remote-session-backend.ts +64 -52
  112. package/src/session-backend/types.ts +43 -32
  113. package/src/settings/live.ts +90 -0
  114. package/src/settings/search.ts +43 -0
  115. package/src/settings/sections/about.ts +49 -0
  116. package/src/settings/sections/appearance.ts +54 -0
  117. package/src/settings/sections/automation.ts +105 -0
  118. package/src/settings/sections/commands.ts +72 -0
  119. package/src/settings/sections/editor.ts +56 -0
  120. package/src/settings/sections/experimental.ts +57 -0
  121. package/src/settings/sections/git.ts +112 -0
  122. package/src/settings/sections/index.ts +95 -0
  123. package/src/settings/sections/integrations.ts +23 -0
  124. package/src/settings/sections/layout.ts +72 -0
  125. package/src/settings/sections/notifications.ts +99 -0
  126. package/src/settings/sections/setup.ts +54 -0
  127. package/src/settings/sections/status-bar.ts +63 -0
  128. package/src/settings/settings-store.ts +207 -0
  129. package/src/settings/types.ts +108 -0
  130. package/src/snippets/run-shell-var.ts +48 -22
  131. package/src/state/actions.ts +342 -0
  132. package/src/state/app-store.ts +2 -1
  133. package/src/state/bars.ts +75 -0
  134. package/src/state/dispatch-ref.ts +1 -1
  135. package/src/state/git-pane-sizing.ts +0 -9
  136. package/src/state/layout-resize.ts +2 -2
  137. package/src/state/pr-status-store.ts +39 -0
  138. package/src/state/project-catalog.ts +275 -0
  139. package/src/state/project-data.ts +143 -0
  140. package/src/state/{session-persistence.ts → project-persistence.ts} +77 -73
  141. package/src/state/project-save.ts +46 -0
  142. package/src/state/project-workspaces.ts +392 -0
  143. package/src/state/reducers/auto-commit-state.ts +11 -10
  144. package/src/state/reducers/git-commit-modal-state.ts +122 -0
  145. package/src/state/reducers/git-mode-state.ts +1 -1
  146. package/src/state/reducers/git-panel-state.ts +10 -53
  147. package/src/state/reducers/modal-state.ts +290 -681
  148. package/src/state/reducers/multi-repo-state.ts +3 -6
  149. package/src/state/reducers/project-state.ts +312 -0
  150. package/src/state/reducers/settings-state.ts +80 -0
  151. package/src/state/reducers/tab-state.ts +107 -60
  152. package/src/state/reducers/ui-state.ts +86 -15
  153. package/src/state/selectors.ts +49 -37
  154. package/src/state/store.ts +83 -57
  155. package/src/state/types.ts +261 -417
  156. package/src/state/validation.ts +14 -12
  157. package/src/terminal-manager/manager-client.ts +32 -32
  158. package/src/terminal-manager/terminal-manager.ts +24 -24
  159. package/src/ui/components/git/diff-renderer/pierre-diff.tsx +2 -1
  160. package/src/ui/components/git/git-panel.tsx +2 -2
  161. package/src/ui/components/git/git-view.tsx +12 -12
  162. package/src/ui/components/git/image-diff/image-diff-view.tsx +1 -1
  163. package/src/ui/components/git/pane/git-pane-header.tsx +105 -39
  164. package/src/ui/components/git/pane/git-pane-widget.tsx +37 -23
  165. package/src/ui/components/git/pane/pr-checks-panel.tsx +197 -0
  166. package/src/ui/components/git/pane/pr-state-row.tsx +103 -0
  167. package/src/ui/components/layout/bar.tsx +191 -0
  168. package/src/ui/components/layout/sidebar/project-list.tsx +444 -0
  169. package/src/ui/components/layout/sidebar/tab-item.tsx +27 -12
  170. package/src/ui/components/layout/sidebar/use-sidebar-auto-scroll.ts +1 -1
  171. package/src/ui/components/layout/sidebar/workspace-row.tsx +253 -0
  172. package/src/ui/components/layout/status-bar.tsx +6 -2
  173. package/src/ui/components/layout/terminal-pane.tsx +34 -29
  174. package/src/ui/components/layout/top-tab-bar.tsx +48 -37
  175. package/src/ui/components/modals/git/git-commit-modal.tsx +8 -8
  176. package/src/ui/components/modals/{sessions/create-session-modal.tsx → projects/create-project-modal.tsx} +12 -12
  177. package/src/ui/components/modals/{sessions/session-name-modal.tsx → projects/project-name-modal.tsx} +2 -2
  178. package/src/ui/components/modals/{sessions/session-picker-modal.tsx → projects/project-picker-modal.tsx} +33 -35
  179. package/src/ui/components/modals/settings/settings-search-modal.tsx +70 -0
  180. package/src/ui/components/modals/shared/form.tsx +34 -22
  181. package/src/ui/components/modals/shared/picker.tsx +10 -10
  182. package/src/ui/components/modals/shared/{worktree-delete-confirm.tsx → workspace-delete-confirm.tsx} +9 -9
  183. package/src/ui/components/modals/tabs/new-tab-modal.tsx +30 -324
  184. package/src/ui/components/modals/workspace/create-workspace-modal.tsx +101 -0
  185. package/src/ui/components/modals/{worktree/worktree-move-confirm-modal.tsx → workspace/workspace-move-confirm-modal.tsx} +5 -5
  186. package/src/ui/components/modals/{worktree/worktree-move-modal.tsx → workspace/workspace-move-modal.tsx} +29 -27
  187. package/src/ui/components/primitives/input-field.tsx +7 -3
  188. package/src/ui/components/primitives/surface.tsx +3 -0
  189. package/src/ui/components/settings/row-value.tsx +74 -0
  190. package/src/ui/components/settings/settings-row.tsx +78 -0
  191. package/src/ui/components/settings/settings-view.tsx +169 -0
  192. package/src/ui/components/setup/setup-widget.tsx +152 -0
  193. package/src/ui/flash/build-labels.ts +27 -27
  194. package/src/ui/hooks/use-activity-sprite.ts +73 -0
  195. package/src/ui/hooks/use-scroll-active-into-view.ts +26 -0
  196. package/src/ui/{session-ordering.ts → project-ordering.ts} +4 -4
  197. package/src/ui/root.tsx +164 -230
  198. package/src/ui/status-bar-model.ts +48 -34
  199. package/src/ui/terminal-graphics/kitty.ts +28 -2
  200. package/src/ui/terminal-graphics/sprites/done@330/0.png +0 -0
  201. package/src/ui/terminal-graphics/sprites/done@330/1.png +0 -0
  202. package/src/ui/terminal-graphics/sprites/idle@1000/0.png +0 -0
  203. package/src/ui/terminal-graphics/sprites/idle@1000/1.png +0 -0
  204. package/src/ui/terminal-graphics/sprites/waiting@170/0.png +0 -0
  205. package/src/ui/terminal-graphics/sprites/waiting@170/1.png +0 -0
  206. package/src/ui/terminal-graphics/sprites/working@150/0.png +0 -0
  207. package/src/ui/terminal-graphics/sprites/working@150/1.png +0 -0
  208. package/src/ui/terminal-graphics/sprites/working@150/2.png +0 -0
  209. package/src/ui/terminal-graphics/sprites/working@150/3.png +0 -0
  210. package/src/ui/terminal-graphics/sprites.ts +246 -0
  211. package/src/ui/truncate.ts +7 -0
  212. package/src/ui/widgets/registry.tsx +21 -0
  213. package/src/ui/widgets/widget-context-menu.ts +85 -0
  214. package/src/update.ts +2 -2
  215. package/src/app-runtime/session-actions.ts +0 -187
  216. package/src/cli/client/workspace-resolver.ts +0 -110
  217. package/src/cli/commands/workspace/close.ts +0 -32
  218. package/src/cli/commands/workspace/show.ts +0 -32
  219. package/src/cli/commands/worktree/create.ts +0 -55
  220. package/src/cli/commands/worktree/list.ts +0 -68
  221. package/src/cli/commands/worktree/remove.ts +0 -81
  222. package/src/git/worktree-branch-poller.ts +0 -54
  223. package/src/git/worktree-divergence-poller.ts +0 -59
  224. package/src/state/reducers/session-state.ts +0 -262
  225. package/src/state/session-catalog.ts +0 -143
  226. package/src/state/session-worktrees.ts +0 -260
  227. package/src/state/workspace-save.ts +0 -45
  228. package/src/ui/components/git/pane/git-pane-context-menu.ts +0 -26
  229. package/src/ui/components/layout/sidebar/sidebar.tsx +0 -163
  230. package/src/ui/components/layout/sidebar/use-top-tab-bar-auto-scroll.ts +0 -30
  231. package/src/ui/components/layout/sidebar/workspace-list.tsx +0 -418
  232. package/src/ui/components/layout/sidebar/worktree-row.tsx +0 -149
  233. /package/src/ui/{components/git/image-diff → terminal-graphics}/dimensions.ts +0 -0
@@ -1,7 +1,6 @@
1
1
  import type { ModeId, SnippetVar } from '@brimveyn/aimux-config'
2
2
  import type { ThemedToken } from 'shiki'
3
3
 
4
- import type { WorktreeTemplate } from '../config'
5
4
  import type { LayoutNode, SplitDirection } from './layout-tree'
6
5
 
7
6
  export type BuiltinAssistantId =
@@ -18,7 +17,7 @@ export type AssistantId = BuiltinAssistantId | (string & {})
18
17
  export type TabStatus = 'starting' | 'running' | 'disconnected' | 'error'
19
18
 
20
19
  /**
21
- * Status values that may appear in legacy on-disk workspace snapshots but are
20
+ * Status values that may appear in legacy on-disk project snapshots but are
22
21
  * not produced by the running app anymore. Filtered at restore time.
23
22
  */
24
23
  export type LegacyPersistedTabStatus = TabStatus | 'exited'
@@ -34,26 +33,51 @@ export type TabActivity = 'working' | 'waiting-input' | 'idle'
34
33
  export type QuestionKind = 'question' | 'permission'
35
34
 
36
35
  /**
37
- * Per-session status flags. Both can be true at once (e.g. one tab working,
36
+ * Per-project status flags. Both can be true at once (e.g. one tab working,
38
37
  * another waiting for user input) so we keep them as independent booleans
39
38
  * rather than a single priority enum.
40
39
  */
41
- export interface SessionStatus {
40
+ export interface ProjectStatus {
42
41
  working: boolean
43
42
  waiting: boolean
44
43
  }
45
44
 
46
- export const IDLE_SESSION_STATUS: SessionStatus = { waiting: false, working: false }
45
+ export const IDLE_PROJECT_STATUS: ProjectStatus = { waiting: false, working: false }
47
46
 
48
- export type FocusMode = 'navigation' | 'terminal-input' | 'modal' | 'command-edit' | 'git'
47
+ /**
48
+ * A workspace's status, as the sidebar draws it. `working`/`waiting` mirror
49
+ * `ProjectStatus` one level down; `done` is a latch — an assistant here
50
+ * finished a turn and nobody has looked yet — cleared when the workspace is
51
+ * entered or when it goes back to work.
52
+ */
53
+ export interface WorkspaceActivity {
54
+ working: boolean
55
+ waiting: boolean
56
+ done: boolean
57
+ }
58
+
59
+ export const IDLE_WORKSPACE_ACTIVITY: WorkspaceActivity = {
60
+ done: false,
61
+ waiting: false,
62
+ working: false,
63
+ }
64
+
65
+ export type FocusMode =
66
+ | 'navigation'
67
+ | 'terminal-input'
68
+ | 'modal'
69
+ | 'command-edit'
70
+ | 'git'
71
+ | 'settings'
49
72
 
50
73
  export type ModalType =
51
74
  | 'new-tab'
52
- | 'session-picker'
53
- | 'session-name'
54
- | 'create-session'
75
+ | 'project-picker'
76
+ | 'project-name'
77
+ | 'create-project'
78
+ | 'create-workspace'
55
79
  | 'rename-tab'
56
- | 'rename-worktree'
80
+ | 'rename-workspace'
57
81
  | 'snippet-picker'
58
82
  | 'snippet-editor'
59
83
  | 'theme-picker'
@@ -62,10 +86,12 @@ export type ModalType =
62
86
  | 'git-commit'
63
87
  | 'update-available'
64
88
  | 'ai-usage'
65
- | 'worktree-move'
66
- | 'worktree-move-confirm'
67
- | 'worktree-delete-confirm'
89
+ | 'workspace-move'
90
+ | 'workspace-move-confirm'
91
+ | 'workspace-delete-confirm'
68
92
  | 'flash-jump'
93
+ | 'setting-text'
94
+ | 'settings-search'
69
95
  | null
70
96
 
71
97
  export interface TerminalSpan {
@@ -132,13 +158,13 @@ export interface PersistedTabSnapshot {
132
158
  terminalModes: TerminalModeState
133
159
  errorMessage?: string
134
160
  exitCode?: number
135
- worktreeId?: string
136
- /** Stable workspace-scoped name assigned by the headless worker facade. */
161
+ workspaceId?: string
162
+ /** Stable project-scoped name assigned by the headless worker facade. */
137
163
  workerName?: string
138
164
  autoRenameStatus?: 'eligible' | 'attempted'
139
165
  }
140
166
 
141
- export interface WorkspaceSnapshotV1 {
167
+ export interface ProjectSnapshotV1 {
142
168
  version: 1
143
169
  savedAt: string
144
170
  activeTabId: string | null
@@ -151,17 +177,17 @@ export interface WorkspaceSnapshotV1 {
151
177
  layoutTrees?: Record<string, LayoutNode>
152
178
  tabGroupMap?: Record<string, string>
153
179
  /**
154
- * Last viewed tab per worktree, keyed by worktree id. Optional and additive
180
+ * Last viewed tab per workspace, keyed by workspace id. Optional and additive
155
181
  * (no version bump): older builds ignore it, newer builds tolerate its
156
182
  * absence. Written now so the data accrues, but restoring it at startup is
157
- * gated off until a future change flips RESTORE_LAST_ACTIVE_TAB_BY_WORKTREE.
183
+ * gated off until a future change flips RESTORE_LAST_ACTIVE_TAB_BY_WORKSPACE.
158
184
  */
159
- lastActiveTabByWorktree?: Record<string, string>
185
+ lastActiveTabByWorkspace?: Record<string, string>
160
186
  }
161
187
 
162
- export type WorktreeSource = 'primary' | 'aimux-temp' | 'external'
188
+ export type WorkspaceSource = 'primary' | 'aimux-temp' | 'external'
163
189
 
164
- export interface WorktreeRecord {
190
+ export interface WorkspaceRecord {
165
191
  id: string
166
192
  name: string
167
193
  path: string
@@ -169,14 +195,18 @@ export interface WorktreeRecord {
169
195
  branch?: string
170
196
  baseRef?: string
171
197
  commitSha?: string
172
- source: WorktreeSource
198
+ source: WorkspaceSource
173
199
  createdByAimux: boolean
174
200
  color?: string
175
201
  createdAt: string
176
202
  updatedAt: string
203
+ /** Last setup-script run for this workspace. Display only — the auto-run gate
204
+ * is "this session has not seen this workspace id before". */
205
+ setupRanAt?: string
206
+ setupExitCode?: number
177
207
  }
178
208
 
179
- export interface SessionRecord {
209
+ export interface ProjectRecord {
180
210
  id: string
181
211
  name: string
182
212
  projectPath?: string
@@ -184,12 +214,12 @@ export interface SessionRecord {
184
214
  updatedAt: string
185
215
  lastOpenedAt: string
186
216
  order?: number
187
- workspaceSnapshot?: WorkspaceSnapshotV1
188
- worktrees?: WorktreeRecord[]
189
- activeWorktreeId?: string
217
+ projectSnapshot?: ProjectSnapshotV1
218
+ workspaces?: WorkspaceRecord[]
219
+ activeWorkspaceId?: string
190
220
  }
191
221
 
192
- export interface SessionBarState {
222
+ export interface ProjectBarState {
193
223
  visible: boolean
194
224
  }
195
225
 
@@ -205,21 +235,58 @@ export interface TabSession {
205
235
  command: string
206
236
  errorMessage?: string
207
237
  exitCode?: number
208
- worktreeId?: string
209
- /** Stable workspace-scoped name assigned by the headless worker facade. */
238
+ workspaceId?: string
239
+ /**
240
+ * This tab is the one that rang: it finished a turn while you were looking
241
+ * elsewhere. Set alongside the workspace's own tick and cleared the moment
242
+ * the tab is opened or goes back to work, so the notification points at
243
+ * something rather than just happening. Ephemeral — never persisted, never
244
+ * on the wire.
245
+ */
246
+ unseen?: boolean
247
+ /** Stable project-scoped name assigned by the headless worker facade. */
210
248
  workerName?: string
211
249
  autoRenameStatus?: 'eligible' | 'attempted'
250
+ /**
251
+ * A real PTY tab that no chrome enumerates: absent from the top tab bar, from
252
+ * tab navigation, from `activeTabId` picks, and from the persisted snapshot.
253
+ * Rendered by whatever created it — today only the setup widget.
254
+ * `filterTabsForActiveWorkspace` is the single guard the UI paths share.
255
+ */
256
+ hidden?: boolean
257
+ /**
258
+ * Who owns this tab. Separate from `hidden` on purpose: promoting a setup tab
259
+ * into the main pane clears `hidden` but must not turn it back into an
260
+ * ordinary tab, or the PTY exit would close it and take the failure output
261
+ * with it — which is the one thing the promotion exists to read.
262
+ *
263
+ * ponytail: not on the ipc wire nor in the snapshot, so a promoted setup tab
264
+ * comes back from a restart as a plain tab running `bash …/setup.sh`. Add it
265
+ * to `TabSessionSummary` if that ever matters.
266
+ */
267
+ role?: 'setup'
268
+ }
269
+
270
+ export type BarSide = 'left' | 'right'
271
+
272
+ /**
273
+ * One widget slot in a bar. `grow` is the flex weight opentui consumes
274
+ * directly; hidden widgets keep their weight but are excluded from the layout.
275
+ */
276
+ export interface BarWidget {
277
+ id: string
278
+ grow: number
279
+ visible: boolean
212
280
  }
213
281
 
214
- export interface SidebarState {
282
+ export interface BarState {
215
283
  visible: boolean
216
284
  width: number
217
- minWidth: number
218
- maxWidth: number
285
+ /** Ordered top → bottom. */
286
+ widgets: BarWidget[]
219
287
  }
220
288
 
221
- export type GitPaneMode = 'embedded' | 'pane'
222
- export type GitPanePosition = 'top' | 'bottom' | 'left' | 'right'
289
+ export type BarsState = Record<BarSide, BarState>
223
290
 
224
291
  export type GitPanePathConfig =
225
292
  | { enabled: false }
@@ -230,11 +297,6 @@ export interface GitPaneDiffCountConfig {
230
297
  }
231
298
 
232
299
  export interface GitPaneState {
233
- visible: boolean
234
- mode: GitPaneMode
235
- position: GitPanePosition
236
- paneRatio: number
237
- embeddedRatio: number
238
300
  diffModeRatio: number
239
301
  fileListMode: GitFileListMode
240
302
  treeCompaction: boolean
@@ -345,59 +407,89 @@ export interface GitModeState {
345
407
  folds: Record<string, Record<string, FoldState>>
346
408
  /** Working-tree-vs-HEAD~N offset. 0 = working tree vs HEAD (default). */
347
409
  headOffset: number
348
- /** When true, diff the active worktree's working tree against its fork point. */
410
+ /** When true, diff the active workspace's working tree against its fork point. */
349
411
  reviewBase: boolean
350
412
  }
351
413
 
352
414
  interface ModalBase {
353
415
  selectedIndex: number
354
416
  editBuffer: string | null
355
- sessionTargetId: string | null
417
+ projectTargetId: string | null
356
418
  cursorPos?: number
419
+ /**
420
+ * Where the focus goes when this modal closes, when it is not back to the
421
+ * panes. Set by whoever opened it, because that is who knows what is behind it
422
+ * — the settings screen opens five different modals, and none of them should
423
+ * have to grow a case in the reducer to find their way home.
424
+ */
425
+ returnTo?: FocusMode
357
426
  }
358
427
 
359
428
  export interface ModalClosed extends ModalBase {
360
429
  type: null
361
430
  editBuffer: null
362
- sessionTargetId: null
431
+ projectTargetId: null
363
432
  }
364
433
 
434
+ /**
435
+ * Picking an assistant for a new tab, and nothing else. The tab lands in the
436
+ * project's active workspace; creating a workspace is `create-workspace`'s job.
437
+ */
365
438
  export interface ModalNewTab extends ModalBase {
366
439
  type: 'new-tab'
367
440
  editingCommand: AssistantId | null
368
- activeField: 'assistant' | 'branch-name' | 'target-worktree' | 'worktree-name' | 'base'
369
- branchError: string | null
370
- branchName: string
371
- createWorktree: boolean
372
- selectedAssistantId: AssistantId | null
373
- step: 'assistant' | 'worktree' | 'worktree-create' | 'template'
374
- targetWorktreeIndex: number
375
- worktreeDeletePrompt: { worktreeId: string; reason: string } | null
376
- worktreeName: string
377
- /** Filter text typed into the "Base" picker on the worktree-create step. */
378
- baseQuery: string
379
- /** Resolved base ref the new worktree is forked from (branch of a worktree or a local branch). */
380
- baseRef: string
381
- /** Local branches available as base refs, loaded when the create step opens. */
382
- baseBranches: string[]
441
+ /**
442
+ * Set when `create-workspace` chained into this picker. The prompt the user
443
+ * typed there is sent to the assistant and drives the workspace's real name,
444
+ * and the tab is pinned to the freshly created workspace rather than the
445
+ * project's active one. Living on the modal means `close-modal` clears it, so
446
+ * escaping the picker cannot leak a stale prompt into a later tab.
447
+ */
448
+ pendingWorkspace?: PendingWorkspaceLaunch
449
+ /**
450
+ * A prompt to hand the picked assistant, with none of `pendingWorkspace`'s
451
+ * other behaviour: no workspace pinning, no rename. Used by "Ask an agent" in
452
+ * the setup widget.
453
+ */
454
+ pendingPrompt?: string
383
455
  }
384
456
 
385
- export interface ModalSessionPicker extends ModalBase {
386
- type: 'session-picker'
457
+ export interface PendingWorkspaceLaunch {
458
+ projectId: string
459
+ workspaceId: string
460
+ prompt: string
387
461
  }
388
462
 
389
- export interface ModalSessionName extends ModalBase {
390
- type: 'session-name'
391
- returnToSessionPicker: boolean
463
+ export interface ModalProjectPicker extends ModalBase {
464
+ type: 'project-picker'
465
+ }
466
+
467
+ export interface ModalProjectName extends ModalBase {
468
+ type: 'project-name'
469
+ returnToProjectPicker: boolean
392
470
  }
393
471
 
394
472
  export interface ModalRenameTab extends ModalBase {
395
473
  type: 'rename-tab'
396
474
  }
475
+ /**
476
+ * One text field over a settings row. Carries the row it belongs to so confirming
477
+ * writes back to the right one, and closing returns to the settings screen rather
478
+ * than to the panes.
479
+ */
480
+ /** Fuzzy-ish search across every setting, from inside the settings screen. */
481
+ export interface ModalSettingsSearch extends ModalBase {
482
+ type: 'settings-search'
483
+ }
484
+ export interface ModalSettingText extends ModalBase {
485
+ type: 'setting-text'
486
+ settingId: string
487
+ settingLabel: string
488
+ }
397
489
 
398
- export interface ModalRenameWorktree extends ModalBase {
399
- type: 'rename-worktree'
400
- worktreeSessionId: string
490
+ export interface ModalRenameWorkspace extends ModalBase {
491
+ type: 'rename-workspace'
492
+ workspaceProjectId: string
401
493
  }
402
494
 
403
495
  export interface ModalSnippetPicker extends ModalBase {
@@ -432,13 +524,35 @@ export interface ModalGitCommit extends ModalBase {
432
524
  stage: 'edit' | 'generating' | 'confirm'
433
525
  }
434
526
 
435
- export interface ModalCreateSession extends ModalBase {
436
- type: 'create-session'
527
+ export interface ModalCreateProject extends ModalBase {
528
+ type: 'create-project'
437
529
  directoryResults: DirectoryResult[]
438
530
  pendingProjectPath: string | null
439
531
  activeField: 'directory' | 'name'
440
532
  nameBuffer: string
441
- returnToSessionPicker: boolean
533
+ returnToProjectPicker: boolean
534
+ }
535
+
536
+ /**
537
+ * Creating a workspace inside the current project — the second of the three
538
+ * creation actions (project / workspace / tab). Deliberately carries no
539
+ * assistant: once the workspace exists the effect chains into the new-tab modal.
540
+ */
541
+ export interface ModalCreateWorkspace extends ModalBase {
542
+ type: 'create-workspace'
543
+ activeField: 'prompt' | 'base'
544
+ /**
545
+ * "What do you want to work on?" — the only thing the user types. It is sent
546
+ * to the assistant, and it names both the workspace and its branch.
547
+ */
548
+ prompt: string
549
+ branchError: string | null
550
+ /** Filter text typed into the "Base" picker. */
551
+ baseQuery: string
552
+ /** Resolved base ref the new workspace is forked from (a workspace's branch or a local branch). */
553
+ baseRef: string
554
+ /** Local branches available as base refs, loaded when the modal opens. */
555
+ baseBranches: string[]
442
556
  }
443
557
 
444
558
  export interface ModalSnippetEditor extends ModalBase {
@@ -462,68 +576,68 @@ export interface ModalAIUsage extends ModalBase {
462
576
  type: 'ai-usage'
463
577
  }
464
578
 
465
- export interface ModalWorktreeMove extends ModalBase {
466
- type: 'worktree-move'
467
- /** The worktree being moved (may differ from the active one, e.g. a tab menu). */
468
- sourceWorktreeId: string
579
+ export interface ModalWorkspaceMove extends ModalBase {
580
+ type: 'workspace-move'
581
+ /** The workspace being moved (may differ from the active one, e.g. a tab menu). */
582
+ sourceWorkspaceId: string
469
583
  deleteSource: boolean
470
- /** Per-worktree dirty file counts, loaded async when the modal opens. */
584
+ /** Per-workspace dirty file counts, loaded async when the modal opens. */
471
585
  stats: { kind: 'loading' } | { kind: 'ready'; dirtyFiles: Record<string, number> }
472
586
  }
473
587
 
474
588
  /**
475
589
  * Confirmation for a recoverable move failure: the target's dirty files
476
590
  * overlap the incoming changes (stash-target) or the squash conflicts
477
- * (keep-conflicts). Both worktrees are already restored; confirming re-runs
478
- * move-worktree with the matching flag.
591
+ * (keep-conflicts). Both workspaces are already restored; confirming re-runs
592
+ * move-workspace with the matching flag.
479
593
  */
480
- export interface ModalWorktreeMoveConfirm extends ModalBase {
481
- type: 'worktree-move-confirm'
594
+ export interface ModalWorkspaceMoveConfirm extends ModalBase {
595
+ type: 'workspace-move-confirm'
482
596
  variant: 'stash-target' | 'keep-conflicts'
483
597
  files: string[]
484
- sessionId: string
485
- sourceWorktreeId: string
486
- targetWorktreeId: string
598
+ projectId: string
599
+ sourceWorkspaceId: string
600
+ targetWorkspaceId: string
487
601
  deleteSource: boolean
488
602
  sourceLabel: string
489
603
  targetLabel: string
490
604
  }
491
605
 
492
606
  /**
493
- * Standalone confirmation for a recoverable worktree delete failure triggered
494
- * outside the new-tab picker (e.g. the sidebar's "Remove worktree"). Carries the
607
+ * Standalone confirmation for a recoverable workspace delete failure triggered
608
+ * outside the new-tab picker (e.g. the sidebar's "Remove workspace"). Carries the
495
609
  * params needed to re-run the delete with force once confirmed.
496
610
  */
497
- export interface ModalWorktreeDeleteConfirm extends ModalBase {
498
- type: 'worktree-delete-confirm'
499
- sessionId: string
500
- worktreeId: string
501
- worktreeLabel: string
611
+ export interface ModalWorkspaceDeleteConfirm extends ModalBase {
612
+ type: 'workspace-delete-confirm'
613
+ projectId: string
614
+ workspaceId: string
615
+ workspaceLabel: string
502
616
  reason: string
503
617
  closeTabs: boolean
504
618
  /** Whether confirming force-deletes — true only after a recoverable failure. */
505
619
  force: boolean
506
620
  }
507
621
 
508
- export type DirectoryResultType = 'git-repo' | 'worktree' | 'workspace'
622
+ export type DirectoryResultType = 'git-repo' | 'workspace' | 'project'
509
623
 
510
624
  export interface DirectoryResult {
511
625
  path: string
512
626
  type: DirectoryResultType
513
627
  }
514
628
 
515
- export type FlashJumpTargetKind = 'workspace' | 'worktree' | 'tab'
629
+ export type FlashJumpTargetKind = 'project' | 'workspace' | 'tab'
516
630
 
517
631
  export interface FlashJumpTarget {
518
632
  kind: FlashJumpTargetKind
519
633
  /**
520
- * 1-based index of the workspace in the visible session ordering — fed to
521
- * the existing `switch-session-by-index` side effect when jumping.
634
+ * 1-based index of the project in the visible project ordering — fed to
635
+ * the existing `switch-project-by-index` side effect when jumping.
522
636
  */
523
- sessionIndex: number
524
- sessionId: string
525
- /** Set for kind 'worktree' (the non-primary target) and kind 'tab' (the tab's worktree). */
526
- worktreeId?: string
637
+ projectIndex: number
638
+ projectId: string
639
+ /** Set for kind 'workspace' (the non-primary target) and kind 'tab' (the tab's workspace). */
640
+ workspaceId?: string
527
641
  /** Set for kind 'tab'. */
528
642
  tabId?: string
529
643
  }
@@ -551,23 +665,26 @@ export interface ModalFlashJump extends ModalBase {
551
665
  export type ModalState =
552
666
  | ModalClosed
553
667
  | ModalNewTab
554
- | ModalSessionPicker
555
- | ModalSessionName
668
+ | ModalProjectPicker
669
+ | ModalProjectName
556
670
  | ModalRenameTab
557
- | ModalRenameWorktree
671
+ | ModalRenameWorkspace
558
672
  | ModalSnippetPicker
559
673
  | ModalThemePicker
560
674
  | ModalHelp
561
675
  | ModalSplitPicker
562
- | ModalCreateSession
676
+ | ModalCreateProject
677
+ | ModalCreateWorkspace
563
678
  | ModalSnippetEditor
564
679
  | ModalGitCommit
565
680
  | ModalUpdateAvailable
566
681
  | ModalAIUsage
567
- | ModalWorktreeMove
568
- | ModalWorktreeMoveConfirm
569
- | ModalWorktreeDeleteConfirm
682
+ | ModalWorkspaceMove
683
+ | ModalWorkspaceMoveConfirm
684
+ | ModalWorkspaceDeleteConfirm
570
685
  | ModalFlashJump
686
+ | ModalSettingText
687
+ | ModalSettingsSearch
571
688
 
572
689
  export interface LayoutState {
573
690
  terminalCols: number
@@ -585,9 +702,9 @@ export interface SnippetRecord {
585
702
  export interface DiscoveredRepo {
586
703
  /** Absolute path to the repo. */
587
704
  path: string
588
- /** Label shown in UI (relative to the workspace root or repo basename). */
705
+ /** Label shown in UI (relative to the project root or repo basename). */
589
706
  name: string
590
- /** True when the repo is the session's projectPath itself. */
707
+ /** True when the repo is the project's projectPath itself. */
591
708
  isRoot: boolean
592
709
  }
593
710
 
@@ -600,18 +717,31 @@ export interface MultiRepoState {
600
717
 
601
718
  export const EMPTY_MULTI_REPO_STATE: MultiRepoState = { prefixes: {}, repos: [] }
602
719
 
720
+ /**
721
+ * Where the cursor is in the settings screen. Only the cursor: the values being
722
+ * edited live in `src/settings/settings-store.ts`, or already have a home in
723
+ * this state (`gitPane`, `customCommands`, …). Duplicating them here would make
724
+ * two of them.
725
+ */
726
+ export interface SettingsUIState {
727
+ sectionId: string
728
+ /** Which of the two columns has the keyboard: the section list, or its rows. */
729
+ pane: 'nav' | 'rows'
730
+ rowIndex: number
731
+ }
732
+
603
733
  export interface AppState {
604
734
  tabs: TabSession[]
605
735
  activeTabId: string | null
606
736
  layoutTrees: Record<string, LayoutNode>
607
737
  tabGroupMap: Record<string, string>
608
- sessions: SessionRecord[]
609
- currentSessionId: string | null
610
- sessionStatuses: Record<string, SessionStatus>
611
- sessionBar: SessionBarState
738
+ projects: ProjectRecord[]
739
+ currentProjectId: string | null
740
+ projectStatuses: Record<string, ProjectStatus>
741
+ projectBar: ProjectBarState
612
742
  snippets: SnippetRecord[]
613
743
  focusMode: FocusMode
614
- sidebar: SidebarState
744
+ bars: BarsState
615
745
  gitPane: GitPaneState
616
746
  modal: ModalState
617
747
  layout: LayoutState
@@ -620,204 +750,30 @@ export interface AppState {
620
750
  gitMode: GitModeState
621
751
  autoCommit: AutoCommitState
622
752
  multiRepo: MultiRepoState
753
+ settings: SettingsUIState
754
+ /**
755
+ * Commits each workspace's branch is ahead/behind the ref it forked from,
756
+ * keyed by workspace id. Ephemeral (polled); not persisted to the catalog.
757
+ */
758
+ workspaceDivergence: Record<string, BranchDivergence>
623
759
  /**
624
- * Commits each worktree's branch is ahead/behind the ref it forked from,
625
- * keyed by worktree id. Ephemeral (polled); not persisted to the catalog.
760
+ * What each workspace's assistants are doing, keyed by workspace id. Covers
761
+ * every project the daemon knows, not just the current one — see
762
+ * `src/app-runtime/workspace-activity.ts`, which owns the aggregation.
763
+ * Ephemeral; not persisted to the catalog.
626
764
  */
627
- worktreeDivergence: Record<string, BranchDivergence>
765
+ workspaceActivity: Record<string, WorkspaceActivity>
628
766
  /**
629
- * Last active tab a user viewed within each worktree, keyed by worktree id.
630
- * Lets switching back to a worktree restore its last-viewed tab instead of
767
+ * Last active tab a user viewed within each workspace, keyed by workspace id.
768
+ * Lets switching back to a workspace restore its last-viewed tab instead of
631
769
  * snapping to the first one. Ephemeral (in-memory); not persisted to the catalog.
632
770
  */
633
- lastActiveTabByWorktree: Record<string, string>
771
+ lastActiveTabByWorkspace: Record<string, string>
634
772
  /** Chord prefix the sequence resolver is currently waiting on, or null when idle. */
635
773
  pendingChords: string[] | null
636
- /** User-defined templates applied at worktree creation. Loaded from aimux.json. */
637
- worktreeTemplates: WorktreeTemplate[]
638
774
  }
639
775
 
640
- // -- Modal actions --
641
- export type ModalAction =
642
- | { type: 'move-modal-cursor'; delta?: number; to?: 'home' | 'end' }
643
- | { type: 'open-new-tab-modal' }
644
- | { type: 'set-new-tab-branch-error'; message: string | null }
645
- | {
646
- type: 'set-new-tab-worktree-delete-prompt'
647
- prompt: { worktreeId: string; reason: string } | null
648
- }
649
- | { type: 'set-new-tab-base-branches'; branches: string[] }
650
- | { type: 'enter-new-tab-worktree-create' }
651
- | { type: 'enter-new-tab-template-pick' }
652
- | { type: 'enter-new-tab-template-shortcut' }
653
- | { type: 'select-new-tab-assistant'; assistantId?: AssistantId }
654
- | { type: 'toggle-new-tab-worktree'; assistantId?: AssistantId }
655
- | { type: 'open-edit-custom-command'; assistantId: AssistantId }
656
- | { type: 'open-help-modal'; scope?: ModeId }
657
- | { type: 'open-split-picker'; direction: SplitDirection }
658
- | { type: 'open-session-picker' }
659
- | {
660
- type: 'open-session-name-modal'
661
- sessionTargetId?: string
662
- initialName?: string
663
- returnToSessionPicker?: boolean
664
- }
665
- | { type: 'close-modal' }
666
- | { type: 'move-modal-selection'; delta: number }
667
- | { type: 'update-command-edit'; char: string }
668
- | { type: 'cancel-command-edit' }
669
- | { type: 'open-create-session-modal'; returnToSessionPicker: boolean }
670
- | { type: 'set-directory-results'; results: DirectoryResult[] }
671
- | { type: 'switch-create-session-field' }
672
- | { type: 'select-directory' }
673
- | { type: 'open-rename-tab-modal' }
674
- | {
675
- type: 'open-rename-worktree-modal'
676
- sessionId: string
677
- worktreeId: string
678
- initialName: string
679
- }
680
- | { type: 'open-snippet-picker' }
681
- | { type: 'open-snippet-editor'; snippetId?: string }
682
- | { type: 'set-help-entry-count'; count: number }
683
- | { type: 'set-theme-entry-count'; count: number }
684
- | { type: 'open-theme-picker' }
685
- | { type: 'open-update-available-modal'; currentVersion: string; latestVersion: string }
686
- | { type: 'set-modal-selection-index'; index: number }
687
- | { type: 'open-ai-usage-modal' }
688
- | { type: 'open-worktree-move-modal'; sourceWorktreeId: string }
689
- | { type: 'toggle-worktree-move-delete' }
690
- | { type: 'set-worktree-move-stats'; dirtyFiles: Record<string, number> }
691
- | {
692
- type: 'open-worktree-move-confirm'
693
- variant: 'stash-target' | 'keep-conflicts'
694
- files: string[]
695
- sessionId: string
696
- sourceWorktreeId: string
697
- targetWorktreeId: string
698
- deleteSource: boolean
699
- sourceLabel: string
700
- targetLabel: string
701
- }
702
- | {
703
- type: 'open-worktree-delete-confirm'
704
- sessionId: string
705
- worktreeId: string
706
- worktreeLabel: string
707
- reason: string
708
- closeTabs: boolean
709
- force: boolean
710
- }
711
- | { type: 'open-flash-jump-modal' }
712
- | { type: 'clear-flash-jump-pending' }
713
-
714
- // -- Session actions --
715
- export type SessionAction =
716
- | {
717
- type: 'load-session'
718
- sessionId: string
719
- workspaceSnapshot?: WorkspaceSnapshotV1
720
- forceDisconnected?: boolean
721
- }
722
- | { type: 'set-sessions'; sessions: SessionRecord[] }
723
- | { type: 'create-session-record'; session: SessionRecord }
724
- | { type: 'rename-session-record'; sessionId: string; name: string }
725
- | { type: 'delete-session-record'; sessionId: string; openSessionPicker?: boolean }
726
- | { type: 'reorder-sessions'; orderedIds: string[] }
727
- | { type: 'reorder-active-session'; delta: number }
728
- | { type: 'set-session-status'; sessionId: string; status: SessionStatus }
729
- | { type: 'add-worktree-record'; sessionId: string; worktree: WorktreeRecord; activate?: boolean }
730
- | { type: 'set-active-worktree'; sessionId: string; worktreeId: string }
731
- | {
732
- type: 'update-worktree-record'
733
- sessionId: string
734
- worktreeId: string
735
- patch: Partial<WorktreeRecord>
736
- }
737
-
738
- // -- Tab actions --
739
- export type TabAction =
740
- | { type: 'add-tab'; tab: TabSession }
741
- | {
742
- type: 'hydrate-workspace'
743
- tabs: TabSession[]
744
- activeTabId: string | null
745
- layoutTree?: LayoutNode | null
746
- layoutTrees?: Record<string, LayoutNode>
747
- tabGroupMap?: Record<string, string>
748
- }
749
- | { type: 'close-tab'; tabId: string }
750
- | { type: 'close-active-tab' }
751
- | { type: 'set-active-tab'; tabId: string }
752
- | { type: 'move-active-tab'; delta: number }
753
- | { type: 'reorder-active-tab'; delta: number }
754
- | { type: 'reorder-tabs'; orderedTabIds: string[] }
755
- | { type: 'reset-tab-session'; tabId: string }
756
- | {
757
- type: 'rename-tab'
758
- tabId: string
759
- title: string
760
- autoRenameStatus?: 'eligible' | 'attempted'
761
- }
762
- | {
763
- type: 'update-tab-metadata'
764
- tabId: string
765
- title?: string
766
- autoRenameStatus?: 'eligible' | 'attempted'
767
- }
768
- | { type: 'append-tab-buffer'; tabId: string; chunk: string }
769
- | {
770
- type: 'replace-tab-viewport'
771
- tabId: string
772
- viewport: TerminalSnapshot
773
- terminalModes: TerminalModeState
774
- source?: 'resize' | 'scroll' | 'data' | 'switch'
775
- }
776
- | { type: 'set-tab-activity'; tabId: string; activity?: TabActivity }
777
- | { type: 'set-tab-error'; tabId: string; message: string }
778
-
779
- // -- Layout actions --
780
- export type LayoutAction =
781
- | {
782
- type: 'split-pane'
783
- direction: SplitDirection
784
- newTab: TabSession
785
- }
786
- | { type: 'close-pane'; tabId: string }
787
- | {
788
- type: 'focus-pane-direction'
789
- direction: 'left' | 'right' | 'up' | 'down'
790
- }
791
- | {
792
- type: 'resize-pane'
793
- tabId: string
794
- delta: number
795
- axis?: SplitDirection
796
- }
797
- | {
798
- type: 'set-split-ratio'
799
- tabId: string
800
- ratio: number
801
- axis?: SplitDirection
802
- }
803
-
804
- // -- UI actions --
805
- export type UIAction =
806
- | { type: 'toggle-sidebar' }
807
- | { type: 'resize-sidebar'; delta: number }
808
- | { type: 'set-sidebar-width'; width: number }
809
- | { type: 'set-focus-mode'; focusMode: FocusMode }
810
- | { type: 'set-terminal-size'; cols: number; rows: number }
811
- | { type: 'toggle-git-pane' }
812
- | { type: 'resize-git-pane'; delta: number }
813
- | { type: 'set-git-pane-ratio'; target: 'pane' | 'embedded'; ratio: number }
814
- | { type: 'resize-git-diff-pane'; delta: number }
815
- | { type: 'set-git-pane-mode'; mode: GitPaneMode }
816
- | { type: 'set-git-pane-position'; position: GitPanePosition }
817
- | { type: 'set-pending-chords'; chords: string[] | null }
818
- | { type: 'toggle-session-bar' }
819
-
820
- // -- Git panel actions --
776
+ // -- Git panel payloads --
821
777
  export interface GitRefreshPayload {
822
778
  branch: string | null
823
779
  ahead: number
@@ -825,19 +781,16 @@ export interface GitRefreshPayload {
825
781
  files: GitFileEntry[]
826
782
  }
827
783
 
828
- /** Commits a worktree branch is ahead/behind the ref it forked from. */
784
+ /** Commits a workspace branch is ahead/behind the ref it forked from. */
829
785
  export interface BranchDivergence {
830
786
  ahead: number
831
787
  behind: number
788
+ /** Lines changed since the fork point, working tree included. */
789
+ added?: number
790
+ removed?: number
832
791
  }
833
792
 
834
- export type GitPanelAction =
835
- | { type: 'git-refresh-success'; payload: GitRefreshPayload }
836
- | { type: 'git-refresh-error'; kind: GitPanelError }
837
- | { type: 'git-panel-reset' }
838
- | { type: 'set-worktree-divergence'; divergence: Record<string, BranchDivergence> }
839
-
840
- // -- Auto-commit state & actions --
793
+ // -- Auto-commit state --
841
794
  export type AutoCommitSuggestion =
842
795
  | { kind: 'idle' }
843
796
  | {
@@ -857,116 +810,7 @@ export type AutoCommitSuggestion =
857
810
  }
858
811
 
859
812
  export interface AutoCommitState {
860
- bySession: Record<string, AutoCommitSuggestion>
813
+ byProject: Record<string, AutoCommitSuggestion>
861
814
  }
862
815
 
863
- export const EMPTY_AUTO_COMMIT_STATE: AutoCommitState = { bySession: {} }
864
-
865
- export type AutoCommitAction =
866
- | {
867
- type: 'auto-commit-generation-started'
868
- sessionId: string
869
- tabId: string
870
- workingTreeHash: string
871
- abortController: AbortController
872
- startedAt: number
873
- }
874
- | {
875
- type: 'auto-commit-generation-ready'
876
- sessionId: string
877
- workingTreeHash: string
878
- title: string
879
- body: string
880
- generatedAt: number
881
- }
882
- | { type: 'auto-commit-clear'; sessionId: string }
883
-
884
- export type GitModeAction =
885
- | { type: 'enter-git-mode' }
886
- | { type: 'exit-git-mode' }
887
- | { type: 'git-mode-move-selection'; delta: -1 | 1 }
888
- | { type: 'git-mode-move-file-selection'; delta: -1 | 1 }
889
- | { type: 'git-mode-select-entry-by-key'; key: string }
890
- | { type: 'git-mode-toggle-folder'; key: string }
891
- | { type: 'git-mode-toggle-selected-folder' }
892
- | { type: 'git-mode-collapse-selection' }
893
- | { type: 'git-mode-expand-selection' }
894
- | { type: 'git-mode-toggle-file-list-mode' }
895
- | { type: 'git-mode-toggle-tree-compaction' }
896
- | { type: 'git-mode-set-diff'; key: string; diff: DiffData; hash: string }
897
- | {
898
- type: 'git-mode-set-parsed'
899
- key: string
900
- hash: string
901
- file: unknown
902
- }
903
- | {
904
- type: 'git-mode-set-highlights'
905
- key: string
906
- hash: string
907
- themeId: string
908
- add: unknown
909
- del: unknown
910
- }
911
- | {
912
- type: 'git-mode-merge-highlights'
913
- key: string
914
- hash: string
915
- themeId: string
916
- add: { start: number; tokens: unknown }[]
917
- del: { start: number; tokens: unknown }[]
918
- }
919
- | { type: 'git-mode-invalidate-diffs'; paths: string[] }
920
- | { type: 'git-mode-set-loading'; key: string; loading: boolean }
921
- | { type: 'git-mode-set-pending-delete'; path: string | null }
922
- | { type: 'git-mode-clear-diff-cache'; path: string }
923
- | { type: 'git-mode-set-message'; message: string | null }
924
- | { type: 'snippet-picker-set-message'; message: string | null }
925
- | { type: 'git-mode-toggle-diff-view' }
926
- | { type: 'git-mode-toggle-review-base' }
927
- | { type: 'git-mode-shift-head-offset'; delta: number }
928
- | { type: 'git-mode-set-head-offset'; offset: number }
929
- | {
930
- type: 'git-mode-fold-adjust'
931
- key: string
932
- foldId: string
933
- side: 'top' | 'bottom'
934
- delta: number
935
- }
936
- | { type: 'git-mode-fold-set'; key: string; foldId: string; top: number; bottom: number }
937
- | { type: 'git-mode-fold-toggle-all'; key: string }
938
- | {
939
- type: 'git-mode-optimistic-move'
940
- path: string
941
- fromSection: GitFileSection
942
- toSection: GitFileSection | null
943
- }
944
- | { type: 'open-git-commit-modal'; sessionId?: string }
945
- | { type: 'git-commit-enter-confirm' }
946
- | { type: 'git-commit-leave-confirm' }
947
- | { type: 'git-commit-enter-generating'; sessionId: string }
948
- | { type: 'git-commit-leave-generating' }
949
- | { type: 'git-commit-use-background-suggestion'; sessionId: string }
950
-
951
- // -- Data actions --
952
- export type DataAction =
953
- | { type: 'set-snippets'; snippets: SnippetRecord[] }
954
- | { type: 'delete-snippet'; snippetId: string }
955
- | { type: 'set-custom-commands'; customCommands: Record<AssistantId, string> }
956
-
957
- // -- Multi-repo actions --
958
- export type MultiRepoAction =
959
- | { type: 'multi-repo-set-repos'; repos: DiscoveredRepo[] }
960
- | { type: 'multi-repo-clear' }
961
-
962
- export type AppAction =
963
- | ModalAction
964
- | SessionAction
965
- | TabAction
966
- | LayoutAction
967
- | UIAction
968
- | DataAction
969
- | GitPanelAction
970
- | GitModeAction
971
- | AutoCommitAction
972
- | MultiRepoAction
816
+ export const EMPTY_AUTO_COMMIT_STATE: AutoCommitState = { byProject: {} }