@mushi-mushi/cli 0.2.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,25 +1,74 @@
1
1
  # @mushi-mushi/cli
2
2
 
3
- CLI tool for Mushi Mushi project management and report triage.
3
+ CLI for Mushi Mushi set up the SDK in one command, then triage reports and monitor the pipeline from your terminal.
4
4
 
5
- ## Install
5
+ ## One-command setup
6
+
7
+ ```bash
8
+ npx @mushi-mushi/cli init
9
+ # equivalently:
10
+ npx mushi-mushi
11
+ ```
12
+
13
+ The wizard:
14
+
15
+ 1. Detects your framework (Next.js, Nuxt, SvelteKit, Angular, Expo, Capacitor, plain React/Vue/Svelte, or vanilla JS) from `package.json` and config files.
16
+ 2. Picks the right SDK package (`@mushi-mushi/react`, `@mushi-mushi/vue`, etc.) plus `@mushi-mushi/web` when the framework SDK is API-only.
17
+ 3. Detects your package manager (npm / pnpm / yarn / bun) from your lockfile and installs with that — `shell: false`, with Windows `.cmd` shim resolution.
18
+ 4. Writes `MUSHI_PROJECT_ID` and `MUSHI_API_KEY` (with the right framework prefix — `NEXT_PUBLIC_`, `NUXT_PUBLIC_`, `VITE_`) to `.env.local` (or `.env`).
19
+ 5. Warns you if `.env.local` isn't in `.gitignore` (covers `.env*.local`, `*.local`, etc.).
20
+ 6. Prints the framework-specific provider snippet to copy-paste.
21
+ 7. Offers to **send a real test report** so you see your first classified bug in the console immediately. Opt out via `--skip-test-report`.
22
+
23
+ It never silently overwrites existing env vars or modifies application code. Pasted credentials are sanitized (stripped of quotes / CR / LF / NUL) and validated against `^proj_[A-Za-z0-9_-]{10,}$` / `^mushi_[A-Za-z0-9_-]{10,}$` before anything is written to disk.
24
+
25
+ ### Flags
26
+
27
+ ```bash
28
+ mushi init --framework next # skip framework detection
29
+ mushi init --project-id proj_xxx --api-key mushi_xxx # skip credential prompts
30
+ mushi init --skip-install # print the install command instead
31
+ mushi init --skip-test-report # don't offer to send a test report
32
+ mushi init --cwd apps/web # run in a sub-package of a monorepo
33
+ mushi init --endpoint https://mushi.your-company.com # self-hosted Mushi API
34
+ mushi init -y # accept the detected framework
35
+ ```
36
+
37
+ Non-interactive use (CI): pass `--yes --project-id proj_xxx --api-key mushi_xxx` or the wizard exits with a clear error instead of hanging on a prompt.
38
+
39
+ Stale-version hint: the wizard checks the npm registry (2s timeout) and prints a one-line upgrade nudge if a newer stable is published. Opt out with `MUSHI_NO_UPDATE_CHECK=1`.
40
+
41
+ Monorepo awareness: if you run the wizard at a workspace root with no framework dep, it scans `apps/*`, `packages/*`, `examples/*` and tells you which sub-package you probably meant (`mushi init --cwd apps/web`).
42
+
43
+ ## Install globally
6
44
 
7
45
  ```bash
8
46
  npm install -g @mushi-mushi/cli
47
+ mushi --help
48
+ mushi --version
9
49
  ```
10
50
 
11
- ## Usage
51
+ ## Other commands
12
52
 
13
53
  ```bash
14
- mushi login # Authenticate with your API key
15
- mushi status # Project overview
16
- mushi reports list # List recent reports
17
- mushi reports show <id> # View report details
18
- mushi reports triage <id> --priority high --assign @dev
19
- mushi deploy check # Check edge function health
20
- mushi test # Submit a test report to verify pipeline
54
+ mushi login --api-key mushi_xxx # store credentials in ~/.mushirc (mode 0o600)
55
+ mushi status # project overview
56
+ mushi reports list # recent reports
57
+ mushi reports show <id> # one report
58
+ mushi reports triage <id> --status acknowledged --severity high
59
+ mushi deploy check # edge-function health probe
60
+ mushi index <path> # walk a local repo and feed RAG
61
+ mushi test # submit a test report end-to-end
62
+ mushi config endpoint https://... # set API endpoint (https:// required outside localhost)
21
63
  ```
22
64
 
65
+ ## Security notes
66
+
67
+ - `~/.mushirc` is written with mode `0o600` on Unix. Legacy configs with looser permissions are tightened on load.
68
+ - `--endpoint` values are parsed through `new URL()` and required to use `https://` except for `localhost` / `127.0.0.1` / `*.local`.
69
+ - The `--api-key` flag leaks into `ps -ef` — prefer the interactive prompt on shared machines.
70
+ - Full stack traces on error: `DEBUG=mushi mushi init`.
71
+
23
72
  ## License
