@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,6 +1,6 @@
1
1
  import { resolve as resolvePath } from 'node:path'
2
2
 
3
- import type { WorktreeRecord } from '../../../state/types'
3
+ import type { WorkspaceRecord } from '../../../state/types'
4
4
  import type { CliContext } from '../../context'
5
5
  import type { CliCommand } from '../../registry'
6
6
 
@@ -22,7 +22,7 @@ import {
22
22
  } from '../../../pty/command-registry'
23
23
  import { CliUsageError, SHARED_FLAGS } from '../../flags'
24
24
  import { EXIT_OK, writeJson } from '../../output'
25
- import { createWorkspaceWorktree } from '../worktree/create-core'
25
+ import { createProjectWorkspace } from '../workspace/create-core'
26
26
 
27
27
  const FALLBACK_COLS = 200
28
28
  const FALLBACK_ROWS = 60
@@ -35,10 +35,10 @@ export interface CreateCliTabOptions {
35
35
  cwd?: string
36
36
  effort?: string
37
37
  model?: string
38
- newWorktree?: boolean | string
38
+ newWorkspace?: boolean | string
39
39
  title?: string
40
40
  workerName?: string
41
- worktreeId?: string
41
+ workspaceId?: string
42
42
  }
43
43
 
44
44
  export interface CreateCliTabResult {
@@ -53,26 +53,26 @@ export interface CreateCliTabResult {
53
53
  tabId: string
54
54
  title: string
55
55
  workerName: string | null
56
- worktreeId: string | null
56
+ workspaceId: string | null
57
57
  }
58
58
 
59
59
  /**
60
60
  * Resolve the cwd a spawned tab should run in. Precedence: explicit `--cwd`
61
- * (resolved to absolute) > the resolved worktree's path > undefined (the
62
- * daemon's default). Worktree record paths are already absolute.
61
+ * (resolved to absolute) > the resolved workspace's path > undefined (the
62
+ * daemon's default). Workspace record paths are already absolute.
63
63
  */
64
64
  export function resolveTabCwd(
65
65
  cwdFlag: string | undefined,
66
- worktreeRecord: { path: string } | undefined
66
+ workspaceRecord: { path: string } | undefined
67
67
  ): string | undefined {
68
68
  if (cwdFlag !== undefined) return resolvePath(cwdFlag)
69
- return worktreeRecord?.path
69
+ return workspaceRecord?.path
70
70
  }
71
71
 
72
72
  /**
73
73
  * Resolve the base command a tab launches. Mirrors the UI's precedence
74
74
  * (`src/app-runtime/side-effects.ts`): an explicit `--command` override wins,
75
- * else the workspace's persisted `customCommands[assistantId]` (so CLI workers
75
+ * else the project's persisted `customCommands[assistantId]` (so CLI workers
76
76
  * inherit e.g. `claude --dangerously-skip-permissions`), else the builtin
77
77
  * default. `getAllAssistantOptions` deliberately does NOT apply builtin
78
78
  * overrides, so this lookup must be explicit.
@@ -85,13 +85,13 @@ export function resolveAssistantCommand(
85
85
  return commandOverride ?? customCommands[option.id] ?? option.command
86
86
  }
87
87
 
88
- async function rollbackCreatedWorktree(
89
- record: WorktreeRecord,
88
+ async function rollbackCreatedWorkspace(
89
+ record: WorkspaceRecord,
90
90
  ctx: CliContext,
91
91
  originalError: unknown
92
92
  ): Promise<never> {
93
93
  const daemon = await ctx.getDaemon()
94
- const workspace = ctx.getWorkspace()
94
+ const project = ctx.getProject()
95
95
  let rollbackError: unknown
96
96
  try {
97
97
  await removeGitWorktree({
@@ -100,9 +100,9 @@ async function rollbackCreatedWorktree(
100
100
  targetPath: record.path,
101
101
  })
102
102
  await pruneEmptyWorktreeParent(record.path)
103
- await daemon.expectOk('removeWorktreeRecord', {
104
- sessionId: workspace.id,
105
- worktreeId: record.id,
103
+ await daemon.expectOk('removeWorkspaceRecord', {
104
+ projectId: project.id,
105
+ workspaceId: record.id,
106
106
  })
107
107
  } catch (error) {
108
108
  rollbackError = error
@@ -117,8 +117,8 @@ async function rollbackCreatedWorktree(
117
117
  /**
118
118
  * Create a tab without writing to stdout. Worker commands compose this helper
119
119
  * with prompt dispatch/await while `tab create` remains a thin compatibility
120
- * adapter. All validation and daemon attachment happen before a worktree is
121
- * created; any later failure rolls the fresh worktree back.
120
+ * adapter. All validation and daemon attachment happen before a workspace is
121
+ * created; any later failure rolls the fresh workspace back.
122
122
  */
123
123
  export async function createCliTab(
124
124
  ctx: CliContext,
@@ -132,10 +132,10 @@ export async function createCliTab(
132
132
  cwd: cwdRaw,
133
133
  effort,
134
134
  model,
135
- newWorktree,
135
+ newWorkspace,
136
136
  title: requestedTitle,
137
137
  workerName,
138
- worktreeId: requestedWorktreeId,
138
+ workspaceId: requestedWorkspaceId,
139
139
  } = options
140
140
  if (assistantId.length === 0) throw new CliUsageError('--assistant is required')
141
141
  if (workerName !== undefined && workerName.trim().length === 0) {
@@ -147,18 +147,18 @@ export async function createCliTab(
147
147
  )
148
148
  }
149
149
 
150
- const createFreshWorktree = newWorktree !== undefined && newWorktree !== false
151
- if (createFreshWorktree && requestedWorktreeId !== undefined) {
150
+ const createFreshWorkspace = newWorkspace !== undefined && newWorkspace !== false
151
+ if (createFreshWorkspace && requestedWorkspaceId !== undefined) {
152
152
  throw new CliUsageError(
153
- '--new-worktree creates its own; use --worktree <id> to co-locate instead'
153
+ '--new-workspace creates its own; use --workspace <id> to co-locate instead'
154
154
  )
155
155
  }
156
- if (createFreshWorktree && cwdRaw !== undefined) {
157
- throw new CliUsageError('--new-worktree sets the cwd to the new worktree; drop --cwd')
156
+ if (createFreshWorkspace && cwdRaw !== undefined) {
157
+ throw new CliUsageError('--new-workspace sets the cwd to the new workspace; drop --cwd')
158
158
  }
159
- if (!createFreshWorktree && (base !== undefined || branch !== undefined)) {
159
+ if (!createFreshWorkspace && (base !== undefined || branch !== undefined)) {
160
160
  throw new CliUsageError(
161
- '--base / --branch require --new-worktree (use `worktree create` otherwise)'
161
+ '--base / --branch require --new-workspace (use `workspace create` otherwise)'
162
162
  )
163
163
  }
164
164
 
@@ -177,7 +177,7 @@ export async function createCliTab(
177
177
  const title = requestedTitle ?? option.label
178
178
  const tabId = createPrefixedId('tab')
179
179
 
180
- const workspace = ctx.getWorkspace()
180
+ const project = ctx.getProject()
181
181
  const daemon = await ctx.getDaemon()
182
182
  if (!daemon.hasCapability(IPC_CAPABILITY_THIN_ATTACH)) {
183
183
  throw new Error(
@@ -193,44 +193,44 @@ export async function createCliTab(
193
193
  if (!daemon.hasCapability(IPC_CAPABILITY_LIST_TABS)) {
194
194
  throw new Error('daemon cannot validate worker-name uniqueness — restart aimux')
195
195
  }
196
- const existing = await daemon.listTabs(workspace.id)
196
+ const existing = await daemon.listTabs(project.id)
197
197
  if (existing.tabs.some((tab) => tab.workerName === workerName)) {
198
- throw new Error(`worker name already exists in workspace "${workspace.name}": ${workerName}`)
198
+ throw new Error(`worker name already exists in project "${project.name}": ${workerName}`)
199
199
  }
200
200
  }
201
201
 
202
- // Attach before creating a worktree so stale daemon/session failures have no
202
+ // Attach before creating a workspace so stale daemon/project failures have no
203
203
  // git-side effect.
204
- await daemon.attach({ cols: 0, rows: 0, sessionId: workspace.id, thin: true })
204
+ await daemon.attach({ cols: 0, projectId: project.id, rows: 0, thin: true })
205
205
 
206
- let worktreeId = requestedWorktreeId ?? workspace.activeWorktreeId
207
- let worktreeRecord =
208
- worktreeId !== undefined
209
- ? workspace.worktrees?.find((entry) => entry.id === worktreeId)
206
+ let workspaceId = requestedWorkspaceId ?? project.activeWorkspaceId
207
+ let workspaceRecord =
208
+ workspaceId !== undefined
209
+ ? project.workspaces?.find((entry) => entry.id === workspaceId)
210
210
  : undefined
211
- if (requestedWorktreeId !== undefined && worktreeRecord === undefined) {
212
- const ids = workspace.worktrees?.map((entry) => entry.id).join(', ') ?? '(none)'
213
- throw new Error(`unknown worktree id: ${requestedWorktreeId} (known: ${ids})`)
211
+ if (requestedWorkspaceId !== undefined && workspaceRecord === undefined) {
212
+ const ids = project.workspaces?.map((entry) => entry.id).join(', ') ?? '(none)'
213
+ throw new Error(`unknown workspace id: ${requestedWorkspaceId} (known: ${ids})`)
214
214
  }
215
215
 
216
- let createdWorktree: WorktreeRecord | undefined
217
- if (createFreshWorktree) {
218
- const worktreeName =
219
- typeof newWorktree === 'string' && newWorktree !== ''
220
- ? newWorktree
216
+ let createdWorkspace: WorkspaceRecord | undefined
217
+ if (createFreshWorkspace) {
218
+ const workspaceName =
219
+ typeof newWorkspace === 'string' && newWorkspace !== ''
220
+ ? newWorkspace
221
221
  : `${assistantId}-${tabId.slice(-6)}`
222
- createdWorktree = await createWorkspaceWorktree({
222
+ createdWorkspace = await createProjectWorkspace({
223
223
  base: base ?? 'HEAD',
224
- branch: branch ?? `aimux/${worktreeName}`,
224
+ branch: branch ?? `aimux/${workspaceName}`,
225
225
  daemon,
226
- name: worktreeName,
227
- workspace,
226
+ name: workspaceName,
227
+ project,
228
228
  })
229
- worktreeId = createdWorktree.id
230
- worktreeRecord = createdWorktree
229
+ workspaceId = createdWorkspace.id
230
+ workspaceRecord = createdWorkspace
231
231
  }
232
232
 
233
- const cwd = resolveTabCwd(cwdRaw, worktreeRecord)
233
+ const cwd = resolveTabCwd(cwdRaw, workspaceRecord)
234
234
  const useFallback = daemon.hasCapability(IPC_CAPABILITY_CREATE_TAB_SIZE_FALLBACK)
235
235
  try {
236
236
  await daemon.expectOk('createTab', {
@@ -244,28 +244,28 @@ export async function createCliTab(
244
244
  tabId,
245
245
  title,
246
246
  workerName,
247
- worktreeId,
247
+ workspaceId,
248
248
  })
249
249
  } catch (error) {
250
- if (createdWorktree !== undefined) {
251
- return rollbackCreatedWorktree(createdWorktree, ctx, error)
250
+ if (createdWorkspace !== undefined) {
251
+ return rollbackCreatedWorkspace(createdWorkspace, ctx, error)
252
252
  }
253
253
  throw error
254
254
  }
255
255
 
256
256
  return {
257
257
  assistant: assistantId,
258
- branch: worktreeRecord?.branch ?? null,
258
+ branch: workspaceRecord?.branch ?? null,
259
259
  command: [executable, ...args].join(' '),
260
260
  cwd: cwd ?? null,
261
261
  effort: effort ?? null,
262
262
  model: model ?? null,
263
- name: worktreeRecord?.name ?? null,
264
- path: worktreeRecord?.path ?? null,
263
+ name: workspaceRecord?.name ?? null,
264
+ path: workspaceRecord?.path ?? null,
265
265
  tabId,
266
266
  title,
267
267
  workerName: workerName ?? null,
268
- worktreeId: worktreeId ?? null,
268
+ workspaceId: workspaceId ?? null,
269
269
  }
270
270
  }
271
271
 
@@ -310,26 +310,27 @@ export const tabCreate: CliCommand = {
310
310
  name: 'effort',
311
311
  },
312
312
  {
313
- complete: { kind: 'dynamic', source: 'worktree' },
314
- description: 'worktree id the tab belongs to (defaults to the workspace’s active worktree)',
313
+ complete: { kind: 'dynamic', source: 'workspace' },
314
+ description: 'workspace id the tab belongs to (defaults to the project’s active workspace)',
315
315
  kind: 'string',
316
- name: 'worktree',
316
+ name: 'workspace',
317
317
  },
318
318
  {
319
319
  complete: { kind: 'none' },
320
- description: 'create a fresh worktree for this tab (optionally named: --new-worktree=<name>)',
320
+ description:
321
+ 'create a fresh workspace for this tab (optionally named: --new-workspace=<name>)',
321
322
  kind: 'optional-string',
322
- name: 'new-worktree',
323
+ name: 'new-workspace',
323
324
  },
324
325
  {
325
326
  complete: { kind: 'dynamic', source: 'git-ref' },
326
- description: 'base ref for --new-worktree (default HEAD)',
327
+ description: 'base ref for --new-workspace (default HEAD)',
327
328
  kind: 'string',
328
329
  name: 'base',
329
330
  },
330
331
  {
331
332
  complete: { kind: 'none' },
332
- description: 'branch for --new-worktree (default aimux/<name>)',
333
+ description: 'branch for --new-workspace (default aimux/<name>)',
333
334
  kind: 'string',
334
335
  name: 'branch',
335
336
  },
@@ -347,13 +348,13 @@ export const tabCreate: CliCommand = {
347
348
 
348
349
  const title = typeof ctx.args.flags.title === 'string' ? ctx.args.flags.title : undefined
349
350
  const cwdRaw = typeof ctx.args.flags.cwd === 'string' ? ctx.args.flags.cwd : undefined
350
- const newWorktreeRaw = ctx.args.flags['new-worktree']
351
- const newWorktreeFlag =
352
- typeof newWorktreeRaw === 'string' || typeof newWorktreeRaw === 'boolean'
353
- ? newWorktreeRaw
351
+ const newWorkspaceRaw = ctx.args.flags['new-workspace']
352
+ const newWorkspaceFlag =
353
+ typeof newWorkspaceRaw === 'string' || typeof newWorkspaceRaw === 'boolean'
354
+ ? newWorkspaceRaw
354
355
  : undefined
355
- const worktreeFlag =
356
- typeof ctx.args.flags.worktree === 'string' ? ctx.args.flags.worktree : undefined
356
+ const workspaceFlag =
357
+ typeof ctx.args.flags.workspace === 'string' ? ctx.args.flags.workspace : undefined
357
358
  const baseFlag = typeof ctx.args.flags.base === 'string' ? ctx.args.flags.base : undefined
358
359
  const branchFlag = typeof ctx.args.flags.branch === 'string' ? ctx.args.flags.branch : undefined
359
360
  const result = await createCliTab(ctx, {
@@ -364,9 +365,9 @@ export const tabCreate: CliCommand = {
364
365
  cwd: cwdRaw,
365
366
  effort,
366
367
  model,
367
- newWorktree: newWorktreeFlag,
368
+ newWorkspace: newWorkspaceFlag,
368
369
  title,
369
- worktreeId: worktreeFlag,
370
+ workspaceId: workspaceFlag,
370
371
  })
371
372
  writeJson(result)
372
373
  return EXIT_OK
@@ -14,7 +14,7 @@ export const tabFocus: CliCommand = {
14
14
  throw new Error('tabId is required')
15
15
  }
16
16
 
17
- const workspace = ctx.getWorkspace()
17
+ const project = ctx.getProject()
18
18
  const daemon = await ctx.getDaemon()
19
19
 
20
20
  if (!daemon.hasCapability(IPC_CAPABILITY_THIN_ATTACH)) {
@@ -22,12 +22,12 @@ export const tabFocus: CliCommand = {
22
22
  'daemon predates thinAttach capability — restart aimux to pick up the new daemon'
23
23
  )
24
24
  }
25
- await daemon.attach({ cols: 0, rows: 0, sessionId: workspace.id, thin: true })
25
+ await daemon.attach({ cols: 0, projectId: project.id, rows: 0, thin: true })
26
26
  await daemon.expectOk('setActiveTab', { tabId })
27
27
 
28
28
  writeJson({ ok: true })
29
29
  return EXIT_OK
30
30
  },
31
- summary: 'Set the active tab in the workspace',
31
+ summary: 'Set the active tab in the project',
32
32
  verb: 'focus',
33
33
  }
@@ -32,7 +32,7 @@ export const tabList: CliCommand = {
32
32
  ],
33
33
  group: 'tab',
34
34
  run: async (ctx) => {
35
- const workspace = ctx.getWorkspace()
35
+ const project = ctx.getProject()
36
36
  const daemon = await ctx.getDaemon()
37
37
  const verbose = ctx.args.flags.verbose === true
38
38
 
@@ -43,7 +43,7 @@ export const tabList: CliCommand = {
43
43
  }
44
44
 
45
45
  if (daemon.hasCapability(IPC_CAPABILITY_LIST_TABS)) {
46
- const result = await daemon.listTabs(workspace.id)
46
+ const result = await daemon.listTabs(project.id)
47
47
  const tabs = verbose ? result.tabs : result.tabs.map(stripLastLine)
48
48
  writeJson({ activeTabId: result.activeTabId, tabs })
49
49
  return EXIT_OK
@@ -56,12 +56,12 @@ export const tabList: CliCommand = {
56
56
  }
57
57
 
58
58
  // Fallback: thin-attach to read the same information without resizing the
59
- // session. Pre-listTabs daemons that advertise thinAttach would in
59
+ // project. Pre-listTabs daemons that advertise thinAttach would in
60
60
  // principle land here, but that combination shouldn't ship.
61
61
  const attach = await daemon.attach({
62
62
  cols: 0,
63
+ projectId: project.id,
63
64
  rows: 0,
64
- sessionId: workspace.id,
65
65
  thin: true,
66
66
  })
67
67
  writeJson({
@@ -73,11 +73,11 @@ export const tabList: CliCommand = {
73
73
  id: tab.id,
74
74
  status: tab.status,
75
75
  title: tab.title,
76
- worktreeId: tab.worktreeId,
76
+ workspaceId: tab.workspaceId,
77
77
  })),
78
78
  })
79
79
  return EXIT_OK
80
80
  },
81
- summary: 'List tabs in the active workspace',
81
+ summary: 'List tabs in the active project',
82
82
  verb: 'list',
83
83
  }
@@ -60,7 +60,7 @@ export const tabRun: CliCommand = {
60
60
  typeof ctx.args.flags.timeout === 'number' ? ctx.args.flags.timeout : DEFAULT_TIMEOUT_MS
61
61
  const appendEnter = ctx.args.flags['no-enter'] !== true
62
62
 
63
- const workspace = ctx.getWorkspace()
63
+ const project = ctx.getProject()
64
64
  const daemon = await ctx.getDaemon()
65
65
  if (
66
66
  !daemon.hasCapability(IPC_CAPABILITY_THIN_ATTACH) ||
@@ -72,7 +72,7 @@ export const tabRun: CliCommand = {
72
72
  )
73
73
  }
74
74
 
75
- const attach = await daemon.attach({ cols: 0, rows: 0, sessionId: workspace.id, thin: true })
75
+ const attach = await daemon.attach({ cols: 0, projectId: project.id, rows: 0, thin: true })
76
76
  if (!attach.tabs.some((t) => t.id === tabId)) {
77
77
  throw new Error(`tab not found: ${tabId}`)
78
78
  }
@@ -97,7 +97,7 @@ export const tabSend: CliCommand = {
97
97
  }
98
98
  const payload = buildPromptPayload(text, asKeys)
99
99
 
100
- const workspace = ctx.getWorkspace()
100
+ const project = ctx.getProject()
101
101
  const daemon = await ctx.getDaemon()
102
102
  if (!daemon.hasCapability(IPC_CAPABILITY_THIN_ATTACH)) {
103
103
  throw new Error(
@@ -105,7 +105,7 @@ export const tabSend: CliCommand = {
105
105
  )
106
106
  }
107
107
 
108
- await daemon.attach({ cols: 0, rows: 0, sessionId: workspace.id, thin: true })
108
+ await daemon.attach({ cols: 0, projectId: project.id, rows: 0, thin: true })
109
109
 
110
110
  if (!awaitSubmit) {
111
111
  const bytesWritten = await writePromptPayload(daemon, tabId, payload, appendEnter)
@@ -38,7 +38,7 @@ export const tabSnapshot: CliCommand = {
38
38
  }
39
39
  const renderOptions = { trim: ctx.args.flags['no-trim'] !== true }
40
40
 
41
- const workspace = ctx.getWorkspace()
41
+ const project = ctx.getProject()
42
42
  const daemon = await ctx.getDaemon()
43
43
  if (!daemon.hasCapability(IPC_CAPABILITY_THIN_ATTACH)) {
44
44
  throw new Error(
@@ -71,8 +71,8 @@ export const tabSnapshot: CliCommand = {
71
71
 
72
72
  const attach = await daemon.attach({
73
73
  cols: 0,
74
+ projectId: project.id,
74
75
  rows: 0,
75
- sessionId: workspace.id,
76
76
  thin: true,
77
77
  })
78
78
  const tab = attach.tabs.find((t) => t.id === tabId)
@@ -57,7 +57,7 @@ export const tabTail: CliCommand = {
57
57
  const followStatus = ctx.args.flags['follow-status'] === true
58
58
  const timeoutMs = typeof ctx.args.flags.timeout === 'number' ? ctx.args.flags.timeout : 0
59
59
 
60
- const workspace = ctx.getWorkspace()
60
+ const project = ctx.getProject()
61
61
  const daemon = await ctx.getDaemon()
62
62
  if (!daemon.hasCapability(IPC_CAPABILITY_THIN_ATTACH)) {
63
63
  throw new Error(
@@ -69,7 +69,7 @@ export const tabTail: CliCommand = {
69
69
  // Attach before wiring subscribers so replay renders arrive after we
70
70
  // print the "attached" marker; that keeps NDJSON output deterministic
71
71
  // for downstream consumers.
72
- const attach = await daemon.attach({ cols: 0, rows: 0, sessionId: workspace.id, thin: true })
72
+ const attach = await daemon.attach({ cols: 0, projectId: project.id, rows: 0, thin: true })
73
73
 
74
74
  // Fail fast on a bad tabId — otherwise tail would sit silent until
75
75
  // --timeout, since no matching tabRender/tabExit would ever fire.
@@ -36,7 +36,7 @@ export const tabWait: CliCommand = {
36
36
  const timeoutMs =
37
37
  typeof ctx.args.flags.timeout === 'number' ? ctx.args.flags.timeout : DEFAULT_TIMEOUT_MS
38
38
 
39
- const workspace = ctx.getWorkspace()
39
+ const project = ctx.getProject()
40
40
  const daemon = await ctx.getDaemon()
41
41
  if (!daemon.hasCapability(IPC_CAPABILITY_THIN_ATTACH)) {
42
42
  throw new Error(
@@ -46,8 +46,8 @@ export const tabWait: CliCommand = {
46
46
 
47
47
  const attach = await daemon.attach({
48
48
  cols: 0,
49
+ projectId: project.id,
49
50
  rows: 0,
50
- sessionId: workspace.id,
51
51
  thin: true,
52
52
  })
53
53
 
@@ -19,14 +19,14 @@ export const workerAwait: CliCommand = {
19
19
  ],
20
20
  group: 'worker',
21
21
  run: async (ctx) => {
22
- const { tab, workspace } = await resolveWorkerTarget(ctx, ctx.args.positionals[0] ?? '')
22
+ const { project, tab } = await resolveWorkerTarget(ctx, ctx.args.positionals[0] ?? '')
23
23
  const outcome = await awaitExistingWorker(
24
24
  ctx,
25
- workspace,
25
+ project,
26
26
  tab.id,
27
27
  typeof ctx.args.flags.timeout === 'number' ? ctx.args.flags.timeout : DEFAULT_TIMEOUT_MS
28
28
  )
29
- writeJson(workerEnvelope(workspace, workerView(workspace, tab), outcome))
29
+ writeJson(workerEnvelope(project, workerView(project, tab), outcome))
30
30
  return workerOutcomeExitCode(outcome)
31
31
  },
32
32
  summary: "Await an existing worker's in-flight turn",
@@ -11,7 +11,7 @@ import {
11
11
  IPC_CAPABILITY_THIN_ATTACH,
12
12
  IPC_CAPABILITY_TURN_LIFECYCLE,
13
13
  IPC_CAPABILITY_WORKER_METADATA,
14
- IPC_CAPABILITY_WORKTREE_LIFECYCLE_EVENTS,
14
+ IPC_CAPABILITY_WORKSPACE_LIFECYCLE_EVENTS,
15
15
  } from '../../../ipc/protocol'
16
16
  import {
17
17
  getAllAssistantOptions,
@@ -19,10 +19,10 @@ import {
19
19
  parseCommand,
20
20
  } from '../../../pty/command-registry'
21
21
  import {
22
- findPrimaryWorktree,
23
- WORKSPACE_ENV_VAR,
24
- workspaceRepoRoot,
25
- } from '../../client/workspace-resolver'
22
+ findPrimaryWorkspace,
23
+ PROJECT_ENV_VAR,
24
+ projectRepoRoot,
25
+ } from '../../client/project-resolver'
26
26
  import { SHARED_FLAGS } from '../../flags'
27
27
  import { EXIT_OK, EXIT_RUNTIME, writeJson } from '../../output'
28
28
  import { WORKER_SCHEMA_VERSION } from './shared'
@@ -33,7 +33,7 @@ const REQUIRED_DAEMON_CAPABILITIES = [
33
33
  IPC_CAPABILITY_THIN_ATTACH,
34
34
  IPC_CAPABILITY_TURN_LIFECYCLE,
35
35
  IPC_CAPABILITY_WORKER_METADATA,
36
- IPC_CAPABILITY_WORKTREE_LIFECYCLE_EVENTS,
36
+ IPC_CAPABILITY_WORKSPACE_LIFECYCLE_EVENTS,
37
37
  ] as const
38
38
 
39
39
  export const workerDoctor: CliCommand = {
@@ -42,7 +42,7 @@ export const workerDoctor: CliCommand = {
42
42
  group: 'worker',
43
43
  run: async (ctx) => {
44
44
  const daemon = await ctx.getDaemon()
45
- const workspace = ctx.getWorkspace()
45
+ const project = ctx.getProject()
46
46
  const { version } = await import('../../../../package.json')
47
47
  const { customCommands } = loadConfig()
48
48
  const assistants = getAllAssistantOptions(customCommands).map((assistant) => ({
@@ -61,8 +61,8 @@ export const workerDoctor: CliCommand = {
61
61
  const missingManagerCapabilities = [MANAGER_CAPABILITY_WORKER_METADATA].filter(
62
62
  (capability) => !managerCapabilities.includes(capability)
63
63
  )
64
- const primaryWorktree = findPrimaryWorktree(workspace)
65
- const workspaceOrigin = ctx.getWorkspaceOrigin?.() ?? 'active'
64
+ const primaryWorkspace = findPrimaryWorkspace(project)
65
+ const projectOrigin = ctx.getProjectOrigin?.() ?? 'active'
66
66
  const availableAssistants = assistants.filter((assistant) => assistant.available)
67
67
  const skillPath = fileURLToPath(
68
68
  new URL('../../../../skills/aimux-orchestrator/', import.meta.url)
@@ -78,8 +78,8 @@ export const workerDoctor: CliCommand = {
78
78
  `restart the terminal manager; it is missing: ${missingManagerCapabilities.join(', ')}`
79
79
  )
80
80
  }
81
- if (primaryWorktree === undefined) {
82
- issues.push('workspace has no primary worktree; default isolated worker runs are unavailable')
81
+ if (primaryWorkspace === undefined) {
82
+ issues.push('project has no primary workspace; default isolated worker runs are unavailable')
83
83
  }
84
84
  if (availableAssistants.length === 0) {
85
85
  issues.push('no configured assistant executable is available on PATH')
@@ -87,14 +87,14 @@ export const workerDoctor: CliCommand = {
87
87
  if (!existsSync(skillPath)) {
88
88
  issues.push(`packaged orchestrator skill is missing: ${skillPath}`)
89
89
  }
90
- // Not an issue — an inferred workspace is the normal interactive case — but
90
+ // Not an issue — an inferred project is the normal interactive case — but
91
91
  // it IS the one resolution mode that can follow the UI to another project
92
92
  // between two calls. An orchestrator dispatching a multi-hour fleet wants to
93
93
  // see this before it starts, not after it reviews diffs from the wrong repo.
94
94
  const warnings: string[] = []
95
- if (workspaceOrigin === 'active') {
95
+ if (projectOrigin === 'active') {
96
96
  warnings.push(
97
- `workspace "${workspace.name}" was inferred from the most recently opened session and follows the UI; pin it with --workspace or ${WORKSPACE_ENV_VAR}`
97
+ `project "${project.name}" was inferred from the most recently opened project and follows the UI; pin it with --project or ${PROJECT_ENV_VAR}`
98
98
  )
99
99
  }
100
100
  const ready = issues.length === 0
@@ -113,16 +113,16 @@ export const workerDoctor: CliCommand = {
113
113
  missing: missingManagerCapabilities,
114
114
  ok: missingManagerCapabilities.length === 0,
115
115
  },
116
- skill: { ok: existsSync(skillPath), path: skillPath },
117
- workspace: {
118
- hasPrimaryWorktree: primaryWorktree !== undefined,
119
- name: workspace.name,
120
- ok: primaryWorktree !== undefined,
121
- /** Repo every fresh worker worktree is cut from — confirm before dispatching. */
122
- repoRoot: workspaceRepoRoot(workspace),
116
+ project: {
117
+ hasPrimaryWorkspace: primaryWorkspace !== undefined,
118
+ name: project.name,
119
+ ok: primaryWorkspace !== undefined,
120
+ /** Repo every fresh worker workspace is cut from — confirm before dispatching. */
121
+ repoRoot: projectRepoRoot(project),
123
122
  /** 'flag' | 'env' | 'active'; only 'active' can follow the UI. */
124
- source: workspaceOrigin,
123
+ source: projectOrigin,
125
124
  },
125
+ skill: { ok: existsSync(skillPath), path: skillPath },
126
126
  },
127
127
  cliVersion: version,
128
128
  daemon: {
@@ -134,20 +134,20 @@ export const workerDoctor: CliCommand = {
134
134
  protocolVersion: daemon.getSelectedVersion(),
135
135
  },
136
136
  issues,
137
+ project: {
138
+ id: project.id,
139
+ name: project.name,
140
+ projectPath: project.projectPath ?? null,
141
+ repoRoot: projectRepoRoot(project),
142
+ source: projectOrigin,
143
+ },
137
144
  ready,
138
145
  schemaVersion: WORKER_SCHEMA_VERSION,
139
146
  skillPath,
140
147
  warnings,
141
- workspace: {
142
- id: workspace.id,
143
- name: workspace.name,
144
- projectPath: workspace.projectPath ?? null,
145
- repoRoot: workspaceRepoRoot(workspace),
146
- source: workspaceOrigin,
147
- },
148
148
  })
149
149
  return ready ? EXIT_OK : EXIT_RUNTIME
150
150
  },
151
- summary: 'Check worker prerequisites, versions, assistants, and workspace',
151
+ summary: 'Check worker prerequisites, versions, assistants, and project',
152
152
  verb: 'doctor',
153
153
  }