@mushi-mushi/cli 0.30.6 → 0.32.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.
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  MUSHI_CLI_VERSION,
4
4
  MUSHI_MCP_PIN_SPEC
5
- } from "./chunk-D7ZVFRYW.js";
5
+ } from "./chunk-XIB5NTHP.js";
6
6
  import {
7
7
  CLOUD_API_ENDPOINT,
8
8
  assertEndpoint,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  MUSHI_CLI_VERSION
3
- } from "./chunk-QR6NCHXT.js";
3
+ } from "./chunk-MARVQVJC.js";
4
4
  import {
5
5
  assertEndpoint,
6
6
  normalizeEndpoint
@@ -0,0 +1,8 @@
1
+ // src/version.ts
2
+ var MUSHI_CLI_VERSION = true ? "0.32.0" : "0.0.0-dev";
3
+ var MUSHI_MCP_PIN_SPEC = "@mushi-mushi/mcp@0.28.0";
4
+
5
+ export {
6
+ MUSHI_CLI_VERSION,
7
+ MUSHI_MCP_PIN_SPEC
8
+ };
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/version.ts
4
+ var MUSHI_CLI_VERSION = true ? "0.32.0" : "0.0.0-dev";
5
+ var MUSHI_MCP_PIN_SPEC = "@mushi-mushi/mcp@0.28.0";
6
+
7
+ export {
8
+ MUSHI_CLI_VERSION,
9
+ MUSHI_MCP_PIN_SPEC
10
+ };
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  resolveSdkEnvKey,
4
4
  runConnect
5
- } from "./chunk-YAF2NATT.js";
6
- import "./chunk-D7ZVFRYW.js";
5
+ } from "./chunk-3YMPNCJ4.js";
6
+ import "./chunk-XIB5NTHP.js";
7
7
  import "./chunk-LGGE3LH7.js";
8
8
  import "./chunk-KOBEOGNF.js";
