@brimveyn/aimux 1.19.2 → 1.19.3
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/package.json
CHANGED
|
@@ -7,7 +7,11 @@ import {
|
|
|
7
7
|
IPC_CAPABILITY_THIN_ATTACH,
|
|
8
8
|
} from '../../../ipc/protocol'
|
|
9
9
|
import { createPrefixedId } from '../../../platform/id'
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
buildAssistantModelArgs,
|
|
12
|
+
getAllAssistantOptions,
|
|
13
|
+
parseCommand,
|
|
14
|
+
} from '../../../pty/command-registry'
|
|
11
15
|
import { SHARED_FLAGS } from '../../flags'
|
|
12
16
|
import { EXIT_OK, writeJson } from '../../output'
|
|
13
17
|
|
|
@@ -30,6 +34,16 @@ export const tabCreate: CliCommand = {
|
|
|
30
34
|
kind: 'string',
|
|
31
35
|
name: 'command',
|
|
32
36
|
},
|
|
37
|
+
{
|
|
38
|
+
description: 'model for the worker (maps to the assistant’s model flag)',
|
|
39
|
+
kind: 'string',
|
|
40
|
+
name: 'model',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
description: 'reasoning-effort level (maps to the assistant’s effort flag)',
|
|
44
|
+
kind: 'string',
|
|
45
|
+
name: 'effort',
|
|
46
|
+
},
|
|
33
47
|
{
|
|
34
48
|
description: 'worktree id the tab belongs to (defaults to the workspace’s active worktree)',
|
|
35
49
|
kind: 'string',
|
|
@@ -49,8 +63,21 @@ export const tabCreate: CliCommand = {
|
|
|
49
63
|
`unknown assistant: ${assistantId} (known: ${options.map((o) => o.id).join(', ')})`
|
|
50
64
|
)
|
|
51
65
|
}
|
|
52
|
-
const
|
|
53
|
-
typeof ctx.args.flags.command === 'string' ? ctx.args.flags.command :
|
|
66
|
+
const commandOverride =
|
|
67
|
+
typeof ctx.args.flags.command === 'string' ? ctx.args.flags.command : undefined
|
|
68
|
+
const model = typeof ctx.args.flags.model === 'string' ? ctx.args.flags.model : undefined
|
|
69
|
+
const effort = typeof ctx.args.flags.effort === 'string' ? ctx.args.flags.effort : undefined
|
|
70
|
+
|
|
71
|
+
// A full `--command` override owns the whole invocation, so `--model` /
|
|
72
|
+
// `--effort` (which only make sense as additions to the assistant default)
|
|
73
|
+
// would be ambiguous alongside it — reject rather than silently drop them.
|
|
74
|
+
if (commandOverride !== undefined && (model !== undefined || effort !== undefined)) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
'--model / --effort cannot be combined with --command (bake them into --command)'
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const command = commandOverride ?? option.command
|
|
54
81
|
const title = typeof ctx.args.flags.title === 'string' ? ctx.args.flags.title : option.label
|
|
55
82
|
const cwdRaw = typeof ctx.args.flags.cwd === 'string' ? ctx.args.flags.cwd : undefined
|
|
56
83
|
const cwd = cwdRaw === undefined ? undefined : resolvePath(cwdRaw)
|
|
@@ -81,7 +108,11 @@ export const tabCreate: CliCommand = {
|
|
|
81
108
|
// Thin-attach so we don't clobber the UI's dimensions on the same session.
|
|
82
109
|
await daemon.attach({ cols: 0, rows: 0, sessionId: workspace.id, thin: true })
|
|
83
110
|
|
|
84
|
-
const { args, executable } = parseCommand(command)
|
|
111
|
+
const { args: baseArgs, executable } = parseCommand(command)
|
|
112
|
+
// Append the model/effort flags to the assistant default. Throws if the
|
|
113
|
+
// assistant has no control for a requested dimension.
|
|
114
|
+
const modelArgs = buildAssistantModelArgs(option, { effort, model })
|
|
115
|
+
const args = [...baseArgs, ...modelArgs]
|
|
85
116
|
const tabId = createPrefixedId('tab')
|
|
86
117
|
|
|
87
118
|
// cols/rows = 0 means "fall back to the session's last attached size" on
|
|
@@ -101,7 +132,16 @@ export const tabCreate: CliCommand = {
|
|
|
101
132
|
worktreeId,
|
|
102
133
|
})
|
|
103
134
|
|
|
104
|
-
|
|
135
|
+
const resolvedCommand = [executable, ...args].join(' ')
|
|
136
|
+
writeJson({
|
|
137
|
+
assistant: assistantId,
|
|
138
|
+
command: resolvedCommand,
|
|
139
|
+
effort: effort ?? null,
|
|
140
|
+
model: model ?? null,
|
|
141
|
+
tabId,
|
|
142
|
+
title,
|
|
143
|
+
worktreeId,
|
|
144
|
+
})
|
|
105
145
|
return EXIT_OK
|
|
106
146
|
},
|
|
107
147
|
summary: 'Create a new tab in the active workspace',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CliCommand } from '../../registry'
|
|
2
2
|
|
|
3
3
|
import { listGitWorktrees } from '../../../git/worktree'
|
|
4
|
+
import { IPC_CAPABILITY_LIST_TABS } from '../../../ipc/protocol'
|
|
4
5
|
import { SHARED_FLAGS } from '../../flags'
|
|
5
6
|
import { EXIT_OK, writeJson } from '../../output'
|
|
6
7
|
|
|
@@ -12,6 +13,28 @@ export const worktreeList: CliCommand = {
|
|
|
12
13
|
const workspace = ctx.getWorkspace()
|
|
13
14
|
const records = workspace.worktrees ?? []
|
|
14
15
|
|
|
16
|
+
// Count live tabs per worktree so an orchestrator can see co-location
|
|
17
|
+
// (several workers sharing one worktree) at a glance. Best-effort: `null`
|
|
18
|
+
// when the daemon is unreachable or predates the listTabs capability —
|
|
19
|
+
// `worktree list` otherwise reads purely from the catalog + git, so we don't
|
|
20
|
+
// want a down daemon to fail it.
|
|
21
|
+
const tabCounts = new Map<string, number>()
|
|
22
|
+
let tabCountsAvailable = false
|
|
23
|
+
try {
|
|
24
|
+
const daemon = await ctx.getDaemon()
|
|
25
|
+
if (daemon.hasCapability(IPC_CAPABILITY_LIST_TABS)) {
|
|
26
|
+
const { tabs } = await daemon.listTabs(workspace.id)
|
|
27
|
+
for (const tab of tabs) {
|
|
28
|
+
if (tab.worktreeId != null) {
|
|
29
|
+
tabCounts.set(tab.worktreeId, (tabCounts.get(tab.worktreeId) ?? 0) + 1)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
tabCountsAvailable = true
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
// daemon unreachable — leave tabCount null rather than failing the list.
|
|
36
|
+
}
|
|
37
|
+
|
|
15
38
|
// Cross-check against git so we can flag catalog entries that git no
|
|
16
39
|
// longer knows about (prunable / vanished) — otherwise the CLI would
|
|
17
40
|
// happily report worktrees that don't exist on disk.
|
|
@@ -35,6 +58,7 @@ export const worktreeList: CliCommand = {
|
|
|
35
58
|
path: w.path,
|
|
36
59
|
repoRoot: w.repoRoot,
|
|
37
60
|
source: w.source,
|
|
61
|
+
tabCount: tabCountsAvailable ? (tabCounts.get(w.id) ?? 0) : null,
|
|
38
62
|
})),
|
|
39
63
|
})
|
|
40
64
|
return EXIT_OK
|
|
@@ -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,30 @@ 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
|
+
},
|
|
32
61
|
},
|
|
33
62
|
{
|
|
34
63
|
command: 'agy',
|
|
@@ -74,3 +103,34 @@ export function parseCommand(commandString: string): { executable: string; args:
|
|
|
74
103
|
const parts = commandString.trim().split(/\s+/).filter(Boolean)
|
|
75
104
|
return { args: parts.slice(1), executable: parts[0] ?? '' }
|
|
76
105
|
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Translate a vendor-neutral `{ model, effort }` selection into the extra CLI
|
|
109
|
+
* args to append to an assistant's default command. Throws if the assistant has
|
|
110
|
+
* no control for a requested dimension (e.g. `--effort` on OpenCode, anything on
|
|
111
|
+
* `terminal`) — that combination is unrepresentable, so we fail at the CLI
|
|
112
|
+
* boundary rather than spawn a worker that silently ignores the request.
|
|
113
|
+
*/
|
|
114
|
+
export function buildAssistantModelArgs(
|
|
115
|
+
option: AssistantOption,
|
|
116
|
+
selection: { model?: string; effort?: string }
|
|
117
|
+
): string[] {
|
|
118
|
+
const args: string[] = []
|
|
119
|
+
const { effort, model } = selection
|
|
120
|
+
|
|
121
|
+
if (model !== undefined && model !== '') {
|
|
122
|
+
if (!option.model?.buildModelArgs) {
|
|
123
|
+
throw new Error(`assistant '${option.id}' does not support --model`)
|
|
124
|
+
}
|
|
125
|
+
args.push(...option.model.buildModelArgs(model))
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (effort !== undefined && effort !== '') {
|
|
129
|
+
if (!option.model?.buildEffortArgs) {
|
|
130
|
+
throw new Error(`assistant '${option.id}' does not support --effort`)
|
|
131
|
+
}
|
|
132
|
+
args.push(...option.model.buildEffortArgs(effort))
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return args
|
|
136
|
+
}
|