@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.
- package/README.md +1 -1
- package/package.json +2 -2
- package/src/auto-commit/headless-commands.ts +14 -2
- package/src/cli/commands/tab/await-turn.ts +159 -0
- package/src/cli/commands/tab/await.ts +97 -0
- package/src/cli/commands/tab/create.ts +151 -20
- package/src/cli/commands/tab/prompt-io.ts +25 -0
- package/src/cli/commands/tab/run.ts +16 -147
- package/src/cli/commands/tab/send.ts +27 -1
- package/src/cli/commands/worktree/create-core.ts +96 -0
- package/src/cli/commands/worktree/create.ts +7 -80
- package/src/cli/commands/worktree/list.ts +24 -0
- package/src/cli/flags.ts +8 -1
- package/src/cli/index.ts +3 -2
- package/src/cli/registry.ts +2 -0
- package/src/config.ts +16 -7
- package/src/doctor.ts +5 -4
- package/src/index.tsx +1 -1
- package/src/pty/assistant-question-extractor.ts +1 -0
- package/src/pty/assistant-status-detector.ts +45 -2
- package/src/pty/command-registry.ts +71 -0
- package/src/state/types.ts +7 -1
|
@@ -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)
|
|
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
|
+
}
|
package/src/state/types.ts
CHANGED
|
@@ -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 =
|
|
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
|
|