@mushi-mushi/cli 0.11.2 → 0.12.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.
@@ -0,0 +1,6 @@
1
+ // src/version.ts
2
+ var MUSHI_CLI_VERSION = true ? "0.12.0" : "0.0.0-dev";
3
+
4
+ export {
5
+ MUSHI_CLI_VERSION
6
+ };
package/dist/index.js CHANGED
@@ -595,7 +595,7 @@ function getFrameworkFromPkg(pkg) {
595
595
  }
596
596
 
597
597
  // src/version.ts
598
- var MUSHI_CLI_VERSION = true ? "0.11.2" : "0.0.0-dev";
598
+ var MUSHI_CLI_VERSION = true ? "0.12.0" : "0.0.0-dev";
599
599
 
600
600
  // src/init.ts
601
601
  var ENV_FILES = [".env.local", ".env"];
@@ -2756,4 +2756,205 @@ fixes.command("tail").description(
2756
2756
  }
2757
2757
  }
2758
2758
  });
2759
+ var stories = program.command("stories").description("TDD story mapping and test generation");
2760
+ stories.command("map").description("Crawl a live app URL and automatically discover user stories (writes inventory proposal)").requiredOption("--url <url>", "Live app URL to crawl (e.g. https://your-app.vercel.app)").option("--max-pages <n>", "Max pages to crawl", "20").option("--provider <p>", "Crawl provider: firecrawl (default) or browserbase", "firecrawl").option("--cursor-refine", "Open a Cursor Cloud PR to refine the draft against repo code").option("--wait", "Wait for the crawl to complete and print results").action(async (opts) => {
2761
+ const config = loadConfig();
2762
+ if (!config.apiKey || !config.projectId) {
2763
+ console.error("Run `mushi login` and `mushi init` first");
2764
+ process.exit(1);
2765
+ }
2766
+ const res = await apiCall(
2767
+ `/v1/admin/inventory/${config.projectId}/map-from-live`,
2768
+ config,
2769
+ {
2770
+ method: "POST",
2771
+ body: JSON.stringify({
2772
+ base_url: opts.url,
2773
+ max_pages: parseInt(opts.maxPages, 10),
2774
+ provider: opts.provider,
2775
+ cursor_cloud_refine: opts.cursorRefine ?? false
2776
+ })
2777
+ }
2778
+ );
2779
+ if (!res.ok) {
2780
+ console.error(`Error: ${res.error.message}`);
2781
+ process.exit(1);
2782
+ }
2783
+ console.log(`\u2713 Crawl started \u2014 run id: ${res.data.runId}`);
2784
+ console.log(` Crawling ${opts.url} with ${opts.provider}\u2026`);
2785
+ if (opts.wait) {
2786
+ console.log(" Polling for results\u2026");
2787
+ for (let i = 0; i < 40; i++) {
2788
+ await new Promise((r) => setTimeout(r, 5e3));
2789
+ const runsRes = await apiCall(
2790
+ `/v1/admin/inventory/${config.projectId}/map-runs`,
2791
+ config
2792
+ );
2793
+ if (!runsRes.ok) break;
2794
+ const run = runsRes.data.runs.find((r) => r.id === res.data.runId);
2795
+ if (!run) break;
2796
+ if (run.status === "completed") {
2797
+ console.log(`
2798
+ \u2713 Done! ${run.pages_crawled ?? 0} pages crawled.`);
2799
+ if (run.proposal_id) console.log(` Proposal id: ${run.proposal_id}`);
2800
+ console.log(` Review in the console: Inventory \u2192 Discovery \u2192 Past proposals`);
2801
+ break;
2802
+ }
2803
+ if (run.status === "failed") {
2804
+ console.error(`
2805
+ \u2717 Crawl failed: ${run.error_message ?? "unknown"}`);
2806
+ process.exit(1);
2807
+ }
2808
+ process.stdout.write(".");
2809
+ }
2810
+ }
2811
+ });
2812
+ var tdd = program.command("tdd").description("TDD test generation and management");
2813
+ tdd.command("gen <storyId>").description("Generate a Playwright TDD test from an inventory user story id").option("--mode <m>", "Gate mode: auto (run immediately) | review (needs approval) | approve (manual)", "review").option("--no-pr", "Skip opening a GitHub PR").action(async (storyId, opts) => {
2814
+ const config = loadConfig();
2815
+ if (!config.apiKey || !config.projectId) {
2816
+ console.error("Run `mushi login` first");
2817
+ process.exit(1);
2818
+ }
2819
+ console.log(`Generating TDD test for story: ${storyId}\u2026`);
2820
+ const res = await apiCall(
2821
+ `/v1/admin/inventory/${config.projectId}/stories/${storyId}/generate-test`,
2822
+ config,
2823
+ { method: "POST", body: JSON.stringify({ automation_mode: opts.mode, open_pr: opts.pr }) }
2824
+ );
2825
+ if (!res.ok) {
2826
+ console.error(`Error: ${res.error.message}`);
2827
+ process.exit(1);
2828
+ }
2829
+ console.log(`\u2713 Test generated \u2014 qa_story id: ${res.data.qaStoryId}`);
2830
+ console.log(` Approval status: ${res.data.approvalStatus}`);
2831
+ if (res.data.prUrl) console.log(` PR: ${res.data.prUrl}`);
2832
+ if (res.data.needsHumanReview) console.log(` \u26A0 Human review recommended \u2014 some selectors or flows are uncertain.`);
2833
+ });
2834
+ tdd.command("improve").description("Run PDCA auto-improve on recently failed QA tests").action(async () => {
2835
+ const config = loadConfig();
2836
+ if (!config.apiKey || !config.projectId) {
2837
+ console.error("Run `mushi login` first");
2838
+ process.exit(1);
2839
+ }
2840
+ console.log("Running PDCA QA story improver\u2026");
2841
+ const res = await apiCall(
2842
+ "/v1/admin/pdca/improve-qa-stories",
2843
+ config,
2844
+ { method: "POST", body: JSON.stringify({ project_id: config.projectId }) }
2845
+ );
2846
+ if (!res.ok) {
2847
+ console.error(`Error: ${res.error.message}`);
2848
+ process.exit(1);
2849
+ }
2850
+ console.log(`\u2713 Improved ${res.data.improved} QA stories.`);
2851
+ });
2852
+ tdd.command("run <qaStoryId>").description("Trigger a manual run for a QA story").action(async (qaStoryId) => {
2853
+ const config = loadConfig();
2854
+ if (!config.apiKey || !config.projectId) {
2855
+ console.error("Run `mushi login` first");
2856
+ process.exit(1);
2857
+ }
2858
+ const res = await apiCall(
2859
+ `/v1/admin/projects/${config.projectId}/qa-stories/${qaStoryId}/run`,
2860
+ config,
2861
+ { method: "POST" }
2862
+ );
2863
+ if (!res.ok) {
2864
+ console.error(`Error: ${res.error.message}`);
2865
+ process.exit(1);
2866
+ }
2867
+ console.log(`\u2713 Run queued \u2014 id: ${res.data.runId}`);
2868
+ });
2869
+ tdd.command("pending").description("List QA tests pending review").action(async () => {
2870
+ const config = loadConfig();
2871
+ if (!config.apiKey || !config.projectId) {
2872
+ console.error("Run `mushi login` first");
2873
+ process.exit(1);
2874
+ }
2875
+ const res = await apiCall(
2876
+ `/v1/admin/inventory/${config.projectId}/stories/pending-review`,
2877
+ config
2878
+ );
2879
+ if (!res.ok) {
2880
+ console.error(`Error: ${res.error.message}`);
2881
+ process.exit(1);
2882
+ }
2883
+ if (res.data.stories.length === 0) {
2884
+ console.log("No stories pending review.");
2885
+ return;
2886
+ }
2887
+ console.log(`${res.data.stories.length} stories pending review:
2888
+ `);
2889
+ for (const s of res.data.stories) {
2890
+ console.log(` ${s.id} ${s.name}${s.origin_story_node_id ? ` (story: ${s.origin_story_node_id})` : ""}`);
2891
+ if (s.generated_pr_url) console.log(` PR: ${s.generated_pr_url}`);
2892
+ }
2893
+ console.log(`
2894
+ Approve: mushi tdd approve <id>`);
2895
+ });
2896
+ tdd.command("approve <qaStoryId>").description("Approve a pending QA story (enables it in the schedule)").option("--reject", "Reject instead of approve").action(async (qaStoryId, opts) => {
2897
+ const config = loadConfig();
2898
+ if (!config.apiKey || !config.projectId) {
2899
+ console.error("Run `mushi login` first");
2900
+ process.exit(1);
2901
+ }
2902
+ const status = opts.reject ? "rejected" : "approved";
2903
+ const res = await apiCall(
2904
+ `/v1/admin/inventory/${config.projectId}/stories/${qaStoryId}/approval`,
2905
+ config,
2906
+ { method: "PATCH", body: JSON.stringify({ status }) }
2907
+ );
2908
+ if (!res.ok) {
2909
+ console.error(`Error: ${res.error.message}`);
2910
+ process.exit(1);
2911
+ }
2912
+ console.log(`\u2713 Story ${status}.`);
2913
+ });
2914
+ var keys = program.command("keys").description("Manage API key pool (BYOK)");
2915
+ keys.command("list").description("List all API keys in the pool with their status").action(async () => {
2916
+ const config = loadConfig();
2917
+ if (!config.apiKey || !config.projectId) {
2918
+ console.error("Run `mushi login` first");
2919
+ process.exit(1);
2920
+ }
2921
+ const res = await apiCall(
2922
+ `/v1/admin/byok/keys?project_id=${encodeURIComponent(config.projectId)}`,
2923
+ config
2924
+ );
2925
+ if (!res.ok) {
2926
+ console.error(`Error: ${res.error.message}`);
2927
+ process.exit(1);
2928
+ }
2929
+ if (res.data.keys.length === 0) {
2930
+ console.log("No keys configured.");
2931
+ return;
2932
+ }
2933
+ for (const k of res.data.keys) {
2934
+ const cooldown = k.cooldown_until && new Date(k.cooldown_until) > /* @__PURE__ */ new Date() ? ` [cooldown until ${new Date(k.cooldown_until).toLocaleTimeString()}]` : "";
2935
+ console.log(`${k.provider_slug.padEnd(14)} [${k.status}] p=${k.priority} ${k.label ?? "(no label)"}${cooldown} \u2014 ${k.id}`);
2936
+ }
2937
+ });
2938
+ keys.command("add").description("Add a new API key to the pool").requiredOption("--provider <p>", "Provider: anthropic, openai, firecrawl, browserbase, cursor").option("--key <k>", "The API key value (prefer the MUSHI_BYOK_KEY env var to keep it out of shell history)").option("--label <l>", "Human-readable label").option("--priority <n>", "Priority (lower = higher priority)", "100").action(async (opts) => {
2939
+ const config = loadConfig();
2940
+ if (!config.apiKey || !config.projectId) {
2941
+ console.error("Run `mushi login` first");
2942
+ process.exit(1);
2943
+ }
2944
+ const key = process.env.MUSHI_BYOK_KEY ?? opts.key;
2945
+ if (!key) {
2946
+ console.error("Provide the key via the MUSHI_BYOK_KEY env var (recommended) or --key <value>.");
2947
+ process.exit(1);
2948
+ }
2949
+ const res = await apiCall(
2950
+ "/v1/admin/byok/keys",
2951
+ config,
2952
+ { method: "POST", body: JSON.stringify({ project_id: config.projectId, provider_slug: opts.provider, key, label: opts.label, priority: parseInt(opts.priority, 10) }) }
2953
+ );
2954
+ if (!res.ok) {
2955
+ console.error(`Error: ${res.error.message}`);
2956
+ process.exit(1);
2957
+ }
2958
+ console.log(`\u2713 Key added \u2014 id: ${res.data.id}`);
2959
+ });
2759
2960
  program.parse();
package/dist/init.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-NYPX5KXR.js";
9
9
  import {
10
10
  MUSHI_CLI_VERSION
11
- } from "./chunk-GPF6KFBM.js";
11
+ } from "./chunk-F43X732E.js";
12
12
 
13
13
  // src/init.ts
14
14
  import * as p from "@clack/prompts";
package/dist/version.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  MUSHI_CLI_VERSION
3
- } from "./chunk-GPF6KFBM.js";
3
+ } from "./chunk-F43X732E.js";
4
4
  export {
5
5
  MUSHI_CLI_VERSION
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mushi-mushi/cli",
3
- "version": "0.11.2",
3
+ "version": "0.12.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": {
@@ -1,6 +0,0 @@
1
- // src/version.ts
2
- var MUSHI_CLI_VERSION = true ? "0.11.2" : "0.0.0-dev";
3
-
4
- export {
5
- MUSHI_CLI_VERSION
6
- };