@mushi-mushi/cli 0.18.0 → 0.18.2

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/CONTRIBUTING.md CHANGED
@@ -58,9 +58,9 @@ packages/
58
58
  react-native/ React Native SDK (MIT)
59
59
  cli/ CLI tool (MIT)
60
60
  mcp/ MCP server for coding agents (MIT)
61
- server/ Supabase Edge Functions (BSL)
62
- agents/ Agentic fix pipeline (BSL)
63
- verify/ Fix verification (BSL)
61
+ server/ Supabase Edge Functions (AGPLv3)
62
+ agents/ Agentic fix pipeline (AGPLv3)
63
+ verify/ Fix verification (AGPLv3)
64
64
  apps/
65
65
  admin/ Admin dashboard (React + Tailwind)
66
66
  docs/ Documentation site (Nextra — apps/docs)
@@ -97,8 +97,10 @@ Guardrails (run locally before pushing):
97
97
  pnpm check:docs-stats # root README stat badges
98
98
  pnpm check:sdk-version-matrix # apps/docs/content/sdks/index.mdx versions
99
99
  pnpm check:internal-doc-links # dead /foo links in MDX
100
+ pnpm check:onboarding-drift # phantom MCP env vars / stale tool names
100
101
  pnpm check:bundle-docs # @mushi-mushi/web size-limit vs README
101
102
  pnpm gen:llms-txt # regenerate apps/docs/public/llms.txt after nav changes
103
+ pnpm gen:mcp-tools-doc # regenerate sdks/mcp-tools.generated.mdx from catalog.ts
102
104
  ```
103
105
 
104
106
  README changes on npm packages only appear on [npmjs.com](https://www.npmjs.com) after the next **Changesets publish**.
@@ -115,13 +117,30 @@ Select the affected packages, the semver bump type, and write a summary. The cha
115
117
 
116
118
  ## Release flow
117
119
 
118
- Releases are fully automated. Maintainers don't run `npm publish` by hand.
120
+ Releases are fully automated once the version PR is merged. Maintainers don't run
121
+ `npm publish` by hand for routine bumps. See **[`docs/DEPLOYMENT.md`](docs/DEPLOYMENT.md)**
122
+ for the full production runbook (edge functions, admin SPA, docs site, migrations).
119
123
 
120
124
  1. PRs land on `master` with one or more changeset files in `.changeset/`.
121
- 2. `release.yml` runs on every push to `master`. It opens (or updates) a `chore: version packages` PR that bumps every affected `package.json`, rolls up the changelogs, and deletes the consumed changesets.
122
- 3. Merging that "Version Packages" PR re-fires `release.yml`. The publish step authenticates to npm via **OpenID Connect (OIDC) Trusted Publishers** — no long-lived `NPM_TOKEN` is exchanged — and every tarball ships with a **Sigstore provenance attestation** uploaded to the public transparency log.
125
+ Orphaned changesets (only ignored packages) fail `pnpm check:changeset-orphans`.
126
+ 2. `release.yml` runs on every push to `master`. It opens (or updates) a
127
+ **`chore: version packages`** PR on `changeset-release/master` that bumps
128
+ affected `package.json` files, rolls up changelogs, and deletes consumed
129
+ changesets.
130
+ 3. **Wait for CI on the version PR.** If checks don't appear (bot-branch
131
+ suppression), push an empty commit to `changeset-release/master`. If
132
+ `check:sdk-version-matrix` fails, run `pnpm gen:sdk-version-matrix` on that
133
+ branch and commit `apps/docs/content/sdks/index.mdx`.
134
+ 4. Merge the version PR. The publish step uses **OIDC Trusted Publishers** +
135
+ Sigstore provenance (with `NPM_TOKEN` only for brand-new package bootstrap).
136
+ 5. **Manually dispatch Release** unless a run starts within ~2 minutes:
137
+ **Actions → Release → Run workflow → `master`**. Squash-merges attributed to
138
+ `github-actions[bot]` usually suppress the auto `push` trigger — manual
139
+ dispatch is the normal path, not a fallback of last resort.
123
140
 
124
- If GitHub's anti-loop protection suppresses the auto re-fire (the squash merge can be attributed to `github-actions[bot]`), trigger the workflow manually: **Actions → release → Run workflow → master**.
141
+ ```bash
142
+ gh workflow run release.yml --ref master
143
+ ```
125
144
 
126
145
  ### Adding a brand-new publishable package
127
146
 
@@ -168,7 +187,7 @@ chore: bump dependencies
168
187
  ## License
169
188
 
170
189
  - SDK packages are MIT — your contributions will be MIT-licensed
171
- - Server/agents/verify are BSL 1.1 — contributions to those packages fall under BSL
190
+ - Server/agents/verify are AGPLv3 — contributions to those packages fall under AGPLv3
172
191
 
173
192
  ## Questions?
174
193
 
package/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # @mushi-mushi/cli
2
2
 
3
+ > **Your AI wrote it. Mushi tells you why it broke.**
4
+ > Plain-English diagnosis + a paste-ready fix, right inside Cursor. MIT-licensed SDKs · self-hostable · no second LLM key.
5
+
3
6
  CLI for Mushi Mushi — set up the SDK in one command, then triage reports and monitor the pipeline from your terminal.
4
7
 
5
8
  ## One-command setup
@@ -51,9 +54,9 @@ mushi --version
51
54
  ## Other commands
52
55
 
53
56
  ```bash
