Skip to content

Commit d572eb2

Browse files
committed
docs: rewrite README flow and prune docs to specs
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.
1 parent a87bd6c commit d572eb2

21 files changed

Lines changed: 369 additions & 2076 deletions

File tree

‎AGENTS.md‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ On 2026-09-02 this repo published **42 accidental npm versions** (0.7.5→0.7.46
1919
- 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)`.
2020
- `package.json` / `manifest.json` version fields are written by automation only; don't bump them in feature PRs.
2121

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+
2226
## Catalog extraction (`src/catalog.ts`) — fragile by design
2327

2428
- 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
3337
- `main` is protected (5 required checks). Never push to `main` — open a PR and let auto-merge handle it.
3438
- `workflow_dispatch` always runs a workflow from **`main`**, never a PR head. Dispatching does not test your branch.
3539
- 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).
40+
41+
## Runtime weight + validation boundaries (2026-09-26: dist/plugin.js ~708KB after zod)
42+
43+
- `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.

‎README.md‎

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,23 @@ This package is based on **[FanFan4204/opencode-commandcode-provider](https://gi
1717
### What this package adds
1818

1919
- 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`.
2223
- 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.
2324
- Reasoning effort **variants** on models that declare `reasoningEfforts`.
2425
- Quiet OpenCode startup (diagnostics go to `startup.json`, not stdout).
2526

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.
33+
- **Artifacts** — `models.json` (the catalog), `_version.txt` (upstream version), `manifest.json` (counts, per-source cost stats, `healthy`/`degraded`/`broken` status).
34+
- **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+
2637
## Quick Start
2738

2839
### 1. Install the plugin
@@ -33,7 +44,7 @@ This package is based on **[FanFan4204/opencode-commandcode-provider](https://gi
3344
}
3445
```
3546

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.
3748

3849
`file://` checkouts are **not** updated by npm; `git pull` after CI commits, or switch to the npm plugin line.
3950

@@ -43,14 +54,26 @@ On OpenCode V2 the plugin registers the `commandcode` provider itself (Provider
4354

4455
### 3. Connect
4556

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.
4758

4859
### 4. Select a model
4960

5061
```
5162
/models
5263
```
5364

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+
5477
## Optional local CLI override
5578

5679
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
6184
git clone https://github.com/BrainerVirus/opencode-commandcode.git
6285
cd opencode-commandcode
6386
bun install
64-
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)
6588
```
6689

6790
```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
6998
```
7099

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`.
72103

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.
74105

75106
## License
76107

‎docs/2026-08-28-auto-merge-sync-prs/plan.md‎

Lines changed: 0 additions & 59 deletions
This file was deleted.

‎docs/2026-08-28-auto-merge-sync-prs/spec.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Auto-merge catalog and manifest sync PRs
22

3-
Status: approved (2026-08-28)
3+
Status: shipped (0.7.1, 2026-08-29)
44
**Branch:** `feature/2026-08-28-auto-merge-sync-prs`
55

66
## Goal

0 commit comments

Comments
 (0)