@aibridge/cli 0.2.0 → 0.4.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
@@ -28,7 +28,7 @@ npx skills add ycmjason/aibridge
28
28
  That's it. The skill runs the CLI on demand via `npx -y @aibridge/cli` — nothing else to install. Ask your agent to "use aibridge", or try it yourself:
29
29
 
30
30
  ```bash
31
- npx -y @aibridge/cli subagent "summarize the architecture of this repo"
31
+ npx -y @aibridge/cli subagent --model xai-grok/grok-4.5 "summarize the architecture of this repo"
32
32
  ```
33
33
 
34
34
  <sup>Want the `aibridge` command on your PATH for manual use? `npm i -g @aibridge/cli` (optional).</sup>
@@ -37,21 +37,22 @@ npx -y @aibridge/cli subagent "summarize the architecture of this repo"
37
37
 
38
38
  | Command | Use when |
39
39
  |---|---|
40
- | `aibridge plan "<task>"` | You want a delegate model to study the repo and expand a task into a detailed, reviewable **plan file** before any code is written |
41
- | `aibridge implement <plan.md>` | You have an approved plan file and want it executed in place — with your project's **real typecheck and tests** run until green |
42
- | `aibridge review [--plan <plan.md>]` | You want a **different model** to pressure-test the working-tree diff against the plan contract (over-reach is a finding) — or to review the plan itself before implementing |
43
- | `aibridge subagent "<task>"` | A self-contained task deserves a concurrent delegate, a cross-model second opinion, or a red-team pass |
44
- | `aibridge image-gen "<prompt>" --out out.png` | You need a real raster image — on a Codex (default), Antigravity, or Grok seat, with render verification |
40
+ | `aibridge plan --model xai-grok/grok-4.5 --out plan.md "<task>"` | You want a delegate model to study the repo and expand a task into a detailed, reviewable **plan file** before any code is written |
41
+ | `aibridge implement --model google-antigravity/gemini-3.6-flash <plan.md>` | You have an approved plan file and want it executed in place — with your project's **real typecheck and tests** run until green |
42
+ | `aibridge review --model xai-grok/grok-4.5 --out review.md [--plan <plan.md>]` | You want a **different model** to pressure-test the working-tree diff against the plan contract (over-reach is a finding) — or to review the plan itself before implementing |
43
+ | `aibridge subagent --model xai-grok/grok-4.5 "<task>"` | A self-contained task deserves a concurrent delegate, a cross-model second opinion, or a red-team pass |
44
+ | `aibridge image-gen --model openai-codex/gpt-5.6-sol --out out.png "<prompt>"` | You need a real raster image — on a Codex, Antigravity, or Grok seat, with render verification |
45
+ | `aibridge models [--json]` | You need the exact facts for every registered model seat (accepted efforts, image format, pinned model ID) |
45
46
  | `aibridge quota` | Two-second check of every backend's remaining quota before you pipeline work |
46
47
  | `aibridge runs` | Inspect or watch past delegation runs (`~/.aibridge/runs`) |
47
48
 
48
49
  The three verbs compose into an orchestrator-driven loop your agent stays in charge of:
49
50
 
50
51
  ```
51
- aibridge plan "add rate limiting to the API" # delegate writes plan.md
52
+ aibridge plan --model xai-grok/grok-4.5 --out plan.md "add rate limiting to the API" # delegate writes plan.md
52
53
  # → your agent reads, edits, approves the plan
53
- aibridge implement plan.md # another model executes it, runs your gates
54
- aibridge review --plan plan.md # a third seat cross-checks the diff
54
+ aibridge implement --model google-antigravity/gemini-3.6-flash plan.md # another model executes it, runs your gates
55
+ aibridge review --model xai-grok/grok-4.5 --out review.md --plan plan.md # a third seat cross-checks the diff
55
56
  ```
56
57
 
57
58
  Plan files — not their contents — travel between stages, so the loop is nearly free on your agent's context.
@@ -63,6 +64,36 @@ Plan files — not their contents — travel between stages, so the loop is near
63
64
  - **No API keys.** Delegation runs on the backing CLIs' existing logins, each spending its own quota. (The skill treats a backend that shares your agent's own quota pool as a last resort.)
64
65
  - **Models are canonical slugs**: `<vendor>-<cli>/<model>[-<effort>]` — e.g. `xai-grok/grok-4.5`, `google-antigravity/gemini-3.6-flash`, `openai-codex/gpt-5.6-sol-high`, `anthropic-claude/opus-5`. No aliases — not short ones, and not moving vendor aliases like `opus`: every seat pins an exact model version. `aibridge <command> --help` lists every seat.
65
66
 
67
+ ## Tell your agent when to reach for it
68
+
69
+ aibridge doesn't decide when to delegate — your agent does, and left alone it will
70
+ mostly keep the work for itself. Put the routing rule in whatever instructions file
71
+ your agent already reads at the start of every session (`AGENTS.md`, `CLAUDE.md`,
72
+ `.cursorrules`, …). Something like:
73
+
74
+ ```markdown
75
+ ## Delegation gate — decide before you implement
76
+
77
+ The moment a task becomes implementation you could fully specify, say the call out
78
+ loud — **solo** or **aibridge** — plus one line of why. Delegating is the default.
79
+ Stay solo only when the edit is smaller than the spec would be, or the work needs
80
+ live judgment, your own session's tools, or tight back-and-forth. Never default to
81
+ solo silently.
82
+
83
+ Route by size and risk:
84
+
85
+ - tiny → solo
86
+ - clearly specified and self-contained → `aibridge subagent`
87
+ - large or risky → `aibridge plan` → read and approve the plan file →
88
+ `aibridge implement` → `aibridge review`
89
+
90
+ Delegated work is yours to verify: re-run the real gates before trusting a diff.
91
+ Prefer a reviewer from a different model family than whoever implemented.
92
+ ```
93
+
94
+ Tune the seats and thresholds to your own quotas. The value is that the decision is
95
+ explicit and made *before* the work starts, rather than rationalised afterwards.
96
+
66
97
  ## Requirements
