@yawlabs/mcp 0.59.0 → 0.60.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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +273 -395
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -136,7 +136,7 @@ yaw-mcp compliance <target> [--publish] # run the compliance suite against an M
136
136
  yaw-mcp --version # print version
137
137
  ```
138
138
 
139
- Account / sync (Pro + Team -- a license key unlocks cross-machine bundle sync):
139
+ Account / sync (Yaw Team -- a license key unlocks cross-machine bundle sync):
140
140
 
141
141
  ```bash
142
142
  yaw-mcp login [--key <license>] # authenticate this machine with a Yaw MCP account
package/dist/index.js CHANGED
@@ -693,7 +693,7 @@ var SUBCOMMAND_SPEC = [
693
693
  positional: ["bash", "zsh", "fish", "powershell"],
694
694
  flags: ["--help"]
695
695
  },
696
- // Account / sync (Pro + Team).
696
+ // Account / sync (Yaw Team).
697
697
  { name: "login", description: "Authenticate with a Yaw MCP account", flags: ["--key", "--json", "--help"] },
698
698
  { name: "logout", description: "Sign out of your account", flags: ["--json", "--help"] },
699
699
  {
@@ -2818,6 +2818,216 @@ async function gcExpiredTrials(opts) {
2818
2818
  return { cleared, failed };
2819
2819
  }
2820
2820
 
2821
+ // src/upgrade-cmd.ts
2822
+ import { spawn as spawn2 } from "child_process";
2823
+ var UPGRADE_USAGE = `Usage: yaw-mcp upgrade [--run] [--json]
2824
+
2825
+ Show (or execute) the command to upgrade @yawlabs/mcp to the latest version.
2826
+
2827
+ --run Run the upgrade in place (global and local npm installs).
2828
+ No-op for npx installs \u2014 they always fetch the latest.
2829
+ --json Emit a machine-readable snapshot ({ current, latest, stale,
2830
+ method, command }) instead of prose.`;
2831
+ function parseUpgradeArgs(argv) {
2832
+ const opts = {};
2833
+ for (const a of argv) {
2834
+ if (a === "--run") opts.run = true;
2835
+ else if (a === "--json") opts.json = true;
2836
+ else if (a === "--help" || a === "-h") return { ok: false, error: UPGRADE_USAGE };
2837
+ else return { ok: false, error: `yaw-mcp upgrade: unknown argument "${a}"
2838
+
2839
+ ${UPGRADE_USAGE}` };
2840
+ }
2841
+ return { ok: true, options: opts };
2842
+ }
2843
+ function detectInstallMethod(argvPath) {
2844
+ if (!argvPath) return "unknown";
2845
+ const normalized = argvPath.replace(/\\/g, "/");
2846
+ if (/\/_npx\//.test(normalized)) return "npx";
2847
+ if (/\/app\.asar\.unpacked\//.test(normalized)) return "bundled-app";
2848
+ if (/\/npm\/node_modules\/@yawlabs\/mcp\//.test(normalized)) return "global-npm";
2849
+ if (/\/lib\/node_modules\/@yawlabs\/mcp\//.test(normalized)) return "global-npm";
2850
+ if (/\/AppData\/Roaming\/npm\/node_modules\/@yawlabs\/mcp\//.test(normalized)) return "global-npm";
2851
+ if (/\/node_modules\/@yawlabs\/mcp\//.test(normalized)) return "local-node-modules";
2852
+ if (/\/(yaw-mcp|mcph)\/(dist|src)\//.test(normalized)) return "dev-checkout";
2853
+ return "unknown";
2854
+ }
2855
+ function localInstallRoot(argvPath) {
2856
+ if (!argvPath) return null;
2857
+ const idx = argvPath.replace(/\\/g, "/").indexOf("/node_modules/");
2858
+ return idx > 0 ? argvPath.slice(0, idx) : null;
2859
+ }
2860
+ function buildUpgradePlan(input) {
2861
+ const { current, latest, method } = input;
2862
+ const stale = latest !== null && current !== "dev" && compareSemverLocal(current, latest) < 0;
2863
+ let command;
2864
+ switch (method) {
2865
+ case "global-npm":
2866
+ command = "npm install -g @yawlabs/mcp@latest";
2867
+ break;
2868
+ case "npx":
2869
+ command = null;
2870
+ break;
2871
+ case "bundled-app":
2872
+ command = null;
2873
+ break;
2874
+ case "local-node-modules":
2875
+ command = "npm install @yawlabs/mcp@latest";
2876
+ break;
2877
+ case "dev-checkout":
2878
+ command = "git pull && npm run build";
2879
+ break;
2880
+ default:
2881
+ command = "npm install -g @yawlabs/mcp@latest";
2882
+ break;
2883
+ }
2884
+ return { current, latest, stale, method, command };
2885
+ }
2886
+ function compareSemverLocal(a, b) {
2887
+ const parse = (s) => {
2888
+ const m = /^v?(\d+)\.(\d+)\.(\d+)/.exec(s);
2889
+ if (!m) return null;
2890
+ return [Number(m[1]), Number(m[2]), Number(m[3])];
2891
+ };
2892
+ const pa = parse(a);
2893
+ const pb = parse(b);
2894
+ if (!pa || !pb) return 0;
2895
+ for (let i = 0; i < 3; i++) {
2896
+ if (pa[i] < pb[i]) return -1;
2897
+ if (pa[i] > pb[i]) return 1;
2898
+ }
2899
+ return 0;
2900
+ }
2901
+ async function defaultFetchLatest() {
2902
+ const ac = new AbortController();
2903
+ const timer = setTimeout(() => ac.abort(), 3e3);
2904
+ try {
2905
+ const res = await fetch("https://registry.npmjs.org/@yawlabs/mcp/latest", {
2906
+ signal: ac.signal,
2907
+ headers: { accept: "application/json" }
2908
+ });
2909
+ if (!res.ok) return null;
2910
+ const body = await res.json();
2911
+ return typeof body.version === "string" ? body.version : null;
2912
+ } catch {
2913
+ return null;
2914
+ } finally {
2915
+ clearTimeout(timer);
2916
+ }
2917
+ }
2918
+ async function defaultSpawn(cmd, args, cwd) {
2919
+ return new Promise((resolve5) => {
2920
+ const child = spawn2(cmd, args, { stdio: "inherit", shell: process.platform === "win32", cwd });
2921
+ child.on("close", (code) => resolve5(typeof code === "number" ? code : 1));
2922
+ child.on("error", () => resolve5(1));
2923
+ });
2924
+ }
2925
+ async function runUpgrade(opts = {}) {
2926
+ const write = opts.out ?? ((s) => process.stdout.write(s));
2927
+ const writeErr = opts.err ?? ((s) => process.stderr.write(s));
2928
+ const lines = [];
2929
+ const print = (s = "") => {
2930
+ lines.push(s);
2931
+ write(`${s}
2932
+ `);
2933
+ };
2934
+ const printErr = (s) => {
2935
+ lines.push(s);
2936
+ writeErr(`${s}
2937
+ `);
2938
+ };
2939
+ const fetcher = opts.fetchLatest ?? defaultFetchLatest;
2940
+ const current = opts.currentVersion ?? readCurrentVersion();
2941
+ const argvPath = opts.argvPath ?? process.argv[1];
2942
+ const method = detectInstallMethod(argvPath);
2943
+ let latest;
2944
+ try {
2945
+ latest = await fetcher();
2946
+ } catch {
2947
+ latest = null;
2948
+ }
2949
+ const plan = buildUpgradePlan({ current, latest, method });
2950
+ if (opts.json) {
2951
+ print(JSON.stringify(plan, null, 2));
2952
+ return { exitCode: plan.stale && !opts.run ? 1 : 0, lines };
2953
+ }
2954
+ if (latest === null) {
2955
+ print("yaw-mcp upgrade: couldn't reach the npm registry (offline? firewall?).");
2956
+ if (plan.command) {
2957
+ print("When you're back online, run:");
2958
+ print("");
2959
+ print(` ${plan.command}`);
2960
+ } else if (method === "bundled-app") {
2961
+ print("This copy of yaw-mcp ships inside Yaw Terminal and updates with the app \u2014 nothing to run.");
2962
+ } else {
2963
+ print("Your install uses `npx -y` \u2014 just restart the MCP client when you're back online.");
2964
+ }
2965
+ return { exitCode: 0, lines };
2966
+ }
2967
+ print(`Current: ${current}`);
2968
+ print(`Latest: ${latest}`);
2969
+ print(`Install: ${method}`);
2970
+ if (!plan.stale) {
2971
+ print("");
2972
+ print("\u2713 You're on the latest version \u2014 nothing to do.");
2973
+ return { exitCode: 0, lines };
2974
+ }
2975
+ print("");
2976
+ if (method === "npx") {
2977
+ print("Your install uses `npx -y` \u2014 restart the MCP client and it will fetch the new version.");
2978
+ return { exitCode: 0, lines };
2979
+ }
2980
+ if (method === "bundled-app") {
2981
+ print("This copy of yaw-mcp ships inside Yaw Terminal and updates with the app \u2014");
2982
+ print("there is nothing to run here. Update Yaw Terminal to get the new version.");
2983
+ return { exitCode: 0, lines };
2984
+ }
2985
+ if (!plan.command) {
2986
+ print("No upgrade command available for this install method.");
2987
+ return { exitCode: 0, lines };
2988
+ }
2989
+ const installRoot = method === "local-node-modules" ? localInstallRoot(argvPath) : null;
2990
+ const autoRunnable = method === "global-npm" || method === "local-node-modules" && installRoot !== null;
2991
+ if (!opts.run) {
2992
+ if (autoRunnable) {
2993
+ print("Run `yaw-mcp upgrade --run` to upgrade in place, or run it yourself:");
2994
+ } else {
2995
+ print("Run it yourself (--run can't safely automate this install method):");
2996
+ }
2997
+ print("");
2998
+ print(` ${plan.command}`);
2999
+ return { exitCode: 1, lines };
3000
+ }
3001
+ if (!autoRunnable) {
3002
+ printErr(`yaw-mcp upgrade --run: a "${method}" install can't be upgraded automatically. Run it yourself:`);
3003
+ printErr("");
3004
+ printErr(` ${plan.command}`);
3005
+ return { exitCode: 2, lines };
3006
+ }
3007
+ const runner = opts.spawnImpl ?? defaultSpawn;
3008
+ const npmArgs = method === "global-npm" ? ["install", "-g", "@yawlabs/mcp@latest"] : ["install", "@yawlabs/mcp@latest"];
3009
+ if (installRoot) {
3010
+ print(`Running in ${installRoot}:`);
3011
+ } else {
3012
+ print("Running:");
3013
+ }
3014
+ print(` ${plan.command}`);
3015
+ print("");
3016
+ const code = await runner("npm", npmArgs, installRoot ?? void 0);
3017
+ if (code === 0) {
3018
+ print("");
3019
+ print(`\u2713 Upgraded @yawlabs/mcp to ${latest}`);
3020
+ return { exitCode: 0, lines };
3021
+ }
3022
+ printErr(`yaw-mcp upgrade: npm exited ${code}. Try running the command yourself:`);
3023
+ printErr("");
3024
+ printErr(` ${plan.command}`);
3025
+ return { exitCode: 3, lines };
3026
+ }
3027
+ function readCurrentVersion() {
3028
+ return true ? "0.60.0" : "dev";
3029
+ }
3030
+
2821
3031
  // src/usage-hints.ts
2822
3032
  var MAX_PEERS = 3;
2823
3033
  var MIN_SUCCESS_TO_SHOW = 1;
@@ -2877,7 +3087,7 @@ function selectFlakyNamespaces(entries, limit) {
2877
3087
  }
2878
3088
 
2879
3089
  // src/doctor-cmd.ts
2880
- var VERSION = true ? "0.59.0" : "dev";
3090
+ var VERSION = true ? "0.60.0" : "dev";
2881
3091
  async function runDoctor(opts = {}) {
2882
3092
  if (opts.json) return runDoctorJson(opts);
2883
3093
  const lines = [];
@@ -2949,10 +3159,24 @@ async function runDoctor(opts = {}) {
2949
3159
  const latest = skipCheck ? null : await fetchLatestVersion(opts.registryFetch);
2950
3160
  const staleHint = latest && VERSION !== "dev" && compareSemver(VERSION, latest) < 0 ? latest : null;
2951
3161
  if (staleHint) {
3162
+ const method = detectInstallMethod(process.argv[1]);
2952
3163
  print("UPGRADE AVAILABLE");
2953
- print(` Running ${VERSION}; npm latest is ${staleHint}.`);
2954
- print(" Run `yaw-mcp upgrade` to see the exact command for your install, or");
2955
- print(" `yaw-mcp upgrade --run` to execute it (global-npm installs only).");
3164
+ if (method === "bundled-app") {
3165
+ print(` Running ${VERSION}; npm latest is ${staleHint}. This copy ships inside`);
3166
+ print(" Yaw Terminal and updates with the app \u2014 update Yaw Terminal to get it.");
3167
+ } else if (method === "npx") {
3168
+ print(` Running ${VERSION}; npm latest is ${staleHint}. npx fetches the latest`);
3169
+ print(" on each spawn \u2014 restart your MCP client to pick it up.");
3170
+ } else if (method === "global-npm" || method === "local-node-modules") {
3171
+ print(` Running ${VERSION}; npm latest is ${staleHint}. To upgrade in place:`);
3172
+ print("");
3173
+ print(" yaw-mcp upgrade --run");
3174
+ } else {
3175
+ const plan = buildUpgradePlan({ current: VERSION, latest: staleHint, method });
3176
+ print(` Running ${VERSION}; npm latest is ${staleHint}. To upgrade:`);
3177
+ print("");
3178
+ print(` ${plan.command ?? "npm install -g @yawlabs/mcp@latest"}`);
3179
+ }
2956
3180
  print("");
2957
3181
  }
2958
3182
  let exitCode = 0;
@@ -4130,146 +4354,10 @@ async function runLogout(opts = {}, io = {
4130
4354
  return { exitCode: 0 };
4131
4355
  }
4132
4356
 
4133
- // src/nag.ts
4134
- import { readFile as readFile7 } from "fs/promises";
4135
- import { homedir as homedir8 } from "os";
4136
- import { join as join8 } from "path";
4137
- var NAG_STATE_FILENAME = "nag-state.json";
4138
- var MIN_THRESHOLD = 2;
4139
- var MAX_THRESHOLD = 4;
4140
- var FLOOR_MS = 36 * 60 * 60 * 1e3;
4141
- var NAG_ELIGIBLE_SUBCOMMANDS = /* @__PURE__ */ new Set([
4142
- "install",
4143
- "add",
4144
- "remove",
4145
- "list",
4146
- "doctor",
4147
- "servers",
4148
- "bundles",
4149
- "compliance",
4150
- "upgrade",
4151
- "try",
4152
- "try-cleanup",
4153
- "reset-learning",
4154
- "login",
4155
- "logout",
4156
- "sync",
4157
- "stats",
4158
- "secrets"
4159
- ]);
4160
- function emptyNagState() {
4161
- return { touchPoints: 0, nextThreshold: MIN_THRESHOLD, lastShownAt: 0 };
4162
- }
4163
- function nagStatePath(home = homedir8()) {
4164
- return join8(home, CONFIG_DIRNAME, NAG_STATE_FILENAME);
4165
- }
4166
- function isFileNotFound2(err) {
4167
- return !!err && typeof err === "object" && "code" in err && err.code === "ENOENT";
4168
- }
4169
- async function loadNagState(filePath = nagStatePath()) {
4170
- try {
4171
- const raw = await readFile7(filePath, "utf8");
4172
- const parsed = JSON.parse(raw);
4173
- if (!parsed || typeof parsed !== "object") return emptyNagState();
4174
- const p = parsed;
4175
- const touchPoints = typeof p.touchPoints === "number" && Number.isFinite(p.touchPoints) && p.touchPoints >= 0 ? p.touchPoints : 0;
4176
- const nextThreshold = typeof p.nextThreshold === "number" && Number.isFinite(p.nextThreshold) && p.nextThreshold >= MIN_THRESHOLD ? Math.min(p.nextThreshold, MAX_THRESHOLD) : MIN_THRESHOLD;
4177
- const lastShownAt = typeof p.lastShownAt === "number" && Number.isFinite(p.lastShownAt) && p.lastShownAt >= 0 ? p.lastShownAt : 0;
4178
- return { touchPoints, nextThreshold, lastShownAt };
4179
- } catch (err) {
4180
- if (isFileNotFound2(err)) return emptyNagState();
4181
- log("warn", "Failed to load nag state, starting fresh", {
4182
- error: err instanceof Error ? err.message : String(err)
4183
- });
4184
- return emptyNagState();
4185
- }
4186
- }
4187
- async function saveNagState(state, filePath = nagStatePath()) {
4188
- try {
4189
- await atomicWriteFile(filePath, JSON.stringify(state, null, 2));
4190
- } catch (err) {
4191
- log("warn", "Failed to save nag state", {
4192
- error: err instanceof Error ? err.message : String(err)
4193
- });
4194
- }
4195
- }
4196
- function evaluateNag(state, now = Date.now(), random = Math.random) {
4197
- const bumped = { ...state, touchPoints: state.touchPoints + 1 };
4198
- if (bumped.touchPoints < bumped.nextThreshold) {
4199
- return { show: false, next: bumped };
4200
- }
4201
- if (now - bumped.lastShownAt < FLOOR_MS) {
4202
- return { show: false, next: { ...bumped, touchPoints: bumped.nextThreshold } };
4203
- }
4204
- return {
4205
- show: true,
4206
- next: {
4207
- touchPoints: 0,
4208
- nextThreshold: pickThreshold(random),
4209
- lastShownAt: now
4210
- }
4211
- };
4212
- }
4213
- function pickThreshold(random = Math.random) {
4214
- const range = MAX_THRESHOLD - MIN_THRESHOLD + 1;
4215
- return MIN_THRESHOLD + Math.floor(random() * range);
4216
- }
4217
- async function recordTouchPoint(opts = {}) {
4218
- const filePath = opts.filePath ?? nagStatePath(opts.home);
4219
- const state = await loadNagState(filePath);
4220
- const decision = evaluateNag(state, opts.now, opts.random);
4221
- await saveNagState(decision.next, filePath);
4222
- return decision;
4223
- }
4224
- async function showNagInterstitial(opts = {}) {
4225
- const stdout = opts.stdout ?? process.stdout;
4226
- const stdin = opts.stdin ?? process.stdin;
4227
- const tty = opts.isTTY ?? (stdout.isTTY === true && stdin.isTTY === true);
4228
- if (!tty) return;
4229
- const content = [
4230
- "Yaw MCP -- support the project",
4231
- "",
4232
- "You're using Yaw MCP free -- all features included.",
4233
- "",
4234
- "Working with a team? Yaw Team ($15/seat/mo or",
4235
- "$150/seat/yr) adds:",
4236
- " * shared team bundles across every seat",
4237
- " * shared org secrets",
4238
- " * per-seat audit log",
4239
- " * SSO",
4240
- "",
4241
- "Learn more: https://yaw.sh/mcp",
4242
- "",
4243
- "Press Enter to continue (Ctrl-C to quit)."
4244
- ];
4245
- const PAD = 2;
4246
- const inner = Math.max(...content.map((l) => l.length));
4247
- const border = `+${"-".repeat(inner + PAD * 2)}+`;
4248
- const boxed = content.map((l) => `|${" ".repeat(PAD)}${l.padEnd(inner)}${" ".repeat(PAD)}|`);
4249
- const lines = ["", border, ...boxed, border, ""];
4250
- stdout.write(`${lines.join("\n")}
4251
- `);
4252
- await new Promise((resolve5) => {
4253
- const onData = () => {
4254
- try {
4255
- stdin.off?.("data", onData);
4256
- } catch {
4257
- }
4258
- resolve5();
4259
- };
4260
- try {
4261
- stdin.on("data", onData);
4262
- stdin.resume?.();
4263
- } catch {
4264
- resolve5();
4265
- }
4266
- });
4267
- }
4268
-
4269
4357
  // src/reset-learning-cmd.ts
4270
4358
  import { unlink as unlink2 } from "fs/promises";
4271
- import { homedir as homedir9 } from "os";
4272
- import { join as join9 } from "path";
4359
+ import { homedir as homedir8 } from "os";
4360
+ import { join as join8 } from "path";
4273
4361
  var RESET_LEARNING_USAGE = `Usage: yaw-mcp reset-learning
4274
4362
 
4275
4363
  Delete ~/.yaw-mcp/state.json so cross-session learning starts fresh.
@@ -4291,7 +4379,7 @@ ${RESET_LEARNING_USAGE}`
4291
4379
  return { kind: "ok", options: {} };
4292
4380
  }
4293
4381
  async function runResetLearning(opts = {}) {
4294
- const home = opts.home ?? homedir9();
4382
+ const home = opts.home ?? homedir8();
4295
4383
  const env = opts.env ?? process.env;
4296
4384
  const write = opts.out ?? ((s) => process.stdout.write(s));
4297
4385
  const writeErr = opts.err ?? ((s) => process.stderr.write(s));
@@ -4306,7 +4394,7 @@ async function runResetLearning(opts = {}) {
4306
4394
  writeErr(`${s}
4307
4395
  `);
4308
4396
  };
4309
- const filePath = join9(userConfigDir(home), STATE_FILENAME);
4397
+ const filePath = join8(userConfigDir(home), STATE_FILENAME);
4310
4398
  const raw = env.YAW_MCP_DISABLE_PERSISTENCE;
4311
4399
  const disabled = raw !== void 0 && raw !== "" && (raw === "1" || raw.toLowerCase() === "true");
4312
4400
  if (disabled) {
@@ -4319,7 +4407,7 @@ async function runResetLearning(opts = {}) {
4319
4407
  try {
4320
4408
  await unlink2(filePath);
4321
4409
  } catch (err) {
4322
- if (isFileNotFound3(err)) {
4410
+ if (isFileNotFound2(err)) {
4323
4411
  print("yaw-mcp reset-learning: no persisted state to reset.");
4324
4412
  print(` path: ${filePath}`);
4325
4413
  return { exitCode: 0, lines, removed: false, path: filePath };
@@ -4334,21 +4422,21 @@ async function runResetLearning(opts = {}) {
4334
4422
  print(` pack history entries removed: ${packCount}`);
4335
4423
  return { exitCode: 0, lines, removed: true, path: filePath };
4336
4424
  }
4337
- function isFileNotFound3(err) {
4425
+ function isFileNotFound2(err) {
4338
4426
  return !!err && typeof err === "object" && "code" in err && err.code === "ENOENT";
4339
4427
  }
4340
4428
 
4341
4429
  // src/secrets-cmd.ts
4342
4430
  import { existsSync as existsSync6 } from "fs";
4343
4431
  import { mkdir as mkdir3 } from "fs/promises";
4344
- import { homedir as homedir11 } from "os";
4432
+ import { homedir as homedir10 } from "os";
4345
4433
  import { dirname as dirname3 } from "path";
4346
4434
 
4347
4435
  // src/secrets-vault.ts
4348
4436
  import { existsSync as existsSync5 } from "fs";
4349
- import { chmod as chmod3, readFile as readFile8 } from "fs/promises";
4350
- import { homedir as homedir10 } from "os";
4351
- import { dirname as dirname2, join as join10 } from "path";
4437
+ import { chmod as chmod3, readFile as readFile7 } from "fs/promises";
4438
+ import { homedir as homedir9 } from "os";
4439
+ import { dirname as dirname2, join as join9 } from "path";
4352
4440
 
4353
4441
  // src/secrets-crypto.ts
4354
4442
  import { createCipheriv, createDecipheriv, randomBytes, scrypt as scryptCb } from "crypto";
@@ -4406,8 +4494,8 @@ function decryptEntry(entry, key) {
4406
4494
  // src/secrets-vault.ts
4407
4495
  var SECRETS_FILENAME = "secrets.json";
4408
4496
  var SECRETS_SCHEMA_VERSION = 1;
4409
- function vaultPath(home = homedir10()) {
4410
- return join10(home, CONFIG_DIRNAME, SECRETS_FILENAME);
4497
+ function vaultPath(home = homedir9()) {
4498
+ return join9(home, CONFIG_DIRNAME, SECRETS_FILENAME);
4411
4499
  }
4412
4500
  function emptyVault() {
4413
4501
  return {
@@ -4420,7 +4508,7 @@ async function loadVault(path3) {
4420
4508
  if (!existsSync5(path3)) return null;
4421
4509
  let raw;
4422
4510
  try {
4423
- raw = await readFile8(path3, "utf8");
4511
+ raw = await readFile7(path3, "utf8");
4424
4512
  } catch (err) {
4425
4513
  log("warn", "Failed to read vault", { path: path3, error: err instanceof Error ? err.message : String(err) });
4426
4514
  return null;
@@ -4680,7 +4768,7 @@ async function runSecrets(opts, io = {
4680
4768
  out: (s) => process.stdout.write(s),
4681
4769
  err: (s) => process.stderr.write(s)
4682
4770
  }) {
4683
- const home = opts.home ?? homedir11();
4771
+ const home = opts.home ?? homedir10();
4684
4772
  const path3 = vaultPath(home);
4685
4773
  if (opts.action === "lock") {
4686
4774
  lock();
@@ -4808,7 +4896,7 @@ async function runSecrets(opts, io = {
4808
4896
  }
4809
4897
  var MCP_SECRETS_RESOURCE = "mcp_secrets";
4810
4898
  async function runSecretsPush(opts, io) {
4811
- const home = opts.home ?? homedir11();
4899
+ const home = opts.home ?? homedir10();
4812
4900
  const path3 = vaultPath(home);
4813
4901
  const session = await getSession({ home, baseUrl: opts.baseUrl });
4814
4902
  if (!session) {
@@ -4871,7 +4959,7 @@ async function runSecretsPush(opts, io) {
4871
4959
  }
4872
4960
  }
4873
4961
  async function runSecretsPull(opts, io) {
4874
- const home = opts.home ?? homedir11();
4962
+ const home = opts.home ?? homedir10();
4875
4963
  const path3 = vaultPath(home);
4876
4964
  const session = await getSession({ home, baseUrl: opts.baseUrl });
4877
4965
  if (!session) {
@@ -4926,8 +5014,8 @@ async function runSecretsPull(opts, io) {
4926
5014
  }
4927
5015
 
4928
5016
  // src/server.ts
4929
- import { readFile as readFile10 } from "fs/promises";
4930
- import { homedir as homedir12 } from "os";
5017
+ import { readFile as readFile9 } from "fs/promises";
5018
+ import { homedir as homedir11 } from "os";
4931
5019
  import { isAbsolute, relative, resolve as resolve4 } from "path";
4932
5020
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4933
5021
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -4943,194 +5031,6 @@ import { request as request10 } from "undici";
4943
5031
 
4944
5032
  // src/auto-upgrade.ts
4945
5033
  import { spawn as spawn3 } from "child_process";
4946
-
4947
- // src/upgrade-cmd.ts
4948
- import { spawn as spawn2 } from "child_process";
4949
- var UPGRADE_USAGE = `Usage: yaw-mcp upgrade [--run] [--json]
4950
-
4951
- Show (or execute) the command to upgrade @yawlabs/mcp to the latest version.
4952
-
4953
- --run If this install is global (npm install -g), spawn the upgrade
4954
- command. No-op for npx installs \u2014 they always fetch the latest.
4955
- --json Emit a machine-readable snapshot ({ current, latest, stale,
4956
- method, command }) instead of prose.`;
4957
- function parseUpgradeArgs(argv) {
4958
- const opts = {};
4959
- for (const a of argv) {
4960
- if (a === "--run") opts.run = true;
4961
- else if (a === "--json") opts.json = true;
4962
- else if (a === "--help" || a === "-h") return { ok: false, error: UPGRADE_USAGE };
4963
- else return { ok: false, error: `yaw-mcp upgrade: unknown argument "${a}"
4964
-
4965
- ${UPGRADE_USAGE}` };
4966
- }
4967
- return { ok: true, options: opts };
4968
- }
4969
- function detectInstallMethod(argvPath) {
4970
- if (!argvPath) return "unknown";
4971
- const normalized = argvPath.replace(/\\/g, "/");
4972
- if (/\/_npx\//.test(normalized)) return "npx";
4973
- if (/\/npm\/node_modules\/@yawlabs\/mcp\//.test(normalized)) return "global-npm";
4974
- if (/\/lib\/node_modules\/@yawlabs\/mcp\//.test(normalized)) return "global-npm";
4975
- if (/\/AppData\/Roaming\/npm\/node_modules\/@yawlabs\/mcp\//.test(normalized)) return "global-npm";
4976
- if (/\/node_modules\/@yawlabs\/mcp\//.test(normalized)) return "local-node-modules";
4977
- if (/\/(yaw-mcp|mcph)\/(dist|src)\//.test(normalized)) return "dev-checkout";
4978
- return "unknown";
4979
- }
4980
- function buildUpgradePlan(input) {
4981
- const { current, latest, method } = input;
4982
- const stale = latest !== null && current !== "dev" && compareSemverLocal(current, latest) < 0;
4983
- let command;
4984
- switch (method) {
4985
- case "global-npm":
4986
- command = "npm install -g @yawlabs/mcp@latest";
4987
- break;
4988
- case "npx":
4989
- command = null;
4990
- break;
4991
- case "local-node-modules":
4992
- command = "npm install @yawlabs/mcp@latest";
4993
- break;
4994
- case "dev-checkout":
4995
- command = "git pull && npm run build";
4996
- break;
4997
- default:
4998
- command = "npm install -g @yawlabs/mcp@latest";
4999
- break;
5000
- }
5001
- return { current, latest, stale, method, command };
5002
- }
5003
- function compareSemverLocal(a, b) {
5004
- const parse = (s) => {
5005
- const m = /^v?(\d+)\.(\d+)\.(\d+)/.exec(s);
5006
- if (!m) return null;
5007
- return [Number(m[1]), Number(m[2]), Number(m[3])];
5008
- };
5009
- const pa = parse(a);
5010
- const pb = parse(b);
5011
- if (!pa || !pb) return 0;
5012
- for (let i = 0; i < 3; i++) {
5013
- if (pa[i] < pb[i]) return -1;
5014
- if (pa[i] > pb[i]) return 1;
5015
- }
5016
- return 0;
5017
- }
5018
- async function defaultFetchLatest() {
5019
- const ac = new AbortController();
5020
- const timer = setTimeout(() => ac.abort(), 3e3);
5021
- try {
5022
- const res = await fetch("https://registry.npmjs.org/@yawlabs/mcp/latest", {
5023
- signal: ac.signal,
5024
- headers: { accept: "application/json" }
5025
- });
5026
- if (!res.ok) return null;
5027
- const body = await res.json();
5028
- return typeof body.version === "string" ? body.version : null;
5029
- } catch {
5030
- return null;
5031
- } finally {
5032
- clearTimeout(timer);
5033
- }
5034
- }
5035
- async function defaultSpawn(cmd, args) {
5036
- return new Promise((resolve5) => {
5037
- const child = spawn2(cmd, args, { stdio: "inherit", shell: process.platform === "win32" });
5038
- child.on("close", (code) => resolve5(typeof code === "number" ? code : 1));
5039
- child.on("error", () => resolve5(1));
5040
- });
5041
- }
5042
- async function runUpgrade(opts = {}) {
5043
- const write = opts.out ?? ((s) => process.stdout.write(s));
5044
- const writeErr = opts.err ?? ((s) => process.stderr.write(s));
5045
- const lines = [];
5046
- const print = (s = "") => {
5047
- lines.push(s);
5048
- write(`${s}
5049
- `);
5050
- };
5051
- const printErr = (s) => {
5052
- lines.push(s);
5053
- writeErr(`${s}
5054
- `);
5055
- };
5056
- const fetcher = opts.fetchLatest ?? defaultFetchLatest;
5057
- const current = opts.currentVersion ?? readCurrentVersion();
5058
- const argvPath = opts.argvPath ?? process.argv[1];
5059
- const method = detectInstallMethod(argvPath);
5060
- let latest;
5061
- try {
5062
- latest = await fetcher();
5063
- } catch {
5064
- latest = null;
5065
- }
5066
- const plan = buildUpgradePlan({ current, latest, method });
5067
- if (opts.json) {
5068
- print(JSON.stringify(plan, null, 2));
5069
- return { exitCode: plan.stale && !opts.run ? 1 : 0, lines };
5070
- }
5071
- if (latest === null) {
5072
- print("yaw-mcp upgrade: couldn't reach the npm registry (offline? firewall?).");
5073
- if (plan.command) {
5074
- print(`When you're back online, run:
5075
- ${plan.command}`);
5076
- } else {
5077
- print("Your install uses `npx -y` \u2014 just restart the MCP client when you're back online.");
5078
- }
5079
- return { exitCode: 0, lines };
5080
- }
5081
- print(`Current: ${current}`);
5082
- print(`Latest: ${latest}`);
5083
- print(`Install: ${method}`);
5084
- if (!plan.stale) {
5085
- print("");
5086
- print("\u2713 You're on the latest version \u2014 nothing to do.");
5087
- return { exitCode: 0, lines };
5088
- }
5089
- print("");
5090
- if (method === "npx") {
5091
- print("Your install uses `npx -y` \u2014 restart the MCP client and it will fetch the new version.");
5092
- return { exitCode: 0, lines };
5093
- }
5094
- if (!plan.command) {
5095
- print("No upgrade command available for this install method.");
5096
- return { exitCode: 0, lines };
5097
- }
5098
- const autoRunnable = method === "global-npm";
5099
- if (!opts.run) {
5100
- if (autoRunnable) {
5101
- print(`Run:
5102
- ${plan.command}
5103
-
5104
- Or re-run with --run to upgrade in place.`);
5105
- } else {
5106
- print(`Suggested command (run it yourself; --run only works for global-npm installs):
5107
- ${plan.command}`);
5108
- }
5109
- return { exitCode: 1, lines };
5110
- }
5111
- if (!autoRunnable) {
5112
- printErr(
5113
- `yaw-mcp upgrade --run: install method "${method}" can't be upgraded automatically. Run manually:
5114
- ${plan.command}`
5115
- );
5116
- return { exitCode: 2, lines };
5117
- }
5118
- const runner = opts.spawnImpl ?? defaultSpawn;
5119
- print(`Running: ${plan.command}`);
5120
- const code = await runner("npm", ["install", "-g", "@yawlabs/mcp@latest"]);
5121
- if (code === 0) {
5122
- print("");
5123
- print(`\u2713 Upgraded @yawlabs/mcp to ${latest}.`);
5124
- return { exitCode: 0, lines };
5125
- }
5126
- printErr(`yaw-mcp upgrade: npm exited ${code}. Try running the command manually.`);
5127
- return { exitCode: 3, lines };
5128
- }
5129
- function readCurrentVersion() {
5130
- return true ? "0.59.0" : "dev";
5131
- }
5132
-
5133
- // src/auto-upgrade.ts
5134
5034
  async function fetchLatestVersion2() {
5135
5035
  const ac = new AbortController();
5136
5036
  const timer = setTimeout(() => ac.abort(), 3e3);
@@ -5175,7 +5075,7 @@ function defaultSpawn2(cmd, args) {
5175
5075
  async function maybeAutoUpgrade(deps = {}) {
5176
5076
  const optOut = process.env.YAW_MCP_AUTO_UPGRADE;
5177
5077
  if (optOut === "0" || optOut?.toLowerCase() === "false") return;
5178
- const current = deps.currentVersion ?? (true ? "0.59.0" : "dev");
5078
+ const current = deps.currentVersion ?? (true ? "0.60.0" : "dev");
5179
5079
  if (current === "dev") return;
5180
5080
  const method = detectInstallMethod(deps.argvPath ?? process.argv[1]);
5181
5081
  const latest = await (deps.fetchLatestImpl ?? fetchLatestVersion2)();
@@ -5187,6 +5087,10 @@ async function maybeAutoUpgrade(deps = {}) {
5187
5087
  (deps.spawnImpl ?? defaultSpawn2)("npm", ["install", "-g", "@yawlabs/mcp@latest"]);
5188
5088
  return;
5189
5089
  }
5090
+ if (method === "bundled-app") {
5091
+ log("info", "yaw-mcp (bundled with Yaw Terminal) is behind npm; it updates with the app", { current, latest });
5092
+ return;
5093
+ }
5190
5094
  log("info", "yaw-mcp is out of date; restart your MCP client to pick up the latest version", {
5191
5095
  current,
5192
5096
  latest,
@@ -5546,13 +5450,13 @@ function stepBindingKey(step, index) {
5546
5450
  }
5547
5451
 
5548
5452
  // src/guide.ts
5549
- import { readFile as readFile9 } from "fs/promises";
5453
+ import { readFile as readFile8 } from "fs/promises";
5550
5454
  var GUIDE_READ_TIMEOUT_MS = 1e3;
5551
5455
  async function readGuide(path3, scope) {
5552
5456
  let raw;
5553
5457
  try {
5554
5458
  raw = await Promise.race([
5555
- readFile9(path3, "utf8"),
5459
+ readFile8(path3, "utf8"),
5556
5460
  new Promise(
5557
5461
  (_, reject) => setTimeout(() => reject(new Error("guide read timeout")), GUIDE_READ_TIMEOUT_MS)
5558
5462
  )
@@ -6890,9 +6794,9 @@ async function readTeamCookie() {
6890
6794
  const teamSync = await import("./team-sync-4JF5LBRB.js");
6891
6795
  const session = await teamSync.getSession();
6892
6796
  if (!session) return null;
6893
- const { readFile: readFile12 } = await import("fs/promises");
6797
+ const { readFile: readFile11 } = await import("fs/promises");
6894
6798
  try {
6895
- const raw = await readFile12(teamSync.sessionStatePath(), "utf8");
6799
+ const raw = await readFile11(teamSync.sessionStatePath(), "utf8");
6896
6800
  const parsed = JSON.parse(raw);
6897
6801
  return typeof parsed.cookie === "string" && parsed.cookie ? parsed.cookie : null;
6898
6802
  } catch {
@@ -7444,7 +7348,7 @@ function categorizeSpawnError(err) {
7444
7348
  }
7445
7349
  async function connectToUpstream(config, onDisconnect, onListChanged) {
7446
7350
  const client = new Client(
7447
- { name: "yaw-mcp", version: true ? "0.59.0" : "dev" },
7351
+ { name: "yaw-mcp", version: true ? "0.60.0" : "dev" },
7448
7352
  { capabilities: {} }
7449
7353
  );
7450
7354
  let transport;
@@ -7753,7 +7657,7 @@ var ConnectServer = class _ConnectServer {
7753
7657
  this.apiUrl = apiUrl5;
7754
7658
  this.token = token5;
7755
7659
  this.server = new Server(
7756
- { name: "yaw-mcp", version: true ? "0.59.0" : "dev" },
7660
+ { name: "yaw-mcp", version: true ? "0.60.0" : "dev" },
7757
7661
  {
7758
7662
  capabilities: {
7759
7663
  tools: { listChanged: true },
@@ -9284,7 +9188,7 @@ ${activeCount} loaded in this session, ${totalTools} tools in context${tokenSumm
9284
9188
  }
9285
9189
  const ALLOWED_FILENAMES = ["claude_desktop_config.json", "mcp.json", "settings.json", "mcp_config.json"];
9286
9190
  try {
9287
- const resolved = filepath.startsWith("~/") || filepath.startsWith("~\\") ? resolve4(homedir12(), filepath.slice(2)) : resolve4(filepath);
9191
+ const resolved = filepath.startsWith("~/") || filepath.startsWith("~\\") ? resolve4(homedir11(), filepath.slice(2)) : resolve4(filepath);
9288
9192
  const resolvedBasename = resolved.split(/[/\\]/).pop() || "";
9289
9193
  if (!ALLOWED_FILENAMES.includes(resolvedBasename)) {
9290
9194
  return {
@@ -9301,7 +9205,7 @@ ${activeCount} loaded in this session, ${totalTools} tools in context${tokenSumm
9301
9205
  const rel = relative(base, p);
9302
9206
  return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
9303
9207
  };
9304
- if (!isUnder(homedir12(), resolved) && !isUnder(process.cwd(), resolved)) {
9208
+ if (!isUnder(homedir11(), resolved) && !isUnder(process.cwd(), resolved)) {
9305
9209
  return {
9306
9210
  content: [
9307
9211
  { type: "text", text: "Import path must be under your home directory or the current working directory." }
@@ -9309,7 +9213,7 @@ ${activeCount} loaded in this session, ${totalTools} tools in context${tokenSumm
9309
9213
  isError: true
9310
9214
  };
9311
9215
  }
9312
- const raw = await readFile10(resolved, "utf-8");
9216
+ const raw = await readFile9(resolved, "utf-8");
9313
9217
  const parsed = JSON.parse(raw);
9314
9218
  if (!parsed.mcpServers || typeof parsed.mcpServers !== "object" || Array.isArray(parsed.mcpServers)) {
9315
9219
  return {
@@ -10035,7 +9939,7 @@ function truncateVersion(v) {
10035
9939
  }
10036
9940
 
10037
9941
  // src/stats-cmd.ts
10038
- import { homedir as homedir13 } from "os";
9942
+ import { homedir as homedir12 } from "os";
10039
9943
  var STATS_USAGE = `Usage: yaw-mcp stats [--json] [--limit N] [--days N]
10040
9944
 
10041
9945
  Print a digest of recent AI tool calls recorded against your Yaw
@@ -10150,7 +10054,7 @@ async function runStats(opts, io = {
10150
10054
  out: (s) => process.stdout.write(s),
10151
10055
  err: (s) => process.stderr.write(s)
10152
10056
  }) {
10153
- const home = opts.home ?? homedir13();
10057
+ const home = opts.home ?? homedir12();
10154
10058
  const session = await getSession({ home, baseUrl: opts.baseUrl });
10155
10059
  if (!session) {
10156
10060
  const msg = "Not signed in. Yaw MCP analytics requires a Yaw Team account.\n - Yaw Team: $15/seat/mo or $150/seat/yr -- https://yaw.sh/mcp\nSign in with: yaw-mcp login --key <license-key>";
@@ -10208,9 +10112,9 @@ async function runStats(opts, io = {
10208
10112
 
10209
10113
  // src/sync-cmd.ts
10210
10114
  import { existsSync as existsSync7 } from "fs";
10211
- import { mkdir as mkdir4, readFile as readFile11 } from "fs/promises";
10212
- import { homedir as homedir14 } from "os";
10213
- import { dirname as dirname4, join as join11 } from "path";
10115
+ import { mkdir as mkdir4, readFile as readFile10 } from "fs/promises";
10116
+ import { homedir as homedir13 } from "os";
10117
+ import { dirname as dirname4, join as join10 } from "path";
10214
10118
  var SYNC_USAGE = `Usage: yaw-mcp sync <push|pull|status> [--json]
10215
10119
 
10216
10120
  Replicate ~/.yaw-mcp/bundles.json across machines via your Yaw
@@ -10253,12 +10157,12 @@ ${SYNC_USAGE}` };
10253
10157
  return { ok: true, options: opts };
10254
10158
  }
10255
10159
  function bundlesPath(home) {
10256
- return join11(home, CONFIG_DIRNAME, BUNDLES_FILENAME2);
10160
+ return join10(home, CONFIG_DIRNAME, BUNDLES_FILENAME2);
10257
10161
  }
10258
10162
  async function readLocalBundles(home) {
10259
10163
  const path3 = bundlesPath(home);
10260
10164
  if (!existsSync7(path3)) return { version: 1, servers: [] };
10261
- const raw = await readFile11(path3, "utf8");
10165
+ const raw = await readFile10(path3, "utf8");
10262
10166
  const parsed = JSON.parse(raw);
10263
10167
  if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.servers)) {
10264
10168
  throw new Error(`${path3}: malformed -- expected { servers: [...] }`);
@@ -10295,7 +10199,7 @@ async function runSync(opts, io = {
10295
10199
  out: (s) => process.stdout.write(s),
10296
10200
  err: (s) => process.stderr.write(s)
10297
10201
  }) {
10298
- const home = opts.home ?? homedir14();
10202
+ const home = opts.home ?? homedir13();
10299
10203
  const session = await getSession({ home, baseUrl: opts.baseUrl });
10300
10204
  if (!session) {
10301
10205
  const msg = "Not signed in. Run `yaw-mcp login --key <license-key>` first.";
@@ -10461,32 +10365,6 @@ var KNOWN_SUBCOMMANDS = [
10461
10365
  "-V"
10462
10366
  ];
10463
10367
  var subcommand = process.argv[2];
10464
- if (subcommand && NAG_ELIGIBLE_SUBCOMMANDS.has(subcommand) && process.env.YAW_MCP_NO_NAG !== "1") {
10465
- const envHasToken = typeof process.env.YAW_MCP_TOKEN === "string" && process.env.YAW_MCP_TOKEN.length > 0;
10466
- let inAccountMode = envHasToken;
10467
- if (!inAccountMode) {
10468
- try {
10469
- const cfg = await loadYawMcpConfig();
10470
- inAccountMode = Boolean(cfg.token);
10471
- } catch {
10472
- inAccountMode = false;
10473
- }
10474
- }
10475
- if (!inAccountMode) {
10476
- try {
10477
- const session = await getSession();
10478
- inAccountMode = session !== null;
10479
- } catch {
10480
- inAccountMode = false;
10481
- }
10482
- }
10483
- if (!inAccountMode) {
10484
- const decision = await recordTouchPoint();
10485
- if (decision.show) {
10486
- await showNagInterstitial();
10487
- }
10488
- }
10489
- }
10490
10368
  if (subcommand === "compliance") {
10491
10369
  runComplianceCommand(process.argv.slice(3)).then((code) => process.exit(code));
10492
10370
  } else if (subcommand === "install") {
@@ -10688,9 +10566,9 @@ if (subcommand === "compliance") {
10688
10566
  fish, or powershell. Redirect to your
10689
10567
  completions directory to install.
10690
10568
 
10691
- Account / sync (Pro + Team):
10692
- login Authenticate this machine with a Yaw MCP account
10693
- (Pro/Team). --key <license> to pass the key inline.
10569
+ Account / sync (Yaw Team):
10570
+ login Authenticate this machine with a Yaw MCP account.
10571
+ --key <license> to pass the key inline.
10694
10572
  logout Sign this machine out of the account.
10695
10573
  sync <push|pull|status> Replicate your local bundles.json to/from the
10696
10574
  account store (env values stripped on push).
@@ -10750,7 +10628,7 @@ if (subcommand === "compliance") {
10750
10628
  `);
10751
10629
  process.exit(0);
10752
10630
  } else if (subcommand === "--version" || subcommand === "-V") {
10753
- process.stdout.write(`yaw-mcp ${true ? "0.59.0" : "dev"}
10631
+ process.stdout.write(`yaw-mcp ${true ? "0.60.0" : "dev"}
10754
10632
  `);
10755
10633
  process.exit(0);
10756
10634
  } else if (subcommand && !subcommand.startsWith("-")) {
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@yawlabs/mcp",
3
- "version": "0.59.0",
3
+ "version": "0.60.0",
4
4
  "mcpName": "io.github.YawLabs/mcp",
5
- "description": "Yaw MCP -- MCP servers, managed. Run locally, sync across machines with Pro.",
5
+ "description": "Yaw MCP -- MCP servers, managed. Free to run locally; Yaw Team adds cross-machine sync.",
6
6
  "license": "UNLICENSED",
7
7
  "author": "Yaw Labs <support@yaw.sh> (https://yaw.sh/mcp)",
8
8
  "type": "module",
9
9
  "bin": {
10
- "yaw-mcp": "./dist/index.js"
10
+ "yaw-mcp": "dist/index.js"
11
11
  },
12
12
  "files": [
13
13
  "dist",