@brimveyn/aimux-config 0.4.6 → 0.5.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.
package/README.md CHANGED
@@ -44,17 +44,21 @@ import { defineConfig, actions } from '@brimveyn/aimux-config'
44
44
 
45
45
  export default defineConfig({
46
46
  sessionBar: {
47
- position: 'top',
48
- visible: true,
47
+ initialPosition: 'top',
48
+ initialVisible: true,
49
49
  },
50
50
 
51
51
  keymaps: (k) =>
52
- k.mode('navigation', (m) => m.map('<C-p>', actions.sessionPicker, 'Session picker')),
52
+ k.mode('navigation', (m) => m.map('<C-p>', actions.sessionPicker, 'Workspace picker')),
53
53
  })
54
54
  ```
55
55
 
56
56
  This example only uses surfaces that are wired into the runtime today.
57
57
 
58
+ Important: typed config expresses startup intent. Fields such as
59
+ `sessionBar.initialVisible` or `gitPane.initialMode` are reapplied on every
60
+ launch; runtime UI changes do not write back into `aimux.config.ts`.
61
+
58
62
  ## Key Notation
59
63
 
60
64
  | Notation | Meaning |
@@ -136,7 +140,7 @@ explanation.
136
140
  Pre-built actions include:
137
141
 
138
142
  - tabs: `nextTab`, `prevTab`, `newTab`, `renameTab`, `closeTab`, `restartTab`
139
- - sessions: `sessionPicker`, `switchSessionByIndex(n)` and session modal actions
143
+ - workspaces: `sessionPicker`, `switchSessionByIndex(n)` and workspace modal actions
140
144
  - snippets: `snippetPicker`, snippet editor and filter actions
141
145
  - themes: `themePicker`, `previewTheme`, `confirmTheme`, `restoreTheme`
142
146
  - panes: `splitVertical`, `splitHorizontal`, `focusPane`, `resizePane`, `closePane`
@@ -149,58 +153,44 @@ You can also bind a custom `ActionFn` for dynamic behavior.
149
153
 
150
154
  ## Themes
151
155
 
152
- aimux themes are [Shiki](https://shiki.style/themes) themes. Two house themes
153
- (`aimux`, `dracula-at-night`) ship alongside the full Shiki catalog — 67
154
- themes total. The same `Theme` object powers UI colors and code highlighting.
156
+ `theme` is a startup override surface, not the persisted selected theme.
155
157
 
156
- Declare your own themes in config:
158
+ Use it to choose the built-in light or dark family when there is no persisted
159
+ `aimux.json.themeId`, and to apply palette overrides on top of the chosen base
160
+ theme:
157
161
 
158
162
  ```ts
159
- import { defineConfig, themes } from '@brimveyn/aimux-config'
163
+ import { defineConfig } from '@brimveyn/aimux-config'
160
164
 