24
73
 
25
74
  MIT
@@ -0,0 +1,6 @@
1
+ // src/version.ts
2
+ var MUSHI_CLI_VERSION = true ? "0.5.0" : "0.0.0-dev";
3
+
4
+ export {
5
+ MUSHI_CLI_VERSION
6
+ };
@@ -0,0 +1,256 @@
1
+ // src/detect.ts
2
+ import { readFileSync, existsSync } from "fs";
3
+ import { join } from "path";
4
+ var FRAMEWORK_IDS = [
5
+ "next",
6
+ "react",
7
+ "vue",
8
+ "nuxt",
9
+ "svelte",
10
+ "sveltekit",
11
+ "angular",
12
+ "expo",
13
+ "react-native",
14
+ "capacitor",
15
+ "vanilla"
16
+ ];
17
+ function isFrameworkId(value) {
18
+ return typeof value === "string" && FRAMEWORK_IDS.includes(value);
19
+ }
20
+ var FRAMEWORKS = {
21
+ next: {
22
+ id: "next",
23
+ label: "Next.js",
24
+ packageName: "@mushi-mushi/react",
25
+ needsWebPackage: false,
26
+ snippet: (apiKey, projectId) => `// app/providers.tsx (or pages/_app.tsx for /pages router)
27
+ 'use client'
28
+ import { MushiProvider } from '@mushi-mushi/react'
29
+
30
+ export function Providers({ children }: { children: React.ReactNode }) {
31
+ return (
32
+ <MushiProvider config={{
33
+ projectId: '${projectId}',
34
+ apiKey: '${apiKey}',
35
+ }}>
36
+ {children}
37
+ </MushiProvider>
38
+ )
39
+ }`
40
+ },
41
+ react: {
42
+ id: "react",
43
+ label: "React",
44
+ packageName: "@mushi-mushi/react",
45
+ needsWebPackage: false,
46
+ snippet: (apiKey, projectId) => `// src/main.tsx
47
+ import { MushiProvider } from '@mushi-mushi/react'
48
+
49
+ createRoot(document.getElementById('root')!).render(
50
+ <MushiProvider config={{
51
+ projectId: '${projectId}',
52
+ apiKey: '${apiKey}',
53
+ }}>
54
+ <App />
55
+ </MushiProvider>
56
+ )`
57
+ },
58
+ vue: {
59
+ id: "vue",
60
+ label: "Vue 3",
61
+ packageName: "@mushi-mushi/vue",
62
+ needsWebPackage: true,
63
+ snippet: (apiKey, projectId) => `// src/main.ts
64
+ import { MushiPlugin } from '@mushi-mushi/vue'
65
+ import { Mushi } from '@mushi-mushi/web'
66
+
67
+ app.use(MushiPlugin, { projectId: '${projectId}', apiKey: '${apiKey}' })
68
+ Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })`
69
+ },
70
+ nuxt: {
71
+ id: "nuxt",
72
+ label: "Nuxt",
73
+ packageName: "@mushi-mushi/vue",
74
+ needsWebPackage: true,
75
+ snippet: (apiKey, projectId) => `// plugins/mushi.client.ts
76
+ import { MushiPlugin } from '@mushi-mushi/vue'
77
+ import { Mushi } from '@mushi-mushi/web'
78
+
79
+ export default defineNuxtPlugin((nuxtApp) => {
80
+ nuxtApp.vueApp.use(MushiPlugin, {
81
+ projectId: '${projectId}',
82
+ apiKey: '${apiKey}',
83
+ })
84
+ Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })
85
+ })`
86
+ },
87
+ svelte: {
88
+ id: "svelte",
89
+ label: "Svelte",
90
+ packageName: "@mushi-mushi/svelte",
91
+ needsWebPackage: true,
92
+ snippet: (apiKey, projectId) => `// src/main.ts (or +layout.svelte for SvelteKit)
93
+ import { initMushi } from '@mushi-mushi/svelte'
94
+ import { Mushi } from '@mushi-mushi/web'
95
+
96
+ initMushi({ projectId: '${projectId}', apiKey: '${apiKey}' })
97
+ Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })`
98
+ },
99
+ sveltekit: {
100
+ id: "sveltekit",
101
+ label: "SvelteKit",
102
+ packageName: "@mushi-mushi/svelte",
103
+ needsWebPackage: true,
104
+ snippet: (apiKey, projectId) => `// src/routes/+layout.svelte
105
+ <script>
106
+ import { onMount } from 'svelte'
107
+ import { initMushi } from '@mushi-mushi/svelte'
108
+
109
+ onMount(async () => {
110
+ const { Mushi } = await import('@mushi-mushi/web')
111
+ initMushi({ projectId: '${projectId}', apiKey: '${apiKey}' })
112
+ Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })
113
+ })
114
+ </script>`
115
+ },
116
+ angular: {
117
+ id: "angular",
118
+ label: "Angular",
119
+ packageName: "@mushi-mushi/angular",
120
+ needsWebPackage: true,
121
+ snippet: (apiKey, projectId) => `// src/main.ts
122
+ import { provideMushi } from '@mushi-mushi/angular'
123
+ import { Mushi } from '@mushi-mushi/web'
124
+
125
+ bootstrapApplication(AppComponent, {
126
+ providers: [
127
+ provideMushi({ projectId: '${projectId}', apiKey: '${apiKey}' }),
128
+ ],
129
+ })
130
+ Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })`
131
+ },
132
+ expo: {
133
+ id: "expo",
134
+ label: "Expo",
135
+ packageName: "@mushi-mushi/react-native",
136
+ needsWebPackage: false,
137
+ snippet: (apiKey, projectId) => `// App.tsx
138
+ import { MushiProvider } from '@mushi-mushi/react-native'
139
+
140
+ export default function App() {
141
+ return (
142
+ <MushiProvider projectId="${projectId}" apiKey="${apiKey}">
143
+ <YourApp />
144
+ </MushiProvider>
145
+ )
146
+ }`
147
+ },
148
+ "react-native": {
149
+ id: "react-native",
150
+ label: "React Native",
151
+ packageName: "@mushi-mushi/react-native",
152
+ needsWebPackage: false,
153
+ snippet: (apiKey, projectId) => `// App.tsx
154
+ import { MushiProvider } from '@mushi-mushi/react-native'
155
+
156
+ export default function App() {
157
+ return (
158
+ <MushiProvider projectId="${projectId}" apiKey="${apiKey}">
159
+ <YourApp />
160
+ </MushiProvider>
161
+ )
162
+ }`
163
+ },
164
+ capacitor: {
165
+ id: "capacitor",
166
+ label: "Capacitor (Ionic)",
167
+ packageName: "@mushi-mushi/capacitor",
168
+ needsWebPackage: false,
169
+ snippet: (apiKey, projectId) => `// src/main.ts
170
+ import { Mushi } from '@mushi-mushi/capacitor'
171
+
172
+ Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })`
173
+ },
174
+ vanilla: {
175
+ id: "vanilla",
176
+ label: "Vanilla JS / unknown",
177
+ packageName: "@mushi-mushi/web",
178
+ needsWebPackage: false,
179
+ snippet: (apiKey, projectId) => `// Anywhere in your client bundle
180
+ import { Mushi } from '@mushi-mushi/web'
181
+
182
+ Mushi.init({ projectId: '${projectId}', apiKey: '${apiKey}' })`
183
+ }
184
+ };
185
+ function readPackageJson(cwd) {
186
+ const path = join(cwd, "package.json");
187
+ if (!existsSync(path)) return null;
188
+ try {
189
+ return JSON.parse(readFileSync(path, "utf-8"));
190
+ } catch {
191
+ return null;
192
+ }
193
+ }
194
+ function detectFramework(cwd, pkg) {
195
+ const deps = collectDeps(pkg);
196
+ if (deps.has("next")) return FRAMEWORKS.next;
197
+ if (deps.has("nuxt")) return FRAMEWORKS.nuxt;
198
+ if (deps.has("@sveltejs/kit")) return FRAMEWORKS.sveltekit;
199
+ if (deps.has("@angular/core")) return FRAMEWORKS.angular;
200
+ if (deps.has("expo")) return FRAMEWORKS.expo;
201
+ if (deps.has("react-native")) return FRAMEWORKS["react-native"];
202
+ if (deps.has("@capacitor/core")) return FRAMEWORKS.capacitor;
203
+ if (deps.has("svelte")) return FRAMEWORKS.svelte;
204
+ if (deps.has("vue")) return FRAMEWORKS.vue;
205
+ if (deps.has("react")) return FRAMEWORKS.react;
206
+ if (existsSync(join(cwd, "next.config.js")) || existsSync(join(cwd, "next.config.ts"))) {
207
+ return FRAMEWORKS.next;
208
+ }
209
+ if (existsSync(join(cwd, "nuxt.config.ts")) || existsSync(join(cwd, "nuxt.config.js"))) {
210
+ return FRAMEWORKS.nuxt;
211
+ }
212
+ if (existsSync(join(cwd, "svelte.config.js"))) return FRAMEWORKS.svelte;
213
+ if (existsSync(join(cwd, "angular.json"))) return FRAMEWORKS.angular;
214
+ return FRAMEWORKS.vanilla;
215
+ }
216
+ function detectPackageManager(cwd) {
217
+ if (existsSync(join(cwd, "bun.lockb")) || existsSync(join(cwd, "bun.lock"))) return "bun";
218
+ if (existsSync(join(cwd, "pnpm-lock.yaml"))) return "pnpm";
219
+ if (existsSync(join(cwd, "yarn.lock"))) return "yarn";
220
+ if (existsSync(join(cwd, "package-lock.json"))) return "npm";
221
+ const userAgent = process.env.npm_config_user_agent ?? "";
222
+ if (userAgent.startsWith("bun")) return "bun";
223
+ if (userAgent.startsWith("pnpm")) return "pnpm";
224
+ if (userAgent.startsWith("yarn")) return "yarn";
225
+ return "npm";
226
+ }
227
+ function installCommand(pm, packages) {
228
+ const verb = pm === "npm" ? "install" : "add";
229
+ return `${pm} ${verb} ${packages.join(" ")}`;
230
+ }
231
+ function envVarsToWrite(apiKey, projectId, framework) {
232
+ const prefix = framework.id === "next" ? "NEXT_PUBLIC_" : framework.id === "nuxt" ? "NUXT_PUBLIC_" : "VITE_";
233
+ return [
234
+ `${prefix}MUSHI_PROJECT_ID=${projectId}`,
235
+ `${prefix}MUSHI_API_KEY=${apiKey}`
236
+ ].join("\n");
237
+ }
238
+ function collectDeps(pkg) {
239
+ if (!pkg) return /* @__PURE__ */ new Set();
240
+ return /* @__PURE__ */ new Set([
241
+ ...Object.keys(pkg.dependencies ?? {}),
242
+ ...Object.keys(pkg.devDependencies ?? {}),
243
+ ...Object.keys(pkg.peerDependencies ?? {})
244
+ ]);
245
+ }
246
+
247
+ export {
248
+ FRAMEWORK_IDS,
249
+ isFrameworkId,
250
+ FRAMEWORKS,
251
+ readPackageJson,
252
+ detectFramework,
253
+ detectPackageManager,
254
+ installCommand,
255
+ envVarsToWrite
256
+ };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * FILE: packages/cli/src/detect.ts
3
+ * PURPOSE: Pure detection helpers for framework, package manager, and project state.
4
+ * Kept side-effect-free so the wizard remains unit-testable.
5
+ */
6
+ type FrameworkId = 'next' | 'react' | 'vue' | 'nuxt' | 'svelte' | 'sveltekit' | 'angular' | 'expo' | 'react-native' | 'capacitor' | 'vanilla';
7
+ interface Framework {
8
+ id: FrameworkId;
9
+ label: string;
10
+ packageName: string;
11
+ needsWebPackage: boolean;
12
+ snippet: (apiKey: string, projectId: string) => string;
13
+ }
14
+ interface PackageJson {
15
+ name?: string;
16
+ dependencies?: Record<string, string>;
17
+ devDependencies?: Record<string, string>;
18
+ peerDependencies?: Record<string, string>;
19
+ }
20
+ type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';
21
+ declare const FRAMEWORK_IDS: ReadonlyArray<FrameworkId>;
22
+ declare function isFrameworkId(value: unknown): value is FrameworkId;
23
+ declare const FRAMEWORKS: Record<FrameworkId, Framework>;
24
+ declare function readPackageJson(cwd: string): PackageJson | null;
25
+ declare function detectFramework(cwd: string, pkg: PackageJson | null): Framework;
26
+ declare function detectPackageManager(cwd: string): PackageManager;
27
+ declare function installCommand(pm: PackageManager, packages: string[]): string;
28
+ declare function envVarsToWrite(apiKey: string, projectId: string, framework: Framework): string;
29
+
30
+ export { FRAMEWORKS, FRAMEWORK_IDS, type Framework, type FrameworkId, type PackageJson, type PackageManager, detectFramework, detectPackageManager, envVarsToWrite, installCommand, isFrameworkId, readPackageJson };
package/dist/detect.js ADDED
@@ -0,0 +1,20 @@
1
+ import {
2
+ FRAMEWORKS,
3
+ FRAMEWORK_IDS,
4
+ detectFramework,
5
+ detectPackageManager,
6
+ envVarsToWrite,
7
+ installCommand,
8
+ isFrameworkId,
9
+ readPackageJson
10
+ } from "./chunk-ZZNVMBMG.js";
11
+ export {
12
+ FRAMEWORKS,
13
+ FRAMEWORK_IDS,
14
+ detectFramework,
15
+ detectPackageManager,
16
+ envVarsToWrite,
17
+ installCommand,
18
+ isFrameworkId,
19
+ readPackageJson
20
+ };