Pattern gives coding agents better design judgment by searching real component libraries, checking options against your framework, product domain, and requirements, then giving them a recommendation they can act on while they build
Use recommend_component before picking a UI component: pass the specific need, my domain, and framework, then act on the verdict. Install what it recommends, or build from the reference it returns
Mid-build, in a real project. This is where an agent needs to choose a component, but it has little context for what good looks like. It can grab something loosely related or invent a generic solution
Pattern turns the component need into a requirements checklist, searches shadcn/ui and 21st.dev, and scores each requirement against the evidence it finds. The server recounts coverage from the checklist instead of trusting a stated percentage
A use_existing verdict includes the source, install command, and a description of what the component actually does. The description is based on the component Pattern found before anything is installed. The install command is untrusted text, so your agent shows it to you before running it
A custom_build verdict returns the checklist plus a grounded Mobbin or Figma Community reference. Deep links are verified server-side against a page that was actually fetched. When only a browse page exists, Pattern says so
Buttons, inputs, checkboxes, badges, spinners, tooltips, avatars, and icons are trivial primitives. A local skip-list catches them before any API request, so they add no API cost or latency
record_component_decision logs decisions the agent actually acted on, per project. A later call can use a similar past decision as a signal, never as a rule and never instead of searching and scoring again
Each requirement is checked against something the search actually found, then recounted in code and thresholded into a verdict. This is not a similarity ranking
no_candidates_found stays distinct from low coverage, so "nothing exists for this" never gets turned into a bad match
A deep link survives only if it appears on a page the server fetched. Otherwise you get the browse page and a note saying exactly what it is
Confirmed decisions become a consistency signal on later calls. Coverage is still recomputed every time, and verdicts are never cached
Near a verdict threshold, the judgment runs again and takes the majority. A genuine 2/3 split returns confidence: low with the runs attached
A local call log for each API-reaching call, a 40-call session cap, prompt caching, and a bounded search budget. Skip-listed primitives never reach the API
| Field | Type | Notes |
|---|---|---|
| component_need | string, required | Specific, not a category. "price breakdown with fees and taxes", not "pricing" |
| domain | string | The product context the component lives in |
| framework | string | e.g. React + Tailwind |
| existing_stack | string | e.g. already using shadcn/ui |
| project_id | string, optional | Enables per-project decision memory. Omit to skip memory entirely |
| Field | Values | Notes |
|---|---|---|
| verdict | use_existing | custom_build | Thresholded in code from the recounted coverage |
| confidence | high | medium | low | Forced to low on a genuine 2/3 ensemble split |
| reason | scored | no_candidates_found | skip_list | Zero candidates stays distinct from low coverage |
| coverage | "5/7 (71%)" | Recomputed from requirements_checked, not taken from the model |
| requirements_checked[] | requirement, met, evidence | The checklist, with the evidence behind each judgment |
| recommendation.reference | object | array | null | Both sources grounded → array. One → object. Neither → null |
| reference.url_type | deep_link | entry_point | Whether the URL is the actual screen or a browse page |
| ensemble | { triggered, runs, agreement } | Present on every response; runs and agreement only when it fired |
| past_decision_signal | { considered, note } | Only when project_id was passed and a real past decision applied |
| Env var | Default | Notes |
|---|---|---|
| ANTHROPIC_API_KEY | required | Your own Console key. Every call bills your account |
| PATTERN_MODEL | claude-sonnet-5 | Swap models without a code change. Re-run the five test cases first |
| PATTERN_SESSION_CAP | 40 | Per-process call cap, a runaway-agent guard, not a usage budget |
| PATTERN_MEMORY_PATH | ~/.pattern/memory.json | Where confirmed decisions are stored, local only |
| PATTERN_LOG_PATH | ~/.pattern/calls.log | One JSON line per API-reaching call, local only |
| Component need | What it tests |
|---|---|
| price breakdown with fees and taxes | clean commodity match |
| cancellation policy display | false-positive-prone |
| host earnings dashboard | boundary / near-tie |
| image gallery for a property listing | flips even with the ensemble, reported as low confidence |
| host-guest messaging inbox | went stale as libraries shipped new components |
All five sit in one Airbnb-style rental-marketplace domain and cover the full range of outcomes. Run them before trusting the pipeline unattended, and again after changing models
Fee breakdowns, policy displays, dashboards, inboxes. Anything where fit requires judgment
The verdict is useful while there's still a decision to make
Buttons, inputs, badges, spinners. The skip-list already answers these for free
Coverage is a snapshot with a computed_at date. Keep caching session-scoped, never across builds
One limitation is worth knowing before you start: the model can judge the same evidence differently between runs on identical input. The ensemble catches cases where that could flip a verdict and reports them as confidence: "low" instead of hiding the disagreement.