mimi-seed 0.19.7 → 0.19.8

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
@@ -6,6 +6,13 @@ git log에서 릴리즈 노트를 생성하고, 출시 전 위험 요소를 자
6
6
 
7
7
  ## 빠른 시작
8
8
 
9
+ ```bash
10
+ npx mimi-seed check --local
11
+ ```
12
+
13
+ 로그인 없이 현재 저장소의 앱 식별자, Android Target API, Google Play Billing 지원 상태를 먼저 검사합니다.
14
+ 정책 결과에는 근거 파일과 공식 출처가 포함됩니다. 실제 스토어 상태까지 연결하려면 그다음 실행합니다.
15
+
9
16
  ```bash
10
17
  npx mimi-seed init
11
18
  ```
@@ -21,7 +28,7 @@ npx mimi-seed init
21
28
  | `mimi-seed status` | 연결 상태 + 등록 앱 목록 |
22
29
  | `mimi-seed auth` | 자격증명 개별 인증 — `login` / `appstore` / `playstore` / `bigquery` / `jenkins` / `ci` / `googleads` / `facebook` / `instagram` / `threads` |
23
30
  | `mimi-seed doctor` | 환경 진단 (토큰·Git·앱·CI 한 번에 체크) |
24
- | `mimi-seed check` | 출시 Readiness 점검 (점수 + 블로커) |
31
+ | `mimi-seed check` | 계정이 없으면 로컬 Release Doctor, 연결 후에는 원격 Readiness 점검 |
25
32
  | `mimi-seed notes` | AI 릴리즈 노트 생성 (git log → 3 톤 → 다국어 → 적용) |
26
33
  | `mimi-seed review` | AI 리뷰 답변 초안 생성 및 Play Store 게시 |
27
34
  | `mimi-seed deploy` | 앱 자동 배포 파이프라인 (CI 빌드 → 릴리즈 노트 → 스토어 적용) |
@@ -76,13 +83,23 @@ mimi-seed notes --locale ko,en-US,ja
76
83
 
77
84
  ## mimi-seed check
78
85
 
79
- 출시 Readiness 점수와 블로커를 확인합니다.
86
+ 계정 연결 전에는 현재 저장소를 읽기 전용으로 검사하고, 연결 후에는 원격 Readiness 점수와 블로커를
87
+ 확인합니다. `--local`은 연결 상태와 관계없이 로컬 검사만 실행합니다.
80
88
 
81
89
  ```bash
82
90
  mimi-seed check
83
91
 
92
+ # 로그인 없이 저장소만 검사
93
+ mimi-seed check --local
94
+
95
+ # 모노레포의 특정 앱 검사
96
+ mimi-seed check --local --path apps/mobile
97
+
84
98
  # CI에서 블로커 있으면 exit 1
85
- mimi-seed check --fail-on-blocker
99
+ mimi-seed check --local --fail-on-blocker
100
+
101
+ # JSON 보고서
102
+ mimi-seed check --local --json
86
103
  ```
87
104
 
88
105
  ### 옵션
@@ -90,6 +107,9 @@ mimi-seed check --fail-on-blocker
90
107
  | 옵션 | 설명 |
91
108
  |------|------|
92
109
  | `--app <id>` | 앱 ID 지정 (기본: 첫 번째 등록 앱) |
110
+ | `--local` | 로그인 없이 현재 저장소만 검사 |
111
+ | `--path <dir>` | 로컬 검사 대상 경로 (기본: 현재 폴더) |
112
+ | `--json` | 로컬 검사 결과를 JSON으로 출력 |
93
113
  | `--fail-on-blocker` | 블로커 존재 시 exit 1 (CI/CD용) |
94
114
 
95
115
  ---
