pattern-mcp 0.11.0 → 0.12.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,9 +15,10 @@ 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
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
21
22
  gate](#enforcement-boundary-hook--ci-gate).
22
23
 
23
24
  <details>
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.0",
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",