@brimveyn/aimux 1.19.2 → 1.19.4

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.
@@ -6,7 +6,7 @@
6
6
  * `src/detect.rs` in that repo.
7
7
  *
8
8
  * The detector classifies a terminal session as `working`, `waiting-input`,
9
- * or `idle`. Built-in CLIs (claude, codex, opencode) use per-CLI substring
9
+ * or `idle`. Built-in CLIs (claude, codex, opencode, grok) have dedicated classify* functions.
10
10
  * tables. Custom CLIs fall back to a generic heuristic that (a) recognises
11
11
  * common shells as always-idle and (b) uses pane-tail change velocity plus
12
12
  * generic y/n / confirm prompt patterns.
@@ -95,7 +95,7 @@ export class AssistantStatusDetector {
95
95
  export function extractTailLines(viewport: TerminalSnapshot, lineCount: number): string[] {
96
96
  const isScrolledToBottom = viewport.viewportY === viewport.baseY
97
97
  const lines = isScrolledToBottom ? viewport.lines : (viewport.tailLines ?? viewport.lines)
98
- // Full-screen TUIs (claude, opencode) paint in the alternate buffer and
98
+ // Full-screen TUIs (claude, opencode, grok) paint in the alternate buffer and
99
99
  // often leave the last rows blank, putting their status bar higher up.
100
100
  // Skip trailing blank rows before taking the last `lineCount`.
101
101
  let end = lines.length
@@ -138,6 +138,8 @@ function classifyBuiltin(
138
138
  return classifyCodex(haystack)
139
139
  case 'opencode':
140
140
  return classifyOpencode(haystack)
141
+ case 'grok':
142
+ return classifyGrok(haystack, rawTail)
141
143
  default:
142
144
  return null
143
145
  }
@@ -212,6 +214,47 @@ function classifyOpencode(haystack: string): TabActivity {
212
214
  return 'idle'
213
215
  }
214
216
 
217
+ function classifyGrok(haystack: string, _rawTail: string): TabActivity {
218
+ // Waiting for user decision / input (plan approval, Q&A, permissions, confirms).
219
+ // These are the distinctive Grok Build TUI states that must produce 'waiting-input'
220
+ // so the rest of the system (turn lifecycle, question events, UI chips, orchestrators)
221
+ // treats grok the same as claude/codex/opencode.
222
+ if (
223
+ haystack.includes('waiting on answers') ||
224
+ haystack.includes('pprove') || // stylized "[ a ] pprove [ c ] omment [ q ] uit plan"
225
+ haystack.includes('omment') ||
226
+ haystack.includes('uit plan') ||
227
+ (haystack.includes('approve') &&
228
+ (haystack.includes('comment') || haystack.includes('quit') || haystack.includes('plan'))) ||
229
+ haystack.includes('enter :select') ||
230
+ haystack.includes('enter to select') ||
231
+ haystack.includes('enter submit') ||
232
+ haystack.includes('do you want') ||
233
+ haystack.includes('would you like') ||
234
+ haystack.includes('permission required') ||
235
+ haystack.includes('permission to') ||
236
+ haystack.includes('approve?') ||
237
+ haystack.includes('allow?')
238
+ ) {
239
+ return 'waiting-input'
240
+ }
241
+
242
+ // Agent actively reasoning or executing (mirrors claude spinner + interrupt logic).
243
+ // "Thought for Xs" is the primary visible trace while Grok thinks/plans.
244
+ if (
245
+ haystack.includes('thought for') || // "Thought for 3.4s", etc.
246
+ haystack.includes('thinking…') ||
247
+ haystack.includes('thinking ...') ||
248
+ haystack.includes('esc to interrupt') ||
249
+ haystack.includes('esc interrupt') ||
250
+ haystack.includes('esc: interrupt')
251
+ ) {
252
+ return 'working'
253
+ }
254
+
255
+ return 'idle'
256
+ }
257
+
215
258
  const GENERIC_WAITING_PATTERNS: string[] = [
216
259
  '[y/n]',
217
260
  '(y/n)',
@@ -1,10 +1,27 @@
1
1
  import type { AssistantId } from '../state/types'
2
2
 
3
+ /**
4
+ * How an assistant renders a vendor-neutral model / reasoning-effort selection
5
+ * into its own CLI args. Vendor flag syntax lives here on the assistant
6
+ * definition — the source of truth — rather than in a separate resolver, so
7
+ * there's exactly one place that knows `claude` takes `--effort` while `codex`
8
+ * takes `-c model_reasoning_effort=…`. An absent builder means the CLI has no
9
+ * control for that dimension, and passing `--model` / `--effort` for it is an
10
+ * error (see `buildAssistantModelArgs`). Values themselves are passed through
11
+ * unvalidated: the worker CLI is the source of truth for its own vocabulary and
12
+ * rejects a bad value with its own clear startup error.
13
+ */
14
+ export interface AssistantModelSpec {
15
+ buildModelArgs?: (model: string) => string[]
16
+ buildEffortArgs?: (effort: string) => string[]
17
+ }
18
+
3
19
  export interface AssistantOption {
4
20
  id: AssistantId
5
21
  label: string
6
22
  command: string
7
23
  description: string
24
+ model?: AssistantModelSpec
8
25
  }
9
26
 
10
27
  const DEFAULT_SHELL =
@@ -17,18 +34,41 @@ export const ASSISTANT_OPTIONS: AssistantOption[] = [
17
34
  description: 'Anthropic Claude CLI',
18
35
  id: 'claude',
19
36
  label: 'Claude',
37
+ model: {
38
+ buildEffortArgs: (effort) => ['--effort', effort],
39
+ buildModelArgs: (model) => ['--model', model],
40
+ },
20
41
  },
21
42
  {
22
43
  command: 'codex',
23
44
  description: 'OpenAI Codex CLI',
24
45
  id: 'codex',
25
46
  label: 'Codex',
47
+ model: {
48
+ buildEffortArgs: (effort) => ['-c', `model_reasoning_effort=${effort}`],
49
+ buildModelArgs: (model) => ['--model', model],
50
+ },
26
51
  },
27
52
  {
28
53
  command: 'opencode',
29
54
  description: 'OpenCode CLI',
30
55
  id: 'opencode',
31
56
  label: 'OpenCode',
57
+ // OpenCode takes a `provider/model` selector but has no reasoning-effort flag.
58
+ model: {
59
+ buildModelArgs: (model) => ['--model', model],
60
+ },
61
+ },
62
+ {
63
+ command: 'grok',
64
+ description: 'xAI Grok Build CLI',
65
+ id: 'grok',
66
+ label: 'Grok',
67
+ model: {
68
+ // Grok supports -m (and likely --model) plus --effort (alias --reasoning-effort).
69
+ buildEffortArgs: (effort) => ['--effort', effort],
70
+ buildModelArgs: (model) => ['-m', model],
71
+ },
32
72
  },
33
73
  {
34
74
  command: 'agy',
@@ -74,3 +114,34 @@ export function parseCommand(commandString: string): { executable: string; args:
74
114
  const parts = commandString.trim().split(/\s+/).filter(Boolean)
75
115
  return { args: parts.slice(1), executable: parts[0] ?? '' }
76
116
  }
117
+
118
+ /**
119
+ * Translate a vendor-neutral `{ model, effort }` selection into the extra CLI
120
+ * args to append to an assistant's default command. Throws if the assistant has
121
+ * no control for a requested dimension (e.g. `--effort` on OpenCode, anything on
122
+ * `terminal`) — that combination is unrepresentable, so we fail at the CLI
123
+ * boundary rather than spawn a worker that silently ignores the request.
124
+ */
125
+ export function buildAssistantModelArgs(
126
+ option: AssistantOption,
127
+ selection: { model?: string; effort?: string }
128
+ ): string[] {
129
+ const args: string[] = []
130
+ const { effort, model } = selection
131
+
132
+ if (model !== undefined && model !== '') {
133
+ if (!option.model?.buildModelArgs) {
134
+ throw new Error(`assistant '${option.id}' does not support --model`)
135
+ }
136
+ args.push(...option.model.buildModelArgs(model))
137
+ }
138
+
139
+ if (effort !== undefined && effort !== '') {
140
+ if (!option.model?.buildEffortArgs) {
141
+ throw new Error(`assistant '${option.id}' does not support --effort`)
142
+ }
143
+ args.push(...option.model.buildEffortArgs(effort))
144
+ }
145
+
146
+ return args
147
+ }
@@ -4,7 +4,13 @@ import type { ThemedToken } from 'shiki'
4
4
  import type { WorktreeTemplate } from '../config'
5
5
  import type { LayoutNode, SplitDirection } from './layout-tree'
6
6
 
7
- export type BuiltinAssistantId = 'claude' | 'codex' | 'opencode' | 'terminal' | 'antigravity'
7
+ export type BuiltinAssistantId =
8
+ | 'claude'
9
+ | 'codex'
10
+ | 'opencode'
11
+ | 'grok'
12
+ | 'terminal'
13
+ | 'antigravity'
8
14
 
9
15
  export type AssistantId = BuiltinAssistantId | (string & {})
10
16