omp-llm-role 0.0.0-stage → 1.0.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 (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +338 -2
  3. package/agents/designer.md +75 -0
  4. package/docs/dev/README.md +54 -0
  5. package/docs/dev/agent-authoring.md +165 -0
  6. package/docs/dev/architecture.md +278 -0
  7. package/docs/dev/current-state.md +391 -0
  8. package/docs/dev/data-sources.md +344 -0
  9. package/docs/dev/explorer.md +206 -0
  10. package/docs/dev/quirks.md +133 -0
  11. package/docs/dev/releasing.md +63 -0
  12. package/docs/dev/scoring.md +266 -0
  13. package/docs/dev/spec.md +633 -0
  14. package/docs/dev/writes-and-state.md +153 -0
  15. package/docs/llm-role-rankings.md +198 -0
  16. package/package.json +822 -4
  17. package/skills/omp-llm-role-create-agent/SKILL.md +168 -0
  18. package/src/agent-architect.ts +134 -0
  19. package/src/agent-create.ts +733 -0
  20. package/src/agent-file.ts +122 -0
  21. package/src/agent-pins.ts +75 -0
  22. package/src/agent-remove.ts +180 -0
  23. package/src/availability.ts +222 -0
  24. package/src/benchmark-author.ts +82 -0
  25. package/src/benchmark-discovery.ts +84 -0
  26. package/src/benchmark-sources.ts +758 -0
  27. package/src/cli/create-role.ts +162 -0
  28. package/src/cli/llm-role-rank.ts +317 -0
  29. package/src/config-edit.ts +456 -0
  30. package/src/engine.ts +1346 -0
  31. package/src/explorer/boot.ts +135 -0
  32. package/src/explorer/explain.ts +329 -0
  33. package/src/explorer/server.ts +238 -0
  34. package/src/extension.ts +518 -0
  35. package/src/guards.ts +8 -0
  36. package/src/prompts/agent-creation-architect.md +35 -0
  37. package/src/prompts/agent-creation-user.md +6 -0
  38. package/src/prompts/benchmark-source-architect.md +36 -0
  39. package/src/prompts/benchmark-source-user.md +6 -0
  40. package/src/role-archetypes.ts +248 -0
  41. package/src/role-settings.ts +187 -0
  42. package/src/settings.ts +562 -0
  43. package/src/state.ts +141 -0
  44. package/src/updater.ts +473 -0
  45. package/web/app.js +840 -0
  46. package/web/index.html +41 -0
  47. package/web/style.css +244 -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,339 @@
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, math, long-context coherence |
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
+
114
+ Per-role knobs (`roles.<name>.*`):
115
+
116
+ | Knob | Default | Meaning |
117
+ |---|---|---|
118
+ | `enabled` | `true` (`designer`: `false`) | Opt a role in/out; `false` drops it from the resolved set and deletes its `modelRoles.<role>` line |
119
+ | `description` | shipped one-liner | Role description |
120
+ | `weights` | shipped set | Metric weights; must sum to 1.0 ± 0.01 |
121
+ | `required` | shipped set | Eligibility gate (metrics a model must have); independent of weights |
122
+ | `filters.image` | `false` (`vision`/`designer`: `true`) | Restrict the pool to image-capable models |
123
+ | `thinking` | shipped level | Thinking level appended to the selector (`off`…`max`, `auto`) |
124
+ | `lambda` | derived | Explicit λ ($ per quality point) override |
125
+ | `locked` | `false` | Leave the role alone: rank it but never rewrite its selector or chain (explorer toggle) |
126
+
127
+ **Typed writes.** `omp plugin config set <plugin> <key> <value>` stores every
128
+ value as a **string**, which the validator rejects for numbers/arrays/booleans,
129
+ so it is only usable for string-valued keys. Write typed role defs with
130
+ `node src/cli/create-role.ts` or the explorer's **Export** (both validate →
131
+ merge → backup → atomic write); for a whole-object override use the `config` escape
132
+ hatch, `omp plugin config set omp-llm-role config '<json>'` (JSON-parsed by the
133
+ plugin). The lock file stores **flat dotted keys** (`roles.slow.weights.code`);
134
+ the reader also accepts nested objects, so an existing nested lock file keeps
135
+ working and normalizes on the next role write. Invalid settings abort the run
136
+ with the offending role/key and no write.
137
+
138
+ ## Commands
139
+
140
+ ```sh
141
+ # in-session (omp)
142
+ /refresh-roles # force a run now
143
+ /explore-roles [--port N] [--no-open] # interactive ranking UI
144
+ /create-agent <request> [options] # agent + role + wiring (free text or flags)
145
+ /create-agent --name <n> --purpose <text> [options] # agent + role + wiring
146
+ /remove-agent --name <n> [--scope user|project] [--lock PATH] [--yes] [--dry-run] # delete an agent and its role
147
+
148
+ # CLI
149
+ node src/cli/llm-role-rank.ts [--top N] [--json] [--out FILE] [--refresh] [--all] [--url URL]
150
+ node src/cli/create-role.ts --name <role> --weights m=w,... [--required m,...] [--thinking <level>] [--description <text>] [--image] [--lambda N] [--lock PATH] [--dry-run] [--json]
151
+ node --test tests/
152
+ ```
153
+
154
+ - `/refresh-roles` — force the daily pipeline now (same path as the session-start
155
+ run).
156
+ - `/explore-roles` — boot the explorer in-process (see below); `--port` picks a
157
+ preferred port (default 5177, a busy port falls back to a free one) and
158
+ `--no-open` skips the browser.
159
+ - `/create-agent` — create an agent **and** its role and wire them in one
160
+ command (see *Add a specialist agent*).
161
+ - `/remove-agent` — the inverse of `/create-agent`: deletes the agent `.md` and
162
+ its role, then runs the updater in-process so `modelRoles.<n>` is dropped;
163
+ refuses shipped default roles (see *Remove a specialist agent*).
164
+ - `src/cli/llm-role-rank.ts` — markdown report to stdout (per-role tables + suggested
165
+ `modelRoles`). `--top N` rows per role (default 10); `--json` machine payload;
166
+ `--out FILE` write instead of stdout; `--refresh` bypass both caches; `--all`
167
+ include opt-in roles (`designer`) — the default ranks the nine built-in roles
168
+ only; `--url` override the llm-stats page URL.
169
+ - `src/cli/create-role.ts` — add/update one role in the settings lock file (validated,
170
+ backup + atomic write); `--dry-run` validates without writing, `--json` prints
171
+ the payload.
172
+ - `node --test tests/` — the unit suite.
173
+
174
+ ## Add a specialist agent
175
+
176
+ One command does all of it. Describe the agent in plain language — the architect
177
+ names it, any benchmark you mention is folded into the weights, and any
178
+ benchmark link you point at is resolved:
179
+
180
+ ```sh
181
+ /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
182
+ ```
183
+
184
+ A link to any llm-stats benchmark page (`llm-stats.com/benchmarks/<id>`) resolves
185
+ to that benchmark; a link to a provider the plugin has never seen is authored
186
+ into a source declaration — the plugin fetches it, proposes a declaration, shows
187
+ the coverage and leader, and asks you to confirm before it is saved. A named
188
+ benchmark takes a decisive share of the weights, so it actually drives the
189
+ ranking. You do not have to name every benchmark: when you give a purpose, the
190
+ command also discovers the llm-stats catalog benchmarks relevant to it (a typed
191
+ judgment through your configured `judge` role) and folds them in — so a writing
192
+ agent is ranked on WritingBench, Creative Writing v3, COLLIE and the other
193
+ writing benchmarks, not just the one you named. Discovery is non-fatal and
194
+ skippable with `--no-discover`; an explicit `--benchmarks` list wins over it.
195
+
196
+ The flag form is equivalent and gives you the name explicitly:
197
+
198
+ ```sh
199
+ /create-agent --name review --purpose "review pull requests for correctness and security"
200
+ ```
201
+
202
+ It fits the weights to the purpose from the archetype table
203
+ (`--list-archetypes` prints the ten sets), authors
204
+ `~/.omp/agent/agents/review.md` with `model: "@review, @default"`, writes the
205
+ validated role into the settings lock file, and runs the updater in-process so
206
+ `modelRoles.review` lands in `config.yml` — nothing to remember. Options:
207
+ `--archetype <id>`, `--weights m=w,...`, `--required`, `--thinking`, `--tools`,
208
+ `--benchmarks m,...` (extra metrics to fold in; `--list-benchmarks` prints the
209
+ weightable set, including any external benchmark in use), `--scope user|project`,
210
+ `--body`/`--body-file`, `--force` (overwrite an existing agent file), `--yes`
211
+ (accept a proposed benchmark source without prompting), `--no-discover` (skip
212
+ catalog discovery), `--dry-run`, `--json`. In
213
+ the free-text form the flags still apply after the request, e.g.
214
+ `/create-agent i want an agent for writing --dry-run`. There is no
215
+ out-of-session CLI for this path — run `/create-agent` in an omp session.
216
+
217
+ By hand, the same three artifacts:
218
+
219
+ 1. Define the role with the plugin's validated write path:
220
+ ```sh
221
+ node ~/.omp/plugins/node_modules/omp-llm-role/src/cli/create-role.ts --name review \
222
+ --weights reasoning=0.30,general=0.24,code=0.20,agents=0.10,price=0.10,throughput=0.06 \
223
+ --required general,price,throughput --thinking high \
224
+ --description "Code review: agentic depth with cost awareness"
225
+ ```
226
+ 2. Author the agent that pins `model: "@review, @default"` (the routing), or pin
227
+ an existing agent through `task: { agentModelOverrides: { <agent>: "@review" } }`.
228
+ 3. Wire it: `/refresh-roles` in a session.
229
+
230
+ The shipped skill `omp-llm-role-create-agent` is the hand-driven equivalent, for
231
+ when the body needs real authoring rather than the archetype scaffold.
232
+
233
+ ## Remove a specialist agent
234
+
235
+ One command deletes an agent **and** its role:
236
+
237
+ ```sh
238
+ /remove-agent --name review
239
+ ```
240
+
241
+ It deletes the agent `.md` (user and/or project scope) and the role's lock-file
242
+ keys, then runs the updater in-process so `modelRoles.review` is dropped from
243
+ `config.yml` — nothing to remember. Options: `--scope user|project`,
244
+ `--lock PATH`, `--yes` (skip the in-session confirmation), `--dry-run`. Shipped
245
+ default roles (e.g. `designer`) cannot be deleted — the message points at the
246
+ explorer's "Reset to shipped default". A role created any way (the explorer's
247
+ "+ new role", `create-role.ts`, or `/create-agent`) is removable.
248
+
249
+ By hand, the same two artifacts:
250
+
251
+ 1. Delete the role from the settings lock file (the explorer's Export, or edit
252
+ the lock file directly).
253
+ 2. Delete the agent file: `rm ~/.omp/agent/agents/review.md`.
254
+ 3. Wire it: `/refresh-roles` in a session.
255
+
256
+ ## Explorer
257
+
258
+ `/explore-roles` in omp boots a loopback-only web UI
259
+ (`http://127.0.0.1:5177`) that answers "why is model X at rank 7 for
260
+ `@slow`?" and "what happens if I care more about price than agents?" without
261
+ editing source and re-running the CLI.
262
+
263
+ - **Role tabs** — every role the plugin knows: the nine omp built-in roles, the
264
+ shipped opt-in `designer`, and any role present only in the lock file. Each
265
+ tab is tinted by provenance — `default` (omp built-in), `plugin` (shipped but
266
+ opt-in), `user` (lock-file only) — and a disabled role is muted and dashed.
267
+ Selecting a disabled role still ranks its models.
268
+ - **Rank table** — every eligible model for the selected role, with `value`,
269
+ `q`, `$/M` (effective, thinking-adjusted), `tok/s`, `ctx`, a `★` Pareto
270
+ marker, and a `Δ` column showing the rank delta against the role's effective
271
+ def (what the plugin does today).
272
+ - **Explain panel** — click a row for the full decomposition: each weighted
273
+ metric's raw value → cardinal transform → renormalized weight → contribution,
274
+ the cost block (`λ`, effective vs billed price, `penalty = λ·price`,
275
+ `value = q − penalty`), "why not higher" (the value gap to the model above
276
+ plus the per-metric target that would close it), and the models that dominate
277
+ it on (price, q).
278
+ - **Weight editor** — edit the role's description, weights, `required` set,
279
+ `filters.image`, `thinking` level, and `λ` override live; the table re-ranks
280
+ on every change. Weights are edited freely (no implicit rescaling): the `Σ`
281
+ readout turns red until `|Σ − 1| ≤ 0.01`, and `Normalize` rescales in one
282
+ click.
283
+ - **Enabled toggle** — opts a role in or out. Exporting writes `enabled` into
284
+ the lock file; the next updater run drops a disabled role from the resolved
285
+ set and deletes its `modelRoles.<role>` line, and re-enabling restores it.
286
+ - **Locked toggle** — tells the plugin to leave the role alone: it is still
287
+ ranked and shown, but the updater never rewrites its selector or fallback
288
+ chain and never removes it. Enable/disable still applies.
289
+ - **+ new role** — creates a role from a template
290
+ (`general/code/price/throughput`, Σ 1.0) that you then tune and Export.
291
+ - **Export** — writes the edited roles into the settings lock file atomically
292
+ and with a `.bak-<timestamp>` sibling, touching only the roles you edited. The
293
+ change takes effect on the next `/refresh-roles` **in any running session**
294
+ (the lock file is re-read from disk on every run). `Copy JSON` / `Download
295
+ JSON` emit the same payload for manual use.
296
+ - **Hover explanations** — every column header, metric name, role tab, and
297
+ control carries a tooltip explaining its semantics.
298
+
299
+ The UI never reimplements the ranking math: the numbers on screen are exactly
300
+ the numbers the plugin would use. The server binds `127.0.0.1` only (no auth)
301
+ and serves the SPA with no build step and no external requests.
302
+
303
+ ## Availability
304
+
305
+ The plugin keeps only models the OpenRouter key can actually run:
306
+
307
+ - **Tier/budget gate** — paid keys with budget get billed variants, free or
308
+ exhausted keys get `:free` variants, `:batch` never. Every candidate must
309
+ resolve in omp's catalog.
310
+ - **Probe verification** — on top of the gate, every candidate is verified with
311
+ a one-token completion probe, because the account's OpenRouter
312
+ **allowed-providers privacy whitelist** is invisible to the key and catalog
313
+ endpoints and a real request's 404 is the only reliable signal. Blocked
314
+ candidates are excluded from selection and chains; a role with no clean
315
+ candidate is left untouched.
316
+
317
+ ## Caveats
318
+
319
+ - **Sparse metrics.** A missing weighted metric contributes 0 while still
320
+ occupying its share of the denominator, so weight sparse metrics as
321
+ differentiators, never as `required` gates. `website`, `writing` and
322
+ `long_context` are capability-filled (0.195) instead, so absence is not a
323
+ penalty for them.
324
+ - **Provider whitelist.** The account's allowed-providers whitelist blocks many
325
+ models, so the report's #1 and the written selector can differ — judge weights
326
+ on the reachable pool.
327
+ - **Model concentration.** The cheapest pick concentrates on one vendor; the
328
+ fallback chains mitigate it.
329
+
330
+ ## Licence
331
+
332
+ MIT — see [LICENSE](LICENSE).
333
+
334
+ ## Documentation
335
+
336
+ Maintainer docs (architecture, data sources, scoring, writes/state, explorer
337
+ internals, releasing, agent authoring, the normative spec) live in
338
+ [docs/dev/README.md](docs/dev/README.md); release history is in
339
+ [CHANGELOG.md](CHANGELOG.md).
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: designer
3
+ description: UI/UX specialist for design implementation, review, visual refinement
4
+ model: "@designer, @default"
5
+ tools: [read, bash, edit, ast_grep, ast_edit, ask, debug, ida, eval, github, glob, grep, find, lsp, checkpoint, rewind, context_notes, new_context, security_scan, task, wait, todo, web_search, write, memory_edit, retain, recall, reflect, learn, manage_skill]
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
+
34
+ ## Review
35
+ 1. Read reviewed files.
36
+ 2. Check UX issues, accessibility gaps, visual inconsistencies.
37
+ 3. Cite file, line, concrete issue; no vague feedback.
38
+ 4. Suggest specific fixes; code when applicable.
39
+ </procedure>
40
+
41
+ <directives>
42
+ - SHOULD prefer editing existing files to creating new ones.
43
+ - Changes MUST be minimal and match existing code style.
44
+ - NEVER create documentation files (`*.md`) unless explicitly requested.
45
+ </directives>
46
+
47
+ <avoid>
48
+ ## AI Slop Patterns
49
+ - Glassmorphism everywhere: decorative blur, glass cards, glow borders
50
+ - Cyan-on-dark with purple gradients: 2024 AI palette
51
+ - Gradient text on metrics/headings: meaningless decoration
52
+ - Identical card grids: repeated icon + heading + text
53
+ - Nested cards: visual noise; flattened hierarchy
54
+ - Large rounded-corner icons above every heading: templated, no value
55
+ - Hero metric layouts: big number, small label, gradient accent; overused
56
+ - Same spacing everywhere: no rhythm; monotony
57
+ - Center-aligning everything: left alignment with asymmetry feels more designed
58
+ - Modals for everything: lazy, rarely best
59
+ - Overused fonts: Inter, Roboto, Open Sans, system defaults
60
+ - Pure black (`#000`) or white (`#fff`): ALWAYS tint neutrals
61
+ - Gray text on colored backgrounds: use a background shade instead
62
+ - Bounce/elastic easing: dated, tacky; use exponential easing (`ease-out-quart`/`expo`)
63
+
64
+ ## UX Anti-Patterns
65
+ - Missing loading, empty, error states
66
+ - Redundant information: heading restates intro text
67
+ - Every button primary: hierarchy matters
68
+ - Empty states saying "nothing here" rather than guiding users
69
+ </avoid>
70
+
71
+ <critical>
72
+ Every interface: "how was this made?", not "which AI made this?"
73
+ MUST commit to clear aesthetic direction; execute precisely.
74
+ MUST continue until implementation complete.
75
+ </critical>
@@ -0,0 +1,54 @@
1
+ # Developer documentation
2
+
3
+ Maintainer docs for the omp-llm-role plugin. `README.md` is for people *using*
4
+ the plugin; everything here is for people *changing* it. A fact has exactly one
5
+ home — if it is here, it is not in the README.
6
+
7
+ ## Where to start
8
+
9
+ | If you are changing… | Read |
10
+ |---|---|
11
+ | a module, an entry point, or the data flow between them | [architecture.md](architecture.md) |
12
+ | a data source, its fetch/cache, or a join between sources | [data-sources.md](data-sources.md) |
13
+ | a metric, a cardinal transform, or a role's weights | [scoring.md](scoring.md) |
14
+ | a config write, the state/history/lock files, or the write gate | [writes-and-state.md](writes-and-state.md) |
15
+ | the explorer surface (server, explain layer, SPA) | [explorer.md](explorer.md) |
16
+ | the dated numbers (matched/priced/eligible counts, per-role leaders) | [current-state.md](current-state.md) |
17
+ | a release (version bump, npm/git/marketplace channels) | [releasing.md](releasing.md) |
18
+ | the agent `.md` contract (frontmatter, routing, read-only rules) | [agent-authoring.md](agent-authoring.md) |
19
+ | a normative decision (what the plugin MUST do) | [spec.md](spec.md) |
20
+ | a debugging-only oddity (a quirk, not a contract) | [quirks.md](quirks.md) |
21
+
22
+ ## The docs
23
+
24
+ - **[architecture.md](architecture.md)** — the module map and entry points:
25
+ `src/cli/llm-role-rank.ts` (CLI report), `src/engine.ts` (shared ranking engine),
26
+ `src/updater.ts` (orchestration), `src/extension.ts` (omp extension), the
27
+ explorer, and how a run flows from fetch to config write.
28
+ - **[data-sources.md](data-sources.md)** — every source the engine reads
29
+ (llm-stats leaderboard, OpenRouter catalog + per-provider routes, Design
30
+ Arena, the writing leaderboard), its cache file and freshness rule, and the
31
+ id joins that stitch them into one model row.
32
+ - **[scoring.md](scoring.md)** — the cardinal transforms, the value formula
33
+ `q − λ·$/M`, the Pareto frontier, hysteresis, and the per-role
34
+ weight/`required` semantics.
35
+ - **[writes-and-state.md](writes-and-state.md)** — the surgical `config.yml`
36
+ patch (`modelRoles`, `retry.fallbackChains`, `task.disabledAgents`), the
37
+ atomic write, the state/history/lock files, and the day gate.
38
+ - **[explorer.md](explorer.md)** — the interactive ranking UI: the
39
+ zero-dependency HTTP server, the pure explain layer (baseline deltas,
40
+ per-model decomposition, inverse-cardinal targets), and the SPA.
41
+ - **[current-state.md](current-state.md)** — the dated snapshot:
42
+ matched/priced/eligible counts, per-role leaders, and the open defects.
43
+ Refresh it with each run.
44
+ - **[releasing.md](releasing.md)** — the release switch (`package.json`
45
+ `version`), the npm/git/marketplace channels, the tarball `files` whitelist,
46
+ and the install-route verification.
47
+ - **[agent-authoring.md](agent-authoring.md)** — the agent `.md` contract:
48
+ frontmatter (`model: "@<role>, @default"`, `tools:`), the routing
49
+ description, read-only classification, and where agents are discovered.
50
+ - **[spec.md](spec.md)** — the normative spec: the confirmed decisions, the
51
+ verified ground truth, and the contracts the implementation MUST satisfy.
52
+ - **[quirks.md](quirks.md)** — debugging-only oddities and gotchas that are not
53
+ contracts (e.g. Node type-stripping not typechecking, stale in-process plugin
54
+ code).
@@ -0,0 +1,165 @@
1
+ # Agent authoring: the `.md` contract
2
+
3
+ An omp subagent is a Markdown file with YAML frontmatter: the frontmatter is the
4
+ routing unit (what the main model dispatches on, and which model role the child
5
+ runs), the body is the system prompt. This document is the **contract** the plugin
6
+ writes and reads; the step-by-step authoring workflow lives in the shipped skill
7
+ [`skills/omp-llm-role-create-agent/SKILL.md`](../../skills/omp-llm-role-create-agent/SKILL.md)
8
+ and in `/create-agent`.
9
+
10
+ Source of truth: [`src/agent-file.ts`](../../src/agent-file.ts) (rendering,
11
+ placement, read-only classification), [`src/agent-pins.ts`](../../src/agent-pins.ts)
12
+ (pin derivation), [`src/agent-create.ts`](../../src/agent-create.ts) (`/create-agent`
13
+ core), [`src/agent-architect.ts`](../../src/agent-architect.ts) (the in-process
14
+ architect).
15
+
16
+ ## Frontmatter
17
+
18
+ | Field | Required | Rule |
19
+ |---|---|---|
20
+ | `name` | yes | `[A-Za-z0-9_-]+` (`AGENT_NAME_RE`); `main` and `sub` are reserved (`RESERVED_AGENT_NAMES` — omp's session sentinels). The file name is `<name>.md`; omp derives the agent id from it. |
21
+ | `description` | yes | The routing rule the main model dispatches on: "MUST be used for …", what it returns, when to skip. `/create-agent` uses the architect's `whenToUse` (which must start with "Use this agent when"); without an architect it derives `MUST be used to/for <purpose>.` |
22
+ | `model` | yes | The ordered fallback chain `@<role>, @default` — never a bare `@<role>` (below). |
23
+ | `tools` | no | Builtin tool allowlist, comma-separated. Absent/empty = full session access. |
24
+ | `thinking-level` | **never** | The role's `thinking` field already sets the effort and prices it; a second pin would silently disagree with the ranking. |
25
+
26
+ ### `model:` is a chain, not an alias
27
+
28
+ `@<role>` is a role alias only when `<role>` is a built-in id (`default, smol,
29
+ slow, vision, plan, commit, tiny, task, advisor`) or a key in `modelRoles`.
30
+ Otherwise omp treats it as a **literal model pattern** and hard-fails
31
+ `Model "@x" not found`. A role is absent whenever it is disabled
32
+ (`roles.<role>.enabled=false`), unranked, or not yet written to `config.yml` — so a
33
+ bare `model: "@designer"` hard-fails in exactly the state a fresh install is in.
34
+
35
+ The chain `model: "@<role>, @default"` keeps the agent spawnable: omp skips the
36
+ unresolved first entry and the child runs on `@default`. The plugin always writes
37
+ the chain (`renderAgentFile`), and the shipped
38
+ [`agents/designer.md`](../../agents/designer.md) pins it.
39
+
40
+ ### `tools:` and the read-only classification
41
+
42
+ `isReadOnlyTools` (`src/agent-file.ts`) classifies an agent as read-only iff its
43
+ tools list is **non-empty and every entry is in `READ_ONLY_TOOLS`**:
44
+
45
+ ```
46
+ read, wait, grep, glob, find, web_search, ast_grep, yield, ask, todo,
47
+ recall, reflect, retain, memory_edit, checkpoint, rewind
48
+ ```
49
+
50
+ Any other name — including an unknown one, which omp silently drops — makes the
51
+ agent a writer. An empty list is full session access, not read-only. The
52
+ `/create-agent` archetypes default to a writer set except `review`/`audit`
53
+ (`read, grep, glob, find`) and `research`/`search` (`read, grep, glob, find,
54
+ web_search`).
55
+
56
+ `renderAgentFile` emits `tools: <comma-joined names>`; the shipped
57
+ `agents/designer.md` uses the equivalent YAML flow form `tools: [a, b, c]`. Both
58
+ are comma-separated lists omp parses.
59
+
60
+ ## Rendering and writing
61
+
62
+ `renderAgentFile` emits exactly, in order: `---`, `name: <name>`,
63
+ `description: "<double-quoted>"`, `model: "<double-quoted>"`, an optional
64
+ `tools:` line, `---`, a blank line, then the trimmed body. `description` and
65
+ `model` are YAML double-quoted (backslashes and quotes escaped, newlines folded).
66
+
67
+ `writeAgentFile` refuses to clobber an existing file unless `--force`, creates the
68
+ target dir if missing, and writes through the same tmp+rename, mtime-guarded
69
+ writer as the settings lock file — a half-written file can never be read as an
70
+ agent.
71
+
72
+ ## Placement and discovery
73
+
74
+ | Scope | Directory | Notes |
75
+ |---|---|---|
76
+ | plugin (shipped) | `agents/` in the plugin repo | discovered from the plugin's extension root — linking/installing the plugin ships it, no install step |
77
+ | user (global) | `~/.omp/agent/agents/` | profile- and env-aware (`agentDir()`: `OMP_LLM_ROLE_AGENT_DIR` → `PI_CODING_AGENT_DIR` → non-default `OMP_PROFILE` → `~/.omp/agent`) |
78
+ | project | `<project>/.omp/agents/` | under the project anchor |
79
+
80
+ Override order is **first-wins, most specific first: project > user > plugin**,
81
+ matching omp's own agent lookup. omp's full discovery order is nearest project
82
+ `.omp/agents/` → `~/.omp/agent/agents/` → extension roots → Claude marketplace
83
+ plugins → bundled.
84
+
85
+ `discoverAgentPins` (`src/agent-pins.ts`) scans the three dirs, skips reserved
86
+ names and files that pin no role, and maps each agent to the role its `model:`
87
+ chain pins (`parseAgentPin`: the first `@<role>` in the `model:` frontmatter
88
+ value; `null` when there is no `model:` line or the value names no `@role`). A
89
+ name present in several scopes resolves to the most specific one.
90
+
91
+ ## The pin-derived disable
92
+
93
+ omp has no per-agent frontmatter gate (`parseAgentFields` has no `enabled`;
94
+ `discoverAgents` scans `<ext>/agents/*.md` unconditionally), so the plugin manages
95
+ the core `task.disabledAgents` key in `config.yml` — the same key the `/agents`
96
+ hub writes. The updater keeps an agent in `task.disabledAgents` **exactly while
97
+ the role its `model:` chain pins is disabled or absent from the resolved set**;
98
+ an agent that pins no role, or a role the settings do not define, is left alone.
99
+ So a shipped agent pinned to a disabled role (the shipped `designer`) is off the
100
+ roster by default, and its `@role, @default` chain only matters if a user
101
+ re-enables the agent by hand. The sync is not day-gated — enabling the role takes
102
+ effect on the next session.
103
+
104
+ ## Authoring paths
105
+
106
+ - **`/create-agent`** (in-session) runs omp's agent-creation architect in-process
107
+ (`src/agent-architect.ts`: the `/agents` hub's prompt shipped verbatim in
108
+ `src/prompts/`, run through `createAgentSession` with no tools) to author the
109
+ routing rule and the body, then `src/agent-file.ts` adds the `model:`/`tools:`
110
+ frontmatter omp's own writer omits. The architect supplies
111
+ `{identifier, whenToUse, systemPrompt}`; the plugin uses `--name` for the file
112
+ (or, in the free-text form, the architect's `identifier`), `whenToUse` as the
113
+ description, and `systemPrompt` as the body. The role definition written to the
114
+ settings lock carries the same `whenToUse` as its `description` (the one-line
115
+ purpose when no architect ran), so the explorer and the report show the
116
+ architect's clean label rather than the raw prompt; benchmark links are
117
+ stripped from the purpose before the archetype fit, so a pasted URL path
118
+ cannot bias the archetype. `parseCreateAgentInput` accepts
119
+ either the flag form (`--name … --purpose …`) or a plain-language request
120
+ (`/create-agent i want an agent for writing`); the free-text form folds any
121
+ benchmark the request names into the weights (`extractBenchmarks`), resolves
122
+ any benchmark link it points at (`extractBenchmarkLinks` → the registry, or
123
+ the authoring step for an unknown provider), and still honours trailing flags
124
+ (`--dry-run`, `--force`, `--scope`, `--lock`, `--yes`, `--no-discover`). Unless
125
+ `--no-discover` or an explicit `--benchmarks` list is given, it also discovers
126
+ the catalog benchmarks relevant to the purpose (`discoverBenchmarks`: coverage
127
+ filter → IDF lexical rank → the configured `judge` role via
128
+ `src/benchmark-discovery.ts`) and folds them in. The resolved benchmark
129
+ labels are given to the architect (and, on the template path, listed in the
130
+ body's `<criteria>`), so the agent's rubric names the benchmark its model was
131
+ chosen on.
132
+ - **The shipped skill** — the hand-driven workflow, for bodies that need real
133
+ authoring rather than the archetype scaffold.
134
+
135
+ ## Removal
136
+
137
+ The `/remove-agent` command deletes an agent **and** its role definition:
138
+
139
+ ```sh
140
+ /remove-agent --name review
141
+ ```
142
+
143
+ It deletes the agent `.md` file (user and/or project scope) and the role's
144
+ lock-file keys (`removeRoleSettings`), then runs the updater in-process so
145
+ `modelRoles.review` is dropped from `config.yml` and the plugin-managed
146
+ `task.disabledAgents` entry is cleaned up. Shipped default roles (e.g.
147
+ `designer`) cannot be deleted — the message points at the explorer's "Reset to
148
+ shipped default". `--scope user|project` restricts the agent-file removal
149
+ (default: both); `--yes` skips the in-session confirmation; `--dry-run`
150
+ validates without writing.
151
+
152
+ By hand, the same two artifacts:
153
+
154
+ 1. Delete the role from the settings lock file (the explorer's Export, or edit
155
+ the lock file directly).
156
+ 2. Delete the agent file: `rm ~/.omp/agent/agents/review.md`.
157
+ 3. Wire it: `/refresh-roles` in a session.
158
+
159
+ ## Pitfalls
160
+
161
+ - A role merely *named* after an agent routes nothing; the agent's `model:` pin is
162
+ the routing.
163
+ - Never put non-agent `.md` files in an `agents/` dir.
164
+ - Restart the session after the plugin writes `modelRoles` so the new alias
165
+ resolves.