@aibridge/cli 0.2.0 → 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 +9 -9
- package/dist/cli.mjs +1 -1
- package/dist/{context-DQxvMUd2.mjs → context-B6I4QI9z.mjs} +18 -28
- package/dist/index.d.mts +1 -4
- package/dist/index.mjs +2 -2
- package/package.json +6 -6
- package/src/commands/image-gen/command.ts +3 -4
- package/src/commands/image-gen/impl.ts +3 -4
- 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 +3 -8
- package/src/flagMapping.test.ts +27 -4
- package/src/index.ts +0 -3
- package/src/models.ts +0 -4
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,21 @@ 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
|
|
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
45
|
| `aibridge quota` | Two-second check of every backend's remaining quota before you pipeline work |
|
|
46
46
|
| `aibridge runs` | Inspect or watch past delegation runs (`~/.aibridge/runs`) |
|
|
47
47
|
|
|
48
48
|
The three verbs compose into an orchestrator-driven loop your agent stays in charge of:
|
|
49
49
|
|
|
50
50
|
```
|
|
51
|
-
aibridge plan "add rate limiting to the API" # delegate writes plan.md
|
|
51
|
+
aibridge plan --model xai-grok/grok-4.5 --out plan.md "add rate limiting to the API" # delegate writes plan.md
|
|
52
52
|
# → your agent reads, edits, approves the plan
|
|
53
|
-
aibridge implement plan.md
|
|
54
|
-
aibridge review --plan plan.md
|
|
53
|
+
aibridge implement --model google-antigravity/gemini-3.6-flash plan.md # another model executes it, runs your gates
|
|
54
|
+
aibridge review --model xai-grok/grok-4.5 --out review.md --plan plan.md # a third seat cross-checks the diff
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
Plan files — not their contents — travel between stages, so the loop is nearly free on your agent's context.
|
package/dist/cli.mjs
CHANGED
|
@@ -112,9 +112,6 @@ const MODELS = {
|
|
|
112
112
|
brief: "Claude Opus 5, 1M context via claude CLI — long-context work; bills subscription"
|
|
113
113
|
}
|
|
114
114
|
};
|
|
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
115
|
const IMAGE_GEN_FORMATS = /* @__PURE__ */ new Map([
|
|
119
116
|
["agy", "jpg"],
|
|
120
117
|
["codex", "png"],
|
|
@@ -240,11 +237,11 @@ async function imageGen$1(flags, prompt) {
|
|
|
240
237
|
this.process.stderr.write(`aibridge image-gen: ${msg}\n`);
|
|
241
238
|
this.process.exitCode = 1;
|
|
242
239
|
};
|
|
243
|
-
const inputSlug = flags.model
|
|
240
|
+
const inputSlug = flags.model;
|
|
244
241
|
const model = resolveModel(inputSlug);
|
|
245
242
|
if (!model) return fail(formatUnknownModelError(inputSlug));
|
|
246
243
|
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);
|
|
244
|
+
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
245
|
const expected = imageFormatFor(model);
|
|
249
246
|
if (expected === void 0) return fail(formatImageGenModelError(inputSlug, model));
|
|
250
247
|
const label = expected === "png" ? "PNG" : "JPEG";
|
|
@@ -378,7 +375,7 @@ const fullDescription$6 = [
|
|
|
378
375
|
"",
|
|
379
376
|
"Image-gen seats (canonical slug):",
|
|
380
377
|
...listModelHelpLines({ imageOnly: true }),
|
|
381
|
-
|
|
378
|
+
"Recommended seat: openai-codex/gpt-5.6-sol."
|
|
382
379
|
].join("\n");
|
|
383
380
|
const imageGen = buildCommand({
|
|
384
381
|
func: imageGen$1,
|
|
@@ -387,8 +384,7 @@ const imageGen = buildCommand({
|
|
|
387
384
|
model: {
|
|
388
385
|
kind: "parsed",
|
|
389
386
|
parse: String,
|
|
390
|
-
|
|
391
|
-
brief: `Model slug (default: ${DEFAULT_IMAGE_GEN})`
|
|
387
|
+
brief: "Model slug (required) — see the seat list above"
|
|
392
388
|
},
|
|
393
389
|
out: {
|
|
394
390
|
kind: "parsed",
|
|
@@ -649,7 +645,7 @@ function readRunLogs(id) {
|
|
|
649
645
|
//#endregion
|
|
650
646
|
//#region src/commands/implement/impl.ts
|
|
651
647
|
async function implement$1(flags, planFile) {
|
|
652
|
-
const inputSlug = flags.model
|
|
648
|
+
const inputSlug = flags.model;
|
|
653
649
|
const model = resolveModel(inputSlug);
|
|
654
650
|
if (!model) {
|
|
655
651
|
this.process.stderr.write(`${formatUnknownModelError(inputSlug)}\n`);
|
|
@@ -719,8 +715,7 @@ const implement = buildCommand({
|
|
|
719
715
|
model: {
|
|
720
716
|
kind: "parsed",
|
|
721
717
|
parse: String,
|
|
722
|
-
|
|
723
|
-
brief: `Model slug (default: ${DEFAULT_IMPLEMENTER})`
|
|
718
|
+
brief: "Model slug (required) — see the seat list above"
|
|
724
719
|
},
|
|
725
720
|
timeout: {
|
|
726
721
|
kind: "parsed",
|
|
@@ -781,7 +776,7 @@ function extractPathFromPorcelainLine(line) {
|
|
|
781
776
|
return content;
|
|
782
777
|
}
|
|
783
778
|
async function plan$1(flags, taskPrompt) {
|
|
784
|
-
const inputSlug = flags.model
|
|
779
|
+
const inputSlug = flags.model;
|
|
785
780
|
const model = resolveModel(inputSlug);
|
|
786
781
|
if (!model) {
|
|
787
782
|
this.process.stderr.write(`${formatUnknownModelError(inputSlug)}\n`);
|
|
@@ -801,7 +796,7 @@ async function plan$1(flags, taskPrompt) {
|
|
|
801
796
|
const cwd = this.process.cwd();
|
|
802
797
|
const promptSnippet = taskPrompt.replace(/\r?\n/g, " ").slice(0, 80);
|
|
803
798
|
const run = startRun("plan", `${model.spec.slug}: ${promptSnippet}`);
|
|
804
|
-
const absOutPath =
|
|
799
|
+
const absOutPath = isAbsolute(flags.out) ? flags.out : resolve(cwd, flags.out);
|
|
805
800
|
const beforePorcelain = await getPorcelainStatus(cwd);
|
|
806
801
|
const outcome = await delegate({
|
|
807
802
|
model,
|
|
@@ -866,14 +861,12 @@ const plan = buildCommand({
|
|
|
866
861
|
model: {
|
|
867
862
|
kind: "parsed",
|
|
868
863
|
parse: String,
|
|
869
|
-
|
|
870
|
-
brief: `Model slug (default: ${DEFAULT_MODEL})`
|
|
864
|
+
brief: "Model slug (required) — see the seat list above"
|
|
871
865
|
},
|
|
872
866
|
out: {
|
|
873
867
|
kind: "parsed",
|
|
874
868
|
parse: String,
|
|
875
|
-
|
|
876
|
-
brief: "Where to write the plan (default: <run.dir>/plan.md)"
|
|
869
|
+
brief: "Where to write the plan file (required)"
|
|
877
870
|
},
|
|
878
871
|
timeout: {
|
|
879
872
|
kind: "parsed",
|
|
@@ -1032,7 +1025,7 @@ function parseReviewVerdict(response) {
|
|
|
1032
1025
|
};
|
|
1033
1026
|
}
|
|
1034
1027
|
async function review$1(flags) {
|
|
1035
|
-
const inputSlug = flags.model
|
|
1028
|
+
const inputSlug = flags.model;
|
|
1036
1029
|
const model = resolveModel(inputSlug);
|
|
1037
1030
|
if (!model) {
|
|
1038
1031
|
this.process.stderr.write(`${formatUnknownModelError(inputSlug)}\n`);
|
|
@@ -1082,7 +1075,7 @@ async function review$1(flags) {
|
|
|
1082
1075
|
const timeoutSec = flags.timeout ?? 1200;
|
|
1083
1076
|
const modeDetail = isDirty ? absPlanPath ? `diff + plan (${absPlanPath})` : `diff (${baseRef})` : `plan-only (${absPlanPath})`;
|
|
1084
1077
|
const run = startRun("review", `${model.spec.slug}: ${modeDetail}`);
|
|
1085
|
-
const absOutPath =
|
|
1078
|
+
const absOutPath = isAbsolute(flags.out) ? flags.out : resolve(cwd, flags.out);
|
|
1086
1079
|
let reviewPrompt;
|
|
1087
1080
|
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
1081
|
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 +1127,7 @@ const review = buildCommand({
|
|
|
1134
1127
|
model: {
|
|
1135
1128
|
kind: "parsed",
|
|
1136
1129
|
parse: String,
|
|
1137
|
-
|
|
1138
|
-
brief: `Model slug (default: ${DEFAULT_MODEL})`
|
|
1130
|
+
brief: "Model slug (required) — see the seat list above"
|
|
1139
1131
|
},
|
|
1140
1132
|
plan: {
|
|
1141
1133
|
kind: "parsed",
|
|
@@ -1152,8 +1144,7 @@ const review = buildCommand({
|
|
|
1152
1144
|
out: {
|
|
1153
1145
|
kind: "parsed",
|
|
1154
1146
|
parse: String,
|
|
1155
|
-
|
|
1156
|
-
brief: "Where to write the review report (default: <run.dir>/review.md)"
|
|
1147
|
+
brief: "Where to write the review report (required)"
|
|
1157
1148
|
},
|
|
1158
1149
|
timeout: {
|
|
1159
1150
|
kind: "parsed",
|
|
@@ -1345,7 +1336,7 @@ const runs = buildCommand({
|
|
|
1345
1336
|
//#endregion
|
|
1346
1337
|
//#region src/commands/subagent/impl.ts
|
|
1347
1338
|
async function subagent$1(flags, prompt) {
|
|
1348
|
-
const inputSlug = flags.model
|
|
1339
|
+
const inputSlug = flags.model;
|
|
1349
1340
|
const model = resolveModel(inputSlug);
|
|
1350
1341
|
if (!model) {
|
|
1351
1342
|
this.process.stderr.write(`${formatUnknownModelError(inputSlug)}\n`);
|
|
@@ -1399,7 +1390,7 @@ const fullDescription = [
|
|
|
1399
1390
|
"",
|
|
1400
1391
|
"Available models (canonical slug):",
|
|
1401
1392
|
...listModelHelpLines(),
|
|
1402
|
-
|
|
1393
|
+
"Recommended first choice: xai-grok/grok-4.5 (off-budget). The claude-backend slugs are FALLBACKS for",
|
|
1403
1394
|
"when the off-budget CLIs are quota-exhausted — they bill your Claude subscription."
|
|
1404
1395
|
].join("\n");
|
|
1405
1396
|
const subagent = buildCommand({
|
|
@@ -1409,8 +1400,7 @@ const subagent = buildCommand({
|
|
|
1409
1400
|
model: {
|
|
1410
1401
|
kind: "parsed",
|
|
1411
1402
|
parse: String,
|
|
1412
|
-
|
|
1413
|
-
brief: `Model slug to delegate to (default: ${DEFAULT_MODEL})`
|
|
1403
|
+
brief: "Model slug (required) — see the seat list above"
|
|
1414
1404
|
},
|
|
1415
1405
|
timeout: {
|
|
1416
1406
|
kind: "parsed",
|
|
@@ -1494,4 +1484,4 @@ function buildContext(process) {
|
|
|
1494
1484
|
return { process };
|
|
1495
1485
|
}
|
|
1496
1486
|
//#endregion
|
|
1497
|
-
export {
|
|
1487
|
+
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
|
@@ -39,9 +39,6 @@ interface ResolvedModel {
|
|
|
39
39
|
readonly effort: Effort | undefined;
|
|
40
40
|
}
|
|
41
41
|
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
42
|
declare function supportsImageGen(resolved: ResolvedModel): boolean;
|
|
46
43
|
declare function resolveModel(input: string): ResolvedModel | undefined;
|
|
47
44
|
declare function backendModelId(resolved: ResolvedModel): string;
|
|
@@ -179,4 +176,4 @@ declare function renderPreflightRefusal(cmd: string, verdict: {
|
|
|
179
176
|
resetAt: string | undefined;
|
|
180
177
|
}): string;
|
|
181
178
|
//#endregion
|
|
182
|
-
export { type AgentCliDriver, type Backend,
|
|
179
|
+
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 {
|
|
2
|
-
export {
|
|
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-B6I4QI9z.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.
|
|
3
|
+
"version": "0.3.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.
|
|
39
|
-
"@aibridge/driver-
|
|
40
|
-
"@aibridge/driver-grok": "0.
|
|
41
|
-
"@aibridge/driver-
|
|
42
|
-
"@aibridge/driver-
|
|
38
|
+
"@aibridge/proc": "0.3.0",
|
|
39
|
+
"@aibridge/driver-agy": "0.3.0",
|
|
40
|
+
"@aibridge/driver-grok": "0.3.0",
|
|
41
|
+
"@aibridge/driver-codex": "0.3.0",
|
|
42
|
+
"@aibridge/driver-claude": "0.3.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"tsdown": "0.22.14"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buildCommand } from '@stricli/core';
|
|
2
|
-
import {
|
|
2
|
+
import { listModelHelpLines } from '../../models.ts';
|
|
3
3
|
import { nonEmptyPrompt, positiveIntSeconds } from '../../parsers.ts';
|
|
4
4
|
import imageGenImpl from './impl.ts';
|
|
5
5
|
|
|
@@ -9,7 +9,7 @@ const fullDescription = [
|
|
|
9
9
|
'',
|
|
10
10
|
'Image-gen seats (canonical slug):',
|
|
11
11
|
...listModelHelpLines({ imageOnly: true }),
|
|
12
|
-
|
|
12
|
+
'Recommended seat: openai-codex/gpt-5.6-sol.',
|
|
13
13
|
].join('\n');
|
|
14
14
|
|
|
15
15
|
export const imageGen = buildCommand({
|
|
@@ -19,8 +19,7 @@ export const imageGen = buildCommand({
|
|
|
19
19
|
model: {
|
|
20
20
|
kind: 'parsed',
|
|
21
21
|
parse: String,
|
|
22
|
-
|
|
23
|
-
brief: `Model slug (default: ${DEFAULT_IMAGE_GEN})`,
|
|
22
|
+
brief: 'Model slug (required) — see the seat list above',
|
|
24
23
|
},
|
|
25
24
|
out: {
|
|
26
25
|
kind: 'parsed',
|
|
@@ -15,7 +15,6 @@ import type { ImageResult } from '../../driver.ts';
|
|
|
15
15
|
import { getDriver } from '../../drivers.ts';
|
|
16
16
|
import {
|
|
17
17
|
backendModelId,
|
|
18
|
-
DEFAULT_IMAGE_GEN,
|
|
19
18
|
formatImageGenModelError,
|
|
20
19
|
formatUnknownModelError,
|
|
21
20
|
imageFormatFor,
|
|
@@ -24,7 +23,7 @@ import {
|
|
|
24
23
|
} from '../../models.ts';
|
|
25
24
|
|
|
26
25
|
export interface ImageGenFlags {
|
|
27
|
-
readonly model
|
|
26
|
+
readonly model: string;
|
|
28
27
|
readonly out: string;
|
|
29
28
|
readonly aspectRatio?: string;
|
|
30
29
|
readonly image?: string;
|
|
@@ -45,14 +44,14 @@ export default async function imageGen(
|
|
|
45
44
|
this.process.exitCode = 1;
|
|
46
45
|
};
|
|
47
46
|
|
|
48
|
-
const inputSlug = flags.model
|
|
47
|
+
const inputSlug = flags.model;
|
|
49
48
|
const model = resolveModel(inputSlug);
|
|
50
49
|
if (!model) return fail(formatUnknownModelError(inputSlug));
|
|
51
50
|
if (!supportsImageGen(model)) return fail(formatImageGenModelError(inputSlug, model));
|
|
52
51
|
|
|
53
52
|
if (model.spec.backend === 'codex' && model.effort) {
|
|
54
53
|
return fail(
|
|
55
|
-
`effort "-${model.effort}" has no effect on image-gen (the image tool renders, not the seat model);
|
|
54
|
+
`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.`,
|
|
56
55
|
);
|
|
57
56
|
}
|
|
58
57
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buildCommand } from '@stricli/core';
|
|
2
|
-
import {
|
|
2
|
+
import { listModelHelpLines } from '../../models.ts';
|
|
3
3
|
import { positiveIntSeconds } from '../../parsers.ts';
|
|
4
4
|
import implementImpl from './impl.ts';
|
|
5
5
|
|
|
@@ -17,8 +17,7 @@ export const implement = buildCommand({
|
|
|
17
17
|
model: {
|
|
18
18
|
kind: 'parsed',
|
|
19
19
|
parse: String,
|
|
20
|
-
|
|
21
|
-
brief: `Model slug (default: ${DEFAULT_IMPLEMENTER})`,
|
|
20
|
+
brief: 'Model slug (required) — see the seat list above',
|
|
22
21
|
},
|
|
23
22
|
timeout: {
|
|
24
23
|
kind: 'parsed',
|
|
@@ -3,12 +3,12 @@ import { isAbsolute, resolve } from 'node:path';
|
|
|
3
3
|
import { runCaptured } from '@aibridge/proc';
|
|
4
4
|
import type { LocalContext } from '../../context.ts';
|
|
5
5
|
import { delegate } from '../../delegate.ts';
|
|
6
|
-
import {
|
|
6
|
+
import { formatUnknownModelError, resolveModel } from '../../models.ts';
|
|
7
7
|
import { preflightModel, renderPreflightRefusal } from '../../quotaPreflight.ts';
|
|
8
8
|
import { startRun } from '../../runlog.ts';
|
|
9
9
|
|
|
10
10
|
export interface ImplementFlags {
|
|
11
|
-
readonly model
|
|
11
|
+
readonly model: string;
|
|
12
12
|
readonly timeout?: number;
|
|
13
13
|
readonly preflight: boolean;
|
|
14
14
|
}
|
|
@@ -18,7 +18,7 @@ export default async function implement(
|
|
|
18
18
|
flags: ImplementFlags,
|
|
19
19
|
planFile: string,
|
|
20
20
|
): Promise<void> {
|
|
21
|
-
const inputSlug = flags.model
|
|
21
|
+
const inputSlug = flags.model;
|
|
22
22
|
const model = resolveModel(inputSlug);
|
|
23
23
|
if (!model) {
|
|
24
24
|
this.process.stderr.write(`${formatUnknownModelError(inputSlug)}\n`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buildCommand } from '@stricli/core';
|
|
2
|
-
import {
|
|
2
|
+
import { listModelHelpLines } from '../../models.ts';
|
|
3
3
|
import { nonEmptyPrompt, positiveIntSeconds } from '../../parsers.ts';
|
|
4
4
|
import planImpl from './impl.ts';
|
|
5
5
|
|
|
@@ -17,14 +17,12 @@ export const plan = buildCommand({
|
|
|
17
17
|
model: {
|
|
18
18
|
kind: 'parsed',
|
|
19
19
|
parse: String,
|
|
20
|
-
|
|
21
|
-
brief: `Model slug (default: ${DEFAULT_MODEL})`,
|
|
20
|
+
brief: 'Model slug (required) — see the seat list above',
|
|
22
21
|
},
|
|
23
22
|
out: {
|
|
24
23
|
kind: 'parsed',
|
|
25
24
|
parse: String,
|
|
26
|
-
|
|
27
|
-
brief: 'Where to write the plan (default: <run.dir>/plan.md)',
|
|
25
|
+
brief: 'Where to write the plan file (required)',
|
|
28
26
|
},
|
|
29
27
|
timeout: {
|
|
30
28
|
kind: 'parsed',
|
|
@@ -3,13 +3,13 @@ import { isAbsolute, resolve } from 'node:path';
|
|
|
3
3
|
import { runCaptured } from '@aibridge/proc';
|
|
4
4
|
import type { LocalContext } from '../../context.ts';
|
|
5
5
|
import { delegate } from '../../delegate.ts';
|
|
6
|
-
import {
|
|
6
|
+
import { formatUnknownModelError, resolveModel } from '../../models.ts';
|
|
7
7
|
import { preflightModel, renderPreflightRefusal } from '../../quotaPreflight.ts';
|
|
8
8
|
import { startRun } from '../../runlog.ts';
|
|
9
9
|
|
|
10
10
|
export interface PlanFlags {
|
|
11
|
-
readonly model
|
|
12
|
-
readonly out
|
|
11
|
+
readonly model: string;
|
|
12
|
+
readonly out: string;
|
|
13
13
|
readonly timeout?: number;
|
|
14
14
|
readonly preflight: boolean;
|
|
15
15
|
}
|
|
@@ -63,7 +63,7 @@ export default async function plan(
|
|
|
63
63
|
flags: PlanFlags,
|
|
64
64
|
taskPrompt: string,
|
|
65
65
|
): Promise<void> {
|
|
66
|
-
const inputSlug = flags.model
|
|
66
|
+
const inputSlug = flags.model;
|
|
67
67
|
const model = resolveModel(inputSlug);
|
|
68
68
|
if (!model) {
|
|
69
69
|
this.process.stderr.write(`${formatUnknownModelError(inputSlug)}\n`);
|
|
@@ -86,11 +86,7 @@ export default async function plan(
|
|
|
86
86
|
const promptSnippet = taskPrompt.replace(/\r?\n/g, ' ').slice(0, 80);
|
|
87
87
|
const run = startRun('plan', `${model.spec.slug}: ${promptSnippet}`);
|
|
88
88
|
|
|
89
|
-
const absOutPath = flags.out
|
|
90
|
-
? isAbsolute(flags.out)
|
|
91
|
-
? flags.out
|
|
92
|
-
: resolve(cwd, flags.out)
|
|
93
|
-
: resolve(run.dir, 'plan.md');
|
|
89
|
+
const absOutPath = isAbsolute(flags.out) ? flags.out : resolve(cwd, flags.out);
|
|
94
90
|
|
|
95
91
|
const beforePorcelain = await getPorcelainStatus(cwd);
|
|
96
92
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buildCommand } from '@stricli/core';
|
|
2
|
-
import {
|
|
2
|
+
import { listModelHelpLines } from '../../models.ts';
|
|
3
3
|
import { positiveIntSeconds } from '../../parsers.ts';
|
|
4
4
|
import reviewImpl from './impl.ts';
|
|
5
5
|
|
|
@@ -17,8 +17,7 @@ export const review = buildCommand({
|
|
|
17
17
|
model: {
|
|
18
18
|
kind: 'parsed',
|
|
19
19
|
parse: String,
|
|
20
|
-
|
|
21
|
-
brief: `Model slug (default: ${DEFAULT_MODEL})`,
|
|
20
|
+
brief: 'Model slug (required) — see the seat list above',
|
|
22
21
|
},
|
|
23
22
|
plan: {
|
|
24
23
|
kind: 'parsed',
|
|
@@ -35,8 +34,7 @@ export const review = buildCommand({
|
|
|
35
34
|
out: {
|
|
36
35
|
kind: 'parsed',
|
|
37
36
|
parse: String,
|
|
38
|
-
|
|
39
|
-
brief: 'Where to write the review report (default: <run.dir>/review.md)',
|
|
37
|
+
brief: 'Where to write the review report (required)',
|
|
40
38
|
},
|
|
41
39
|
timeout: {
|
|
42
40
|
kind: 'parsed',
|
|
@@ -3,15 +3,15 @@ import { isAbsolute, resolve } from 'node:path';
|
|
|
3
3
|
import { runCaptured } from '@aibridge/proc';
|
|
4
4
|
import type { LocalContext } from '../../context.ts';
|
|
5
5
|
import { delegate } from '../../delegate.ts';
|
|
6
|
-
import {
|
|
6
|
+
import { formatUnknownModelError, resolveModel } from '../../models.ts';
|
|
7
7
|
import { preflightModel, renderPreflightRefusal } from '../../quotaPreflight.ts';
|
|
8
8
|
import { startRun } from '../../runlog.ts';
|
|
9
9
|
|
|
10
10
|
export interface ReviewFlags {
|
|
11
|
-
readonly model
|
|
11
|
+
readonly model: string;
|
|
12
12
|
readonly plan?: string;
|
|
13
13
|
readonly base?: string;
|
|
14
|
-
readonly out
|
|
14
|
+
readonly out: string;
|
|
15
15
|
readonly timeout?: number;
|
|
16
16
|
readonly preflight: boolean;
|
|
17
17
|
}
|
|
@@ -78,7 +78,7 @@ export function parseReviewVerdict(response: string): ReviewVerdictResult {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
export default async function review(this: LocalContext, flags: ReviewFlags): Promise<void> {
|
|
81
|
-
const inputSlug = flags.model
|
|
81
|
+
const inputSlug = flags.model;
|
|
82
82
|
const model = resolveModel(inputSlug);
|
|
83
83
|
if (!model) {
|
|
84
84
|
this.process.stderr.write(`${formatUnknownModelError(inputSlug)}\n`);
|
|
@@ -140,11 +140,7 @@ export default async function review(this: LocalContext, flags: ReviewFlags): Pr
|
|
|
140
140
|
|
|
141
141
|
const run = startRun('review', `${model.spec.slug}: ${modeDetail}`);
|
|
142
142
|
|
|
143
|
-
const absOutPath = flags.out
|
|
144
|
-
? isAbsolute(flags.out)
|
|
145
|
-
? flags.out
|
|
146
|
-
: resolve(cwd, flags.out)
|
|
147
|
-
: resolve(run.dir, 'review.md');
|
|
143
|
+
const absOutPath = isAbsolute(flags.out) ? flags.out : resolve(cwd, flags.out);
|
|
148
144
|
|
|
149
145
|
let reviewPrompt: string;
|
|
150
146
|
if (isDirty) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buildCommand } from '@stricli/core';
|
|
2
|
-
import {
|
|
2
|
+
import { listModelHelpLines } from '../../models.ts';
|
|
3
3
|
import { nonEmptyPrompt, positiveIntSeconds } from '../../parsers.ts';
|
|
4
4
|
import subagentImpl from './impl.ts';
|
|
5
5
|
|
|
@@ -8,7 +8,7 @@ const fullDescription = [
|
|
|
8
8
|
'',
|
|
9
9
|
'Available models (canonical slug):',
|
|
10
10
|
...listModelHelpLines(),
|
|
11
|
-
|
|
11
|
+
'Recommended first choice: xai-grok/grok-4.5 (off-budget). The claude-backend slugs are FALLBACKS for',
|
|
12
12
|
'when the off-budget CLIs are quota-exhausted — they bill your Claude subscription.',
|
|
13
13
|
].join('\n');
|
|
14
14
|
|
|
@@ -19,8 +19,7 @@ export const subagent = buildCommand({
|
|
|
19
19
|
model: {
|
|
20
20
|
kind: 'parsed',
|
|
21
21
|
parse: String,
|
|
22
|
-
|
|
23
|
-
brief: `Model slug to delegate to (default: ${DEFAULT_MODEL})`,
|
|
22
|
+
brief: 'Model slug (required) — see the seat list above',
|
|
24
23
|
},
|
|
25
24
|
timeout: {
|
|
26
25
|
kind: 'parsed',
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import type { LocalContext } from '../../context.ts';
|
|
2
2
|
import { delegate } from '../../delegate.ts';
|
|
3
|
-
import {
|
|
4
|
-
backendModelId,
|
|
5
|
-
DEFAULT_MODEL,
|
|
6
|
-
formatUnknownModelError,
|
|
7
|
-
resolveModel,
|
|
8
|
-
} from '../../models.ts';
|
|
3
|
+
import { backendModelId, formatUnknownModelError, resolveModel } from '../../models.ts';
|
|
9
4
|
import { preflightModel, renderPreflightRefusal } from '../../quotaPreflight.ts';
|
|
10
5
|
import { startRun } from '../../runlog.ts';
|
|
11
6
|
|
|
12
7
|
export interface SubagentFlags {
|
|
13
|
-
readonly model
|
|
8
|
+
readonly model: string;
|
|
14
9
|
readonly timeout?: number;
|
|
15
10
|
readonly tools: boolean;
|
|
16
11
|
readonly preflight: boolean;
|
|
@@ -22,7 +17,7 @@ export default async function subagent(
|
|
|
22
17
|
flags: SubagentFlags,
|
|
23
18
|
prompt: string,
|
|
24
19
|
): Promise<void> {
|
|
25
|
-
const inputSlug = flags.model
|
|
20
|
+
const inputSlug = flags.model;
|
|
26
21
|
const model = resolveModel(inputSlug);
|
|
27
22
|
if (!model) {
|
|
28
23
|
this.process.stderr.write(`${formatUnknownModelError(inputSlug)}\n`);
|
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.ts
CHANGED
|
@@ -95,10 +95,6 @@ export const MODELS: Record<string, ModelSpec> = {
|
|
|
95
95
|
},
|
|
96
96
|
} as const satisfies Record<string, ModelSpec>;
|
|
97
97
|
|
|
98
|
-
export const DEFAULT_MODEL = 'xai-grok/grok-4.5';
|
|
99
|
-
export const DEFAULT_IMPLEMENTER = 'google-antigravity/gemini-3.6-flash';
|
|
100
|
-
export const DEFAULT_IMAGE_GEN = 'openai-codex/gpt-5.6-sol';
|
|
101
|
-
|
|
102
98
|
export type ImageFormat = 'jpg' | 'png';
|
|
103
99
|
|
|
104
100
|
const IMAGE_GEN_FORMATS: ReadonlyMap<Backend, ImageFormat> = new Map([
|