@mushi-mushi/cli 0.34.1 → 0.35.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/init.js CHANGED
@@ -25,10 +25,10 @@ import {
25
25
  saveConfig,
26
26
  savedSdkKeyFor,
27
27
  trySaveKeyToKeychain
28
- } from "./chunk-67WU4UYB.js";
28
+ } from "./chunk-FBCX3OBT.js";
29
29
  import {
30
30
  MUSHI_CLI_VERSION
31
- } from "./chunk-QI7DE6J3.js";
31
+ } from "./chunk-Q4TOO3HX.js";
32
32
  import {
33
33
  FRAMEWORKS,
34
34
  detectFramework,
@@ -53,6 +53,26 @@ import { randomUUID } from "crypto";
53
53
  import { appendFileSync, existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
54
54
  import { join as join2 } from "path";
55
55
 
56
+ // src/hosting-env-hint.ts
57
+ var LOVABLE_BOLT_DOCS = "https://kensaur.us/mushi-mushi/docs/quickstart/lovable-bolt";
58
+ function envVarNames(block) {
59
+ return block.split("\n").map((line) => /^([A-Z_][A-Z0-9_]*)=/.exec(line.trim())?.[1]).filter((name) => Boolean(name));
60
+ }
61
+ function hostingEnvHint(envBlock, envFile) {
62
+ const names = envVarNames(envBlock);
63
+ const first = names[0] ?? "MUSHI_PROJECT_ID";
64
+ const last = names[names.length - 1] ?? first;
65
+ return [
66
+ `Your deployed site needs these too. The values are in ${envFile}; copy them from there:`,
67
+ ...names.map((n) => ` ${n}`),
68
+ "",
69
+ `Vercel: Project \u2192 Settings \u2192 Environment Variables, or \`vercel env add ${first} production\` (it asks for the value).`,
70
+ `Netlify: Project configuration \u2192 Environment variables, or \`netlify env:set ${last} "<value>"\`.`,
71
+ "Then redeploy: a NEXT_PUBLIC_ / VITE_ variable only reaches the site on the next build.",
72
+ `Lovable or Bolt: no variables needed; paste the script tag instead: ${LOVABLE_BOLT_DOCS}`
73
+ ].join("\n");
74
+ }
75
+
56
76
  // src/device-auth.ts
57
77
  var DEVICE_FETCH_TIMEOUT_MS = 15e3;
58
78
  var DEFAULT_SLOW_DOWN_MS = 5e3;
@@ -1226,6 +1246,46 @@ async function checkPipelineDoctor(config, doFetch) {
1226
1246
  }];
1227
1247
  }
1228
1248
  }