54
- mushi login --api-key mushi_xxx # store credentials in ~/.mushirc (mode 0o600)
57
+ mushi login --api-key mushi_xxx # store credentials in ~/.config/mushi/config.json (mode 0o600)
55
58
  mushi connect --api-key mushi_xxx --project-id <uuid> --endpoint <url> --wait
56
- # one-shot wiring: ~/.mushirc + .env.local + .cursor/mcp.json + heartbeat wait
59
+ # one-shot wiring: ~/.config/mushi/config.json + .env.local + .cursor/mcp.json + heartbeat wait
57
60
  mushi upgrade # bump installed @mushi-mushi/* packages to latest stable
58
61
  mushi status # project overview
59
62
  mushi reports list # recent reports
@@ -73,7 +76,7 @@ mushi sourcemaps upload --release <ver> --dir <dist> # upload .js.map / .css.m
73
76
  Non-interactive equivalent of `mushi init` for agents and scripts — wires a
74
77
  repo to an existing project in one shot:
75
78
 
76
- 1. Saves credentials to `~/.mushirc` (mode `0o600`).
79
+ 1. Saves credentials to `~/.config/mushi/config.json` (mode `0o600`).
77
80
  2. Merges `MUSHI_*` / framework-prefixed env vars into `.env.local` —
78
81
  existing keys are never overwritten (skip with `--no-env`).
79
82
  3. Writes the `@mushi-mushi/mcp` server block into `.cursor/mcp.json` and
@@ -177,7 +180,7 @@ Local checks performed:
177
180
 
178
181
  | Check | What it verifies |
179
182
  |---|---|
180
- | CLI config | `~/.mushirc` exists, `projectId` and `apiKey` fields are present |
183
+ | CLI config | `~/.config/mushi/config.json` exists, `projectId` and `apiKey` fields are present |
181
184
  | Endpoint reachability | `GET /v1/sdk/config?project_id=...` returns 200 |
182
185
  | SDK install | `@mushi-mushi/web` or framework-specific SDK is in `node_modules` |
183
186
 
@@ -301,7 +304,7 @@ mushi slack test
301
304
 
302
305
  ## Security notes
303
306
 
304
- - `~/.mushirc` is written with mode `0o600` on Unix. Legacy configs with looser permissions are tightened on load.
307
+ - `~/.config/mushi/config.json` is written with mode `0o600` on Unix. Legacy configs with looser permissions are tightened on load.
305
308
  - `--endpoint` values are parsed through `new URL()` and required to use `https://` except for `localhost` / `127.0.0.1` / `*.local`.
306
309
  - The `--api-key` flag leaks into `ps -ef` — prefer the interactive prompt on shared machines.
307
310
  - Full stack traces on error: `DEBUG=mushi mushi init`.
