pattern-mcp 0.1.1 → 0.2.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.
- package/README.md +1005 -439
- package/dist/index.js +583 -111
- package/dist/staged/anthropic.js +110 -0
- package/dist/staged/extract.js +22 -0
- package/dist/staged/pipeline.js +103 -0
- package/dist/staged/reference.js +67 -0
- package/dist/staged/score.js +48 -0
- package/dist/staged/search.js +41 -0
- package/dist/staged/types.js +7 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,54 +1,171 @@
|
|
|
1
1
|
# Pattern
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
[
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
3
|
+
[](https://github.com/donaldrichard19-LVD/pattern-mcp/actions/workflows/publish.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/pattern-mcp)
|
|
5
|
+
[](https://www.npmjs.com/package/pattern-mcp)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
|
|
8
|
+
Pattern is an MCP server that helps coding agents make better UI
|
|
9
|
+
component decisions.
|
|
10
|
+
|
|
11
|
+
[Website](https://usepattern.sh) · [npm](https://www.npmjs.com/package/pattern-mcp) · [Report an issue](https://github.com/donaldrichard19-LVD/pattern-mcp/issues/new/choose)
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install pattern-mcp
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
See [Quick Start](#quick-start) below to add your Anthropic API key and connect
|
|
20
|
+
Pattern to your MCP client.
|
|
21
|
+
|
|
22
|
+
## What Pattern Does
|
|
23
|
+
|
|
24
|
+
Instead of returning a list of search results, Pattern looks at what you
|
|
25
|
+
need, checks real components against that need, and tells the agent
|
|
26
|
+
whether to:
|
|
27
|
+
|
|
28
|
+
- **Use an existing component** from shadcn/ui, 21st.dev, or ReUI
|
|
29
|
+
- **Build a custom component**, using a real product reference from
|
|
30
|
+
Mobbin and/or Figma Community
|
|
31
|
+
|
|
32
|
+
Pattern is designed for agents to use **while they are building**.
|
|
33
|
+
|
|
34
|
+
It exposes three tools:
|
|
35
|
+
|
|
36
|
+
- `recommend_component` — evaluates a UI component need and returns a
|
|
37
|
+
structured recommendation.
|
|
38
|
+
- `extract_requirements` — runs just the requirement-extraction step on
|
|
39
|
+
its own, so you can inspect or hand-edit the checklist before
|
|
40
|
+
`recommend_component` spends its search+score budget on it.
|
|
41
|
+
- `record_component_decision` — records what the agent actually did so
|
|
42
|
+
future recommendations in the same project can take that decision into
|
|
43
|
+
account.
|
|
19
44
|
|
|
20
45
|
## How it works
|
|
21
46
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+

|
|
48
|
+
|
|
49
|
+
For each `recommend_component` call, Pattern:
|
|
50
|
+
|
|
51
|
+
1. Checks whether the need is a simple primitive that doesn't require a
|
|
52
|
+
search.
|
|
53
|
+
2. Turns the request into a set of specific requirements, unless a
|
|
54
|
+
checklist was already supplied (see [`checklist`](#checklist)).
|
|
55
|
+
3. Searches for matching shadcn/ui, 21st.dev, and ReUI components.
|
|
56
|
+
4. Checks each candidate against the requirements using evidence from the
|
|
57
|
+
actual component.
|
|
58
|
+
5. Calculates how much of the requirement is covered.
|
|
59
|
+
6. Decides whether to use an existing component or build a custom one.
|
|
60
|
+
7. If a custom build is needed, searches Mobbin and Figma Community for
|
|
61
|
+
real product examples.
|
|
62
|
+
8. Returns the result as structured JSON the calling agent can act on.
|
|
63
|
+
|
|
64
|
+
Coverage is calculated by the server from the individual requirements it
|
|
65
|
+
checked. It does not simply trust the percentage returned by the model.
|
|
66
|
+
|
|
67
|
+
A result can also be:
|
|
68
|
+
|
|
69
|
+
- `use_existing`
|
|
70
|
+
- `custom_build`
|
|
71
|
+
- `no_candidates_found`
|
|
72
|
+
- `skip_list`
|
|
73
|
+
|
|
74
|
+
`no_candidates_found` is kept separate from a low-coverage result. Not
|
|
75
|
+
finding a candidate is different from finding candidates that don't cover
|
|
76
|
+
the requirements.
|
|
77
|
+
|
|
78
|
+
If `project_id` is supplied, Pattern also checks for past confirmed
|
|
79
|
+
decisions on that project and factors them in as a consistency signal —
|
|
80
|
+
never a rule that overrides a genuinely better match found in the current
|
|
81
|
+
search.
|
|
82
|
+
|
|
83
|
+
Every result includes `computed_at`, because coverage is a snapshot of the
|
|
84
|
+
search at that point in time, not a permanent fact. Every result also
|
|
85
|
+
includes `_meta` — the timing and token cost of that specific call (see
|
|
86
|
+
[Cost](#cost)).
|
|
87
|
+
|
|
88
|
+
### Boundary-risk checks
|
|
89
|
+
|
|
90
|
+
The same evidence can sometimes be judged slightly differently between
|
|
91
|
+
model runs. When a result is close enough to a decision threshold that it
|
|
92
|
+
could change the verdict, Pattern automatically runs the judgment two more
|
|
93
|
+
times and uses the majority result.
|
|
94
|
+
|
|
95
|
+
If the three runs disagree, Pattern returns:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"confidence": "low",
|
|
100
|
+
"ensemble": {
|
|
101
|
+
"triggered": true,
|
|
102
|
+
"runs": ["use_existing", "custom_build", "use_existing"],
|
|
103
|
+
"agreement": "2/3"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Results that are clearly inside a threshold don't trigger extra runs — see
|
|
109
|
+
[Cost](#cost) below for the measured impact.
|
|
110
|
+
|
|
111
|
+
### Simple primitives
|
|
112
|
+
|
|
113
|
+
These are handled locally without an API call:
|
|
114
|
+
|
|
115
|
+
| Primitive | Use it for |
|
|
116
|
+
| --- | --- |
|
|
117
|
+
| `button` | A clickable action trigger |
|
|
118
|
+
| `input` | A single-line text entry field |
|
|
119
|
+
| `checkbox` | A binary on/off toggle |
|
|
120
|
+
| `label` | A caption for a field or control |
|
|
121
|
+
| `badge` | A small status or count indicator |
|
|
122
|
+
| `spinner` | An indeterminate loading indicator |
|
|
123
|
+
| `tooltip` | A contextual hover/focus hint |
|
|
124
|
+
| `avatar` | A user or entity image, or initials |
|
|
125
|
+
| `icon` | A single glyph or symbol |
|
|
126
|
+
|
|
127
|
+
This keeps trivial requests fast and avoids unnecessary API usage.
|
|
128
|
+
|
|
129
|
+
### What powers the search
|
|
130
|
+
|
|
131
|
+
Pattern does not scrape shadcn/ui, 21st.dev, ReUI, Mobbin, or Figma Community
|
|
132
|
+
itself.
|
|
133
|
+
|
|
134
|
+
Each tool call makes one or more requests to the Anthropic Messages API,
|
|
135
|
+
using `claude-sonnet-5` by default. The server enables Anthropic's
|
|
136
|
+
`web_search` tool and provides a system prompt that defines the full
|
|
137
|
+
decision process.
|
|
138
|
+
|
|
139
|
+
That process includes:
|
|
140
|
+
|
|
141
|
+
- Skip-list checks
|
|
142
|
+
- Requirement extraction
|
|
143
|
+
- Component search
|
|
144
|
+
- Evidence-based coverage scoring
|
|
145
|
+
- Decision thresholds
|
|
146
|
+
- Mobbin and Figma Community reference searches when a custom build is
|
|
147
|
+
needed
|
|
148
|
+
|
|
149
|
+
Figma Community does not require a Figma API key. Pattern uses the same
|
|
150
|
+
web search mechanism for Figma Community as it does for the other sources.
|
|
151
|
+
|
|
152
|
+
The model returns structured JSON. Pattern then applies important checks
|
|
153
|
+
itself, including recalculating coverage and applying the decision
|
|
154
|
+
threshold.
|
|
155
|
+
|
|
156
|
+
## Quick Start
|
|
157
|
+
|
|
158
|
+
### 1. Install
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npm install pattern-mcp
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
This installs the `pattern-mcp` command via `npx` (or your project's
|
|
165
|
+
local `node_modules/.bin`), used in the client configs below.
|
|
166
|
+
|
|
167
|
+
<details>
|
|
168
|
+
<summary>Build from source instead</summary>
|
|
52
169
|
|
|
53
170
|
```bash
|
|
54
171
|
git clone <this repo>
|
|
@@ -57,104 +174,172 @@ npm install
|
|
|
57
174
|
npm run build
|
|
58
175
|
```
|
|
59
176
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
177
|
+
Use `node /absolute/path/to/pattern-mcp/dist/index.js` as the server
|
|
178
|
+
command in place of `npx pattern-mcp` in the examples below.
|
|
179
|
+
|
|
180
|
+
</details>
|
|
181
|
+
|
|
182
|
+
### 2. Add your Anthropic API key
|
|
183
|
+
|
|
184
|
+
Pattern requires:
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
ANTHROPIC_API_KEY
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The API account associated with this key pays for the requests Pattern
|
|
191
|
+
makes (see [Cost](#cost) below).
|
|
192
|
+
|
|
193
|
+
You get the key from the Anthropic Console under Settings → API Keys.
|
|
194
|
+
API billing is separate from Claude.ai or Claude Code subscriptions. A
|
|
195
|
+
Claude Pro or Max subscription does not include API usage.
|
|
196
|
+
|
|
197
|
+
### Connect Pattern to your MCP client
|
|
198
|
+
|
|
199
|
+
Pattern is a standard MCP server, so it works with MCP-compatible
|
|
200
|
+
clients.
|
|
201
|
+
|
|
202
|
+
The server command is:
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
npx pattern-mcp
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### Claude Code
|
|
209
|
+
|
|
210
|
+
You can add Pattern to your project's `.mcp.json` or register it with the
|
|
211
|
+
CLI.
|
|
212
|
+
|
|
213
|
+
For the current project:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
claude mcp add pattern \
|
|
217
|
+
-e ANTHROPIC_API_KEY=sk-ant-... \
|
|
218
|
+
-- npx pattern-mcp
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
This uses the default local scope, so the server is available to the
|
|
222
|
+
current project.
|
|
223
|
+
|
|
224
|
+
To make Pattern available across your projects:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
claude mcp add pattern \
|
|
228
|
+
-e ANTHROPIC_API_KEY=sk-ant-... \
|
|
229
|
+
--scope user \
|
|
230
|
+
-- npx pattern-mcp
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Important:** put `-e`/`--env` and `--scope` before the `--`. Everything
|
|
234
|
+
after `--` is treated as the command and its arguments.
|
|
235
|
+
|
|
236
|
+
Check the connection with:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
claude mcp list
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
You should see Pattern with a `✔ Connected` status.
|
|
243
|
+
|
|
244
|
+
`claude mcp add` stores the configuration in `~/.claude.json`. Avoid
|
|
245
|
+
`claude mcp get pattern` when possible because it can print your API key
|
|
246
|
+
in plaintext.
|
|
247
|
+
|
|
248
|
+
#### Cursor
|
|
249
|
+
|
|
250
|
+
Add Pattern to:
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
.cursor/mcp.json
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
#### Codex CLI
|
|
257
|
+
|
|
258
|
+
Pattern can be configured globally in:
|
|
259
|
+
|
|
260
|
+
```
|
|
261
|
+
~/.codex/config.toml
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
or at the project level in:
|
|
265
|
+
|
|
266
|
+
```
|
|
267
|
+
.codex/config.json
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Use the MCP configuration format supported by your Codex CLI version.
|
|
271
|
+
|
|
272
|
+
#### Claude Desktop
|
|
273
|
+
|
|
274
|
+
Add Pattern through Claude Desktop's MCP settings.
|
|
275
|
+
|
|
276
|
+
The configuration looks like:
|
|
109
277
|
|
|
110
278
|
```json
|
|
111
279
|
{
|
|
112
280
|
"mcpServers": {
|
|
113
281
|
"pattern": {
|
|
114
|
-
"command": "
|
|
115
|
-
"args": ["
|
|
116
|
-
"env": {
|
|
282
|
+
"command": "npx",
|
|
283
|
+
"args": ["pattern-mcp"],
|
|
284
|
+
"env": {
|
|
285
|
+
"ANTHROPIC_API_KEY": "sk-ant-..."
|
|
286
|
+
}
|
|
117
287
|
}
|
|
118
288
|
}
|
|
119
289
|
}
|
|
120
290
|
```
|
|
121
291
|
|
|
122
|
-
Restart your MCP client
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
292
|
+
Restart your MCP client after adding Pattern.
|
|
293
|
+
|
|
294
|
+
Then ask your agent to list its available MCP tools and look for:
|
|
295
|
+
|
|
296
|
+
```
|
|
297
|
+
recommend_component
|
|
298
|
+
```
|
|
126
299
|
|
|
127
300
|
## Try it
|
|
128
301
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
302
|
+
Give your agent a specific UI need, for example:
|
|
303
|
+
|
|
304
|
+
> Use recommend_component to find me a UI component for a price breakdown
|
|
305
|
+
> showing nightly rate, cleaning fee, service fee, and taxes. I'm building
|
|
306
|
+
> an Airbnb-style booking checkout in React with Tailwind.
|
|
307
|
+
|
|
308
|
+
The agent should use the result to make the next decision:
|
|
309
|
+
|
|
310
|
+
- Install or use the recommended component, or
|
|
311
|
+
- Start a custom build using the returned requirements and product
|
|
312
|
+
references.
|
|
313
|
+
|
|
314
|
+
Pattern returns useful descriptions for both paths.
|
|
315
|
+
|
|
316
|
+
- For an existing component, `component_description` explains what the
|
|
317
|
+
component does and looks like before the agent installs it.
|
|
318
|
+
- For a custom build, `reference_description` explains what each Mobbin
|
|
319
|
+
or Figma Community reference actually shows.
|
|
320
|
+
|
|
321
|
+
These descriptions are grounded in what Pattern found during the search
|
|
322
|
+
rather than generic descriptions.
|
|
323
|
+
|
|
324
|
+
## Validation examples
|
|
325
|
+
|
|
326
|
+
Pattern's validation suite uses five UI needs from an Airbnb-style rental
|
|
327
|
+
marketplace:
|
|
328
|
+
|
|
329
|
+
- Price breakdown with fees and taxes
|
|
330
|
+
- Cancellation policy display
|
|
331
|
+
- Host earnings dashboard
|
|
332
|
+
- Property image gallery
|
|
333
|
+
- Host-guest messaging inbox
|
|
334
|
+
|
|
335
|
+
Together, these cover different outcomes, including clear matches,
|
|
336
|
+
false-positive-prone searches, no candidates, and decisions close to the
|
|
337
|
+
threshold.
|
|
154
338
|
|
|
155
339
|
## Tool: `recommend_component`
|
|
156
340
|
|
|
157
|
-
|
|
341
|
+
### Input
|
|
342
|
+
|
|
158
343
|
```json
|
|
159
344
|
{
|
|
160
345
|
"component_need": "price breakdown with fees and taxes",
|
|
@@ -164,137 +349,250 @@ messaging inbox — all in the same Airbnb-style rental marketplace domain.
|
|
|
164
349
|
"project_id": "my-booking-app"
|
|
165
350
|
}
|
|
166
351
|
```
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
`
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
352
|
+
|
|
353
|
+
`component_need` should describe the actual UI you need, not just a
|
|
354
|
+
category.
|
|
355
|
+
|
|
356
|
+
Good: `price breakdown with fees and taxes`
|
|
357
|
+
Too vague: `pricing`
|
|
358
|
+
|
|
359
|
+
Vague requests can produce misleading matches. For example, a generic
|
|
360
|
+
SaaS pricing table may look like a match for "pricing" even though it
|
|
361
|
+
doesn't work for a booking checkout.
|
|
362
|
+
|
|
363
|
+
#### `project_id`
|
|
364
|
+
|
|
365
|
+
`project_id` is optional.
|
|
366
|
+
|
|
367
|
+
When provided, Pattern can use decisions previously recorded for the
|
|
368
|
+
same project (see [Per-project decision memory](#per-project-decision-memory))
|
|
369
|
+
as a consistency signal.
|
|
370
|
+
|
|
371
|
+
A previous decision can help the model stay consistent with similar UI
|
|
372
|
+
decisions, but it cannot override a better match found in the current
|
|
373
|
+
search.
|
|
374
|
+
|
|
375
|
+
Pattern still searches and scores every request from scratch. Past
|
|
376
|
+
decisions never cause a search to be skipped.
|
|
377
|
+
|
|
378
|
+
If you leave out `project_id`, Pattern does not use project memory.
|
|
379
|
+
|
|
380
|
+
#### `checklist`
|
|
381
|
+
|
|
382
|
+
`checklist` is optional -- an array of requirement strings.
|
|
383
|
+
|
|
384
|
+
When provided, `recommend_component` skips its own internal requirement
|
|
385
|
+
extraction entirely and scores coverage against exactly the items you
|
|
386
|
+
passed, instead of extracting its own checklist. Search and scoring still
|
|
387
|
+
run fresh every call; only the extraction step is skipped.
|
|
388
|
+
|
|
389
|
+
This is meant to be used together with [`extract_requirements`](#tool-extract_requirements):
|
|
390
|
+
call `extract_requirements` first, inspect (or hand-edit) the checklist it
|
|
391
|
+
returns, then pass that checklist here. That gives you a chance to catch a
|
|
392
|
+
misread requirement before Pattern spends its search+score budget.
|
|
393
|
+
|
|
394
|
+
Leave `checklist` out to keep today's default behavior: `recommend_component`
|
|
395
|
+
extracts its own checklist internally, exactly as before this option
|
|
396
|
+
existed.
|
|
397
|
+
|
|
398
|
+
**Is the checklist actually skipped, not just re-derived?** Checked, not
|
|
399
|
+
assumed. `breakdown_ms.extract` for a `checklist`-provided call is smaller
|
|
400
|
+
than the default path's, but not near-zero -- which raised the question of
|
|
401
|
+
whether the model is still doing some of the extraction work in that
|
|
402
|
+
window rather than treating the checklist as fixed input. Reading the
|
|
403
|
+
model's actual reasoning (via `thinking` with `display: "summarized"`,
|
|
404
|
+
5 runs: 3 with `checklist` provided, 2 default) answered it: the
|
|
405
|
+
`checklist`-provided runs' pre-search reasoning was a short, generic
|
|
406
|
+
"search shadcn/ui and 21st.dev" thought with no mention of the checklist's
|
|
407
|
+
content, e.g. *"I should look for existing image gallery component options
|
|
408
|
+
on shadcn/ui and 21st.dev"* -- consistently ~3-4 seconds. The default
|
|
409
|
+
runs' reasoning, by contrast, explicitly enumerated and derived the
|
|
410
|
+
checklist items (*"...mapping out the checklist: a photo grid with hero
|
|
411
|
+
and thumbnails... a full-screen lightbox with next/prev navigation,
|
|
412
|
+
keyboard support..."*) and took roughly 2x longer (~7-8 seconds). The
|
|
413
|
+
remaining time in the `checklist`-provided path is baseline model latency
|
|
414
|
+
before it decides to search, not re-extraction -- it doesn't scale with or
|
|
415
|
+
reference the checklist's content.
|
|
416
|
+
|
|
417
|
+
### Output
|
|
418
|
+
|
|
186
419
|
```json
|
|
187
420
|
{
|
|
188
421
|
"verdict": "use_existing | custom_build",
|
|
189
422
|
"confidence": "high | medium | low",
|
|
190
423
|
"reason": "scored | no_candidates_found | skip_list",
|
|
191
424
|
"computed_at": "2026-08-23",
|
|
192
|
-
"requirements_checked": [
|
|
425
|
+
"requirements_checked": [
|
|
426
|
+
{
|
|
427
|
+
"requirement": "...",
|
|
428
|
+
"met": true,
|
|
429
|
+
"evidence": "..."
|
|
430
|
+
}
|
|
431
|
+
],
|
|
193
432
|
"coverage": "5/7 (71%)",
|
|
194
433
|
"recommendation": {
|
|
195
|
-
"source": "21st.dev | shadcn | null",
|
|
434
|
+
"source": "21st.dev | shadcn | reui | null",
|
|
196
435
|
"install_command": "string | null",
|
|
197
|
-
"component_description": "string
|
|
436
|
+
"component_description": "string | null",
|
|
198
437
|
"reference": {
|
|
199
438
|
"source": "Mobbin | Figma Community",
|
|
200
439
|
"url": "...",
|
|
201
|
-
"flow_name": "...
|
|
202
|
-
"file_name": "...
|
|
440
|
+
"flow_name": "...",
|
|
441
|
+
"file_name": "...",
|
|
203
442
|
"reference_description": "...",
|
|
204
443
|
"url_type": "deep_link | entry_point"
|
|
205
444
|
}
|
|
206
445
|
},
|
|
207
|
-
"ensemble": {
|
|
208
|
-
|
|
446
|
+
"ensemble": {
|
|
447
|
+
"triggered": false
|
|
448
|
+
},
|
|
449
|
+
"checklist_source": "extracted | provided",
|
|
450
|
+
"_meta": {
|
|
451
|
+
"total_ms": 41516,
|
|
452
|
+
"breakdown_ms": { "extract": 5006, "search": 3114, "score": 33396 },
|
|
453
|
+
"tokens_used": { "input": 8400, "output": 620 },
|
|
454
|
+
"estimated_cost_usd": 0.14
|
|
455
|
+
}
|
|
209
456
|
}
|
|
210
457
|
```
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
`
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
458
|
+
|
|
459
|
+
The `past_decision_signal` field is included only when there is a
|
|
460
|
+
relevant previous decision for the supplied `project_id`.
|
|
461
|
+
|
|
462
|
+
`checklist_source` is always present: `"extracted"` when Pattern derived
|
|
463
|
+
the checklist itself (the default, unchanged behavior), `"provided"` when
|
|
464
|
+
you passed one in via `checklist`.
|
|
465
|
+
|
|
466
|
+
`_meta` is always present. See [Cost](#cost) for what each field means,
|
|
467
|
+
how `breakdown_ms` is measured, and what it means when the ensemble
|
|
468
|
+
triggers.
|
|
469
|
+
|
|
470
|
+
### Reference links
|
|
471
|
+
|
|
472
|
+
When Pattern recommends a custom build, it may return references from
|
|
473
|
+
Mobbin, Figma Community, or both.
|
|
474
|
+
|
|
475
|
+
The `reference` field can be:
|
|
476
|
+
|
|
477
|
+
- An array when both sources returned useful results.
|
|
478
|
+
- A single object when only one source returned a useful result.
|
|
479
|
+
- `null` when neither source produced a grounded reference.
|
|
480
|
+
|
|
481
|
+
#### Deep links vs. entry points
|
|
482
|
+
|
|
483
|
+
Pattern tells you whether a reference URL points directly to the
|
|
484
|
+
identified screen or flow.
|
|
485
|
+
|
|
486
|
+
`"url_type": "deep_link"` means Pattern verified that the URL points to
|
|
487
|
+
the specific reference.
|
|
488
|
+
|
|
489
|
+
`"url_type": "entry_point"` means the URL is a search or browse page. The
|
|
490
|
+
agent may need to find the specific screen or flow from there.
|
|
491
|
+
|
|
492
|
+
For Mobbin, Pattern fetches the search result page and looks for a more
|
|
493
|
+
specific link to the screen or flow it identified.
|
|
494
|
+
|
|
495
|
+
For Figma Community, URLs containing `/community/file/` are already
|
|
496
|
+
specific to a file and are treated as deep links. Other Figma URLs are
|
|
497
|
+
checked like Mobbin URLs.
|
|
498
|
+
|
|
499
|
+
Pattern never invents a URL. If it cannot verify a specific link, it
|
|
500
|
+
keeps the real search result URL and clearly identifies it as an entry
|
|
501
|
+
point.
|
|
502
|
+
|
|
503
|
+
### Installation commands are not trusted
|
|
504
|
+
|
|
505
|
+
The `install_command` comes from search results. It is not verified
|
|
506
|
+
against a package registry, and Pattern does not execute it.
|
|
507
|
+
|
|
508
|
+
The calling agent should:
|
|
509
|
+
|
|
510
|
+
1. Show the command to the user.
|
|
511
|
+
2. Get confirmation.
|
|
512
|
+
3. Run it only after confirmation.
|
|
513
|
+
|
|
514
|
+
See [SECURITY.md](./SECURITY.md) for more details.
|
|
515
|
+
|
|
516
|
+
## Tool: `extract_requirements`
|
|
517
|
+
|
|
518
|
+
Runs only the requirement-extraction step `recommend_component` normally
|
|
519
|
+
does internally, and returns just the checklist -- no search, no scoring,
|
|
520
|
+
no verdict.
|
|
521
|
+
|
|
522
|
+
This is an opt-in, two-call pattern for agents that support tool search or
|
|
523
|
+
code-mode style tool use: call `extract_requirements` first, inspect (or
|
|
524
|
+
hand-edit) the checklist it returns, then pass that checklist to
|
|
525
|
+
`recommend_component`'s optional `checklist` input to score against it
|
|
526
|
+
directly, skipping `recommend_component`'s own internal extraction.
|
|
527
|
+
|
|
528
|
+
The single-call default -- just calling `recommend_component` with no
|
|
529
|
+
`checklist` -- is unchanged and is still the recommended path for most
|
|
530
|
+
callers. Reach for `extract_requirements` when you specifically want to
|
|
531
|
+
catch a misread requirement before Pattern spends its search+score budget,
|
|
532
|
+
not as a routine first step.
|
|
533
|
+
|
|
534
|
+
### Input
|
|
535
|
+
|
|
536
|
+
```json
|
|
537
|
+
{
|
|
538
|
+
"component_need": "image gallery for a property listing",
|
|
539
|
+
"domain": "Airbnb-style rental marketplace"
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
Same fields, same meaning, as `recommend_component`'s `component_need` and
|
|
544
|
+
`domain`. There is no `framework` input here -- extraction is grounded in
|
|
545
|
+
the domain, not the framework, so `framework` doesn't affect the checklist
|
|
546
|
+
in `recommend_component` either.
|
|
547
|
+
|
|
548
|
+
### Output
|
|
549
|
+
|
|
550
|
+
```json
|
|
551
|
+
{
|
|
552
|
+
"checklist": ["...", "...", "..."],
|
|
553
|
+
"extraction_confidence": "high | medium | low",
|
|
554
|
+
"_meta": {
|
|
555
|
+
"total_ms": 6798,
|
|
556
|
+
"breakdown_ms": { "extract": 6798, "search": 0, "score": 0 },
|
|
557
|
+
"tokens_used": { "input": 275, "output": 302 },
|
|
558
|
+
"estimated_cost_usd": 0.0036
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
Typical latency is a few seconds -- one small API call with no tools
|
|
564
|
+
declared, versus `recommend_component`'s full search+score pipeline.
|
|
565
|
+
|
|
566
|
+
**`extraction_confidence` is a placeholder heuristic, not a validated
|
|
567
|
+
signal.** It's currently derived from how specific `component_need` is
|
|
568
|
+
(word count) -- the same "vague category name" problem the rest of this
|
|
569
|
+
README warns about elsewhere. It is not based on any measured correlation
|
|
570
|
+
with actual extraction quality. Treat `"low"` as a prompt to reread your
|
|
571
|
+
`component_need`, not as a calibrated confidence score. This is flagged
|
|
572
|
+
here as a known gap, to revisit once there's real usage data to base a
|
|
573
|
+
better signal on.
|
|
574
|
+
|
|
575
|
+
Trivial primitives (see [Simple primitives](#simple-primitives)) return an
|
|
576
|
+
empty `checklist` with `extraction_confidence: "high"` and no API call, the
|
|
577
|
+
same local skip-list short-circuit `recommend_component` uses.
|
|
287
578
|
|
|
288
579
|
## Tool: `record_component_decision`
|
|
289
580
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
581
|
+
Use this tool after the agent has actually acted on a component
|
|
582
|
+
decision.
|
|
583
|
+
|
|
584
|
+
For example, call it after:
|
|
585
|
+
|
|
586
|
+
- Installing an existing component
|
|
587
|
+
- Completing a custom build
|
|
588
|
+
|
|
589
|
+
Do not call it for every recommendation.
|
|
590
|
+
|
|
591
|
+
The tool only saves the decision. It does not run a judgment or make an
|
|
592
|
+
Anthropic API call.
|
|
593
|
+
|
|
594
|
+
### Input
|
|
296
595
|
|
|
297
|
-
**Input:**
|
|
298
596
|
```json
|
|
299
597
|
{
|
|
300
598
|
"project_id": "my-booking-app",
|
|
@@ -305,32 +603,38 @@ effectively free and instant.
|
|
|
305
603
|
"timestamp": "2026-08-25T14:32:00.000Z"
|
|
306
604
|
}
|
|
307
605
|
```
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
- `
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
- `timestamp` (optional) — ISO 8601; defaults to the current time if
|
|
318
|
-
omitted.
|
|
319
|
-
|
|
320
|
-
**Output:**
|
|
606
|
+
|
|
607
|
+
- `project_id` is required and should be stable. A project directory
|
|
608
|
+
path or project name works well.
|
|
609
|
+
- `action` must be `"installed"` or `"custom_built"`.
|
|
610
|
+
- `source` can be `"shadcn"`, `"21st.dev"`, `"reui"`, or `"custom"`.
|
|
611
|
+
- `timestamp` is optional. If omitted, Pattern uses the current time.
|
|
612
|
+
|
|
613
|
+
### Output
|
|
614
|
+
|
|
321
615
|
```json
|
|
322
|
-
{
|
|
616
|
+
{
|
|
617
|
+
"status": "recorded",
|
|
618
|
+
"project_id": "my-booking-app",
|
|
619
|
+
"entry": { "..." }
|
|
620
|
+
}
|
|
323
621
|
```
|
|
324
622
|
|
|
325
623
|
## Per-project decision memory
|
|
326
624
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
625
|
+
Pattern stores confirmed decisions locally in:
|
|
626
|
+
|
|
627
|
+
```
|
|
628
|
+
~/.pattern/memory.json
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
You can change the location with:
|
|
632
|
+
|
|
633
|
+
```
|
|
634
|
+
PATTERN_MEMORY_PATH
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
The file is organized by project:
|
|
334
638
|
|
|
335
639
|
```json
|
|
336
640
|
{
|
|
@@ -346,198 +650,460 @@ that's the key):
|
|
|
346
650
|
}
|
|
347
651
|
```
|
|
348
652
|
|
|
349
|
-
Each project
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
653
|
+
Each project keeps its 50 most recent decisions. Older entries are
|
|
654
|
+
removed as new ones are added.
|
|
655
|
+
|
|
656
|
+
Only decisions explicitly recorded through `record_component_decision`
|
|
657
|
+
are saved. Pattern does not automatically save recommendations.
|
|
658
|
+
|
|
659
|
+
If an agent ignores or changes a recommendation, nothing is recorded
|
|
660
|
+
unless the agent explicitly calls `record_component_decision` with what
|
|
661
|
+
it actually did.
|
|
662
|
+
|
|
663
|
+
The memory file is local plaintext. Pattern does not send it anywhere.
|
|
664
|
+
|
|
665
|
+
`component_need` and `domain` are stored in this file, so avoid putting
|
|
666
|
+
sensitive information in them. See [SECURITY.md](./SECURITY.md).
|
|
667
|
+
|
|
668
|
+
A failure to write the decision file is returned as an error from
|
|
669
|
+
`record_component_decision`.
|
|
670
|
+
|
|
671
|
+
**No caching, by design.** Project memory does not cache recommendations.
|
|
672
|
+
A previous decision is only additional context for a new judgment. Every
|
|
673
|
+
`recommend_component` call performs a fresh search and recalculates
|
|
674
|
+
coverage. This means Pattern can use past decisions to improve
|
|
675
|
+
consistency without letting stale decisions replace current evidence —
|
|
676
|
+
see [Known limitations](#known-limitations) for more.
|
|
677
|
+
|
|
678
|
+
## Security and privacy
|
|
679
|
+
|
|
680
|
+
Pattern uses the Anthropic API and web search to make its
|
|
681
|
+
recommendations.
|
|
682
|
+
|
|
683
|
+
Local project memory and the local call log are stored on the machine
|
|
684
|
+
running Pattern. They are not sent anywhere by Pattern itself.
|
|
685
|
+
|
|
686
|
+
Review [SECURITY.md](./SECURITY.md) before putting sensitive information
|
|
687
|
+
into fields such as `component_need`, `domain`, or project IDs.
|
|
378
688
|
|
|
379
689
|
## Cost
|
|
380
690
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
691
|
+
Pattern uses the Anthropic API, so `recommend_component` has a cost.
|
|
692
|
+
|
|
693
|
+
A typical single pass costs about $0.06–$0.10 with Sonnet 5 at current
|
|
694
|
+
pricing. Skip-listed primitives cost $0 because they're handled locally
|
|
695
|
+
and never reach the API.
|
|
696
|
+
|
|
697
|
+
### The `_meta` field
|
|
698
|
+
|
|
699
|
+
Every `recommend_component` and `extract_requirements` response includes
|
|
700
|
+
an internal `_meta` block reporting what that call actually spent:
|
|
701
|
+
|
|
702
|
+
```json
|
|
703
|
+
{
|
|
704
|
+
"total_ms": 41516,
|
|
705
|
+
"breakdown_ms": { "extract": 5006, "search": 3114, "score": 33396 },
|
|
706
|
+
"tokens_used": { "input": 8400, "output": 620 },
|
|
707
|
+
"estimated_cost_usd": 0.14
|
|
708
|
+
}
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
- `total_ms` -- wall-clock time for the call.
|
|
712
|
+
- `tokens_used` -- total input tokens (fresh + cache write + cache read,
|
|
713
|
+
summed) and output tokens, read directly from the API response's own
|
|
714
|
+
usage data.
|
|
715
|
+
- `estimated_cost_usd` -- computed from `tokens_used` at Pattern's
|
|
716
|
+
configured model's current per-token rate (checked against Anthropic's
|
|
717
|
+
pricing, not assumed). This is an estimate: it doesn't account for
|
|
718
|
+
pricing changes Pattern hasn't been updated for, or any account-specific
|
|
719
|
+
discounts.
|
|
720
|
+
- `breakdown_ms` -- how `total_ms` splits across `recommend_component`'s
|
|
721
|
+
three internal phases.
|
|
722
|
+
|
|
723
|
+
**How `breakdown_ms` is measured, and its one real caveat.** The bundled
|
|
724
|
+
call runs extraction, search, and scoring inside a single model turn
|
|
725
|
+
(search/fetch happen server-side, not as separate requests this code
|
|
726
|
+
makes), so there's no natural place for three separate stopwatches.
|
|
727
|
+
Pattern gets a real per-phase split by streaming the response and timing
|
|
728
|
+
content-block boundaries instead: `extract` ends the moment the first
|
|
729
|
+
search call starts, and `search` ends when that first wave of search
|
|
730
|
+
calls and results finishes. This was checked against real traces (not
|
|
731
|
+
assumed) across both `use_existing` and `custom_build` cases before
|
|
732
|
+
shipping, and both boundaries land cleanly and consistently.
|
|
733
|
+
|
|
734
|
+
The one place this needs a caveat: for a `custom_build` verdict, step 6's
|
|
735
|
+
Mobbin/Figma reference search and its deep-link verification fetch happen
|
|
736
|
+
*after* the coverage-scoring reasoning that decided `custom_build` in the
|
|
737
|
+
first place -- so `breakdown_ms.score`, for those cases, covers coverage
|
|
738
|
+
scoring **and** reference-finding **and** the final write-up, not just
|
|
739
|
+
"scoring" in the narrow step-4 sense. It's still a real, measured number;
|
|
740
|
+
it's just a wider bucket for `custom_build` than for `use_existing`. This
|
|
741
|
+
is disclosed here rather than presented as a narrower number than it is.
|
|
742
|
+
|
|
743
|
+
**When the ensemble triggers** (see below), `_meta` reports the sum
|
|
744
|
+
across all reruns that actually happened -- total tokens and cost spent,
|
|
745
|
+
not the wall-clock time you waited. The three ensemble passes run with the
|
|
746
|
+
2nd and 3rd concurrent, so perceived latency is closer to ~2x one pass,
|
|
747
|
+
not the ~3x `total_ms` will show. Cost and token spend are genuinely
|
|
748
|
+
additive across reruns, which is what `_meta` is reporting there.
|
|
749
|
+
|
|
750
|
+
Three things help keep the cost down without changing the decision process.
|
|
751
|
+
|
|
752
|
+
### Prompt caching
|
|
753
|
+
|
|
754
|
+
Pattern caches its system instructions using `cache_control: ephemeral`.
|
|
755
|
+
|
|
756
|
+
The instructions are the same across calls, so repeated requests don't
|
|
757
|
+
pay the full input cost for that block.
|
|
758
|
+
|
|
759
|
+
### Search limits
|
|
760
|
+
|
|
761
|
+
Pattern limits candidate discovery to 3 web searches -- one per source.
|
|
762
|
+
|
|
763
|
+
If a custom build is needed, it reserves 2 additional searches for
|
|
764
|
+
references:
|
|
765
|
+
|
|
766
|
+
- 1 for Mobbin
|
|
767
|
+
- 1 for Figma Community
|
|
768
|
+
|
|
769
|
+
shadcn/ui, 21st.dev, and ReUI are searched in the same turn rather than
|
|
770
|
+
sequentially, which reduces how much conversation context needs to be
|
|
771
|
+
sent repeatedly.
|
|
772
|
+
|
|
773
|
+
### Reference verification
|
|
774
|
+
|
|
775
|
+
Pattern allows up to 2 `web_fetch` calls, used only to verify reference
|
|
776
|
+
URLs.
|
|
777
|
+
|
|
778
|
+
A fetch can read up to 15,000 content tokens. `web_fetch` has no separate
|
|
779
|
+
per-call fee; the cost comes from the content added to the model's
|
|
780
|
+
context.
|
|
781
|
+
|
|
782
|
+
Pattern does not use `web_fetch` during requirement scoring. It's
|
|
783
|
+
reserved for verifying reference links.
|
|
784
|
+
|
|
785
|
+
### Choosing a cheaper model
|
|
786
|
+
|
|
787
|
+
You can change the model with:
|
|
788
|
+
|
|
789
|
+
```
|
|
790
|
+
PATTERN_MODEL
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
It defaults to:
|
|
794
|
+
|
|
795
|
+
```
|
|
796
|
+
claude-sonnet-5
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
You could use a cheaper model such as Haiku 4.5 without changing the code.
|
|
800
|
+
|
|
801
|
+
Before using a cheaper model in production, run the five validation cases
|
|
802
|
+
and compare its results with Sonnet's:
|
|
803
|
+
|
|
804
|
+
- Price breakdown
|
|
805
|
+
- Cancellation policy
|
|
806
|
+
- Earnings dashboard
|
|
807
|
+
- Image gallery
|
|
808
|
+
- Messaging inbox
|
|
809
|
+
|
|
810
|
+
The cheaper model hasn't been validated yet, so these results should be
|
|
811
|
+
treated as an open question rather than an established performance claim.
|
|
409
812
|
|
|
410
813
|
### Ensemble cost (boundary-risk cases only)
|
|
411
814
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
815
|
+
Pattern uses extra model calls only when a result is close enough to a
|
|
816
|
+
decision threshold that a small change in judgment could change the
|
|
817
|
+
verdict.
|
|
818
|
+
|
|
819
|
+
The requirement checklist has eight items, so coverage can only land on
|
|
820
|
+
these values:
|
|
821
|
+
|
|
822
|
+
```
|
|
823
|
+
0%
|
|
824
|
+
12.5%
|
|
825
|
+
25%
|
|
826
|
+
37.5%
|
|
827
|
+
50%
|
|
828
|
+
62.5%
|
|
829
|
+
75%
|
|
830
|
+
87.5%
|
|
831
|
+
100%
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
The decision thresholds are 40% and 80%.
|
|
835
|
+
|
|
836
|
+
That means results at 37.5%, 50%, 75%, and 87.5% are the cases where
|
|
837
|
+
changing the judgment on one requirement can flip the verdict.
|
|
838
|
+
|
|
839
|
+
For those cases, Pattern runs the full judgment three times and takes
|
|
840
|
+
the majority result.
|
|
841
|
+
|
|
842
|
+
For example:
|
|
843
|
+
|
|
844
|
+
```json
|
|
845
|
+
{
|
|
846
|
+
"ensemble": {
|
|
847
|
+
"triggered": true,
|
|
848
|
+
"runs": ["use_existing", "custom_build", "use_existing"],
|
|
849
|
+
"agreement": "2/3"
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
If all three runs agree, the majority verdict is returned normally.
|
|
855
|
+
|
|
856
|
+
If they split 2/3, Pattern sets confidence to `"low"`. The disagreement
|
|
857
|
+
is surfaced rather than hidden.
|
|
858
|
+
|
|
859
|
+
Results at 0, 12.5, 25, 62.5, and 100% stay single-pass because one
|
|
860
|
+
changed requirement can't move them across either threshold.
|
|
861
|
+
|
|
862
|
+
### Measured ensemble cost
|
|
863
|
+
|
|
864
|
+
The ensemble doesn't mean every call costs 3x.
|
|
865
|
+
|
|
866
|
+
In the latest five-case validation, Pattern made 15 outer calls:
|
|
867
|
+
|
|
868
|
+
- 8 stayed single-pass
|
|
869
|
+
- 7 triggered the ensemble
|
|
870
|
+
- 21 model passes were used for those 7 ensemble calls
|
|
871
|
+
- 29 total model calls across the test
|
|
872
|
+
|
|
873
|
+
That works out to about a 1.9x average multiplier across that test set.
|
|
874
|
+
|
|
875
|
+
The worst case is still 3x for an individual call when the ensemble is
|
|
876
|
+
triggered.
|
|
877
|
+
|
|
878
|
+
### What the ensemble can and cannot solve
|
|
879
|
+
|
|
880
|
+
The ensemble reduces the chance that one unlucky model judgment
|
|
881
|
+
determines the result. It doesn't eliminate uncertainty.
|
|
882
|
+
|
|
883
|
+
If the underlying evidence is genuinely ambiguous, three runs can still
|
|
884
|
+
disagree.
|
|
885
|
+
|
|
886
|
+
For example, the image-gallery validation case continued to flip between
|
|
887
|
+
outer runs. When that happened, the ensemble consistently reported a 2/3
|
|
888
|
+
split with `confidence: "low"`.
|
|
889
|
+
|
|
890
|
+
That's expected behavior: the tool is exposing uncertainty instead of
|
|
891
|
+
presenting an ambiguous result as certain.
|
|
454
892
|
|
|
455
893
|
### Session call cap
|
|
456
894
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
895
|
+
Pattern limits the number of API calls to 40 per server process by
|
|
896
|
+
default.
|
|
897
|
+
|
|
898
|
+
You can change this with:
|
|
899
|
+
|
|
900
|
+
```
|
|
901
|
+
PATTERN_SESSION_CAP
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
The cap protects against runaway agents, such as an agent stuck in a
|
|
905
|
+
retry loop or repeatedly asking for the same recommendation.
|
|
906
|
+
|
|
907
|
+
The 40-call default is based on the project's validation work. A
|
|
908
|
+
realistic project with roughly 25 components would use about 25 calls
|
|
909
|
+
for a full pass, leaving room for iteration.
|
|
910
|
+
|
|
911
|
+
Skip-listed primitives don't count because they never reach the API.
|
|
912
|
+
|
|
913
|
+
The counter lives in memory and resets when the server restarts.
|
|
914
|
+
|
|
915
|
+
If 40 calls is too low for your project, increase `PATTERN_SESSION_CAP`
|
|
916
|
+
rather than repeatedly restarting the server.
|
|
470
917
|
|
|
471
918
|
## Local call log
|
|
472
919
|
|
|
473
|
-
Every call
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
920
|
+
Every API call is recorded in a local log.
|
|
921
|
+
|
|
922
|
+
By default:
|
|
923
|
+
|
|
924
|
+
```
|
|
925
|
+
~/.pattern/calls.log
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
You can change the location with:
|
|
929
|
+
|
|
930
|
+
```
|
|
931
|
+
PATTERN_LOG_PATH
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
The log is local. Pattern does not send it anywhere.
|
|
935
|
+
|
|
936
|
+
Each API call adds one JSON line, for example:
|
|
479
937
|
|
|
480
|
-
Each line looks like:
|
|
481
938
|
```json
|
|
482
|
-
{
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
`
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
939
|
+
{
|
|
940
|
+
"timestamp": "2026-08-24T21:12:43.882Z",
|
|
941
|
+
"component_need": "cancellation policy display",
|
|
942
|
+
"domain": "Airbnb-style rental marketplace",
|
|
943
|
+
"framework": "React + Tailwind",
|
|
944
|
+
"verdict": "custom_build",
|
|
945
|
+
"confidence": "high",
|
|
946
|
+
"reason": "scored",
|
|
947
|
+
"coverage": "2/8 (25%)",
|
|
948
|
+
"ensemble_triggered": false,
|
|
949
|
+
"reference_sources_grounded": ["Mobbin", "Figma Community"],
|
|
950
|
+
"checklist_source": "extracted",
|
|
951
|
+
"total_ms": 44834,
|
|
952
|
+
"estimated_cost_usd": 0.15
|
|
953
|
+
}
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
Additional fields appear when relevant:
|
|
957
|
+
|
|
958
|
+
- `ensemble_agreement` appears when the ensemble runs.
|
|
959
|
+
- `reference_sources_grounded` appears for `custom_build` results and
|
|
960
|
+
lists only sources that produced a grounded reference.
|
|
961
|
+
|
|
962
|
+
`checklist_source`, `total_ms`, and `estimated_cost_usd` mirror the
|
|
963
|
+
call's `_meta` block (see [Cost](#cost)) -- `total_ms` and
|
|
964
|
+
`estimated_cost_usd` are the same aggregated-across-reruns numbers when
|
|
965
|
+
the ensemble triggers, not per-pass figures.
|
|
966
|
+
|
|
967
|
+
The log deliberately does not contain:
|
|
968
|
+
|
|
969
|
+
- The full `requirements_checked` evidence
|
|
970
|
+
- Your Anthropic API key
|
|
971
|
+
|
|
972
|
+
It does contain `component_need` and `domain`, so avoid putting sensitive
|
|
973
|
+
information in those fields. See [SECURITY.md](./SECURITY.md).
|
|
974
|
+
|
|
975
|
+
The log directory is created automatically.
|
|
976
|
+
|
|
977
|
+
If Pattern cannot write to the log because of permissions, a read-only
|
|
978
|
+
filesystem, or a full disk, it reports the problem to stderr but does not
|
|
979
|
+
fail the tool call.
|
|
980
|
+
|
|
981
|
+
### Review a log
|
|
982
|
+
|
|
983
|
+
You can summarize a log with:
|
|
984
|
+
|
|
985
|
+
```
|
|
986
|
+
node summarize-log.js [path]
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
If no path is provided, it uses the same default location as the server.
|
|
990
|
+
|
|
991
|
+
The summary includes:
|
|
992
|
+
|
|
993
|
+
- Verdict and confidence breakdown
|
|
994
|
+
- Reason breakdown
|
|
995
|
+
- Ensemble trigger and agreement rates
|
|
996
|
+
- Reference-source grounding rates for custom builds
|
|
997
|
+
- Component needs that were requested more than once
|
|
998
|
+
|
|
999
|
+
Repeated component needs can be useful to investigate alongside the
|
|
1000
|
+
[session call cap](#session-call-cap).
|
|
1001
|
+
|
|
1002
|
+
## Known limitations
|
|
1003
|
+
|
|
1004
|
+
### Model judgment can vary
|
|
1005
|
+
|
|
1006
|
+
Pattern's search results can stay the same while the model's
|
|
1007
|
+
interpretation of those results changes between runs.
|
|
1008
|
+
|
|
1009
|
+
Validation found cases where two runs found the same named components
|
|
1010
|
+
using the same search queries but judged the same evidence differently.
|
|
1011
|
+
|
|
1012
|
+
For example, the model interpreted an Export action as present in one
|
|
1013
|
+
run and absent in another.
|
|
1014
|
+
|
|
1015
|
+
This is a limitation of model-based evidence judgment, not necessarily a
|
|
1016
|
+
search or code problem.
|
|
1017
|
+
|
|
1018
|
+
The boundary-risk ensemble exists to detect and surface this uncertainty.
|
|
1019
|
+
|
|
1020
|
+
### A staged pipeline was evaluated and not adopted
|
|
1021
|
+
|
|
1022
|
+
To address the variance above, an alternative architecture was built and
|
|
1023
|
+
tested: splitting the single bundled judgment call into separate stages
|
|
1024
|
+
(extract requirements, search evidence, score coverage), on the theory
|
|
1025
|
+
that isolating each step would make results more consistent and easier
|
|
1026
|
+
to diagnose.
|
|
1027
|
+
|
|
1028
|
+
A pilot comparison (5 cases, 3 repeated runs per case, per
|
|
1029
|
+
architecture) found no consistent benefit. The staged pipeline improved
|
|
1030
|
+
consistency on one boundary-risk case but was less consistent than the
|
|
1031
|
+
bundled pipeline on another, including one run that failed outright.
|
|
1032
|
+
Net accuracy against hand-graded gold answers was statistically
|
|
1033
|
+
indistinguishable between the two architectures, and the staged
|
|
1034
|
+
pipeline cost roughly **2x** the bundled pipeline's call volume across
|
|
1035
|
+
the board, not only on the boundary-risk cases it was expected to help
|
|
1036
|
+
most.
|
|
1037
|
+
|
|
1038
|
+
Pattern ships the bundled pipeline. The staged implementation remains
|
|
1039
|
+
in the repo (`src/staged/`) as an evaluated, unshipped experiment, not
|
|
1040
|
+
a supported alternative.
|
|
1041
|
+
|
|
1042
|
+
**`extract_requirements` is not a revival of this.** It's a standalone
|
|
1043
|
+
tool for inspecting the extraction step's output before an agent commits
|
|
1044
|
+
to `recommend_component`'s search+score budget -- an opt-in visibility
|
|
1045
|
+
tool, not an internal re-architecture. `recommend_component`'s own
|
|
1046
|
+
pipeline is still fully bundled; nothing about this evaluation changed.
|
|
1047
|
+
|
|
1048
|
+
### No caching, by design
|
|
1049
|
+
|
|
1050
|
+
Every recommendation searches and scores again.
|
|
1051
|
+
|
|
1052
|
+
This means a recommendation can change as component libraries change.
|
|
1053
|
+
For example, a later shadcn/ui release can introduce a component that
|
|
1054
|
+
changes a previous `custom_build` result.
|
|
1055
|
+
|
|
1056
|
+
Do not persist a recommendation across sessions or builds at the
|
|
1057
|
+
calling-agent layer.
|
|
1058
|
+
|
|
1059
|
+
If you add caching, keep it session-scoped.
|
|
1060
|
+
|
|
1061
|
+
[Project decision memory](#per-project-decision-memory) does not change
|
|
1062
|
+
this. It provides context from previous decisions, but every
|
|
1063
|
+
`recommend_component` call still performs a fresh search and scoring
|
|
1064
|
+
pass.
|
|
1065
|
+
|
|
1066
|
+
### The skip-list is still evolving
|
|
1067
|
+
|
|
1068
|
+
The primitive skip-list is a starting point and has not yet been
|
|
1069
|
+
validated against broad real-world usage.
|
|
1070
|
+
|
|
1071
|
+
Watch for two failure modes:
|
|
1072
|
+
|
|
1073
|
+
- Agents calling Pattern for things that should have been skipped.
|
|
1074
|
+
- Agents building generic UI for something that should have been on the
|
|
1075
|
+
skip-list.
|
|
1076
|
+
|
|
1077
|
+
The local call log can help identify both patterns.
|
|
1078
|
+
|
|
1079
|
+
### Pattern needs internet access
|
|
1080
|
+
|
|
1081
|
+
Pattern requires outbound access to:
|
|
1082
|
+
|
|
1083
|
+
```
|
|
1084
|
+
api.anthropic.com
|
|
1085
|
+
```
|
|
1086
|
+
|
|
1087
|
+
It also depends on whatever external sites the model's `web_search` tool
|
|
1088
|
+
can reach.
|
|
1089
|
+
|
|
1090
|
+
It will not work in an environment that blocks general outbound internet
|
|
1091
|
+
access.
|
|
1092
|
+
|
|
1093
|
+
### Requirements and coverage are judgment calls
|
|
1094
|
+
|
|
1095
|
+
Requirement extraction and evidence scoring are performed by the model.
|
|
1096
|
+
|
|
1097
|
+
Pattern adds safeguards such as:
|
|
1098
|
+
|
|
1099
|
+
- Structured requirements
|
|
1100
|
+
- Server-side coverage recalculation
|
|
1101
|
+
- Decision thresholds
|
|
1102
|
+
- Boundary-risk ensembling
|
|
1103
|
+
- Grounding checks for reference URLs
|
|
1104
|
+
|
|
1105
|
+
But the underlying interpretation of whether evidence satisfies a
|
|
1106
|
+
requirement is still model judgment.
|
|
1107
|
+
|
|
1108
|
+
When introducing Pattern into a new workflow, spot-check early results
|
|
1109
|
+
against the actual components before relying on it unattended.
|