You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
README now leads with how the pipeline works (extract/filter, cost merge, artifacts, V1+V2 injection, fallbacks) and the shipped plugin config file. Plans and task notes are removed; docs/specs describe what ships, including the new catalog-freshness spec. AGENTS.md gains the docs rule and the validation/runtime-weight boundaries.
Copy file name to clipboardExpand all lines: AGENTS.md
+12Lines changed: 12 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -19,6 +19,10 @@ On 2026-09-02 this repo published **42 accidental npm versions** (0.7.5→0.7.46
19
19
- The intended release path for catalog updates is the automation's `fix(catalog): sync command-code@X` commit (patch). Do not rename it to `chore(catalog)`.
20
20
-`package.json` / `manifest.json` version fields are written by automation only; don't bump them in feature PRs.
21
21
22
+
## Docs rule — shipped behavior changes touch docs in the same branch
23
+
24
+
Any branch that changes shipped behavior — `plugin.ts`, `index.ts`, `src/**`, or the sync pipeline in `scripts/sync-models.ts` — must also touch `README.md` or the matching spec under `docs/specs/` in the same branch. Plans and task notes are not committed; specs describe what ships. Verify with `git diff --name-only main...HEAD`: if it lists a behavior path, it must also list `README.md` or a `docs/specs/` file.
25
+
22
26
## Catalog extraction (`src/catalog.ts`) — fragile by design
23
27
24
28
- It slices balanced `{…}` spans around the anchor `SONNET_4_6:{id:"claude-sonnet-4-6"` and evals them with string bindings collected from the 12k chars before the anchor (`extractStringBindings`).
@@ -33,3 +37,11 @@ On 2026-09-02 this repo published **42 accidental npm versions** (0.7.5→0.7.46
33
37
-`main` is protected (5 required checks). Never push to `main` — open a PR and let auto-merge handle it.
34
38
-`workflow_dispatch` always runs a workflow from **`main`**, never a PR head. Dispatching does not test your branch.
35
39
- Secrets: `NPMJS` (npm **Automation** token, mapped to `NPM_TOKEN`/`NODE_AUTH_TOKEN` — a login token fails with `EOTP`), `RELEASE_SYNC_TOKEN` (PAT; PRs opened with `GITHUB_TOKEN` do not trigger CI runs on their branch).
-`src/schemas.ts` is the single source of truth for `ModelEntry` / `CatalogManifest` shapes. `src/catalog.ts` and `src/manifest.ts` derive them via `z.infer` — never redeclare the shape. Verify: `grep -rn "interface ModelEntry\|type CatalogManifest = {" src` must be empty.
44
+
- zod lives only at validation boundaries (bundled `models.json` / `manifest.json` reads in `plugin.ts`, provider availability payloads, user config files). Hot paths (`src/convert.ts`, `src/stream.ts`, `src/model.ts`, the `generate*` model loops) stay zod-free. Verify: `grep -rn 'from "zod"' src/convert.ts src/stream.ts src/model.ts` must be empty.
45
+
-`dist/plugin.js` budget: ~708KB with zod bundled (was ~45KB type-only). A second runtime dependency requires either `--external` in `scripts/build-plugin.ts` or updating this budget line. Verify: `bun run build && du -h dist/plugin.js`.
46
+
- V1 (`generateOpencodeModels` in `src/catalog.ts`) and V2 (`toV2Model` in `src/v2models.ts`) cost/limit/modality mappings must stay in parity — change one, update the other plus `tests/unit/v2models.test.ts`. UI/map keys use `toConfigKey` in `src/catalog.ts` (do not duplicate it); the Command Code wire id is always catalog `entry.id` (V1 model `id`, V2 `modelID`) — bare short names 400 as unsupported_model.
47
+
- Entry points: `plugin.ts` owns all config-hook logic; `index.ts` re-exports + SDK factory; `src/entry.ts` is bundle glue for `scripts/build-plugin.ts` only. Do not add a fourth entry or duplicate the catalog-load path.
Copy file name to clipboardExpand all lines: README.md
+39-8Lines changed: 39 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -17,12 +17,23 @@ This package is based on **[FanFan4204/opencode-commandcode-provider](https://gi
17
17
### What this package adds
18
18
19
19
- Bundled `models.json` is the default runtime catalog (no local CLI scrape).
20
-
- CLI cost extraction can fail (as on `command-code@1.38.x`) without dropping models.
21
-
- Official docs fill missing costs; remaining paid gaps use [models.dev](https://models.dev) as a reference. Command Code free SKUs stay `$0`.
20
+
- Dual OpenCode entry: V2 `setup` injects the provider plus models via transforms; V1 `server` fills `provider.commandcode` defaults plus models and registers API-key auth (auth stays V1-only).
21
+
- Validation at the boundaries via `src/schemas.ts` (zod): bundled `models.json` / `manifest.json` reads, provider availability payloads, and the plugin config file.
22
+
- CLI cost extraction can fail without dropping models; official docs fill missing costs, remaining paid gaps use [models.dev](https://models.dev) as a reference price at sync time. Command Code free SKUs stay `$0`.
22
23
- Vision vs text-only comes from the Command Code CLI catalog (`inputModalities` on every SKU). [models.dev](https://models.dev) only adds extra inputs (video/audio/pdf) when it matches.
23
24
- Reasoning effort **variants** on models that declare `reasoningEfforts`.
24
25
- Quiet OpenCode startup (diagnostics go to `startup.json`, not stdout).
25
26
27
+
## How it works
28
+
29
+
Each CI sync extracts the model catalog from the latest `command-code` npm bundle, merges costs, and commits versioned artifacts; at startup the plugin loads those artifacts and registers them with OpenCode — never the other way around.
30
+
31
+
-**Extract + filter** — model entries (ids, names, reasoning, `inputModalities`, limits) are evaluated out of the minified CLI bundle (`src/catalog.ts`), then intersected with the callable IDs reported by the provider API.
32
+
-**Costs merge** — per model, first hit wins: CLI bundle costs → official Command Code docs → free SKUs (`$0`) → [models.dev](https://models.dev) reference prices → unmatched placeholder. Anything still unmatched marks the catalog `degraded`. This runs at sync time only; runtime never fetches prices.
-**V1 injection + V2 transform** — V1 `server()` fills `provider.commandcode` defaults and the models map; V2 `setup()` adds/updates the provider inventory (baseURL, API key binding) and sets models through `ctx.provider.transform`.
35
+
-**Degraded/cache fallbacks** — a `degraded`/`broken` manifest sets the degraded flag with a reason; an unreadable bundled `models.json` falls back to the last-good cache; auth/connect still registers even with an empty catalog.
36
+
26
37
## Quick Start
27
38
28
39
### 1. Install the plugin
@@ -33,7 +44,7 @@ This package is based on **[FanFan4204/opencode-commandcode-provider](https://gi
33
44
}
34
45
```
35
46
36
-
Pin a version instead of `@latest` if you do not want automatic catalog patches.
47
+
Pin a version instead of `@latest` if you do not want automatic catalog patches. The config key stays `plugin` in OpenCode V2 — there is no `plugins` key.
37
48
38
49
`file://` checkouts are **not** updated by npm; `git pull` after CI commits, or switch to the npm plugin line.
39
50
@@ -43,14 +54,26 @@ On OpenCode V2 the plugin registers the `commandcode` provider itself (Provider
43
54
44
55
### 3. Connect
45
56
46
-
Run `/connect` in opencode, search for **Command Code**, and enter your API key, or set `COMMANDCODE_API_KEY`.
57
+
Set `COMMANDCODE_API_KEY`, or on OpenCode V1 run `/connect`, search for **Command Code**, and enter your API key. V2 registers the provider with the env binding; the `/connect` API-key method is V1-only.
47
58
48
59
### 4. Select a model
49
60
50
61
```
51
62
/models
52
63
```
53
64
65
+
Catalog patches arrive as plugin updates: `@latest` refreshes in the background and takes effect on the next OpenCode restart. If a new model is missing after an announced sync, restart opencode once.
66
+
67
+
## Plugin config file
68
+
69
+
Optional, `~/.config/opencode/opencode-commandcode.json` (legacy fallback name `commandcode-go-opencode-provider.json` still loads). Unknown keys are ignored; every field has a default.
70
+
71
+
| Key | Default | Effect |
72
+
|---|---|---|
73
+
|`commandCodePackagePath`|`""`| Maintainer override: extract the catalog from a local `command-code` checkout instead of the bundle. Same as env `COMMANDCODE_PACKAGE_PATH`. |
74
+
|`debugStartupLogs`|`false`| Also mirror the startup summary to stderr. Default is quiet (`startup.json` only). |
75
+
|`disableModelSync`|`false`| Accepted for forward compatibility; currently has no effect. |
76
+
54
77
## Optional local CLI override
55
78
56
79
Maintainers only. OpenCode will scrape a local `command-code` install when `COMMANDCODE_PACKAGE_PATH` or `commandCodePackagePath` in `~/.config/opencode/opencode-commandcode.json` is set.
@@ -61,16 +84,24 @@ Maintainers only. OpenCode will scrape a local `command-code` install when `COMM
bun run check # oxlint + oxfmt + bun test + tsc (same stack as workit)
87
+
bun run check # oxlint + oxfmt --check + bun test tests/unit/ + tsc (the release gate)
65
88
```
66
89
67
90
```bash
68
-
bun run sync -- --remote # refresh models.json + manifest.json from command-code@latest
91
+
bun run sync -- --remote # refresh models.json + manifest.json + _version.txt from command-code@latest
92
+
bun run build # bundle src/entry.ts to dist/plugin.js
93
+
bun test tests/unit/ # unit suite (also via bun run test)
94
+
bun run test:integration # live-endpoint tests, not part of the gate
95
+
bun run verify:release-candidate # dry-run pack; manifest and package versions must match
96
+
bun run generate-readme # reports catalog counts only; README is hand-edited
97
+
bun run catalog:ci # entry used by the catalog-sync workflow
69
98
```
70
99
71
-
CI (`.github/workflows/catalog-sync.yml`) opens a PR every 6 hours when Command Code ships a new catalog. That PR, and the post-release `chore/manifest-sync-v*` PR, auto-merge after **check (test)**, **check (typecheck)**, **check (lint)**, **check (format)**, and **check (pack)** are green. `.github/workflows/release.yml` then runs **semantic-release** (npm publish + GitHub Release + tag). Do not push to `main`.
100
+
Entry points: `plugin.ts` owns all config-hook logic (dual default `{ id, setup }` plus `server`); `index.ts` re-exports the plugin plus the `createCommandCode` SDK factory; `src/entry.ts` is bundle glue for `scripts/build-plugin.ts` only — it produces `dist/plugin.js`.
101
+
102
+
CI (`.github/workflows/catalog-sync.yml`) opens a `fix(catalog)` PR every 6 hours when Command Code ships a new catalog; if extraction fails it opens a `catalog-break` issue instead. The PR auto-merges after **check (test)**, **check (typecheck)**, **check (lint)**, **check (format)**, and **check (pack)** are green. `.github/workflows/release.yml` then runs **semantic-release** (npm publish + GitHub Release + tag). Do not push to `main`.
72
103
73
-
The GitHub Actions secret name is `NPMJS` (same as workit). It is mapped to both `NPM_TOKEN` and `NODE_AUTH_TOKEN`. Use an npm **Automation** token (bypasses 2FA). A login token from `~/.npmrc` fails CI with `EOTP`. Catalog PRs get a real CI run when `RELEASE_SYNC_TOKEN` (or `CATALOG_PUSH_TOKEN`) is a PAT; `GITHUB_TOKEN` can open the PR but GitHub will not start workflows from that event.
104
+
The GitHub Actions secret name is `NPMJS`. It is mapped to both `NPM_TOKEN` and `NODE_AUTH_TOKEN`. Use an npm **Automation** token (bypasses 2FA). A login token from `~/.npmrc` fails CI with `EOTP`. Catalog PRs get a real CI run when `RELEASE_SYNC_TOKEN` is a PAT; `GITHUB_TOKEN` can open the PR but GitHub will not start workflows from that event.
0 commit comments