161
165
  export default defineConfig({
162
- theme: 'my-neon',
163
- themes: {
164
- // Palette shortcut — patch VSCode color keys on top of a base theme.
165
- 'my-neon': themes.define('My Neon', 'aimux', {
166
- 'textLink.foreground': '#ff00aa',
167
- 'terminal.ansiMagenta': '#00ffcc',
168
- }),
169
- // Or drop a full VSCode theme JSON verbatim:
170
- 'custom': themes.full({
171
- name: 'custom',
172
- displayName: 'Custom',
173
- type: 'dark',
174
- fg: '#eee',
175
- bg: '#1a1a1a',
176
- colors: {
177
- /* VSCode workbench keys */
178
- },
179
- settings: [
180
- /* TextMate token rules */
181
- ],
182
- }),
166
+ theme: {
167
+ initialMode: 'dark',
168
+ paletteOverrides: {
169
+ primary: '#7dd3fc',
170
+ warning: '#fbbf24',
171
+ },
183
172
  },
184
173
  })
185
174
  ```
186
175
 
187
- See [`../../docs/guide/themes.md`](../../docs/guide/themes.md) for picker
188
- shortcuts, the VSCode color key reference, and migration notes if you're
189
- coming from the old palette alias API.
176
+ The runtime theme picker persists the selected theme id separately in
177
+ `aimux.json`, and that persisted choice wins on restart. See
178
+ [`../../docs/guide/themes.md`](../../docs/guide/themes.md) for picker behavior,
179
+ palette guidance, and precedence details.
190
180
 
191
181
  ## Support Status
192
182
 
193
- | Surface | Status | Notes |
194
- | ------------ | ------------------ | -------------------------------------------------------------------- |
195
- | `keymaps` | Supported | Fully registered by the runtime |
196
- | `sessionBar` | Supported | Used during app initialization |
197
- | `gitPane` | Supported | Placement and rendering of the git file list (see docs reference) |
198
- | `theme` | Supported | Initial theme id; `aimux.json.themeId` wins if still known |
199
- | `themes` | Supported | User themes; appear in the picker and power synthesized highlighting |
200
- | `backends` | Typed surface only | Runtime wiring deferred |
201
- | `sidebar` | Typed surface only | Type exists, runtime not currently driven by this field |
202
- | `hooks` | Typed surface only | Type exists, runtime use not currently wired |
203
- | `snippets` | Typed surface only | Runtime currently uses `aimux-snippets.json` |
183
+ | Surface | Status | Notes |
184
+ | ------------ | ------------------ | ------------------------------------------------------------------------------------ |
185
+ | `keymaps` | Supported | Fully registered by the runtime |
186
+ | `sessionBar` | Supported | Startup overrides; app-managed runtime state still persists separately |
187
+ | `gitPane` | Supported | Startup overrides for placement and rendering; runtime state persists separately |
188
+ | `theme` | Supported | `theme.initialMode` is a startup override; persisted `aimux.json.themeId` still wins |
189
+ | `themes` | Supported | User themes; appear in the picker and power synthesized highlighting |
190
+ | `backends` | Typed surface only | Runtime wiring deferred |
191
+ | `sidebar` | Typed surface only | Type exists, runtime not currently driven by this field |
192
+ | `hooks` | Typed surface only | Type exists, runtime use not currently wired |
193
+ | `snippets` | Typed surface only | Runtime currently uses `aimux-snippets.json` |
204
194
 
205
195
  ## Backends Subpath
206
196
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brimveyn/aimux-config",
3
- "version": "0.4.6",
3
+ "version": "0.5.0",
4
4
  "description": "TypeScript configuration API for aimux — keymaps, themes, and backends with a fluent builder.",
5
5
  "keywords": [
6
6
  "aimux",
package/src/actions.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  import type { ActionFn, AppAction, KeyResult, ModeContext, ModeId } from './types'
2
2
 
3
+ import { isAutoCommitEnabled } from './auto-commit-runtime'
4
+
5
+ const AUTO_COMMIT_DISABLED_MESSAGE =
6
+ 'auto-commit is disabled — set autoCommit: { enabled: true } in aimux.config.ts'
7
+
3
8
  function r(
4
9
  actions: KeyResult['actions'] = [],
5
10
  effects: KeyResult['effects'] = [],
@@ -545,7 +550,12 @@ export const gitCommitOpen: ActionFn = (ctx: ModeContext) => {
545
550
  },
546
551
  ])
547
552
  }
548
- return r([...clearPendingDelete(ctx), { type: 'open-git-commit-modal' }], [], 'modal.git-commit')
553
+ const sessionId = ctx.state.currentSessionId ?? undefined
554
+ return r(
555
+ [...clearPendingDelete(ctx), { sessionId, type: 'open-git-commit-modal' }],
556
+ [],
557
+ 'modal.git-commit'
558
+ )
549
559
  }
550
560
 
551
561
  export const gitPush: ActionFn = (ctx: ModeContext) => {
@@ -564,18 +574,88 @@ export const gitPush: ActionFn = (ctx: ModeContext) => {
564
574
  // Modal: git-commit
565
575
  // ---------------------------------------------------------------------------
566
576
 
567
- export const gitCommitCancel: KeyResult = r([{ type: 'close-modal' }], [], 'git-mode')
568
-
569
- export const gitCommitSubmit: ActionFn = (ctx: ModeContext) => {
570
- const modal = ctx.state.modal
571
- if (modal.type !== 'git-commit') {
572
- return r([{ type: 'close-modal' }], [], 'git-mode')
573
- }
577
+ function extractGitCommitFields(modal: ModeContext['state']['modal']): {
578
+ title: string
579
+ body: string
580
+ } | null {
581
+ if (modal.type !== 'git-commit') return null
574
582
  const editBuffer = modal.editBuffer ?? ''
575
583
  const activeIsTitle = modal.activeField === 'title'
576
584
  const title = (activeIsTitle ? editBuffer : modal.contentBuffer).trim()
577
585
  const body = (activeIsTitle ? modal.contentBuffer : editBuffer).trim()
578
- return r([{ type: 'close-modal' }], [{ body, title, type: 'git-commit' }], 'git-mode')
586
+ return { body, title }
587
+ }
588
+
589
+ export const gitCommitCancel: ActionFn = (ctx: ModeContext) => {
590
+ const modal = ctx.state.modal
591
+ if (modal.type === 'git-commit' && modal.stage === 'confirm') {
592
+ return r([{ type: 'git-commit-leave-confirm' }], [], 'modal.git-commit')
593
+ }
594
+ return r([{ type: 'close-modal' }], [], 'git-mode')
595
+ }
596
+
597
+ export const gitCommitEnterConfirm: ActionFn = (ctx: ModeContext) => {
598
+ const modal = ctx.state.modal
599
+ if (modal.type !== 'git-commit') return null
600
+ if (!isAutoCommitEnabled()) {
601
+ return r([{ message: AUTO_COMMIT_DISABLED_MESSAGE, type: 'git-mode-set-message' }])
602
+ }
603
+ const fields = extractGitCommitFields(modal)
604
+ const hasTitle = !!fields && fields.title.length > 0
605
+ if (hasTitle) {
606
+ return r([{ type: 'git-commit-enter-confirm' }], [], 'modal.git-commit.confirm')
607
+ }
608
+ const sessionId = ctx.state.currentSessionId
609
+ if (!sessionId) {
610
+ return r([{ type: 'git-commit-enter-confirm' }], [], 'modal.git-commit.confirm')
611
+ }
612
+ return r(
613
+ [{ sessionId, type: 'git-commit-enter-generating' }],
614
+ [{ sessionId, type: 'generate-auto-commit-now' }],
615
+ 'modal.git-commit.generating'
616
+ )
617
+ }
618
+
619
+ export const gitCommitLeaveConfirm: KeyResult = r(
620
+ [{ type: 'git-commit-leave-confirm' }],
621
+ [],
622
+ 'modal.git-commit'
623
+ )
624
+
625
+ export const gitCommitLeaveGenerating: ActionFn = (ctx: ModeContext) => {
626
+ const sessionId = ctx.state.currentSessionId
627
+ const actionsList: AppAction[] = [{ type: 'git-commit-leave-generating' }]
628
+ if (sessionId) {
629
+ actionsList.push({ sessionId, type: 'auto-commit-clear' })
630
+ }
631
+ return r(actionsList, [], 'modal.git-commit')
632
+ }
633
+
634
+ export const gitCommitAutoAccept: ActionFn = (ctx: ModeContext) => {
635
+ if (!isAutoCommitEnabled()) {
636
+ return r([{ message: AUTO_COMMIT_DISABLED_MESSAGE, type: 'git-mode-set-message' }])
637
+ }
638
+ const fields = extractGitCommitFields(ctx.state.modal)
639
+ if (!fields) {
640
+ return r([{ type: 'close-modal' }], [], 'git-mode')
641
+ }
642
+ return r(
643
+ [{ type: 'close-modal' }],
644
+ [{ body: fields.body, title: fields.title, type: 'git-commit-auto' }],
645
+ 'git-mode'
646
+ )
647
+ }
648
+
649
+ export const gitCommitSubmit: ActionFn = (ctx: ModeContext) => {
650
+ const fields = extractGitCommitFields(ctx.state.modal)
651
+ if (!fields) {
652
+ return r([{ type: 'close-modal' }], [], 'git-mode')
653
+ }
654
+ return r(
655
+ [{ type: 'close-modal' }],
656
+ [{ body: fields.body, title: fields.title, type: 'git-commit' }],
657
+ 'git-mode'
658
+ )
579
659
  }
580
660
 
581
661
  export const gitCommitReturnKey: ActionFn = (ctx: ModeContext) => {
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Module-level flag that mirrors `autoCommit.enabled` from the resolved config.
3
+ *
4
+ * Actions (which run outside React and can't subscribe to a React context) read
5
+ * this via `isAutoCommitEnabled()`. The app sets it once at startup via
6
+ * `setAutoCommitEnabled(resolvedConfig.autoCommit.enabled)` before any children
7
+ * render, so downstream readers always see the correct value.
8
+ */
9
+
10
+ let enabled = false
11
+
12
+ export function setAutoCommitEnabled(value: boolean): void {
13
+ enabled = value
14
+ }
15
+
16
+ export function isAutoCommitEnabled(): boolean {
17
+ return enabled
18
+ }
package/src/defaults.ts CHANGED
@@ -1,8 +1,17 @@
1
- import type { ResolvedKeymapConfig } from './types'
1
+ import type { AutoCommitConfig, ResolvedKeymapConfig } from './types'
2
2
 
3
3
  import * as actions from './actions'
4
4
  import { KeymapBuilder } from './keymap-builder'
5
5
 
6
+ export const DEFAULT_AUTO_COMMIT_CONFIG: AutoCommitConfig = {
7
+ enabled: false,
8
+ models: {
9
+ claude: 'claude-haiku-4-5',
10
+ codex: 'gpt-5-mini',
11
+ },
12
+ timeoutMs: 60_000,
13
+ }
14
+
6
15
  /**
7
16
  * Build the default keymap — 1:1 transcription of all existing handlers.
8
17
  */
@@ -276,10 +285,30 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
276
285
  m
277
286
  .map('<Esc>', actions.gitCommitCancel, 'Cancel')
278
287
  .map('<C-CR>', actions.gitCommitSubmit, 'Commit')
288
+ .map('<C-a>', actions.gitCommitEnterConfirm, 'Auto-commit (stage all)')
279
289
  .map('<Tab>', actions.switchField, 'Next field')
280
290
  .map('<CR>', actions.gitCommitReturnKey, 'Newline / confirm')
281
291
  .passthrough()
282
292
  )
283
293
 
294
+ // -----------------------------------------------------------------------
295
+ // Modal: git-commit confirm (auto-commit stage all + commit)
296
+ // -----------------------------------------------------------------------
297
+ .mode('modal.git-commit.confirm', (m) =>
298
+ m
299
+ .map('<Esc>', actions.gitCommitLeaveConfirm, 'Cancel auto-commit')
300
+ .map('<CR>', actions.gitCommitAutoAccept, 'Stage all + commit')
301
+ .map('<C-CR>', actions.gitCommitAutoAccept, 'Stage all + commit')
302
+ .map('<Tab>', actions.switchField, 'Next field')
303
+ .passthrough()
304
+ )
305
+
306
+ // -----------------------------------------------------------------------
307
+ // Modal: git-commit generating (waiting for LLM)
308
+ // -----------------------------------------------------------------------
309
+ .mode('modal.git-commit.generating', (m) =>
310
+ m.map('<Esc>', actions.gitCommitLeaveGenerating, 'Cancel generation')
311
+ )
312
+
284
313
  return kb._build()
285
314
  }
package/src/index.ts CHANGED
@@ -22,6 +22,7 @@ export { paletteToShikiTheme } from './palette-to-shiki'
22
22
  export { GroupBuilder, KeymapBuilder, ModeBindingBuilder } from './keymap-builder'
23
23
  export { getDefaultKeymapConfig } from './defaults'
24
24
  export { resolveConfig } from './resolver'
25
+ export { isAutoCommitEnabled, setAutoCommitEnabled } from './auto-commit-runtime'
25
26
 
26
27
  // Type exports
27
28
  export type {
@@ -33,8 +34,11 @@ export type {
33
34
  AimuxThemeConfig,
34
35
  // User-facing config
35
36
  AimuxUserConfig,
37
+ AIUsageTool,
38
+ AIUsageToolConfig,
36
39
  AppAction,
37
40
  AppState,
41
+ AutoCommitConfig,
38
42
  BackendConfig,
39
43
  BindingDef,
40
44
  FocusMode,
@@ -68,6 +72,7 @@ export type {
68
72
  SnippetDef,
69
73
  SnippetRecord,
70
74
  SplitDirection,
75
+ StatusBarConfig,
71
76
  TabSession,
72
77
  Theme,
73
78
  ThemeId,
package/src/resolver.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import type {
2
2
  AimuxUserConfig,
3
+ AutoCommitConfig,
3
4
  ModeId,
4
5
  ModeKeymapDef,
5
6
  ResolvedConfig,
6
7
  ResolvedKeymapConfig,
7
8
  } from './types'
8
9
 
9
- import { getDefaultKeymapConfig } from './defaults'
10
+ import { DEFAULT_AUTO_COMMIT_CONFIG, getDefaultKeymapConfig } from './defaults'
10
11
  import { KeymapBuilder } from './keymap-builder'
11
12
 
12
13
  /**
@@ -16,15 +17,97 @@ import { KeymapBuilder } from './keymap-builder'
16
17
  export function resolveConfig(userConfig: AimuxUserConfig): ResolvedConfig {
17
18
  const keymaps = resolveKeymaps(userConfig)
18
19
 
20
+ const autoCommit: AutoCommitConfig = {
21
+ enabled: userConfig.autoCommit?.enabled ?? DEFAULT_AUTO_COMMIT_CONFIG.enabled,
22
+ models: { ...DEFAULT_AUTO_COMMIT_CONFIG.models, ...userConfig.autoCommit?.models },
23
+ timeoutMs: userConfig.autoCommit?.timeoutMs ?? DEFAULT_AUTO_COMMIT_CONFIG.timeoutMs,
24
+ }
25
+
19
26
  return {
27
+ autoCommit,
20
28
  backends: userConfig.backends ?? {},
21
- gitPane: userConfig.gitPane ?? {},
29
+ gitPane: resolveGitPane(userConfig.gitPane),
22
30
  hooks: userConfig.hooks ?? {},
23
31
  keymaps,
24
- sessionBar: userConfig.sessionBar ?? {},
32
+ sessionBar: resolveSessionBar(userConfig.sessionBar),
25
33
  sidebar: userConfig.sidebar ?? {},
26
34
  snippets: userConfig.snippets ?? [],
27
- theme: userConfig.theme,
35
+ statusBar: userConfig.statusBar ?? {},
36
+ theme: resolveTheme(userConfig.theme),
37
+ }
38
+ }
39
+
40
+ function resolveTheme(userConfig: AimuxUserConfig['theme']): ResolvedConfig['theme'] {
41
+ if (!userConfig) return undefined
42
+ return {
43
+ initialMode: userConfig.initialMode ?? userConfig.mode,
44
+ paletteOverrides: userConfig.paletteOverrides,
45
+ }
46
+ }
47
+
48
+ function resolveSessionBar(
49
+ userConfig: AimuxUserConfig['sessionBar']
50
+ ): ResolvedConfig['sessionBar'] {
51
+ if (!userConfig) return {}
52
+ return {
53
+ initialPosition: userConfig.initialPosition ?? userConfig.position,
54
+ initialVisible: userConfig.initialVisible ?? userConfig.visible,
55
+ }
56
+ }
57
+
58
+ function resolvePaneInitialPosition(
59
+ userConfig: NonNullable<AimuxUserConfig['gitPane']>
60
+ ): 'left' | 'right' | undefined {
61
+ if (userConfig.initialPosition === 'left' || userConfig.initialPosition === 'right') {
62
+ return userConfig.initialPosition
63
+ }
64
+ if (userConfig.position === 'left' || userConfig.position === 'right') {
65
+ return userConfig.position
66
+ }
67
+ return undefined
68
+ }
69
+
70
+ function resolveEmbeddedInitialPosition(
71
+ userConfig: NonNullable<AimuxUserConfig['gitPane']>
72
+ ): 'top' | 'bottom' | undefined {
73
+ if (userConfig.initialPosition === 'top' || userConfig.initialPosition === 'bottom') {
74
+ return userConfig.initialPosition
75
+ }
76
+ if (userConfig.position === 'top' || userConfig.position === 'bottom') {
77
+ return userConfig.position
78
+ }
79
+ return undefined
80
+ }
81
+
82
+ function resolveGitPane(userConfig: AimuxUserConfig['gitPane']): ResolvedConfig['gitPane'] {
83
+ if (!userConfig) return {}
84
+
85
+ const initialMode = userConfig.initialMode ?? userConfig.mode
86
+ const paneInitialPosition = resolvePaneInitialPosition(userConfig)
87
+ const embeddedInitialPosition = resolveEmbeddedInitialPosition(userConfig)
88
+ const shared = {
89
+ diffCount: userConfig.diffCount,
90
+ initialDiffModeRatio: userConfig.initialDiffModeRatio ?? userConfig.diffModeRatio,
91
+ initialFileListMode: userConfig.initialFileListMode ?? userConfig.fileListMode,
92
+ initialRatio: userConfig.initialRatio ?? userConfig.ratio,
93
+ initialTreeCompaction: userConfig.initialTreeCompaction ?? userConfig.treeCompaction,
94
+ initialVisible: userConfig.initialVisible ?? userConfig.visible,
95
+ path: userConfig.path,
96
+ prefetchRadius: userConfig.prefetchRadius,
97
+ }
98
+
99
+ if (initialMode === 'pane') {
100
+ return {
101
+ ...shared,
102
+ initialMode: 'pane',
103
+ initialPosition: paneInitialPosition,
104
+ }
105
+ }
106
+
107
+ return {
108
+ ...shared,
109
+ ...(initialMode === 'embedded' ? { initialMode: 'embedded' as const } : {}),
110
+ initialPosition: embeddedInitialPosition,
28
111
  }
29
112
  }
30
113
 
package/src/types.ts CHANGED
@@ -24,6 +24,8 @@ export type ModeId =
24
24
  | 'modal.help.filtering'
25
25
  | 'modal.split-picker'
26
26
  | 'modal.git-commit'
27
+ | 'modal.git-commit.confirm'
28
+ | 'modal.git-commit.generating'
27
29
  | 'modal.update-available'
28
30
 
29
31
  // ─── Primitive app types ──────────────────────────────────────────────────────
@@ -63,6 +65,7 @@ export interface TerminalLine {
63
65
 
64
66
  export interface TerminalSnapshot {
65
67
  lines: TerminalLine[]
68
+ tailLines?: TerminalLine[]
66
69
  viewportY: number
67
70
  baseY: number
68
71
  cursorVisible: boolean
@@ -291,6 +294,7 @@ export interface ModalGitCommit extends ModalBase {
291
294
  type: 'git-commit'
292
295
  activeField: 'title' | 'body'
293
296
  contentBuffer: string
297
+ stage: 'edit' | 'generating' | 'confirm'
294
298
  }
295
299
  export interface ModalSnippetEditor extends ModalBase {
296
300
  type: 'snippet-editor'
@@ -331,6 +335,28 @@ export interface SessionBarState {
331
335
  position: SessionBarPosition
332
336
  }
333
337
 
338
+ export type AutoCommitSuggestion =
339
+ | { kind: 'idle' }
340
+ | {
341
+ kind: 'generating'
342
+ tabId: string
343
+ workingTreeHash: string
344
+ abortController: AbortController
345
+ startedAt: number
346
+ }
347
+ | {
348
+ kind: 'ready'
349
+ tabId: string
350
+ workingTreeHash: string
351
+ title: string
352
+ body: string
353
+ generatedAt: number
354
+ }
355
+
356
+ export interface AutoCommitState {
357
+ bySession: Record<string, AutoCommitSuggestion>
358
+ }
359
+
334
360
  export interface AppState {
335
361
  tabs: TabSession[]
336
362
  activeTabId: string | null
@@ -349,6 +375,7 @@ export interface AppState {
349
375
  customCommands: Record<AssistantId, string>
350
376
  gitPanel: GitPanelState
351
377
  gitMode: GitModeState
378
+ autoCommit: AutoCommitState
352
379
  pendingChords: string[] | null
353
380
  }
354
381
 
@@ -386,7 +413,7 @@ export type SessionAction =
386
413
  | { type: 'set-sessions'; sessions: SessionRecord[] }
387
414
  | { type: 'create-session-record'; session: SessionRecord }
388
415
  | { type: 'rename-session-record'; sessionId: string; name: string }
389
- | { type: 'delete-session-record'; sessionId: string }
416
+ | { type: 'delete-session-record'; sessionId: string; openSessionPicker?: boolean }
390
417
  | { type: 'reorder-sessions'; orderedIds: string[] }
391
418
  | { type: 'set-session-status'; sessionId: string; status: SessionStatus }
392
419
 
@@ -503,12 +530,35 @@ export type GitModeAction =
503
530
  fromSection: GitFileSection
504
531
  toSection: GitFileSection | null
505
532
  }
506
- | { type: 'open-git-commit-modal' }
533
+ | { type: 'open-git-commit-modal'; sessionId?: string }
534
+ | { type: 'git-commit-enter-confirm' }
535
+ | { type: 'git-commit-leave-confirm' }
536
+ | { type: 'git-commit-enter-generating'; sessionId: string }
537
+ | { type: 'git-commit-leave-generating' }
507
538
 
508
539
  export type DataAction =
509
540
  | { type: 'set-snippets'; snippets: SnippetRecord[] }
510
541
  | { type: 'delete-snippet'; snippetId: string }
511
542
 
543
+ export type AutoCommitAction =
544
+ | {
545
+ type: 'auto-commit-generation-started'
546
+ sessionId: string
547
+ tabId: string
548
+ workingTreeHash: string
549
+ abortController: AbortController
550
+ startedAt: number
551
+ }
552
+ | {
553
+ type: 'auto-commit-generation-ready'
554
+ sessionId: string
555
+ workingTreeHash: string
556
+ title: string
557
+ body: string
558
+ generatedAt: number
559
+ }
560
+ | { type: 'auto-commit-clear'; sessionId: string }
561
+
512
562
  export type AppAction =
513
563
  | ModalAction
514
564
  | SessionAction
@@ -518,6 +568,7 @@ export type AppAction =
518
568
  | DataAction
519
569
  | GitPanelAction
520
570
  | GitModeAction
571
+ | AutoCommitAction
521
572
 
522
573
  // ─── Side effects ─────────────────────────────────────────────────────────────
523
574
 
@@ -552,6 +603,8 @@ export type SideEffect =
552
603
  | { type: 'git-restore'; path: string }
553
604
  | { type: 'git-rm'; path: string }
554
605
  | { type: 'git-commit'; title: string; body: string }
606
+ | { type: 'git-commit-auto'; title: string; body: string }
607
+ | { type: 'generate-auto-commit-now'; sessionId: string }
555
608
  | { type: 'git-push' }
556
609
  | { type: 'confirm-update-selection' }
557
610
  | { type: 'switch-session-by-index'; index: number }
@@ -692,7 +745,13 @@ export interface KeymapBuilderApi {
692
745
  // ─── Top-level user config ────────────────────────────────────────────────────
693
746
 
694
747
  export interface SessionBarConfig {
748
+ /** Startup override for the session bar visibility. Reapplied on each launch. */
749
+ initialVisible?: boolean
750
+ /** Startup override for the session bar position. Reapplied on each launch. */
751
+ initialPosition?: SessionBarPosition
752
+ /** @deprecated Use `initialVisible` instead. */
695
753
  visible?: boolean
754
+ /** @deprecated Use `initialPosition` instead. */
696
755
  position?: SessionBarPosition
697
756
  }
698
757
 
@@ -705,32 +764,78 @@ export type GitPanePathConfig =
705
764
  export type GitPaneDiffCountConfig = { enabled: boolean }
706
765
 
707
766
  interface GitPaneBaseConfig {
767
+ /** Startup override for git pane visibility. Reapplied on each launch. */
768
+ initialVisible?: boolean
769
+ /** Startup override for the pane split ratio. Reapplied on each launch. */
770
+ initialRatio?: number
771
+ /** Startup override for fullscreen diff ratio. Reapplied on each launch. */
772
+ initialDiffModeRatio?: number
773
+ /** Startup override for tree/flat file list mode. Reapplied on each launch. */
774
+ initialFileListMode?: GitFileListMode
775
+ /** Startup override for tree compaction. Reapplied on each launch. */
776
+ initialTreeCompaction?: boolean
777
+ path?: GitPanePathConfig
778
+ diffCount?: GitPaneDiffCountConfig
779
+ /** Prefetch N neighbouring diffs around the cursor; 0 disables. */
780
+ prefetchRadius?: number
781
+ /** @deprecated Use `initialVisible` instead. */
708
782
  visible?: boolean
783
+ /** @deprecated Use `initialRatio` instead. */
709
784
  ratio?: number
785
+ /** @deprecated Use `initialDiffModeRatio` instead. */
710
786
  diffModeRatio?: number
787
+ /** @deprecated Use `initialFileListMode` instead. */
711
788
  fileListMode?: GitFileListMode
789
+ /** @deprecated Use `initialTreeCompaction` instead. */
712
790
  treeCompaction?: boolean
713
- path?: GitPanePathConfig
714
- diffCount?: GitPaneDiffCountConfig
715
- /** Prefetch N neighbouring diffs around the cursor; 0 disables. */
716
- prefetchRadius?: number
717
791
  }
718
792
 
719
793
  export interface GitPaneEmbeddedConfig extends GitPaneBaseConfig {
794
+ initialMode?: 'embedded'
795
+ initialPosition?: 'top' | 'bottom'
796
+ /** @deprecated Use `initialMode` instead. */
720
797
  mode?: 'embedded'
798
+ /** @deprecated Use `initialPosition` instead. */
721
799
  position?: 'top' | 'bottom'
722
800
  }
723
801
 
724
802
  export interface GitPanePaneConfig extends GitPaneBaseConfig {
803
+ initialMode: 'pane'
804
+ initialPosition?: 'left' | 'right'
805
+ /** @deprecated Use `initialMode` instead. */
725
806
  mode: 'pane'
807
+ /** @deprecated Use `initialPosition` instead. */
726
808
  position?: 'left' | 'right'
727
809
  }
728
810
 
729
811
  export type GitPaneConfig = GitPaneEmbeddedConfig | GitPanePaneConfig
730
812
 
813
+ export interface AutoCommitConfig {
814
+ enabled: boolean
815
+ timeoutMs: number
816
+ models: Partial<Record<string, string>>
817
+ }
818
+
731
819
  export interface AimuxThemeConfig {
732
- mode?: ThemeMode
820
+ /** Startup override for light/dark theme family. Reapplied on each launch. */
821
+ initialMode?: ThemeMode
733
822
  paletteOverrides?: Partial<AimuxPalette>
823
+ /** @deprecated Use `initialMode` instead. */
824
+ mode?: ThemeMode
825
+ }
826
+
827
+ export type AIUsageTool = 'claude' | 'codex'
828
+
829
+ export interface AIUsageToolConfig {
830
+ enabled?: boolean
831
+ pollSeconds?: number
832
+ claudePlan?: 'auto' | 'pro' | 'max5' | 'max20'
833
+ codexWeeklyLimit?: number
834
+ tools?: AIUsageTool[]
835
+ }
836
+
837
+ export interface StatusBarConfig {
838
+ aiUsage?: AIUsageToolConfig
734
839
  }
735
840
 
736
841
  export interface AimuxUserConfig {
@@ -742,6 +847,8 @@ export interface AimuxUserConfig {
742
847
  gitPane?: GitPaneConfig
743
848
  hooks?: HooksConfig
744
849
  snippets?: SnippetDef[]
850
+ autoCommit?: Partial<AutoCommitConfig>
851
+ statusBar?: StatusBarConfig
745
852
  }
746
853
 
747
854
  // ─── Resolved config (internal) ───────────────────────────────────────────────
@@ -767,12 +874,46 @@ export interface ResolvedKeymapConfig {
767
874
  }
768
875
 
769
876
  export interface ResolvedConfig {
770
- theme: AimuxThemeConfig | undefined
877
+ theme:
878
+ | {
879
+ initialMode?: ThemeMode
880
+ paletteOverrides?: Partial<AimuxPalette>
881
+ }
882
+ | undefined
771
883
  keymaps: ResolvedKeymapConfig
772
884
  backends: Record<string, BackendConfig>
773
885
  sidebar: SidebarConfig
774
- sessionBar: SessionBarConfig
775
- gitPane: GitPaneConfig
886
+ sessionBar: {
887
+ initialVisible?: boolean
888
+ initialPosition?: SessionBarPosition
889
+ }
890
+ gitPane:
891
+ | {
892
+ initialMode?: 'embedded'
893
+ initialPosition?: 'top' | 'bottom'
894
+ initialVisible?: boolean
895
+ initialRatio?: number
896
+ initialDiffModeRatio?: number
897
+ initialFileListMode?: GitFileListMode
898
+ initialTreeCompaction?: boolean
899
+ path?: GitPanePathConfig
900
+ diffCount?: GitPaneDiffCountConfig
901
+ prefetchRadius?: number
902
+ }
903
+ | {
904
+ initialMode: 'pane'
905
+ initialPosition?: 'left' | 'right'
906
+ initialVisible?: boolean
907
+ initialRatio?: number
908
+ initialDiffModeRatio?: number
909
+ initialFileListMode?: GitFileListMode
910
+ initialTreeCompaction?: boolean
911
+ path?: GitPanePathConfig
912
+ diffCount?: GitPaneDiffCountConfig
913
+ prefetchRadius?: number
914
+ }
776
915
  hooks: HooksConfig
777
916
  snippets: SnippetDef[]
917
+ autoCommit: AutoCommitConfig
918
+ statusBar: StatusBarConfig
778
919
  }