pattern-mcp 0.14.0 → 0.15.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 +68 -55
- package/dist/client-connect.js +56 -17
- package/dist/index.js +54 -35
- package/dist/telemetry.js +16 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,17 +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.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
**Current release: v0.15.0** — `npx pattern-mcp init` is now the first
|
|
19
|
+
and only command shown for getting started, everywhere (this README
|
|
20
|
+
and the website); bare `npx pattern-mcp` is documented as the server
|
|
21
|
+
command your client uses, not something to run yourself. The connect
|
|
22
|
+
wizard also no longer asks just once: if you run Pattern bare in your
|
|
23
|
+
own terminal and skip or miss it, it keeps offering on every later run
|
|
24
|
+
until it can actually confirm a client is connected, instead of going
|
|
25
|
+
silent forever after the first try. Previously: v0.14.1 made the
|
|
26
|
+
crash/exit telemetry added in v0.14.0 (`pattern_cli_exited`) register
|
|
27
|
+
before any of this file's own module-level code runs, and tagged both
|
|
28
|
+
it and `pattern_cli_started` with the running package version, so a
|
|
29
|
+
crash right around a release can be tied to the old or new binary. Before
|
|
30
|
+
that: v0.14.0 made a crash on startup diagnosable instead of silent,
|
|
31
|
+
warned at startup if `ANTHROPIC_API_KEY` is missing or clearly
|
|
32
|
+
malformed instead of only surfacing a raw 401 mid-call, and added one
|
|
33
|
+
respectful retry on a 429 (honoring `Retry-After`). See
|
|
29
34
|
[Connect Pattern to your MCP client](#connect-pattern-to-your-mcp-client)
|
|
30
35
|
for more details.
|
|
31
36
|
|
|
@@ -45,11 +50,22 @@ for more details.
|
|
|
45
50
|
## Install
|
|
46
51
|
|
|
47
52
|
```bash
|
|
48
|
-
npx pattern-mcp
|
|
53
|
+
npx pattern-mcp init
|
|
49
54
|
```
|
|
50
55
|
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
This is the only command you run yourself. It downloads Pattern,
|
|
57
|
+
detects which MCP client(s) you have (Claude Code, Claude Desktop,
|
|
58
|
+
Cursor, or Codex CLI), connects each one for you, and offers to add
|
|
59
|
+
your Anthropic API key. See [Quick Start](#quick-start) below for what it
|
|
60
|
+
does step by step, or
|
|
61
|
+
[Connect Pattern to your MCP client](#connect-pattern-to-your-mcp-client)
|
|
62
|
+
if you'd rather connect a client by hand.
|
|
63
|
+
|
|
64
|
+
`npx pattern-mcp` on its own (no `init`) is the server command your MCP
|
|
65
|
+
client will use once connected -- running it yourself in a bare
|
|
66
|
+
terminal starts a real process that just sits there waiting for a
|
|
67
|
+
client, since nothing has told it to connect to one yet. Prefer `init`
|
|
68
|
+
above for getting started.
|
|
53
69
|
|
|
54
70
|
## What Pattern Does
|
|
55
71
|
|
|
@@ -219,14 +235,40 @@ threshold.
|
|
|
219
235
|
|
|
220
236
|
## Quick Start
|
|
221
237
|
|
|
222
|
-
### 1. Install
|
|
238
|
+
### 1. Install and connect
|
|
223
239
|
|
|
224
240
|
```bash
|
|
225
|
-
npx pattern-mcp
|
|
241
|
+
npx pattern-mcp init
|
|
226
242
|
```
|
|
227
243
|
|
|
228
|
-
|
|
229
|
-
|
|
244
|
+
This is the only command you need to run yourself -- `npx` downloads
|
|
245
|
+
`pattern-mcp` on demand, then `init` detects which clients you have
|
|
246
|
+
installed and offers to connect each one:
|
|
247
|
+
|
|
248
|
+
- **Claude Code** -- runs `claude mcp add` for you (asks whether to make
|
|
249
|
+
Pattern available in every project or just this one); skips if already
|
|
250
|
+
connected (`claude mcp list` already shows it).
|
|
251
|
+
- **Claude Desktop** and **Cursor** -- merges a `pattern` entry into the
|
|
252
|
+
client's own config file, showing the exact change before writing it
|
|
253
|
+
and never touching any other server already configured there.
|
|
254
|
+
- **Codex CLI** -- prints the config snippet to add by hand (Codex's
|
|
255
|
+
config is TOML; this doesn't auto-edit it).
|
|
256
|
+
|
|
257
|
+
Optionally pastes your `ANTHROPIC_API_KEY` into whichever configs you set
|
|
258
|
+
up (visible in plain text as you type it, and in the files it writes) --
|
|
259
|
+
press Enter to skip and add it yourself later, see
|
|
260
|
+
[step 2](#2-add-your-anthropic-api-key) below. Run non-interactively with
|
|
261
|
+
`--yes` (skips the API key prompt entirely, accepts every detected
|
|
262
|
+
client).
|
|
263
|
+
|
|
264
|
+
If `init` doesn't detect your client, or you'd rather set it up by
|
|
265
|
+
hand, see [Connect Pattern to your MCP client](#connect-pattern-to-your-mcp-client)
|
|
266
|
+
below for the same configs, per client, done manually. The server
|
|
267
|
+
command either way is `npx pattern-mcp` -- this is what your client's
|
|
268
|
+
config launches; you shouldn't need to run it yourself. If you do run
|
|
269
|
+
it bare in your own terminal (e.g. to double check the install), it
|
|
270
|
+
will just sit there waiting for a client and periodically remind you
|
|
271
|
+
to run `init` -- that's expected, not a hang.
|
|
230
272
|
|
|
231
273
|
<details>
|
|
232
274
|
<summary>Build from source instead</summary>
|
|
@@ -238,14 +280,15 @@ npm install
|
|
|
238
280
|
npm run build
|
|
239
281
|
```
|
|
240
282
|
|
|
241
|
-
Use `node /absolute/path/to/pattern-mcp/dist/index.js`
|
|
242
|
-
|
|
283
|
+
Use `node /absolute/path/to/pattern-mcp/dist/index.js` in place of
|
|
284
|
+
`npx pattern-mcp` everywhere in this README, including inside `init`'s
|
|
285
|
+
own generated client configs.
|
|
243
286
|
|
|
244
287
|
</details>
|
|
245
288
|
|
|
246
289
|
### 2. Add your Anthropic API key
|
|
247
290
|
|
|
248
|
-
Pattern requires:
|
|
291
|
+
Skipped it above, or want to change it? Pattern requires:
|
|
249
292
|
|
|
250
293
|
```
|
|
251
294
|
ANTHROPIC_API_KEY
|
|
@@ -260,40 +303,10 @@ Claude Pro or Max subscription does not include API usage.
|
|
|
260
303
|
|
|
261
304
|
### Connect Pattern to your MCP client
|
|
262
305
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
```
|
|
269
|
-
npx pattern-mcp
|
|
270
|
-
```
|
|
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.
|
|
306
|
+
[Step 1](#1-install-and-connect) above (`npx pattern-mcp init`) does
|
|
307
|
+
this automatically for every client it detects -- the sections below
|
|
308
|
+
are the same configs done by hand, for a client `init` didn't detect,
|
|
309
|
+
or if you'd simply rather edit the config yourself.
|
|
297
310
|
|
|
298
311
|
#### Claude Code
|
|
299
312
|
|
package/dist/client-connect.js
CHANGED
|
@@ -127,6 +127,28 @@ async function mergeServerConfig(label, path, apiKey, options) {
|
|
|
127
127
|
writeFileSync(path, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
128
128
|
console.log(`Written. Restart ${label} to pick it up.`);
|
|
129
129
|
}
|
|
130
|
+
function clientConfigHasPattern(path) {
|
|
131
|
+
if (!path)
|
|
132
|
+
return false;
|
|
133
|
+
const { config } = readJsonConfig(path);
|
|
134
|
+
return Boolean(config.mcpServers?.pattern);
|
|
135
|
+
}
|
|
136
|
+
// Best-effort, read-only check across every client Pattern knows how to
|
|
137
|
+
// detect a prior successful setup for -- used to decide whether to keep
|
|
138
|
+
// offering the connect wizard on a later bare run (see
|
|
139
|
+
// offerClientConnectSetupOnce below), instead of asking only once ever
|
|
140
|
+
// regardless of outcome. Codex is deliberately excluded: its config is
|
|
141
|
+
// TOML, which this project never parses or writes (see
|
|
142
|
+
// offerCodexInstructions), so there's no way to confirm a Codex-only
|
|
143
|
+
// setup from here. That means a Codex-only user keeps getting offered
|
|
144
|
+
// the wizard -- a false negative, which is the safe failure mode (asks
|
|
145
|
+
// again when already connected) rather than a false positive (goes quiet
|
|
146
|
+
// when it isn't).
|
|
147
|
+
export function isAnyClientConnected(root) {
|
|
148
|
+
return (claudeCodeAlreadyConnected() ||
|
|
149
|
+
clientConfigHasPattern(claudeDesktopConfigPath()) ||
|
|
150
|
+
clientConfigHasPattern(join(root, ".cursor", "mcp.json")));
|
|
151
|
+
}
|
|
130
152
|
// Claude Desktop isn't shipped on Linux -- there's no config path to
|
|
131
153
|
// even guess at there, so this target is simply not offered on that
|
|
132
154
|
// platform rather than writing a file no client will ever read.
|
|
@@ -225,36 +247,53 @@ export async function runConnect(root, options) {
|
|
|
225
247
|
// Option 1/#1 from the activation-funnel discussion: piggybacks on the
|
|
226
248
|
// same first-run moment as the telemetry and enforcement-boundary
|
|
227
249
|
// notices (see telemetry.ts's printTelemetryNoticeOnce, which states the
|
|
228
|
-
// stdin constraint first). Always prints
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
//
|
|
250
|
+
// stdin constraint first). Always prints the full notice once, ever --
|
|
251
|
+
// including when a real MCP client has spawned this as a subprocess,
|
|
252
|
+
// where it's genuinely irrelevant but harmless, since the notice is
|
|
253
|
+
// gated on a marker file the same as the others.
|
|
254
|
+
//
|
|
255
|
+
// The interactive "set it up now?" prompt is different: it used to be
|
|
256
|
+
// gated on that same one-time marker, so a human who ignored or missed
|
|
257
|
+
// it on the very first bare run never saw it again -- a permanent
|
|
258
|
+
// drop-off with no second chance, found while mapping the new-install
|
|
259
|
+
// journey (see project_pattern_activation_funnel memory). It now keeps
|
|
260
|
+
// reappearing on every bare TTY run -- a human running `npx pattern-mcp`
|
|
261
|
+
// in their own shell, never a real client's spawned subprocess -- for as
|
|
262
|
+
// long as isAnyClientConnected() can't confirm a real connection exists
|
|
263
|
+
// yet. This is the concrete fix for "don't rely on the user to figure
|
|
264
|
+
// out how to connect": Pattern keeps offering, not just once, until it
|
|
265
|
+
// can verify success, or until PATTERN_NO_CONNECT_NOTICE opts out.
|
|
234
266
|
const CONNECT_NOTICE_PATH = process.env.PATTERN_CONNECT_NOTICE_PATH ?? join(homedir(), ".pattern", "connect_notice_shown");
|
|
235
267
|
export async function offerClientConnectSetupOnce(root) {
|
|
236
268
|
if (process.env.PATTERN_NO_CONNECT_NOTICE)
|
|
237
269
|
return;
|
|
270
|
+
let noticeAlreadyShown = true;
|
|
238
271
|
try {
|
|
239
272
|
readFileSync(CONNECT_NOTICE_PATH, "utf8");
|
|
240
|
-
return;
|
|
241
273
|
}
|
|
242
274
|
catch {
|
|
243
|
-
|
|
275
|
+
noticeAlreadyShown = false;
|
|
244
276
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
277
|
+
if (!noticeAlreadyShown) {
|
|
278
|
+
console.error(["", "Pattern -- one-time setup notice (this will not print again)", connectInstructionsText(), ""].join("\n"));
|
|
279
|
+
try {
|
|
280
|
+
mkdirSync(dirname(CONNECT_NOTICE_PATH), { recursive: true });
|
|
281
|
+
writeFileSync(CONNECT_NOTICE_PATH, new Date().toISOString(), "utf8");
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
// Couldn't persist the marker -- worst case this prints again next
|
|
285
|
+
// run. Never blocks startup over it, same as the other notices.
|
|
286
|
+
}
|
|
253
287
|
}
|
|
254
288
|
if (!process.stdin.isTTY)
|
|
255
289
|
return;
|
|
290
|
+
if (isAnyClientConnected(root))
|
|
291
|
+
return;
|
|
256
292
|
try {
|
|
257
|
-
const
|
|
293
|
+
const question = noticeAlreadyShown
|
|
294
|
+
? "No MCP client is connected to Pattern yet -- run the connect wizard now?"
|
|
295
|
+
: "Run the connect wizard now?";
|
|
296
|
+
const setUpNow = await confirm(question, { yes: false }, true);
|
|
258
297
|
if (setUpNow) {
|
|
259
298
|
await runConnect(root, { yes: false }); // closes the shared readline itself
|
|
260
299
|
}
|
package/dist/index.js
CHANGED
|
@@ -40,6 +40,53 @@ import { fileURLToPath } from "node:url";
|
|
|
40
40
|
import { captureApiError, captureCliExited, captureCliStarted, captureRecommendation, getClient as getPostHogClient, installId, printTelemetryNoticeOnce, shutdownTelemetry, TELEMETRY_ENABLED, } from "./telemetry.js";
|
|
41
41
|
import { offerEnforcementSetupOnce } from "./init-enforcement.js";
|
|
42
42
|
import { connectInstructionsText, offerClientConnectSetupOnce, runConnect } from "./client-connect.js";
|
|
43
|
+
// Read as early as possible in this file's own module-level code, wrapped
|
|
44
|
+
// so a missing/corrupt package.json can't itself become a new, unguarded
|
|
45
|
+
// crash source -- this is read before the crash handlers below exist to
|
|
46
|
+
// catch anything. Moved up from where it used to live (just above the
|
|
47
|
+
// `server` construction, far later in this file) after a 2026-09-14
|
|
48
|
+
// incident where a caller crashed on every single launch, ~29 times in 4
|
|
49
|
+
// minutes, immediately after a version bump published -- telemetry had no
|
|
50
|
+
// way to say whether the crashing process was the old or new version (see
|
|
51
|
+
// project_pattern_activation_funnel memory). Every pattern_cli_started/
|
|
52
|
+
// pattern_cli_exited event now carries this, closing that gap for next
|
|
53
|
+
// time.
|
|
54
|
+
const PACKAGE_VERSION = (() => {
|
|
55
|
+
try {
|
|
56
|
+
return JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf8")).version;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return "unknown";
|
|
60
|
+
}
|
|
61
|
+
})();
|
|
62
|
+
// Registered before anything else in this file runs (all the way up here,
|
|
63
|
+
// not down by main() where it used to be) so a throw anywhere in this
|
|
64
|
+
// file's own module-level code -- not just inside main() -- is captured
|
|
65
|
+
// instead of dying silently before these handlers would otherwise have
|
|
66
|
+
// existed. Can't cover a throw during the import statements above this
|
|
67
|
+
// line (nothing can run before those resolve), but this closes the much
|
|
68
|
+
// larger window between "imports finished" and "main() starts," which is
|
|
69
|
+
// most of this file's ~4700 lines of function/constant definitions and
|
|
70
|
+
// tool registrations.
|
|
71
|
+
let exitTelemetryCaptured = false;
|
|
72
|
+
function captureExitOnce(reason, err) {
|
|
73
|
+
if (exitTelemetryCaptured)
|
|
74
|
+
return;
|
|
75
|
+
exitTelemetryCaptured = true;
|
|
76
|
+
captureCliExited(reason, err, PACKAGE_VERSION);
|
|
77
|
+
}
|
|
78
|
+
process.on("uncaughtException", async (err) => {
|
|
79
|
+
console.error("Pattern: uncaught exception, exiting.", err);
|
|
80
|
+
captureExitOnce("uncaught_exception", err);
|
|
81
|
+
await shutdownTelemetry();
|
|
82
|
+
process.exit(1);
|
|
83
|
+
});
|
|
84
|
+
process.on("unhandledRejection", async (reason) => {
|
|
85
|
+
console.error("Pattern: unhandled rejection, exiting.", reason);
|
|
86
|
+
captureExitOnce("unhandled_rejection", reason);
|
|
87
|
+
await shutdownTelemetry();
|
|
88
|
+
process.exit(1);
|
|
89
|
+
});
|
|
43
90
|
export const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
|
|
44
91
|
// Only required for org-scoped keys (not tied to one workspace); unset for
|
|
45
92
|
// legacy workspace-scoped keys, which don't need it.
|
|
@@ -3414,11 +3461,8 @@ export function extractJson(text) {
|
|
|
3414
3461
|
return text;
|
|
3415
3462
|
return text.slice(start, end + 1);
|
|
3416
3463
|
}
|
|
3417
|
-
//
|
|
3418
|
-
//
|
|
3419
|
-
// initialize response) reflects the version actually installed instead of
|
|
3420
|
-
// staying frozen at whatever it was when this line was first written.
|
|
3421
|
-
const PACKAGE_VERSION = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf8")).version;
|
|
3464
|
+
// PACKAGE_VERSION is defined near the top of this file now (read as early
|
|
3465
|
+
// as possible, before the crash handlers -- see the comment there).
|
|
3422
3466
|
const server = new Server({ name: "pattern-mcp", version: PACKAGE_VERSION }, { capabilities: { tools: {} } });
|
|
3423
3467
|
// Standard MCP tool-call analytics (tool name, duration, success/failure,
|
|
3424
3468
|
// unique installs/sessions) via PostHog's own MCP SDK -- separate from
|
|
@@ -3938,41 +3982,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3938
3982
|
// they're still looking at the terminal, not five minutes after they
|
|
3939
3983
|
// alt-tabbed away.
|
|
3940
3984
|
const IDLE_CONNECT_NUDGE_MS = 20_000;
|
|
3941
|
-
//
|
|
3942
|
-
//
|
|
3943
|
-
//
|
|
3944
|
-
// all: captureCliStarted fires, the process dies, and nothing explains why
|
|
3945
|
-
// (see project_pattern_activation_funnel memory -- the incident this
|
|
3946
|
-
// exists to make diagnosable next time). Both handlers exit(1) after
|
|
3947
|
-
// capturing: Node considers the process's state undefined past an uncaught
|
|
3948
|
-
// exception, so continuing to run is the wrong default regardless of
|
|
3949
|
-
// telemetry.
|
|
3950
|
-
let exitTelemetryCaptured = false;
|
|
3951
|
-
function captureExitOnce(reason, err) {
|
|
3952
|
-
if (exitTelemetryCaptured)
|
|
3953
|
-
return;
|
|
3954
|
-
exitTelemetryCaptured = true;
|
|
3955
|
-
captureCliExited(reason, err);
|
|
3956
|
-
}
|
|
3957
|
-
process.on("uncaughtException", async (err) => {
|
|
3958
|
-
console.error("Pattern: uncaught exception, exiting.", err);
|
|
3959
|
-
captureExitOnce("uncaught_exception", err);
|
|
3960
|
-
await shutdownTelemetry();
|
|
3961
|
-
process.exit(1);
|
|
3962
|
-
});
|
|
3963
|
-
process.on("unhandledRejection", async (reason) => {
|
|
3964
|
-
console.error("Pattern: unhandled rejection, exiting.", reason);
|
|
3965
|
-
captureExitOnce("unhandled_rejection", reason);
|
|
3966
|
-
await shutdownTelemetry();
|
|
3967
|
-
process.exit(1);
|
|
3968
|
-
});
|
|
3985
|
+
// captureExitOnce and the uncaughtException/unhandledRejection handlers
|
|
3986
|
+
// are registered near the top of this file now, before PACKAGE_VERSION's
|
|
3987
|
+
// definition -- see the comment there for why.
|
|
3969
3988
|
async function main() {
|
|
3970
3989
|
// `npx pattern-mcp init` -- the connect wizard -- exits without ever
|
|
3971
3990
|
// starting the server. Checked before anything else so it can't be
|
|
3972
3991
|
// shadowed by a tool name collision later.
|
|
3973
3992
|
const argv = process.argv.slice(2);
|
|
3974
3993
|
if (argv[0] === "init") {
|
|
3975
|
-
captureCliStarted("init");
|
|
3994
|
+
captureCliStarted("init", PACKAGE_VERSION);
|
|
3976
3995
|
await runConnect(PROJECT_ROOT, { yes: argv.includes("--yes") });
|
|
3977
3996
|
await shutdownTelemetry();
|
|
3978
3997
|
// Explicit exit, not a bare return -- shutdownTelemetry races a
|
|
@@ -3982,7 +4001,7 @@ async function main() {
|
|
|
3982
4001
|
// event loop to drain on its own if some other handle is lingering.
|
|
3983
4002
|
process.exit(0);
|
|
3984
4003
|
}
|
|
3985
|
-
captureCliStarted("server");
|
|
4004
|
+
captureCliStarted("server", PACKAGE_VERSION);
|
|
3986
4005
|
warnIfAnthropicKeyLooksWrong();
|
|
3987
4006
|
printTelemetryNoticeOnce();
|
|
3988
4007
|
// Piggybacks on this same first-run moment (Option B, see
|
package/dist/telemetry.js
CHANGED
|
@@ -241,21 +241,30 @@ export function captureRecommendation(args) {
|
|
|
241
241
|
// project_pattern_reddit_launch_spike memory for why that gap mattered:
|
|
242
242
|
// a 2026-09-11 download spike showed almost no matching $mcp_initialize
|
|
243
243
|
// growth, and there was no signal at all for the step in between.
|
|
244
|
-
|
|
245
|
-
|
|
244
|
+
// `version` is the running package.json version (see index.ts's
|
|
245
|
+
// PACKAGE_VERSION) -- added 2026-09-14 after an incident where a caller
|
|
246
|
+
// crashed on every launch attempt (~29 times in 4 minutes) in the few
|
|
247
|
+
// minutes right after a version bump published, and telemetry had no way
|
|
248
|
+
// to say whether the crashing process was the old or new version. Without
|
|
249
|
+
// it, "did the fix actually ship before this happened" is unanswerable
|
|
250
|
+
// from telemetry alone.
|
|
251
|
+
export function captureCliStarted(mode, version) {
|
|
252
|
+
capture("pattern_cli_started", { mode, version });
|
|
246
253
|
}
|
|
247
254
|
// Paired with captureCliStarted so a start with no matching handshake is
|
|
248
255
|
// diagnosable instead of silent -- added after a 2026-09-13 incident where
|
|
249
256
|
// 33 starts in one hour produced exactly 1 successful handshake, and
|
|
250
257
|
// telemetry had no way to say why the other 32 processes ended (see
|
|
251
|
-
// project_pattern_activation_funnel memory). Only a coarse reason
|
|
252
|
-
// thrown value's constructor name
|
|
253
|
-
// stack, which could contain a file path, a
|
|
254
|
-
// local detail never sent by design (see
|
|
255
|
-
|
|
258
|
+
// project_pattern_activation_funnel memory). Only a coarse reason, the
|
|
259
|
+
// thrown value's constructor name, and the running version travel --
|
|
260
|
+
// never the error message or stack, which could contain a file path, a
|
|
261
|
+
// stray argument value, or other local detail never sent by design (see
|
|
262
|
+
// this file's header).
|
|
263
|
+
export function captureCliExited(reason, err, version) {
|
|
256
264
|
capture("pattern_cli_exited", {
|
|
257
265
|
exit_reason: reason,
|
|
258
266
|
error_name: err instanceof Error ? err.name : null,
|
|
267
|
+
version,
|
|
259
268
|
});
|
|
260
269
|
}
|
|
261
270
|
export function captureApiError(args) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pattern-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.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",
|