@aibridge/cli 0.1.1 → 0.3.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 +15 -11
- package/dist/cli.mjs +1 -1
- package/dist/{context-8JjDzZBG.mjs → context-B6I4QI9z.mjs} +92 -134
- package/dist/index.d.mts +9 -14
- package/dist/index.mjs +2 -2
- package/package.json +6 -6
- package/src/app.ts +1 -1
- package/src/commands/image-gen/command.ts +9 -17
- package/src/commands/image-gen/impl.ts +46 -87
- package/src/commands/implement/command.ts +2 -3
- package/src/commands/implement/impl.ts +3 -3
- package/src/commands/plan/command.ts +3 -5
- package/src/commands/plan/impl.ts +5 -9
- package/src/commands/review/command.ts +3 -5
- package/src/commands/review/impl.ts +5 -9
- package/src/commands/subagent/command.ts +3 -4
- package/src/commands/subagent/impl.ts +4 -10
- package/src/delegate.ts +1 -1
- package/src/driver.ts +3 -4
- package/src/drivers.ts +1 -0
- package/src/flagMapping.test.ts +27 -4
- package/src/index.ts +0 -3
- package/src/models.test.ts +28 -14
- package/src/models.ts +35 -21
- package/src/quotaPreflight.ts +1 -2
package/src/flagMapping.test.ts
CHANGED
|
@@ -35,7 +35,7 @@ describe('flag mapping & defaults lock', () => {
|
|
|
35
35
|
it('plan command maps defaults correctly', async () => {
|
|
36
36
|
mockPlanImpl.mockReset();
|
|
37
37
|
const ctx = fakeCtx();
|
|
38
|
-
await runCli(ctx, ['plan', 'do something']);
|
|
38
|
+
await runCli(ctx, ['plan', '--model', 'xai-grok/grok-4.5', '--out', 'plan.md', 'do something']);
|
|
39
39
|
expect(mockPlanImpl).toHaveBeenCalledTimes(1);
|
|
40
40
|
const [call] = mockPlanImpl.mock.calls;
|
|
41
41
|
expect(call).toBeDefined();
|
|
@@ -43,6 +43,8 @@ describe('flag mapping & defaults lock', () => {
|
|
|
43
43
|
const [flags, prompt] = call;
|
|
44
44
|
expect(prompt).toBe('do something');
|
|
45
45
|
expect(flags).toEqual({
|
|
46
|
+
model: 'xai-grok/grok-4.5',
|
|
47
|
+
out: 'plan.md',
|
|
46
48
|
preflight: true,
|
|
47
49
|
});
|
|
48
50
|
});
|
|
@@ -50,7 +52,17 @@ describe('flag mapping & defaults lock', () => {
|
|
|
50
52
|
it('plan command handles --no-preflight and --timeout', async () => {
|
|
51
53
|
mockPlanImpl.mockReset();
|
|
52
54
|
const ctx = fakeCtx();
|
|
53
|
-
await runCli(ctx, [
|
|
55
|
+
await runCli(ctx, [
|
|
56
|
+
'plan',
|
|
57
|
+
'--model',
|
|
58
|
+
'xai-grok/grok-4.5',
|
|
59
|
+
'--out',
|
|
60
|
+
'plan.md',
|
|
61
|
+
'--no-preflight',
|
|
62
|
+
'--timeout',
|
|
63
|
+
'120',
|
|
64
|
+
'task',
|
|
65
|
+
]);
|
|
54
66
|
expect(mockPlanImpl).toHaveBeenCalledTimes(1);
|
|
55
67
|
const [call] = mockPlanImpl.mock.calls;
|
|
56
68
|
expect(call).toBeDefined();
|
|
@@ -58,6 +70,8 @@ describe('flag mapping & defaults lock', () => {
|
|
|
58
70
|
const [flags, prompt] = call;
|
|
59
71
|
expect(prompt).toBe('task');
|
|
60
72
|
expect(flags).toEqual({
|
|
73
|
+
model: 'xai-grok/grok-4.5',
|
|
74
|
+
out: 'plan.md',
|
|
61
75
|
preflight: false,
|
|
62
76
|
timeout: 120,
|
|
63
77
|
});
|
|
@@ -66,7 +80,7 @@ describe('flag mapping & defaults lock', () => {
|
|
|
66
80
|
it('subagent command maps defaults correctly', async () => {
|
|
67
81
|
mockSubagentImpl.mockReset();
|
|
68
82
|
const ctx = fakeCtx();
|
|
69
|
-
await runCli(ctx, ['subagent', 'hello agent']);
|
|
83
|
+
await runCli(ctx, ['subagent', '--model', 'xai-grok/grok-4.5', 'hello agent']);
|
|
70
84
|
expect(mockSubagentImpl).toHaveBeenCalledTimes(1);
|
|
71
85
|
const [call] = mockSubagentImpl.mock.calls;
|
|
72
86
|
expect(call).toBeDefined();
|
|
@@ -74,6 +88,7 @@ describe('flag mapping & defaults lock', () => {
|
|
|
74
88
|
const [flags, prompt] = call;
|
|
75
89
|
expect(prompt).toBe('hello agent');
|
|
76
90
|
expect(flags).toEqual({
|
|
91
|
+
model: 'xai-grok/grok-4.5',
|
|
77
92
|
tools: true,
|
|
78
93
|
preflight: true,
|
|
79
94
|
json: false,
|
|
@@ -83,7 +98,14 @@ describe('flag mapping & defaults lock', () => {
|
|
|
83
98
|
it('subagent command handles --no-tools and --no-preflight', async () => {
|
|
84
99
|
mockSubagentImpl.mockReset();
|
|
85
100
|
const ctx = fakeCtx();
|
|
86
|
-
await runCli(ctx, [
|
|
101
|
+
await runCli(ctx, [
|
|
102
|
+
'subagent',
|
|
103
|
+
'--model',
|
|
104
|
+
'xai-grok/grok-4.5',
|
|
105
|
+
'--no-tools',
|
|
106
|
+
'--no-preflight',
|
|
107
|
+
'hello agent',
|
|
108
|
+
]);
|
|
87
109
|
expect(mockSubagentImpl).toHaveBeenCalledTimes(1);
|
|
88
110
|
const [call] = mockSubagentImpl.mock.calls;
|
|
89
111
|
expect(call).toBeDefined();
|
|
@@ -91,6 +113,7 @@ describe('flag mapping & defaults lock', () => {
|
|
|
91
113
|
const [flags, prompt] = call;
|
|
92
114
|
expect(prompt).toBe('hello agent');
|
|
93
115
|
expect(flags).toEqual({
|
|
116
|
+
model: 'xai-grok/grok-4.5',
|
|
94
117
|
tools: false,
|
|
95
118
|
preflight: false,
|
|
96
119
|
json: false,
|
package/src/index.ts
CHANGED
package/src/models.test.ts
CHANGED
|
@@ -22,7 +22,17 @@ describe('models registry', () => {
|
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
it('rejects short aliases — canonical slugs only', () => {
|
|
25
|
-
for (const alias of [
|
|
25
|
+
for (const alias of [
|
|
26
|
+
'grok',
|
|
27
|
+
'gemini',
|
|
28
|
+
'codex',
|
|
29
|
+
'sonnet',
|
|
30
|
+
'opus',
|
|
31
|
+
'gemini-3.6',
|
|
32
|
+
'gpt-oss',
|
|
33
|
+
'anthropic-claude/sonnet',
|
|
34
|
+
'anthropic-claude/opus',
|
|
35
|
+
]) {
|
|
26
36
|
expect(resolveModel(alias)).toBeUndefined();
|
|
27
37
|
}
|
|
28
38
|
});
|
|
@@ -32,8 +42,8 @@ describe('models registry', () => {
|
|
|
32
42
|
expect(grokMedium?.spec.slug).toBe('xai-grok/grok-4.5');
|
|
33
43
|
expect(grokMedium?.effort).toBe('medium');
|
|
34
44
|
|
|
35
|
-
const sonnetMax = resolveModel('anthropic-claude/sonnet-max');
|
|
36
|
-
expect(sonnetMax?.spec.slug).toBe('anthropic-claude/sonnet');
|
|
45
|
+
const sonnetMax = resolveModel('anthropic-claude/sonnet-5-max');
|
|
46
|
+
expect(sonnetMax?.spec.slug).toBe('anthropic-claude/sonnet-5');
|
|
37
47
|
expect(sonnetMax?.effort).toBe('max');
|
|
38
48
|
});
|
|
39
49
|
|
|
@@ -71,37 +81,41 @@ describe('models registry', () => {
|
|
|
71
81
|
if (!grok) throw new Error('grok resolution failed');
|
|
72
82
|
expect(backendModelId(grok)).toBe('grok-4.5');
|
|
73
83
|
|
|
74
|
-
const sonnet = resolveModel('anthropic-claude/sonnet');
|
|
84
|
+
const sonnet = resolveModel('anthropic-claude/sonnet-5');
|
|
75
85
|
if (!sonnet) throw new Error('sonnet resolution failed');
|
|
76
|
-
expect(backendModelId(sonnet)).toBe('sonnet');
|
|
86
|
+
expect(backendModelId(sonnet)).toBe('claude-sonnet-5');
|
|
77
87
|
});
|
|
78
88
|
|
|
79
|
-
it('marks
|
|
89
|
+
it('marks codex, grok, and gemini-3.6-flash seats as image-gen capable', () => {
|
|
80
90
|
const codex = resolveModel('openai-codex/gpt-5.6-sol');
|
|
81
91
|
const grok = resolveModel('xai-grok/grok-4.5');
|
|
82
92
|
const gemini = resolveModel('google-antigravity/gemini-3.6-flash');
|
|
83
|
-
|
|
93
|
+
const claudeSonnet = resolveModel('anthropic-claude/sonnet-5');
|
|
94
|
+
if (!codex || !grok || !gemini || !claudeSonnet) throw new Error('resolution failed');
|
|
84
95
|
expect(supportsImageGen(codex)).toBe(true);
|
|
85
96
|
expect(supportsImageGen(grok)).toBe(true);
|
|
86
|
-
expect(supportsImageGen(gemini)).toBe(
|
|
97
|
+
expect(supportsImageGen(gemini)).toBe(true);
|
|
98
|
+
expect(supportsImageGen(claudeSonnet)).toBe(false);
|
|
87
99
|
});
|
|
88
100
|
|
|
89
101
|
it('lists only image-capable seats when imageOnly', () => {
|
|
90
102
|
const lines = listModelHelpLines({ imageOnly: true }).join('\n');
|
|
91
103
|
expect(lines).toContain('xai-grok/grok-4.5');
|
|
92
104
|
expect(lines).toContain('openai-codex/gpt-5.6-sol');
|
|
93
|
-
expect(lines).
|
|
105
|
+
expect(lines).toContain('google-antigravity/gemini-3.6-flash');
|
|
106
|
+
expect(lines).not.toContain('anthropic-claude/sonnet-5');
|
|
94
107
|
});
|
|
95
108
|
|
|
96
109
|
it('formats image-gen model errors with capable seats only', () => {
|
|
97
|
-
const
|
|
98
|
-
if (!
|
|
99
|
-
const err = formatImageGenModelError('
|
|
110
|
+
const claudeSonnet = resolveModel('anthropic-claude/sonnet-5');
|
|
111
|
+
if (!claudeSonnet) throw new Error('claudeSonnet resolution failed');
|
|
112
|
+
const err = formatImageGenModelError('anthropic-claude/sonnet-5', claudeSonnet);
|
|
100
113
|
expect(err).toContain('cannot generate images');
|
|
101
|
-
expect(err).toContain('backend "
|
|
114
|
+
expect(err).toContain('backend "claude"');
|
|
102
115
|
expect(err).toContain('xai-grok/grok-4.5');
|
|
103
116
|
expect(err).toContain('openai-codex/gpt-5.6-sol');
|
|
117
|
+
expect(err).toContain('google-antigravity/gemini-3.6-flash');
|
|
104
118
|
const seatsSection = err.slice(err.indexOf('Image-gen seats'));
|
|
105
|
-
expect(seatsSection).not.toContain('
|
|
119
|
+
expect(seatsSection).not.toContain('anthropic-claude/sonnet-5');
|
|
106
120
|
});
|
|
107
121
|
});
|
package/src/models.ts
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Models are registered by canonical, provider-qualified slug —
|
|
5
5
|
* `<vendor>-<cli>/<model>[-<effort>]`, e.g. `openai-codex/gpt-5.6-sol-high`.
|
|
6
|
-
* Canonical slugs only — no short aliases, by design.
|
|
6
|
+
* Canonical slugs only — no short aliases, by design. That holds on both sides:
|
|
7
|
+
* `backendModel` is a pinned model id (`claude-opus-5`), never a moving vendor
|
|
8
|
+
* alias (`opus`), so a seat never silently changes model under you.
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
11
|
export type Backend = 'agy' | 'claude' | 'codex' | 'grok';
|
|
@@ -12,7 +14,7 @@ export type Effort = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
|
|
|
12
14
|
export interface ModelSpec {
|
|
13
15
|
readonly slug: string; // canonical, effort-less
|
|
14
16
|
readonly backend: Backend;
|
|
15
|
-
readonly backendModel: string
|
|
17
|
+
readonly backendModel: string; // always explicit — never "whatever the CLI defaults to"
|
|
16
18
|
readonly efforts: readonly Effort[] | null;
|
|
17
19
|
readonly defaultEffort?: Effort; // only when backend REQUIRES one (agy gemini)
|
|
18
20
|
readonly brief: string;
|
|
@@ -37,7 +39,8 @@ export const MODELS: Record<string, ModelSpec> = {
|
|
|
37
39
|
backendModel: 'gemini-3.6-flash',
|
|
38
40
|
efforts: ['low', 'medium', 'high'],
|
|
39
41
|
defaultEffort: 'high',
|
|
40
|
-
brief:
|
|
42
|
+
brief:
|
|
43
|
+
'Google Gemini 3.6 Flash via agy — default for implement, also image-gen; own Antigravity login',
|
|
41
44
|
},
|
|
42
45
|
'google-antigravity/claude-sonnet-4-6': {
|
|
43
46
|
slug: 'google-antigravity/claude-sonnet-4-6',
|
|
@@ -67,31 +70,45 @@ export const MODELS: Record<string, ModelSpec> = {
|
|
|
67
70
|
efforts: ['low', 'medium', 'high', 'xhigh'],
|
|
68
71
|
brief: 'OpenAI Codex gpt-5.6-sol via codex CLI',
|
|
69
72
|
},
|
|
70
|
-
'anthropic-claude/sonnet': {
|
|
71
|
-
slug: 'anthropic-claude/sonnet',
|
|
73
|
+
'anthropic-claude/sonnet-5': {
|
|
74
|
+
slug: 'anthropic-claude/sonnet-5',
|
|
72
75
|
backend: 'claude',
|
|
73
|
-
backendModel: 'sonnet',
|
|
76
|
+
backendModel: 'claude-sonnet-5',
|
|
74
77
|
efforts: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
75
|
-
brief: 'Claude Sonnet via claude CLI — bills your Claude subscription',
|
|
78
|
+
brief: 'Claude Sonnet 5 via claude CLI — bills your Claude subscription',
|
|
76
79
|
},
|
|
77
|
-
'anthropic-claude/opus': {
|
|
78
|
-
slug: 'anthropic-claude/opus',
|
|
80
|
+
'anthropic-claude/opus-5': {
|
|
81
|
+
slug: 'anthropic-claude/opus-5',
|
|
79
82
|
backend: 'claude',
|
|
80
|
-
backendModel: 'opus',
|
|
83
|
+
backendModel: 'claude-opus-5',
|
|
81
84
|
efforts: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
82
85
|
defaultEffort: 'high',
|
|
83
|
-
brief: 'Claude Opus via claude CLI (default effort: high) — bills subscription',
|
|
86
|
+
brief: 'Claude Opus 5 via claude CLI (default effort: high) — bills subscription',
|
|
87
|
+
},
|
|
88
|
+
'anthropic-claude/opus-5-1m': {
|
|
89
|
+
slug: 'anthropic-claude/opus-5-1m',
|
|
90
|
+
backend: 'claude',
|
|
91
|
+
backendModel: 'claude-opus-5[1m]',
|
|
92
|
+
efforts: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
93
|
+
defaultEffort: 'high',
|
|
94
|
+
brief: 'Claude Opus 5, 1M context via claude CLI — long-context work; bills subscription',
|
|
84
95
|
},
|
|
85
96
|
} as const satisfies Record<string, ModelSpec>;
|
|
86
97
|
|
|
87
|
-
export
|
|
88
|
-
export const DEFAULT_IMPLEMENTER = 'google-antigravity/gemini-3.6-flash';
|
|
89
|
-
export const DEFAULT_IMAGE_GEN = 'openai-codex/gpt-5.6-sol';
|
|
98
|
+
export type ImageFormat = 'jpg' | 'png';
|
|
90
99
|
|
|
91
|
-
const
|
|
100
|
+
const IMAGE_GEN_FORMATS: ReadonlyMap<Backend, ImageFormat> = new Map([
|
|
101
|
+
['agy', 'jpg'],
|
|
102
|
+
['codex', 'png'],
|
|
103
|
+
['grok', 'jpg'],
|
|
104
|
+
]);
|
|
92
105
|
|
|
93
106
|
export function supportsImageGen(resolved: ResolvedModel): boolean {
|
|
94
|
-
return
|
|
107
|
+
return IMAGE_GEN_FORMATS.has(resolved.spec.backend);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function imageFormatFor(resolved: ResolvedModel): ImageFormat | undefined {
|
|
111
|
+
return IMAGE_GEN_FORMATS.get(resolved.spec.backend);
|
|
95
112
|
}
|
|
96
113
|
|
|
97
114
|
const EFFORTS_SET: ReadonlySet<string> = new Set<Effort>(['low', 'medium', 'high', 'xhigh', 'max']);
|
|
@@ -122,10 +139,7 @@ export function resolveModel(input: string): ResolvedModel | undefined {
|
|
|
122
139
|
return undefined;
|
|
123
140
|
}
|
|
124
141
|
|
|
125
|
-
export function backendModelId(resolved: ResolvedModel): string
|
|
126
|
-
if (!resolved.spec.backendModel) {
|
|
127
|
-
return undefined;
|
|
128
|
-
}
|
|
142
|
+
export function backendModelId(resolved: ResolvedModel): string {
|
|
129
143
|
if (resolved.spec.backend === 'agy') {
|
|
130
144
|
const effort = resolved.effort ?? resolved.spec.defaultEffort;
|
|
131
145
|
if (effort) {
|
|
@@ -138,7 +152,7 @@ export function backendModelId(resolved: ResolvedModel): string | undefined {
|
|
|
138
152
|
export function listModelHelpLines(opts: { readonly imageOnly?: boolean } = {}): string[] {
|
|
139
153
|
const lines: string[] = [];
|
|
140
154
|
for (const [slug, spec] of Object.entries(MODELS)) {
|
|
141
|
-
if (opts.imageOnly && !
|
|
155
|
+
if (opts.imageOnly && !IMAGE_GEN_FORMATS.has(spec.backend)) continue;
|
|
142
156
|
lines.push(` ${slug}`);
|
|
143
157
|
lines.push(` ${spec.brief}`);
|
|
144
158
|
}
|
package/src/quotaPreflight.ts
CHANGED
|
@@ -62,8 +62,7 @@ export async function preflightModel(resolved: ResolvedModel): Promise<Preflight
|
|
|
62
62
|
|
|
63
63
|
try {
|
|
64
64
|
const snapshot = await fetchAgyQuota();
|
|
65
|
-
|
|
66
|
-
return evaluateAgyPreflight(snapshot, modelId);
|
|
65
|
+
return evaluateAgyPreflight(snapshot, backendModelId(resolved));
|
|
67
66
|
} catch (err) {
|
|
68
67
|
return {
|
|
69
68
|
ok: true,
|