pattern-mcp 0.11.0 → 0.12.1

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,10 +15,13 @@ 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.10.0** — adds an opt-in enforcement boundary (a
19
- `PreToolUse` hook plus a paired CI check) so a new component decision can
20
- be required, not just logged. See [Enforcement boundary: hook + CI
21
- gate](#enforcement-boundary-hook--ci-gate).
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)
24
+ for more details.
22
25
 
23
26
  <details>
24
27
  <summary><strong>Contents</strong> (click to expand)</summary>
@@ -1636,6 +1639,31 @@ Run non-interactively with `--yes` (accepts every safe default; branch
1636
1639
  protection is never auto-confirmed even then -- it's the one step that
1637
1640
  reaches outside your local filesystem into real, shared GitHub config).
1638
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
+
1639
1667
  **Or set it up by hand**, two pieces, neither installed automatically:
1640
1668
 
1641
1669
  - **`.claude/settings.json`** wired to run `npx --yes
@@ -1772,7 +1800,9 @@ whether you act on it. There's no interactive y/n prompt: Pattern's stdin
1772
1800
  is the MCP JSON-RPC channel the client uses to talk to it, so blocking on
1773
1801
  stdin for a keypress would fight the protocol handshake instead of
1774
1802
  showing a dialog -- a stderr notice is the safe equivalent for a stdio
1775
- MCP server.
1803
+ MCP server. The same first-run moment also surfaces the enforcement
1804
+ boundary, with the same constraint handled the same way -- see
1805
+ [Enforcement boundary: hook + CI gate](#enforcement-boundary-hook--ci-gate).
1776
1806
 
1777
1807
  **Why it exists.** Three things about real usage can't be answered from
1778
1808
  this repo alone: whether people actually come back and use Pattern on a
package/dist/index.js CHANGED
@@ -38,6 +38,7 @@ import { homedir } from "node:os";
38
38
  import { dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
39
39
  import { fileURLToPath } from "node:url";
40
40
  import { captureApiError, captureRecommendation, getClient as getPostHogClient, installId, printTelemetryNoticeOnce, shutdownTelemetry, TELEMETRY_ENABLED, } from "./telemetry.js";
41
+ import { offerEnforcementSetupOnce } from "./init-enforcement.js";
41
42
  export const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
42
43
  // Only required for org-scoped keys (not tied to one workspace); unset for
43
44
  // legacy workspace-scoped keys, which don't need it.
@@ -3864,6 +3865,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3864
3865
  });
3865
3866
  async function main() {
3866
3867
  printTelemetryNoticeOnce();
3868
+ // Piggybacks on this same first-run moment (Option B, see
3869
+ // init-enforcement.ts) -- always prints a one-time, non-blocking mention;
3870
+ // only prompts interactively when stdin is a real TTY, never when a real
3871
+ // MCP client has piped stdio into this process for JSON-RPC. Always
3872
+ // returns before the transport below claims stdin.
3873
+ await offerEnforcementSetupOnce(PROJECT_ROOT);
3867
3874
  const transport = new StdioServerTransport();
3868
3875
  await server.connect(transport);
3869
3876
  // Best-effort telemetry drain on clean shutdown -- no-op when telemetry
@@ -12,6 +12,7 @@
12
12
  // post_ledger_provenance_to_github.
13
13
  import { execFileSync } from "node:child_process";
14
14
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
15
+ import { homedir } from "node:os";
15
16
  import { dirname, join } from "node:path";
16
17
  import { fileURLToPath } from "node:url";
17
18
  import { createInterface } from "node:readline";
@@ -314,3 +315,71 @@ export async function runInit(root, options) {
314
315
  }
315
316
  console.log("\nDone. Review the changes with `git status` / `git diff`, then commit when ready.");
316
317
  }
318
+ // Option B from BACKLOG.md's "Enforcement boundary setup" entry: piggyback
319
+ // on the moment someone's already setting Pattern up, rather than leaving
320
+ // enforcement as something only the README mentions. Called once from
321
+ // index.ts's main(), right alongside printTelemetryNoticeOnce, before the
322
+ // stdio transport connects.
323
+ //
324
+ // The literal original phrasing of this option ("extend the first-run
325
+ // notice into a [y/N] prompt") turns out not to be safely buildable as
326
+ // written: stdin is the live JSON-RPC channel a real MCP client uses to
327
+ // talk to this process (see telemetry.ts's printTelemetryNoticeOnce for
328
+ // the same constraint, stated first). Blocking it on a keypress here would
329
+ // fight the protocol handshake, not show a dialog. So this does two
330
+ // different things depending on how stdin is actually connected:
331
+ //
332
+ // - Always (any context, including a real client subprocess): print a
333
+ // one-time, non-blocking mention that the enforcement boundary exists
334
+ // and how to set it up. Same "print once, gated by a marker file"
335
+ // pattern as the telemetry notice, deliberately a separate marker/
336
+ // message so the two stay independently legible in a terminal.
337
+ // - Only when process.stdin.isTTY is true -- which a real MCP client's
338
+ // spawned subprocess never has, since it always pipes stdio to speak
339
+ // JSON-RPC over it, but a human running `npx pattern-mcp` bare in
340
+ // their own terminal does -- also offer a real interactive prompt,
341
+ // reusing runInit itself rather than duplicating its logic.
342
+ const ENFORCEMENT_NOTICE_PATH = process.env.PATTERN_ENFORCEMENT_NOTICE_PATH ?? join(homedir(), ".pattern", "enforcement_notice_shown");
343
+ export async function offerEnforcementSetupOnce(root) {
344
+ if (process.env.PATTERN_NO_ENFORCEMENT_NOTICE)
345
+ return;
346
+ try {
347
+ readFileSync(ENFORCEMENT_NOTICE_PATH, "utf8");
348
+ return; // Already shown -- never repeat, same discipline as the telemetry notice.
349
+ }
350
+ catch {
351
+ // No marker yet -- fall through and show it.
352
+ }
353
+ console.error([
354
+ "",
355
+ "Pattern -- enforcement boundary available (this will not print again)",
356
+ "By default, Pattern is something the calling agent chooses to use.",
357
+ "An opt-in hook + CI check can require it instead: run `npx pattern-check-gate init`",
358
+ "in your repo to set it up.",
359
+ "Full details: https://github.com/donaldrichard19-LVD/pattern-mcp#enforcement-boundary-hook--ci-gate",
360
+ "",
361
+ ].join("\n"));
362
+ try {
363
+ mkdirSync(dirname(ENFORCEMENT_NOTICE_PATH), { recursive: true });
364
+ writeFileSync(ENFORCEMENT_NOTICE_PATH, new Date().toISOString(), "utf8");
365
+ }
366
+ catch {
367
+ // Couldn't persist the marker -- worst case this prints again next
368
+ // run. Never blocks startup over it, same as the telemetry notice.
369
+ }
370
+ if (!process.stdin.isTTY)
371
+ return;
372
+ try {
373
+ const setUpNow = await confirm("Set it up now?", { yes: false }, false);
374
+ if (setUpNow) {
375
+ await runInit(root, { yes: false }); // closes the shared readline itself, in its own finally block
376
+ }
377
+ }
378
+ finally {
379
+ // closeRl() is safe to call even if runInit already closed it (checks
380
+ // rl?.close() and no-ops on null) -- this just guarantees stdin is
381
+ // always released back before main() connects the stdio transport,
382
+ // whether the answer was no or runInit already cleaned up after itself.
383
+ closeRl();
384
+ }
385
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pattern-mcp",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
4
4
  "description": "MCP server that turns your design guidance into a checkable process -- evaluates UI components from external libraries (shadcn/ui, 21st.dev, ReUI) or your own registered design system against a requirements checklist, then tells the agent whether to reuse an existing component or build one from a concrete design reference.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",