@brimveyn/aimux-config 0.4.5 → 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.5",
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
@@ -168,7 +171,8 @@ export interface GitPaneState {
168
171
  visible: boolean
169
172
  mode: 'embedded' | 'pane'
170
173
  position: 'top' | 'bottom' | 'left' | 'right'
171
- ratio: number
174
+ paneRatio: number
175
+ embeddedRatio: number
172
176
  diffModeRatio: number
173
177
  fileListMode: GitFileListMode
174
178
  treeCompaction: boolean
@@ -290,6 +294,7 @@ export interface ModalGitCommit extends ModalBase {
290
294
  type: 'git-commit'
291
295
  activeField: 'title' | 'body'
292
296
  contentBuffer: string
297
+ stage: 'edit' | 'generating' | 'confirm'
293
298
  }
294
299
  export interface ModalSnippetEditor extends ModalBase {
295
300
  type: 'snippet-editor'
@@ -330,6 +335,28 @@ export interface SessionBarState {
330
335
  position: SessionBarPosition
331
336
  }
332
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
+
333
360
  export interface AppState {
334
361
  tabs: TabSession[]
335
362
  activeTabId: string | null
@@ -348,6 +375,7 @@ export interface AppState {
348
375
  customCommands: Record<AssistantId, string>
349
376
  gitPanel: GitPanelState
350
377
  gitMode: GitModeState
378
+ autoCommit: AutoCommitState
351
379
  pendingChords: string[] | null
352
380
  }
353
381
 
@@ -385,7 +413,7 @@ export type SessionAction =
385
413
  | { type: 'set-sessions'; sessions: SessionRecord[] }
386
414
  | { type: 'create-session-record'; session: SessionRecord }
387
415
  | { type: 'rename-session-record'; sessionId: string; name: string }
388
- | { type: 'delete-session-record'; sessionId: string }
416
+ | { type: 'delete-session-record'; sessionId: string; openSessionPicker?: boolean }
389
417
  | { type: 'reorder-sessions'; orderedIds: string[] }
390
418
  | { type: 'set-session-status'; sessionId: string; status: SessionStatus }
391
419
 
@@ -443,10 +471,12 @@ export type LayoutAction =
443
471
  export type UIAction =
444
472
  | { type: 'toggle-sidebar' }
445
473
  | { type: 'resize-sidebar'; delta: number }
474
+ | { type: 'set-sidebar-width'; width: number }
446
475
  | { type: 'set-focus-mode'; focusMode: FocusMode }
447
476
  | { type: 'set-terminal-size'; cols: number; rows: number }
448
477
  | { type: 'toggle-git-pane' }
449
478
  | { type: 'resize-git-pane'; delta: number }
479
+ | { type: 'set-git-pane-ratio'; target: 'pane' | 'embedded'; ratio: number }
450
480
  | { type: 'resize-git-diff-pane'; delta: number }
451
481
  | { type: 'set-git-pane-mode'; mode: 'embedded' | 'pane' }
452
482
  | { type: 'set-git-pane-position'; position: 'top' | 'bottom' | 'left' | 'right' }
@@ -500,12 +530,35 @@ export type GitModeAction =
500
530
  fromSection: GitFileSection
501
531
  toSection: GitFileSection | null
502
532
  }
503
- | { 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' }
504
538
 
505
539
  export type DataAction =
506
540
  | { type: 'set-snippets'; snippets: SnippetRecord[] }
507
541
  | { type: 'delete-snippet'; snippetId: string }
508
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
+
509
562
  export type AppAction =
510
563
  | ModalAction
511
564
  | SessionAction
@@ -515,6 +568,7 @@ export type AppAction =
515
568
  | DataAction
516
569
  | GitPanelAction
517
570
  | GitModeAction
571
+ | AutoCommitAction
518
572
 
519
573
  // ─── Side effects ─────────────────────────────────────────────────────────────
520
574
 
@@ -549,6 +603,8 @@ export type SideEffect =
549
603
  | { type: 'git-restore'; path: string }
550
604
  | { type: 'git-rm'; path: string }
551
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 }
552
608
  | { type: 'git-push' }
553
609
  | { type: 'confirm-update-selection' }
554
610
  | { type: 'switch-session-by-index'; index: number }
@@ -689,7 +745,13 @@ export interface KeymapBuilderApi {
689
745
  // ─── Top-level user config ────────────────────────────────────────────────────
690
746
 
691
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. */
692
753
  visible?: boolean
754
+ /** @deprecated Use `initialPosition` instead. */
693
755
  position?: SessionBarPosition
694
756
  }
695
757
 
@@ -702,32 +764,78 @@ export type GitPanePathConfig =
702
764
  export type GitPaneDiffCountConfig = { enabled: boolean }
703
765
 
704
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. */
705
782
  visible?: boolean
783
+ /** @deprecated Use `initialRatio` instead. */
706
784
  ratio?: number
785
+ /** @deprecated Use `initialDiffModeRatio` instead. */
707
786
  diffModeRatio?: number
787
+ /** @deprecated Use `initialFileListMode` instead. */
708
788
  fileListMode?: GitFileListMode
789
+ /** @deprecated Use `initialTreeCompaction` instead. */
709
790
  treeCompaction?: boolean
710
- path?: GitPanePathConfig
711
- diffCount?: GitPaneDiffCountConfig
712
- /** Prefetch N neighbouring diffs around the cursor; 0 disables. */
713
- prefetchRadius?: number
714
791
  }
715
792
 
716
793
  export interface GitPaneEmbeddedConfig extends GitPaneBaseConfig {
794
+ initialMode?: 'embedded'
795
+ initialPosition?: 'top' | 'bottom'
796
+ /** @deprecated Use `initialMode` instead. */
717
797
  mode?: 'embedded'
798
+ /** @deprecated Use `initialPosition` instead. */
718
799
  position?: 'top' | 'bottom'
719
800
  }
720
801
 
721
802
  export interface GitPanePaneConfig extends GitPaneBaseConfig {
803
+ initialMode: 'pane'
804
+ initialPosition?: 'left' | 'right'
805
+ /** @deprecated Use `initialMode` instead. */
722
806
  mode: 'pane'
807
+ /** @deprecated Use `initialPosition` instead. */
723
808
  position?: 'left' | 'right'
724
809
  }
725
810
 
726
811
  export type GitPaneConfig = GitPaneEmbeddedConfig | GitPanePaneConfig
727
812
 
813
+ export interface AutoCommitConfig {
814
+ enabled: boolean
815
+ timeoutMs: number
816
+ models: Partial<Record<string, string>>
817
+ }
818
+
728
819
  export interface AimuxThemeConfig {
729
- mode?: ThemeMode
820
+ /** Startup override for light/dark theme family. Reapplied on each launch. */
821
+ initialMode?: ThemeMode
730
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
731
839
  }
732
840
 
733
841
  export interface AimuxUserConfig {
@@ -739,6 +847,8 @@ export interface AimuxUserConfig {
739
847
  gitPane?: GitPaneConfig
740
848
  hooks?: HooksConfig
741
849
  snippets?: SnippetDef[]
850
+ autoCommit?: Partial<AutoCommitConfig>
851
+ statusBar?: StatusBarConfig
742
852
  }
743
853
 
744
854
  // ─── Resolved config (internal) ───────────────────────────────────────────────
@@ -764,12 +874,46 @@ export interface ResolvedKeymapConfig {
764
874
  }
765
875
 
766
876
  export interface ResolvedConfig {
767
- theme: AimuxThemeConfig | undefined
877
+ theme:
878
+ | {
879
+ initialMode?: ThemeMode
880
+ paletteOverrides?: Partial<AimuxPalette>
881
+ }
882
+ | undefined
768
883
  keymaps: ResolvedKeymapConfig
769
884
  backends: Record<string, BackendConfig>
770
885
  sidebar: SidebarConfig
771
- sessionBar: SessionBarConfig
772
- 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
+ }
773
915
  hooks: HooksConfig
774
916
  snippets: SnippetDef[]
917
+ autoCommit: AutoCommitConfig
918
+ statusBar: StatusBarConfig
775
919
  }