pattern-mcp 0.12.1 → 0.14.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 +303 -266
- package/dist/client-connect.js +265 -0
- package/dist/index.js +391 -245
- package/dist/init-enforcement.js +1 -60
- package/dist/prompt.js +65 -0
- package/dist/telemetry.js +63 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,22 +15,27 @@ design reference.
|
|
|
15
15
|
|
|
16
16
|
[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)
|
|
17
17
|
|
|
18
|
-
**Current release: v0.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
**Current release: v0.14.0** — a crash on startup is now diagnosable
|
|
19
|
+
instead of silent (a new, coarse `pattern_cli_exited` telemetry event),
|
|
20
|
+
Pattern warns at startup if `ANTHROPIC_API_KEY` is missing or clearly
|
|
21
|
+
malformed instead of only surfacing a raw 401 mid-call, and a 429 from
|
|
22
|
+
the Anthropic API now gets one respectful retry (honoring `Retry-After`)
|
|
23
|
+
before it's raised. Previously: v0.13.0 added `npx pattern-mcp init`,
|
|
24
|
+
which sets up the connection to your MCP client for you (Claude Code,
|
|
25
|
+
Claude Desktop, Cursor detected and configured automatically; Codex CLI
|
|
26
|
+
gets manual instructions). Running `npx pattern-mcp` bare in your own
|
|
27
|
+
terminal also tells you it needs a client connected, instead of silently
|
|
28
|
+
sitting there. See
|
|
29
|
+
[Connect Pattern to your MCP client](#connect-pattern-to-your-mcp-client)
|
|
24
30
|
for more details.
|
|
25
31
|
|
|
26
32
|
<details>
|
|
27
33
|
<summary><strong>Contents</strong> (click to expand)</summary>
|
|
28
34
|
|
|
29
35
|
- [Install](#install) · [What Pattern Does](#what-pattern-does) · [How it works](#how-it-works) · [Quick Start](#quick-start) · [Try it](#try-it) · [Validation examples](#validation-examples)
|
|
30
|
-
-
|
|
31
|
-
- **
|
|
32
|
-
- **
|
|
33
|
-
- [Enforcement boundary: hook + CI gate](#enforcement-boundary-hook--ci-gate) (new in v0.10.0 — require the call, don't just log it)
|
|
36
|
+
- [Enforcement boundary: hook + CI gate](#enforcement-boundary-hook--ci-gate) (require the call, don't just log it)
|
|
37
|
+
- **Core tools** (on by default -- see [Tool tiers](#tool-tiers)): [`recommend_component`](#tool-recommend_component) · [`extract_requirements`](#tool-extract_requirements) · [`record_component_decision`](#tool-record_component_decision)
|
|
38
|
+
- **[Advanced tools](#advanced-tools)** (`PATTERN_TOOLS=full`): [`register_design_system`](#tool-register_design_system) · [`read_ledger`](#tool-read_ledger) · [`report_build_cost`](#tool-report_build_cost) · [`report_outcome_proxy`](#tool-report_outcome_proxy) · [Feature cost attribution](#feature-cost-attribution) · [Outcome proxies](#outcome-proxies) · [Per-project judgment ledger](#per-project-judgment-ledger) · [`check_ledger_liveness`](#tool-check_ledger_liveness) · [`sweep_ledger_liveness`](#tool-sweep_ledger_liveness) · [`export_ledger_provenance`](#tool-export_ledger_provenance) · [`backfill_ledger_snapshot_ref`](#tool-backfill_ledger_snapshot_ref) · [`post_ledger_provenance_to_github`](#tool-post_ledger_provenance_to_github) · [Ledger integrity and decision provenance](#ledger-integrity-and-decision-provenance)
|
|
34
39
|
- [Per-project decision memory](#per-project-decision-memory) · [Security and privacy](#security-and-privacy) · [Telemetry](#telemetry)
|
|
35
40
|
- **Cost:** [The `_meta` field](#the-_meta-field) · [Prompt caching](#prompt-caching) · [Measured cache and fetch behavior](#measured-cache-and-fetch-behavior) · [Search limits](#search-limits) · [Ensemble cost](#ensemble-cost-boundary-risk-cases-only) · [Session call cap](#session-call-cap)
|
|
36
41
|
- [Local call log](#local-call-log) · [Known limitations](#known-limitations)
|
|
@@ -58,54 +63,38 @@ whether to:
|
|
|
58
63
|
|
|
59
64
|
Pattern is designed for agents to use **while they are building**.
|
|
60
65
|
|
|
61
|
-
It exposes
|
|
66
|
+
It exposes twelve tools. Three are on by default -- the ones the
|
|
67
|
+
install → recommend → enforce → build path actually needs -- and the
|
|
68
|
+
rest reveal themselves once you need them. See [Tool
|
|
69
|
+
tiers](#tool-tiers).
|
|
62
70
|
|
|
63
|
-
**
|
|
71
|
+
**Core, on by default.**
|
|
64
72
|
|
|
65
73
|
- `recommend_component` — evaluates a UI component need and returns a
|
|
66
74
|
structured recommendation.
|
|
67
75
|
- `extract_requirements` — runs just the requirement-extraction step on
|
|
68
76
|
its own, so you can inspect or hand-edit the checklist before
|
|
69
77
|
`recommend_component` spends its search+score budget on it.
|
|
70
|
-
|
|
71
|
-
**Track what it cost and what actually happened.**
|
|
72
|
-
|
|
73
78
|
- `record_component_decision` — records what the agent actually did so
|
|
74
79
|
future recommendations in the same project can take that decision into
|
|
75
80
|
account.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
- `check_ledger_liveness` — checks whether a ledger entry's recorded
|
|
95
|
-
`file_path` still exists and still references its `chosen_candidate`.
|
|
96
|
-
- `sweep_ledger_liveness` — batch version of `check_ledger_liveness`
|
|
97
|
-
across a whole project (or every project in the ledger), plus
|
|
98
|
-
dangling-cluster detection. Meant to be invoked by your own cron/CI, not
|
|
99
|
-
something Pattern schedules itself.
|
|
100
|
-
- `export_ledger_provenance` — formats one ledger entry as a stable
|
|
101
|
-
markdown block (checklist, candidates, verdict, `snapshot_ref`) you can
|
|
102
|
-
paste into a PR or issue by hand.
|
|
103
|
-
- `backfill_ledger_snapshot_ref` — best-effort `snapshot_ref`
|
|
104
|
-
reconstruction for entries written before that field existed.
|
|
105
|
-
- `post_ledger_provenance_to_github` — posts an `export_ledger_provenance`
|
|
106
|
-
artifact as a real comment on a GitHub PR/issue, idempotently. The one
|
|
107
|
-
tool here with a real, visible side effect outside your own machine;
|
|
108
|
-
confirm with the user before calling it.
|
|
81
|
+
|
|
82
|
+
**[Advanced](#advanced-tools), behind `PATTERN_TOOLS=full`.** Pointing
|
|
83
|
+
Pattern at your own design system, cost/outcome tracking, and ledger
|
|
84
|
+
provenance/liveness. See [Advanced tools](#advanced-tools) for the full
|
|
85
|
+
list.
|
|
86
|
+
|
|
87
|
+
### Tool tiers
|
|
88
|
+
|
|
89
|
+
By default Pattern's `tools/list` response advertises only the three
|
|
90
|
+
core tools above, so a first-time agent sees a small, obvious surface
|
|
91
|
+
instead of all twelve at once. Every tool still works when called
|
|
92
|
+
directly, tiering only changes what gets *advertised* -- so a script or
|
|
93
|
+
an agent that already knows a tool's name (e.g. from this README) can
|
|
94
|
+
still call `register_design_system` or `read_ledger` without setting
|
|
95
|
+
anything. Set `PATTERN_TOOLS=full` in the server's environment to
|
|
96
|
+
advertise all twelve tools immediately, e.g. for the "verify and export
|
|
97
|
+
old decisions" or cost-tracking workflows described below.
|
|
109
98
|
|
|
110
99
|
## How it works
|
|
111
100
|
|
|
@@ -280,6 +269,32 @@ The server command is:
|
|
|
280
269
|
npx pattern-mcp
|
|
281
270
|
```
|
|
282
271
|
|
|
272
|
+
#### Automatic setup
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
npx pattern-mcp init
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Detects which clients are installed and offers to connect each one:
|
|
279
|
+
|
|
280
|
+
- **Claude Code** -- runs `claude mcp add` for you (asks whether to make
|
|
281
|
+
Pattern available in every project or just this one); skips if already
|
|
282
|
+
connected (`claude mcp list` already shows it).
|
|
283
|
+
- **Claude Desktop** and **Cursor** -- merges a `pattern` entry into the
|
|
284
|
+
client's own config file, showing the exact change before writing it
|
|
285
|
+
and never touching any other server already configured there.
|
|
286
|
+
- **Codex CLI** -- prints the config snippet to add by hand (Codex's
|
|
287
|
+
config is TOML; this doesn't auto-edit it).
|
|
288
|
+
|
|
289
|
+
Optionally pastes your `ANTHROPIC_API_KEY` into whichever configs you set
|
|
290
|
+
up (visible in plain text as you type it, and in the files it writes) --
|
|
291
|
+
press Enter to skip and add it yourself later instead. Run
|
|
292
|
+
non-interactively with `--yes` (skips the API key prompt entirely,
|
|
293
|
+
accepts every detected client).
|
|
294
|
+
|
|
295
|
+
If you'd rather do it by hand, or `init` didn't detect your client, the
|
|
296
|
+
per-client instructions below cover the same configs manually.
|
|
297
|
+
|
|
283
298
|
#### Claude Code
|
|
284
299
|
|
|
285
300
|
You can add Pattern to your project's `.mcp.json` or register it with the
|
|
@@ -411,6 +426,117 @@ Together, these cover different outcomes, including clear matches,
|
|
|
411
426
|
false-positive-prone searches, no candidates, and decisions close to the
|
|
412
427
|
threshold.
|
|
413
428
|
|
|
429
|
+
## Enforcement boundary: hook + CI gate
|
|
430
|
+
|
|
431
|
+
**Decisions can be enforced, not just tracked.** An opt-in `PreToolUse`
|
|
432
|
+
hook can block a new component from being written until a matching
|
|
433
|
+
ledger entry exists; a paired GitHub Action can also fail the PR if that
|
|
434
|
+
decision record isn't committed alongside the code.
|
|
435
|
+
|
|
436
|
+
**The gap this closes:** SKILL.md instructs the calling agent to call
|
|
437
|
+
`recommend_component` before scaffolding a new, non-trivial UI component,
|
|
438
|
+
but nothing before this feature *enforced* that -- an agent could simply
|
|
439
|
+
skip the call, and nothing server-side would know. This is opt-in and
|
|
440
|
+
Claude-Code-specific for the hook half; a consuming repo that never wires
|
|
441
|
+
either piece up gets Pattern exactly as it worked before, and any other
|
|
442
|
+
MCP host (Cursor, Codex, etc.) is entirely unaffected either way.
|
|
443
|
+
|
|
444
|
+
**Set it up with one command:**
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
npx pattern-check-gate init
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
Confirms each step independently rather than one blanket "proceed?", and
|
|
451
|
+
never auto-commits -- review with `git status`/`git diff` and commit
|
|
452
|
+
yourself when ready:
|
|
453
|
+
|
|
454
|
+
1. Confirms a project id (pre-filled from `package.json`'s `name`, or
|
|
455
|
+
your git remote/directory name -- accept it or type your own).
|
|
456
|
+
2. Writes or merges `.claude/settings.json` -- if one already exists, it
|
|
457
|
+
parses it, leaves any unrelated hooks untouched, and only appends the
|
|
458
|
+
`PreToolUse` entry if it isn't already there (safe to rerun).
|
|
459
|
+
3. Writes `.github/workflows/pattern-gate.yml`, if a GitHub remote is
|
|
460
|
+
detected and the file doesn't already exist with different content
|
|
461
|
+
(never silently overwritten).
|
|
462
|
+
4. Asks, as its own explicit yes/no: **mark the check required in branch
|
|
463
|
+
protection?** Needs `gh` installed and authenticated with admin rights
|
|
464
|
+
on the repo; skips with clear next steps otherwise. Deliberately only
|
|
465
|
+
offered when no branch protection exists yet on the default branch --
|
|
466
|
+
GitHub's branch-protection API replaces the *entire* configuration on
|
|
467
|
+
write, not just the required-checks list, so this refuses to guess at
|
|
468
|
+
merging into whatever you already have rather than risk silently
|
|
469
|
+
dropping an unrelated setting (e.g. required PR reviews). If
|
|
470
|
+
protection already exists, add `pattern-gate` to it by hand instead.
|
|
471
|
+
|
|
472
|
+
Run non-interactively with `--yes` (accepts every safe default; branch
|
|
473
|
+
protection is never auto-confirmed even then -- it's the one step that
|
|
474
|
+
reaches outside your local filesystem into real, shared GitHub config).
|
|
475
|
+
|
|
476
|
+
**You don't have to find this section to learn this exists.** Every
|
|
477
|
+
`npx pattern-mcp` run surfaces it at the same first-run moment as the
|
|
478
|
+
[telemetry notice](#telemetry):
|
|
479
|
+
|
|
480
|
+
- **Always**, in every context, including when a real MCP client has
|
|
481
|
+
spawned this as a subprocess: a one-time, non-blocking stderr mention
|
|
482
|
+
that the enforcement boundary exists and the command above sets it up.
|
|
483
|
+
Same "prints once, gated by a marker file" discipline as the telemetry
|
|
484
|
+
notice -- tracked at `~/.pattern/enforcement_notice_shown`
|
|
485
|
+
(`PATTERN_ENFORCEMENT_NOTICE_PATH` to override), never repeats after
|
|
486
|
+
that regardless of whether you act on it.
|
|
487
|
+
- **Only when stdin is a real terminal** (`process.stdin.isTTY`) --
|
|
488
|
+
meaning a human ran `npx pattern-mcp` bare in their own shell, never
|
|
489
|
+
true for a real MCP client's spawned subprocess -- it also offers a
|
|
490
|
+
genuine interactive prompt right there: *"Set it up now?"* A yes runs
|
|
491
|
+
the exact same `init` flow described above. The same JSON-RPC-channel
|
|
492
|
+
constraint that rules out an interactive telemetry prompt (see
|
|
493
|
+
[Telemetry](#telemetry)) applies here too, which is why this only ever
|
|
494
|
+
asks when nothing is piping protocol messages into stdin to begin
|
|
495
|
+
with.
|
|
496
|
+
|
|
497
|
+
Set `PATTERN_NO_ENFORCEMENT_NOTICE` to suppress both halves. See
|
|
498
|
+
`offerEnforcementSetupOnce` in `src/init-enforcement.ts` for the
|
|
499
|
+
implementation.
|
|
500
|
+
|
|
501
|
+
**Or set it up by hand**, two pieces, neither installed automatically:
|
|
502
|
+
|
|
503
|
+
- **`.claude/settings.json`** wired to run `npx --yes
|
|
504
|
+
pattern-check-gate-hook` on `PreToolUse` (see
|
|
505
|
+
`templates/claude-settings/settings.json` for the exact shape) -- a
|
|
506
|
+
Claude Code hook that runs on `Write`/`Edit` calls. For a genuinely new
|
|
507
|
+
`.tsx`/`.jsx` file that exports a non-trivial component, it looks up a
|
|
508
|
+
ledger entry (via `~/.pattern/ledger.jsonl`, same as everywhere else in
|
|
509
|
+
Pattern) whose `file_path` matches the file being written. A match
|
|
510
|
+
writes a receipt and allows the write; no match blocks it with a reason
|
|
511
|
+
fed back to the model as retryable guidance, not a hard failure.
|
|
512
|
+
**This is the one new exception where Pattern writes into your repo**
|
|
513
|
+
(`.pattern/receipts/<feature_id>.json`) -- everything else described in
|
|
514
|
+
this README is read-only. `project_id` no longer needs to be set by
|
|
515
|
+
hand either -- it's derived the same way `init` pre-fills it (see
|
|
516
|
+
`src/project-id.ts`); set `PATTERN_PROJECT_ID` only to override that.
|
|
517
|
+
- **`templates/github-workflows/pattern-gate.yml`** -- a required PR
|
|
518
|
+
check that reads the same receipt files back out of the diff. It never
|
|
519
|
+
touches `~/.pattern/` (not reachable from a CI runner) and needs no
|
|
520
|
+
`GITHUB_TOKEN` -- it trusts the committed receipt as the artifact of
|
|
521
|
+
record, the same way it would trust a committed test fixture.
|
|
522
|
+
|
|
523
|
+
The join between the two depends on `file_path` being passed to
|
|
524
|
+
`recommend_component`/`record_component_decision` -- if it's omitted, the
|
|
525
|
+
gate has nothing to match against and fails closed (blocks) rather than
|
|
526
|
+
guessing. Pass `file_path` whenever you know it.
|
|
527
|
+
|
|
528
|
+
An escape hatch exists for both a whole-hook kill switch
|
|
529
|
+
(`PATTERN_NO_ENFORCEMENT_HOOK`, local only -- does not affect the CI
|
|
530
|
+
check) and a per-file override (a `// pattern-mcp:override reason="..."`
|
|
531
|
+
comment) -- the override still writes a receipt recording
|
|
532
|
+
`manual_override: true` and the reason, so it stays visible rather than
|
|
533
|
+
silent. See `src/component-gate.ts`, `src/gate-receipt.ts`,
|
|
534
|
+
`src/check-gate.ts` (the `pattern-check-gate` CLI, this project's first
|
|
535
|
+
entry point separate from the stdio MCP server), `src/check-gate-hook.ts`,
|
|
536
|
+
and `src/init-enforcement.ts` for the implementation, and BACKLOG.md's
|
|
537
|
+
"Enforcement boundary" entries for the fuller design writeup.
|
|
538
|
+
|
|
539
|
+
|
|
414
540
|
## Tool: `recommend_component`
|
|
415
541
|
|
|
416
542
|
### Input
|
|
@@ -609,114 +735,6 @@ The calling agent should:
|
|
|
609
735
|
|
|
610
736
|
See [SECURITY.md](./SECURITY.md) for more details.
|
|
611
737
|
|
|
612
|
-
## Tool: `register_design_system`
|
|
613
|
-
|
|
614
|
-
Points `recommend_component` at *this project's own* design system instead
|
|
615
|
-
of shadcn/ui, 21st.dev, and ReUI -- for a solo dev with their own component
|
|
616
|
-
library or design spec who wants Pattern's coverage scoring against
|
|
617
|
-
candidates they'll actually use, not external libraries they won't. This is
|
|
618
|
-
the Solo Dev architecture from `pattern-solo-design-system-architecture.md`:
|
|
619
|
-
local, per-project, one-or-the-other -- registering a design system for a
|
|
620
|
-
`project_id` **replaces** external-library scoring for that project
|
|
621
|
-
entirely, it does not add to it. There's no shared/remote ledger, no
|
|
622
|
-
multi-user attribution, and no team auth in this scope -- those are
|
|
623
|
-
deliberately deferred to a team phase, only if this use case proves out.
|
|
624
|
-
|
|
625
|
-
### Input
|
|
626
|
-
|
|
627
|
-
Exactly one of `manifest_path` or `directory_path` is required, both
|
|
628
|
-
relative to the project root (`PATTERN_PROJECT_ROOT`, defaults to this
|
|
629
|
-
server's working directory) -- never an absolute path.
|
|
630
|
-
|
|
631
|
-
```json
|
|
632
|
-
{
|
|
633
|
-
"project_id": "my-booking-app",
|
|
634
|
-
"directory_path": "src/components"
|
|
635
|
-
}
|
|
636
|
-
```
|
|
637
|
-
|
|
638
|
-
- **`manifest_path`** -- a components manifest. Two recognized shapes:
|
|
639
|
-
- A hand-authored JSON array of `{name, props, description,
|
|
640
|
-
usage_example}` objects, optionally wrapped in `{"components": [...]}`.
|
|
641
|
-
- A Storybook-exported `stories.json`/`index.json` file (an object with a
|
|
642
|
-
top-level `entries` or `stories` map). Component names only in this
|
|
643
|
-
case -- Storybook's basic export doesn't carry prop data, so candidates
|
|
644
|
-
from this path start with an empty `props` list.
|
|
645
|
-
- **`directory_path`** -- a directory of real component source files,
|
|
646
|
-
scanned recursively for `.jsx`/`.tsx`/`.js`/`.ts` files (excluding
|
|
647
|
-
`node_modules`/`dist`/`build`/`.git` and `.test.`/`.spec.`/`.stories.`
|
|
648
|
-
files). Each exported, uppercase-named function or const component found
|
|
649
|
-
becomes a candidate, with props read in priority order from a
|
|
650
|
-
`<Name>Props` interface/type, a `.propTypes` block, or (last resort) the
|
|
651
|
-
component's own destructured parameters. This is a heuristic scan, not a
|
|
652
|
-
full parser -- a sparse or partial props list for some components is
|
|
653
|
-
expected, not a bug, especially on plain JS with no prop typing at all.
|
|
654
|
-
|
|
655
|
-
### Output
|
|
656
|
-
|
|
657
|
-
```json
|
|
658
|
-
{
|
|
659
|
-
"status": "registered",
|
|
660
|
-
"registration": {
|
|
661
|
-
"project_id": "my-booking-app",
|
|
662
|
-
"source_kind": "directory_scan",
|
|
663
|
-
"source_path": "src/components",
|
|
664
|
-
"registered_at": "2026-09-03T18:04:11.201Z",
|
|
665
|
-
"candidate_count": 29,
|
|
666
|
-
"candidates": [
|
|
667
|
-
{ "name": "ReferralBanner", "props": ["code", "bonusAmount"], "description": null, "usage_example": null, "file_path": "rewards/ReferralBanner.jsx" }
|
|
668
|
-
]
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
```
|
|
672
|
-
|
|
673
|
-
Registering overwrites (does not merge with) any prior registration for the
|
|
674
|
-
same `project_id`. Once registered, `recommend_component` scores ONLY
|
|
675
|
-
against these candidates for calls with this `project_id` -- no separate
|
|
676
|
-
flag needed, it's automatic based on `project_id` alone, and step 3's live
|
|
677
|
-
web search is skipped entirely (`web_search` is still available, but
|
|
678
|
-
reserved for a `custom_build` verdict's Mobbin/Figma Community reference
|
|
679
|
-
grounding, same as the external-library path). A `use_existing` verdict
|
|
680
|
-
scored this way always carries `"source": "design_system"` on the
|
|
681
|
-
resulting ledger entry, set server-side regardless of what the model wrote,
|
|
682
|
-
so `read_ledger` and `export_ledger_provenance` can match on it reliably.
|
|
683
|
-
|
|
684
|
-
This only writes local config to `~/.pattern/design_systems.json` (override
|
|
685
|
-
with `PATTERN_DESIGN_SYSTEMS_PATH`) -- it never calls the Anthropic API.
|
|
686
|
-
Registration is a point-in-time snapshot, not a live link: re-run this
|
|
687
|
-
whenever the design system's own components change meaningfully.
|
|
688
|
-
|
|
689
|
-
### A safety net for a missed match
|
|
690
|
-
|
|
691
|
-
The model can occasionally say `custom_build`/`no_candidates_found`
|
|
692
|
-
against a registered design system even when a real match is sitting
|
|
693
|
-
right there in its own prompt -- a reading-comprehension miss over its own
|
|
694
|
-
known-complete candidate list, not evidence the list was actually empty.
|
|
695
|
-
When this happens, `recommend_component`'s response may carry a
|
|
696
|
-
`design_system_recall_check` field: a deterministic, zero-cost, local
|
|
697
|
-
keyword-overlap check (component name, props, description/usage_example
|
|
698
|
-
vs. `component_need`/`domain`) run automatically whenever reason is
|
|
699
|
-
`no_candidates_found` in this mode.
|
|
700
|
-
|
|
701
|
-
```json
|
|
702
|
-
{
|
|
703
|
-
"verdict": "custom_build",
|
|
704
|
-
"reason": "no_candidates_found",
|
|
705
|
-
"design_system_recall_check": {
|
|
706
|
-
"possible_missed_candidates": [
|
|
707
|
-
{ "name": "ReferralBanner", "shared_keywords": ["referral", "bonus"] }
|
|
708
|
-
],
|
|
709
|
-
"note": "These registered design-system candidates share keywords with this component_need but were not selected as a match -- the verdict may have missed a real one. This is a weak, keyword-only signal, not proof of an actual match: double-check these candidates yourself (or re-run this call) before trusting custom_build here."
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
```
|
|
713
|
-
|
|
714
|
-
This never overrides the verdict -- a shared keyword is weak evidence, not
|
|
715
|
-
proof of a real match -- it only surfaces the risk so you (or the calling
|
|
716
|
-
agent) know to double-check before accepting a `custom_build` verdict at
|
|
717
|
-
face value. Absent entirely when there's no overlap, or outside
|
|
718
|
-
design-system mode.
|
|
719
|
-
|
|
720
738
|
## Tool: `extract_requirements`
|
|
721
739
|
|
|
722
740
|
Runs only the requirement-extraction step `recommend_component` normally
|
|
@@ -834,6 +852,118 @@ Anthropic API call.
|
|
|
834
852
|
}
|
|
835
853
|
```
|
|
836
854
|
|
|
855
|
+
## Advanced tools
|
|
856
|
+
|
|
857
|
+
Not advertised by default -- set `PATTERN_TOOLS=full` to see these in `tools/list`, or call them directly by name at any time (see [Tool tiers](#tool-tiers)).
|
|
858
|
+
|
|
859
|
+
## Tool: `register_design_system`
|
|
860
|
+
|
|
861
|
+
Points `recommend_component` at *this project's own* design system instead
|
|
862
|
+
of shadcn/ui, 21st.dev, and ReUI -- for a solo dev with their own component
|
|
863
|
+
library or design spec who wants Pattern's coverage scoring against
|
|
864
|
+
candidates they'll actually use, not external libraries they won't. This is
|
|
865
|
+
the Solo Dev architecture from `pattern-solo-design-system-architecture.md`:
|
|
866
|
+
local, per-project, one-or-the-other -- registering a design system for a
|
|
867
|
+
`project_id` **replaces** external-library scoring for that project
|
|
868
|
+
entirely, it does not add to it. There's no shared/remote ledger, no
|
|
869
|
+
multi-user attribution, and no team auth in this scope -- those are
|
|
870
|
+
deliberately deferred to a team phase, only if this use case proves out.
|
|
871
|
+
|
|
872
|
+
### Input
|
|
873
|
+
|
|
874
|
+
Exactly one of `manifest_path` or `directory_path` is required, both
|
|
875
|
+
relative to the project root (`PATTERN_PROJECT_ROOT`, defaults to this
|
|
876
|
+
server's working directory) -- never an absolute path.
|
|
877
|
+
|
|
878
|
+
```json
|
|
879
|
+
{
|
|
880
|
+
"project_id": "my-booking-app",
|
|
881
|
+
"directory_path": "src/components"
|
|
882
|
+
}
|
|
883
|
+
```
|
|
884
|
+
|
|
885
|
+
- **`manifest_path`** -- a components manifest. Two recognized shapes:
|
|
886
|
+
- A hand-authored JSON array of `{name, props, description,
|
|
887
|
+
usage_example}` objects, optionally wrapped in `{"components": [...]}`.
|
|
888
|
+
- A Storybook-exported `stories.json`/`index.json` file (an object with a
|
|
889
|
+
top-level `entries` or `stories` map). Component names only in this
|
|
890
|
+
case -- Storybook's basic export doesn't carry prop data, so candidates
|
|
891
|
+
from this path start with an empty `props` list.
|
|
892
|
+
- **`directory_path`** -- a directory of real component source files,
|
|
893
|
+
scanned recursively for `.jsx`/`.tsx`/`.js`/`.ts` files (excluding
|
|
894
|
+
`node_modules`/`dist`/`build`/`.git` and `.test.`/`.spec.`/`.stories.`
|
|
895
|
+
files). Each exported, uppercase-named function or const component found
|
|
896
|
+
becomes a candidate, with props read in priority order from a
|
|
897
|
+
`<Name>Props` interface/type, a `.propTypes` block, or (last resort) the
|
|
898
|
+
component's own destructured parameters. This is a heuristic scan, not a
|
|
899
|
+
full parser -- a sparse or partial props list for some components is
|
|
900
|
+
expected, not a bug, especially on plain JS with no prop typing at all.
|
|
901
|
+
|
|
902
|
+
### Output
|
|
903
|
+
|
|
904
|
+
```json
|
|
905
|
+
{
|
|
906
|
+
"status": "registered",
|
|
907
|
+
"registration": {
|
|
908
|
+
"project_id": "my-booking-app",
|
|
909
|
+
"source_kind": "directory_scan",
|
|
910
|
+
"source_path": "src/components",
|
|
911
|
+
"registered_at": "2026-09-03T18:04:11.201Z",
|
|
912
|
+
"candidate_count": 29,
|
|
913
|
+
"candidates": [
|
|
914
|
+
{ "name": "ReferralBanner", "props": ["code", "bonusAmount"], "description": null, "usage_example": null, "file_path": "rewards/ReferralBanner.jsx" }
|
|
915
|
+
]
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
Registering overwrites (does not merge with) any prior registration for the
|
|
921
|
+
same `project_id`. Once registered, `recommend_component` scores ONLY
|
|
922
|
+
against these candidates for calls with this `project_id` -- no separate
|
|
923
|
+
flag needed, it's automatic based on `project_id` alone, and step 3's live
|
|
924
|
+
web search is skipped entirely (`web_search` is still available, but
|
|
925
|
+
reserved for a `custom_build` verdict's Mobbin/Figma Community reference
|
|
926
|
+
grounding, same as the external-library path). A `use_existing` verdict
|
|
927
|
+
scored this way always carries `"source": "design_system"` on the
|
|
928
|
+
resulting ledger entry, set server-side regardless of what the model wrote,
|
|
929
|
+
so `read_ledger` and `export_ledger_provenance` can match on it reliably.
|
|
930
|
+
|
|
931
|
+
This only writes local config to `~/.pattern/design_systems.json` (override
|
|
932
|
+
with `PATTERN_DESIGN_SYSTEMS_PATH`) -- it never calls the Anthropic API.
|
|
933
|
+
Registration is a point-in-time snapshot, not a live link: re-run this
|
|
934
|
+
whenever the design system's own components change meaningfully.
|
|
935
|
+
|
|
936
|
+
### A safety net for a missed match
|
|
937
|
+
|
|
938
|
+
The model can occasionally say `custom_build`/`no_candidates_found`
|
|
939
|
+
against a registered design system even when a real match is sitting
|
|
940
|
+
right there in its own prompt -- a reading-comprehension miss over its own
|
|
941
|
+
known-complete candidate list, not evidence the list was actually empty.
|
|
942
|
+
When this happens, `recommend_component`'s response may carry a
|
|
943
|
+
`design_system_recall_check` field: a deterministic, zero-cost, local
|
|
944
|
+
keyword-overlap check (component name, props, description/usage_example
|
|
945
|
+
vs. `component_need`/`domain`) run automatically whenever reason is
|
|
946
|
+
`no_candidates_found` in this mode.
|
|
947
|
+
|
|
948
|
+
```json
|
|
949
|
+
{
|
|
950
|
+
"verdict": "custom_build",
|
|
951
|
+
"reason": "no_candidates_found",
|
|
952
|
+
"design_system_recall_check": {
|
|
953
|
+
"possible_missed_candidates": [
|
|
954
|
+
{ "name": "ReferralBanner", "shared_keywords": ["referral", "bonus"] }
|
|
955
|
+
],
|
|
956
|
+
"note": "These registered design-system candidates share keywords with this component_need but were not selected as a match -- the verdict may have missed a real one. This is a weak, keyword-only signal, not proof of an actual match: double-check these candidates yourself (or re-run this call) before trusting custom_build here."
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
This never overrides the verdict -- a shared keyword is weak evidence, not
|
|
962
|
+
proof of a real match -- it only surfaces the risk so you (or the calling
|
|
963
|
+
agent) know to double-check before accepting a `custom_build` verdict at
|
|
964
|
+
face value. Absent entirely when there's no overlap, or outside
|
|
965
|
+
design-system mode.
|
|
966
|
+
|
|
837
967
|
## Tool: `read_ledger`
|
|
838
968
|
|
|
839
969
|
Lists past `recommend_component` judgments for a `project_id` -- every
|
|
@@ -1592,116 +1722,6 @@ the source line, most recent record wins at read time" convention as
|
|
|
1592
1722
|
layered onto `ledger.jsonl`'s own entries at read time -- the ledger line
|
|
1593
1723
|
itself is never rewritten.
|
|
1594
1724
|
|
|
1595
|
-
## Enforcement boundary: hook + CI gate
|
|
1596
|
-
|
|
1597
|
-
**Decisions can be enforced, not just tracked.** An opt-in `PreToolUse`
|
|
1598
|
-
hook can block a new component from being written until a matching
|
|
1599
|
-
ledger entry exists; a paired GitHub Action can also fail the PR if that
|
|
1600
|
-
decision record isn't committed alongside the code.
|
|
1601
|
-
|
|
1602
|
-
**The gap this closes:** SKILL.md instructs the calling agent to call
|
|
1603
|
-
`recommend_component` before scaffolding a new, non-trivial UI component,
|
|
1604
|
-
but nothing before this feature *enforced* that -- an agent could simply
|
|
1605
|
-
skip the call, and nothing server-side would know. This is opt-in and
|
|
1606
|
-
Claude-Code-specific for the hook half; a consuming repo that never wires
|
|
1607
|
-
either piece up gets Pattern exactly as it worked before, and any other
|
|
1608
|
-
MCP host (Cursor, Codex, etc.) is entirely unaffected either way.
|
|
1609
|
-
|
|
1610
|
-
**Set it up with one command:**
|
|
1611
|
-
|
|
1612
|
-
```bash
|
|
1613
|
-
npx pattern-check-gate init
|
|
1614
|
-
```
|
|
1615
|
-
|
|
1616
|
-
Confirms each step independently rather than one blanket "proceed?", and
|
|
1617
|
-
never auto-commits -- review with `git status`/`git diff` and commit
|
|
1618
|
-
yourself when ready:
|
|
1619
|
-
|
|
1620
|
-
1. Confirms a project id (pre-filled from `package.json`'s `name`, or
|
|
1621
|
-
your git remote/directory name -- accept it or type your own).
|
|
1622
|
-
2. Writes or merges `.claude/settings.json` -- if one already exists, it
|
|
1623
|
-
parses it, leaves any unrelated hooks untouched, and only appends the
|
|
1624
|
-
`PreToolUse` entry if it isn't already there (safe to rerun).
|
|
1625
|
-
3. Writes `.github/workflows/pattern-gate.yml`, if a GitHub remote is
|
|
1626
|
-
detected and the file doesn't already exist with different content
|
|
1627
|
-
(never silently overwritten).
|
|
1628
|
-
4. Asks, as its own explicit yes/no: **mark the check required in branch
|
|
1629
|
-
protection?** Needs `gh` installed and authenticated with admin rights
|
|
1630
|
-
on the repo; skips with clear next steps otherwise. Deliberately only
|
|
1631
|
-
offered when no branch protection exists yet on the default branch --
|
|
1632
|
-
GitHub's branch-protection API replaces the *entire* configuration on
|
|
1633
|
-
write, not just the required-checks list, so this refuses to guess at
|
|
1634
|
-
merging into whatever you already have rather than risk silently
|
|
1635
|
-
dropping an unrelated setting (e.g. required PR reviews). If
|
|
1636
|
-
protection already exists, add `pattern-gate` to it by hand instead.
|
|
1637
|
-
|
|
1638
|
-
Run non-interactively with `--yes` (accepts every safe default; branch
|
|
1639
|
-
protection is never auto-confirmed even then -- it's the one step that
|
|
1640
|
-
reaches outside your local filesystem into real, shared GitHub config).
|
|
1641
|
-
|
|
1642
|
-
**You don't have to find this section to learn this exists.** Every
|
|
1643
|
-
`npx pattern-mcp` run surfaces it at the same first-run moment as the
|
|
1644
|
-
[telemetry notice](#telemetry):
|
|
1645
|
-
|
|
1646
|
-
- **Always**, in every context, including when a real MCP client has
|
|
1647
|
-
spawned this as a subprocess: a one-time, non-blocking stderr mention
|
|
1648
|
-
that the enforcement boundary exists and the command above sets it up.
|
|
1649
|
-
Same "prints once, gated by a marker file" discipline as the telemetry
|
|
1650
|
-
notice -- tracked at `~/.pattern/enforcement_notice_shown`
|
|
1651
|
-
(`PATTERN_ENFORCEMENT_NOTICE_PATH` to override), never repeats after
|
|
1652
|
-
that regardless of whether you act on it.
|
|
1653
|
-
- **Only when stdin is a real terminal** (`process.stdin.isTTY`) --
|
|
1654
|
-
meaning a human ran `npx pattern-mcp` bare in their own shell, never
|
|
1655
|
-
true for a real MCP client's spawned subprocess -- it also offers a
|
|
1656
|
-
genuine interactive prompt right there: *"Set it up now?"* A yes runs
|
|
1657
|
-
the exact same `init` flow described above. The same JSON-RPC-channel
|
|
1658
|
-
constraint that rules out an interactive telemetry prompt (see
|
|
1659
|
-
[Telemetry](#telemetry)) applies here too, which is why this only ever
|
|
1660
|
-
asks when nothing is piping protocol messages into stdin to begin
|
|
1661
|
-
with.
|
|
1662
|
-
|
|
1663
|
-
Set `PATTERN_NO_ENFORCEMENT_NOTICE` to suppress both halves. See
|
|
1664
|
-
`offerEnforcementSetupOnce` in `src/init-enforcement.ts` for the
|
|
1665
|
-
implementation.
|
|
1666
|
-
|
|
1667
|
-
**Or set it up by hand**, two pieces, neither installed automatically:
|
|
1668
|
-
|
|
1669
|
-
- **`.claude/settings.json`** wired to run `npx --yes
|
|
1670
|
-
pattern-check-gate-hook` on `PreToolUse` (see
|
|
1671
|
-
`templates/claude-settings/settings.json` for the exact shape) -- a
|
|
1672
|
-
Claude Code hook that runs on `Write`/`Edit` calls. For a genuinely new
|
|
1673
|
-
`.tsx`/`.jsx` file that exports a non-trivial component, it looks up a
|
|
1674
|
-
ledger entry (via `~/.pattern/ledger.jsonl`, same as everywhere else in
|
|
1675
|
-
Pattern) whose `file_path` matches the file being written. A match
|
|
1676
|
-
writes a receipt and allows the write; no match blocks it with a reason
|
|
1677
|
-
fed back to the model as retryable guidance, not a hard failure.
|
|
1678
|
-
**This is the one new exception where Pattern writes into your repo**
|
|
1679
|
-
(`.pattern/receipts/<feature_id>.json`) -- everything else described in
|
|
1680
|
-
this README is read-only. `project_id` no longer needs to be set by
|
|
1681
|
-
hand either -- it's derived the same way `init` pre-fills it (see
|
|
1682
|
-
`src/project-id.ts`); set `PATTERN_PROJECT_ID` only to override that.
|
|
1683
|
-
- **`templates/github-workflows/pattern-gate.yml`** -- a required PR
|
|
1684
|
-
check that reads the same receipt files back out of the diff. It never
|
|
1685
|
-
touches `~/.pattern/` (not reachable from a CI runner) and needs no
|
|
1686
|
-
`GITHUB_TOKEN` -- it trusts the committed receipt as the artifact of
|
|
1687
|
-
record, the same way it would trust a committed test fixture.
|
|
1688
|
-
|
|
1689
|
-
The join between the two depends on `file_path` being passed to
|
|
1690
|
-
`recommend_component`/`record_component_decision` -- if it's omitted, the
|
|
1691
|
-
gate has nothing to match against and fails closed (blocks) rather than
|
|
1692
|
-
guessing. Pass `file_path` whenever you know it.
|
|
1693
|
-
|
|
1694
|
-
An escape hatch exists for both a whole-hook kill switch
|
|
1695
|
-
(`PATTERN_NO_ENFORCEMENT_HOOK`, local only -- does not affect the CI
|
|
1696
|
-
check) and a per-file override (a `// pattern-mcp:override reason="..."`
|
|
1697
|
-
comment) -- the override still writes a receipt recording
|
|
1698
|
-
`manual_override: true` and the reason, so it stays visible rather than
|
|
1699
|
-
silent. See `src/component-gate.ts`, `src/gate-receipt.ts`,
|
|
1700
|
-
`src/check-gate.ts` (the `pattern-check-gate` CLI, this project's first
|
|
1701
|
-
entry point separate from the stdio MCP server), `src/check-gate-hook.ts`,
|
|
1702
|
-
and `src/init-enforcement.ts` for the implementation, and BACKLOG.md's
|
|
1703
|
-
"Enforcement boundary" entries for the fuller design writeup.
|
|
1704
|
-
|
|
1705
1725
|
## Per-project decision memory
|
|
1706
1726
|
|
|
1707
1727
|
Pattern stores confirmed decisions locally in:
|
|
@@ -1835,6 +1855,23 @@ are a biased, tiny sample of everyone who installs.
|
|
|
1835
1855
|
- On a failed Anthropic API call specifically: the HTTP status code and a
|
|
1836
1856
|
coarse classification (`rate_limit`, `insufficient_credit`, or `other`)
|
|
1837
1857
|
-- never the request or response body.
|
|
1858
|
+
- On every invocation of the `pattern-mcp` binary, immediately at
|
|
1859
|
+
startup: a single `pattern_cli_started` event carrying only which
|
|
1860
|
+
mode it ran in (`server` -- the normal MCP-server start, or `init` --
|
|
1861
|
+
the [connect wizard](#connect-pattern-to-your-mcp-client)). This
|
|
1862
|
+
exists to separate real executions from npm registry traffic that
|
|
1863
|
+
never runs the code at all (security scanners, mirrors) -- something
|
|
1864
|
+
neither `recommend_component` counts nor `@posthog/mcp`'s handshake
|
|
1865
|
+
event below can answer, since both require getting further than a
|
|
1866
|
+
bare `npx pattern-mcp` run.
|
|
1867
|
+
- On process exit, as of v0.14.0: a single `pattern_cli_exited` event
|
|
1868
|
+
carrying only a coarse reason (`sigint`, `sigterm`,
|
|
1869
|
+
`uncaught_exception`, `unhandled_rejection`, or
|
|
1870
|
+
`fatal_startup_error`) and, for the two exception cases, the thrown
|
|
1871
|
+
value's constructor name (e.g. `TypeError`) -- never the error
|
|
1872
|
+
message or stack trace. Paired with `pattern_cli_started` so a start
|
|
1873
|
+
with no matching MCP handshake is diagnosable as a crash instead of
|
|
1874
|
+
silent.
|
|
1838
1875
|
2. Standard MCP tool-call analytics, via
|
|
1839
1876
|
[`@posthog/mcp`](https://posthog.com/docs/mcp-analytics): which tool
|
|
1840
1877
|
was called, call duration, and success/failure, so unique installs and
|