@mushi-mushi/cli 0.5.2 → 0.6.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/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTING.md +4 -0
- package/README.md +23 -0
- package/SECURITY.md +75 -1
- package/dist/chunk-KNU5OWYY.js +6 -0
- package/dist/{chunk-ZZNVMBMG.js → chunk-XHD3H54W.js} +8 -2
- package/dist/detect.js +1 -1
- package/dist/index.js +155 -4
- package/dist/init.js +6 -3
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-FE5YYKNI.js +0 -6
package/CODE_OF_CONDUCT.md
CHANGED
|
@@ -40,7 +40,7 @@ Examples of unacceptable behavior:
|
|
|
40
40
|
## Enforcement
|
|
41
41
|
|
|
42
42
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
43
|
-
reported to the project team at **
|
|
43
|
+
reported to the project team at **kensaurus@gmail.com**.
|
|
44
44
|
|
|
45
45
|
All complaints will be reviewed and investigated promptly and fairly.
|
|
46
46
|
|
package/CONTRIBUTING.md
CHANGED
|
@@ -33,6 +33,10 @@ pnpm lint # ESLint
|
|
|
33
33
|
pnpm format # Prettier
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
Ad-hoc screenshots captured during UI reviews can live temporarily at the repo
|
|
37
|
+
root, but root-level `*.png` files are intentionally ignored. Canonical
|
|
38
|
+
screenshots that should be versioned belong under `docs/screenshots/`.
|
|
39
|
+
|
|
36
40
|
### Working on a single package
|
|
37
41
|
|
|
38
42
|
```bash
|
package/README.md
CHANGED
|
@@ -59,9 +59,32 @@ mushi reports triage <id> --status acknowledged --severity high
|
|
|
59
59
|
mushi deploy check # edge-function health probe
|
|
60
60
|
mushi index <path> # walk a local repo and feed RAG
|
|
61
61
|
mushi test # submit a test report end-to-end
|
|
62
|
+
mushi migrate # suggest the most relevant migration guide
|
|
63
|
+
mushi migrate --json # machine-readable JSON for CI
|
|
62
64
|
mushi config endpoint https://... # set API endpoint (https:// required outside localhost)
|
|
63
65
|
```
|
|
64
66
|
|
|
67
|
+
### `mushi migrate`
|
|
68
|
+
|
|
69
|
+
Reads `package.json` (deps + devDeps + peerDeps) and prints links to the
|
|
70
|
+
matching guides on the docs site. Detects:
|
|
71
|
+
|
|
72
|
+
- **In-transition shapes** — Capacitor + React Native side-by-side, Cordova
|
|
73
|
+
(or `cordova-ios`/`cordova-android`), Create React App.
|
|
74
|
+
- **Competitor SDKs** — Instabug / Luciq, Shake, LogRocket Feedback,
|
|
75
|
+
BugHerd, Pendo Feedback.
|
|
76
|
+
|
|
77
|
+
Exits non-zero when nothing matches, so it composes in shell scripts:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
mushi migrate || echo "no migration suggestions for this project"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Only `published` guides ever surface — `draft` entries are filtered out so
|
|
84
|
+
the CLI never points users at a 404. This safety property is unit-pinned in
|
|
85
|
+
`packages/cli/src/migrate.test.ts` (positive control + negative control +
|
|
86
|
+
real-catalog regression guard).
|
|
87
|
+
|
|
65
88
|
## Security notes
|
|
66
89
|
|
|
67
90
|
- `~/.mushirc` is written with mode `0o600` on Unix. Legacy configs with looser permissions are tightened on load.
|
package/SECURITY.md
CHANGED
|
@@ -19,7 +19,7 @@ If you discover a security vulnerability, please report it responsibly.
|
|
|
19
19
|
|
|
20
20
|
**Do NOT open a public GitHub issue.**
|
|
21
21
|
|
|
22
|
-
Instead, email: **
|
|
22
|
+
Instead, email: **kensaurus@gmail.com**
|
|
23
23
|
|
|
24
24
|
Include:
|
|
25
25
|
- Description of the vulnerability
|
|
@@ -48,3 +48,77 @@ We will acknowledge receipt within 48 hours and aim to release a patch within 7
|
|
|
48
48
|
- **Rotate API keys** regularly via the admin console
|
|
49
49
|
- **Enable SSO** for team projects (Enterprise tier)
|
|
50
50
|
- **Review audit logs** periodically for suspicious activity
|
|
51
|
+
|
|
52
|
+
## Supply-chain hardening (how this package is protected)
|
|
53
|
+
|
|
54
|
+
Mushi Mushi is built and published with the controls below. Consumers can
|
|
55
|
+
verify each control independently — the goal is to make tampering both
|
|
56
|
+
difficult and detectable.
|
|
57
|
+
|
|
58
|
+
### Publish-time controls
|
|
59
|
+
|
|
60
|
+
| Control | What it does | How to verify |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| **npm Trusted Publisher (OIDC)** | Every release is published from `.github/workflows/release.yml` on `master` using a short-lived OIDC token. Long-lived `NPM_TOKEN` is not used for publishing. | `npm view @mushi-mushi/<pkg> --json` shows `"trustedPublisher"` populated for recent versions. |
|
|
63
|
+
| **npm provenance attestations** | Every published tarball ships a [Sigstore provenance attestation](https://docs.npmjs.com/generating-provenance-statements) cryptographically linking the tarball to the exact GitHub Actions run that built it. | `npm audit signatures` (run inside any project that depends on `@mushi-mushi/*`) reports `verified registry signatures` and `verified attestations`. The npm web UI shows a "Built and signed on GitHub Actions" badge on each version. |
|
|
64
|
+
| **Pre-publish workspace-protocol guard** | Aborts the publish if `workspace:*` ranges leaked into the tarball (the bug class behind the v0.1.0 incident). | `scripts/check-workspace-protocol.mjs` runs before `changeset publish` in `pnpm release`. |
|
|
65
|
+
| **Post-publish tarball verification** | Re-downloads each just-published tarball and asserts it doesn't contain `workspace:*`. | See the "Verify published tarballs do not contain workspace:*" step in `release.yml`. |
|
|
66
|
+
| **Post-publish `npm audit signatures`** | Re-installs each published version and validates registry signatures + provenance against npm's transparency log. | See the "Audit signatures of installed dependencies" step in `release.yml`. |
|
|
67
|
+
|
|
68
|
+
### Build-time controls
|
|
69
|
+
|
|
70
|
+
| Control | What it does |
|
|
71
|
+
|---|---|
|
|
72
|
+
| **All third-party GitHub Actions pinned to commit SHAs** | Every `uses:` in every workflow under `.github/workflows/` is pinned to a 40-character commit SHA with a version comment. Floating tags (`@v4`, `@main`) are mutable and were the entry point for the [tj-actions/changed-files compromise (CVE-2025-30066)](https://github.com/step-security/harden-runner#detected-attacks). |
|
|
73
|
+
| **Harden-Runner egress audit on every job** | [step-security/harden-runner](https://github.com/step-security/harden-runner) records every outbound network call, file write, and process spawn on every CI runner. Detects exfiltration attempts in real time — caught the tj-actions, NX, Shai-Hulud, and Axios attacks for other projects. |
|
|
74
|
+
| **OpenSSF Scorecard** | Weekly + on-push score of the repo's security posture (Pinned-Dependencies, Token-Permissions, Branch-Protection, Code-Review, Dangerous-Workflow, Maintained, SAST, Security-Policy, Signed-Releases, Vulnerabilities). Public results at [scorecard.dev](https://scorecard.dev/viewer/?uri=github.com/kensaurus/mushi-mushi). |
|
|
75
|
+
| **Server-side secret scan (Gitleaks)** | Every PR and every push to `master` runs Gitleaks across the diff / full tree. Belt-and-suspenders to the local pre-commit hook (`scripts/check-no-secrets.mjs`) which can be bypassed with `--no-verify`. |
|
|
76
|
+
| **Local pre-commit secret scanner** | `scripts/check-no-secrets.mjs` runs as a git hook installed by `pnpm install`, blocking commits that look like AWS / Stripe / GitHub / Anthropic / OpenAI / Slack / Supabase keys. |
|
|
77
|
+
| **CodeQL `security-extended`** | Semantic analysis of every TypeScript / JavaScript change finds injection sinks, taint flows, prototype pollution, etc. Runs on every PR, push, and weekly cron. |
|
|
78
|
+
| **Dependency review on PRs** | `actions/dependency-review-action` blocks the PR if it adds or upgrades a dep with a high-severity advisory. |
|
|
79
|
+
| **`pnpm audit --prod --audit-level=high`** | Weekly cron + every push to `master` fails on any high/critical advisory in production deps. |
|
|
80
|
+
|
|
81
|
+
### Install-time controls (protect the project's own dependency graph)
|
|
82
|
+
|
|
83
|
+
| Control | What it does |
|
|
84
|
+
|---|---|
|
|
85
|
+
| **`min-release-age` (npm) / `minimumReleaseAge` (pnpm)** | Refuses to resolve any dep version published less than 7 days ago. The Axios 1.14.1 / 0.30.4 compromise (Mar 2026) was detected and removed within ~5 hours; Shai-Hulud (Sep 2025) within <12 hours — a 7-day cooldown blocks every publicly-disclosed 2025–2026 npm supply-chain attack outright. |
|
|
86
|
+
| **`strictDepBuilds: true`** | Fails the install if any transitive dep tries to run a `postinstall` hook the workspace hasn't pre-approved (`onlyBuiltDependencies` allow-list). |
|
|
87
|
+
| **`blockExoticSubdeps: true`** | Refuses to resolve transitive deps from git URLs, tarball URLs, or filesystem paths — anything that didn't go through the npm registry's signing pipeline. |
|
|
88
|
+
| **Dependabot with cooldown** | Routine dep upgrades wait 7 days; security advisories bypass the cooldown automatically. |
|
|
89
|
+
| **`pnpm audit signatures`-style verification** | The release pipeline re-runs `npm audit signatures` against each published version after the publish, with `--audit-level=high`. |
|
|
90
|
+
|
|
91
|
+
### Verifying a Mushi Mushi tarball before installing
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# 1. Check provenance attestation matches the public GitHub Actions run
|
|
95
|
+
npm view @mushi-mushi/core --json | jq '.signatures, .dist'
|
|
96
|
+
|
|
97
|
+
# 2. Inside your own project after install
|
|
98
|
+
npm audit signatures
|
|
99
|
+
|
|
100
|
+
# Expected: every @mushi-mushi/* package reports
|
|
101
|
+
# "verified registry signature"
|
|
102
|
+
# "verified attestation"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
If `npm audit signatures` reports any `@mushi-mushi/*` package as unsigned
|
|
106
|
+
or with an invalid attestation, **stop the install and email
|
|
107
|
+
kensaurus@gmail.com immediately** — that's the symptom of either a
|
|
108
|
+
registry compromise or a tampered tarball, and we want to know within
|
|
109
|
+
hours, not days.
|
|
110
|
+
|
|
111
|
+
### What this hardening does NOT cover
|
|
112
|
+
|
|
113
|
+
- **Self-hosted deployments.** Once the package is on your machine, the
|
|
114
|
+
security of your `node_modules`, build pipeline, and runtime is your
|
|
115
|
+
responsibility. The hardening above protects the path from source to
|
|
116
|
+
registry; it cannot protect a tarball after it has been downloaded.
|
|
117
|
+
- **Compromise of `kensaurus@gmail.com`.** A trusted-publisher rule still
|
|
118
|
+
lets the legitimate maintainer publish from any branch they push. If
|
|
119
|
+
you find yourself with admin access to this repo, treat
|
|
120
|
+
`.github/workflows/release.yml` as a tier-0 secret.
|
|
121
|
+
- **First-party bugs.** Provenance proves *who* built the tarball and
|
|
122
|
+
*when*; it does not prove the code is bug-free. CodeQL + tests cover
|
|
123
|
+
that surface, but no automation catches everything — please continue
|
|
124
|
+
to report issues to the address above.
|
|
@@ -166,10 +166,16 @@ export default function App() {
|
|
|
166
166
|
label: "Capacitor (Ionic)",
|
|
167
167
|
packageName: "@mushi-mushi/capacitor",
|
|
168
168
|
needsWebPackage: false,
|
|
169
|
-
|
|
169
|
+
/* The Capacitor plugin's public API is `Mushi.configure(...)`, not
|
|
170
|
+
* `Mushi.init(...)` — see packages/capacitor/src/definitions.ts. We
|
|
171
|
+
* shipped the wrong call here for two releases and users got a runtime
|
|
172
|
+
* `TypeError: Mushi.init is not a function`. The accompanying admin
|
|
173
|
+
* console snippet (apps/admin/src/lib/sdkSnippets.ts) emits the same
|
|
174
|
+
* `Mushi.configure(...)` shape; both are pinned by tests. */
|
|
175
|
+
snippet: (apiKey, projectId) => `// src/main.ts \u2014 after install, run \`npx cap sync\`
|
|
170
176
|
import { Mushi } from '@mushi-mushi/capacitor'
|
|
171
177
|
|
|
172
|
-
Mushi.
|
|
178
|
+
await Mushi.configure({ projectId: '${projectId}', apiKey: '${apiKey}' })`
|
|
173
179
|
},
|
|
174
180
|
vanilla: {
|
|
175
181
|
id: "vanilla",
|
package/dist/detect.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -190,10 +190,16 @@ export default function App() {
|
|
|
190
190
|
label: "Capacitor (Ionic)",
|
|
191
191
|
packageName: "@mushi-mushi/capacitor",
|
|
192
192
|
needsWebPackage: false,
|
|
193
|
-
|
|
193
|
+
/* The Capacitor plugin's public API is `Mushi.configure(...)`, not
|
|
194
|
+
* `Mushi.init(...)` — see packages/capacitor/src/definitions.ts. We
|
|
195
|
+
* shipped the wrong call here for two releases and users got a runtime
|
|
196
|
+
* `TypeError: Mushi.init is not a function`. The accompanying admin
|
|
197
|
+
* console snippet (apps/admin/src/lib/sdkSnippets.ts) emits the same
|
|
198
|
+
* `Mushi.configure(...)` shape; both are pinned by tests. */
|
|
199
|
+
snippet: (apiKey, projectId) => `// src/main.ts \u2014 after install, run \`npx cap sync\`
|
|
194
200
|
import { Mushi } from '@mushi-mushi/capacitor'
|
|
195
201
|
|
|
196
|
-
Mushi.
|
|
202
|
+
await Mushi.configure({ projectId: '${projectId}', apiKey: '${apiKey}' })`
|
|
197
203
|
},
|
|
198
204
|
vanilla: {
|
|
199
205
|
id: "vanilla",
|
|
@@ -448,7 +454,7 @@ function getFrameworkFromPkg(pkg) {
|
|
|
448
454
|
}
|
|
449
455
|
|
|
450
456
|
// src/version.ts
|
|
451
|
-
var MUSHI_CLI_VERSION = true ? "0.
|
|
457
|
+
var MUSHI_CLI_VERSION = true ? "0.6.0" : "0.0.0-dev";
|
|
452
458
|
|
|
453
459
|
// src/init.ts
|
|
454
460
|
var ENV_FILES = [".env.local", ".env"];
|
|
@@ -565,8 +571,11 @@ async function promptText(opts) {
|
|
|
565
571
|
const value = await p.text({
|
|
566
572
|
message: opts.message,
|
|
567
573
|
placeholder: opts.placeholder,
|
|
574
|
+
// @clack/prompts v1 widened the validate input to `string | undefined`
|
|
575
|
+
// (the previous v0.x API guaranteed a string). Guard the empty case
|
|
576
|
+
// explicitly so the rest of the pipeline keeps its `string` invariant.
|
|
568
577
|
validate: (v) => {
|
|
569
|
-
const clean = sanitizeSecret(v);
|
|
578
|
+
const clean = sanitizeSecret(v ?? "");
|
|
570
579
|
if (clean.length === 0) return "Required";
|
|
571
580
|
return opts.validate ? opts.validate(clean) : void 0;
|
|
572
581
|
}
|
|
@@ -763,6 +772,142 @@ function isSameDirectory(a, b) {
|
|
|
763
772
|
return a.replace(/\\/g, "/").replace(/\/+$/, "") === b.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
764
773
|
}
|
|
765
774
|
|
|
775
|
+
// src/migrate.ts
|
|
776
|
+
var DOCS_BASE = "https://docs.mushimushi.dev";
|
|
777
|
+
var COMPETITOR_PACKAGES = {
|
|
778
|
+
"instabug-to-mushi": [
|
|
779
|
+
"instabug-reactnative",
|
|
780
|
+
"luciq-reactnative-sdk",
|
|
781
|
+
"@instabug/web",
|
|
782
|
+
"instabug"
|
|
783
|
+
],
|
|
784
|
+
"shake-to-mushi": [
|
|
785
|
+
"@shakebugs/react-native-shake",
|
|
786
|
+
"@shakebugs/shake-react-native",
|
|
787
|
+
"@softnoesis/shakebug-js"
|
|
788
|
+
],
|
|
789
|
+
"logrocket-feedback-to-mushi": ["logrocket", "logrocket-react"],
|
|
790
|
+
"bugherd-to-mushi": ["bugherd-pubsub"],
|
|
791
|
+
"pendo-feedback-to-mushi": ["pendo-io-browser", "@pendo/web"]
|
|
792
|
+
};
|
|
793
|
+
var MIGRATE_CATALOG = [
|
|
794
|
+
// ── In-transition / cross-stack shapes (highest priority) ─────────
|
|
795
|
+
{
|
|
796
|
+
slug: "capacitor-to-react-native",
|
|
797
|
+
title: "Capacitor \u2192 React Native",
|
|
798
|
+
summary: "Looks like you have BOTH Capacitor and React Native installed \u2014 finishing this port? See the full Cap \u2192 RN plan.",
|
|
799
|
+
category: "mobile",
|
|
800
|
+
status: "published",
|
|
801
|
+
detectionLabel: "@capacitor/core + react-native",
|
|
802
|
+
match: (d) => d.has("@capacitor/core") && d.has("react-native")
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
slug: "cordova-to-capacitor",
|
|
806
|
+
title: "Cordova \u2192 Capacitor",
|
|
807
|
+
summary: "Cordova detected. Migrate in place to Capacitor for the modern WebView, plugin ecosystem, and first-party Mushi support.",
|
|
808
|
+
category: "mobile",
|
|
809
|
+
status: "published",
|
|
810
|
+
detectionLabel: "cordova",
|
|
811
|
+
match: (d) => d.has("cordova") || d.has("cordova-android") || d.has("cordova-ios")
|
|
812
|
+
},
|
|
813
|
+
// ── Web framework legacy shapes ───────────────────────────────────
|
|
814
|
+
{
|
|
815
|
+
slug: "cra-to-vite",
|
|
816
|
+
title: "Create React App \u2192 Vite",
|
|
817
|
+
summary: "react-scripts detected. CRA is unmaintained \u2014 `npx migrate-to-vite@latest cra` covers ~90 % of the move.",
|
|
818
|
+
category: "web",
|
|
819
|
+
status: "published",
|
|
820
|
+
detectionLabel: "react-scripts",
|
|
821
|
+
match: (d) => d.has("react-scripts")
|
|
822
|
+
},
|
|
823
|
+
// ── Competitor packages ───────────────────────────────────────────
|
|
824
|
+
...Object.entries(COMPETITOR_PACKAGES).map(([slug, pkgs]) => ({
|
|
825
|
+
slug,
|
|
826
|
+
title: titleForCompetitor(slug),
|
|
827
|
+
summary: `Found ${pkgs.join(" / ")} \u2014 Mushi covers the same surface with built-in AI triage and a Shadow-DOM widget.`,
|
|
828
|
+
category: "competitor",
|
|
829
|
+
status: "published",
|
|
830
|
+
detectionLabel: pkgs[0],
|
|
831
|
+
match: (d) => pkgs.some((p2) => d.has(p2))
|
|
832
|
+
}))
|
|
833
|
+
];
|
|
834
|
+
function titleForCompetitor(slug) {
|
|
835
|
+
switch (slug) {
|
|
836
|
+
case "instabug-to-mushi":
|
|
837
|
+
return "Instabug (Luciq) \u2192 Mushi";
|
|
838
|
+
case "shake-to-mushi":
|
|
839
|
+
return "Shake \u2192 Mushi";
|
|
840
|
+
case "logrocket-feedback-to-mushi":
|
|
841
|
+
return "LogRocket Feedback \u2192 Mushi";
|
|
842
|
+
case "bugherd-to-mushi":
|
|
843
|
+
return "BugHerd \u2192 Mushi";
|
|
844
|
+
case "pendo-feedback-to-mushi":
|
|
845
|
+
return "Pendo Feedback \u2192 Mushi";
|
|
846
|
+
default:
|
|
847
|
+
return slug;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
function detectMigrations(deps, catalog = MIGRATE_CATALOG) {
|
|
851
|
+
return catalog.filter((g) => g.status === "published" && g.match?.(deps)).map((g) => ({ guide: g, url: `${DOCS_BASE}/migrations/${g.slug}` }));
|
|
852
|
+
}
|
|
853
|
+
function depsFromPackageJson(pkg) {
|
|
854
|
+
if (!pkg) return /* @__PURE__ */ new Set();
|
|
855
|
+
return /* @__PURE__ */ new Set([
|
|
856
|
+
...Object.keys(pkg.dependencies ?? {}),
|
|
857
|
+
...Object.keys(pkg.devDependencies ?? {}),
|
|
858
|
+
...Object.keys(pkg.peerDependencies ?? {})
|
|
859
|
+
]);
|
|
860
|
+
}
|
|
861
|
+
function runMigrate(opts = {}) {
|
|
862
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
863
|
+
const log2 = opts.log ?? ((s) => console.log(s));
|
|
864
|
+
const pkg = readPackageJson(cwd);
|
|
865
|
+
if (!pkg) {
|
|
866
|
+
log2(
|
|
867
|
+
opts.json ? JSON.stringify({ ok: false, error: "no-package-json", cwd, matches: [] }, null, 2) : `No package.json found in ${cwd}. Run \`mushi migrate\` from your project root.`
|
|
868
|
+
);
|
|
869
|
+
return { matches: [] };
|
|
870
|
+
}
|
|
871
|
+
const deps = depsFromPackageJson(pkg);
|
|
872
|
+
const matches = detectMigrations(deps);
|
|
873
|
+
if (opts.json) {
|
|
874
|
+
log2(
|
|
875
|
+
JSON.stringify(
|
|
876
|
+
{
|
|
877
|
+
ok: true,
|
|
878
|
+
cwd,
|
|
879
|
+
matches: matches.map((m) => ({
|
|
880
|
+
slug: m.guide.slug,
|
|
881
|
+
title: m.guide.title,
|
|
882
|
+
url: m.url,
|
|
883
|
+
category: m.guide.category,
|
|
884
|
+
detectionLabel: m.guide.detectionLabel
|
|
885
|
+
}))
|
|
886
|
+
},
|
|
887
|
+
null,
|
|
888
|
+
2
|
|
889
|
+
)
|
|
890
|
+
);
|
|
891
|
+
return { matches };
|
|
892
|
+
}
|
|
893
|
+
if (matches.length === 0) {
|
|
894
|
+
log2("No migrations suggested for this project.");
|
|
895
|
+
log2(`Browse the full catalog: ${DOCS_BASE}/migrations`);
|
|
896
|
+
return { matches };
|
|
897
|
+
}
|
|
898
|
+
log2(`Suggested migration${matches.length > 1 ? "s" : ""} for this project:`);
|
|
899
|
+
log2("");
|
|
900
|
+
for (const { guide, url } of matches) {
|
|
901
|
+
log2(` \u2022 ${guide.title}`);
|
|
902
|
+
if (guide.detectionLabel) log2(` detected: ${guide.detectionLabel}`);
|
|
903
|
+
log2(` ${guide.summary}`);
|
|
904
|
+
log2(` ${url}`);
|
|
905
|
+
log2("");
|
|
906
|
+
}
|
|
907
|
+
log2(`Browse the full catalog: ${DOCS_BASE}/migrations`);
|
|
908
|
+
return { matches };
|
|
909
|
+
}
|
|
910
|
+
|
|
766
911
|
// src/index.ts
|
|
767
912
|
async function apiCall(path, config, options = {}) {
|
|
768
913
|
const endpoint = config.endpoint ?? DEFAULT_ENDPOINT;
|
|
@@ -791,6 +936,12 @@ program.command("init").description("Set up the Mushi Mushi SDK in this project
|
|
|
791
936
|
sendTestReport: opts.skipTestReport ? false : void 0
|
|
792
937
|
});
|
|
793
938
|
});
|
|
939
|
+
program.command("migrate").description(
|
|
940
|
+
"Suggest the most relevant Mushi Mushi migration guide based on your package.json"
|
|
941
|
+
).option("--cwd <path>", "Run from a different directory").option("--json", "Machine-readable JSON output").action((opts) => {
|
|
942
|
+
const { matches } = runMigrate({ cwd: opts.cwd, json: opts.json });
|
|
943
|
+
if (matches.length === 0) process.exit(1);
|
|
944
|
+
});
|
|
794
945
|
program.command("login").description("Store API key for authentication").requiredOption("--api-key <key>", "API key").option("--endpoint <url>", "API endpoint URL").option("--project-id <id>", "Default project ID").action((opts) => {
|
|
795
946
|
const config = loadConfig();
|
|
796
947
|
config.apiKey = opts.apiKey;
|
package/dist/init.js
CHANGED
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
envVarsToWrite,
|
|
6
6
|
installCommand,
|
|
7
7
|
readPackageJson
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-XHD3H54W.js";
|
|
9
9
|
import {
|
|
10
10
|
MUSHI_CLI_VERSION
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-KNU5OWYY.js";
|
|
12
12
|
|
|
13
13
|
// src/init.ts
|
|
14
14
|
import * as p from "@clack/prompts";
|
|
@@ -325,8 +325,11 @@ async function promptText(opts) {
|
|
|
325
325
|
const value = await p.text({
|
|
326
326
|
message: opts.message,
|
|
327
327
|
placeholder: opts.placeholder,
|
|
328
|
+
// @clack/prompts v1 widened the validate input to `string | undefined`
|
|
329
|
+
// (the previous v0.x API guaranteed a string). Guard the empty case
|
|
330
|
+
// explicitly so the rest of the pipeline keeps its `string` invariant.
|
|
328
331
|
validate: (v) => {
|
|
329
|
-
const clean = sanitizeSecret(v);
|
|
332
|
+
const clean = sanitizeSecret(v ?? "");
|
|
330
333
|
if (clean.length === 0) return "Required";
|
|
331
334
|
return opts.validate ? opts.validate(clean) : void 0;
|
|
332
335
|
}
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mushi-mushi/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "CLI for Mushi Mushi — `mushi init` wizard installs the right SDK for your framework, plus report triage and pipeline health commands",
|
|
6
6
|
"bin": {
|
|
7
7
|
"mushi": "./dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@clack/prompts": "^
|
|
10
|
+
"@clack/prompts": "^1.2.0",
|
|
11
11
|
"commander": "^14.0.3"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|