|
| 1 | +# @razroo/iso-route |
| 2 | + |
| 3 | +**One model policy, every harness.** |
| 4 | + |
| 5 | +`agentmd`, `isolint`, and `iso-harness` get your *prompts* to every coding |
| 6 | +agent. `@razroo/iso-route` does the same thing for your *model choices*: |
| 7 | +you declare a default model plus named roles once, and iso-route compiles |
| 8 | +that policy into the config file each harness actually reads — |
| 9 | +`.claude/settings.json`, `.codex/config.toml`, `opencode.json` — plus a |
| 10 | +README note for Cursor (which has no file-based model binding). |
| 11 | + |
| 12 | +Use it to swap Opus for Sonnet everywhere with a single edit, pin a |
| 13 | +cheaper model to a `fast-edit` role, or send a `reviewer` role to a |
| 14 | +different provider entirely. |
| 15 | + |
| 16 | +> **v0.1 scope:** emits config files for Claude Code, Codex, and OpenCode, |
| 17 | +> and a resolved role map (`iso-route.resolved.json`) that `iso-harness` |
| 18 | +> consumes when it stamps per-subagent frontmatter. Fallback chains are |
| 19 | +> recorded in the resolved map but *not* encoded into any harness config |
| 20 | +> — runtime routing lives in proxy layers (OpenRouter, LiteLLM), not |
| 21 | +> iso-route. |
| 22 | +
|
| 23 | +## Install |
| 24 | + |
| 25 | +```bash |
| 26 | +npm install -D @razroo/iso-route |
| 27 | +``` |
| 28 | + |
| 29 | +## Policy shape |
| 30 | + |
| 31 | +```yaml |
| 32 | +# models.yaml |
| 33 | +default: |
| 34 | + provider: anthropic |
| 35 | + model: claude-sonnet-4-6 |
| 36 | + |
| 37 | +roles: |
| 38 | + planner: |
| 39 | + provider: anthropic |
| 40 | + model: claude-opus-4-7 |
| 41 | + reasoning: high |
| 42 | + |
| 43 | + fast-edit: |
| 44 | + provider: anthropic |
| 45 | + model: claude-haiku-4-5 |
| 46 | + |
| 47 | + reviewer: |
| 48 | + provider: openai |
| 49 | + model: gpt-5 |
| 50 | + fallback: |
| 51 | + - { provider: anthropic, model: claude-sonnet-4-6 } |
| 52 | +``` |
| 53 | +
|
| 54 | +Valid providers: `anthropic`, `openai`, `google`, `xai`, `deepseek`, |
| 55 | +`mistral`, `groq`, `ollama`, `openrouter`, `local`. |
| 56 | +Valid `reasoning` levels: `low`, `medium`, `high`. |
| 57 | + |
| 58 | +## Fan-out mapping |
| 59 | + |
| 60 | +| Field | Claude Code | Codex | OpenCode | Cursor | |
| 61 | +| ------------------------- | ------------------------------------ | ---------------------------------------------------- | -------------------------------------- | -------------------------------- | |
| 62 | +| `default.model` | `.claude/settings.json` `model` | `.codex/config.toml` `model` | `opencode.json` top-level `model` | README note only | |
| 63 | +| `roles.<name>.model` | resolved map (iso-harness stamps) | `[profiles.<name>]` in `config.toml` | `agent.<name>.model` in `opencode.json`| advisory row in README note | |
| 64 | +| `reasoning` | closest model tier | `model_reasoning_effort` | provider-specific | advisory | |
| 65 | +| `fallback[]` | resolved map only (runtime unsupported) | resolved map only | resolved map only | resolved map only | |
| 66 | +| provider auth | env var convention | `[model_providers.<name>]` block | `provider` block with `npm` package | — | |
| 67 | + |
| 68 | +Cursor has no programmatic way to bind a model to a rule or chat, so |
| 69 | +iso-route emits a README note at `.cursor/iso-route.md` and warns at build |
| 70 | +time. Everything else gets a real config file. |
| 71 | + |
| 72 | +## CLI |
| 73 | + |
| 74 | +```bash |
| 75 | +iso-route build models.yaml --out . |
| 76 | +iso-route build models.yaml --targets claude,codex --dry-run |
| 77 | +iso-route plan models.yaml |
| 78 | +``` |
| 79 | + |
| 80 | +`build` writes per-harness files under `--out` (defaults to `.`). Add |
| 81 | +`--dry-run` to preview without touching disk. `plan` prints the resolved |
| 82 | +role table so you can eyeball what each harness will see. |
| 83 | + |
| 84 | +## Library API |
| 85 | + |
| 86 | +```ts |
| 87 | +import { build, loadPolicy } from "@razroo/iso-route"; |
| 88 | +
|
| 89 | +const result = build({ source: "./models.yaml", out: "./.out", dryRun: true }); |
| 90 | +for (const w of result.warnings) console.warn(w); |
| 91 | +``` |
| 92 | + |
| 93 | +Individual emitters are exported too (`emitClaude`, `emitCodex`, |
| 94 | +`emitOpenCode`, `emitCursor`) if you only need one target. |
| 95 | + |
| 96 | +## How this fits the rest of the pipeline |
| 97 | + |
| 98 | +``` |
| 99 | +agent.md → agentmd lint → agentmd render → isolint lint → iso-harness build |
| 100 | + + |
| 101 | + models.yaml → iso-route build ┘ |
| 102 | + │ |
| 103 | + ▼ |
| 104 | + project with CLAUDE.md, settings.json, |
| 105 | + config.toml, opencode.json, … |
| 106 | +``` |
| 107 | +
|
| 108 | +`iso-harness` owns *what the agent reads*. `iso-route` owns *which model |
| 109 | +reads it*. They share one output directory and are designed to be run |
| 110 | +back-to-back — the `@razroo/iso` wrapper will compose them for you. |
| 111 | +
|
| 112 | +## What iso-route is NOT |
| 113 | +
|
| 114 | +- **Not a request-level router.** Picking a cheaper model per-request |
| 115 | + based on prompt complexity belongs in a proxy (OpenRouter, LiteLLM, |
| 116 | + Portkey, Not Diamond). iso-route is a build-time transpiler, not an |
| 117 | + inference-path component. |
| 118 | +- **Not a model catalog.** It validates provider names, not model IDs. |
| 119 | + If you type a model name your provider doesn't recognize, you'll find |
| 120 | + out at runtime. A catalog package may land in v0.2. |
| 121 | +
|
| 122 | +## License |
| 123 | +
|
| 124 | +MIT — see [LICENSE](./LICENSE). |
0 commit comments