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