67
98
 
68
99
  - **Node ≥ 24.11**
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { r as runCli, t as buildContext } from "./context-DQxvMUd2.mjs";
2
+ import { r as runCli, t as buildContext } from "./context-v_ns4n_W.mjs";
3
3
  //#region src/cli.ts
4
4
  await runCli(buildContext(process), process.argv.slice(2));
5
5
  //#endregion
@@ -10,6 +10,7 @@ import { fetchClaudeQuota } from "@aibridge/driver-claude";
10
10
  import * as codex from "@aibridge/driver-codex";
11
11
  import { fetchCodexQuota } from "@aibridge/driver-codex";
12
12
  import * as grok from "@aibridge/driver-grok";
13
+ import { fetchGrokQuota } from "@aibridge/driver-grok";
13
14
  import { runCaptured } from "@aibridge/proc";
14
15
  import { randomBytes } from "node:crypto";
15
16
  //#region src/models.ts
@@ -23,7 +24,7 @@ const MODELS = {
23
24
  "medium",
24
25
  "high"
25
26
  ],
26
- brief: "xAI Grok 4.5 via grok CLI — default for plan & review; own xAI login"
27
+ brief: "xAI Grok 4.5 via grok CLI — own xAI login; ~30 req/min, ~1k msgs/day, single-flight"
27
28
  },
28
29
  "google-antigravity/gemini-3.6-flash": {
29
30
  slug: "google-antigravity/gemini-3.6-flash",
@@ -35,7 +36,15 @@ const MODELS = {
35
36
  "high"
36
37
  ],
37
38
  defaultEffort: "high",
38
- brief: "Google Gemini 3.6 Flash via agy — default for implement, also image-gen; own Antigravity login"
39
+ brief: "Google Gemini 3.6 Flash via agy — own Antigravity login; quota shared across all Gemini tiers"
40
+ },
41
+ "google-antigravity/gemini-3.1-pro": {
42
+ slug: "google-antigravity/gemini-3.1-pro",
43
+ backend: "agy",
44
+ efforts: ["low", "high"],
45
+ backendModel: "gemini-3.1-pro",
46
+ defaultEffort: "high",
47
+ brief: "Google Gemini 3.1 Pro via agy — own Antigravity login; quota shared across all Gemini tiers"
39
48
  },
40
49
  "google-antigravity/claude-sonnet-4-6": {
41
50
  slug: "google-antigravity/claude-sonnet-4-6",
@@ -68,12 +77,36 @@ const MODELS = {
68
77
  "high",
69
78
  "xhigh"
70
79
  ],
71
- brief: "OpenAI Codex gpt-5.6-sol via codex CLI"
80
+ brief: "OpenAI gpt-5.6-sol via codex CLI — frontier agentic coding; own ChatGPT login"
72
81
  },
