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` |
|
|
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
|
-
|
|
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
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
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
|
|
643
|
-
const cmd =
|
|
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 =
|
|
685
|
+
const args = localPath ? extraArgs : ["-y", pkg, bin, ...extraArgs];
|
|
646
686
|
return new Promise((resolve) => {
|
|
647
|
-
const
|
|
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:
|
|
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
|
|
664
|
-
var CONFIG_DIR =
|
|
665
|
-
var JENKINS_PATH =
|
|
666
|
-
var LEGACY_PATH =
|
|
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 =
|
|
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 =
|
|
677
|
-
const jenkinsPath =
|
|
678
|
-
const legacyPath =
|
|
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
|
|
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(
|
|
795
|
+
await visit(path5.join(dir, e.name), depth + 1);
|
|
747
796
|
} else if (e.isFile() && match(e.name)) {
|
|
748
|
-
found.push(
|
|
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(
|
|
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: [
|
|
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: [
|
|
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: [
|
|
866
|
+
hints.push({ bundleId: bid, source: [path5.relative(cwd, f)] });
|
|
818
867
|
}
|
|
819
868
|
}
|
|
820
869
|
}
|
|
821
|
-
const pkgJson = await readIfExists(
|
|
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(
|
|
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
|
|
915
|
+
import path6 from "path";
|
|
867
916
|
import os3 from "os";
|
|
868
|
-
var CONFIG_DIR2 =
|
|
869
|
-
var CONFIG_PATH =
|
|
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
|
|
955
|
+
import path7 from "path";
|
|
907
956
|
import os4 from "os";
|
|
908
|
-
var CI_CONFIG_PATH =
|
|
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 =
|
|
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-
|
|
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 = {
|
|
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
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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-
|
|
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
|
|
package/package.json
CHANGED