pattern-mcp 0.12.1 → 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,22 +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.12.0** — When you run `npx pattern-mcp` for the
19
- first time, Pattern now shows the optional enforcement boundary
20
- alongside the telemetry notice. This boundary includes a `PreToolUse`
21
- hook and a matching CI check. Together, they can require a decision for
22
- each new component instead of only recording it. See
23
- [Enforcement boundary: hook + CI 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)
24
25
  for more details.
25
26
 
26
27
  <details>
27
28
  <summary><strong>Contents</strong> (click to expand)</summary>
28
29
 
29
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)
30
- - **Make the judgment call:** [`recommend_component`](#tool-recommend_component) · [`extract_requirements`](#tool-extract_requirements)
31
- - **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)
32
- - **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)
33
- - [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)
34
34
  - [Per-project decision memory](#per-project-decision-memory) · [Security and privacy](#security-and-privacy) · [Telemetry](#telemetry)
35
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)
36
36
  - [Local call log](#local-call-log) · [Known limitations](#known-limitations)
@@ -58,54 +58,38 @@ whether to:
58
58
 
59
59
  Pattern is designed for agents to use **while they are building**.
60
60
 
61
- 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).
62
65
 
63
- **Make the judgment call.**
66
+ **Core, on by default.**
64
67
 
65
68
  - `recommend_component` — evaluates a UI component need and returns a
66
69
  structured recommendation.
67
70
  - `extract_requirements` — runs just the requirement-extraction step on
68
71
  its own, so you can inspect or hand-edit the checklist before
69
72
  `recommend_component` spends its search+score budget on it.
70
-
71
- **Track what it cost and what actually happened.**
72
-
73
73
  - `record_component_decision` — records what the agent actually did so
74
74
  future recommendations in the same project can take that decision into
75
75
  account.
76
- - `read_ledger` — lists past `recommend_component` judgments for a
77
- `project_id`, including any that were served from the ledger cache (see
78
- [Per-project judgment ledger](#per-project-judgment-ledger)); pass
79
- `feature_id` instead of browsing by keyword to get a full cost rollup for
80
- one feature (see [Tool: `report_build_cost`](#tool-report_build_cost)).
81
- - `report_build_cost` — self-reports the end-to-end build cost for one
82
- feature, so cost incurred after Pattern's own verdict (the actual
83
- scaffold/install/build) is still attributable back to it.
84
- - `report_outcome_proxy` self-reports a value signal (rework, time to
85
- merge, kept-vs-replaced) for one feature, deliberately independent of
86
- Pattern's own verdict -- see [Outcome
87
- proxies](#outcome-proxies).
88
-
89
- **Verify old decisions still hold up, and export a record of them.** See
90
- [Ledger integrity and decision
91
- provenance](#ledger-integrity-and-decision-provenance) for how these five
92
- fit together.
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.
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.
109
93
 
110
94
  ## How it works
111
95
 
@@ -280,6 +264,32 @@ The server command is:
280
264
  npx pattern-mcp
281
265
  ```
282
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
+
283
293
  #### Claude Code
284
294
 
285
295
  You can add Pattern to your project's `.mcp.json` or register it with the
@@ -411,6 +421,117 @@ Together, these cover different outcomes, including clear matches,
411
421
  false-positive-prone searches, no candidates, and decisions close to the
412
422
  threshold.
413
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
+
414
535
  ## Tool: `recommend_component`
415
536
 
416
537
  ### Input
@@ -609,114 +730,6 @@ The calling agent should:
609
730
 
610
731
  See [SECURITY.md](./SECURITY.md) for more details.
611
732
 
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
733
  ## Tool: `extract_requirements`
721
734
 
722
735
  Runs only the requirement-extraction step `recommend_component` normally
@@ -834,6 +847,118 @@ Anthropic API call.
834
847
  }
835
848
  ```
836
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
+
837
962
  ## Tool: `read_ledger`
838
963
 
839
964
  Lists past `recommend_component` judgments for a `project_id` -- every
@@ -1592,116 +1717,6 @@ the source line, most recent record wins at read time" convention as
1592
1717
  layered onto `ledger.jsonl`'s own entries at read time -- the ledger line
1593
1718
  itself is never rewritten.
1594
1719
 
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
1720
  ## Per-project decision memory
1706
1721
 
1707
1722
  Pattern stores confirmed decisions locally in:
@@ -1835,6 +1850,15 @@ are a biased, tiny sample of everyone who installs.
1835
1850
  - On a failed Anthropic API call specifically: the HTTP status code and a
1836
1851
  coarse classification (`rate_limit`, `insufficient_credit`, or `other`)
1837
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.
1838
1862
  2. Standard MCP tool-call analytics, via
1839
1863
  [`@posthog/mcp`](https://posthog.com/docs/mcp-analytics): which tool
1840
1864
  was called, call duration, and success/failure, so unique installs and