@mushi-mushi/cli 0.30.3 → 0.30.5

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.
@@ -2,6 +2,14 @@
2
2
  var TEST_REPORT_TIMEOUT_MS = 1e4;
3
3
  var TEST_REPORT_FETCH_TIMEOUT_MS = TEST_REPORT_TIMEOUT_MS;
4
4
  var CLOUD_API_ENDPOINT = "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api";
5
+ var CLOUD_MCP_OAUTH_URL = "https://kensaur.us/mushi-mushi/hosted-mcp/";
6
+ function firstEndpoint(...candidates) {
7
+ for (const c of candidates) {
8
+ const v = c?.trim();
9
+ if (v) return v;
10
+ }
11
+ return void 0;
12
+ }
5
13
  function resolveCloudEndpoint(explicit) {
6
14
  if (explicit?.trim()) return explicit.trim();
7
15
  const fromEnv = process.env.MUSHI_API_ENDPOINT?.trim();
@@ -36,6 +44,8 @@ function normalizeEndpoint(url) {
36
44
  export {
37
45
  TEST_REPORT_FETCH_TIMEOUT_MS,
38
46
  CLOUD_API_ENDPOINT,
47
+ CLOUD_MCP_OAUTH_URL,
48
+ firstEndpoint,
39
49
  resolveCloudEndpoint,
40
50
  assertEndpoint,
41
51
  normalizeEndpoint
@@ -0,0 +1,8 @@
1
+ // src/version.ts
2
+ var MUSHI_CLI_VERSION = true ? "0.30.5" : "0.0.0-dev";
3
+ var MUSHI_MCP_PIN_SPEC = "@mushi-mushi/mcp@0.26.0";
4
+
5
+ export {
6
+ MUSHI_CLI_VERSION,
7
+ MUSHI_MCP_PIN_SPEC
8
+ };
@@ -1,24 +1,23 @@
1
1
  import {
2
2
  MUSHI_CLI_VERSION
3
- } from "./chunk-ZQFQRBNL.js";
3
+ } from "./chunk-AZZNO4T4.js";
4
4
  import {
5
5
  assertEndpoint,
6
6
  normalizeEndpoint
7
- } from "./chunk-SXSI2W6Z.js";
8
- import {
9
- __require
10
- } from "./chunk-3RG5ZIWI.js";
7
+ } from "./chunk-2DMHQVRU.js";
11
8
 
12
9
  // src/keychain.ts
10
+ import { createRequire } from "module";
13
11
  var KEYCHAIN_SERVICE = "mushi-cli";
14
12
  function accountName(profile = "default") {
15
13
  return `api-key:${profile}`;
16
14
  }
17
15
  var _EntryClass = void 0;
18
16
  function getEntryClass() {
17
+ if (process.env["MUSHI_NO_KEYCHAIN"]) return null;
19
18
  if (_EntryClass === void 0) {
20
19
  try {
21
- const mod = __require("@napi-rs/keyring");
20
+ const mod = createRequire(import.meta.url)("@napi-rs/keyring");
22
21
  _EntryClass = mod.Entry ?? null;
23
22
  } catch {
24
23
  _EntryClass = null;
@@ -108,11 +107,13 @@ function loadConfig(path = CONFIG_PATH, opts = {}) {
108
107
  if (keychainKey) {
109
108
  file = { ...file, apiKey: keychainKey };
110
109
  }
111
- const endpointFromEnv = process.env["MUSHI_API_ENDPOINT"] ?? process.env["MUSHI_ENDPOINT"] ?? void 0;
110
+ const endpointFromEnv = process.env["MUSHI_API_ENDPOINT"]?.trim() || process.env["MUSHI_ENDPOINT"]?.trim() || void 0;
112
111
  const fromEnv = {
113
112
  ...process.env["MUSHI_API_KEY"] ? { apiKey: process.env["MUSHI_API_KEY"] } : {},
114
113
  ...process.env["MUSHI_PROJECT_ID"] ? { projectId: process.env["MUSHI_PROJECT_ID"] } : {},
115
- ...endpointFromEnv ? { endpoint: endpointFromEnv } : {},
114
+ // Saved endpoints are normalized by assertEndpoint; the env value isn't,
115
+ // so strip trailing slashes here (no `.../api//v1/...` in any caller).
116
+ ...endpointFromEnv ? { endpoint: normalizeEndpoint(endpointFromEnv) } : {},
116
117
  ...process.env["MUSHI_CONSOLE_URL"] ? { consoleUrl: process.env["MUSHI_CONSOLE_URL"].trim() } : {}
117
118
  };
118
119
  return { ...file, ...fromEnv };
@@ -495,7 +496,7 @@ function normalizeApiError(error, httpStatus) {
495
496
  }
496
497
  async function apiCall(path, config, options = {}, callOptions = {}) {
497
498
  const timeoutMs = callOptions.timeoutMs ?? API_TIMEOUT_MS;
498
- const endpoint = config.endpoint;
499
+ const endpoint = config.endpoint ? normalizeEndpoint(config.endpoint) : config.endpoint;
499
500
  if (!endpoint) {
500
501
  return {
501
502
  ok: false,
@@ -2,20 +2,17 @@
2
2
  import {
3
3
  MUSHI_CLI_VERSION,
4
4
  MUSHI_MCP_PIN_SPEC
5
- } from "./chunk-VJZVFEMZ.js";
6
- import {
7
- detectFramework,
8
- envVarsToWrite,
9
- readPackageJson
10
- } from "./chunk-KOBEOGNF.js";
5
+ } from "./chunk-LKC2BEKQ.js";
11
6
  import {
12
7
  CLOUD_API_ENDPOINT,
13
8
  assertEndpoint,
14
9
  normalizeEndpoint
15
- } from "./chunk-LDPFTQLE.js";
10
+ } from "./chunk-LGGE3LH7.js";
16
11
  import {
17
- __require
18
- } from "./chunk-PDX44BCA.js";
12
+ detectFramework,
13
+ envVarsToWrite,
14
+ readPackageJson
15
+ } from "./chunk-KOBEOGNF.js";
19
16
 
20
17
  // src/connect.ts
21
18
  import { appendFile, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
@@ -29,15 +26,17 @@ import { homedir } from "os";
29
26
  import { dirname, join } from "path";
30
27
 
31
28
  // src/keychain.ts
29
+ import { createRequire } from "module";
32
30
  var KEYCHAIN_SERVICE = "mushi-cli";
33
31
  function accountName(profile = "default") {
34
32
  return `api-key:${profile}`;
35
33
  }
36
34
  var _EntryClass = void 0;
37
35
  function getEntryClass() {
36
+ if (process.env["MUSHI_NO_KEYCHAIN"]) return null;
38
37
  if (_EntryClass === void 0) {
39
38
  try {
40
- const mod = __require("@napi-rs/keyring");
39
+ const mod = createRequire(import.meta.url)("@napi-rs/keyring");
41
40
  _EntryClass = mod.Entry ?? null;
42
41
  } catch {
43
42
  _EntryClass = null;
@@ -68,6 +67,21 @@ function tryLoadKeyFromKeychain(profile = "default") {
68
67
  return null;
69
68
  }
70
69
  }
70
+ function tryDeleteKeyFromKeychain(profile = "default") {
71
+ const EntryClass = getEntryClass();
72
+ if (!EntryClass) return;
73
+ try {
74
+ const entry = new EntryClass(KEYCHAIN_SERVICE, accountName(profile));
75
+ entry.deletePassword();
76
+ } catch {
77
+ }
78
+ }
79
+ function syncKeychainAfterKeyChange(apiKey, profile = "default") {
80
+ if (tryLoadKeyFromKeychain(profile) === null) return "none";
81
+ if (trySaveKeyToKeychain(apiKey, profile) && tryLoadKeyFromKeychain(profile) === apiKey) return "updated";
82
+ tryDeleteKeyFromKeychain(profile);
83
+ return "removed";
84
+ }
71
85
 
72
86
  // src/config.ts
73
87
  var DEFAULT_PROFILE = "default";
@@ -122,11 +136,13 @@ function loadConfig(path = CONFIG_PATH, opts = {}) {
122
136
  if (keychainKey) {
123
137
  file = { ...file, apiKey: keychainKey };
124
138
  }
125
- const endpointFromEnv = process.env["MUSHI_API_ENDPOINT"] ?? process.env["MUSHI_ENDPOINT"] ?? void 0;
139
+ const endpointFromEnv = process.env["MUSHI_API_ENDPOINT"]?.trim() || process.env["MUSHI_ENDPOINT"]?.trim() || void 0;
126
140
  const fromEnv = {
127
141
  ...process.env["MUSHI_API_KEY"] ? { apiKey: process.env["MUSHI_API_KEY"] } : {},
128
142
  ...process.env["MUSHI_PROJECT_ID"] ? { projectId: process.env["MUSHI_PROJECT_ID"] } : {},
129
- ...endpointFromEnv ? { endpoint: endpointFromEnv } : {},
143
+ // Saved endpoints are normalized by assertEndpoint; the env value isn't,
144
+ // so strip trailing slashes here (no `.../api//v1/...` in any caller).
145
+ ...endpointFromEnv ? { endpoint: normalizeEndpoint(endpointFromEnv) } : {},
130
146
  ...process.env["MUSHI_CONSOLE_URL"] ? { consoleUrl: process.env["MUSHI_CONSOLE_URL"].trim() } : {}
131
147
  };
132
148
  return { ...file, ...fromEnv };
@@ -564,7 +580,7 @@ function normalizeApiError(error, httpStatus) {
564
580
  }
565
581
  async function apiCall(path, config, options = {}, callOptions = {}) {
566
582
  const timeoutMs = callOptions.timeoutMs ?? API_TIMEOUT_MS;
567
- const endpoint = config.endpoint;
583
+ const endpoint = config.endpoint ? normalizeEndpoint(config.endpoint) : config.endpoint;
568
584
  if (!endpoint) {
569
585
  return {
570
586
  ok: false,
@@ -878,8 +894,8 @@ var MCP_CLIENT_LABEL = {
878
894
  continue: "Continue",
879
895
  zed: "Zed"
880
896
  };
881
- function buildHostedMcpServerBlock(url) {
882
- return { type: "http", url };
897
+ function buildHostedMcpServerBlock(url, oauth) {
898
+ return oauth ? { type: "http", url, oauth } : { type: "http", url };
883
899
  }
884
900
  function isPlainObject(value) {
885
901
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -1099,6 +1115,7 @@ export {
1099
1115
  printAndExit,
1100
1116
  installSignalHandlers,
1101
1117
  trySaveKeyToKeychain,
1118
+ syncKeychainAfterKeyChange,
1102
1119
  savedSdkKeyFor,
1103
1120
  resolveProfileName,
1104
1121
  CONFIG_PATH,
@@ -4,6 +4,14 @@
4
4
  var TEST_REPORT_TIMEOUT_MS = 1e4;
5
5
  var TEST_REPORT_FETCH_TIMEOUT_MS = TEST_REPORT_TIMEOUT_MS;
6
6
  var CLOUD_API_ENDPOINT = "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api";
7
+ var CLOUD_MCP_OAUTH_URL = "https://kensaur.us/mushi-mushi/hosted-mcp/";
8
+ function firstEndpoint(...candidates) {
9
+ for (const c of candidates) {
10
+ const v = c?.trim();
11
+ if (v) return v;
12
+ }
13
+ return void 0;
14
+ }
7
15
  function resolveCloudEndpoint(explicit) {
8
16
  if (explicit?.trim()) return explicit.trim();
9
17
  const fromEnv = process.env.MUSHI_API_ENDPOINT?.trim();
@@ -38,6 +46,8 @@ function normalizeEndpoint(url) {
38
46
  export {
39
47
  TEST_REPORT_FETCH_TIMEOUT_MS,
40
48
  CLOUD_API_ENDPOINT,
49
+ CLOUD_MCP_OAUTH_URL,
50
+ firstEndpoint,
41
51
  resolveCloudEndpoint,
42
52
  assertEndpoint,
43
53
  normalizeEndpoint
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/version.ts
4
+ var MUSHI_CLI_VERSION = true ? "0.30.5" : "0.0.0-dev";
5
+ var MUSHI_MCP_PIN_SPEC = "@mushi-mushi/mcp@0.26.0";
6
+
7
+ export {
8
+ MUSHI_CLI_VERSION,
9
+ MUSHI_MCP_PIN_SPEC
10
+ };
@@ -2,11 +2,10 @@
2
2
  import {
3
3
  resolveSdkEnvKey,
4
4
  runConnect
5
- } from "./chunk-ZRDGR4TV.js";
6
- import "./chunk-VJZVFEMZ.js";
5
+ } from "./chunk-HVNXKTTL.js";
6
+ import "./chunk-LKC2BEKQ.js";
7
+ import "./chunk-LGGE3LH7.js";
7
8
  import "./chunk-KOBEOGNF.js";
8
- import "./chunk-LDPFTQLE.js";
9
- import "./chunk-PDX44BCA.js";
10
9
  export {
11
10
  resolveSdkEnvKey,
12
11
  runConnect
@@ -6,10 +6,10 @@ import {
6
6
  saveConfig,
7
7
  savedSdkKeyFor,
8
8
  waitForIngestReady
9
- } from "./chunk-LBVNX3PN.js";
9
+ } from "./chunk-EZJN53QO.js";
10
10
  import {
11
11
  MUSHI_MCP_PIN_SPEC
12
- } from "./chunk-ZQFQRBNL.js";
12
+ } from "./chunk-AZZNO4T4.js";
13
13
  import {
14
14
  detectFramework,
15
15
  envVarsToWrite,
@@ -17,8 +17,7 @@ import {
17
17
  } from "./chunk-AIZRI665.js";
18
18
  import {
19
19
  assertEndpoint
20
- } from "./chunk-SXSI2W6Z.js";
21
- import "./chunk-3RG5ZIWI.js";
20
+ } from "./chunk-2DMHQVRU.js";
22
21
 
23
22
  // src/connect.ts
24
23
  import { appendFile, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
@@ -9,7 +9,6 @@ import {
9
9
  isFrameworkId,
10
10
  readPackageJson
11
11
  } from "./chunk-KOBEOGNF.js";
12
- import "./chunk-PDX44BCA.js";
13
12
  export {
14
13
  FRAMEWORKS,
15
14
  FRAMEWORK_IDS,
package/dist/detect.js CHANGED
@@ -8,7 +8,6 @@ import {
8
8
  isFrameworkId,
9
9
  readPackageJson
10
10
  } from "./chunk-AIZRI665.js";
11
- import "./chunk-3RG5ZIWI.js";
12
11
  export {
13
12
  FRAMEWORKS,
14
13
  FRAMEWORK_IDS,
@@ -1,15 +1,18 @@
1
1
  import {
2
2
  CLOUD_API_ENDPOINT,
3
+ CLOUD_MCP_OAUTH_URL,
3
4
  TEST_REPORT_FETCH_TIMEOUT_MS,
4
5
  assertEndpoint,
6
+ firstEndpoint,
5
7
  normalizeEndpoint,
6
8
  resolveCloudEndpoint
7
- } from "./chunk-SXSI2W6Z.js";
8
- import "./chunk-3RG5ZIWI.js";
9
+ } from "./chunk-2DMHQVRU.js";
9
10
  export {
10
11
  CLOUD_API_ENDPOINT,
12
+ CLOUD_MCP_OAUTH_URL,
11
13
  TEST_REPORT_FETCH_TIMEOUT_MS,
12
14
  assertEndpoint,
15
+ firstEndpoint,
13
16
  normalizeEndpoint,
14
17
  resolveCloudEndpoint
15
18
  };
@@ -1,16 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CLOUD_API_ENDPOINT,
4
+ CLOUD_MCP_OAUTH_URL,
4
5
  TEST_REPORT_FETCH_TIMEOUT_MS,
5
6
  assertEndpoint,
7
+ firstEndpoint,
6
8
  normalizeEndpoint,
7
9
  resolveCloudEndpoint
8
- } from "./chunk-LDPFTQLE.js";
9
- import "./chunk-PDX44BCA.js";
10
+ } from "./chunk-LGGE3LH7.js";
10
11
  export {
11
12
  CLOUD_API_ENDPOINT,
13
+ CLOUD_MCP_OAUTH_URL,
12
14
  TEST_REPORT_FETCH_TIMEOUT_MS,
13
15
  assertEndpoint,
16
+ firstEndpoint,
14
17
  normalizeEndpoint,
15
18
  resolveCloudEndpoint
16
19
  };
package/dist/index.js CHANGED
@@ -51,13 +51,23 @@ import {
51
51
  savedSdkKeyFor,
52
52
  setActiveProfile,
53
53
  setGlobalOutputFormat,
54
+ syncKeychainAfterKeyChange,
54
55
  trySaveKeyToKeychain,
55
56
  writeMcpServerEntry
56
- } from "./chunk-ZRDGR4TV.js";
57
+ } from "./chunk-HVNXKTTL.js";
57
58
  import {
58
59
  MUSHI_CLI_VERSION,
59
60
  MUSHI_MCP_PIN_SPEC
60
- } from "./chunk-VJZVFEMZ.js";
61
+ } from "./chunk-LKC2BEKQ.js";
62
+ import {
63
+ CLOUD_API_ENDPOINT,
64
+ CLOUD_MCP_OAUTH_URL,
65
+ TEST_REPORT_FETCH_TIMEOUT_MS,
66
+ assertEndpoint,
67
+ firstEndpoint,
68
+ normalizeEndpoint,
69
+ resolveCloudEndpoint
70
+ } from "./chunk-LGGE3LH7.js";
61
71
  import {
62
72
  FRAMEWORKS,
63
73
  detectFramework,
@@ -66,14 +76,6 @@ import {
66
76
  installCommand,
67
77
  readPackageJson
68
78
  } from "./chunk-KOBEOGNF.js";
69
- import {
70
- CLOUD_API_ENDPOINT,
71
- TEST_REPORT_FETCH_TIMEOUT_MS,
72
- assertEndpoint,
73
- normalizeEndpoint,
74
- resolveCloudEndpoint
75
- } from "./chunk-LDPFTQLE.js";
76
- import "./chunk-PDX44BCA.js";
77
79
 
78
80
  // src/index.ts
79
81
  import { Command } from "commander";
@@ -465,7 +467,7 @@ function chooseProjectNonInteractive(projects, preferredName) {
465
467
  return match ? { kind: "existing", id: match.id, name: match.name } : { kind: "new", name: preferredName };
466
468
  }
467
469
  async function runLogin(opts = {}) {
468
- const { CLOUD_API_ENDPOINT: CLOUD_API_ENDPOINT2, resolveCloudEndpoint: resolveCloudEndpoint2 } = await import("./endpoint-TEBF4IH6.js");
470
+ const { CLOUD_API_ENDPOINT: CLOUD_API_ENDPOINT2, resolveCloudEndpoint: resolveCloudEndpoint2 } = await import("./endpoint-MJATXOXM.js");
469
471
  const endpoint = opts.endpoint ? resolveCloudEndpoint2(opts.endpoint) : CLOUD_API_ENDPOINT2;
470
472
  const consoleBase = await resolveConsoleUrl();
471
473
  let apiKey = opts.apiKey;
@@ -556,7 +558,7 @@ error: Could not start login session: ${err instanceof Error ? err.message : Str
556
558
  choice = await resolveProjectChoice(projectsList, ask);
557
559
  rl.close();
558
560
  } else {
559
- const { readPackageJson: readPackageJson2 } = await import("./detect-4PYRZXVK.js");
561
+ const { readPackageJson: readPackageJson2 } = await import("./detect-WZT5X6LP.js");
560
562
  const cwd = process.cwd();
561
563
  const nonInteractive = chooseProjectNonInteractive(
562
564
  projectsList,
@@ -1185,7 +1187,7 @@ var HASH_ROUTER_PATTERNS = [
1185
1187
  /location\.hash\b/,
1186
1188
  /addEventListener\(['"]hashchange['"]/
1187
1189
  ];
1188
- var HASH_TEMPLATE_PATTERN = /['"`]#\//;
1190
+ var HASH_TEMPLATE_PATTERN = /['"`]\/?#\//;
1189
1191
  async function scanDirForPattern(dir, patterns, maxDepth = 6, maxFiles = 2e3) {
1190
1192
  const { readdir: readdir2, readFile: readFile3 } = await import("fs/promises");
1191
1193
  const { join: join11 } = await import("path");
@@ -1294,6 +1296,19 @@ async function collectMcpSources(root, home) {
1294
1296
  }
1295
1297
  return { sources, invalid };
1296
1298
  }
1299
+ function claudeOAuthCannotDiscover(entry, url) {
1300
+ let host;
1301
+ try {
1302
+ host = new URL(url).hostname.toLowerCase();
1303
+ } catch {
1304
+ return false;
1305
+ }
1306
+ if (!host.endsWith(".supabase.co")) return false;
1307
+ const headers = isJsonObject(entry.headers) ? Object.keys(entry.headers).map((h) => h.toLowerCase()) : [];
1308
+ if (headers.includes("authorization") || headers.includes("x-mushi-api-key")) return false;
1309
+ const oauth = isJsonObject(entry.oauth) ? entry.oauth : {};
1310
+ return typeof oauth.authServerMetadataUrl !== "string" || oauth.authServerMetadataUrl.length === 0;
1311
+ }
1297
1312
  function assessMushiEntry(source, name, entry, cliKeySaved) {
1298
1313
  const label = `${name} (${source.path})`;
1299
1314
  if (!isJsonObject(entry)) return { usable: false, stdio: false, detail: `${label}: not an object` };
@@ -1305,6 +1320,13 @@ function assessMushiEntry(source, name, entry, cliKeySaved) {
1305
1320
  detail: `${label}: has a url but no "type": "http" \u2014 Claude Code skips it`
1306
1321
  };
1307
1322
  }
1323
+ if (source.claudeCode && claudeOAuthCannotDiscover(entry, entry.url)) {
1324
+ return {
1325
+ usable: false,
1326
+ stdio: false,
1327
+ detail: `${label}: Claude Code sign-in cannot find OAuth metadata on a supabase.co URL ("Dynamic Client Registration rejected (HTTP 404)") \u2014 use ${CLOUD_MCP_OAUTH_URL} for Mushi Cloud, or add "oauth": { "authServerMetadataUrl": "<url>/.well-known/oauth-authorization-server" }`
1328
+ };
1329
+ }
1308
1330
  return { usable: true, stdio: false, detail: `${label}: hosted OAuth \u2014 sign in from the IDE's MCP panel` };
1309
1331
  }
1310
1332
  const env = isJsonObject(entry.env) ? entry.env : {};
@@ -1524,7 +1546,7 @@ async function checkOnboardingStatus(config, _consoleBase, cwd, doFetch = global
1524
1546
  }
1525
1547
  async function checkVersionDrift(cwd, doFetch) {
1526
1548
  const checks = [];
1527
- const { MUSHI_CLI_VERSION: MUSHI_CLI_VERSION2 } = await import("./version-HJSICWYN.js");
1549
+ const { MUSHI_CLI_VERSION: MUSHI_CLI_VERSION2 } = await import("./version-AAAJ5462.js");
1528
1550
  if (MUSHI_CLI_VERSION2 === "0.0.0-dev") return checks;
1529
1551
  const parse2 = (v) => {
1530
1552
  const m = v.replace(/^[~^]/, "").match(/^(\d+)\.(\d+)\.(\d+)/);
@@ -1641,7 +1663,7 @@ async function runDoctor(config, options = {}) {
1641
1663
  }
1642
1664
  if (sdkCheck) checks.push(sdkCheck);
1643
1665
  if (sdkCheck?.ok && !sdkCheck.warn) {
1644
- const { findSdkImport } = await import("./snippet-inject-CCXILWYZ.js");
1666
+ const { findSdkImport } = await import("./snippet-inject-LPRWTQDE.js");
1645
1667
  const imported = await findSdkImport(options.cwd ?? process.cwd());
1646
1668
  checks.push(
1647
1669
  imported ? { name: "SDK init snippet wired", ok: true, detail: `Found SDK usage in ${imported.file}` } : {
@@ -1769,7 +1791,7 @@ async function runInit(options = {}) {
1769
1791
  const framework = await chooseFramework(detected, options);
1770
1792
  const consoleBase = await resolveConsoleUrl({ cwd });
1771
1793
  const endpoint = resolveCloudEndpoint(
1772
- options.endpoint ?? process.env.MUSHI_API_ENDPOINT?.trim() ?? loadConfig().endpoint
1794
+ firstEndpoint(options.endpoint, process.env.MUSHI_API_ENDPOINT, loadConfig().endpoint)
1773
1795
  );
1774
1796
  options = { ...options, endpoint };
1775
1797
  const credentials = await acquireCredentials(options, consoleBase, endpoint);
@@ -2392,7 +2414,7 @@ function emitWizardFunnelEvent(credentials, endpoint, event, metadata = {}) {
2392
2414
  }
2393
2415
  async function maybeInjectSnippet(cwd, framework, options) {
2394
2416
  if (options.yes || !process.stdin.isTTY) return;
2395
- const { ENTRY_CANDIDATES, injectSnippet, MUSHI_MARKER_START } = await import("./snippet-inject-CCXILWYZ.js");
2417
+ const { ENTRY_CANDIDATES, injectSnippet, MUSHI_MARKER_START } = await import("./snippet-inject-LPRWTQDE.js");
2396
2418
  const candidates = ENTRY_CANDIDATES[framework.id];
2397
2419
  if (!candidates) return;
2398
2420
  const { readFile: readFile3, writeFile: writeFile3 } = await import("fs/promises");
@@ -2448,7 +2470,7 @@ async function maybeOfferConnect(credentials, options, _consoleBase) {
2448
2470
  if (p.isCancel(answer) || !answer) return;
2449
2471
  const endpoint = resolveCloudEndpoint(options.endpoint);
2450
2472
  try {
2451
- const { runConnect: runConnect2 } = await import("./connect-RZUYFR66.js");
2473
+ const { runConnect: runConnect2 } = await import("./connect-36PCIH7N.js");
2452
2474
  const saved = loadConfig();
2453
2475
  const result = await runConnect2(
2454
2476
  {
@@ -2862,6 +2884,7 @@ Examples:
2862
2884
  const safeValue = key === "endpoint" ? assertEndpoint(value) : value;
2863
2885
  config[key] = safeValue;
2864
2886
  saveConfig(config);
2887
+ if (key === "apiKey") syncKeychainAfterKeyChange(safeValue, resolveProfileName());
2865
2888
  console.log(`\u2713 Set ${key}`);
2866
2889
  } else {
2867
2890
  const safe = {
@@ -3673,7 +3696,8 @@ async function writeProjectBootstrapFiles(opts) {
3673
3696
  try {
3674
3697
  existing = await readFile2(envPath, "utf8");
3675
3698
  envUpdated = true;
3676
- } catch {
3699
+ } catch (err) {
3700
+ if (err.code !== "ENOENT") throw err;
3677
3701
  }
3678
3702
  const MUSHI_LINE_RE = /^(NEXT_PUBLIC_|NUXT_PUBLIC_|VITE_|EXPO_PUBLIC_)?MUSHI_[A-Z_]+=.*/gm;
3679
3703
  const MUSHI_COMMENT_RE = /^# Mushi MCP\b.*/gm;
@@ -3715,7 +3739,7 @@ Typical first-time flow:
3715
3739
  # mushi whoami to confirm`).action(async (opts) => {
3716
3740
  const savedConfig = loadConfig();
3717
3741
  const endpoint = resolveCloudEndpoint(
3718
- opts.endpoint ?? process.env.MUSHI_API_ENDPOINT?.trim() ?? savedConfig.endpoint
3742
+ firstEndpoint(opts.endpoint, process.env.MUSHI_API_ENDPOINT, savedConfig.endpoint)
3719
3743
  );
3720
3744
  const consoleBase = await resolveConsoleUrl();
3721
3745
  console.log("");
@@ -3867,7 +3891,14 @@ function buildSetupServerBlock(opts) {
3867
3891
  note: "Note: endpoint does not end in /api \u2014 cannot derive the hosted MCP URL; writing a local stdio entry instead."
3868
3892
  };
3869
3893
  }
3870
- return { block: buildHostedMcpServerBlock(hostedUrl), hosted: true };
3894
+ return { block: hostedOAuthBlock(opts.ide, opts.endpoint, hostedUrl), hosted: true };
3895
+ }
3896
+ function hostedOAuthBlock(ide, endpoint, hostedUrl) {
3897
+ if (endpoint.replace(/\/+$/, "") === CLOUD_API_ENDPOINT) return buildHostedMcpServerBlock(CLOUD_MCP_OAUTH_URL);
3898
+ if (ide !== "claude") return buildHostedMcpServerBlock(hostedUrl);
3899
+ return buildHostedMcpServerBlock(hostedUrl, {
3900
+ authServerMetadataUrl: `${hostedUrl}/.well-known/oauth-authorization-server`
3901
+ });
3871
3902
  }
3872
3903
  function buildZedServerBlock(opts) {
3873
3904
  const { env } = buildMcpServerBlock({ ...opts, client: "zed" });
@@ -4681,17 +4712,17 @@ function registerNudgeCommand(program2) {
4681
4712
  }
4682
4713
  const phase = opts.phase;
4683
4714
  const overrides = {};
4684
- const parseNumericFlag = (flag, raw, min) => {
4715
+ const parseNumericFlag = (flag, raw, min, integer = false) => {
4685
4716
  const n = Number(raw);
4686
- if (!Number.isFinite(n) || n < min) {
4717
+ if (!Number.isFinite(n) || n < min || integer && !Number.isInteger(n)) {
4687
4718
  console.error(
4688
- `error: --${flag} must be a finite number >= ${min} (got "${raw}")`
4719
+ `error: --${flag} must be a finite ${integer ? "integer" : "number"} >= ${min} (got "${raw}")`
4689
4720
  );
4690
4721
  process.exit(1);
4691
4722
  }
4692
4723
  return n;
4693
4724
  };
4694
- if (opts.max !== void 0) overrides.maxProactivePerSession = parseNumericFlag("max", opts.max, 1);
4725
+ if (opts.max !== void 0) overrides.maxProactivePerSession = parseNumericFlag("max", opts.max, 1, true);
4695
4726
  if (opts.cooldown !== void 0) overrides.dismissCooldownHours = parseNumericFlag("cooldown", opts.cooldown, 0);
4696
4727
  if (opts.dwell !== void 0) overrides.pageDwellMinutes = parseNumericFlag("dwell", opts.dwell, 0);
4697
4728
  if (opts.welcome !== void 0) overrides.firstSessionSeconds = parseNumericFlag("welcome", opts.welcome, 0);
@@ -5373,6 +5404,7 @@ function registerKeysCommands(program2) {
5373
5404
  const oldPrefix = oldKey?.slice(0, 12) ?? "(none)";
5374
5405
  config.apiKey = newKey;
5375
5406
  saveConfig(config);
5407
+ syncKeychainAfterKeyChange(newKey, resolveProfileName());
5376
5408
  let serverRevoked = false;
5377
5409
  if (oldKey && config.projectId) {
5378
5410
  const revokeResult = await revokeProjectKey(
package/dist/init.d.ts CHANGED
@@ -87,8 +87,9 @@ declare function createProject(endpoint: string, cliToken: string, name: string,
87
87
  /**
88
88
  * Mint a fresh report:write key for an existing project (raw keys can't be
89
89
  * recovered, so selecting a project always mints a new one). Throws
90
- * DeviceAuthRequestError on failure (after one transient retry) so the wizard
91
- * can tell the user WHY minting failed instead of a bare "could not mint".
90
+ * DeviceAuthRequestError on failure (a single attempt, no retry — minting isn't
91
+ * idempotent) so the wizard can tell the user WHY minting failed instead of a
92
+ * bare "could not mint".
92
93
  */
93
94
  declare function mintProjectKey(endpoint: string, cliToken: string, projectId: string, opts?: {
94
95
  /**
package/dist/init.js CHANGED
@@ -25,10 +25,10 @@ import {
25
25
  saveConfig,
26
26
  savedSdkKeyFor,
27
27
  trySaveKeyToKeychain
28
- } from "./chunk-LBVNX3PN.js";
28
+ } from "./chunk-EZJN53QO.js";
29
29
  import {
30
30
  MUSHI_CLI_VERSION
31
- } from "./chunk-ZQFQRBNL.js";
31
+ } from "./chunk-AZZNO4T4.js";
32
32
  import {
33
33
  FRAMEWORKS,
34
34
  detectFramework,
@@ -39,11 +39,12 @@ import {
39
39
  } from "./chunk-AIZRI665.js";
40
40
  import {
41
41
  CLOUD_API_ENDPOINT,
42
+ CLOUD_MCP_OAUTH_URL,
42
43
  TEST_REPORT_FETCH_TIMEOUT_MS,
44
+ firstEndpoint,
43
45
  normalizeEndpoint,
44
46
  resolveCloudEndpoint
45
- } from "./chunk-SXSI2W6Z.js";
46
- import "./chunk-3RG5ZIWI.js";
47
+ } from "./chunk-2DMHQVRU.js";
47
48
 
48
49
  // src/init.ts
49
50
  import * as p from "@clack/prompts";
@@ -860,7 +861,7 @@ var HASH_ROUTER_PATTERNS = [
860
861
  /location\.hash\b/,
861
862
  /addEventListener\(['"]hashchange['"]/
862
863
  ];
863
- var HASH_TEMPLATE_PATTERN = /['"`]#\//;
864
+ var HASH_TEMPLATE_PATTERN = /['"`]\/?#\//;
864
865
  async function scanDirForPattern(dir, patterns, maxDepth = 6, maxFiles = 2e3) {
865
866
  const { readdir, readFile } = await import("fs/promises");
866
867
  const { join: join3 } = await import("path");
@@ -969,6 +970,19 @@ async function collectMcpSources(root, home) {
969
970
  }
970
971
  return { sources, invalid };
971
972
  }
973
+ function claudeOAuthCannotDiscover(entry, url) {
974
+ let host;
975
+ try {
976
+ host = new URL(url).hostname.toLowerCase();
977
+ } catch {
978
+ return false;
979
+ }
980
+ if (!host.endsWith(".supabase.co")) return false;
981
+ const headers = isJsonObject(entry.headers) ? Object.keys(entry.headers).map((h) => h.toLowerCase()) : [];
982
+ if (headers.includes("authorization") || headers.includes("x-mushi-api-key")) return false;
983
+ const oauth = isJsonObject(entry.oauth) ? entry.oauth : {};
984
+ return typeof oauth.authServerMetadataUrl !== "string" || oauth.authServerMetadataUrl.length === 0;
985
+ }
972
986
  function assessMushiEntry(source, name, entry, cliKeySaved) {
973
987
  const label = `${name} (${source.path})`;
974
988
  if (!isJsonObject(entry)) return { usable: false, stdio: false, detail: `${label}: not an object` };
@@ -980,6 +994,13 @@ function assessMushiEntry(source, name, entry, cliKeySaved) {
980
994
  detail: `${label}: has a url but no "type": "http" \u2014 Claude Code skips it`
981
995
  };
982
996
  }
997
+ if (source.claudeCode && claudeOAuthCannotDiscover(entry, entry.url)) {
998
+ return {
999
+ usable: false,
1000
+ stdio: false,
1001
+ detail: `${label}: Claude Code sign-in cannot find OAuth metadata on a supabase.co URL ("Dynamic Client Registration rejected (HTTP 404)") \u2014 use ${CLOUD_MCP_OAUTH_URL} for Mushi Cloud, or add "oauth": { "authServerMetadataUrl": "<url>/.well-known/oauth-authorization-server" }`
1002
+ };
1003
+ }
983
1004
  return { usable: true, stdio: false, detail: `${label}: hosted OAuth \u2014 sign in from the IDE's MCP panel` };
984
1005
  }
985
1006
  const env = isJsonObject(entry.env) ? entry.env : {};
@@ -1230,7 +1251,7 @@ async function runDoctor(config, options = {}) {
1230
1251
  }
1231
1252
  if (sdkCheck) checks.push(sdkCheck);
1232
1253
  if (sdkCheck?.ok && !sdkCheck.warn) {
1233
- const { findSdkImport } = await import("./snippet-inject-7ABYE57O.js");
1254
+ const { findSdkImport } = await import("./snippet-inject-NDAPEL2T.js");
1234
1255
  const imported = await findSdkImport(options.cwd ?? process.cwd());
1235
1256
  checks.push(
1236
1257
  imported ? { name: "SDK init snippet wired", ok: true, detail: `Found SDK usage in ${imported.file}` } : {
@@ -1358,7 +1379,7 @@ async function runInit(options = {}) {
1358
1379
  const framework = await chooseFramework(detected, options);
1359
1380
  const consoleBase = await resolveConsoleUrl({ cwd });
1360
1381
  const endpoint = resolveCloudEndpoint(
1361
- options.endpoint ?? process.env.MUSHI_API_ENDPOINT?.trim() ?? loadConfig().endpoint
1382
+ firstEndpoint(options.endpoint, process.env.MUSHI_API_ENDPOINT, loadConfig().endpoint)
1362
1383
  );
1363
1384
  options = { ...options, endpoint };
1364
1385
  const credentials = await acquireCredentials(options, consoleBase, endpoint);
@@ -1981,7 +2002,7 @@ function emitWizardFunnelEvent(credentials, endpoint, event, metadata = {}) {
1981
2002
  }
1982
2003
  async function maybeInjectSnippet(cwd, framework, options) {
1983
2004
  if (options.yes || !process.stdin.isTTY) return;
1984
- const { ENTRY_CANDIDATES, injectSnippet, MUSHI_MARKER_START } = await import("./snippet-inject-7ABYE57O.js");
2005
+ const { ENTRY_CANDIDATES, injectSnippet, MUSHI_MARKER_START } = await import("./snippet-inject-NDAPEL2T.js");
1985
2006
  const candidates = ENTRY_CANDIDATES[framework.id];
1986
2007
  if (!candidates) return;
1987
2008
  const { readFile, writeFile } = await import("fs/promises");
@@ -2037,7 +2058,7 @@ async function maybeOfferConnect(credentials, options, _consoleBase) {
2037
2058
  if (p.isCancel(answer) || !answer) return;
2038
2059
  const endpoint = resolveCloudEndpoint(options.endpoint);
2039
2060
  try {
2040
- const { runConnect } = await import("./connect-Y77XNVUP.js");
2061
+ const { runConnect } = await import("./connect-TY6YTXQD.js");
2041
2062
  const saved = loadConfig();
2042
2063
  const result = await runConnect(
2043
2064
  {
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-PDX44BCA.js";
3
2
 
4
3
  // src/snippet-inject.ts
5
4
  import { readFile, readdir } from "fs/promises";
@@ -1,5 +1,3 @@
1
- import "./chunk-3RG5ZIWI.js";
2
-
3
1
  // src/snippet-inject.ts
4
2
  import { readFile, readdir } from "fs/promises";
5
3
  import { join } from "path";
@@ -2,8 +2,7 @@
2
2
  import {
3
3
  MUSHI_CLI_VERSION,
4
4
  MUSHI_MCP_PIN_SPEC
5
- } from "./chunk-VJZVFEMZ.js";
6
- import "./chunk-PDX44BCA.js";
5
+ } from "./chunk-LKC2BEKQ.js";
7
6
  export {
8
7
  MUSHI_CLI_VERSION,
9
8
  MUSHI_MCP_PIN_SPEC
package/dist/version.d.ts CHANGED
@@ -14,6 +14,6 @@ declare const MUSHI_CLI_VERSION: string;
14
14
  * setup refreshes the pin. Synced to packages/mcp/package.json by
15
15
  * `scripts/sync-mcp-pin.mjs` — never hand-edit the version.
16
16
  */
17
- declare const MUSHI_MCP_PIN_SPEC = "@mushi-mushi/mcp@0.24.6";
17
+ declare const MUSHI_MCP_PIN_SPEC = "@mushi-mushi/mcp@0.26.0";
18
18
 
19
19
  export { MUSHI_CLI_VERSION, MUSHI_MCP_PIN_SPEC };
package/dist/version.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import {
2
2
  MUSHI_CLI_VERSION,
3
3
  MUSHI_MCP_PIN_SPEC
4
- } from "./chunk-ZQFQRBNL.js";
5
- import "./chunk-3RG5ZIWI.js";
4
+ } from "./chunk-AZZNO4T4.js";
6
5
  export {
7
6
  MUSHI_CLI_VERSION,
8
7
  MUSHI_MCP_PIN_SPEC
@@ -2,7 +2,6 @@ import {
2
2
  FRAMEWORK_IDS,
3
3
  isFrameworkId
4
4
  } from "./chunk-AIZRI665.js";
5
- import "./chunk-3RG5ZIWI.js";
6
5
 
7
6
  // src/wizard-args.ts
8
7
  var MIN_NODE_MAJOR = 20;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mushi-mushi/cli",
3
- "version": "0.30.3",
3
+ "version": "0.30.5",
4
4
  "license": "MIT",
5
5
  "description": "Mushi CLI: SDK setup, MCP wiring, bug triage. The bug mediator for AI-built apps: plain-English diagnosis + a ready fix, in your editor.",
6
6
  "bin": {
@@ -1,10 +0,0 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
- export {
9
- __require
10
- };
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env node
2
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
- }) : x)(function(x) {
5
- if (typeof require !== "undefined") return require.apply(this, arguments);
6
- throw Error('Dynamic require of "' + x + '" is not supported');
7
- });
8
-
9
- export {
10
- __require
11
- };
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/version.ts
4
- var MUSHI_CLI_VERSION = true ? "0.30.3" : "0.0.0-dev";
5
- var MUSHI_MCP_PIN_SPEC = "@mushi-mushi/mcp@0.24.6";
6
-
7
- export {
8
- MUSHI_CLI_VERSION,
9
- MUSHI_MCP_PIN_SPEC
10
- };
@@ -1,8 +0,0 @@
1
- // src/version.ts
2
- var MUSHI_CLI_VERSION = true ? "0.30.3" : "0.0.0-dev";
3
- var MUSHI_MCP_PIN_SPEC = "@mushi-mushi/mcp@0.24.6";
4
-
5
- export {
6
- MUSHI_CLI_VERSION,
7
- MUSHI_MCP_PIN_SPEC
8
- };