1249
+ var HEARTBEAT_ROWS = /* @__PURE__ */ new Set(["never_seen", "sdk_stale"]);
1250
+ async function checkHealthRegistry(config, doFetch) {
1251
+ if (!config.endpoint || !config.apiKey || !config.projectId) return [];
1252
+ let creds;
1253
+ try {
1254
+ creds = sanitizeCliCredentials(config);
1255
+ } catch {
1256
+ return [];
1257
+ }
1258
+ try {
1259
+ const { endpoint, apiKey, projectId } = creds;
1260
+ const res = await doFetch(`${endpoint}/v1/admin/health-checks?projectId=${encodeURIComponent(projectId)}`, {
1261
+ headers: apiKeyHeaders(apiKey, projectId),
1262
+ signal: AbortSignal.timeout(3e4)
1263
+ });
1264
+ if (res.status === 401 || res.status === 403 || res.status === 404) return [];
1265
+ if (!res.ok) {
1266
+ return [{ name: "[health] Health check", ok: true, warn: true, detail: `GET /v1/admin/health-checks returned HTTP ${res.status}; health unknown.` }];
1267
+ }
1268
+ const body = await res.json();
1269
+ const rows = (body.data?.projects?.[0]?.checks ?? []).filter((r) => r.source !== "doctor" && !HEARTBEAT_ROWS.has(r.id));
1270
+ const out = [];
1271
+ for (const r of rows) {
1272
+ if (r.status === "ok") continue;
1273
+ const waiting = r.affects && r.affects > 0 ? ` ${r.affects} waiting on this.` : "";
1274
+ const fix = r.fix ? ` \u2192 Fix: ${r.fix.label}${r.fix.to ? ` (console ${r.fix.to})` : ""}${r.fix.steps?.length ? `: ${r.fix.steps.join(" ")}` : ""}` : "";
1275
+ out.push({
1276
+ name: `[health] ${r.title}`,
1277
+ ok: true,
1278
+ warn: true,
1279
+ detail: r.status === "unknown" ? `Not checked: ${r.detail}` : `${r.detail}${waiting}${fix}`
1280
+ });
1281
+ }
1282
+ const passing = rows.filter((r) => r.status === "ok").length;
1283
+ if (passing > 0) out.push({ name: "[health] Other checks", ok: true, detail: `${passing} passed.` });
1284
+ return out;
1285
+ } catch {
1286
+ return [{ name: "[health] Health check", ok: true, warn: true, detail: "Could not reach /v1/admin/health-checks (timeout); health unknown." }];
1287
+ }
1288
+ }
1229
1289
  async function runDoctor(config, options = {}) {
1230
1290
  const doFetch = options.fetch ?? globalThis.fetch;
1231
1291
  const checks = [];
@@ -1266,6 +1326,7 @@ async function runDoctor(config, options = {}) {
1266
1326
  const serverChecks = await checkServerPreflight(config, doFetch);
1267
1327
  checks.push(...serverChecks);
1268
1328
  checks.push(...await checkPipelineDoctor(config, doFetch));
1329
+ checks.push(...await checkHealthRegistry(config, doFetch));
1269
1330
  }
1270
1331
  checks.push(...await checkVersionDrift(options.cwd ?? process.cwd(), doFetch));
1271
1332
  if (runIngest) {
@@ -1341,6 +1402,7 @@ ${failed.length} check${failed.length === 1 ? "" : "s"} failed.`);
1341
1402
  }
1342
1403
 
1343
1404
  // src/init.ts
1405
+ var NATIVE_FRAMEWORK_IDS = /* @__PURE__ */ new Set(["expo", "react-native", "capacitor"]);
1344
1406
  var ENV_FILES = [".env.local", ".env"];
1345
1407
  function isInteractive() {
1346
1408
  return Boolean(process.stdin.isTTY && process.stdout.isTTY);
@@ -1391,7 +1453,7 @@ async function runInit(options = {}) {
1391
1453
  } else {
1392
1454
  p.log.info(`Skipped install. Run \`${installCommand(pm, packagesToInstall)}\` yourself.`);
1393
1455
  }
1394
- await writeEnvFile(
1456
+ const envFile = await writeEnvFile(
1395
1457
  cwd,
1396
1458
  credentials.sdkKey,
1397
1459
  credentials.projectId,
@@ -1399,6 +1461,12 @@ async function runInit(options = {}) {
1399
1461
  endpoint,
1400
1462
  Boolean(options.yes)
1401
1463
  );
1464
+ if (!NATIVE_FRAMEWORK_IDS.has(framework.id)) {
1465
+ p.note(
1466
+ hostingEnvHint(envVarsToWrite(credentials.sdkKey, credentials.projectId, framework, endpoint), envFile),
1467
+ "Before you deploy:"
1468
+ );
1469
+ }
1402
1470
  persistCliConfig(credentials, endpoint);
1403
1471
  emitWizardFunnelEvent(credentials, endpoint, "wizard_env_written", { framework: framework.id });
1404
1472
  const enableRewards = await maybeEnableRewards(options);
@@ -1908,13 +1976,13 @@ async function writeEnvFile(cwd, apiKey, projectId, framework, endpoint, overwri
1908
1976
  });
1909
1977
  if (p.isCancel(answer)) {
1910
1978
  p.log.info(`Kept existing env vars in ${target}.`);
1911
- return;
1979
+ return target;
1912
1980
  }
1913
1981
  shouldOverwrite = Boolean(answer);
1914
1982
  }
1915
1983
  if (!shouldOverwrite) {
1916
1984
  p.log.info(`Kept existing env vars in ${target}. Re-run and confirm to overwrite.`);
1917
- return;
1985
+ return target;
1918
1986
  }
1919
1987
  const MUSHI_LINE_RE = /^(NEXT_PUBLIC_|NUXT_PUBLIC_|VITE_|EXPO_PUBLIC_)?MUSHI_[A-Z_]+=.*/gm;
1920
1988
  const stripped = existing.replace(MUSHI_LINE_RE, "").replace(/\n{3,}/g, "\n\n").trimEnd();
@@ -1925,7 +1993,7 @@ ${newVars}
1925
1993
  `);
1926
1994
  p.log.success(`Updated MUSHI_* env vars in ${target}`);
1927
1995
  warnIfMissingFromGitignore(cwd, target);
1928
- return;
1996
+ return target;
1929
1997
  }
1930
1998
  const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
1931
1999
  appendFileSync(targetPath, `${prefix}