@@ -26,15 +26,15 @@ var FRAMEWORKS = {
26
26
  label: "Next.js",
27
27
  packageName: "@mushi-mushi/react",
28
28
  needsWebPackage: false,
29
- snippet: (apiKey, projectId) => `// app/providers.tsx (or pages/_app.tsx for /pages router)
29
+ snippet: () => `// app/providers.tsx (or pages/_app.tsx for /pages router)
30
30
  'use client'
31
31
  import { MushiProvider } from '@mushi-mushi/react'
32
32
 
33
33
  export function Providers({ children }: { children: React.ReactNode }) {
34
34
  return (
35
35
  <MushiProvider config={{
36
- projectId: '${projectId}',
37
- apiKey: '${apiKey}',
36
+ projectId: process.env.NEXT_PUBLIC_MUSHI_PROJECT_ID!,
37
+ apiKey: process.env.NEXT_PUBLIC_MUSHI_API_KEY!,
38
38
  }}>
39
39
  {children}
40
40
  </MushiProvider>
@@ -46,13 +46,13 @@ export function Providers({ children }: { children: React.ReactNode }) {
46
46
  label: "React",
47
47
  packageName: "@mushi-mushi/react",
48
48
  needsWebPackage: false,
49
- snippet: (apiKey, projectId) => `// src/main.tsx
49
+ snippet: () => `// src/main.tsx
50
50
  import { MushiProvider } from '@mushi-mushi/react'
51
51
 
52
52
  createRoot(document.getElementById('root')!).render(
53
53
  <MushiProvider config={{
54
- projectId: '${projectId}',
55
- apiKey: '${apiKey}',
54
+ projectId: import.meta.env.VITE_MUSHI_PROJECT_ID,
55
+ apiKey: import.meta.env.VITE_MUSHI_API_KEY,
56
56
  }}>
57
57
  <App />
58
58
  </MushiProvider>
@@ -62,57 +62,58 @@ createRoot(document.getElementById('root')!).render(
62
62
  id: "vue",
63
63
  label: "Vue 3",
64
64
  packageName: "@mushi-mushi/vue",
65
- needsWebPackage: true,
66
- snippet: (apiKey, projectId) => `// src/main.ts
65
+ needsWebPackage: false,
66
+ snippet: () => `// src/main.ts
67
67
  import { MushiPlugin } from '@mushi-mushi/vue'
68
- import { Mushi } from '@mushi-mushi/web'
69
68
 
70
- app.use(MushiPlugin, { projectId: '${projectId}', apiKey: '${apiKey}' })
71
- Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })`
69
+ app.use(MushiPlugin, {
70
+ projectId: import.meta.env.VITE_MUSHI_PROJECT_ID,
71
+ apiKey: import.meta.env.VITE_MUSHI_API_KEY,
72
+ })`
72
73
  },
73
74
  nuxt: {
74
75
  id: "nuxt",
75
76
  label: "Nuxt",
76
77
  packageName: "@mushi-mushi/vue",
77
- needsWebPackage: true,
78
- snippet: (apiKey, projectId) => `// plugins/mushi.client.ts
78
+ needsWebPackage: false,
79
+ snippet: () => `// plugins/mushi.client.ts
79
80
  import { MushiPlugin } from '@mushi-mushi/vue'
80
- import { Mushi } from '@mushi-mushi/web'
81
81
 
82
82
  export default defineNuxtPlugin((nuxtApp) => {
83
83
  nuxtApp.vueApp.use(MushiPlugin, {
84
- projectId: '${projectId}',
85
- apiKey: '${apiKey}',
84
+ projectId: import.meta.env.NUXT_PUBLIC_MUSHI_PROJECT_ID,
85
+ apiKey: import.meta.env.NUXT_PUBLIC_MUSHI_API_KEY,
86
86
  })
87
- Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })
88
87
  })`
89
88
  },
90
89
  svelte: {
91
90
  id: "svelte",
92
91
  label: "Svelte",
93
92
  packageName: "@mushi-mushi/svelte",
94
- needsWebPackage: true,
95
- snippet: (apiKey, projectId) => `// src/main.ts (or +layout.svelte for SvelteKit)
93
+ needsWebPackage: false,
94
+ snippet: () => `// src/main.ts
96
95
  import { initMushi } from '@mushi-mushi/svelte'
97
- import { Mushi } from '@mushi-mushi/web'
98
96
 
99
- initMushi({ projectId: '${projectId}', apiKey: '${apiKey}' })
100
- Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })`
97
+ initMushi({
98
+ projectId: import.meta.env.VITE_MUSHI_PROJECT_ID,
99
+ apiKey: import.meta.env.VITE_MUSHI_API_KEY,
100
+ })`
101
101
  },
102
102
  sveltekit: {
103
103
  id: "sveltekit",
104
104
  label: "SvelteKit",
105
105
  packageName: "@mushi-mushi/svelte",
106
- needsWebPackage: true,
107
- snippet: (apiKey, projectId) => `// src/routes/+layout.svelte
106
+ needsWebPackage: false,
107
+ snippet: () => `// src/routes/+layout.svelte
108
108
  <script>
109
109
  import { onMount } from 'svelte'
110
- import { initMushi } from '@mushi-mushi/svelte'
111
110
 
112
111
  onMount(async () => {
113
- const { Mushi } = await import('@mushi-mushi/web')
114
- initMushi({ projectId: '${projectId}', apiKey: '${apiKey}' })
115
- Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })
112
+ const { initMushi } = await import('@mushi-mushi/svelte')
113
+ initMushi({
114
+ projectId: import.meta.env.VITE_MUSHI_PROJECT_ID,
115
+ apiKey: import.meta.env.VITE_MUSHI_API_KEY,
116
+ })
116
117
  })
117
118
  </script>`
118
119
  },
@@ -120,29 +121,33 @@ Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })`
120
121
  id: "angular",
121
122
  label: "Angular",
122
123
  packageName: "@mushi-mushi/angular",
123
- needsWebPackage: true,
124
- snippet: (apiKey, projectId) => `// src/main.ts
124
+ needsWebPackage: false,
125
+ snippet: () => `// src/main.ts
125
126
  import { provideMushi } from '@mushi-mushi/angular'
126
- import { Mushi } from '@mushi-mushi/web'
127
127
 
128
128
  bootstrapApplication(AppComponent, {
129
129
  providers: [
130
- provideMushi({ projectId: '${projectId}', apiKey: '${apiKey}' }),
130
+ provideMushi({
131
+ projectId: import.meta.env.VITE_MUSHI_PROJECT_ID,
132
+ apiKey: import.meta.env.VITE_MUSHI_API_KEY,
133
+ }),
131
134
  ],
132
- })
133
- Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })`
135
+ })`
134
136
  },
135
137
  expo: {
136
138
  id: "expo",
137
139
  label: "Expo",
138
140
  packageName: "@mushi-mushi/react-native",
139
141
  needsWebPackage: false,
140
- snippet: (apiKey, projectId) => `// App.tsx
142
+ snippet: () => `// App.tsx
141
143
  import { MushiProvider } from '@mushi-mushi/react-native'
142
144
 
143
145
  export default function App() {
144
146
  return (
145
- <MushiProvider projectId="${projectId}" apiKey="${apiKey}">
147
+ <MushiProvider
148
+ projectId={process.env.EXPO_PUBLIC_MUSHI_PROJECT_ID!}
149
+ apiKey={process.env.EXPO_PUBLIC_MUSHI_API_KEY!}
150
+ >
146
151
  <YourApp />
147
152
  </MushiProvider>
148
153
  )
@@ -153,12 +158,15 @@ export default function App() {
153
158
  label: "React Native",
154
159
  packageName: "@mushi-mushi/react-native",
155
160
  needsWebPackage: false,
156
- snippet: (apiKey, projectId) => `// App.tsx
161
+ snippet: () => `// App.tsx
157
162
  import { MushiProvider } from '@mushi-mushi/react-native'
158
163
 
159
164
  export default function App() {
160
165
  return (
161
- <MushiProvider projectId="${projectId}" apiKey="${apiKey}">
166
+ <MushiProvider
167
+ projectId={process.env.MUSHI_PROJECT_ID!}
168
+ apiKey={process.env.MUSHI_API_KEY!}
169
+ >
162
170
  <YourApp />
163
171
  </MushiProvider>
164
172
  )
@@ -175,24 +183,27 @@ export default function App() {
175
183
  * `TypeError: Mushi.init is not a function`. The accompanying admin
176
184
  * console snippet (apps/admin/src/lib/sdkSnippets.ts) emits the same
177
185
  * `Mushi.configure(...)` shape; both are pinned by tests. */
178
- snippet: (apiKey, projectId) => `// src/main.ts \u2014 after install, run \`npx cap sync\`
186
+ snippet: () => `// src/main.ts \u2014 after install, run \`npx cap sync\`
179
187
  import { Mushi } from '@mushi-mushi/capacitor'
180
188
 
181
- await Mushi.configure({ projectId: '${projectId}', apiKey: '${apiKey}' })`
189
+ await Mushi.configure({
190
+ projectId: import.meta.env.VITE_MUSHI_PROJECT_ID,
191
+ apiKey: import.meta.env.VITE_MUSHI_API_KEY,
192
+ })`
182
193
  },
183
194
  express: {
184
195
  id: "express",
185
196
  label: "Express",
186
197
  packageName: "@mushi-mushi/node",
187
198
  needsWebPackage: false,
188
- snippet: (apiKey, projectId) => `// src/instrument.ts \u2014 load with: node --import ./dist/instrument.js
199
+ snippet: () => `// src/instrument.ts \u2014 load with: node --import ./dist/instrument.js
189
200
  import { MushiNodeClient, attachUnhandledHook } from '@mushi-mushi/node'
190
201
  import { mushiExpressErrorHandler } from '@mushi-mushi/node/express'
191
202
  import type { Express } from 'express'
192
203
 
193
204
  export const mushi = new MushiNodeClient({
194
- projectId: '${projectId}',
195
- apiKey: '${apiKey}',
205
+ projectId: process.env.MUSHI_PROJECT_ID!,
206
+ apiKey: process.env.MUSHI_API_KEY!,
196
207
  environment: process.env.NODE_ENV ?? 'production',
197
208
  })
198
209
  attachUnhandledHook({ client: mushi })
@@ -206,14 +217,14 @@ export function attachMushi(app: Express) {
206
217
  label: "Fastify",
207
218
  packageName: "@mushi-mushi/node",
208
219
  needsWebPackage: false,
209
- snippet: (apiKey, projectId) => `// src/instrument.ts \u2014 load with: node --import ./dist/instrument.js
220
+ snippet: () => `// src/instrument.ts \u2014 load with: node --import ./dist/instrument.js
210
221
  import { MushiNodeClient, attachUnhandledHook } from '@mushi-mushi/node'
211
222
  import { mushiFastifyPlugin } from '@mushi-mushi/node/fastify'
212
223
  import Fastify from 'fastify'
213
224
 
214
225
  export const mushi = new MushiNodeClient({
215
- projectId: '${projectId}',
216
- apiKey: '${apiKey}',
226
+ projectId: process.env.MUSHI_PROJECT_ID!,
227
+ apiKey: process.env.MUSHI_API_KEY!,
217
228
  environment: process.env.NODE_ENV ?? 'production',
218
229
  })
219
230
  attachUnhandledHook({ client: mushi })
@@ -226,14 +237,14 @@ mushiFastifyPlugin(app, { client: mushi })`
226
237
  label: "Hono",
