@brimveyn/aimux 1.21.0 → 1.22.2
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.
- package/README.md +27 -25
- package/package.json +2 -2
- package/src/app-runtime/auto-commit-driver.ts +22 -27
- package/src/app-runtime/backend-attach-runtime.ts +43 -36
- package/src/app-runtime/backend-runtime-events.ts +107 -88
- package/src/app-runtime/editor-actions.ts +201 -0
- package/src/app-runtime/git-actions.ts +197 -0
- package/src/app-runtime/measure-ref.ts +19 -0
- package/src/app-runtime/navigation-actions.ts +213 -0
- package/src/app-runtime/project-actions.ts +188 -0
- package/src/app-runtime/prompt-injection.ts +123 -0
- package/src/app-runtime/selection.ts +29 -0
- package/src/app-runtime/settings-actions.ts +156 -0
- package/src/app-runtime/setup-actions.ts +237 -0
- package/src/app-runtime/side-effect-context.ts +33 -0
- package/src/app-runtime/side-effects.ts +302 -1541
- package/src/app-runtime/snippet-actions.ts +3 -2
- package/src/app-runtime/tab-actions.ts +218 -0
- package/src/app-runtime/tab-runtime-timeouts.ts +1 -1
- package/src/app-runtime/use-auto-commit-driver.ts +14 -13
- package/src/app-runtime/use-backend-runtime.ts +12 -11
- package/src/app-runtime/use-directory-search.ts +5 -4
- package/src/app-runtime/use-mouse-handlers.ts +2 -1
- package/src/app-runtime/{use-workspace-autosave.ts → use-project-autosave.ts} +4 -4
- package/src/app-runtime/use-renderer-bindings.ts +2 -1
- package/src/app-runtime/use-setup-runner.ts +96 -0
- package/src/app-runtime/use-terminal-resize.ts +18 -14
- package/src/app-runtime/workspace-actions.ts +366 -0
- package/src/app-runtime/workspace-activity.ts +138 -0
- package/src/app-runtime/workspace-naming.ts +90 -0
- package/src/app.tsx +93 -48
- package/src/assets/sounds/bell.wav +0 -0
- package/src/assets/sounds/ding.wav +0 -0
- package/src/assets/sounds/plane.wav +0 -0
- package/src/cli/client/daemon-client.ts +2 -2
- package/src/cli/client/project-resolver.ts +112 -0
- package/src/cli/commands/project/close.ts +32 -0
- package/src/cli/commands/project/create.ts +93 -0
- package/src/cli/commands/project/list.ts +25 -0
- package/src/cli/commands/project/show.ts +32 -0
- package/src/cli/commands/{workspace → project}/switch.ts +19 -19
- package/src/cli/commands/tab/await.ts +2 -2
- package/src/cli/commands/tab/close.ts +3 -3
- package/src/cli/commands/tab/create.ts +74 -73
- package/src/cli/commands/tab/focus.ts +3 -3
- package/src/cli/commands/tab/list.ts +6 -6
- package/src/cli/commands/tab/run.ts +2 -2
- package/src/cli/commands/tab/send.ts +2 -2
- package/src/cli/commands/tab/snapshot.ts +2 -2
- package/src/cli/commands/tab/tail.ts +2 -2
- package/src/cli/commands/tab/wait.ts +2 -2
- package/src/cli/commands/worker/await.ts +3 -3
- package/src/cli/commands/worker/doctor.ts +30 -30
- package/src/cli/commands/worker/list.ts +15 -15
- package/src/cli/commands/worker/prompt.ts +3 -3
- package/src/cli/commands/worker/run.ts +23 -23
- package/src/cli/commands/worker/shared.ts +53 -59
- package/src/cli/commands/worker/stop.ts +29 -29
- package/src/cli/commands/worker/submit.ts +3 -3
- package/src/cli/commands/{worktree → workspace}/create-core.ts +26 -26
- package/src/cli/commands/workspace/create.ts +30 -68
- package/src/cli/commands/workspace/list.ts +52 -9
- package/src/cli/commands/workspace/remove.ts +81 -0
- package/src/cli/completion/plan.ts +2 -2
- package/src/cli/completion/sources.ts +18 -18
- package/src/cli/context.ts +13 -13
- package/src/cli/flags.ts +30 -10
- package/src/cli/index.ts +26 -16
- package/src/cli/output.ts +1 -1
- package/src/cli/registry.ts +12 -12
- package/src/config/loader.ts +16 -5
- package/src/config.ts +70 -112
- package/src/daemon/catalog-writer.ts +67 -67
- package/src/daemon/daemon.ts +205 -201
- package/src/daemon/session-manager.ts +39 -39
- package/src/daemon/session-registry.ts +14 -14
- package/src/git/divergence.ts +33 -2
- package/src/git/git-poller.ts +1 -1
- package/src/git/git-status.ts +2 -2
- package/src/git/{move-worktree.ts → move-workspace.ts} +5 -5
- package/src/git/pr-status.ts +1 -1
- package/src/git/repo-discovery.ts +1 -1
- package/src/git/use-repo-discovery.ts +1 -1
- package/src/git/workspace-branch-poller.ts +54 -0
- package/src/git/workspace-divergence-poller.ts +65 -0
- package/src/git/worktree.ts +29 -0
- package/src/index.tsx +4 -4
- package/src/input/keymap/help-entries.ts +7 -5
- package/src/input/modes/bridge.ts +13 -12
- package/src/input/modes/handlers/shared.ts +1 -1
- package/src/input/modes/transitions.ts +34 -21
- package/src/input/modes/types.ts +48 -31
- package/src/ipc/manager-protocol.ts +44 -44
- package/src/ipc/protocol.ts +192 -166
- package/src/platform/play-sound.ts +184 -0
- package/src/platform/project-search.ts +8 -8
- package/src/platform/worktree-paths.ts +8 -6
- package/src/pty/assistant-question-extractor.ts +1 -1
- package/src/pty/assistant-status-detection-loop.ts +79 -55
- package/src/pty/assistant-status-detector.ts +40 -15
- package/src/pty/command-registry.ts +81 -1
- package/src/restart-terminal-manager.ts +1 -1
- package/src/services/ai-usage/provider.ts +9 -2
- package/src/services/ai-usage/spawn.ts +19 -6
- package/src/session-backend/bootstrap.ts +4 -4
- package/src/session-backend/local-session-backend.ts +78 -75
- package/src/session-backend/remote-session-backend.ts +64 -52
- package/src/session-backend/types.ts +43 -32
- package/src/settings/live.ts +90 -0
- package/src/settings/search.ts +43 -0
- package/src/settings/sections/about.ts +49 -0
- package/src/settings/sections/appearance.ts +54 -0
- package/src/settings/sections/automation.ts +105 -0
- package/src/settings/sections/commands.ts +72 -0
- package/src/settings/sections/editor.ts +56 -0
- package/src/settings/sections/experimental.ts +57 -0
- package/src/settings/sections/git.ts +112 -0
- package/src/settings/sections/index.ts +95 -0
- package/src/settings/sections/integrations.ts +23 -0
- package/src/settings/sections/layout.ts +72 -0
- package/src/settings/sections/notifications.ts +99 -0
- package/src/settings/sections/setup.ts +54 -0
- package/src/settings/sections/status-bar.ts +63 -0
- package/src/settings/settings-store.ts +207 -0
- package/src/settings/types.ts +108 -0
- package/src/snippets/run-shell-var.ts +48 -22
- package/src/state/actions.ts +342 -0
- package/src/state/app-store.ts +2 -1
- package/src/state/bars.ts +1 -1
- package/src/state/dispatch-ref.ts +1 -1
- package/src/state/layout-resize.ts +2 -2
- package/src/state/project-catalog.ts +275 -0
- package/src/state/project-data.ts +143 -0
- package/src/state/{session-persistence.ts → project-persistence.ts} +74 -68
- package/src/state/project-save.ts +46 -0
- package/src/state/project-workspaces.ts +392 -0
- package/src/state/reducers/auto-commit-state.ts +11 -10
- package/src/state/reducers/git-commit-modal-state.ts +122 -0
- package/src/state/reducers/git-mode-state.ts +1 -1
- package/src/state/reducers/git-panel-state.ts +10 -6
- package/src/state/reducers/modal-state.ts +290 -681
- package/src/state/reducers/multi-repo-state.ts +3 -6
- package/src/state/reducers/project-state.ts +312 -0
- package/src/state/reducers/settings-state.ts +80 -0
- package/src/state/reducers/tab-state.ts +107 -60
- package/src/state/reducers/ui-state.ts +4 -3
- package/src/state/selectors.ts +49 -37
- package/src/state/store.ts +57 -24
- package/src/state/types.ts +244 -405
- package/src/state/validation.ts +14 -12
- package/src/terminal-manager/manager-client.ts +32 -32
- package/src/terminal-manager/terminal-manager.ts +24 -24
- package/src/ui/components/git/git-panel.tsx +2 -2
- package/src/ui/components/git/git-view.tsx +12 -12
- package/src/ui/components/git/image-diff/image-diff-view.tsx +1 -1
- package/src/ui/components/git/pane/git-pane-widget.tsx +7 -7
- package/src/ui/components/layout/sidebar/project-list.tsx +444 -0
- package/src/ui/components/layout/sidebar/tab-item.tsx +27 -12
- package/src/ui/components/layout/sidebar/use-sidebar-auto-scroll.ts +1 -1
- package/src/ui/components/layout/sidebar/workspace-row.tsx +253 -0
- package/src/ui/components/layout/status-bar.tsx +6 -2
- package/src/ui/components/layout/terminal-pane.tsx +34 -29
- package/src/ui/components/layout/top-tab-bar.tsx +46 -36
- package/src/ui/components/modals/git/git-commit-modal.tsx +8 -8
- package/src/ui/components/modals/{sessions/create-session-modal.tsx → projects/create-project-modal.tsx} +12 -12
- package/src/ui/components/modals/{sessions/session-name-modal.tsx → projects/project-name-modal.tsx} +2 -2
- package/src/ui/components/modals/{sessions/session-picker-modal.tsx → projects/project-picker-modal.tsx} +33 -35
- package/src/ui/components/modals/settings/settings-search-modal.tsx +70 -0
- package/src/ui/components/modals/shared/form.tsx +34 -22
- package/src/ui/components/modals/shared/picker.tsx +10 -10
- package/src/ui/components/modals/shared/{worktree-delete-confirm.tsx → workspace-delete-confirm.tsx} +9 -9
- package/src/ui/components/modals/tabs/new-tab-modal.tsx +30 -324
- package/src/ui/components/modals/workspace/create-workspace-modal.tsx +101 -0
- package/src/ui/components/modals/{worktree/worktree-move-confirm-modal.tsx → workspace/workspace-move-confirm-modal.tsx} +5 -5
- package/src/ui/components/modals/{worktree/worktree-move-modal.tsx → workspace/workspace-move-modal.tsx} +29 -27
- package/src/ui/components/primitives/input-field.tsx +7 -3
- package/src/ui/components/primitives/surface.tsx +3 -0
- package/src/ui/components/settings/row-value.tsx +74 -0
- package/src/ui/components/settings/settings-row.tsx +78 -0
- package/src/ui/components/settings/settings-view.tsx +169 -0
- package/src/ui/components/setup/setup-widget.tsx +152 -0
- package/src/ui/flash/build-labels.ts +27 -27
- package/src/ui/hooks/use-activity-sprite.ts +73 -0
- package/src/ui/hooks/use-scroll-active-into-view.ts +26 -0
- package/src/ui/{session-ordering.ts → project-ordering.ts} +4 -4
- package/src/ui/root.tsx +139 -121
- package/src/ui/status-bar-model.ts +48 -34
- package/src/ui/terminal-graphics/kitty.ts +28 -2
- package/src/ui/terminal-graphics/sprites/done@330/0.png +0 -0
- package/src/ui/terminal-graphics/sprites/done@330/1.png +0 -0
- package/src/ui/terminal-graphics/sprites/idle@1000/0.png +0 -0
- package/src/ui/terminal-graphics/sprites/idle@1000/1.png +0 -0
- package/src/ui/terminal-graphics/sprites/waiting@170/0.png +0 -0
- package/src/ui/terminal-graphics/sprites/waiting@170/1.png +0 -0
- package/src/ui/terminal-graphics/sprites/working@150/0.png +0 -0
- package/src/ui/terminal-graphics/sprites/working@150/1.png +0 -0
- package/src/ui/terminal-graphics/sprites/working@150/2.png +0 -0
- package/src/ui/terminal-graphics/sprites/working@150/3.png +0 -0
- package/src/ui/terminal-graphics/sprites.ts +246 -0
- package/src/ui/truncate.ts +7 -0
- package/src/ui/widgets/registry.tsx +6 -3
- package/src/ui/widgets/widget-context-menu.ts +26 -10
- package/src/update.ts +2 -2
- package/src/app-runtime/session-actions.ts +0 -187
- package/src/cli/client/workspace-resolver.ts +0 -110
- package/src/cli/commands/workspace/close.ts +0 -32
- package/src/cli/commands/workspace/show.ts +0 -32
- package/src/cli/commands/worktree/create.ts +0 -55
- package/src/cli/commands/worktree/list.ts +0 -68
- package/src/cli/commands/worktree/remove.ts +0 -81
- package/src/git/worktree-branch-poller.ts +0 -54
- package/src/git/worktree-divergence-poller.ts +0 -59
- package/src/state/reducers/session-state.ts +0 -262
- package/src/state/session-catalog.ts +0 -143
- package/src/state/session-worktrees.ts +0 -260
- package/src/state/workspace-save.ts +0 -43
- package/src/ui/components/layout/sidebar/use-top-tab-bar-auto-scroll.ts +0 -30
- package/src/ui/components/layout/sidebar/workspace-list.tsx +0 -418
- package/src/ui/components/layout/sidebar/worktree-row.tsx +0 -149
- /package/src/ui/{components/git/image-diff → terminal-graphics}/dimensions.ts +0 -0
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# aimux
|
|
2
2
|
|
|
3
3
|
A terminal multiplexer for AI CLIs. Run Claude, Codex, OpenCode, and normal
|
|
4
|
-
shell tabs side by side in one TUI with persistent
|
|
4
|
+
shell tabs side by side in one TUI with persistent projects, split panes,
|
|
5
5
|
snippets, themes, and fully configurable keymaps.
|
|
6
6
|
|
|
7
7
|