73
- "anthropic-claude/sonnet-5": {
74
- slug: "anthropic-claude/sonnet-5",
82
+ "openai-codex/gpt-5.6-terra": {
83
+ slug: "openai-codex/gpt-5.6-terra",
84
+ backend: "codex",
85
+ backendModel: "gpt-5.6-terra",
86
+ efforts: [
87
+ "low",
88
+ "medium",
89
+ "high",
90
+ "xhigh"
91
+ ],
92
+ brief: "OpenAI gpt-5.6-terra via codex CLI — balanced, everyday coding; own ChatGPT login"
93
+ },
94
+ "openai-codex/gpt-5.6-luna": {
95
+ slug: "openai-codex/gpt-5.6-luna",
96
+ backend: "codex",
97
+ backendModel: "gpt-5.6-luna",
98
+ efforts: [
99
+ "low",
100
+ "medium",
101
+ "high",
102
+ "xhigh"
103
+ ],
104
+ brief: "OpenAI gpt-5.6-luna via codex CLI — fast and affordable coding; own ChatGPT login"
105
+ },
106
+ "anthropic-claude/fable-5": {
107
+ slug: "anthropic-claude/fable-5",
75
108
  backend: "claude",
76
- backendModel: "claude-sonnet-5",
109
+ backendModel: "claude-fable-5",
77
110
  efforts: [
78
111
  "low",
79
112
  "medium",
@@ -81,12 +114,13 @@ const MODELS = {
81
114
  "xhigh",
82
115
  "max"
83
116
  ],
84
- brief: "Claude Sonnet 5 via claude CLI — bills your Claude subscription"
117
+ defaultEffort: "high",
118
+ brief: "Claude Fable 5 via claude CLI — hardest, longest-running work; bills the claude CLI subscription"
85
119
  },
86
120
  "anthropic-claude/opus-5": {
87
121
  slug: "anthropic-claude/opus-5",
88
122
  backend: "claude",
89
- backendModel: "claude-opus-5",
123
+ backendModel: "claude-opus-5[1m]",
90
124
  efforts: [
91
125
  "low",
92
126
  "medium",
@@ -95,12 +129,12 @@ const MODELS = {
95
129
  "max"
96
130
  ],
97
131
  defaultEffort: "high",
98
- brief: "Claude Opus 5 via claude CLI (default effort: high) bills subscription"
132
+ brief: "Claude Opus 5, 1M context via claude CLI everyday complex work; bills the claude CLI subscription"
99
133
  },
100
- "anthropic-claude/opus-5-1m": {
101
- slug: "anthropic-claude/opus-5-1m",
134
+ "anthropic-claude/sonnet-5": {
135
+ slug: "anthropic-claude/sonnet-5",
102
136
  backend: "claude",
103
- backendModel: "claude-opus-5[1m]",
137
+ backendModel: "claude-sonnet-5",
104
138
  efforts: [
105
139
  "low",
106
140
  "medium",
@@ -108,13 +142,22 @@ const MODELS = {
108
142
  "xhigh",
109
143
  "max"
110
144
  ],
111
- defaultEffort: "high",
112
- brief: "Claude Opus 5, 1M context via claude CLI — long-context work; bills subscription"
145
+ brief: "Claude Sonnet 5 via claude CLI — routine work; bills the claude CLI subscription"
146
+ },
147
+ "anthropic-claude/haiku-4-5": {
148
+ slug: "anthropic-claude/haiku-4-5",
149
+ backend: "claude",
150
+ backendModel: "claude-haiku-4-5-20251001",
151
+ efforts: [
152
+ "low",
153
+ "medium",
154
+ "high",
155
+ "xhigh",
156
+ "max"
157
+ ],
158
+ brief: "Claude Haiku 4.5 via claude CLI — quick answers; bills the claude CLI subscription"
113
159
  }
114
160
  };
115
- const DEFAULT_MODEL = "xai-grok/grok-4.5";
116
- const DEFAULT_IMPLEMENTER = "google-antigravity/gemini-3.6-flash";
117
- const DEFAULT_IMAGE_GEN = "openai-codex/gpt-5.6-sol";
118
161
  const IMAGE_GEN_FORMATS = /* @__PURE__ */ new Map([
119
162
  ["agy", "jpg"],
120
163
  ["codex", "png"],
@@ -212,6 +255,7 @@ const DRIVERS = {
212
255
  grok: {
213
256
  probe: () => grok.probe(),
214
257
  run: (task) => grok.run(task),
258
+ quota: () => grok.fetchGrokQuota(),
215
259
  generateImage: (req) => grok.generateImage(req)
216
260
  },
217
261
  codex: {
@@ -240,11 +284,11 @@ async function imageGen$1(flags, prompt) {
240
284
  this.process.stderr.write(`aibridge image-gen: ${msg}\n`);
241
285
  this.process.exitCode = 1;
242
286
  };
243
- const inputSlug = flags.model ?? "openai-codex/gpt-5.6-sol";
287
+ const inputSlug = flags.model;
244
288
  const model = resolveModel(inputSlug);
245
289
  if (!model) return fail(formatUnknownModelError(inputSlug));
246
290
  if (!supportsImageGen(model)) return fail(formatImageGenModelError(inputSlug, model));
247
- if (model.spec.backend === "codex" && model.effort) return fail(`effort "-${model.effort}" has no effect on image-gen (the image tool renders, not the seat model); use ${DEFAULT_IMAGE_GEN}.`);
291
+ if (model.spec.backend === "codex" && model.effort) return fail(`effort "-${model.effort}" has no effect on image-gen (the image tool renders, not the seat model); pass the un-suffixed slug "${model.spec.slug}" instead.`);
248
292
  const expected = imageFormatFor(model);
249
293
  if (expected === void 0) return fail(formatImageGenModelError(inputSlug, model));
250
294
  const label = expected === "png" ? "PNG" : "JPEG";
@@ -378,7 +422,7 @@ const fullDescription$6 = [
378
422
  "",
379
423
  "Image-gen seats (canonical slug):",
380
424
  ...listModelHelpLines({ imageOnly: true }),
381
- `Default: ${DEFAULT_IMAGE_GEN}.`
425
+ "Recommended seat: openai-codex/gpt-5.6-sol."
382
426
  ].join("\n");
383
427
  const imageGen = buildCommand({
384
428
  func: imageGen$1,
@@ -387,8 +431,7 @@ const imageGen = buildCommand({
387
431
  model: {
388
432
  kind: "parsed",
389
433
  parse: String,
390
- optional: true,
391
- brief: `Model slug (default: ${DEFAULT_IMAGE_GEN})`
434
+ brief: "Model slug (required) — see the seat list above"
392
435
  },
393
436
  out: {
394
437
  kind: "parsed",
@@ -492,8 +535,17 @@ function evaluateCodexPreflight(snapshot) {
492
535
  };
493
536
  return { ok: true };
494
537
  }
538
+ function evaluateGrokPreflight(snapshot) {
539
+ if (snapshot.usedPercent !== void 0 && snapshot.usedPercent >= 100) return {
540
+ ok: false,
541
+ message: "grok credit quota exhausted",
542
+ resetAt: snapshot.periodEnd
543
+ };
544
+ return { ok: true };
545
+ }
495
546
  async function preflightModel(resolved) {
496
547
  if (resolved.spec.backend === "codex") return preflightCodex();
548
+ if (resolved.spec.backend === "grok") return preflightGrok();
497
549
  if (resolved.spec.backend !== "agy") return { ok: true };
498
550
  try {
499
551
  return evaluateAgyPreflight(await fetchAgyQuota(), backendModelId(resolved));
@@ -514,6 +566,16 @@ async function preflightCodex() {
514
566
  };
515
567
  }
516
568
  }
569
+ async function preflightGrok() {
570
+ try {
571
+ return evaluateGrokPreflight(await fetchGrokQuota());
572
+ } catch (err) {
573
+ return {
574
+ ok: true,
575
+ warning: `quota preflight failed (${err.message}); proceeding`
576
+ };
577
+ }
578
+ }
517
579
  function formatReset$1(resetTime) {
518
580
  if (!resetTime) return "-";
519
581
  const ms = new Date(resetTime).getTime() - Date.now();
@@ -649,7 +711,7 @@ function readRunLogs(id) {
649
711
  //#endregion
650
712
  //#region src/commands/implement/impl.ts
651
713
  async function implement$1(flags, planFile) {
652
- const inputSlug = flags.model ?? "google-antigravity/gemini-3.6-flash";
714
+ const inputSlug = flags.model;
653
715
  const model = resolveModel(inputSlug);
654
716
  if (!model) {
655
717
  this.process.stderr.write(`${formatUnknownModelError(inputSlug)}\n`);
@@ -719,8 +781,7 @@ const implement = buildCommand({
719
781
  model: {
720
782
  kind: "parsed",
721
783
  parse: String,
722
- optional: true,
723
- brief: `Model slug (default: ${DEFAULT_IMPLEMENTER})`
784
+ brief: "Model slug (required) — see the seat list above"
724
785
  },
725
786
  timeout: {
726
787
  kind: "parsed",
@@ -749,6 +810,70 @@ const implement = buildCommand({
749
810
  }
750
811
  });
751
812
  //#endregion
813
+ //#region src/commands/models/impl.ts
814
+ const BACKEND_DISPLAY_NAMES = {
815
+ grok: "grok (Grok CLI)",
816
+ agy: "agy (Antigravity)",
817
+ codex: "codex (Codex CLI)",
818
+ claude: "claude (Claude Code CLI)"
819
+ };
820
+ function modelsImpl(flags) {
821
+ const specs = Object.values(MODELS);
822
+ if (flags.json) {
823
+ const jsonOutput = specs.map((spec) => ({
824
+ slug: spec.slug,
825
+ backend: spec.backend,
826
+ backendModel: spec.backendModel,
827
+ efforts: spec.efforts ? [...spec.efforts] : [],
828
+ defaultEffort: spec.defaultEffort ?? null,
829
+ image: imageFormatFor({
830
+ spec,
831
+ effort: void 0
832
+ }) ?? null,
833
+ brief: spec.brief
834
+ }));
835
+ this.process.stdout.write(`${JSON.stringify(jsonOutput)}\n`);
836
+ return;
837
+ }
838
+ const backends = [];
839
+ for (const spec of specs) if (!backends.includes(spec.backend)) backends.push(spec.backend);
840
+ let firstBackend = true;
841
+ for (const backend of backends) {
842
+ if (!firstBackend) this.process.stdout.write("\n");
843
+ firstBackend = false;
844
+ this.process.stdout.write(`=== ${BACKEND_DISPLAY_NAMES[backend]} ===\n`);
845
+ const backendSpecs = specs.filter((spec) => spec.backend === backend);
846
+ for (const spec of backendSpecs) {
847
+ this.process.stdout.write(` ${spec.slug}\n`);
848
+ const segments = [];
849
+ if (spec.efforts) {
850
+ const formattedEfforts = spec.efforts.map((e) => e === spec.defaultEffort ? `${e}*` : e).join(" | ");
851
+ segments.push(`efforts: ${formattedEfforts}`);
852
+ }
853
+ const img = imageFormatFor({
854
+ spec,
855
+ effort: void 0
856
+ });
857
+ segments.push(`image: ${img ?? "—"}`);
858
+ segments.push(`id: ${spec.backendModel}`);
859
+ this.process.stdout.write(` ${segments.join(" · ")}\n`);
860
+ this.process.stdout.write(` ${spec.brief}\n`);
861
+ }
862
+ }
863
+ if (specs.some((spec) => spec.defaultEffort !== void 0)) this.process.stdout.write("\n* = effort used when the slug has no -<effort> suffix\n");
864
+ }
865
+ //#endregion
866
+ //#region src/commands/models/command.ts
867
+ const models = buildCommand({
868
+ func: modelsImpl,
869
+ parameters: { flags: { json: {
870
+ kind: "boolean",
871
+ withNegated: false,
872
+ brief: "Emit the registry as JSON"
873
+ } } },
874
+ docs: { brief: "List every model seat in the registry (slug, efforts, image format)" }
875
+ });
876
+ //#endregion
752
877
  //#region src/commands/plan/impl.ts
753
878
  function countOpenQuestions(markdown) {
754
879
  const headingIdx = markdown.search(/^## Open questions[ \t]*$/m);
@@ -781,7 +906,7 @@ function extractPathFromPorcelainLine(line) {
781
906
  return content;
782
907
  }
783
908
  async function plan$1(flags, taskPrompt) {
784
- const inputSlug = flags.model ?? "xai-grok/grok-4.5";
909
+ const inputSlug = flags.model;
785
910
  const model = resolveModel(inputSlug);
786
911
  if (!model) {
787
912
  this.process.stderr.write(`${formatUnknownModelError(inputSlug)}\n`);
@@ -801,7 +926,7 @@ async function plan$1(flags, taskPrompt) {
801
926
  const cwd = this.process.cwd();
802
927
  const promptSnippet = taskPrompt.replace(/\r?\n/g, " ").slice(0, 80);
803
928
  const run = startRun("plan", `${model.spec.slug}: ${promptSnippet}`);
804
- const absOutPath = flags.out ? isAbsolute(flags.out) ? flags.out : resolve(cwd, flags.out) : resolve(run.dir, "plan.md");
929
+ const absOutPath = isAbsolute(flags.out) ? flags.out : resolve(cwd, flags.out);
805
930
  const beforePorcelain = await getPorcelainStatus(cwd);
806
931
  const outcome = await delegate({
807
932
  model,
@@ -866,14 +991,12 @@ const plan = buildCommand({
866
991
  model: {
867
992
  kind: "parsed",
868
993
  parse: String,
869
- optional: true,
870
- brief: `Model slug (default: ${DEFAULT_MODEL})`
994
+ brief: "Model slug (required) — see the seat list above"
871
995
  },
872
996
  out: {
873
997
  kind: "parsed",
874
998
  parse: String,
875
- optional: true,
876
- brief: "Where to write the plan (default: <run.dir>/plan.md)"
999
+ brief: "Where to write the plan file (required)"
877
1000
  },
878
1001
  timeout: {
879
1002
  kind: "parsed",
@@ -912,6 +1035,17 @@ function formatReset(resetTime) {
912
1035
  const rel = mins < 60 ? `${mins}m` : `${Math.floor(mins / 60)}h${mins % 60}m`;
913
1036
  return `${new Date(resetTime).toLocaleTimeString()} (in ${rel})`;
914
1037
  }
1038
+ function renderGrok(ctx, snapshot) {
1039
+ ctx.process.stdout.write("=== grok (xAI) — used this period ===\n");
1040
+ ctx.process.stdout.write(`${"PERIOD".padEnd(10)} ${"USED".padEnd(10)} RESET\n`);
1041
+ const usedPctStr = snapshot.usedPercent !== void 0 ? `${snapshot.usedPercent}%` : "?";
1042
+ const periodStr = snapshot.periodType ?? "-";
1043
+ ctx.process.stdout.write(`${periodStr.padEnd(10)} ${usedPctStr.padEnd(10)} ${formatReset(snapshot.periodEnd)}\n`);
1044
+ if (snapshot.products.length > 0) {
1045
+ const prods = snapshot.products.map((p) => `${p.product} ${p.usedPercent}%`).join(" · ");
1046
+ ctx.process.stdout.write(` ${prods}\n`);
1047
+ }
1048
+ }
915
1049
  function renderAgy(ctx, snapshot) {
916
1050
  ctx.process.stdout.write("=== agy (Antigravity) — remaining per model group ===\n");
917
1051
  for (const group of snapshot.groups) {
@@ -941,14 +1075,16 @@ function renderSection(ctx, result, title, render) {
941
1075
  else ctx.process.stdout.write(`=== ${title} ===\nunavailable: ${result.reason.message}\n`);
942
1076
  }
943
1077
  async function quotaImpl(flags) {
944
- const [agy, codex, claude] = await Promise.allSettled([
1078
+ const [grok, agy, codex, claude] = await Promise.allSettled([
1079
+ fetchGrokQuota(),
945
1080
  fetchAgyQuota(),
946
1081
  fetchCodexQuota(),
947
1082
  fetchClaudeQuota()
948
1083
  ]);
949
- const allFailed = agy.status === "rejected" && codex.status === "rejected" && claude.status === "rejected";
1084
+ const allFailed = grok.status === "rejected" && agy.status === "rejected" && codex.status === "rejected" && claude.status === "rejected";
950
1085
  if (flags.json) {
951
1086
  this.process.stdout.write(`${JSON.stringify({
1087
+ grok: grok.status === "fulfilled" ? grok.value : { error: String(grok.reason) },
952
1088
  agy: agy.status === "fulfilled" ? agy.value : { error: String(agy.reason) },
953
1089
  codex: codex.status === "fulfilled" ? codex.value : { error: String(codex.reason) },
954
1090
  claude: claude.status === "fulfilled" ? claude.value : { error: String(claude.reason) }
@@ -956,6 +1092,8 @@ async function quotaImpl(flags) {
956
1092
  if (allFailed) this.process.exitCode = 1;
957
1093
  return;
958
1094
  }
1095
+ renderSection(this, grok, "grok (xAI)", renderGrok);
1096
+ this.process.stdout.write("\n");
959
1097
  renderSection(this, agy, "agy (Antigravity)", renderAgy);
960
1098
  this.process.stdout.write("\n");
961
1099
  renderSection(this, codex, "codex (ChatGPT)", renderCodex);
@@ -971,8 +1109,10 @@ const quota = buildCommand({
971
1109
  brief: "Emit the raw snapshot as JSON"
972
1110
  } } },
973
1111
  docs: {
974
- brief: "Show agy / codex / claude quota with reset times",
1112
+ brief: "Show grok / agy / codex / claude quota with reset times",
975
1113
  fullDescription: [
1114
+ "grok: reads ~/.grok/auth.json and asks the xAI billing endpoint for the",
1115
+ "weekly credit usage percentage and per-product split.",
976
1116
  "agy: reads its cached OAuth token (~/.gemini/antigravity-cli/) and asks the",
977
1117
  "Cloud Code API for per-model remaining quota. EXHAUSTED means agy turns on",
978
1118
  "that model fail with an empty answer until the reset time.",
@@ -1032,7 +1172,7 @@ function parseReviewVerdict(response) {
1032
1172
  };
1033
1173
  }
1034
1174
  async function review$1(flags) {
1035
- const inputSlug = flags.model ?? "xai-grok/grok-4.5";
1175
+ const inputSlug = flags.model;
1036
1176
  const model = resolveModel(inputSlug);
1037
1177
  if (!model) {
1038
1178
  this.process.stderr.write(`${formatUnknownModelError(inputSlug)}\n`);
@@ -1082,7 +1222,7 @@ async function review$1(flags) {
1082
1222
  const timeoutSec = flags.timeout ?? 1200;
1083
1223
  const modeDetail = isDirty ? absPlanPath ? `diff + plan (${absPlanPath})` : `diff (${baseRef})` : `plan-only (${absPlanPath})`;
1084
1224
  const run = startRun("review", `${model.spec.slug}: ${modeDetail}`);
1085
- const absOutPath = flags.out ? isAbsolute(flags.out) ? flags.out : resolve(cwd, flags.out) : resolve(run.dir, "review.md");
1225
+ const absOutPath = isAbsolute(flags.out) ? flags.out : resolve(cwd, flags.out);
1086
1226
  let reviewPrompt;
1087
1227
  if (isDirty) reviewPrompt = `You are an expert code reviewer. Inspect the working tree diff against base '${baseRef}' and untracked files at ${cwd}.\n` + (absPlanPath ? `Compare the implementation against the plan contract at ${absPlanPath}. Any file modified or feature added outside the plan contract counts as over-reach (severity: major unless harmful, then critical).\n` : "") + `Write your detailed review report to the file ${absOutPath}. For each finding, include file:line, severity (critical|major|minor), and rationale.\nYour final answer (last message) must consist of EXACTLY ONE VERDICT LINE:\nEither: "PASS"\nOr: "FINDINGS: <c> critical, <m> major, <n> minor"`;
1088
1228
  else reviewPrompt = `You are an expert architecture reviewer. Inspect the plan contract file at ${absPlanPath}.\nReview the plan for soundness, missing edge cases, safety, and feasibility.\nWrite your detailed review report to the file ${absOutPath}. For each finding, include severity (critical|major|minor) and rationale.\nYour final answer (last message) must consist of EXACTLY ONE VERDICT LINE:\nEither: "PASS"\nOr: "FINDINGS: <c> critical, <m> major, <n> minor"`;
@@ -1134,8 +1274,7 @@ const review = buildCommand({
1134
1274
  model: {
1135
1275
  kind: "parsed",
1136
1276
  parse: String,
1137
- optional: true,
1138
- brief: `Model slug (default: ${DEFAULT_MODEL})`
1277
+ brief: "Model slug (required) — see the seat list above"
1139
1278
  },
1140
1279
  plan: {
1141
1280
  kind: "parsed",
@@ -1152,8 +1291,7 @@ const review = buildCommand({
1152
1291
  out: {
1153
1292
  kind: "parsed",
1154
1293
  parse: String,
1155
- optional: true,
1156
- brief: "Where to write the review report (default: <run.dir>/review.md)"
1294
+ brief: "Where to write the review report (required)"
1157
1295
  },
1158
1296
  timeout: {
1159
1297
  kind: "parsed",
@@ -1345,7 +1483,7 @@ const runs = buildCommand({
1345
1483
  //#endregion
1346
1484
  //#region src/commands/subagent/impl.ts
1347
1485
  async function subagent$1(flags, prompt) {
1348
- const inputSlug = flags.model ?? "xai-grok/grok-4.5";
1486
+ const inputSlug = flags.model;
1349
1487
  const model = resolveModel(inputSlug);
1350
1488
  if (!model) {
1351
1489
  this.process.stderr.write(`${formatUnknownModelError(inputSlug)}\n`);
@@ -1399,8 +1537,8 @@ const fullDescription = [
1399
1537
  "",
1400
1538
  "Available models (canonical slug):",
1401
1539
  ...listModelHelpLines(),
1402
- `Default: ${DEFAULT_MODEL} (off-budget). The claude-backend slugs are FALLBACKS for`,
1403
- "when the off-budget CLIs are quota-exhaustedthey bill your Claude subscription."
1540
+ "Recommended first choice: xai-grok/grok-4.5. Whichever seat runs on the same provider as the",
1541
+ "agent you orchestrate from is your last resort it spends the pool you are already burning."
1404
1542
  ].join("\n");
1405
1543
  const subagent = buildCommand({
1406
1544
  func: subagent$1,
@@ -1409,8 +1547,7 @@ const subagent = buildCommand({
1409
1547
  model: {
1410
1548
  kind: "parsed",
1411
1549
  parse: String,
1412
- optional: true,
1413
- brief: `Model slug to delegate to (default: ${DEFAULT_MODEL})`
1550
+ brief: "Model slug (required) — see the seat list above"
1414
1551
  },
1415
1552
  timeout: {
1416
1553
  kind: "parsed",
@@ -1475,9 +1612,10 @@ const app = buildApplication(buildRouteMap({
1475
1612
  subagent,
1476
1613
  "image-gen": imageGen,
1477
1614
  runs,
1478
- quota
1615
+ quota,
1616
+ models
1479
1617
  },
1480
- docs: { brief: "Bridge tasks to non-Claude AI CLIs — a plan → implement → review workflow, task delegation, and image generation (codex / agy / grok seats)." }
1618
+ docs: { brief: "Bridge tasks to the other AI CLIs on this machine — a plan → implement → review workflow, task delegation, and image generation (codex / agy / grok seats)." }
1481
1619
  }), {
1482
1620
  name: "aibridge",
1483
1621
  versionInfo: { currentVersion: version },
@@ -1494,4 +1632,4 @@ function buildContext(process) {
1494
1632
  return { process };
1495
1633
  }
1496
1634
  //#endregion
1497
- export { listModelHelpLines as C, formatUnknownModelError as S, supportsImageGen as T, DEFAULT_IMPLEMENTER as _, readRunLogs as a, backendModelId as b, evaluateCodexPreflight as c, renderPreflightRefusal as d, delegate as f, DEFAULT_IMAGE_GEN as g, positiveIntSeconds as h, listRuns as i, preflightCodex as l, nonEmptyPrompt as m, app as n, startRun as o, getDriver as p, runCli as r, evaluateAgyPreflight as s, buildContext as t, preflightModel as u, DEFAULT_MODEL as v, resolveModel as w, formatImageGenModelError as x, MODELS as y };
1635
+ export { supportsImageGen as S, backendModelId as _, readRunLogs as a, listModelHelpLines as b, evaluateCodexPreflight as c, renderPreflightRefusal as d, delegate as f, MODELS as g, positiveIntSeconds as h, listRuns as i, preflightCodex as l, nonEmptyPrompt as m, app as n, startRun as o, getDriver as p, runCli as r, evaluateAgyPreflight as s, buildContext as t, preflightModel as u, formatImageGenModelError as v, resolveModel as x, formatUnknownModelError as y };
package/dist/index.d.mts CHANGED
@@ -2,6 +2,7 @@ import { CommandContext } from "@stricli/core";
2
2
  import { AgyQuotaSnapshot } from "@aibridge/driver-agy";
3
3
  import { ClaudeQuotaSnapshot } from "@aibridge/driver-claude";
4
4
  import { CodexQuotaSnapshot } from "@aibridge/driver-codex";
5
+ import { GrokQuotaSnapshot } from "@aibridge/driver-grok";
5
6
  //#region src/context.d.ts
6
7
  interface LocalContext extends CommandContext {
7
8
  /** Full Node process — satisfies stricli WritableStreams + exitCode/env/cwd used by impls. */
@@ -21,7 +22,7 @@ declare function runCli(ctx: LocalContext, argv: readonly string[]): Promise<voi
21
22
  * Models are registered by canonical, provider-qualified slug —
22
23
  * `<vendor>-<cli>/<model>[-<effort>]`, e.g. `openai-codex/gpt-5.6-sol-high`.
23
24
  * Canonical slugs only — no short aliases, by design. That holds on both sides:
24
- * `backendModel` is a pinned model id (`claude-opus-5`), never a moving vendor
25
+ * `backendModel` is a pinned model id (`claude-sonnet-5`), never a moving vendor
25
26
  * alias (`opus`), so a seat never silently changes model under you.
26
27
  */
27
28
  type Backend = 'agy' | 'claude' | 'codex' | 'grok';
@@ -39,9 +40,6 @@ interface ResolvedModel {
39
40
  readonly effort: Effort | undefined;
40
41
  }
41
42
  declare const MODELS: Record<string, ModelSpec>;
42
- declare const DEFAULT_MODEL = "xai-grok/grok-4.5";
43
- declare const DEFAULT_IMPLEMENTER = "google-antigravity/gemini-3.6-flash";
44
- declare const DEFAULT_IMAGE_GEN = "openai-codex/gpt-5.6-sol";
45
43
  declare function supportsImageGen(resolved: ResolvedModel): boolean;
46
44
  declare function resolveModel(input: string): ResolvedModel | undefined;
47
45
  declare function backendModelId(resolved: ResolvedModel): string;
@@ -80,7 +78,7 @@ type DelegationResult = {
80
78
  readonly message: string;
81
79
  readonly exitCode: number | null;
82
80
  };
83
- type QuotaSnapshot = AgyQuotaSnapshot | CodexQuotaSnapshot | ClaudeQuotaSnapshot;
81
+ type QuotaSnapshot = AgyQuotaSnapshot | CodexQuotaSnapshot | ClaudeQuotaSnapshot | GrokQuotaSnapshot;
84
82
  interface ImageGenRequest {
85
83
  readonly prompt: string;
86
84
  readonly workDir: string;
@@ -179,4 +177,4 @@ declare function renderPreflightRefusal(cmd: string, verdict: {
179
177
  resetAt: string | undefined;
180
178
  }): string;
181
179
  //#endregion
182
- export { type AgentCliDriver, type Backend, DEFAULT_IMAGE_GEN, DEFAULT_IMPLEMENTER, DEFAULT_MODEL, type DelegateOptions, type DelegateOutcome, type DelegationResult, type DelegationTask, type Effort, type LocalContext, MODELS, type ModelSpec, type PreflightVerdict, type ResolvedModel, type RunLog, type RunMeta, app, backendModelId, buildContext, delegate, evaluateAgyPreflight, evaluateCodexPreflight, formatImageGenModelError, formatUnknownModelError, getDriver, listModelHelpLines, listRuns, nonEmptyPrompt, positiveIntSeconds, preflightCodex, preflightModel, readRunLogs, renderPreflightRefusal, resolveModel, runCli, startRun, supportsImageGen };
180
+ export { type AgentCliDriver, type Backend, type DelegateOptions, type DelegateOutcome, type DelegationResult, type DelegationTask, type Effort, type LocalContext, MODELS, type ModelSpec, type PreflightVerdict, type ResolvedModel, type RunLog, type RunMeta, app, backendModelId, buildContext, delegate, evaluateAgyPreflight, evaluateCodexPreflight, formatImageGenModelError, formatUnknownModelError, getDriver, listModelHelpLines, listRuns, nonEmptyPrompt, positiveIntSeconds, preflightCodex, preflightModel, readRunLogs, renderPreflightRefusal, resolveModel, runCli, startRun, supportsImageGen };
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { C as listModelHelpLines, S as formatUnknownModelError, T as supportsImageGen, _ as DEFAULT_IMPLEMENTER, a as readRunLogs, b as backendModelId, c as evaluateCodexPreflight, d as renderPreflightRefusal, f as delegate, g as DEFAULT_IMAGE_GEN, h as positiveIntSeconds, i as listRuns, l as preflightCodex, m as nonEmptyPrompt, n as app, o as startRun, p as getDriver, r as runCli, s as evaluateAgyPreflight, t as buildContext, u as preflightModel, v as DEFAULT_MODEL, w as resolveModel, x as formatImageGenModelError, y as MODELS } from "./context-DQxvMUd2.mjs";
2
- export { DEFAULT_IMAGE_GEN, DEFAULT_IMPLEMENTER, DEFAULT_MODEL, MODELS, app, backendModelId, buildContext, delegate, evaluateAgyPreflight, evaluateCodexPreflight, formatImageGenModelError, formatUnknownModelError, getDriver, listModelHelpLines, listRuns, nonEmptyPrompt, positiveIntSeconds, preflightCodex, preflightModel, readRunLogs, renderPreflightRefusal, resolveModel, runCli, startRun, supportsImageGen };
1
+ import { S as supportsImageGen, _ as backendModelId, a as readRunLogs, b as listModelHelpLines, c as evaluateCodexPreflight, d as renderPreflightRefusal, f as delegate, g as MODELS, h as positiveIntSeconds, i as listRuns, l as preflightCodex, m as nonEmptyPrompt, n as app, o as startRun, p as getDriver, r as runCli, s as evaluateAgyPreflight, t as buildContext, u as preflightModel, v as formatImageGenModelError, x as resolveModel, y as formatUnknownModelError } from "./context-v_ns4n_W.mjs";
2
+ export { MODELS, app, backendModelId, buildContext, delegate, evaluateAgyPreflight, evaluateCodexPreflight, formatImageGenModelError, formatUnknownModelError, getDriver, listModelHelpLines, listRuns, nonEmptyPrompt, positiveIntSeconds, preflightCodex, preflightModel, readRunLogs, renderPreflightRefusal, resolveModel, runCli, startRun, supportsImageGen };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aibridge/cli",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "CLI that bridges tasks to AI CLIs on your machine (plan / implement / review / subagent / image-gen)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -35,11 +35,11 @@
35
35
  ],
36
36
  "dependencies": {
37
37
  "@stricli/core": "1.3.0",
38
- "@aibridge/proc": "0.2.0",
39
- "@aibridge/driver-codex": "0.2.0",
40
- "@aibridge/driver-grok": "0.2.0",
41
- "@aibridge/driver-claude": "0.2.0",
42
- "@aibridge/driver-agy": "0.2.0"
38
+ "@aibridge/proc": "0.4.0",
39
+ "@aibridge/driver-grok": "0.4.0",
40
+ "@aibridge/driver-agy": "0.4.0",
41
+ "@aibridge/driver-codex": "0.4.0",
42
+ "@aibridge/driver-claude": "0.4.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "tsdown": "0.22.14"
package/src/app.ts CHANGED
@@ -2,6 +2,7 @@ import { createRequire } from 'node:module';
2
2
  import { buildApplication, buildRouteMap, run } from '@stricli/core';
3
3
  import { imageGen } from './commands/image-gen/command.ts';
4
4
  import { implement } from './commands/implement/command.ts';
5
+ import { models } from './commands/models/command.ts';
5
6
  import { plan } from './commands/plan/command.ts';
6
7
  import { quota } from './commands/quota/command.ts';
7
8
  import { review } from './commands/review/command.ts';
@@ -14,7 +15,7 @@ const require = createRequire(import.meta.url);
14
15
  const { version } = require('../package.json') as { version: string };
15
16
 
16
17
  const BRIEF =
17
- 'Bridge tasks to non-Claude AI CLIs — a plan → implement → review workflow, task delegation, and image generation (codex / agy / grok seats).';
18
+ 'Bridge tasks to the other AI CLIs on this machine — a plan → implement → review workflow, task delegation, and image generation (codex / agy / grok seats).';
18
19
 
19
20
  const routes = buildRouteMap({
20
21
  routes: {
@@ -25,6 +26,7 @@ const routes = buildRouteMap({
25
26
  'image-gen': imageGen,
26
27
  runs,
27
28
  quota,
29
+ models,
28
30
  },
29
31
  docs: {
30
32
  brief: BRIEF,