9
9
  export {
@@ -6,10 +6,10 @@ import {
6
6
  saveConfig,
7
7
  savedSdkKeyFor,
8
8
  waitForIngestReady
9
- } from "./chunk-BNO37ZO5.js";
9
+ } from "./chunk-7PSMATX3.js";
10
10
  import {
11
11
  MUSHI_MCP_PIN_SPEC
12
- } from "./chunk-QR6NCHXT.js";
12
+ } from "./chunk-MARVQVJC.js";
13
13
  import {
14
14
  detectFramework,
15
15
  envVarsToWrite,
package/dist/index.js CHANGED
@@ -54,11 +54,11 @@ import {
54
54
  syncKeychainAfterKeyChange,
55
55
  trySaveKeyToKeychain,
56
56
  writeMcpServerEntry
57
- } from "./chunk-YAF2NATT.js";
57
+ } from "./chunk-3YMPNCJ4.js";
58
58
  import {
59
59
  MUSHI_CLI_VERSION,
60
60
  MUSHI_MCP_PIN_SPEC
61
- } from "./chunk-D7ZVFRYW.js";
61
+ } from "./chunk-XIB5NTHP.js";
62
62
  import {
63
63
  CLOUD_API_ENDPOINT,
64
64
  CLOUD_MCP_OAUTH_URL,
@@ -1546,7 +1546,7 @@ async function checkOnboardingStatus(config, _consoleBase, cwd, doFetch = global
1546
1546
  }
1547
1547
  async function checkVersionDrift(cwd, doFetch) {
1548
1548
  const checks = [];
1549
- const { MUSHI_CLI_VERSION: MUSHI_CLI_VERSION2 } = await import("./version-NJJQDMYO.js");
1549
+ const { MUSHI_CLI_VERSION: MUSHI_CLI_VERSION2 } = await import("./version-AX72RMKE.js");
1550
1550
  if (MUSHI_CLI_VERSION2 === "0.0.0-dev") return checks;
1551
1551
  const parse2 = (v) => {
1552
1552
  const m = v.replace(/^[~^]/, "").match(/^(\d+)\.(\d+)\.(\d+)/);
@@ -2470,7 +2470,7 @@ async function maybeOfferConnect(credentials, options, _consoleBase) {
2470
2470
  if (p.isCancel(answer) || !answer) return;
2471
2471
  const endpoint = resolveCloudEndpoint(options.endpoint);
2472
2472
  try {
2473
- const { runConnect: runConnect2 } = await import("./connect-D7FYRBAP.js");
2473
+ const { runConnect: runConnect2 } = await import("./connect-JWNAYQEU.js");
2474
2474
  const saved = loadConfig();
2475
2475
  const result = await runConnect2(
2476
2476
  {
@@ -4521,6 +4521,9 @@ Examples:
4521
4521
  "--method <method>",
4522
4522
  "GitHub merge method: squash (default), merge, or rebase",
4523
4523
  "squash"
4524
+ ).option(
4525
+ "--override",
4526
+ 'Not available here: the CLI signs in with an API key, and only a person in the Mushi console can override an Opus "hold" (with a reason)'
4524
4527
  ).option("--json", "Machine-readable JSON output").action(async (fixId, opts) => {
4525
4528
  const config = loadConfig();
4526
4529
  if (!config.apiKey) {
@@ -4531,6 +4534,12 @@ Examples:
4531
4534
  console.error("No endpoint configured. Run `mushi init`");
4532
4535
  process.exit(1);
4533
4536
  }
4537
+ if (opts.override) {
4538
+ console.error(
4539
+ 'Overriding an Opus "hold" needs a person in the Mushi console: open the fix, read the findings, and choose Merge anyway with a reason. The CLI signs in with an API key, which cannot override (ADR 0030).'
4540
+ );
4541
+ process.exit(1);
4542
+ }
4534
4543
  const method = opts.method;
4535
4544
  if (!["squash", "merge", "rebase"].includes(method)) {
4536
4545
  console.error("--method must be squash, merge, or rebase");
@@ -4545,6 +4554,9 @@ Examples:
4545
4554
  console.log(JSON.stringify({ ok: false, error: result.error }, null, 2));
4546
4555
  } else {
4547
4556
  console.error("Merge failed:", result.error.message);
4557
+ if (result.error.code === "REVIEW_HOLD") {
4558
+ console.error(`Read the review: mushi fixes review ${fixId} --show. To merge anyway, override the hold in the Mushi console (with a reason).`);
4559
+ }
4548
4560
  }
4549
4561
  process.exit(1);
4550
4562
  }
@@ -4558,6 +4570,46 @@ Examples:
4558
4570
  } else {
4559
4571
  console.log(`Merged successfully. Report status: ${data.reportStatus ?? "fixed"}`);
4560
4572
  }
4573
+ if (data.review) {
4574
+ console.log(`${data.review.label}${data.review.overridden ? " (overridden)" : ""}`);
4575
+ }
4576
+ });
4577
+ function printReview(review) {
4578
+ console.log(review.label);
4579
+ if (review.reason) console.log(review.reason);
4580
+ if (review.summary) console.log(`
4581
+ ${review.summary}`);
4582
+ for (const f of review.findings) {
4583
+ const where = f.file === "(general)" ? "" : ` ${f.file}${f.line ? `:${f.line}` : ""}`;
4584
+ console.log(` - ${f.severity}${where}: ${f.issue} Fix: ${f.suggested_fix}`);
4585
+ }
4586
+ for (const r of review.removesBehavior) console.log(` - removes: ${r}`);
4587
+ if (review.needsHumanDecision) console.log(`Needs your decision: ${review.needsHumanDecision}`);
4588
+ if (review.commentUrl) console.log(`On GitHub: ${review.commentUrl}`);
4589
+ }
4590
+ fixes.command("review <fixId>").description("Re-run the Opus review of a fix PR (ADR 0030), or show the current one with --show").option("--show", "Show the current review instead of starting a new one").option("--json", "Machine-readable JSON output").action(async (fixId, opts) => {
4591
+ const config = loadConfig();
4592
+ if (!config.apiKey) {
4593
+ console.error("Run `mushi login` first");
4594
+ process.exit(1);
4595
+ }
4596
+ if (!config.endpoint) {
4597
+ console.error("No endpoint configured. Run `mushi init`");
4598
+ process.exit(1);
4599
+ }
4600
+ const result = opts.show ? await apiCall(`/v1/admin/fixes/${fixId}/timeline`, config) : await apiCall(`/v1/admin/fixes/${fixId}/review`, config, { method: "POST" });
4601
+ if (!result.ok) {
4602
+ if (opts.json) console.log(JSON.stringify({ ok: false, error: result.error }, null, 2));
4603
+ else console.error(opts.show ? "Could not read the review:" : "Review not started:", result.error.message);
4604
+ process.exit(1);
4605
+ }
4606
+ const review = result.data?.review;
4607
+ if (opts.json) {
4608
+ console.log(JSON.stringify({ ok: true, review: review ?? null }, null, 2));
4609
+ return;
4610
+ }
4611
+ if (!opts.show) console.log("Review queued. It takes a few minutes; check with --show.");
4612
+ if (review) printReview(review);
4561
4613
  });
4562
4614
  fixes.command("refresh-ci <fixId>").description("Pull latest GitHub Actions check-run status for a fix attempt (on-demand ci-sync)").option("--json", "Machine-readable JSON output").action(async (fixId, opts) => {
4563
4615
  const config = loadConfig();
@@ -5322,6 +5374,31 @@ Approve: mushi tdd approve <id>`);
5322
5374
  });
5323
5375
  }
5324
5376
 
5377
+ // src/spend-down.ts
5378
+ function wholeNumber(raw, flag, min, max) {
5379
+ const n = Number(raw);
5380
+ if (!Number.isInteger(n) || n < min || n > max) throw new Error(`${flag} must be a whole number from ${min} to ${max}`);
5381
+ return n;
5382
+ }
5383
+ function spendDownBody(flags) {
5384
+ if (flags.on && flags.off) throw new Error("Pass --on or --off, not both");
5385
+ const body = {};
5386
+ if (flags.on) body.enabled = true;
5387
+ if (flags.off) body.enabled = false;
5388
+ if (flags.expires !== void 0) body.creditsExpireAt = flags.expires;
5389
+ if (flags.model !== void 0) body.model = flags.model === "" ? null : flags.model;
5390
+ if (flags.perDay !== void 0) body.dailyLimit = wholeNumber(flags.perDay, "--per-day", 1, 200);
5391
+ if (flags.windowDays !== void 0) body.windowDays = wholeNumber(flags.windowDays, "--window-days", 1, 90);
5392
+ return Object.keys(body).length > 0 ? body : null;
5393
+ }
5394
+ function spendDownStatusLine(k) {
5395
+ const name = `${k.label ?? "(no label)"} ${k.keyHint ?? ""}`.trim();
5396
+ const expires = k.creditsExpireAt ? k.creditsExpireAt.slice(0, 10) : "not set";
5397
+ const model = k.model ?? "app default";
5398
+ const apps = k.projects.length > 0 ? k.projects.map((p3) => p3.name).join(", ") : "none";
5399
+ return `${name} [${k.scope}] ${k.state} \xB7 ${k.dispatchedToday}/${k.dailyLimit} today \xB7 credits expire ${expires} \xB7 model ${model} \xB7 pays for ${apps} \u2014 ${k.id}`;
5400
+ }
5401
+
5325
5402
  // src/commands/keys.ts
5326
5403
  function registerKeysCommands(program2) {
5327
5404
  const keys = program2.command("keys").description("Manage API key pool (BYOK)");
@@ -5501,6 +5578,59 @@ function registerKeysCommands(program2) {
5501
5578
  );
5502
5579
  console.log(` ${res.data.validation.detail}`);
5503
5580
  });
5581
+ keys.command("spend-down [key-id]").description(
5582
+ "Spend down expiring Cursor credits on open bugs (ADR 0028): show status, or set it for one key"
5583
+ ).option("--on", "Turn spend-down on (needs --expires once)").option("--off", "Turn spend-down off").option("--expires <date>", "When the credits expire, YYYY-MM-DD").option("--model <spec>", "Cursor model, e.g. 'grok-4.7?reasoning_effort=xhigh&context=500k' ('' = app default)").option("--per-day <n>", "Fixes per day across the apps this key pays for (1-200)").option("--window-days <n>", "Start this many days before the credits expire (1-90)").option("--json", "Output as JSON (alias for -o json)").action(async (keyId, opts) => {
5584
+ const config = loadConfig();
5585
+ if (!config.apiKey || !config.projectId) {
5586
+ console.error("Run `mushi login` first");
5587
+ process.exit(1);
5588
+ }
5589
+ let body;
5590
+ try {
5591
+ body = spendDownBody(opts);
5592
+ } catch (err) {
5593
+ console.error(`Error: ${err.message}`);
5594
+ process.exit(1);
5595
+ }
5596
+ if (keyId && body) {
5597
+ const res2 = await apiCall(
5598
+ `/v1/admin/byok/keys/${encodeURIComponent(keyId)}/spend-down`,
5599
+ config,
5600
+ { method: "PATCH", body: JSON.stringify(body) }
5601
+ );
5602
+ if (!res2.ok) {
5603
+ console.error(`Error: ${res2.error.message}`);
5604
+ process.exit(1);
5605
+ }
5606
+ console.log(
5607
+ `\u2713 Spend-down ${res2.data.enabled ? "on" : "off"}${res2.data.creditsExpireAt ? `, credits expire ${res2.data.creditsExpireAt.slice(0, 10)}` : ""} \u2014 id: ${res2.data.id}`
5608
+ );
5609
+ return;
5610
+ }
5611
+ if (keyId && !body) {
5612
+ console.error("Nothing to change: pass --on, --off, --expires, --model, --per-day or --window-days");
5613
+ process.exit(1);
5614
+ }
5615
+ const res = await apiCall(
5616
+ `/v1/admin/byok/spend-down?project_id=${encodeURIComponent(config.projectId)}`,
5617
+ config
5618
+ );
5619
+ if (!res.ok) {
5620
+ console.error(`Error: ${res.error.message}`);
5621
+ process.exit(1);
5622
+ }
5623
+ printResult(res.data, {
5624
+ json: opts.json,
5625
+ render(d) {
5626
+ if (d.keys.length === 0) {
5627
+ console.log("No Cursor key for this project. Add one with `mushi keys add --provider cursor`.");
5628
+ return;
5629
+ }
5630
+ for (const k of d.keys) console.log(spendDownStatusLine(k));
5631
+ }
5632
+ });
5633
+ });
5504
5634
  keys.command("remove <key-id>").description("Permanently remove one pooled key and its Vault secret").action(async (keyId) => {
5505
5635
  const config = loadConfig();
5506
5636
  if (!config.apiKey || !config.projectId) {
package/dist/init.js CHANGED
@@ -25,10 +25,10 @@ import {
25
25
  saveConfig,
26
26
  savedSdkKeyFor,
27
27
  trySaveKeyToKeychain
28
- } from "./chunk-BNO37ZO5.js";
28
+ } from "./chunk-7PSMATX3.js";
29
29
  import {
30
30
  MUSHI_CLI_VERSION
31
- } from "./chunk-QR6NCHXT.js";
31
+ } from "./chunk-MARVQVJC.js";
32
32
  import {
33
33
  FRAMEWORKS,
34
34
  detectFramework,
@@ -2058,7 +2058,7 @@ async function maybeOfferConnect(credentials, options, _consoleBase) {
2058
2058
  if (p.isCancel(answer) || !answer) return;
2059
2059
  const endpoint = resolveCloudEndpoint(options.endpoint);
2060
2060
  try {
2061
- const { runConnect } = await import("./connect-YKI7VZMP.js");
2061
+ const { runConnect } = await import("./connect-MINIBKHX.js");
2062
2062
  const saved = loadConfig();
2063
2063
  const result = await runConnect(
2064
2064
  {
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  MUSHI_CLI_VERSION,
4
4
  MUSHI_MCP_PIN_SPEC
5
- } from "./chunk-D7ZVFRYW.js";
5
+ } from "./chunk-XIB5NTHP.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.26.1";
17
+ declare const MUSHI_MCP_PIN_SPEC = "@mushi-mushi/mcp@0.28.0";
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-QR6NCHXT.js";
4
+ } from "./chunk-MARVQVJC.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.30.6",
3
+ "version": "0.32.0",
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
- #!/usr/bin/env node
2
-
3
- // src/version.ts
4
- var MUSHI_CLI_VERSION = true ? "0.30.6" : "0.0.0-dev";
5
- var MUSHI_MCP_PIN_SPEC = "@mushi-mushi/mcp@0.26.1";
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.6" : "0.0.0-dev";
3
- var MUSHI_MCP_PIN_SPEC = "@mushi-mushi/mcp@0.26.1";
4
-
5
- export {
6
- MUSHI_CLI_VERSION,
7
- MUSHI_MCP_PIN_SPEC
8
- };