omp-llm-role 0.0.0-stage → 1.1.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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +506 -2
  3. package/agents/designer.md +76 -0
  4. package/docs/dev/README.md +91 -0
  5. package/docs/dev/agent-authoring.md +223 -0
  6. package/docs/dev/architecture.md +335 -0
  7. package/docs/dev/ci.md +152 -0
  8. package/docs/dev/current-state.md +694 -0
  9. package/docs/dev/data-sources.md +508 -0
  10. package/docs/dev/explorer.md +317 -0
  11. package/docs/dev/quirks.md +174 -0
  12. package/docs/dev/releasing.md +175 -0
  13. package/docs/dev/scoring.md +1027 -0
  14. package/docs/dev/testing.md +206 -0
  15. package/docs/dev/writes-and-state.md +301 -0
  16. package/docs/llm-role-rankings.md +199 -0
  17. package/package.json +1496 -4
  18. package/skills/omp-llm-role-create-agent/SKILL.md +174 -0
  19. package/src/agent-architect.ts +263 -0
  20. package/src/agent-create.ts +1493 -0
  21. package/src/agent-file.ts +119 -0
  22. package/src/agent-pins.ts +75 -0
  23. package/src/agent-remove.ts +180 -0
  24. package/src/architect-provenance.ts +16 -0
  25. package/src/availability.ts +320 -0
  26. package/src/benchmark-author.ts +87 -0
  27. package/src/benchmark-discovery.ts +84 -0
  28. package/src/benchmark-sources.ts +1023 -0
  29. package/src/cli/create-role.ts +190 -0
  30. package/src/cli/llm-role-rank.ts +356 -0
  31. package/src/config-edit.ts +583 -0
  32. package/src/engine.ts +1709 -0
  33. package/src/error-report.ts +562 -0
  34. package/src/explorer/boot.ts +147 -0
  35. package/src/explorer/explain.ts +426 -0
  36. package/src/explorer/scopes.ts +110 -0
  37. package/src/explorer/server.ts +318 -0
  38. package/src/extension.ts +891 -0
  39. package/src/features.ts +103 -0
  40. package/src/guards.ts +8 -0
  41. package/src/project-registry.ts +65 -0
  42. package/src/project-setup.ts +382 -0
  43. package/src/prompts/agent-creation-architect.md +37 -0
  44. package/src/prompts/agent-creation-user.md +6 -0
  45. package/src/prompts/benchmark-source-architect.md +36 -0
  46. package/src/prompts/benchmark-source-user.md +6 -0
  47. package/src/prompts/project-profile-architect.md +33 -0
  48. package/src/prompts/project-profile-user.md +8 -0
  49. package/src/role-archetypes.ts +258 -0
  50. package/src/role-settings.ts +209 -0
  51. package/src/settings.ts +743 -0
  52. package/src/state.ts +141 -0
  53. package/src/updater.ts +592 -0
  54. package/web/app.js +1239 -0
  55. package/web/index.html +42 -0
  56. package/web/style.css +287 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gott50
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,507 @@
1
- # Temporary Holding Version
1
+ # omp-llm-role
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Ranks today's LLM leaderboard into best-fit picks for each omp model role
4
+ (`default, smol, slow, vision, plan, commit, tiny, task, advisor`, plus the
5
+ opt-in `designer`) and ships an omp plugin that applies those picks to
6
+ `~/.omp/agent/config.yml` daily. Every role weights price and throughput, so
7
+ the pick is the cheapest model that can do the job, not the strongest one.
8
+
9
+ ## Example output
10
+
11
+ The report is a full worked example — per-role top-10 tables (value, q, $/M,
12
+ tok/s, ctx, weighted metric contributions, ★ Pareto marker) plus the suggested
13
+ `modelRoles` block. See
14
+ [docs/llm-role-rankings.md](docs/llm-role-rankings.md).
15
+
16
+ ## Install
17
+
18
+ Any one of three routes:
19
+
20
+ ```sh
21
+ omp plugin install omp-llm-role # npm
22
+ omp plugin install github:Gott50/omp-llm-role # git (public repo)
23
+ omp plugin marketplace add Gott50/omp-llm-role # marketplace
24
+ omp plugin install omp-llm-role@gott50-plugins
25
+ ```
26
+
27
+ - npm and git installs shell out to `bun install`, so **bun must be on PATH**;
28
+ marketplace installs need no package manager.
29
+ - No runtime dependencies.
30
+ - **Restart the session after install** so the extension module loads.
31
+ - Upgrades: `omp plugin upgrade omp-llm-role` (npm/git); for the marketplace,
32
+ `omp plugin marketplace update gott50-plugins` then
33
+ `omp plugin upgrade omp-llm-role@gott50-plugins`.
34
+ - Dev: `omp plugin link ~/Documents/omp-llm-role`.
35
+
36
+ ## Daily behaviour
37
+
38
+ - **UTC-day trigger.** The first omp session of each UTC day rewrites
39
+ `modelRoles` (and `retry.fallbackChains`) to that day's best key-eligible
40
+ models; later same-day sessions no-op. `/refresh-roles` forces a run anytime.
41
+ - **Awaited session-start run.** The session-start run is awaited before the
42
+ session's first prompt is dispatched, so the day's write lands even in a
43
+ short-lived session (`omp -p`, a subagent).
44
+ - **Session model activation.** When the day's run switches `default` and the
45
+ triggering session still has an empty conversation, the plugin also switches
46
+ the live session model to the new selector — the first prompt then runs on
47
+ the freshly ranked pick. Guards: main sessions only, empty conversation, the
48
+ session actually booted on the previous `default` selector (an explicit
49
+ `omp --model X` is never clobbered), and only on a real change. Disable with
50
+ `activateDefaultOnEmptySession=false`.
51
+ - **Surgical writes.** Only managed role lines, managed chain keys, and the
52
+ plugin-managed `task.disabledAgents` names change; comments, blank lines and
53
+ unknown keys stay byte-identical, and a no-change run writes nothing.
54
+ `~/.omp/agent/llm-role-state.json` snapshots the previous `modelRoles` block
55
+ on every write as a rollback aid.
56
+
57
+ ## Roles
58
+
59
+ | Role | Purpose |
60
+ |---|---|
61
+ | `default` | Main workhorse: strong general coding-agent quality, sane cost |
62
+ | `smol` | Fast lightweight model: cheap and quick, still competent |
63
+ | `slow` | Most capable model for hard problems; cost and speed as tiebreakers |
64
+ | `vision` | Image understanding: vision index dominates |
65
+ | `plan` | Planning: reasoning and long-context coherence, priced so the cheapest capable planner wins |
66
+ | `commit` | Commit messages: cheap and fast with decent general quality |
67
+ | `tiny` | Background tasks (titles, memory): cheapest and fastest wins |
68
+ | `task` | Subagents: agentic + tool calling, moderate cost sensitivity |
69
+ | `advisor` | Advisor/watchdog: deep reasoning over long context |
70
+ | `designer` | Design work: visual/UX judgement on image-capable models — **opt-in** |
71
+
72
+ `designer` is the only non-built-in shipped role and ships **disabled**: a stock
73
+ run ranks the nine built-in roles and fetches only their sources. Enable it with
74
+
75
+ ```sh
76
+ omp plugin config set omp-llm-role config '{"roles":{"designer":{"enabled":true}}}'
77
+ ```
78
+
79
+ The plugin then ranks it and fetches Design Arena. The shipped `designer` agent
80
+ is opt-in too: the plugin keeps it in `task.disabledAgents` until
81
+ `roles.designer.enabled=true`, so it is absent from the roster and refuses
82
+ spawns by default. Its `model: "@designer, @default"` chain only matters if a
83
+ user re-enables the agent by hand — with the role disabled the unresolved first
84
+ entry is skipped and the child runs on `@default`.
85
+
86
+ **Agent pins.** The plugin derives each agent's pinned role from the first
87
+ `@<role>` in its `model:` frontmatter, across the shipped `agents/` dir, the
88
+ user dir (`~/.omp/agent/agents/`) and the project dir (`<project>/.omp/agents/`).
89
+ Disabling a role disables the agents that pin it (they are kept in
90
+ `task.disabledAgents`); enabling re-enables them. This sync is **not**
91
+ day-gated — it runs on every session start, so enabling a role takes effect on
92
+ the next session. An agent pinning a role the plugin does not know is left
93
+ alone, and other `task.disabledAgents` entries and their order are preserved.
94
+
95
+ ## Settings
96
+
97
+ Settings live in `~/.omp/plugins/omp-plugins.lock.json` →
98
+ `settings["omp-llm-role"]`, deep-merged over the shipped defaults. The plugin
99
+ also exposes the whole shipped surface in omp's **`/settings` → Plugins →
100
+ *omp-llm-role*** tab (booleans as toggles, `thinking` as a picker, `required` as
101
+ a comma-separated string); an edit there takes effect on the next
102
+ `/refresh-roles` (or the next day-gated session start).
103
+
104
+ Global knobs:
105
+
106
+ | Knob | Default | Meaning |
107
+ |---|---|---|
108
+ | `switchMargin` | `0.02` | Value margin a challenger must beat the incumbent by before switching; `0` = always take the best |
109
+ | `priceSwitchFraction` | `0.5` | Inside the margin, adopt a challenger at least this fraction cheaper (0.5 = half the $/M); `0` disables |
110
+ | `writeFallbackChains` | `true` | Write per-role fallback chains into `config.yml` |
111
+ | `fallbackChainDepth` | `2` | Fallback models per role chain |
112
+ | `activateDefaultOnEmptySession` | `true` | Reapply the new `default` selector to an empty session's active model |
113
+ | `errorReporting` | `off` | Opt-in reporting of **unexpected** plugin errors as GitHub issues on `Gott50/omp-llm-role`; one of `off`, `ask`, `auto` (see below) |
114
+
115
+ Per-role knobs (`roles.<name>.*`):
116
+
117
+ | Knob | Default | Meaning |
118
+ |---|---|---|
119
+ | `enabled` | `true` (`designer`: `false`) | Opt a role in/out; `false` drops it from the resolved set and deletes its `modelRoles.<role>` line |
120
+ | `description` | shipped one-liner | Role description |
121
+ | `weights` | shipped set | Metric weights; must sum to 1.0 ± 0.01 |
122
+ | `required` | shipped set | Eligibility gate (metrics a model must have); independent of weights |
123
+ | `filters.image` | `false` (`vision`/`designer`: `true`) | Restrict the pool to image-capable models |
124
+ | `filters.tools` | `false` | Restrict the pool to models with a tool-capable route |
125
+ | `filters.minContextTokens` | `0` (off) | Drop routes whose endpoint context is below this (tokens) |
126
+ | `filters.minOutputTokens` | `0` (off) | Drop routes whose endpoint output ceiling is below this (tokens) |
127
+ | `filters.maxPriceUsdPerM` | `0` (off) | Drop a model whose thinking-adjusted blend exceeds this ($/M) |
128
+ | `thinking` | shipped level | Thinking level appended to the selector (`off`…`max`, `auto`) |
129
+ | `providerPin` | `""` (unset) | Pin the role's requests to one OpenRouter provider route (`@<slug>`, may be tiered like `deepinfra/fp8`). The role is then priced and gated by that route — its billed price and p50 throughput, not the `1/price²` blend — and a model with no matching route is ineligible |
130
+ | `cacheHitRate` | `0` (off) | Assumed cache-hit rate (0–1) for the role's input tokens; each route's effective input price becomes `h·cacheRead + (1−h)·input` before the 3:1 billed blend and the thinking factor. The `1/price²` weight basis stays the **listed** input price (the router's sort key), so a cheap cache on a lightly-weighted route cannot dominate; a route with no cache-read price keeps its full input price. `0`/absent = off (byte-identical ranking) |
131
+ | `lambda` | derived | Explicit λ ($ per quality point) override |
132
+ | `locked` | `false` | Leave the role alone: rank it but never rewrite its selector or chain (explorer toggle) |
133
+
134
+ **Capability flags** (`features.<id>` global, `roles.<role>.features.<id>` per
135
+ role). One named flag per opt-in capability; turning it on applies the plugin's
136
+ recommended settings for that capability to the role. Every individual knob
137
+ stays settable, and an explicit knob always beats the flag. **Every flag ships
138
+ OFF** — with no flags set, the resolved roles are byte-identical to the shipped
139
+ defaults.
140
+
141
+ | Flag | Recommended settings | What it buys |
142
+ |---|---|---|
143
+ | `endpointCeilings` | `filters.tools: true`, `filters.minOutputTokens: 16384` | Routes that cannot call tools and routes whose output ceiling is below 16 384 tokens leave the pool — a reasoning model is not cut off mid-thought |
144
+ | `cachePricing` | `cacheHitRate: 0.5` | The role is priced on a cache-heavy agent loop's invoice (the endpoint's `cacheReadPrice` blended in) rather than the sticker price |
145
+ | `providerPinning` | `preferOwnProvider: true` | A model is priced **and measured** on its own lab's route where one exists (DeepSeek's own endpoint, 94.5 % cache hit) — the route's billed price and its p50 throughput, so `q` moves too — falling back to the default blend otherwise |
146
+ | `costCap` | `filters.maxPriceUsdPerM: 10` | The priciest tail of the eligible pool leaves it, so the pick cannot be an expensive outlier |
147
+
148
+ Precedence is one rule:
149
+
150
+ ```
151
+ effective def = shipped default ⊕ preset(enabled flags) ⊕ explicit user keys
152
+ ```
153
+
154
+ A flag is on when `roles.<role>.features.<id>` is present (it wins either way)
155
+ and otherwise when the global `features.<id>` is `true` — so a per-role `false`
156
+ beats a global `true`. The preset fills only knobs the role does not already
157
+ set, and an explicit key is merged last, so it always wins. `providerPinning`'s
158
+ recommended setting is a **soft per-model policy** (`preferOwnProvider`), not a
159
+ hard `providerPin`: it re-prices a model on its own lab's route where one exists
160
+ (the route's billed price **and** its p50 throughput, so `q` moves as well as
161
+ `priceEff`) and never makes a model ineligible (the hard `providerPin` stays
162
+ available by hand). `minContextTokens` is deliberately **not** part of `endpointCeilings` —
163
+ the complaint it would answer is an endpoint advertising a *fraction of the
164
+ model's own window*, which an absolute token floor cannot express, so it stays a
165
+ hand-set knob.
166
+
167
+ The flags are settable from every role-authoring surface: `--feature <id,...>`
168
+ (comma-separated) on `node src/cli/create-role.ts`, `/create-agent` and
169
+ `/project-roles` (`--list-features` lists them on the first two), plus the
170
+ explorer's Features panel. An
171
+ unknown flag id or a non-boolean value aborts the run with the offending key and
172
+ no write.
173
+
174
+ **Error reporting (`errorReporting`).** This knob ships **`off`** — installing
175
+ or upgrading the plugin never starts uploading anything. When something
176
+ *genuinely unexpected* happens (a `/refresh-roles` or authoring command throws,
177
+ or the updater hits an internal error), the plugin can file one structured
178
+ report — the plugin version, runtime, platform, the command that failed, the
179
+ error name/message/stack and a timestamp — as a GitHub issue on
180
+ `Gott50/omp-llm-role`. The three values:
181
+
182
+ - **`off`** — never files; behaviour is exactly today's (one warning line).
183
+ - **`ask`** — shows you the exact text and files it only if you confirm. In a
184
+ session with no UI (`omp -p`, RPC, a subagent) it never uploads: it notifies a
185
+ prefilled new-issue URL instead, so you can submit it yourself.
186
+ - **`auto`** — files without asking.
187
+
188
+ The payload is redacted before it leaves the machine (your home directory → `~`,
189
+ token-shaped strings → `<redacted>`) and carries **no** config contents, model
190
+ selectors, prompts or history rows. Filing goes through the GitHub API when a
191
+ token is available (`GITHUB_TOKEN` → `GH_TOKEN` → `gh auth token` — the token is
192
+ never stored in a settings file); a token that cannot write issues here, or no
193
+ token at all, falls through to a prefilled `…/issues/new` URL (opened in your
194
+ browser, or printed). A repeated crash comments on the existing open issue
195
+ instead of opening a new one, and the path is capped (one open issue per
196
+ fingerprint, one creation per fingerprint per UTC day, 3 per day) so a crash
197
+ loop cannot spam the tracker. Reporting is **best-effort**: a failure in it only
198
+ adds one notification line and never affects the run it reports on. Expected
199
+ environment conditions (no key budget, a concurrent refresh holding the lock,
200
+ write conflicts, unavailable ranking data, invalid settings) are **never**
201
+ reported. In project mode the project lock file's value applies.
202
+
203
+ **Validation.** A run aborts (notify, no write) on: a weight ≤ 0, or a role's
204
+ weights not summing to 1.0 ± 0.01; a `required` entry or a weighted metric
205
+ outside the known-metric set; a `thinking` level outside
206
+ `off|minimal|low|medium|high|xhigh|max|auto`; `switchMargin`/`priceSwitchFraction`
207
+ outside `[0, 1]`; a non-boolean `enabled`; a `cacheHitRate` outside `[0, 1]`; a
208
+ `providerPin` that is empty or
209
+ contains `@`/`:` (the selector's own delimiters); an unknown capability id
210
+ (global or per-role) or a non-boolean flag value; a non-boolean
211
+ `preferOwnProvider`. `weights: null` opts a role out;
212
+ the legacy `suffixes.*` keys are rejected with a migration hint (moved into
213
+ `roles.<role>.thinking`).
214
+
215
+ **Typed writes.** `omp plugin config set <plugin> <key> <value>` stores every
216
+ value as a **string**, which the validator rejects for numbers/arrays/booleans,
217
+ so it is only usable for string-valued keys. Write typed role defs with
218
+ `node src/cli/create-role.ts` or the explorer's **Export** (both validate →
219
+ merge → backup → atomic write); for a whole-object override use the `config` escape
220
+ hatch, `omp plugin config set omp-llm-role config '<json>'` (JSON-parsed by the
221
+ plugin). The lock file stores **flat dotted keys** (`roles.slow.weights.code`);
222
+ the reader also accepts nested objects, so an existing nested lock file keeps
223
+ working and normalizes on the next role write. Invalid settings abort the run
224
+ with the offending role/key and no write.
225
+
226
+ ## Commands
227
+
228
+ ```sh
229
+ # in-session (omp)
230
+ /refresh-roles # force a run now
231
+ /explore-roles [--port N] [--no-open] # interactive ranking UI
232
+ /create-agent <request> [options] # agent + role + wiring (free text or flags)
233
+ /create-agent --name <n> --purpose <text> [options] # agent + role + wiring
234
+ /remove-agent --name <n> [--scope user|project] [--lock PATH] [--yes] [--dry-run] # delete an agent and its role
235
+ /project-roles [--purpose <text>] [--roles <spec>] [--feature <id,...>] [--force] [--yes] [--dry-run] [--json] # project-scoped role set
236
+
237
+ # CLI
238
+ node src/cli/llm-role-rank.ts [--top N] [--json] [--out FILE] [--refresh] [--all] [--url URL]
239
+ node src/cli/create-role.ts --name <role> --weights m=w,... [--required m,...] [--thinking <level>] [--description <text>] [--image] [--lambda N] [--feature <id,...>] [--list-features] [--lock PATH] [--dry-run] [--json]
240
+ node --test tests/
241
+ ```
242
+
243
+ - `/refresh-roles` — force the daily pipeline now (same path as the session-start
244
+ run).
245
+ - `/explore-roles` — boot the explorer in-process (see below); `--port` picks a
246
+ preferred port (default 5177, a busy port falls back to a free one) and
247
+ `--no-open` skips the browser.
248
+ - `/create-agent` — create an agent **and** its role and wire them in one
249
+ command (see *Add a specialist agent*).
250
+ - `/remove-agent` — the inverse of `/create-agent`: deletes the agent `.md` and
251
+ its role, then runs the updater in-process so `modelRoles.<n>` is dropped;
252
+ refuses shipped default roles (see *Remove a specialist agent*).
253
+ - `/project-roles` — discover the project's usecase from its own artifacts and
254
+ apply a project-scoped role set to `<cwd>/.omp/config.yml` (see *Project
255
+ roles*).
256
+ - `src/cli/llm-role-rank.ts` — markdown report to stdout (per-role tables + suggested
257
+ `modelRoles`). `--top N` rows per role (default 10); `--json` machine payload;
258
+ `--out FILE` write instead of stdout; `--refresh` bypass both caches; `--all`
259
+ include opt-in roles (`designer`) — the default ranks the nine built-in roles
260
+ only; `--url` override the llm-stats page URL.
261
+ - `src/cli/create-role.ts` — add/update one role in the settings lock file (validated,
262
+ backup + atomic write); `--dry-run` validates without writing, `--json` prints
263
+ the payload.
264
+ - `node --test tests/` — the unit suite.
265
+
266
+ ## Add a specialist agent
267
+
268
+ One command does all of it. Describe the agent in plain language — the architect
269
+ names it, any benchmark you mention is folded into the weights, and any
270
+ benchmark link you point at is resolved:
271
+
272
+ ```sh
273
+ /create-agent i want an agent for writing. use the writing related Benchmarks in the Leaderboard https://llm-stats.com/leaderboards/best-ai-for-writing
274
+ ```
275
+
276
+ A link to any llm-stats benchmark page (`llm-stats.com/benchmarks/<id>`) resolves
277
+ to that benchmark; a link to a provider the plugin has never seen is authored
278
+ into a source declaration — the plugin fetches it, proposes a declaration, shows
279
+ the coverage and leader, and asks you to confirm before it is saved. A named
280
+ benchmark takes a decisive share of the weights, so it actually drives the
281
+ ranking. You do not have to name every benchmark: when you give a purpose, the
282
+ command also discovers the llm-stats catalog benchmarks relevant to it (a typed
283
+ judgment through your configured `judge` role) and folds them in — so a writing
284
+ agent is ranked on WritingBench, Creative Writing v3, COLLIE and the other
285
+ writing benchmarks, not just the one you named. Discovery is non-fatal and
286
+ skippable with `--no-discover`; an explicit `--benchmarks` list wins over it.
287
+
288
+ Discovery is **quality-gated**: each candidate is assessed on nine axes —
289
+ coverage (share of the field carrying the metric), dispersion (does it separate
290
+ models), provider composition, freshness, trust (the share of the payload's
291
+ entries that are self-reported, so a mostly vendor-submitted source is flagged),
292
+ modality (the catalog row's modality and the payload's multimodal share — an
293
+ annotation, never a gate), maintenance (the catalog row's dataset age against
294
+ a 12-month bar — an annotation, never a gate), provenance (the benchmark's
295
+ owner and the source's own per-entry org mix, cross-checked against the
296
+ composition axis — an annotation, never a gate) and cross-source (the zeroeval
297
+ per-entry price/context against the plugin's own, flagging a divergence past a
298
+ tolerance — an annotation, never a gate, and never a change to the `1/price²`
299
+ blend) — and a discovered benchmark that
300
+ fails a gating axis is dropped rather than folded in (a benchmark you name yourself is
301
+ never dropped, only warned). The focus set is capped at three benchmarks so one
302
+ keeps a decisive share. The report prints the nine signals and warns per below-bar
303
+ axis, and warns when the new role's top pick is the same as the `default` role's
304
+ (the role adds nothing). The agent file is written before the role, so a partial
305
+ failure never leaves a dangling role.
306
+
307
+ The flag form is equivalent and gives you the name explicitly:
308
+
309
+ ```sh
310
+ /create-agent --name review --purpose "review pull requests for correctness and security"
311
+ ```
312
+
313
+ It fits the weights to the purpose from the archetype table
314
+ (`--list-archetypes` prints the ten sets), authors
315
+ `~/.omp/agent/agents/review.md` with `model: "@review, @default"`, writes the
316
+ validated role into the settings lock file, and runs the updater in-process so
317
+ `modelRoles.review` lands in `config.yml` — nothing to remember. Options:
318
+ `--archetype <id>`, `--weights m=w,...`, `--required`, `--thinking`, `--tools`,
319
+ `--benchmarks m,...` (extra metrics to fold in; `--list-benchmarks` prints the
320
+ weightable set, including any external benchmark in use), `--feature <id,...>`
321
+ (comma-separated capability flags; `--list-features` prints the capability set),
322
+ `--scope user|project`,
323
+ `--body`/`--body-file`, `--force` (overwrite an existing agent file), `--yes`
324
+ (accept a proposed benchmark source without prompting), `--no-discover` (skip
325
+ catalog discovery), `--dry-run`, `--json`. In
326
+ the free-text form the flags still apply after the request, e.g.
327
+ `/create-agent i want an agent for writing --dry-run`. There is no
328
+ out-of-session CLI for this path — run `/create-agent` in an omp session.
329
+
330
+ By hand, the same three artifacts:
331
+
332
+ 1. Define the role with the plugin's validated write path:
333
+ ```sh
334
+ node ~/.omp/plugins/node_modules/omp-llm-role/src/cli/create-role.ts --name review \
335
+ --weights reasoning=0.30,general=0.24,code=0.20,agents=0.10,price=0.10,throughput=0.06 \
336
+ --required general,price,throughput --thinking high \
337
+ --description "Code review: agentic depth with cost awareness"
338
+ ```
339
+ 2. Author the agent that pins `model: "@review, @default"` (the routing), or pin
340
+ an existing agent through `task: { agentModelOverrides: { <agent>: "@review" } }`.
341
+ 3. Wire it: `/refresh-roles` in a session.
342
+
343
+ The shipped skill `omp-llm-role-create-agent` is the hand-driven equivalent, for
344
+ when the body needs real authoring rather than the archetype scaffold.
345
+
346
+ ## Remove a specialist agent
347
+
348
+ One command deletes an agent **and** its role:
349
+
350
+ ```sh
351
+ /remove-agent --name review
352
+ ```
353
+
354
+ It deletes the agent `.md` (user and/or project scope) and the role's lock-file
355
+ keys, then runs the updater in-process so `modelRoles.review` is dropped from
356
+ `config.yml` — nothing to remember. Options: `--scope user|project`,
357
+ `--lock PATH`, `--yes` (skip the in-session confirmation), `--dry-run`. Shipped
358
+ default roles (e.g. `designer`) cannot be deleted — the message points at the
359
+ explorer's "Reset to shipped default". A role created any way (the explorer's
360
+ "+ new role", `create-role.ts`, or `/create-agent`) is removable.
361
+
362
+ By hand, the same two artifacts:
363
+
364
+ 1. Delete the role from the settings lock file (the explorer's Export, or edit
365
+ the lock file directly).
366
+ 2. Delete the agent file: `rm ~/.omp/agent/agents/review.md`.
367
+ 3. Wire it: `/refresh-roles` in a session.
368
+
369
+ ## Project roles
370
+
371
+ `/project-roles` discovers a project's usecase from its own artifacts (README,
372
+ package.json, AGENTS.md, docs listing, git log, file tree) and applies a
373
+ **project-scoped** role set — the roles the project needs, with agents authored
374
+ for the new ones — to `<cwd>/.omp/config.yml`:
375
+
376
+ ```sh
377
+ /project-roles
378
+ /project-roles --purpose "a Python data pipeline repo" --dry-run
379
+ /project-roles --roles "data=build pipelines,-vision,default"
380
+ ```
381
+
382
+ It prints the plan (kept / dropped / added roles, each new role's archetype,
383
+ weights, focus benchmarks and top pick) before writing. `--purpose` overrides the
384
+ discovered usecase; `--roles` overrides the role set (`-name` drops a shipped
385
+ role, `name` keeps or adds one, `name=purpose` adds a new role); `--dry-run`
386
+ writes nothing; without `--force` an existing project role config is refused.
387
+
388
+ When a project carries its own `omp-llm-role` settings entry, the updater scopes
389
+ the whole run to `<cwd>/.omp` — the config, the state/history/lock files and the
390
+ day gate — so the project's `modelRoles` are refreshed independently of the
391
+ global `~/.omp/agent/config.yml`, which is never written in project mode. A
392
+ successful apply registers the project, so the explorer's **scope switcher**
393
+ lists it (see below).
394
+
395
+ ## Explorer
396
+
397
+ `/explore-roles` in omp boots a loopback-only web UI
398
+ (`http://127.0.0.1:5177`) that answers "why is model X at rank 7 for
399
+ `@slow`?" and "what happens if I care more about price than agents?" without
400
+ editing source and re-running the CLI.
401
+
402
+ - **Scope switcher** — the header lists every role-config source: the
403
+ **user-level** scope, plus every project where `/project-roles` was used (the
404
+ plugin records each project root in a small global registry, and the current
405
+ session's project is listed immediately). The explorer opens on the session's
406
+ project when the cwd has a project role config, else user-level. Switching
407
+ re-reads that scope's roles and universe from disk and points **Export** at
408
+ that scope's lock file — a project scope resolves as the project lock merged
409
+ over the user-level lock, exactly what the updater ranks. A project whose lock
410
+ file is gone is shown disabled. Switching with unsaved edits asks first.
411
+ - **Role tabs** — every role the plugin knows: the nine omp built-in roles, the
412
+ shipped opt-in `designer`, and any role present only in the lock file. Each
413
+ tab is tinted by provenance — `default` (omp built-in), `plugin` (shipped but
414
+ opt-in), `user` (lock-file only) — and a disabled role is muted and dashed.
415
+ Selecting a disabled role still ranks its models.
416
+ - **Rank table** — every eligible model for the selected role, with `value`,
417
+ `q`, `$/M` (effective, thinking-adjusted), `tok/s`, `ctx`, a `★` Pareto
418
+ marker, and a `Δ` column showing the rank delta against the role's effective
419
+ def (what the plugin does today).
420
+ - **Explain panel** — click a row for the full decomposition: each weighted
421
+ metric's raw value → cardinal transform → renormalized weight → contribution,
422
+ the cost block (`λ`, effective vs billed price, `penalty = λ·price`,
423
+ `value = q − penalty`), "why not higher" (the value gap to the model above
424
+ plus the per-metric target that would close it), and the models that dominate
425
+ it on (price, q).
426
+ - **Weight editor** — edit the role's description, weights, `required` set,
427
+ `filters.image`, `thinking` level, and `λ` override live; the table re-ranks
428
+ on every change. Weights are edited freely (no implicit rescaling): the `Σ`
429
+ readout turns red until `|Σ − 1| ≤ 0.01`, and `Normalize` rescales in one
430
+ click.
431
+ - **Enabled toggle** — opts a role in or out. Exporting writes `enabled` into
432
+ the lock file; the next updater run drops a disabled role from the resolved
433
+ set and deletes its `modelRoles.<role>` line, and re-enabling restores it.
434
+ - **Locked toggle** — tells the plugin to leave the role alone: it is still
435
+ ranked and shown, but the updater never rewrites its selector or fallback
436
+ chain and never removes it. Enable/disable still applies.
437
+ - **+ new role** — creates a role from a template
438
+ (`general/code/price/throughput`, Σ 1.0) that you then tune and Export.
439
+ - **Export** — writes the edited roles into the settings lock file atomically
440
+ and with a `.bak-<timestamp>` sibling, touching only the roles you edited. The
441
+ change takes effect on the next `/refresh-roles` **in any running session**
442
+ (the lock file is re-read from disk on every run). `Copy JSON` / `Download
443
+ JSON` emit the same payload for manual use.
444
+ - **Hover explanations** — every column header, metric name, role tab, and
445
+ control carries a tooltip explaining its semantics.
446
+
447
+ The UI never reimplements the ranking math: the numbers on screen are exactly
448
+ the numbers the plugin would use. The server binds `127.0.0.1` only (no auth)
449
+ and serves the SPA with no build step and no external requests.
450
+
451
+ ## Availability
452
+
453
+ The plugin keeps only models the OpenRouter key can actually run:
454
+
455
+ - **Tier/budget gate** — paid keys with budget get billed variants, free or
456
+ exhausted keys get `:free` variants, `:batch` never. Every candidate must
457
+ resolve in omp's catalog.
458
+ - **Keyed-catalog fast path** — when the account's OpenRouter setting **"Filter the
459
+ model catalog for API keys"** is on, the key-authenticated `GET /api/v1/models` is a
460
+ per-key allowlist: allowed candidates skip the probe and key-blocked candidates are
461
+ pruned from the walk. With the setting off (or the fetch failing) the plugin falls
462
+ back to the probe walk below — no abort.
463
+ - **Probe verification** — the fallback: every candidate is verified with a one-token
464
+ completion probe, because the account's OpenRouter **allowed-providers privacy
465
+ whitelist** is invisible to the key endpoint and the unauthenticated catalog, and a
466
+ real request's 404 is the only reliable signal. Blocked candidates are excluded from
467
+ selection and chains; a role with no clean candidate is left untouched.
468
+ - **Explorer key marking** — the explorer overlays every ranked model with a
469
+ **usable** / **key-blocked** / **unknown** badge derived from the
470
+ key-authenticated `GET /api/v1/models` (the account setting **"Filter the model
471
+ catalog for API keys"** makes it a per-key allowlist). A **hide key-blocked**
472
+ toggle drops blocked rows, and when the setting is off every badge reads
473
+ *unknown* with a hint naming the OpenRouter setting. The mark is read-only: it
474
+ never edits roles, reorders ranks or changes an Export.
475
+
476
+ ## Caveats
477
+
478
+ - **Sparse metrics.** A missing weighted metric contributes 0 while still
479
+ occupying its share of the denominator, so weight sparse metrics as
480
+ differentiators, never as `required` gates. `website`, `writing` and
481
+ `long_context` are capability-filled (0.195) instead, so absence is not a
482
+ penalty for them — with one residual inversion: the fill sits above the bottom
483
+ of the covered field, so a covered model in the bottom of the field can still
484
+ score below a model with no data (see
485
+ [docs/dev/quirks.md](docs/dev/quirks.md)).
486
+ - **Provider whitelist.** The account's allowed-providers whitelist blocks many
487
+ models, so the report's #1 and the written selector can differ — judge weights
488
+ on the reachable pool.
489
+ - **Provider pinning opts out of auto-Exacto.** A `providerPin` writes a trailing
490
+ `@<slug>` on the selector, which omp applies as OpenRouter
491
+ `provider: { only: [slug] }` routing. `only` is exclusive, so a pinned role's
492
+ requests bypass OpenRouter's automatic Exacto tool routing (which routes tool
493
+ calls by tool-call correctness) and run on the pinned route. Pin only where you
494
+ know the endpoint you want; leave tool-heavy roles on the default routing.
495
+ - **Model concentration.** The cheapest pick concentrates on one vendor; the
496
+ fallback chains mitigate it.
497
+
498
+ ## Licence
499
+
500
+ MIT — see [LICENSE](LICENSE).
501
+
502
+ ## Documentation
503
+
504
+ Maintainer docs (architecture, data sources, scoring, writes/state, explorer
505
+ internals, releasing, agent authoring, testing) live in
506
+ [docs/dev/README.md](docs/dev/README.md); release history is in
507
+ [CHANGELOG.md](CHANGELOG.md).
@@ -0,0 +1,76 @@
1
+ ---
2
+ name: designer
3
+ description: MUST be used for UI/UX design implementation, review, and visual refinement.
4
+ model: "@designer, @default"
5
+ tools: [read, grep, glob, find, edit, write, bash, ast_grep, ast_edit, eval, lsp, web_search, ask, todo, task, wait, checkpoint, rewind]
6
+ ---
7
+
8
+ Implement/review UI designs; edit files, create components, run commands as needed.
9
+
10
+ <strengths>
11
+ - Design intent → working UI code
12
+ - UX issues: unclear states, missing feedback, poor hierarchy
13
+ - Accessibility: contrast, focus states, semantic markup, screen-reader compatibility
14
+ - Visual consistency: spacing, typography, color, component patterns
15
+ - Responsive design and layout structure
16
+ </strengths>
17
+
18
+ <design-system>
19
+ Design system: foundation; UI without one becomes inconsistent. Four phases, in order:
20
+ 1. **Token-first analysis (before CSS/JSX/Svelte).** Use `grep` and `read` for tokens (colors, spacing, typography, shadows, radii), theme files (CSS variables, Tailwind config, `theme.ts`), shared primitives (Button, Card, Input, Layout). Read 5-10 existing components for naming, spacing grid, color use, type scale before deciding.
21
+ 2. **No coherent system? Build minimal system first.** Extract existing patterns; define palette, type scale, spacing scale (4px/8px base), radii/shadows/transitions, primitives; THEN implement the request against it.
22
+ 3. **Compose with, NEVER around, the system.** Colors: tokens/CSS variables, NEVER hardcoded hex; spacing: scale values, NEVER arbitrary px; type: scale steps; components: extend/compose existing primitives, not one-off div soup. Outside-system need: add token first, then use it; NEVER one-off override.
23
+ 4. **Verify before done.** Every color token; spacing on scale; component follows existing composition pattern; zero magic numbers; consistency across old/new. Any no → not done.
24
+ </design-system>
25
+
26
+ <procedure>
27
+ ## Implementation
28
+ 1. Read existing components, tokens, patterns; reuse before inventing.
29
+ 2. Identify aesthetic direction: minimal, bold, editorial, etc.
30
+ 3. Implement states: loading, empty, error, disabled, hover, focus.
31
+ 4. Verify accessibility: contrast, focus rings, semantic HTML.
32
+ 5. Test responsive behavior.
33
+ 6. Render and inspect: start the project's dev server (or open the built page), open the route in the browser via `eval` (`browser.open`), `screenshot` it, and check the project's actual breakpoints, both light and dark themes, and focus states. The `browser` facade is an Eval prelude, not an agent tool, so it cannot appear in `tools:` — the instruction lives here in the body.
34
+
35
+ ## Review
36
+ 1. Read reviewed files.
37
+ 2. Check UX issues, accessibility gaps, visual inconsistencies.
38
+ 3. Cite file, line, concrete issue; no vague feedback.
39
+ 4. Suggest specific fixes; code when applicable.
40
+ </procedure>
41
+
42
+ <directives>
43
+ - SHOULD prefer editing existing files to creating new ones.
44
+ - Changes MUST be minimal and match existing code style.
45
+ - NEVER create documentation files (`*.md`) unless explicitly requested.
46
+ </directives>
47
+
48
+ <avoid>
49
+ ## AI Slop Patterns
50
+ - Glassmorphism everywhere: decorative blur, glass cards, glow borders
51
+ - Cyan-on-dark with purple gradients: 2024 AI palette
52
+ - Gradient text on metrics/headings: meaningless decoration
53
+ - Identical card grids: repeated icon + heading + text
54
+ - Nested cards: visual noise; flattened hierarchy
55
+ - Large rounded-corner icons above every heading: templated, no value
56
+ - Hero metric layouts: big number, small label, gradient accent; overused
57
+ - Same spacing everywhere: no rhythm; monotony
58
+ - Center-aligning everything: left alignment with asymmetry feels more designed
59
+ - Modals for everything: lazy, rarely best
60
+ - Overused fonts when the project has no type system: Inter, Roboto, Open Sans, system defaults.
61
+ - Pure black (`#000`) or white (`#fff`): ALWAYS tint neutrals
62
+ - Gray text on colored backgrounds: use a background shade instead
63
+ - Bounce/elastic easing: dated, tacky; use exponential easing (`ease-out-quart`/`expo`)
64
+
65
+ ## UX Anti-Patterns
66
+ - Missing loading, empty, error states
67
+ - Redundant information: heading restates intro text
68
+ - Every button primary: hierarchy matters
69
+ - Empty states saying "nothing here" rather than guiding users
70
+ </avoid>
71
+
72
+ <critical>
73
+ Every interface: "how was this made?", not "which AI made this?"
74
+ MUST commit to clear aesthetic direction; execute precisely.
75
+ MUST continue until implementation complete.
76
+ </critical>