@@ -1934,6 +2002,7 @@ ${newVars}
1934
2002
  `);
1935
2003
  p.log.success(`Wrote env vars to ${target}`);
1936
2004
  warnIfMissingFromGitignore(cwd, target);
2005
+ return target;
1937
2006
  }
1938
2007
  function isEnvFileCoveredByGitignore(gitignoreContent, envFile) {
1939
2008
  const lines = gitignoreContent.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
@@ -2044,7 +2113,7 @@ import { MushiRewardsBadge } from '@mushi-mushi/react';
2044
2113
  " [ ] 1. Paste the init snippet above into your app entry file",
2045
2114
  " [ ] 2. Start your dev server",
2046
2115
  " [ ] 3. Wire your editor: npx mushi-mushi setup --ide cursor (or --ide claude)",
2047
- ` [ ] 4. Open the Verify tab to send a test report: ${consoleUrl(consoleBase, "/onboarding?tab=verify")}`
2116
+ ` [ ] 4. Open the Test it tab to send a test report: ${consoleUrl(consoleBase, "/onboarding?tab=verify")}`
2048
2117
  ].join("\n"),
2049
2118
  "Next steps:"
2050
2119
  );
@@ -2058,7 +2127,7 @@ async function maybeOfferConnect(credentials, options, _consoleBase) {
2058
2127
  if (p.isCancel(answer) || !answer) return;
2059
2128
  const endpoint = resolveCloudEndpoint(options.endpoint);
2060
2129
  try {
2061
- const { runConnect } = await import("./connect-354QHAUQ.js");
2130
+ const { runConnect } = await import("./connect-45YP7OGH.js");
2062
2131
  const saved = loadConfig();
2063
2132
  const result = await runConnect(
2064
2133
  {
@@ -2076,7 +2145,9 @@ async function maybeOfferConnect(credentials, options, _consoleBase) {
2076
2145
  for (const line of result.messages) p.log.message(line);
2077
2146
  } catch (err) {
2078
2147
  p.log.warn(err instanceof Error ? err.message : String(err));
2079
- p.log.info("You can run manually: mushi connect --write-env --wire-ide --wait");
2148
+ p.log.info(
2149
+ `You can run it yourself: npx mushi-mushi connect --project-id ${credentials.projectId} --endpoint ${endpoint} --write-env --wire-ide --wait`
2150
+ );
2080
2151
  }
2081
2152
  }
2082
2153
  async function maybeEnableRewards(options) {
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  MUSHI_CLI_VERSION,
4
4
  MUSHI_MCP_PIN_SPEC
5
- } from "./chunk-XXBGTUGX.js";
5
+ } from "./chunk-Z2AFR5AI.js";
6
6
  export {
7
7
  MUSHI_CLI_VERSION,
8
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.29.2";
17
+ declare const MUSHI_MCP_PIN_SPEC = "@mushi-mushi/mcp@0.30.1";
18
18
 
19
19
  export { MUSHI_CLI_VERSION, MUSHI_MCP_PIN_SPEC };
package/dist/version.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  MUSHI_CLI_VERSION,
3
3
  MUSHI_MCP_PIN_SPEC
4
- } from "./chunk-QI7DE6J3.js";
4
+ } from "./chunk-Q4TOO3HX.js";
5
5
  export {
6
6
  MUSHI_CLI_VERSION,
7
7
  MUSHI_MCP_PIN_SPEC
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mushi-mushi/cli",
3
- "version": "0.34.1",
3
+ "version": "0.35.1",
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,8 +0,0 @@
1
- // src/version.ts
2
- var MUSHI_CLI_VERSION = true ? "0.34.1" : "0.0.0-dev";
3
- var MUSHI_MCP_PIN_SPEC = "@mushi-mushi/mcp@0.29.2";
4
-
5
- export {
6
- MUSHI_CLI_VERSION,
7
- MUSHI_MCP_PIN_SPEC
8
- };
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/version.ts
4
- var MUSHI_CLI_VERSION = true ? "0.34.1" : "0.0.0-dev";
5
- var MUSHI_MCP_PIN_SPEC = "@mushi-mushi/mcp@0.29.2";
6
-
7
- export {
8
- MUSHI_CLI_VERSION,
9
- MUSHI_MCP_PIN_SPEC
10
- };