|
|
@@ -12,19 +12,19 @@ snippets, themes, and fully configurable keymaps.
|
|
|
12
12
|
|
|
13
13
|
## Features
|
|
14
14
|
|
|
15
|
-
- multi-
|
|
15
|
+
- multi-project workflow with a dedicated project picker
|
|
16
16
|
- tabs for `claude`, `codex`, `opencode`, `grok`, `kimi`, and `terminal`
|
|
17
17
|
- split panes with pane focus and resize shortcuts
|
|
18
|
-
- persistent
|
|
18
|
+
- persistent projects with saved layout and tab state
|
|
19
19
|
- profile-isolated config, catalogs, daemon sockets, and runtime state
|
|
20
20
|
- typed keymap customization through `@brimveyn/aimux-config`
|
|
21
21
|
- snippets catalog and snippet picker
|
|
22
22
|
- configurable git pane (embedded in the sidebar or as a standalone pane) and a
|
|
23
23
|
dedicated [git mode](docs/guide/git-mode.md) for review / stage / commit /
|
|
24
24
|
push with a split or stacked diff view and shiki-powered highlighting
|
|
25
|
-
- [git worktrees](docs/guide/
|
|
26
|
-
branches — create per-branch
|
|
27
|
-
squash-move a
|
|
25
|
+
- [git worktrees](docs/guide/workspaces.md) for running agents on parallel
|
|
26
|
+
branches — create per-branch workspaces, review each against its base, and
|
|
27
|
+
squash-move a workspace's work into another
|
|
28
28
|
- built-in help generated from the resolved keymap
|
|
29
29
|
- theme picker with 67 built-in themes (shiki catalog + aimux house themes) and a `/` filter
|
|
30
30
|
|
|
@@ -59,18 +59,18 @@ Create `~/.config/aimux/default/aimux.config.ts`:
|
|
|
59
59
|
import { defineConfig, actions } from '@brimveyn/aimux-config'
|
|
60
60
|
|
|
61
61
|
export default defineConfig({
|
|
62
|
-
|
|
62
|
+
projectBar: {
|
|
63
63
|
initialPosition: 'top',
|
|
64
64
|
initialVisible: true,
|
|
65
65
|
},
|
|
66
66
|
|
|
67
67
|
keymaps: (k) =>
|
|
68
|
-
k.mode('navigation', (m) => m.map('<C-
|
|
68
|
+
k.mode('navigation', (m) => m.map('<C-g>', actions.projectPicker, 'Project picker')),
|
|
69
69
|
})
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
Typed config is startup intent, not app-managed persisted state. Fields like
|
|
73
|
-
`
|
|
73
|
+
`projectBar.initialVisible` and `projectBar.initialPosition` are reapplied on
|
|
74
74
|
every launch, so runtime UI changes for those fields do not stick while the
|
|
75
75
|
config entry remains set.
|
|
76
76
|
|
|
@@ -80,7 +80,7 @@ Then start the app:
|
|
|
80
80
|
aimux
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
On first launch, use the
|
|
83
|
+
On first launch, use the project picker flow to create your first project.
|
|
84
84
|
|
|
85
85
|
For the full setup path, see [`docs/getting-started.md`](docs/getting-started.md).
|
|
86
86
|
|
|
@@ -109,7 +109,7 @@ profile. See [`docs/concepts/profiles.md`](docs/concepts/profiles.md).
|
|
|
109
109
|
|
|
110
110
|
- `aimux.config.ts` or `aimux.config.js` - typed user config
|
|
111
111
|
- `aimux.json` - app-managed preferences and runtime state
|
|
112
|
-
- `aimux-
|
|
112
|
+
- `aimux-projects.json` - project catalog and project snapshots
|
|
113
113
|
- `aimux-snippets.json` - snippet catalog
|
|
114
114
|
|
|
115
115
|
Rule of thumb:
|
|
@@ -120,17 +120,19 @@ Rule of thumb:
|
|
|
120
120
|
|
|
121
121
|
See [`docs/concepts/config-and-state.md`](docs/concepts/config-and-state.md).
|
|
122
122
|
|
|
123
|
-
###
|
|
123
|
+
### Projects
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
125
|
+
A project is a repository you add by picking a folder. Inside it live
|
|
126
|
+
workspaces (git worktrees), and inside those live tabs — one creation action
|
|
127
|
+
per level: `Ctrl+G` for a project, `Ctrl+P` for a workspace, `Ctrl+N` for a
|
|
128
|
+
tab. A project can have:
|
|
127
129
|
|
|
128
130
|
- a name
|
|
129
131
|
- an optional project directory
|
|
130
|
-
- a persisted
|
|
131
|
-
- an order in the
|
|
132
|
+
- a persisted project snapshot
|
|
133
|
+
- an order in the project bar and project picker
|
|
132
134
|
|
|
133
|
-
See [`docs/guide/
|
|
135
|
+
See [`docs/guide/projects.md`](docs/guide/projects.md).
|
|
134
136
|
|
|
135
137
|
### Keymaps
|
|
136
138
|
|
|
@@ -149,13 +151,13 @@ See [`docs/guide/keymaps.md`](docs/guide/keymaps.md).
|
|
|
149
151
|
- `i` - focus terminal
|
|
150
152
|
- `Ctrl+Z` - leave terminal-input mode
|
|
151
153
|
- `Ctrl+N` - open new-tab modal
|
|
152
|
-
- `Ctrl+G` - open
|
|
154
|
+
- `Ctrl+G` - open project picker
|
|
153
155
|
- `Ctrl+S` - open snippet picker
|
|
154
156
|
- `Ctrl+T` - open theme picker
|
|
155
157
|
- `Ctrl+B` - toggle sidebar
|
|
156
158
|
- `Ctrl+D` - enter git mode
|
|
157
|
-
- `Ctrl+W b` - toggle
|
|
158
|
-
- `Ctrl+W 1` through `Ctrl+W 9` - switch
|
|
159
|
+
- `Ctrl+W b` - toggle project bar
|
|
160
|
+
- `Ctrl+W 1` through `Ctrl+W 9` - switch projects by index
|
|
159
161
|
|
|
160
162
|
The help modal reflects the resolved keymap, so it includes your overrides.
|
|
161
163
|
|
|
@@ -175,10 +177,10 @@ aimux restart-terminal-manager
|
|
|
175
177
|
See [`docs/reference/cli.md`](docs/reference/cli.md) for behavior details.
|
|
176
178
|
|
|
177
179
|
For agent orchestration, prefer the named `aimux worker` commands. They combine
|
|
178
|
-
isolated
|
|
180
|
+
isolated workspace creation, prompt dispatch, authoritative turn waiting, fleet
|
|
179
181
|
inspection, and guarded cleanup without shell wrappers or `jq`. Pin the target
|
|
180
|
-
with `--
|
|
181
|
-
follows whichever
|
|
182
|
+
with `--project` (or `AIMUX_PROJECT`) for anything long-running: the default
|
|
183
|
+
follows whichever project the UI opened last.
|
|
182
184
|
|
|
183
185
|
## Runtime Model
|
|
184
186
|
|
|
@@ -198,7 +200,7 @@ See [`docs/developer/architecture.md`](docs/developer/architecture.md).
|
|
|
198
200
|
- [`docs/getting-started.md`](docs/getting-started.md)
|
|
199
201
|
- [`docs/concepts/config-and-state.md`](docs/concepts/config-and-state.md)
|
|
200
202
|
- [`docs/concepts/profiles.md`](docs/concepts/profiles.md)
|
|
201
|
-
- [`docs/guide/
|
|
203
|
+
- [`docs/guide/projects.md`](docs/guide/projects.md)
|
|
202
204
|
- [`docs/guide/keymaps.md`](docs/guide/keymaps.md)
|
|
203
205
|
- [`docs/guide/themes.md`](docs/guide/themes.md)
|
|
204
206
|
- [`docs/reference/cli.md`](docs/reference/cli.md)
|
|
@@ -233,7 +235,7 @@ not collide with a globally installed `aimux` instance.
|
|
|
233
235
|
and the source of the bundled theme catalog.
|
|
234
236
|
- [herdr](https://github.com/ogulcancelik/herdr) by @ogulcancelik — the
|
|
235
237
|
per-CLI assistant status heuristics (working / waiting-input / idle)
|
|
236
|
-
used in the
|
|
238
|
+
used in the project bar are adapted from herdr's `detect.rs` rule tables.
|
|
237
239
|
|
|
238
240
|
## License
|
|
239
241
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brimveyn/aimux",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.2",
|
|
4
4
|
"description": "A terminal multiplexer for AI CLIs. Run Claude, Codex, OpenCode, Kimi side-by-side with tabbed navigation, split panes, and persistent sessions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"bump:terminal_manager": "bun run scripts/bump-protocol.ts terminal-manager"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@brimveyn/aimux-config": "0.
|
|
69
|
+
"@brimveyn/aimux-config": "0.10.0",
|
|
70
70
|
"@opentui/core": "^0.1.90",
|
|
71
71
|
"@opentui/react": "^0.1.90",
|
|
72
72
|
"@resvg/resvg-wasm": "^2.6.2",
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { $ } from 'bun'
|
|
2
2
|
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
AppState,
|
|
6
|
-
AssistantId,
|
|
7
|
-
AutoCommitState,
|
|
8
|
-
GitRefreshPayload,
|
|
9
|
-
} from '../state/types'
|
|
3
|
+
import type { AppAction } from '../state/actions'
|
|
4
|
+
import type { AppState, AssistantId, AutoCommitState, GitRefreshPayload } from '../state/types'
|
|
10
5
|
|
|
11
6
|
import { buildHeadlessInvocation, isSupportedProvider } from '../auto-commit/headless-commands'
|
|
12
7
|
import { composePromptFromTemplate, loadBriefingTemplate } from '../auto-commit/prompt-loader'
|
|
@@ -22,7 +17,7 @@ export interface TriggerInput {
|
|
|
22
17
|
assistant: AssistantId
|
|
23
18
|
hasProjectPath: boolean
|
|
24
19
|
git: GitRefreshPayload
|
|
25
|
-
|
|
20
|
+
projectId: string
|
|
26
21
|
state: AutoCommitState
|
|
27
22
|
currentHash: string
|
|
28
23
|
}
|
|
@@ -33,7 +28,7 @@ export function shouldTriggerAutoCommit(input: TriggerInput): boolean {
|
|
|
33
28
|
if (!input.hasProjectPath) return false
|
|
34
29
|
if (input.git.files.length === 0) return false
|
|
35
30
|
|
|
36
|
-
const existing = input.state.
|
|
31
|
+
const existing = input.state.byProject[input.projectId]
|
|
37
32
|
if (!existing || existing.kind === 'idle') return true
|
|
38
33
|
if (existing.workingTreeHash === input.currentHash) return false
|
|
39
34
|
return true
|
|
@@ -61,14 +56,14 @@ async function getTemplate(deps: DriverDeps): Promise<string> {
|
|
|
61
56
|
}
|
|
62
57
|
|
|
63
58
|
const UNTRACKED_FILE_BYTES_CAP = 8_000
|
|
64
|
-
const
|
|
59
|
+
const PROJECT_TAIL_BYTES_CAP = 8_000
|
|
65
60
|
|
|
66
61
|
export function extractSessionTail(buffer: string | undefined): string {
|
|
67
62
|
if (!(buffer != null && buffer !== '')) return '[no session tail available]'
|
|
68
63
|
const stripped = stripAnsi(buffer).trimEnd()
|
|
69
64
|
if (stripped.length === 0) return '[no session tail available]'
|
|
70
|
-
return stripped.length >
|
|
71
|
-
? stripped.slice(stripped.length -
|
|
65
|
+
return stripped.length > PROJECT_TAIL_BYTES_CAP
|
|
66
|
+
? stripped.slice(stripped.length - PROJECT_TAIL_BYTES_CAP)
|
|
72
67
|
: stripped
|
|
73
68
|
}
|
|
74
69
|
|
|
@@ -140,7 +135,7 @@ async function gatherContext(cwd: string, stagedOnly: boolean): Promise<Gathered
|
|
|
140
135
|
}
|
|
141
136
|
|
|
142
137
|
export interface ActivityTransitionArgs {
|
|
143
|
-
|
|
138
|
+
projectId: string
|
|
144
139
|
tabId: string
|
|
145
140
|
assistant: AssistantId
|
|
146
141
|
projectPath: string | undefined
|
|
@@ -163,7 +158,7 @@ export async function onActivityTransition(
|
|
|
163
158
|
enabled: config.enabled,
|
|
164
159
|
git: args.git,
|
|
165
160
|
hasProjectPath: !!(args.projectPath != null && args.projectPath !== ''),
|
|
166
|
-
|
|
161
|
+
projectId: args.projectId,
|
|
167
162
|
state: state.autoCommit,
|
|
168
163
|
})
|
|
169
164
|
if (!should) return
|
|
@@ -180,15 +175,15 @@ export async function onManualTrigger(
|
|
|
180
175
|
// Manual auto-commit triggers can fire outside git mode, so surface the
|
|
181
176
|
// reason as a toast rather than an inline git-pane message that's unseen.
|
|
182
177
|
toast.warning(`Auto-commit: ${reason}`)
|
|
183
|
-
deps.dispatch({
|
|
178
|
+
deps.dispatch({ projectId: args.projectId, type: 'auto-commit-clear' })
|
|
184
179
|
}
|
|
185
180
|
if (!config.enabled) return fail('disabled in config')
|
|
186
181
|
if (!args.git || args.git.files.length === 0) return fail('no changes to summarise')
|
|
187
182
|
if (!(args.projectPath != null && args.projectPath !== ''))
|
|
188
|
-
return fail('no project path for current
|
|
183
|
+
return fail('no project path for current project')
|
|
189
184
|
|
|
190
185
|
const currentHash = workingTreeHash(args.git)
|
|
191
|
-
const existing = deps.getState().autoCommit.
|
|
186
|
+
const existing = deps.getState().autoCommit.byProject[args.projectId]
|
|
192
187
|
// An in-flight generation will dispatch ready/clear; just wait for it.
|
|
193
188
|
if (existing && existing.kind === 'generating') return
|
|
194
189
|
// Manual trigger is always user-intent to regenerate, even if a ready
|
|
@@ -213,11 +208,11 @@ async function runGeneration(
|
|
|
213
208
|
if (!probe) return
|
|
214
209
|
if (!isCommandAvailable(probe.executable)) return
|
|
215
210
|
|
|
216
|
-
// Supersede any in-flight generation for this
|
|
211
|
+
// Supersede any in-flight generation for this project. Without this, a
|
|
217
212
|
// rapid sequence of activity transitions (each with a different working
|
|
218
213
|
// tree hash) would leak concurrent headless subprocesses until each hit
|
|
219
214
|
// its 60 s timeout — burning real API credits.
|
|
220
|
-
const existing = deps.getState().autoCommit.
|
|
215
|
+
const existing = deps.getState().autoCommit.byProject[args.projectId]
|
|
221
216
|
if (existing && existing.kind === 'generating') {
|
|
222
217
|
try {
|
|
223
218
|
existing.abortController.abort()
|
|
@@ -229,7 +224,7 @@ async function runGeneration(
|
|
|
229
224
|
const controller = new AbortController()
|
|
230
225
|
deps.dispatch({
|
|
231
226
|
abortController: controller,
|
|
232
|
-
|
|
227
|
+
projectId: args.projectId,
|
|
233
228
|
startedAt: Date.now(),
|
|
234
229
|
tabId: args.tabId,
|
|
235
230
|
type: 'auto-commit-generation-started',
|
|
@@ -240,7 +235,7 @@ async function runGeneration(
|
|
|
240
235
|
const stagedOnly = args.git ? hasStagedFiles(args.git) : false
|
|
241
236
|
const ctx = await gatherContext(args.projectPath as string, stagedOnly)
|
|
242
237
|
if (!ctx) {
|
|
243
|
-
deps.dispatch({
|
|
238
|
+
deps.dispatch({ projectId: args.projectId, type: 'auto-commit-clear' })
|
|
244
239
|
return
|
|
245
240
|
}
|
|
246
241
|
const tab = deps.getState().tabs.find((t) => t.id === args.tabId)
|
|
@@ -252,7 +247,7 @@ async function runGeneration(
|
|
|
252
247
|
config.models[args.assistant]
|
|
253
248
|
)
|
|
254
249
|
if (!finalInvocation) {
|
|
255
|
-
deps.dispatch({
|
|
250
|
+
deps.dispatch({ projectId: args.projectId, type: 'auto-commit-clear' })
|
|
256
251
|
return
|
|
257
252
|
}
|
|
258
253
|
|
|
@@ -262,13 +257,13 @@ async function runGeneration(
|
|
|
262
257
|
timeoutMs: config.timeoutMs,
|
|
263
258
|
})
|
|
264
259
|
if (!parsed) {
|
|
265
|
-
deps.dispatch({
|
|
260
|
+
deps.dispatch({ projectId: args.projectId, type: 'auto-commit-clear' })
|
|
266
261
|
return
|
|
267
262
|
}
|
|
268
263
|
deps.dispatch({
|
|
269
264
|
body: parsed.body,
|
|
270
265
|
generatedAt: Date.now(),
|
|
271
|
-
|
|
266
|
+
projectId: args.projectId,
|
|
272
267
|
title: parsed.title,
|
|
273
268
|
type: 'auto-commit-generation-ready',
|
|
274
269
|
workingTreeHash: currentHash,
|
|
@@ -277,14 +272,14 @@ async function runGeneration(
|
|
|
277
272
|
|
|
278
273
|
export function onGitRefresh(
|
|
279
274
|
deps: DriverDeps,
|
|
280
|
-
|
|
275
|
+
projectId: string,
|
|
281
276
|
payload: GitRefreshPayload
|
|
282
277
|
): void {
|
|
283
278
|
const state = deps.getState()
|
|
284
|
-
const current = state.autoCommit.
|
|
279
|
+
const current = state.autoCommit.byProject[projectId]
|
|
285
280
|
if (!current || current.kind === 'idle') return
|
|
286
281
|
const newHash = workingTreeHash(payload)
|
|
287
282
|
if (newHash !== current.workingTreeHash) {
|
|
288
|
-
deps.dispatch({
|
|
283
|
+
deps.dispatch({ projectId, type: 'auto-commit-clear' })
|
|
289
284
|
}
|
|
290
285
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { MutableRefObject } from 'react'
|
|
2
2
|
|
|
3
3
|
import type { SessionBackend } from '../session-backend/types'
|
|
4
|
-
import type { AppAction
|
|
4
|
+
import type { AppAction } from '../state/actions'
|
|
5
|
+
import type { LayoutState, ProjectSnapshotV1, TabSession } from '../state/types'
|
|
5
6
|
|
|
6
7
|
import { logInputDebug } from '../debug/input-log'
|
|
7
8
|
import {
|
|
@@ -10,9 +11,10 @@ import {
|
|
|
10
11
|
getSnapshotTrees,
|
|
11
12
|
toTerminalContentSize,
|
|
12
13
|
} from '../state/layout-resize'
|
|
14
|
+
import { seedTabActivity } from './workspace-activity'
|
|
13
15
|
|
|
14
16
|
export function resizeSnapshotPanes(
|
|
15
|
-
snapshot:
|
|
17
|
+
snapshot: ProjectSnapshotV1 | undefined,
|
|
16
18
|
layoutRef: MutableRefObject<LayoutState>,
|
|
17
19
|
backend: SessionBackend
|
|
18
20
|
): void {
|
|
@@ -33,64 +35,69 @@ export function resizeSnapshotPanes(
|
|
|
33
35
|
|
|
34
36
|
function mergeSnapshotTabMetadata(
|
|
35
37
|
tabs: TabSession[],
|
|
36
|
-
|
|
38
|
+
projectSnapshot: ProjectSnapshotV1 | undefined
|
|
37
39
|
): TabSession[] {
|
|
38
|
-
if (!
|
|
39
|
-
const persistedById = new Map(
|
|
40
|
+
if (!projectSnapshot) return tabs
|
|
41
|
+
const persistedById = new Map(projectSnapshot.tabs.map((tab) => [tab.id, tab]))
|
|
40
42
|
return tabs.map((tab) => {
|
|
41
43
|
const persisted = persistedById.get(tab.id)
|
|
42
|
-
return persisted?.
|
|
43
|
-
persisted?.
|
|
44
|
-
!(tab.
|
|
45
|
-
? { ...tab,
|
|
44
|
+
return persisted?.workspaceId != null &&
|
|
45
|
+
persisted?.workspaceId !== '' &&
|
|
46
|
+
!(tab.workspaceId != null && tab.workspaceId !== '')
|
|
47
|
+
? { ...tab, workspaceId: persisted.workspaceId }
|
|
46
48
|
: tab
|
|
47
49
|
})
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
function hydrateAttachedSession(
|
|
51
53
|
dispatch: (action: AppAction) => void,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
projectId: string,
|
|
55
|
+
projectSnapshot: ProjectSnapshotV1 | undefined,
|
|
54
56
|
result: Awaited<ReturnType<SessionBackend['attach']>>,
|
|
55
57
|
layoutRef: MutableRefObject<LayoutState>,
|
|
56
58
|
backend: SessionBackend
|
|
57
59
|
): void {
|
|
58
60
|
if (result) {
|
|
61
|
+
const tabs = mergeSnapshotTabMetadata(result.tabs, projectSnapshot)
|
|
59
62
|
dispatch({
|
|
60
63
|
activeTabId: result.activeTabId,
|
|
61
|
-
layoutTree:
|
|
62
|
-
layoutTrees:
|
|
63
|
-
tabGroupMap:
|
|
64
|
-
tabs
|
|
65
|
-
type: 'hydrate-
|
|
64
|
+
layoutTree: projectSnapshot?.layoutTree,
|
|
65
|
+
layoutTrees: projectSnapshot?.layoutTrees,
|
|
66
|
+
tabGroupMap: projectSnapshot?.tabGroupMap,
|
|
67
|
+
tabs,
|
|
68
|
+
type: 'hydrate-project',
|
|
66
69
|
})
|
|
67
|
-
//
|
|
68
|
-
//
|
|
70
|
+
// Same reason as the project-status snapshot below, one level down: the
|
|
71
|
+
// per-tab statuses are edge-triggered, so a workspace whose agent has been
|
|
72
|
+
// waiting since before this client started would otherwise draw nothing.
|
|
73
|
+
seedTabActivity(tabs)
|
|
74
|
+
// Dispatch the project-status snapshot *after* hydrate-project so
|
|
75
|
+
// sidebar chips reflect per-project state on attach without waiting
|
|
69
76
|
// for the next daemon-side status transition.
|
|
70
|
-
for (const entry of result.
|
|
71
|
-
dispatch({
|
|
77
|
+
for (const entry of result.initialProjectStatuses) {
|
|
78
|
+
dispatch({ projectId: entry.projectId, status: entry.status, type: 'set-project-status' })
|
|
72
79
|
}
|
|
73
|
-
resizeSnapshotPanes(
|
|
80
|
+
resizeSnapshotPanes(projectSnapshot, layoutRef, backend)
|
|
74
81
|
return
|
|
75
82
|
}
|
|
76
83
|
|
|
77
|
-
if (!
|
|
84
|
+
if (!projectSnapshot) {
|
|
78
85
|
return
|
|
79
86
|
}
|
|
80
87
|
|
|
81
88
|
dispatch({
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
89
|
+
projectId,
|
|
90
|
+
projectSnapshot,
|
|
91
|
+
type: 'load-project',
|
|
85
92
|
})
|
|
86
|
-
resizeSnapshotPanes(
|
|
93
|
+
resizeSnapshotPanes(projectSnapshot, layoutRef, backend)
|
|
87
94
|
}
|
|
88
95
|
|
|
89
96
|
interface AttachCurrentSessionOptions {
|
|
90
97
|
backend: SessionBackend
|
|
91
98
|
dispatch: (action: AppAction) => void
|
|
92
|
-
|
|
93
|
-
|
|
99
|
+
currentProjectId: string
|
|
100
|
+
currentProjectProjectSnapshot: Parameters<SessionBackend['attach']>[0]['projectSnapshot']
|
|
94
101
|
layoutRef: MutableRefObject<LayoutState>
|
|
95
102
|
attachRequestIdRef: MutableRefObject<number>
|
|
96
103
|
}
|
|
@@ -98,8 +105,8 @@ interface AttachCurrentSessionOptions {
|
|
|
98
105
|
export function attachCurrentSession({
|
|
99
106
|
attachRequestIdRef,
|
|
100
107
|
backend,
|
|
101
|
-
|
|
102
|
-
|
|
108
|
+
currentProjectId,
|
|
109
|
+
currentProjectProjectSnapshot,
|
|
103
110
|
dispatch,
|
|
104
111
|
layoutRef,
|
|
105
112
|
}: AttachCurrentSessionOptions): () => void {
|
|
@@ -111,9 +118,9 @@ export function attachCurrentSession({
|
|
|
111
118
|
try {
|
|
112
119
|
const result = await backend.attach({
|
|
113
120
|
cols: layoutRef.current.terminalCols,
|
|
121
|
+
projectId: currentProjectId,
|
|
122
|
+
projectSnapshot: currentProjectProjectSnapshot,
|
|
114
123
|
rows: layoutRef.current.terminalRows,
|
|
115
|
-
sessionId: currentSessionId,
|
|
116
|
-
workspaceSnapshot: currentSessionWorkspaceSnapshot,
|
|
117
124
|
})
|
|
118
125
|
if (cancelled || attachRequestIdRef.current !== attachRequestId) {
|
|
119
126
|
return
|
|
@@ -126,8 +133,8 @@ export function attachCurrentSession({
|
|
|
126
133
|
})
|
|
127
134
|
hydrateAttachedSession(
|
|
128
135
|
dispatch,
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
currentProjectId,
|
|
137
|
+
currentProjectProjectSnapshot,
|
|
131
138
|
result,
|
|
132
139
|
layoutRef,
|
|
133
140
|
backend
|
|
@@ -142,8 +149,8 @@ export function attachCurrentSession({
|
|
|
142
149
|
})
|
|
143
150
|
hydrateAttachedSession(
|
|
144
151
|
dispatch,
|
|
145
|
-
|
|
146
|
-
|
|
152
|
+
currentProjectId,
|
|
153
|
+
currentProjectProjectSnapshot,
|
|
147
154
|
null,
|
|
148
155
|
layoutRef,
|
|
149
156
|
backend
|