227
238
  packageName: "@mushi-mushi/node",
228
239
  needsWebPackage: false,
229
- snippet: (apiKey, projectId) => `// src/instrument.ts \u2014 load with: node --import ./dist/instrument.js
240
+ snippet: () => `// src/instrument.ts \u2014 load with: node --import ./dist/instrument.js
230
241
  import { MushiNodeClient, attachUnhandledHook } from '@mushi-mushi/node'
231
242
  import { mushiHonoErrorHandler } from '@mushi-mushi/node/hono'
232
243
  import { Hono } from 'hono'
233
244
 
234
245
  export const mushi = new MushiNodeClient({
235
- projectId: '${projectId}',
236
- apiKey: '${apiKey}',
246
+ projectId: process.env.MUSHI_PROJECT_ID!,
247
+ apiKey: process.env.MUSHI_API_KEY!,
237
248
  environment: process.env.NODE_ENV ?? 'production',
238
249
  })
239
250
  attachUnhandledHook({ client: mushi })
@@ -250,10 +261,13 @@ app.onError(
250
261
  label: "Vanilla JS / unknown",
251
262
  packageName: "@mushi-mushi/web",
252
263
  needsWebPackage: false,
253
- snippet: (apiKey, projectId) => `// Anywhere in your client bundle
264
+ snippet: () => `// Anywhere in your client bundle
254
265
  import { Mushi } from '@mushi-mushi/web'
255
266
 
256
- Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })`
267
+ Mushi.init({
268
+ projectId: import.meta.env.VITE_MUSHI_PROJECT_ID,
269
+ apiKey: import.meta.env.VITE_MUSHI_API_KEY,
270
+ })`
257
271
  }
258
272
  };
259
273
  function readPackageJson(cwd) {
@@ -314,6 +328,18 @@ function envVarsToWrite(apiKey, projectId, framework) {
314
328
  `MUSHI_API_KEY=${apiKey}`
315
329
  ].join("\n");
316
330
  }
331
+ if (framework.id === "react-native") {
332
+ return [
333
+ `MUSHI_PROJECT_ID=${projectId}`,
334
+ `MUSHI_API_KEY=${apiKey}`
335
+ ].join("\n");
336
+ }
337
+ if (framework.id === "expo") {
338
+ return [
339
+ `EXPO_PUBLIC_MUSHI_PROJECT_ID=${projectId}`,
340
+ `EXPO_PUBLIC_MUSHI_API_KEY=${apiKey}`
341
+ ].join("\n");
342
+ }
317
343
  const prefix = framework.id === "next" ? "NEXT_PUBLIC_" : framework.id === "nuxt" ? "NUXT_PUBLIC_" : "VITE_";
318
344
  return [
319
345
  `${prefix}MUSHI_PROJECT_ID=${projectId}`,