@@ -631,22 +631,71 @@ function planSetup(detected, opts = {}) {
631
631
 
632
632
  // src/mcp-bin.ts
633
633
  import { spawn, spawnSync } from "child_process";
634
+ import { existsSync, readFileSync } from "fs";
635
+ import path3 from "path";
634
636
  var MCP_PKG = "@yoonion/mimi-seed-mcp";
635
- function resolveOnPath(bin) {
636
- if (process.env.MIMI_SEED_FORCE_NPX) return false;
637
- const probe = process.platform === "win32" ? "where" : "which";
638
- const r = spawnSync(probe, [bin], { stdio: "ignore", shell: true });
639
- return r.status === 0;
637
+ function resolveOnPath(bin, honorForceNpx = true) {
638
+ if (honorForceNpx && process.env.MIMI_SEED_FORCE_NPX) return null;
639
+ if (process.platform === "win32") {
640
+ const pathKey = Object.keys(process.env).find((key) => key.toLowerCase() === "path");
641
+ const directories = (pathKey ? process.env[pathKey] ?? "" : "").split(path3.delimiter).filter(Boolean);
642
+ const names = bin.toLowerCase().endsWith(".cmd") ? [bin] : [`${bin}.cmd`, bin];
643
+ for (const directory of directories) {
644
+ for (const name of names) {
645
+ const candidate = path3.join(directory.replace(/^"|"$/g, ""), name);
646
+ if (existsSync(candidate)) return candidate;
647
+ }
648
+ }
649
+ return null;
650
+ }
651
+ const probe = "which";
652
+ const result = spawnSync(probe, [bin], { encoding: "utf8", shell: false });
653
+ if (result.status !== 0) return null;
654
+ const candidates = result.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
655
+ return candidates[0] ?? null;
656
+ }
657
+ function resolveWindowsShimTarget(shimPath, source) {
658
+ const matches = [...source.matchAll(/["']([^"']+\.js)["']\s+%\*/gi)];
659
+ const raw = matches.at(-1)?.[1];
660
+ if (!raw) return null;
661
+ return path3.win32.normalize(raw.replace(/%~?dp0%?/gi, `${path3.win32.dirname(shimPath)}${path3.win32.sep}`));
662
+ }
663
+ function npxCliPath(shimPath) {
664
+ const candidates = [
665
+ shimPath ? path3.join(path3.dirname(shimPath), "node_modules", "npm", "bin", "npx-cli.js") : "",
666
+ path3.join(path3.dirname(process.execPath), "node_modules", "npm", "bin", "npx-cli.js"),
667
+ process.env.npm_execpath ? path3.join(path3.dirname(process.env.npm_execpath), "npx-cli.js") : ""
668
+ ];
669
+ return candidates.find((candidate) => candidate && existsSync(candidate)) ?? null;
670
+ }
671
+ function windowsNodeTarget(command, shimPath) {
672
+ if (command === "npx") return npxCliPath(shimPath);
673
+ if (!shimPath) return null;
674
+ try {
675
+ const target = resolveWindowsShimTarget(shimPath, readFileSync(shimPath, "utf8"));
676
+ return target && existsSync(target) ? target : null;
677
+ } catch {
678
+ return null;
679
+ }
640
680
  }
641
681
  async function runMcpBin(bin, extraArgs = []) {
642
- const local = resolveOnPath(bin);
643
- const cmd = local ? bin : "npx";
682
+ const localPath = resolveOnPath(bin);
683
+ const cmd = localPath ? bin : "npx";
644
684
  const pkg = process.env.MIMI_SEED_FORCE_NPX ? `${MCP_PKG}@latest` : MCP_PKG;
645
- const args = local ? extraArgs : ["-y", pkg, bin, ...extraArgs];
685
+ const args = localPath ? extraArgs : ["-y", pkg, bin, ...extraArgs];
646
686
  return new Promise((resolve) => {
647
- const child = spawn(cmd, args, {
687
+ const shimPath = process.platform === "win32" ? localPath ?? resolveOnPath(cmd, false) : null;
688
+ const nodeTarget = process.platform === "win32" ? windowsNodeTarget(cmd, shimPath) : null;
689
+ if (process.platform === "win32" && !nodeTarget) {
690
+ process.stderr.write(t().auth.npxFailed(cmd, `could not resolve the JavaScript entrypoint for ${shimPath ?? cmd}`));
691
+ resolve(1);
692
+ return;
693
+ }
694
+ const executable = process.platform === "win32" ? process.execPath : localPath ?? cmd;
695
+ const childArgs = nodeTarget ? [nodeTarget, ...args] : args;
696
+ const child = spawn(executable, childArgs, {
648
697
  stdio: "inherit",
649
- shell: true,
698
+ shell: false,
650
699
  env: { ...process.env, MIMI_SEED_LANG: resolveLang() }
651
700
  });
652
701
  child.on("error", (e) => {
@@ -660,22 +709,22 @@ async function runMcpBin(bin, extraArgs = []) {
660
709
  // src/jenkins-config.ts
661
710
  import fs3 from "fs";
662
711
  import os2 from "os";
663
- import path3 from "path";
664
- var CONFIG_DIR = path3.join(os2.homedir(), ".mimi-seed");
665
- var JENKINS_PATH = path3.join(CONFIG_DIR, "jenkins.json");
666
- var LEGACY_PATH = path3.join(CONFIG_DIR, "config.json");
712
+ import path4 from "path";
713
+ var CONFIG_DIR = path4.join(os2.homedir(), ".mimi-seed");
714
+ var JENKINS_PATH = path4.join(CONFIG_DIR, "jenkins.json");
715
+ var LEGACY_PATH = path4.join(CONFIG_DIR, "config.json");
667
716
  function loadJenkinsConfig(home = os2.homedir()) {
668
717
  try {
669
- const p = path3.join(home, ".mimi-seed", "jenkins.json");
718
+ const p = path4.join(home, ".mimi-seed", "jenkins.json");
670
719
  return JSON.parse(fs3.readFileSync(p, "utf-8"));
671
720
  } catch {
672
721
  return null;
673
722
  }
674
723
  }
675
724
  function migrateLegacyJenkins(home = os2.homedir()) {
676
- const dir = path3.join(home, ".mimi-seed");
677
- const jenkinsPath = path3.join(dir, "jenkins.json");
678
- const legacyPath = path3.join(dir, "config.json");
725
+ const dir = path4.join(home, ".mimi-seed");
726
+ const jenkinsPath = path4.join(dir, "jenkins.json");
727
+ const legacyPath = path4.join(dir, "config.json");
679
728
  if (fs3.existsSync(jenkinsPath)) return false;
680
729
  let legacy;
681
730
  try {
@@ -704,7 +753,7 @@ function migrateLegacyJenkins(home = os2.homedir()) {
704
753
 
705
754
  // src/detect.ts
706
755
  import fs4 from "fs/promises";
707
- import path4 from "path";
756
+ import path5 from "path";
708
757
  async function readIfExists(p) {
709
758
  try {
710
759
  return await fs4.readFile(p, "utf8");
@@ -743,9 +792,9 @@ async function walk(root, match, maxDepth = 5) {
743
792
  for (const e of entries) {
744
793
  if (e.isDirectory()) {
745
794
  if (skipDirs.has(e.name)) continue;
746
- await visit(path4.join(dir, e.name), depth + 1);
795
+ await visit(path5.join(dir, e.name), depth + 1);
747
796
  } else if (e.isFile() && match(e.name)) {
748
- found.push(path4.join(dir, e.name));
797
+ found.push(path5.join(dir, e.name));
749
798
  }
750
799
  }
751
800
  }
@@ -755,7 +804,7 @@ async function walk(root, match, maxDepth = 5) {
755
804
  async function detectHints(cwd) {
756
805
  const hints = [];
757
806
  for (const fname of ["app.json", "app.config.json"]) {
758
- const txt = await readIfExists(path4.join(cwd, fname));
807
+ const txt = await readIfExists(path5.join(cwd, fname));
759
808
  if (!txt) continue;
760
809
  try {
761
810
  const json = JSON.parse(txt);
@@ -786,7 +835,7 @@ async function detectHints(cwd) {
786
835
  if (m?.[1]) {
787
836
  const pkg = m[1];
788
837
  if (!hints.some((h) => h.packageName === pkg)) {
789
- hints.push({ packageName: pkg, source: [path4.relative(cwd, f)] });
838
+ hints.push({ packageName: pkg, source: [path5.relative(cwd, f)] });
790
839
  }
791
840
  }
792
841
  }
@@ -801,7 +850,7 @@ async function detectHints(cwd) {
801
850
  const bid = m[1];
802
851
  if (bid.includes("$(PRODUCT_BUNDLE_IDENTIFIER)")) continue;
803
852
  if (!hints.some((h) => h.bundleId === bid)) {
804
- hints.push({ bundleId: bid, source: [path4.relative(cwd, f)] });
853
+ hints.push({ bundleId: bid, source: [path5.relative(cwd, f)] });
805
854
  }
806
855
  }
807
856
  }
@@ -814,11 +863,11 @@ async function detectHints(cwd) {
814
863
  const bid = m[1].trim().replace(/^["']|["']$/g, "");
815
864
  if (!bid || bid.includes("$")) continue;
816
865
  if (!hints.some((h) => h.bundleId === bid)) {
817
- hints.push({ bundleId: bid, source: [path4.relative(cwd, f)] });
866
+ hints.push({ bundleId: bid, source: [path5.relative(cwd, f)] });
818
867
  }
819
868
  }
820
869
  }
821
- const pkgJson = await readIfExists(path4.join(cwd, "package.json"));
870
+ const pkgJson = await readIfExists(path5.join(cwd, "package.json"));
822
871
  if (pkgJson) {
823
872
  try {
824
873
  const json = JSON.parse(pkgJson);
@@ -854,7 +903,7 @@ async function detectHints(cwd) {
854
903
  return merged.filter((h) => h.packageName || h.bundleId);
855
904
  }
856
905
  async function hasAnyProjectSignal(cwd) {
857
- return await pathExists(path4.join(cwd, "package.json")) || await pathExists(path4.join(cwd, "app.json")) || await pathExists(path4.join(cwd, "android")) || await pathExists(path4.join(cwd, "ios"));
906
+ return await pathExists(path5.join(cwd, "package.json")) || await pathExists(path5.join(cwd, "app.json")) || await pathExists(path5.join(cwd, "android")) || await pathExists(path5.join(cwd, "ios"));
858
907
  }
859
908
 
860
909
  // src/deploy.ts
@@ -863,10 +912,10 @@ import * as readline from "readline";
863
912
 
864
913
  // src/config.ts
865
914
  import fs5 from "fs/promises";
866
- import path5 from "path";
915
+ import path6 from "path";
867
916
  import os3 from "os";
868
- var CONFIG_DIR2 = path5.join(os3.homedir(), ".mimi-seed");
869
- var CONFIG_PATH = path5.join(CONFIG_DIR2, "config.json");
917
+ var CONFIG_DIR2 = path6.join(os3.homedir(), ".mimi-seed");
918
+ var CONFIG_PATH = path6.join(CONFIG_DIR2, "config.json");
870
919
  async function readConfig() {
871
920
  try {
872
921
  const txt = await fs5.readFile(CONFIG_PATH, "utf8");
@@ -903,9 +952,9 @@ async function getEffectiveConfig() {
903
952
 
904
953
  // src/ci-providers.ts
905
954
  import fs6 from "fs";
906
- import path6 from "path";
955
+ import path7 from "path";
907
956
  import os4 from "os";
908
- var CI_CONFIG_PATH = path6.join(os4.homedir(), ".mimi-seed", "ci.json");
957
+ var CI_CONFIG_PATH = path7.join(os4.homedir(), ".mimi-seed", "ci.json");
909
958
  var M = catalog(
910
959
  {
911
960
  badToken: (provider, status) => `${provider} ${status} \u2014 \uD1A0\uD070\uC774 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC544`,
@@ -937,7 +986,7 @@ function normalizeHost(host) {
937
986
  return withScheme.replace(/\/+$/, "");
938
987
  }
939
988
  function saveCiProviderConfig(cfg) {
940
- const dir = path6.dirname(CI_CONFIG_PATH);
989
+ const dir = path7.dirname(CI_CONFIG_PATH);
941
990
  if (!fs6.existsSync(dir)) {
942
991
  fs6.mkdirSync(dir, { recursive: true, mode: 448 });
943
992
  }
package/dist/index.js CHANGED
@@ -22,7 +22,7 @@ import {
22
22
  runMcpBin,
23
23
  tryCredById,
24
24
  writeConfig
25
- } from "./chunk-OXYELVMI.js";
25
+ } from "./chunk-KY6LI3CK.js";
26
26
  import {
27
27
  catalog,
28
28
  isLang,
@@ -325,10 +325,22 @@ Score: ${bar}
325
325
  }
326
326
  );
327
327
  function parseArgs(argv) {
328
- const args = { failOnBlocker: false };
328
+ const args = {
329
+ projectPath: process.cwd(),
330
+ projectPathExplicit: false,
331
+ failOnBlocker: false,
332
+ local: false,
333
+ json: false
334
+ };
329
335
  for (let i = 0; i < argv.length; i++) {
330
336
  if (argv[i] === "--app" && argv[i + 1]) args.appId = argv[++i];
337
+ if (argv[i] === "--path" && argv[i + 1]) {
338
+ args.projectPath = argv[++i];
339
+ args.projectPathExplicit = true;
340
+ }
331
341
  if (argv[i] === "--fail-on-blocker") args.failOnBlocker = true;
342
+ if (argv[i] === "--local") args.local = true;
343
+ if (argv[i] === "--json") args.json = true;
332
344
  }
333
345
  return args;
334
346
  }
@@ -347,9 +359,14 @@ var MODULE_LABELS = {
347
359
  async function cmdCheck(argv) {
348
360
  const args = parseArgs(argv);
349
361
  const cfg = await getEffectiveConfig();
350
- if (!cfg) {
351
- process.stdout.write(kleur2.red(M2().noAccount));
352
- process.exit(1);
362
+ const localRequested = args.local || args.json || args.projectPathExplicit;
363
+ if (localRequested || !cfg) {
364
+ const doctorArgs = [args.projectPath];
365
+ if (args.json) doctorArgs.push("--json");
366
+ if (args.failOnBlocker) doctorArgs.push("--fail-on-blocker");
367
+ const exitCode = await runMcpBin("mimi-seed-release-doctor", doctorArgs);
368
+ if (exitCode !== 0) process.exit(exitCode);
369
+ return;
353
370
  }
354
371
  process.stdout.write(kleur2.bold(M2().title));
355
372
  const appsResult = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
@@ -1068,7 +1085,7 @@ async function cmdAuth(args) {
1068
1085
  if (sub === "threads") return void exitWith(await runMcpBin("mimi-seed-social-auth", ["threads", ...rest]));
1069
1086
  if (sub === "tiktok") return void exitWith(await runMcpBin("mimi-seed-tiktok-business-auth", rest));
1070
1087
  if (sub === "ci") {
1071
- const { cmdSetup: cmdSetup2 } = await import("./setup-PEHGP5MD.js");
1088
+ const { cmdSetup: cmdSetup2 } = await import("./setup-RI2NDOZR.js");
1072
1089
  await cmdSetup2(["--only", "github,gitlab", "--reconnect", "github,gitlab"]);
1073
1090
  return;
1074
1091
  }
@@ -1613,6 +1630,9 @@ ${kleur9.dim("setup \uB9C8\uBC95\uC0AC\uAC00 \uCCAB \uC2E4\uD589 \uB54C \uBB3C\u
1613
1630
 
1614
1631
  \uC635\uC158:
1615
1632
  --app <id> \uC571 ID \uC9C0\uC815
1633
+ --local \uB85C\uADF8\uC778 \uC5C6\uC774 \uD604\uC7AC \uC800\uC7A5\uC18C\uB9CC \uAC80\uC0AC
1634
+ --path <dir> \uB85C\uCEEC \uAC80\uC0AC \uB300\uC0C1 \uACBD\uB85C (\uAE30\uBCF8: \uD604\uC7AC \uD3F4\uB354)
1635
+ --json \uB85C\uCEEC \uAC80\uC0AC \uACB0\uACFC\uB97C JSON\uC73C\uB85C \uCD9C\uB825
1616
1636
  --fail-on-blocker \uBE14\uB85C\uCEE4 \uC788\uC73C\uBA74 exit 1 (CI\uC6A9)`,
1617
1637
  review: `${kleur9.bold("mimi-seed review")} \u2014 \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
1618
1638
 
@@ -1779,6 +1799,9 @@ Options:
1779
1799
 
1780
1800
  Options:
1781
1801
  --app <id> app ID
1802
+ --local inspect the current repository without signing in
1803
+ --path <dir> local project path (default: current directory)
1804
+ --json print the local report as JSON
1782
1805
  --fail-on-blocker exit 1 if a blocker is found (for CI)`,
1783
1806
  review: `${kleur9.bold("mimi-seed review")} \u2014 draft a review reply with AI and post it to the Play Store
1784
1807
 
@@ -3,7 +3,7 @@ import {
3
3
  cmdSetup,
4
4
  parseSetupArgs,
5
5
  resolveMode
6
- } from "./chunk-OXYELVMI.js";
6
+ } from "./chunk-KY6LI3CK.js";
7
7
  import "./chunk-ZGMPOJRY.js";
8
8
  export {
9
9
  cmdSetup,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mimi-seed",
3
- "version": "0.19.7",
3
+ "version": "0.19.8",
4
4
  "description": "Mimi Seed CLI \u2014 Claude Code\uc640 Codex\uc5d0\uc11c \uc571 \ucd9c\uc2dc \uc6b4\uc601\uc744 \uad00\ub9ac\ud569\ub2c8\ub2e4.",
5
5
  "bin": {
6
6
  "mimi-seed": "dist/index.js"