@aibridge/cli 0.8.0 → 0.10.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 +48 -28
- package/dist/cli.mjs +1 -1
- package/dist/{context-DJtEcg6f.mjs → context-DeJmEXLe.mjs} +194 -125
- package/dist/index.mjs +1 -1
- package/instructions/SKILL.md +115 -0
- package/instructions/reference/image-gen.md +107 -0
- package/instructions/reference/implement.md +51 -0
- package/instructions/reference/plan.md +69 -0
- package/instructions/reference/review.md +75 -0
- package/instructions/reference/subagent.md +48 -0
- package/instructions/reference/why.md +54 -0
- package/package.json +8 -7
- package/src/app.exit-code.test.ts +10 -1
- package/src/app.ts +4 -5
- package/src/commands/image-gen/command.ts +5 -0
- package/src/commands/image-gen/impl.test.ts +1 -0
- package/src/commands/image-gen/impl.ts +14 -0
- package/src/commands/review/command.ts +2 -2
- package/src/commands/review/impl.ts +1 -1
- package/src/commands/skill/command.ts +28 -0
- package/src/commands/skill/impl.test.ts +59 -0
- package/src/commands/skill/impl.ts +60 -0
- package/src/package.ts +5 -0
- package/src/quotaPreflight.test.ts +13 -1
- package/src/quotaPreflight.ts +10 -2
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# image-gen — generate an image via a model seat
|
|
2
|
+
|
|
3
|
+
Part A covers the command. Part B covers prompt design, which applies even when
|
|
4
|
+
you only need to return a prompt.
|
|
5
|
+
|
|
6
|
+
## A. Calling it
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
aibridge image-gen --model <slug> --out <file.png> "<full prompt — see part B>" \
|
|
10
|
+
[--aspect-ratio 16:9] [--image ref.png] [--transparent] \
|
|
11
|
+
[--timeout 600] [--no-preflight] [--json]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| slug | renders via | format |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| `openai-codex/gpt-5.6-sol` (recommended) | Codex CLI | PNG |
|
|
17
|
+
| `google-antigravity/gemini-3.7-flash` | Antigravity CLI (`agy`) | JPEG |
|
|
18
|
+
| `xai-grok/grok-4.6` | `api.x.ai` directly, on `~/.grok/auth.json` | JPEG |
|
|
19
|
+
|
|
20
|
+
Other seats fail fast with a list of capable models. The grok seat renders
|
|
21
|
+
without `grok` on `PATH`; the CLI is spawned only to refresh the token, before a
|
|
22
|
+
request when `expires_at` is close and again as a backstop if one comes back
|
|
23
|
+
401. `grok login` is still what sets the seat up.
|
|
24
|
+
|
|
25
|
+
- `--out` is required and its extension must match the seat's format above
|
|
26
|
+
(`.png` for any `--transparent` run). A mismatch is rejected before anything
|
|
27
|
+
runs. The file holds the model's own bytes verbatim, except on a
|
|
28
|
+
chroma-keyed `--transparent` run, which writes the locally keyed PNG.
|
|
29
|
+
- **Render into the asset's real home** (`public/icons/settings.png`) when the
|
|
30
|
+
project keeps it. Drafts go to `.aibridge/`; see [SKILL.md](../SKILL.md).
|
|
31
|
+
- `--aspect-ratio N:M` sets geometry. Exact pixels are whatever the model
|
|
32
|
+
renders; resize downstream.
|
|
33
|
+
- `--image a.png,b.png` attaches references and routes to the seat's edit path.
|
|
34
|
+
- `--json` prints `{ out, bytes, width, height, aspectRatio, model, backend, transparency, real }`.
|
|
35
|
+
- Preflight is on by default: an exhausted seat exits 3 and names the
|
|
36
|
+
alternatives instead of spending a paid render. Argument checks run first, so
|
|
37
|
+
bad flags still cost no network call.
|
|
38
|
+
|
|
39
|
+
### Transparency
|
|
40
|
+
|
|
41
|
+
- `--transparent` works on every image seat and always writes PNG. Codex gives
|
|
42
|
+
native alpha; grok and gemini are chroma-keyed with binary edges. Fine for
|
|
43
|
+
flat icons, logos and stickers; not for hair, smoke or glass.
|
|
44
|
+
- **Say nothing about the background in the prompt when using it.** The CLI
|
|
45
|
+
writes the backdrop instruction per seat, and a colour of your own overrides
|
|
46
|
+
it: the key then finds nothing and the paid render comes back opaque. Writing
|
|
47
|
+
"transparent background" into the prompt without the flag is refused on
|
|
48
|
+
chroma seats.
|
|
49
|
+
- **A green subject is keyed away with the backdrop** on chroma seats. Anything
|
|
50
|
+
that must be green needs `openai-codex/*`.
|
|
51
|
+
- Both surfaces report the path taken: the result line says
|
|
52
|
+
`transparency: native` / `chroma-keyed`, `--json` carries
|
|
53
|
+
`"native" | "chroma" | null`. Quote it when the edges matter. Keying under 2%
|
|
54
|
+
of the image warns: the model likely ignored the backdrop instruction.
|
|
55
|
+
|
|
56
|
+
### Reference images
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
aibridge image-gen --model openai-codex/gpt-5.6-sol \
|
|
60
|
+
"the same woman, now in a denim shirt in a bright kitchen, waist-up" \
|
|
61
|
+
--out avatar2.png --image avatar.png --aspect-ratio 9:16
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
With a reference, write the prompt as a **diff**: say only what changes. The
|
|
65
|
+
reference carries identity, framing and style.
|
|
66
|
+
|
|
67
|
+
## B. Writing the prompt
|
|
68
|
+
|
|
69
|
+
1. **Never state the image's purpose** (no "app icon", "hero banner"). Describe
|
|
70
|
+
how it looks, not its job.
|
|
71
|
+
2. **Give the aspect ratio** via `--aspect-ratio` and/or the prompt text.
|
|
72
|
+
3. **Always specify the background**: a hex colour, or `opaque`/`auto`. Except
|
|
73
|
+
on a `--transparent` run, where you say nothing about it.
|
|
74
|
+
4. **Specify** subject, composition, palette, style/medium, mood, lighting.
|
|
75
|
+
5. **Lock the critical, free the rest.** Lock verbatim text in straight quotes,
|
|
76
|
+
brand hex, required layout, the hero subject, and every "no X". Over-specified
|
|
77
|
+
incidental detail flattens the result.
|
|
78
|
+
|
|
79
|
+
### Worked example
|
|
80
|
+
|
|
81
|
+
> `Square 1:1 aspect ratio. Solid warm cream #F7F1EA background. A single flat-design
|
|
82
|
+
> fanned stack of three rounded-corner cards in coral orange #FF5A1F, centered,
|
|
83
|
+
> ~55% of the frame, soft drop shadow. Calm, minimal, modern. No text, no border,
|
|
84
|
+
> no gradient.`
|
|
85
|
+
|
|
86
|
+
Locked: ratio, background hex, subject, brand orange, the no-text/border/gradient
|
|
87
|
+
constraints. Free: fan angle, shadow softness, spacing. On a `--transparent` run,
|
|
88
|
+
drop the background sentence and the drop shadow: a soft shadow has no flat
|
|
89
|
+
backdrop colour to key against and survives as a pale halo.
|
|
90
|
+
|
|
91
|
+
### Edits and retries
|
|
92
|
+
|
|
93
|
+
1. **Describe the desired result.** State what the finished image contains,
|
|
94
|
+
rather than only what to remove or correct.
|
|
95
|
+
2. **Name the visual criterion first.** Phrases such as "the near plane" or
|
|
96
|
+
"everyone waiting" often work better than an object inventory. Enumerate
|
|
97
|
+
objects only if the criterion fails.
|
|
98
|
+
3. **Rewrite failed prompts; do not append to them.** Simplify the request. For
|
|
99
|
+
a near-correct render, make the next instruction as short as possible.
|
|
100
|
+
4. **Change strategy after the same failure occurs twice.** Try a different
|
|
101
|
+
reference, composition, or fresh generation instead of another rewording.
|
|
102
|
+
5. **Retry from the original reference.** Chaining generated outputs causes
|
|
103
|
+
unrequested details to drift.
|
|
104
|
+
6. **Check your terminology.** A near-miss word can produce a coherent image of
|
|
105
|
+
the wrong concept.
|
|
106
|
+
7. **Verify unchanged details in context.** Inspect the asset in its final
|
|
107
|
+
placement and, when relevant, in motion.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# implement — execute a plan file
|
|
2
|
+
|
|
3
|
+
An implementer edits the working tree and runs the project's checks. This is the
|
|
4
|
+
third step in **`plan` → read and approve → `implement` → `review`**.
|
|
5
|
+
|
|
6
|
+
Run this only after reading and approving the plan. If the implementer would
|
|
7
|
+
need to make a product or architecture decision, fix the plan first.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
aibridge implement --model <slug> <plan-file>
|
|
13
|
+
--model <slug> implementer model (required, e.g. google-antigravity/gemini-3.7-flash)
|
|
14
|
+
--timeout <secs> max seconds (default: 1800)
|
|
15
|
+
--no-preflight skip the quota preflight
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Behaviour
|
|
19
|
+
|
|
20
|
+
The delegate runs with full tools at the repository root. It must follow the
|
|
21
|
+
plan, edit only named files, run the specified checks until they pass, and avoid
|
|
22
|
+
commits, pushes, and unrelated deletions.
|
|
23
|
+
|
|
24
|
+
## Output
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
<delegate's short summary>
|
|
28
|
+
|
|
29
|
+
<git diff --stat>
|
|
30
|
+
untracked files: <count>
|
|
31
|
+
run: <run id>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Exit 0: completed with tree changes. Exit 1: delegate failed, timed out, gave no
|
|
35
|
+
usable answer, or changed nothing (a no-op implement is a failure). Exit 2: bad
|
|
36
|
+
args or missing plan file. Exit 3: quota preflight refusal.
|
|
37
|
+
|
|
38
|
+
## After it returns
|
|
39
|
+
|
|
40
|
+
1. **Re-run the checks yourself.** The summary reports the delegate's claim.
|
|
41
|
+
2. Then `aibridge review --model xai-grok/grok-4.6 --plan <plan-file> --out .aibridge/review.md`.
|
|
42
|
+
|
|
43
|
+
## Gotchas
|
|
44
|
+
|
|
45
|
+
- Keep the implementer a different model family from the reviewer. The
|
|
46
|
+
recommended seats (gemini implements, grok reviews) comply; if you override
|
|
47
|
+
one, check the other.
|
|
48
|
+
- agy quota is shared by model group. Two concurrent agy-heavy runs drain
|
|
49
|
+
the same window. Run `aibridge quota` before pipelining.
|
|
50
|
+
- The timeout covers the whole run including gate-fixing loops. Raise it for big
|
|
51
|
+
plans rather than letting a near-done run get killed.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# plan — write a detailed implementation plan file
|
|
2
|
+
|
|
3
|
+
A planner studies the codebase and writes a detailed plan to a file. This is the
|
|
4
|
+
first step in **`plan` → read and approve → `implement` → `review`**. Pass the
|
|
5
|
+
file path—not its contents—between stages.
|
|
6
|
+
|
|
7
|
+
Use it for sizeable or risky work. For small, fully-specified chunks use
|
|
8
|
+
`subagent`, or just do them.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
aibridge plan --model <slug> --out <file> "<task prompt>"
|
|
14
|
+
--model <slug> planner model (required, e.g. xai-grok/grok-4.6)
|
|
15
|
+
--out <file> where to write the plan (required)
|
|
16
|
+
--timeout <secs> max seconds (default: 1800)
|
|
17
|
+
--no-preflight skip the quota preflight
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The positional argument is the task prompt, not a file path.
|
|
21
|
+
|
|
22
|
+
## Writing the task prompt
|
|
23
|
+
|
|
24
|
+
The planner can read the code. Do not paste file contents. Provide only what it
|
|
25
|
+
cannot infer:
|
|
26
|
+
|
|
27
|
+
- the goal and the user-visible behaviour change;
|
|
28
|
+
- hard constraints (APIs to keep stable, zero-dep rules, style conventions);
|
|
29
|
+
- scope boundaries and non-goals;
|
|
30
|
+
- architectural calls you have already made, since the planner details your
|
|
31
|
+
design rather than overruling it;
|
|
32
|
+
- starting files, if the repo is large.
|
|
33
|
+
|
|
34
|
+
## Behaviour
|
|
35
|
+
|
|
36
|
+
- The planner writes exactly one file (`--out`). Any other change to the working
|
|
37
|
+
tree fails the run (exit 1, paths listed). An `--out` inside the repo is
|
|
38
|
+
exempt.
|
|
39
|
+
- The plan must end with `## Open questions` (`None.` when confident). The count
|
|
40
|
+
prints on stdout.
|
|
41
|
+
|
|
42
|
+
## Output
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
plan: /abs/path/to/plan.md
|
|
46
|
+
open questions: 2
|
|
47
|
+
run: <run id>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Exit 0 even with open questions. Exit 1: plan missing, empty, no open-questions
|
|
51
|
+
section, tree dirtied, delegate failure or timeout. Exit 2: bad arguments.
|
|
52
|
+
Exit 3: quota preflight refusal.
|
|
53
|
+
|
|
54
|
+
## After it returns
|
|
55
|
+
|
|
56
|
+
1. **Read the plan file.** It is the implementation contract.
|
|
57
|
+
2. Resolve every open question: edit the file directly, or re-run `plan` with a
|
|
58
|
+
sharpened prompt.
|
|
59
|
+
3. High-risk design? Gate it first:
|
|
60
|
+
`aibridge review --model xai-grok/grok-4.6 --plan <file> --out .aibridge/review.md`
|
|
61
|
+
on a clean tree.
|
|
62
|
+
4. Then `aibridge implement --model google-antigravity/gemini-3.7-flash <file>`.
|
|
63
|
+
|
|
64
|
+
## Gotchas
|
|
65
|
+
|
|
66
|
+
- `--out` belongs in `.aibridge/` (see [SKILL.md](../SKILL.md)); check it is
|
|
67
|
+
gitignored once per session.
|
|
68
|
+
- grok is capped at ~30 req/min, ~1k msgs/day, one run at a time. Never run two
|
|
69
|
+
grok stages concurrently.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# review — cross-model review of a diff or a plan
|
|
2
|
+
|
|
3
|
+
A reviewer inspects a diff, optionally against a plan contract, and writes its
|
|
4
|
+
report to a file. Standard output contains only the verdict and paths.
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
aibridge review --model <slug> --out <file> [options]
|
|
10
|
+
--model <slug> reviewer model (required, e.g. xai-grok/grok-4.6)
|
|
11
|
+
--plan <file> plan contract; over-reach against it is a finding
|
|
12
|
+
--base <ref> any git ref or range to diff against (default: HEAD)
|
|
13
|
+
--out <file> full report destination (required)
|
|
14
|
+
--timeout <secs> max seconds (default: 1200)
|
|
15
|
+
--no-preflight skip the quota preflight
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Picking the diff
|
|
19
|
+
|
|
20
|
+
`--base` is passed straight to `git diff`, so any ref or range git accepts
|
|
21
|
+
works: `HEAD~3`, a branch, a SHA, a tag, `v1.2.0..HEAD`.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# uncommitted work only (the default base, HEAD)
|
|
25
|
+
aibridge review --model xai-grok/grok-4.6 --out .aibridge/review.md
|
|
26
|
+
|
|
27
|
+
# the 3 commits you just made
|
|
28
|
+
aibridge review --model xai-grok/grok-4.6 --base HEAD~3 --out .aibridge/review.md
|
|
29
|
+
|
|
30
|
+
# the whole branch, against the plan contract
|
|
31
|
+
aibridge review --model xai-grok/grok-4.6 --base main \
|
|
32
|
+
--plan .aibridge/plan.md --out .aibridge/review.md
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Use `--base` to review committed work. Do not copy a diff into a file for
|
|
36
|
+
`subagent`. Uncommitted changes are included unless the range fixes both ends.
|
|
37
|
+
|
|
38
|
+
## Modes (detected before any model spend)
|
|
39
|
+
|
|
40
|
+
1. **Diff review:** changes or untracked files exist relative to `--base`. With
|
|
41
|
+
`--plan`, unrequested changes count as over-reach.
|
|
42
|
+
2. **Plan review:** nothing differs from `--base` and `--plan` is given.
|
|
43
|
+
Reviews the plan for soundness, edge cases, safety, feasibility. Use it as a
|
|
44
|
+
pre-implementation gate on high-risk designs.
|
|
45
|
+
3. **No input:** nothing differs and no `--plan` is given. Exits 2.
|
|
46
|
+
|
|
47
|
+
## Output & exit codes
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
PASS | FINDINGS: 1 critical, 0 major, 3 minor
|
|
51
|
+
review: /abs/path/to/review.md
|
|
52
|
+
run: <run id>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- `0`: PASS, or minor-only findings.
|
|
56
|
+
- `1`: critical or major findings; unparseable verdict (raw answer + paths still
|
|
57
|
+
print); missing or empty report file; delegate failure or timeout.
|
|
58
|
+
- `2`: bad flags, missing plan file, nothing to review, bad base ref.
|
|
59
|
+
- `3`: quota preflight refusal.
|
|
60
|
+
|
|
61
|
+
## After it returns
|
|
62
|
+
|
|
63
|
+
- `PASS` → proceed (commit, or report done).
|
|
64
|
+
- Findings → read the report, then judge. Over-reach findings can be scope you
|
|
65
|
+
added deliberately: the reviewer flags, you decide. Fix what is real, re-run.
|
|
66
|
+
- Never let a model review its own diff. The recommended seats (grok reviews,
|
|
67
|
+
gemini implements) already comply; if you override one, check the other.
|
|
68
|
+
|
|
69
|
+
## Gotchas
|
|
70
|
+
|
|
71
|
+
- The report file is required even for PASS. Missing or empty fails the run.
|
|
72
|
+
- Verdict parsing accepts the line first, last, or at the end of a narration
|
|
73
|
+
blob (grok concatenates progress prose). If it still fails, you get exit 1
|
|
74
|
+
with the raw answer plus the report path on stdout.
|
|
75
|
+
- A bad `--base` ref is a hard error (exit 2), not a silent dirty-tree fallback.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# subagent — delegate a task to another model
|
|
2
|
+
|
|
3
|
+
Delegate one self-contained task to another provider's model. Use this for a
|
|
4
|
+
second opinion, red-team review, long-context analysis, or a clearly specified
|
|
5
|
+
piece of work.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
aibridge subagent --model <slug> "<self-contained prompt>" [options]
|
|
11
|
+
--model <slug> required, canonical slug (no short aliases)
|
|
12
|
+
--timeout <secs> max seconds (default: 600)
|
|
13
|
+
--no-tools reasoning only: no file or shell access
|
|
14
|
+
--no-preflight skip the quota preflight
|
|
15
|
+
--json machine-readable, e.g. {"model": "grok-4.6", "slug": "xai-grok/grok-4.6", ...}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The answer prints to stdout. There is no `--out`; redirect if you want a file.
|
|
19
|
+
|
|
20
|
+
Effort suffixes work on seats that support them
|
|
21
|
+
(`xai-grok/grok-4.6-medium`, `google-antigravity/gemini-3.7-flash-low`,
|
|
22
|
+
`anthropic-claude/sonnet-5-max`). The seat table is in [SKILL.md](../SKILL.md);
|
|
23
|
+
`aibridge subagent --help` prints the live list.
|
|
24
|
+
|
|
25
|
+
**Tools are ON by default** — the delegate reads/writes files and runs shell.
|
|
26
|
+
|
|
27
|
+
## Writing the prompt
|
|
28
|
+
|
|
29
|
+
Write for a capable model with no conversation context:
|
|
30
|
+
|
|
31
|
+
1. **Self-contained.** Paste the code or spec to act on. Never reference "what
|
|
32
|
+
we discussed".
|
|
33
|
+
2. **Specify the approach.** Include design decisions, interfaces, files, and
|
|
34
|
+
constraints without prescribing every line of code.
|
|
35
|
+
3. **State the constraints.** Run the real typecheck and tests until green;
|
|
36
|
+
write code only, no commit/push/deploy/delete; reply with a short summary.
|
|
37
|
+
4. **Verify the result.** Re-run the real gates yourself.
|
|
38
|
+
|
|
39
|
+
## When to stay native instead
|
|
40
|
+
|
|
41
|
+
- The task needs session-specific tools, skills, or MCP servers.
|
|
42
|
+
- It needs conversational context or live user guidance.
|
|
43
|
+
- It needs strict schema validation or guaranteed retry orchestration.
|
|
44
|
+
|
|
45
|
+
## Scaling up
|
|
46
|
+
|
|
47
|
+
For large or multi-file work, use the three verbs instead of raw subagent calls:
|
|
48
|
+
[plan](plan.md) → you approve → [implement](implement.md) → [review](review.md).
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# why — the reasoning behind the rules
|
|
2
|
+
|
|
3
|
+
This page explains rules that the command guides state without rationale. Read
|
|
4
|
+
it when a rule appears unsuitable for your case.
|
|
5
|
+
|
|
6
|
+
**Paths travel between stages, not contents.** A plan re-emitted into your
|
|
7
|
+
context costs you the tokens the split was meant to save. `plan` writes a file,
|
|
8
|
+
`implement` reads that path, `review` takes it as `--plan`. Nothing round-trips
|
|
9
|
+
through you except your judgment.
|
|
10
|
+
|
|
11
|
+
**You read the plan file.** That sign-off is the entire point of splitting plan
|
|
12
|
+
from implement. Skip it and you have an unreviewed contract that a second model
|
|
13
|
+
will now execute literally.
|
|
14
|
+
|
|
15
|
+
**The implementer is a pure do-er.** It executes a fully-designed plan and does
|
|
16
|
+
not hold the surrounding vision. When a plan turns out to need that vision, the
|
|
17
|
+
design work upstream was insufficient. Fix the plan rather than briefing the
|
|
18
|
+
implementer.
|
|
19
|
+
|
|
20
|
+
**Reviewers are cross-model on purpose.** A model reviewing its own diff shares
|
|
21
|
+
its own blind spots and will wave through the thing it just failed to see. This
|
|
22
|
+
applies to you too: prefer a reviewer outside your own model family for code you
|
|
23
|
+
wrote yourself.
|
|
24
|
+
|
|
25
|
+
**Over-reach is a finding.** A delegate that also refactors three neighbouring
|
|
26
|
+
files has left the contract, and the diff you now have to verify is bigger than
|
|
27
|
+
the one you asked for. The reviewer flags it; you decide whether it was scope
|
|
28
|
+
you wanted.
|
|
29
|
+
|
|
30
|
+
**Quota is relative to whoever runs the skill.** Every backend spends its own
|
|
31
|
+
CLI's login. A backend on the same provider as you (`anthropic-claude/*` for a
|
|
32
|
+
Claude-based agent) drains the pool you are already burning, so it buys no extra
|
|
33
|
+
capacity and no independent perspective. That is why it is a last resort rather
|
|
34
|
+
than merely a choice.
|
|
35
|
+
|
|
36
|
+
**Seats pin exact model versions.** A vendor alias like `opus` moves under you
|
|
37
|
+
when a release lands, silently changing what a documented pipeline does.
|
|
38
|
+
|
|
39
|
+
**Some commands write files, some print.** `plan`, `review` and `image-gen`
|
|
40
|
+
produce artifacts worth keeping and re-reading, so they take `--out` and keep
|
|
41
|
+
stdout to a verdict line. `subagent` returns an answer you consume immediately,
|
|
42
|
+
and `implement`'s output IS the working tree.
|
|
43
|
+
|
|
44
|
+
**The CLI catches fake successes, not bad work.** Backends fail in ways that
|
|
45
|
+
look like success: an agy model with no quota returns an empty answer and exit 0;
|
|
46
|
+
codex sometimes draws a tiny image in code instead of rendering one. So a
|
|
47
|
+
too-small render is rejected as fake, an empty answer is an error, and an
|
|
48
|
+
`implement` that changed nothing exits 1. A review with no report file fails as
|
|
49
|
+
well, because a verdict without evidence is not a review. None of that judges
|
|
50
|
+
the work itself, which is why you still re-run the real gates on a diff.
|
|
51
|
+
|
|
52
|
+
**Chroma keying is a fallback, not a feature.** Only codex renders true alpha.
|
|
53
|
+
On the JPEG seats aibridge asks for a flat backdrop and removes it locally,
|
|
54
|
+
which gives binary edges and eats any subject the same colour as the backdrop.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aibridge/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "CLI
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "Agent instructions and CLI for delegating work to authenticated AI CLIs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"engines": {
|
|
@@ -31,16 +31,17 @@
|
|
|
31
31
|
},
|
|
32
32
|
"files": [
|
|
33
33
|
"dist",
|
|
34
|
+
"instructions",
|
|
34
35
|
"src"
|
|
35
36
|
],
|
|
36
37
|
"dependencies": {
|
|
37
38
|
"@stricli/core": "1.3.0",
|
|
38
39
|
"sharp": "^0.35.3",
|
|
39
|
-
"@aibridge/
|
|
40
|
-
"@aibridge/
|
|
41
|
-
"@aibridge/driver-codex": "0.
|
|
42
|
-
"@aibridge/driver-claude": "0.
|
|
43
|
-
"@aibridge/driver-grok": "0.
|
|
40
|
+
"@aibridge/driver-agy": "0.9.0",
|
|
41
|
+
"@aibridge/proc": "0.9.0",
|
|
42
|
+
"@aibridge/driver-codex": "0.9.0",
|
|
43
|
+
"@aibridge/driver-claude": "0.9.0",
|
|
44
|
+
"@aibridge/driver-grok": "0.9.0"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"tsdown": "0.22.14"
|
|
@@ -84,7 +84,16 @@ describe('stricli exit-code lock & routing', () => {
|
|
|
84
84
|
const ctx = fakeCtx();
|
|
85
85
|
await runCli(ctx, ['--help']);
|
|
86
86
|
const output = ctx._stdout.join('');
|
|
87
|
-
for (const cmd of [
|
|
87
|
+
for (const cmd of [
|
|
88
|
+
'plan',
|
|
89
|
+
'implement',
|
|
90
|
+
'review',
|
|
91
|
+
'subagent',
|
|
92
|
+
'image-gen',
|
|
93
|
+
'runs',
|
|
94
|
+
'quota',
|
|
95
|
+
'skill',
|
|
96
|
+
]) {
|
|
88
97
|
expect(output).toContain(cmd);
|
|
89
98
|
}
|
|
90
99
|
});
|
package/src/app.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';
|
|
2
1
|
import { buildApplication, buildRouteMap, run } from '@stricli/core';
|
|
3
2
|
import { imageGen } from './commands/image-gen/command.ts';
|
|
4
3
|
import { implement } from './commands/implement/command.ts';
|
|
@@ -7,12 +6,11 @@ import { plan } from './commands/plan/command.ts';
|
|
|
7
6
|
import { quota } from './commands/quota/command.ts';
|
|
8
7
|
import { review } from './commands/review/command.ts';
|
|
9
8
|
import { runs } from './commands/runs/command.ts';
|
|
9
|
+
import { skill } from './commands/skill/command.ts';
|
|
10
10
|
import { subagent } from './commands/subagent/command.ts';
|
|
11
11
|
import type { LocalContext } from './context.ts';
|
|
12
12
|
import { normalizeExitCode } from './exitCode.ts';
|
|
13
|
-
|
|
14
|
-
const require = createRequire(import.meta.url);
|
|
15
|
-
const { version } = require('../package.json') as { version: string };
|
|
13
|
+
import { PACKAGE_VERSION } from './package.ts';
|
|
16
14
|
|
|
17
15
|
const BRIEF =
|
|
18
16
|
'Bridge tasks to the other AI CLIs on this machine — a plan → implement → review workflow, task delegation, and image generation (codex / agy / grok seats).';
|
|
@@ -27,6 +25,7 @@ const routes = buildRouteMap({
|
|
|
27
25
|
runs,
|
|
28
26
|
quota,
|
|
29
27
|
models,
|
|
28
|
+
skill,
|
|
30
29
|
},
|
|
31
30
|
docs: {
|
|
32
31
|
brief: BRIEF,
|
|
@@ -36,7 +35,7 @@ const routes = buildRouteMap({
|
|
|
36
35
|
export const app = buildApplication(routes, {
|
|
37
36
|
name: 'aibridge',
|
|
38
37
|
versionInfo: {
|
|
39
|
-
currentVersion:
|
|
38
|
+
currentVersion: PACKAGE_VERSION,
|
|
40
39
|
},
|
|
41
40
|
scanner: {
|
|
42
41
|
// Accept --no-preflight / --no-tools while flag keys stay camelCase in TS
|
|
@@ -51,6 +51,11 @@ export const imageGen = buildCommand({
|
|
|
51
51
|
optional: true,
|
|
52
52
|
brief: 'Max seconds to wait for the render (default: 600)',
|
|
53
53
|
},
|
|
54
|
+
preflight: {
|
|
55
|
+
kind: 'boolean',
|
|
56
|
+
default: true,
|
|
57
|
+
brief: 'Check model quota before rendering (use --no-preflight to skip)',
|
|
58
|
+
},
|
|
54
59
|
json: {
|
|
55
60
|
kind: 'boolean',
|
|
56
61
|
withNegated: false,
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
resolveModel,
|
|
26
26
|
supportsImageGen,
|
|
27
27
|
} from '../../models.ts';
|
|
28
|
+
import { preflightModel, renderPreflightRefusal } from '../../quotaPreflight.ts';
|
|
28
29
|
import {
|
|
29
30
|
CHROMA_CLAUSE,
|
|
30
31
|
chromaKeyToPng,
|
|
@@ -38,6 +39,7 @@ export interface ImageGenFlags {
|
|
|
38
39
|
readonly aspectRatio?: string;
|
|
39
40
|
readonly image?: string;
|
|
40
41
|
readonly timeout?: number;
|
|
42
|
+
readonly preflight: boolean;
|
|
41
43
|
readonly json: boolean;
|
|
42
44
|
readonly transparent: boolean;
|
|
43
45
|
}
|
|
@@ -122,6 +124,18 @@ export default async function imageGen(
|
|
|
122
124
|
return fail(formatImageGenModelError(inputSlug, model));
|
|
123
125
|
}
|
|
124
126
|
|
|
127
|
+
// Last gate before a paid render. Every check above is local and must stay
|
|
128
|
+
// above it, so a bad --out or aspect ratio still fails without a network call.
|
|
129
|
+
if (flags.preflight) {
|
|
130
|
+
const verdict = await preflightModel(model);
|
|
131
|
+
if (!verdict.ok) {
|
|
132
|
+
this.process.stderr.write(`${renderPreflightRefusal('image-gen', verdict)}\n`);
|
|
133
|
+
this.process.exitCode = 3;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (verdict.warning) this.process.stderr.write(`aibridge image-gen: ${verdict.warning}\n`);
|
|
137
|
+
}
|
|
138
|
+
|
|
125
139
|
const minBytes = model.spec.backend === 'codex' ? MIN_REAL_BYTES_CODEX : MIN_REAL_BYTES_TOOL;
|
|
126
140
|
const work = mkdtempSync(join(tmpdir(), 'aibridge-imagegen-'));
|
|
127
141
|
|
|
@@ -29,7 +29,7 @@ export const review = buildCommand({
|
|
|
29
29
|
kind: 'parsed',
|
|
30
30
|
parse: String,
|
|
31
31
|
optional: true,
|
|
32
|
-
brief: 'Base git ref to diff against (default: HEAD)',
|
|
32
|
+
brief: 'Base git ref or range to diff against, e.g. HEAD~3 or main (default: HEAD)',
|
|
33
33
|
},
|
|
34
34
|
out: {
|
|
35
35
|
kind: 'parsed',
|
|
@@ -50,7 +50,7 @@ export const review = buildCommand({
|
|
|
50
50
|
},
|
|
51
51
|
},
|
|
52
52
|
docs: {
|
|
53
|
-
brief: 'Review working tree
|
|
53
|
+
brief: 'Review a diff (working tree, or any commit range via --base) or a plan contract',
|
|
54
54
|
fullDescription,
|
|
55
55
|
},
|
|
56
56
|
});
|
|
@@ -145,7 +145,7 @@ export default async function review(this: LocalContext, flags: ReviewFlags): Pr
|
|
|
145
145
|
let reviewPrompt: string;
|
|
146
146
|
if (isDirty) {
|
|
147
147
|
reviewPrompt =
|
|
148
|
-
`You are an expert code reviewer. Inspect the
|
|
148
|
+
`You are an expert code reviewer. Inspect the diff produced by \`git diff ${baseRef}\` (this covers committed and uncommitted changes) plus any untracked files at ${cwd}.\n` +
|
|
149
149
|
(absPlanPath
|
|
150
150
|
? `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`
|
|
151
151
|
: '') +
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { buildCommand } from '@stricli/core';
|
|
2
|
+
import type { LocalContext } from '../../context.ts';
|
|
3
|
+
import skillImpl from './impl.ts';
|
|
4
|
+
|
|
5
|
+
function skillCommand(this: LocalContext, _flags: Record<never, never>, topic?: string): void {
|
|
6
|
+
skillImpl.call(this, topic);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const skill = buildCommand({
|
|
10
|
+
func: skillCommand,
|
|
11
|
+
parameters: {
|
|
12
|
+
flags: {},
|
|
13
|
+
positional: {
|
|
14
|
+
kind: 'tuple',
|
|
15
|
+
parameters: [
|
|
16
|
+
{
|
|
17
|
+
brief: 'Command-specific instructions to append to the router',
|
|
18
|
+
parse: String,
|
|
19
|
+
placeholder: 'topic',
|
|
20
|
+
optional: true,
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
docs: {
|
|
26
|
+
brief: 'Print the canonical agent instructions bundled with this CLI',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { LocalContext } from '../../context.ts';
|
|
3
|
+
import { PACKAGE_VERSION } from '../../package.ts';
|
|
4
|
+
import skillImpl from './impl.ts';
|
|
5
|
+
|
|
6
|
+
function fakeCtx(): LocalContext & { stdout: string[]; stderr: string[] } {
|
|
7
|
+
const stdout: string[] = [];
|
|
8
|
+
const stderr: string[] = [];
|
|
9
|
+
return {
|
|
10
|
+
process: {
|
|
11
|
+
stdout: {
|
|
12
|
+
write: (value: string) => {
|
|
13
|
+
stdout.push(value);
|
|
14
|
+
return true;
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
stderr: {
|
|
18
|
+
write: (value: string) => {
|
|
19
|
+
stderr.push(value);
|
|
20
|
+
return true;
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
exitCode: undefined,
|
|
24
|
+
} as unknown as NodeJS.Process,
|
|
25
|
+
stdout,
|
|
26
|
+
stderr,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('skill command', () => {
|
|
31
|
+
it('prints the router with an exact-version runner', () => {
|
|
32
|
+
const ctx = fakeCtx();
|
|
33
|
+
skillImpl.call(ctx);
|
|
34
|
+
const output = ctx.stdout.join('');
|
|
35
|
+
|
|
36
|
+
expect(output).toContain(`npx -y @aibridge/cli@${PACKAGE_VERSION}`);
|
|
37
|
+
expect(output).toContain('# aibridge');
|
|
38
|
+
expect(output).not.toContain('# plan —');
|
|
39
|
+
expect(ctx.process.exitCode).toBeUndefined();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('appends command-specific instructions', () => {
|
|
43
|
+
const ctx = fakeCtx();
|
|
44
|
+
skillImpl.call(ctx, 'plan');
|
|
45
|
+
const output = ctx.stdout.join('');
|
|
46
|
+
|
|
47
|
+
expect(output).toContain('# aibridge');
|
|
48
|
+
expect(output).toContain('# plan — write a detailed implementation plan file');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('rejects an unknown topic', () => {
|
|
52
|
+
const ctx = fakeCtx();
|
|
53
|
+
skillImpl.call(ctx, 'nope');
|
|
54
|
+
|
|
55
|
+
expect(ctx.process.exitCode).toBe(2);
|
|
56
|
+
expect(ctx.stderr.join('')).toContain('unknown topic "nope"');
|
|
57
|
+
expect(ctx.stdout).toEqual([]);
|
|
58
|
+
});
|
|
59
|
+
});
|