mimi-seed 0.4.2 → 0.5.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/dist/index.js CHANGED
@@ -1,170 +1,32 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  mcpCall
4
- } from "./chunk-Q5YGYAFK.js";
4
+ } from "./chunk-DW3LOLPJ.js";
5
+ import {
6
+ CONFIG_LOCATION,
7
+ CREDENTIALS,
8
+ MCP_PKG,
9
+ cmdDeploy,
10
+ cmdSetup,
11
+ deleteConfig,
12
+ detectAll,
13
+ detectHints,
14
+ getEffectiveConfig,
15
+ hasAnyProjectSignal,
16
+ isSatisfied,
17
+ migrateLegacyJenkins,
18
+ runMcpBin,
19
+ tryCredById,
20
+ writeConfig
21
+ } from "./chunk-T757WVSG.js";
5
22
 
6
23
  // src/index.ts
7
- import os7 from "os";
8
- import fs10 from "fs";
9
- import path10 from "path";
10
- import kleur9 from "kleur";
24
+ import os3 from "os";
25
+ import fs5 from "fs";
26
+ import path5 from "path";
27
+ import kleur8 from "kleur";
11
28
  import open from "open";
12
29
 
13
- // src/detect.ts
14
- import fs from "fs/promises";
15
- import path from "path";
16
- async function readIfExists(p) {
17
- try {
18
- return await fs.readFile(p, "utf8");
19
- } catch {
20
- return null;
21
- }
22
- }
23
- async function pathExists(p) {
24
- try {
25
- await fs.access(p);
26
- return true;
27
- } catch {
28
- return false;
29
- }
30
- }
31
- async function walk(root, match, maxDepth = 5) {
32
- const found = [];
33
- const skipDirs = /* @__PURE__ */ new Set([
34
- "node_modules",
35
- ".git",
36
- "build",
37
- "dist",
38
- ".next",
39
- ".expo",
40
- "Pods",
41
- "DerivedData"
42
- ]);
43
- async function visit(dir, depth) {
44
- if (depth > maxDepth) return;
45
- let entries;
46
- try {
47
- entries = await fs.readdir(dir, { withFileTypes: true });
48
- } catch {
49
- return;
50
- }
51
- for (const e of entries) {
52
- if (e.isDirectory()) {
53
- if (skipDirs.has(e.name)) continue;
54
- await visit(path.join(dir, e.name), depth + 1);
55
- } else if (e.isFile() && match(e.name)) {
56
- found.push(path.join(dir, e.name));
57
- }
58
- }
59
- }
60
- await visit(root, 0);
61
- return found;
62
- }
63
- async function detectHints(cwd) {
64
- const hints = [];
65
- for (const fname of ["app.json", "app.config.json"]) {
66
- const txt = await readIfExists(path.join(cwd, fname));
67
- if (!txt) continue;
68
- try {
69
- const json = JSON.parse(txt);
70
- const expo = json.expo ?? json;
71
- const pkg = expo?.android?.package;
72
- const bid = expo?.ios?.bundleIdentifier;
73
- const name = expo?.name;
74
- if (pkg || bid) {
75
- hints.push({
76
- name,
77
- packageName: typeof pkg === "string" ? pkg : void 0,
78
- bundleId: typeof bid === "string" ? bid : void 0,
79
- source: [fname]
80
- });
81
- }
82
- } catch {
83
- }
84
- }
85
- const gradleFiles = await walk(
86
- cwd,
87
- (n) => n === "build.gradle" || n === "build.gradle.kts",
88
- 4
89
- );
90
- for (const f of gradleFiles) {
91
- const txt = await readIfExists(f);
92
- if (!txt) continue;
93
- const m = txt.match(/applicationId[\s=]+["']([^"']+)["']/);
94
- if (m?.[1]) {
95
- const pkg = m[1];
96
- if (!hints.some((h) => h.packageName === pkg)) {
97
- hints.push({ packageName: pkg, source: [path.relative(cwd, f)] });
98
- }
99
- }
100
- }
101
- const plistFiles = await walk(cwd, (n) => n === "Info.plist", 5);
102
- for (const f of plistFiles) {
103
- const txt = await readIfExists(f);
104
- if (!txt) continue;
105
- const m = txt.match(
106
- /<key>CFBundleIdentifier<\/key>\s*<string>([^<]+)<\/string>/
107
- );
108
- if (m?.[1]) {
109
- let bid = m[1];
110
- if (bid.includes("$(PRODUCT_BUNDLE_IDENTIFIER)")) continue;
111
- if (!hints.some((h) => h.bundleId === bid)) {
112
- hints.push({ bundleId: bid, source: [path.relative(cwd, f)] });
113
- }
114
- }
115
- }
116
- const pbxFiles = await walk(cwd, (n) => n === "project.pbxproj", 5);
117
- for (const f of pbxFiles) {
118
- const txt = await readIfExists(f);
119
- if (!txt) continue;
120
- const matches = [...txt.matchAll(/PRODUCT_BUNDLE_IDENTIFIER = ([^;]+);/g)];
121
- for (const m of matches) {
122
- const bid = m[1].trim().replace(/^["']|["']$/g, "");
123
- if (!bid || bid.includes("$")) continue;
124
- if (!hints.some((h) => h.bundleId === bid)) {
125
- hints.push({ bundleId: bid, source: [path.relative(cwd, f)] });
126
- }
127
- }
128
- }
129
- const pkgJson = await readIfExists(path.join(cwd, "package.json"));
130
- if (pkgJson) {
131
- try {
132
- const json = JSON.parse(pkgJson);
133
- if (typeof json.name === "string") {
134
- for (const h of hints) if (!h.name) h.name = json.name;
135
- }
136
- } catch {
137
- }
138
- }
139
- const merged = [];
140
- const androidOnly = hints.filter((h) => h.packageName && !h.bundleId);
141
- const iosOnly = hints.filter((h) => h.bundleId && !h.packageName);
142
- const both = hints.filter((h) => h.packageName && h.bundleId);
143
- merged.push(...both);
144
- for (const a of androidOnly) {
145
- const match = iosOnly.find((i) => i.bundleId === a.packageName);
146
- if (match) {
147
- merged.push({
148
- name: a.name ?? match.name,
149
- packageName: a.packageName,
150
- bundleId: match.bundleId,
151
- source: [...a.source, ...match.source]
152
- });
153
- } else {
154
- merged.push(a);
155
- }
156
- }
157
- for (const i of iosOnly) {
158
- if (!androidOnly.some((a) => a.packageName === i.bundleId)) {
159
- merged.push(i);
160
- }
161
- }
162
- return merged.filter((h) => h.packageName || h.bundleId);
163
- }
164
- async function hasAnyProjectSignal(cwd) {
165
- return await pathExists(path.join(cwd, "package.json")) || await pathExists(path.join(cwd, "app.json")) || await pathExists(path.join(cwd, "android")) || await pathExists(path.join(cwd, "ios"));
166
- }
167
-
168
30
  // src/handshake.ts
169
31
  import http from "http";
170
32
  async function awaitHandshake(timeoutMs) {
@@ -214,50 +76,7 @@ async function awaitHandshake(timeoutMs) {
214
76
  return { port, promise };
215
77
  }
216
78
 
217
- // src/config.ts
218
- import fs2 from "fs/promises";
219
- import path2 from "path";
220
- import os from "os";
221
- var CONFIG_DIR = path2.join(os.homedir(), ".mimi-seed");
222
- var CONFIG_PATH = path2.join(CONFIG_DIR, "config.json");
223
- async function readConfig() {
224
- try {
225
- const txt = await fs2.readFile(CONFIG_PATH, "utf8");
226
- return JSON.parse(txt);
227
- } catch {
228
- return null;
229
- }
230
- }
231
- async function writeConfig(cfg) {
232
- await fs2.mkdir(CONFIG_DIR, { recursive: true });
233
- await fs2.writeFile(CONFIG_PATH, JSON.stringify(cfg, null, 2));
234
- if (process.platform !== "win32") {
235
- await fs2.chmod(CONFIG_PATH, 384);
236
- }
237
- }
238
- async function deleteConfig() {
239
- await fs2.rm(CONFIG_PATH, { force: true });
240
- }
241
- var CONFIG_LOCATION = CONFIG_PATH;
242
- async function getEffectiveConfig() {
243
- const envToken = process.env.MIMI_SEED_TOKEN;
244
- if (envToken) {
245
- const webBase = process.env.MIMI_SEED_WEB_BASE ?? "https://mimi-seed.pryzm.gg";
246
- return {
247
- token: envToken,
248
- prefix: envToken.slice(0, 8),
249
- endpoint: `${webBase}/api/mcp`,
250
- webBase,
251
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
252
- };
253
- }
254
- return readConfig();
255
- }
256
-
257
79
  // src/doctor.ts
258
- import fs4 from "fs";
259
- import os2 from "os";
260
- import path4 from "path";
261
80
  import kleur from "kleur";
262
81
 
263
82
  // src/git.ts
@@ -301,22 +120,22 @@ function formatCommitsForPrompt(commits) {
301
120
  }
302
121
 
303
122
  // src/project-manifest.ts
304
- import fs3 from "fs";
305
- import path3 from "path";
123
+ import fs from "fs";
124
+ import path from "path";
306
125
  var MANIFEST_FILENAME = ".mimi-seed.json";
307
126
  function findProjectManifest(startDir = process.cwd(), maxDepth = 8) {
308
- let dir = path3.resolve(startDir);
127
+ let dir = path.resolve(startDir);
309
128
  for (let i = 0; i <= maxDepth; i++) {
310
- const candidate = path3.join(dir, MANIFEST_FILENAME);
311
- if (fs3.existsSync(candidate)) {
129
+ const candidate = path.join(dir, MANIFEST_FILENAME);
130
+ if (fs.existsSync(candidate)) {
312
131
  try {
313
- const obj = JSON.parse(fs3.readFileSync(candidate, "utf-8"));
132
+ const obj = JSON.parse(fs.readFileSync(candidate, "utf-8"));
314
133
  if (obj && typeof obj === "object") return { manifest: obj, filePath: candidate };
315
134
  } catch {
316
135
  }
317
136
  return null;
318
137
  }
319
- const parent = path3.dirname(dir);
138
+ const parent = path.dirname(dir);
320
139
  if (parent === dir) break;
321
140
  dir = parent;
322
141
  }
@@ -379,63 +198,34 @@ async function cmdDoctor() {
379
198
  ok("Mimi Seed \uC11C\uBC84 \uC5F0\uACB0\uB428", `\uC571 ${lines.length}\uAC1C`);
380
199
  }
381
200
  }
382
- section("\uB85C\uCEEC MCP \uC790\uACA9\uC99D\uBA85 (~/.mimi-seed)");
383
- const credDir = path4.join(os2.homedir(), ".mimi-seed");
384
- const hasFile = (name) => fs4.existsSync(path4.join(credDir, name));
385
- const hasPrefix = (prefix) => {
386
- try {
387
- return fs4.readdirSync(credDir).some((f) => f.startsWith(prefix));
388
- } catch {
389
- return false;
390
- }
391
- };
392
- const hasPlaySa = hasFile("play-service-account.json") || (() => {
393
- try {
394
- return fs4.readdirSync(path4.join(credDir, "play-service-accounts")).some((f) => f.endsWith(".json"));
395
- } catch {
396
- return false;
397
- }
398
- })();
399
- const oauthPresent = hasFile("tokens.json");
400
- const appstorePresent = hasFile("appstore.json");
401
- const bigquerySaPresent = hasPrefix("bigquery");
402
- const creds = [
403
- ["Google OAuth (Firebase/AdMob/Play/Ads)", oauthPresent, "mimi-seed auth login"],
404
- ["App Store Connect", appstorePresent, "mimi-seed auth appstore"],
405
- ["Play \uC11C\uBE44\uC2A4 \uACC4\uC815 (\uC120\uD0DD \u2014 OAuth\uB85C\uB3C4 \uAC00\uB2A5)", hasPlaySa, "mimi-seed auth playstore"],
406
- ["BigQuery \uC11C\uBE44\uC2A4 \uACC4\uC815", bigquerySaPresent, "mimi-seed auth bigquery"]
407
- ];
408
- for (const [label, present, cmd] of creds) {
409
- if (present) ok(label);
410
- else warn(label, `\u2192 ${cmd}`);
411
- }
412
- process.stdout.write(kleur.dim(" OAuth \uD1A0\uD070 \uC2E0\uC120\uB3C4 \uD655\uC778: mimi-seed auth status\n"));
201
+ section("\uB85C\uCEEC \uC790\uACA9\uC99D\uBA85 (~/.mimi-seed)");
202
+ migrateLegacyJenkins();
203
+ const detected = detectAll();
204
+ for (const spec of CREDENTIALS) {
205
+ const d = detected.get(spec.id);
206
+ const label = spec.note ? `${spec.label} (${spec.note})` : spec.label;
207
+ if (d.present) ok(spec.label, d.detail);
208
+ else if (isSatisfied(spec, detected)) warn(label, "\uD3F4\uBC31\uC73C\uB85C \uB3D9\uC791 \uC911");
209
+ else if (spec.requirement === "optional") warn(`${label}`, `\u2192 ${spec.fix}`);
210
+ else fail(spec.label, `\u2192 ${spec.fix}`);
211
+ }
212
+ process.stdout.write(kleur.dim(" \uC804\uBD80 \uC5F0\uACB0\uD558\uAE30: mimi-seed setup \xB7 OAuth \uC2E0\uC120\uB3C4: mimi-seed auth status\n"));
413
213
  const loaded = findProjectManifest(cwd);
414
214
  if (loaded) {
415
215
  const projName = loaded.manifest.displayName ?? loaded.manifest.project ?? "\uC774 \uD504\uB85C\uC81D\uD2B8";
416
216
  section(`${projName} \uC694\uAD6C\uC0AC\uD56D (.mimi-seed.json)`);
417
- const connectedOf = {
418
- oauth: oauthPresent,
419
- bigquery: bigquerySaPresent || oauthPresent,
420
- playstore: hasPlaySa || oauthPresent,
421
- appstore: appstorePresent,
422
- jenkins: false
423
- // mimi-seed 로컬 자격증명 대상 아님(별도 MCP) — note 로만 안내
424
- };
425
- const fixOf = {
426
- oauth: "mimi-seed auth login",
427
- bigquery: "mimi-seed auth bigquery",
428
- playstore: "mimi-seed auth playstore",
429
- appstore: "mimi-seed auth appstore",
430
- jenkins: "claude mcp add <your-jenkins-mcp> -s user"
431
- };
432
217
  for (const [id, svc] of manifestServiceEntries(loaded.manifest)) {
433
218
  const required = svc.required !== false;
434
- const connected = connectedOf[id];
435
219
  const detail = manifestDetail(id, svc);
220
+ const spec = tryCredById(id);
221
+ if (!spec) {
222
+ warn(`${id} (\uC54C \uC218 \uC5C6\uB294 \uC11C\uBE44\uC2A4)`, svc.note ?? detail);
223
+ continue;
224
+ }
225
+ const connected = isSatisfied(spec, detected);
436
226
  if (connected) ok(id, detail);
437
227
  else if (!required) warn(`${id} (\uC120\uD0DD)`, svc.note ?? detail);
438
- else fail(id, `\u2192 ${fixOf[id]}${detail ? " " + detail : ""}`);
228
+ else fail(id, `\u2192 ${spec.fix}${detail ? " " + detail : ""}`);
439
229
  }
440
230
  }
441
231
  section("\uB85C\uCEEC \uD658\uACBD");
@@ -443,10 +233,8 @@ async function cmdDoctor() {
443
233
  const [, major] = nodeVer.match(/v(\d+)/) ?? [];
444
234
  if (Number(major) >= 20) {
445
235
  ok("Node.js", nodeVer);
446
- } else if (Number(major) >= 18) {
447
- warn("Node.js", `${nodeVer} \u2014 CLI \uB294 \uB3D9\uC791\uD558\uC9C0\uB9CC Local MCP \uC11C\uBC84(@yoonion/mimi-seed-mcp)\uB294 v20 \uC774\uC0C1 \uD544\uC694`);
448
236
  } else {
449
- fail("Node.js", `${nodeVer} \u2014 v18 \uC774\uC0C1(CLI) / v20 \uC774\uC0C1(Local MCP) \uD544\uC694`);
237
+ fail("Node.js", `${nodeVer} \u2014 v20 \uC774\uC0C1 \uD544\uC694 (.nvmrc \uCC38\uACE0)`);
450
238
  }
451
239
  if (isGitRepo(cwd)) {
452
240
  const latestTag = getLatestTag(cwd);
@@ -455,11 +243,6 @@ async function cmdDoctor() {
455
243
  } else {
456
244
  warn("Git \uC800\uC7A5\uC18C \uC5C6\uC74C", "mimi-seed notes \uC0AC\uC6A9 \uBD88\uAC00");
457
245
  }
458
- if (process.env.ANTHROPIC_API_KEY) {
459
- ok("ANTHROPIC_API_KEY", "AI \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 \uC0AC\uC6A9 \uAC00\uB2A5");
460
- } else {
461
- warn("ANTHROPIC_API_KEY \uC5C6\uC74C", "\uC124\uC815 \uC2DC AI \uB9B4\uB9AC\uC988 \uB178\uD2B8/\uB9AC\uBDF0 \uB2F5\uBCC0 \uC0DD\uC131 \uAC00\uB2A5");
462
- }
463
246
  section("\uC571 \uAC10\uC9C0");
464
247
  const hints = await detectHints(cwd);
465
248
  if (hints.length === 0) {
@@ -910,13 +693,7 @@ async function cmdReview(argv) {
910
693
  }
911
694
 
912
695
  // src/auth.ts
913
- import { spawn } from "child_process";
914
- import fs5 from "fs";
915
- import os3 from "os";
916
- import path5 from "path";
917
696
  import kleur5 from "kleur";
918
- var MCP_PKG = "@yoonion/mimi-seed-mcp";
919
- var MIMI_DIR = path5.join(os3.homedir(), ".mimi-seed");
920
697
  function log(msg) {
921
698
  process.stdout.write(msg + "\n");
922
699
  }
@@ -934,8 +711,18 @@ ${kleur5.bold("\uD50C\uB7AB\uD3FC\uBCC4 \uC790\uACA9\uC99D\uBA85:")}
934
711
  ${kleur5.cyan("mimi-seed auth playstore")} Play \uC11C\uBE44\uC2A4 \uACC4\uC815 JSON \uB4F1\uB85D
935
712
  ${kleur5.cyan("mimi-seed auth bigquery")} BigQuery \uC11C\uBE44\uC2A4 \uACC4\uC815 (Crashlytics export \uB4F1)
936
713
 
714
+ ${kleur5.bold("\uBE4C\uB4DC / \uB9C8\uCF00\uD305:")}
715
+ ${kleur5.cyan("mimi-seed auth jenkins")} Jenkins \uC5F0\uACB0 (jenkins.json)
716
+ ${kleur5.cyan("mimi-seed auth ci")} GitHub Actions / GitLab CI (ci.json)
717
+ ${kleur5.cyan("mimi-seed auth googleads")} Google Ads (google-ads.json)
718
+ ${kleur5.cyan("mimi-seed auth facebook")} Facebook \uD398\uC774\uC9C0
719
+ ${kleur5.cyan("mimi-seed auth instagram")} Instagram
720
+
937
721
  ${kleur5.bold("\uC804\uCCB4 \uC0C1\uD0DC:")}
938
- ${kleur5.cyan("mimi-seed auth status --all")} 4\uAC1C \uC790\uACA9\uC99D\uBA85 \uBCF4\uC720 \uC5EC\uBD80 \uD55C\uB208\uC5D0
722
+ ${kleur5.cyan("mimi-seed auth status --all")} \uBAA8\uB4E0 \uC790\uACA9\uC99D\uBA85 \uBCF4\uC720 \uC5EC\uBD80 \uD55C\uB208\uC5D0
723
+
724
+ ${kleur5.bold("\uD55C \uBC88\uC5D0 \uB2E4 \uC5F0\uACB0:")}
725
+ ${kleur5.cyan("mimi-seed setup")} \uC548\uB0B4\uB97C \uB530\uB77C\uAC00\uBA70 \uC21C\uC11C\uB300\uB85C \uC5F0\uACB0 (\uAD8C\uC7A5)
939
726
 
940
727
  ${kleur5.bold("login \uC635\uC158:")}
941
728
  --no-browser URL \uC790\uB3D9 \uC624\uD508 \uC548 \uD568 (\uC9C1\uC811 \uBCF5\uBD99)
@@ -944,44 +731,16 @@ ${kleur5.bold("login \uC635\uC158:")}
944
731
 
945
732
  ${kleur5.dim(`\uB0B4\uBD80\uC801\uC73C\uB85C ${MCP_PKG} \uC758 mimi-seed-*-auth CLI\uB97C \uD638\uCD9C\uD569\uB2C8\uB2E4.`)}`);
946
733
  }
947
- async function runMcpBin(bin, extraArgs) {
948
- return new Promise((resolve) => {
949
- const child = spawn("npx", ["-y", `${MCP_PKG}`, bin, ...extraArgs], {
950
- stdio: "inherit",
951
- shell: true
952
- });
953
- child.on("error", (e) => {
954
- process.stderr.write(`
955
- \u274C npx \uC2E4\uD589 \uC2E4\uD328: ${e.message}
956
- `);
957
- resolve(1);
958
- });
959
- child.on("exit", (code) => resolve(code ?? 0));
960
- });
961
- }
962
- function fileExists(name) {
963
- return fs5.existsSync(path5.join(MIMI_DIR, name));
964
- }
965
- function anyFileStarting(prefix) {
966
- try {
967
- return fs5.readdirSync(MIMI_DIR).some((f) => f.startsWith(prefix));
968
- } catch {
969
- return false;
970
- }
971
- }
972
734
  function printAllCredStatus() {
973
735
  log(kleur5.bold("\uB85C\uCEEC \uC790\uACA9\uC99D\uBA85 \uC0C1\uD0DC ") + kleur5.dim("(~/.mimi-seed)"));
974
- const rows = [
975
- ["Google OAuth (Firebase/AdMob/Play)", fileExists("tokens.json"), "mimi-seed auth login"],
976
- ["App Store Connect", fileExists("appstore.json"), "mimi-seed auth appstore"],
977
- ["Play \uC11C\uBE44\uC2A4 \uACC4\uC815", anyFileStarting("play-service-account"), "mimi-seed auth playstore"],
978
- ["BigQuery \uC11C\uBE44\uC2A4 \uACC4\uC815", anyFileStarting("bigquery"), "mimi-seed auth bigquery"]
979
- ];
980
- for (const [label, ok2, cmd] of rows) {
981
- const mark = ok2 ? kleur5.green("\u2713") : kleur5.red("\u2717");
982
- const hint = ok2 ? "" : kleur5.dim(` \u2192 ${cmd}`);
983
- log(` ${mark} ${label}${hint}`);
736
+ const detected = detectAll();
737
+ for (const spec of CREDENTIALS) {
738
+ const d = detected.get(spec.id);
739
+ const mark = d.present ? kleur5.green("\u2713") : isSatisfied(spec, detected) ? kleur5.yellow("~") : spec.requirement === "optional" ? kleur5.dim("\xB7") : kleur5.red("\u2717");
740
+ const tail = d.present ? kleur5.dim(d.detail ?? "") : kleur5.dim(`\u2192 ${spec.fix}`);
741
+ log(` ${mark} ${spec.label.padEnd(24)} ${tail}`);
984
742
  }
743
+ log(kleur5.dim("\n \uD55C \uBC88\uC5D0 \uC5F0\uACB0: mimi-seed setup"));
985
744
  }
986
745
  async function cmdAuth(args) {
987
746
  const sub = args[0];
@@ -997,6 +756,15 @@ async function cmdAuth(args) {
997
756
  if (sub === "appstore") return void exitWith(await runMcpBin("mimi-seed-appstore-auth", rest));
998
757
  if (sub === "playstore") return void exitWith(await runMcpBin("mimi-seed-playstore-auth", rest));
999
758
  if (sub === "bigquery") return void exitWith(await runMcpBin("mimi-seed-bigquery-auth", rest));
759
+ if (sub === "jenkins") return void exitWith(await runMcpBin("mimi-seed-jenkins-auth", rest));
760
+ if (sub === "googleads") return void exitWith(await runMcpBin("mimi-seed-googleads-auth", rest));
761
+ if (sub === "facebook") return void exitWith(await runMcpBin("mimi-seed-social-auth", ["facebook"]));
762
+ if (sub === "instagram") return void exitWith(await runMcpBin("mimi-seed-social-auth", ["instagram"]));
763
+ if (sub === "ci") {
764
+ const { cmdSetup: cmdSetup2 } = await import("./setup-ENJIMGJF.js");
765
+ await cmdSetup2(["--only", "github,gitlab", "--reconnect", "github,gitlab"]);
766
+ return;
767
+ }
1000
768
  let mcpArgs;
1001
769
  switch (sub) {
1002
770
  case void 0:
@@ -1024,11 +792,11 @@ function exitWith(code) {
1024
792
  }
1025
793
 
1026
794
  // src/cloud.ts
1027
- import { spawn as spawn2 } from "child_process";
795
+ import { spawn } from "child_process";
1028
796
  var MCP_PKG2 = "@yoonion/mimi-seed-mcp";
1029
797
  async function runMcpBin2(bin, extraArgs) {
1030
798
  return new Promise((resolve) => {
1031
- const child = spawn2("npx", ["-y", `${MCP_PKG2}`, bin, ...extraArgs], {
799
+ const child = spawn("npx", ["-y", `${MCP_PKG2}`, bin, ...extraArgs], {
1032
800
  stdio: "inherit",
1033
801
  shell: true
1034
802
  });
@@ -1054,541 +822,19 @@ async function cmdGa4(args) {
1054
822
  exitWith2(await runMcpBin2("mimi-seed-ga4", args));
1055
823
  }
1056
824
 
1057
- // src/deploy.ts
1058
- import kleur6 from "kleur";
1059
- import * as readline from "readline";
1060
-
1061
- // src/ci-providers.ts
1062
- import fs6 from "fs";
1063
- import path6 from "path";
1064
- import os4 from "os";
1065
- var CI_CONFIG_PATH = path6.join(os4.homedir(), ".mimi-seed", "ci.json");
1066
- function loadCiProviderConfig() {
1067
- try {
1068
- return JSON.parse(fs6.readFileSync(CI_CONFIG_PATH, "utf-8"));
1069
- } catch {
1070
- return null;
1071
- }
1072
- }
1073
- function saveCiProviderConfig(cfg) {
1074
- const dir = path6.dirname(CI_CONFIG_PATH);
1075
- if (!fs6.existsSync(dir)) {
1076
- fs6.mkdirSync(dir, { recursive: true, mode: 448 });
1077
- }
1078
- fs6.writeFileSync(CI_CONFIG_PATH, JSON.stringify(cfg, null, 2));
1079
- if (process.platform !== "win32") {
1080
- fs6.chmodSync(CI_CONFIG_PATH, 384);
1081
- }
1082
- }
1083
- function ghBase(cfg) {
1084
- if (cfg.host) return `${cfg.host.replace(/\/$/, "")}/api/v3`;
1085
- return "https://api.github.com";
1086
- }
1087
- function ghHeaders(token) {
1088
- return {
1089
- Authorization: `Bearer ${token}`,
1090
- Accept: "application/vnd.github+json",
1091
- "X-GitHub-Api-Version": "2022-11-28",
1092
- "Content-Type": "application/json"
1093
- };
1094
- }
1095
- async function ghTriggerWorkflow(cfg, workflow, ref, inputs = {}) {
1096
- const startTime = /* @__PURE__ */ new Date();
1097
- const wfId = /^\d+$/.test(workflow) ? Number(workflow) : workflow;
1098
- const dispatchRes = await fetch(
1099
- `${ghBase(cfg)}/repos/${cfg.owner}/${cfg.repo}/actions/workflows/${wfId}/dispatches`,
1100
- {
1101
- method: "POST",
1102
- headers: ghHeaders(cfg.token),
1103
- body: JSON.stringify({ ref, inputs })
1104
- }
1105
- );
1106
- if (!dispatchRes.ok) {
1107
- throw new Error(`GitHub dispatch ${dispatchRes.status}: ${await dispatchRes.text()}`);
1108
- }
1109
- await new Promise((r) => setTimeout(r, 3e3));
1110
- const runsRes = await fetch(
1111
- `${ghBase(cfg)}/repos/${cfg.owner}/${cfg.repo}/actions/workflows/${wfId}/runs?per_page=5`,
1112
- { headers: ghHeaders(cfg.token) }
1113
- );
1114
- if (!runsRes.ok) return null;
1115
- const data = await runsRes.json();
1116
- const run = data.workflow_runs.find((r) => new Date(r.created_at) >= startTime) ?? data.workflow_runs[0];
1117
- if (!run) return null;
1118
- return { runId: run.id, url: run.html_url };
1119
- }
1120
- async function ghPollRun(cfg, runId, onTick, timeoutMs = 30 * 60 * 1e3, intervalMs = 15e3) {
1121
- const start = Date.now();
1122
- let consecutiveErrors = 0;
1123
- while (Date.now() - start < timeoutMs) {
1124
- await new Promise((r) => setTimeout(r, intervalMs));
1125
- let res;
1126
- try {
1127
- res = await fetch(
1128
- `${ghBase(cfg)}/repos/${cfg.owner}/${cfg.repo}/actions/runs/${runId}`,
1129
- { headers: ghHeaders(cfg.token) }
1130
- );
1131
- } catch {
1132
- consecutiveErrors++;
1133
- if (consecutiveErrors >= 3) throw new Error("GitHub API \uC5F0\uC18D \uC624\uB958 3\uD68C");
1134
- continue;
1135
- }
1136
- if (!res.ok) {
1137
- consecutiveErrors++;
1138
- if (consecutiveErrors >= 3) throw new Error(`GitHub API \uC5F0\uC18D \uC624\uB958 (HTTP ${res.status})`);
1139
- continue;
1140
- }
1141
- consecutiveErrors = 0;
1142
- const data = await res.json();
1143
- onTick?.(data.status);
1144
- if (data.status === "completed") {
1145
- if (data.conclusion === "success") return "success";
1146
- if (data.conclusion === "cancelled") return "cancelled";
1147
- return "failure";
1148
- }
1149
- }
1150
- return "timeout";
1151
- }
1152
- function glBase(cfg) {
1153
- return `${cfg.host ?? "https://gitlab.com"}/api/v4`;
1154
- }
1155
- function glProjectId(cfg) {
1156
- return encodeURIComponent(`${cfg.owner}/${cfg.repo}`);
1157
- }
1158
- async function glTriggerPipeline(cfg, ref, variables = {}) {
1159
- const vars = Object.entries(variables).map(([key, value]) => ({ key, value }));
1160
- const body = { ref };
1161
- if (vars.length > 0) body.variables = vars;
1162
- const res = await fetch(`${glBase(cfg)}/projects/${glProjectId(cfg)}/pipeline`, {
1163
- method: "POST",
1164
- headers: { "PRIVATE-TOKEN": cfg.token, "Content-Type": "application/json" },
1165
- body: JSON.stringify(body)
1166
- });
1167
- if (!res.ok) throw new Error(`GitLab trigger ${res.status}: ${await res.text()}`);
1168
- const data = await res.json();
1169
- return { pipelineId: data.id, url: data.web_url };
1170
- }
1171
- async function glPollPipeline(cfg, pipelineId, onTick, timeoutMs = 30 * 60 * 1e3, intervalMs = 15e3) {
1172
- const start = Date.now();
1173
- let consecutiveErrors = 0;
1174
- while (Date.now() - start < timeoutMs) {
1175
- await new Promise((r) => setTimeout(r, intervalMs));
1176
- let res;
1177
- try {
1178
- res = await fetch(
1179
- `${glBase(cfg)}/projects/${glProjectId(cfg)}/pipelines/${pipelineId}`,
1180
- { headers: { "PRIVATE-TOKEN": cfg.token } }
1181
- );
1182
- } catch {
1183
- consecutiveErrors++;
1184
- if (consecutiveErrors >= 3) throw new Error("GitLab API \uC5F0\uC18D \uC624\uB958 3\uD68C");
1185
- continue;
1186
- }
1187
- if (!res.ok) {
1188
- consecutiveErrors++;
1189
- if (consecutiveErrors >= 3) throw new Error(`GitLab API \uC5F0\uC18D \uC624\uB958 (HTTP ${res.status})`);
1190
- continue;
1191
- }
1192
- consecutiveErrors = 0;
1193
- const data = await res.json();
1194
- onTick?.(data.status);
1195
- if (data.status === "success") return "success";
1196
- if (data.status === "failed") return "failure";
1197
- if (data.status === "canceled" || data.status === "skipped") return "cancelled";
1198
- }
1199
- return "timeout";
1200
- }
1201
-
1202
- // src/deploy.ts
1203
- var PHASE_ICON = {
1204
- init: "\u{1F680}",
1205
- verify: "\u{1F50D}",
1206
- notes: "\u{1F4DD}",
1207
- apply: "\u{1F4E4}",
1208
- promote: "\u{1F3AF}",
1209
- done: "\u{1F389}",
1210
- error: "\u274C"
1211
- };
1212
- function log2(msg) {
1213
- process.stdout.write(msg + "\n");
1214
- }
1215
- function jenkinsHeaders(cfg) {
1216
- const creds = Buffer.from(`${cfg.user ?? "admin"}:${cfg.token}`).toString("base64");
1217
- return { Authorization: `Basic ${creds}`, "Content-Type": "application/json" };
1218
- }
1219
- async function triggerBuild(cfg, jobName, params) {
1220
- const qs = new URLSearchParams(params).toString();
1221
- const url = `${cfg.url}/job/${encodeURIComponent(jobName)}/buildWithParameters?${qs}`;
1222
- const res = await fetch(url, { method: "POST", headers: jenkinsHeaders(cfg) });
1223
- if (!res.ok) {
1224
- throw new Error(`Jenkins \uD2B8\uB9AC\uAC70 \uC2E4\uD328 ${res.status}: ${await res.text()}`);
1225
- }
1226
- const location = res.headers.get("Location") ?? "";
1227
- const match = location.match(/\/queue\/item\/(\d+)\//);
1228
- return match?.[1] ?? "";
1229
- }
1230
- async function getQueueBuildNumber(cfg, queueItemId) {
1231
- const url = `${cfg.url}/queue/item/${queueItemId}/api/json`;
1232
- const res = await fetch(url, { headers: jenkinsHeaders(cfg) });
1233
- if (!res.ok) return null;
1234
- const data = await res.json();
1235
- return data.executable?.number ?? null;
1236
- }
1237
- async function getBuildStatus(cfg, jobName, buildNumber) {
1238
- const url = `${cfg.url}/job/${encodeURIComponent(jobName)}/${buildNumber}/api/json`;
1239
- const res = await fetch(url, { headers: jenkinsHeaders(cfg) });
1240
- if (!res.ok) throw new Error(`\uBE4C\uB4DC \uC0C1\uD0DC \uC870\uD68C \uC2E4\uD328 ${res.status}`);
1241
- const data = await res.json();
1242
- return {
1243
- building: data.building ?? true,
1244
- result: data.result ?? null,
1245
- duration: data.duration ?? 0
1246
- };
1247
- }
1248
- async function pollBuildComplete(cfg, jobName, buildNumber, timeoutMs = 30 * 60 * 1e3) {
1249
- const start = Date.now();
1250
- const intervalMs = 15e3;
1251
- let dots = 0;
1252
- let consecutiveErrors = 0;
1253
- while (Date.now() - start < timeoutMs) {
1254
- await new Promise((r) => setTimeout(r, intervalMs));
1255
- let status;
1256
- try {
1257
- status = await getBuildStatus(cfg, jobName, buildNumber);
1258
- consecutiveErrors = 0;
1259
- } catch {
1260
- consecutiveErrors++;
1261
- if (consecutiveErrors >= 3) throw new Error("Jenkins \uC5F0\uACB0 \uC624\uB958 3\uD68C \uC5F0\uC18D \u2014 \uB124\uD2B8\uC6CC\uD06C\uB97C \uD655\uC778\uD558\uC138\uC694");
1262
- process.stdout.write(`\r \u26A0 Jenkins \uC5F0\uACB0 \uC624\uB958 (${consecutiveErrors}/3\uD68C), \uC7AC\uC2DC\uB3C4... `);
1263
- continue;
1264
- }
1265
- dots = (dots + 1) % 4;
1266
- process.stdout.write(`\r \u23F3 \uBE4C\uB4DC \uC9C4\uD589 \uC911${".".repeat(dots + 1)} `);
1267
- if (!status.building) {
1268
- process.stdout.write("\n");
1269
- return status.result ?? "FAILURE";
1270
- }
1271
- }
1272
- throw new Error("\uBE4C\uB4DC \uD0C0\uC784\uC544\uC6C3 (30\uBD84)");
1273
- }
1274
- async function streamDeploy(webBase, token, body) {
1275
- const res = await fetch(`${webBase}/api/deploy`, {
1276
- method: "POST",
1277
- headers: {
1278
- "Content-Type": "application/json",
1279
- Authorization: `Bearer ${token}`
1280
- },
1281
- body: JSON.stringify(body)
1282
- });
1283
- if (!res.ok) {
1284
- const text = await res.text().catch(() => "");
1285
- throw new Error(`\uC11C\uBC84 \uBC30\uD3EC \uC2E4\uD328 ${res.status}: ${text.slice(0, 200)}`);
1286
- }
1287
- const reader = res.body?.getReader();
1288
- if (!reader) throw new Error("SSE \uC2A4\uD2B8\uB9BC \uC5C6\uC74C");
1289
- const decoder = new TextDecoder();
1290
- let buf = "";
1291
- while (true) {
1292
- const { done, value } = await reader.read();
1293
- if (done) break;
1294
- buf += decoder.decode(value, { stream: true });
1295
- const lines = buf.split("\n");
1296
- buf = lines.pop() ?? "";
1297
- for (const line of lines) {
1298
- if (!line.startsWith("data: ")) continue;
1299
- try {
1300
- const event = JSON.parse(line.slice(6));
1301
- const icon = PHASE_ICON[event.phase] ?? "\u25B8";
1302
- const color = event.status === "done" ? kleur6.green : event.status === "failed" ? kleur6.red : event.status === "skipped" ? kleur6.yellow : kleur6.dim;
1303
- log2(` ${icon} ${color(event.message)}`);
1304
- if (event.phase === "error" || event.phase === "verify" && event.status === "failed" || event.phase === "promote" && event.status === "failed") {
1305
- process.exit(1);
1306
- }
1307
- } catch {
1308
- }
1309
- }
1310
- }
1311
- }
1312
- function parseArgs4(argv) {
1313
- const args = {
1314
- platform: "android",
1315
- language: "ko-KR",
1316
- dryRun: false,
1317
- yes: false,
1318
- skipBuild: false,
1319
- setupJenkins: false,
1320
- setupGithub: false,
1321
- setupGitlab: false,
1322
- ci: "auto",
1323
- ref: "main"
1324
- };
1325
- for (let i = 0; i < argv.length; i++) {
1326
- if ((argv[i] === "--platform" || argv[i] === "-p") && argv[i + 1]) args.platform = argv[++i];
1327
- if (argv[i] === "--app" && argv[i + 1]) args.appId = argv[++i];
1328
- if (argv[i] === "--version-code" && argv[i + 1]) args.versionCode = Number(argv[++i]);
1329
- if (argv[i] === "--from" && argv[i + 1]) args.fromRef = argv[++i];
1330
- if (argv[i] === "--to" && argv[i + 1]) args.toRef = argv[++i];
1331
- if (argv[i] === "--language" && argv[i + 1]) args.language = argv[++i];
1332
- if (argv[i] === "--dry-run") args.dryRun = true;
1333
- if (argv[i] === "--yes" || argv[i] === "-y") args.yes = true;
1334
- if (argv[i] === "--skip-build") args.skipBuild = true;
1335
- if (argv[i] === "--ci" && argv[i + 1]) args.ci = argv[++i];
1336
- if (argv[i] === "--workflow" && argv[i + 1]) args.workflow = argv[++i];
1337
- if (argv[i] === "--ref" && argv[i + 1]) args.ref = argv[++i];
1338
- if (argv[i] === "setup-jenkins") args.setupJenkins = true;
1339
- if (argv[i] === "setup-github") args.setupGithub = true;
1340
- if (argv[i] === "setup-gitlab") args.setupGitlab = true;
1341
- }
1342
- return args;
1343
- }
1344
- async function promptJenkinsSetup() {
1345
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1346
- const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
1347
- log2(kleur6.bold("Jenkins \uC124\uC815"));
1348
- const url = await ask(" Jenkins URL (\uC608: http://your-jenkins.example.com:8080): ");
1349
- const user = await ask(" Jenkins \uC0AC\uC6A9\uC790\uBA85 (\uC608: admin): ");
1350
- const token = await ask(" Jenkins API Token: ");
1351
- const jobAndroid = await ask(" Android Job \uC774\uB984 (\uC608: my-app-android): ");
1352
- const jobIos = await ask(" iOS Job \uC774\uB984 (\uC120\uD0DD, \uC5D4\uD130 \uC2A4\uD0B5): ");
1353
- rl.close();
1354
- return { url, user, token, jobAndroid: jobAndroid || void 0, jobIos: jobIos || void 0 };
1355
- }
1356
- async function promptGitProviderSetup(provider) {
1357
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1358
- const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
1359
- const isGh = provider === "github";
1360
- log2(kleur6.bold(isGh ? "GitHub Actions \uC124\uC815" : "GitLab CI \uC124\uC815"));
1361
- const tokenLabel = isGh ? " GitHub Personal Access Token (repo+workflow \uC2A4\uCF54\uD504): " : " GitLab Personal Access Token: ";
1362
- const token = await ask(tokenLabel);
1363
- const owner = await ask(isGh ? " Owner (org/user): " : " Namespace/group: ");
1364
- const repo = await ask(" Repo \uC774\uB984 (\uACBD\uB85C \uC5C6\uC774): ");
1365
- const hostPrompt = isGh ? " GitHub Enterprise host (\uC120\uD0DD, \uC5D4\uD130=github.com): " : " GitLab self-hosted URL (\uC120\uD0DD, \uC5D4\uD130=gitlab.com): ";
1366
- const host = await ask(hostPrompt);
1367
- rl.close();
1368
- return {
1369
- provider,
1370
- token,
1371
- owner,
1372
- repo,
1373
- host: host || void 0
1374
- };
1375
- }
1376
- function resolveCi(ciOption, jenkins, ciProvider) {
1377
- if (ciOption !== "auto") return ciOption;
1378
- if (jenkins?.url && jenkins.token) return "jenkins";
1379
- if (ciProvider) return ciProvider.provider;
1380
- throw new Error(
1381
- "CI \uC124\uC815 \uC5C6\uC74C. \uB2E4\uC74C \uC911 \uD558\uB098 \uC2E4\uD589:\n \u2022 mimi-seed deploy setup-jenkins\n \u2022 mimi-seed deploy setup-github\n \u2022 mimi-seed deploy setup-gitlab"
1382
- );
1383
- }
1384
- async function runGitProviderBuild(cfg, args) {
1385
- let runUrl = "";
1386
- let runId;
1387
- if (cfg.provider === "github") {
1388
- if (!args.workflow) {
1389
- throw new Error("--workflow \uD544\uC694 (\uC608: --workflow deploy.yml)");
1390
- }
1391
- log2(`\u{1F528} GitHub Actions \uD2B8\uB9AC\uAC70: ${kleur6.cyan(args.workflow)} @ ${args.ref}`);
1392
- const inputs = {};
1393
- if (args.appId) inputs.MIMI_APP_ID = args.appId;
1394
- inputs.PLATFORM = args.platform;
1395
- const result2 = await ghTriggerWorkflow(cfg, args.workflow, args.ref, inputs);
1396
- if (!result2) {
1397
- throw new Error("GitHub Actions run_id \uC870\uD68C \uC2E4\uD328. \uC7A0\uC2DC \uD6C4 ci_list_recent_builds \uB85C \uD655\uC778\uD558\uC138\uC694.");
1398
- }
1399
- runId = result2.runId;
1400
- runUrl = result2.url;
1401
- log2(kleur6.dim(` Run ID: ${runId} \u2192 ${runUrl}`));
1402
- } else {
1403
- log2(`\u{1F528} GitLab Pipeline \uD2B8\uB9AC\uAC70: ${args.ref}`);
1404
- const variables = { PLATFORM: args.platform };
1405
- if (args.appId) variables.MIMI_APP_ID = args.appId;
1406
- const result2 = await glTriggerPipeline(cfg, args.ref, variables);
1407
- runId = result2.pipelineId;
1408
- runUrl = result2.url;
1409
- log2(kleur6.dim(` Pipeline ID: ${runId} \u2192 ${runUrl}`));
1410
- }
1411
- log2(" \uC644\uB8CC \uB300\uAE30 \uC911...");
1412
- let dots = 0;
1413
- const onTick = (status) => {
1414
- dots = (dots + 1) % 4;
1415
- process.stdout.write(`\r \u23F3 ${status}${".".repeat(dots + 1)} `);
1416
- };
1417
- const result = cfg.provider === "github" ? await ghPollRun(cfg, runId, onTick) : await glPollPipeline(cfg, runId, onTick);
1418
- process.stdout.write("\n");
1419
- if (result === "success") {
1420
- log2(kleur6.green(`\u2705 \uBE4C\uB4DC #${runId} \uC131\uACF5`));
1421
- return runId;
1422
- }
1423
- log2(kleur6.red(`\uBE4C\uB4DC \uC885\uB8CC: ${result}`));
1424
- log2(kleur6.dim(` ${runUrl}`));
1425
- log2(kleur6.dim(` \uBE4C\uB4DC\uAC00 \uC774\uBBF8 \uC644\uB8CC\uB410\uB2E4\uBA74: mimi-seed deploy --skip-build --version-code <N> --platform ${args.platform}`));
1426
- process.exit(1);
1427
- }
1428
- async function cmdDeploy(argv) {
1429
- const args = parseArgs4(argv);
1430
- const cfg = await getEffectiveConfig();
1431
- if (!cfg) {
1432
- log2(kleur6.red("\uC5F0\uACB0\uB41C \uACC4\uC815 \uC5C6\uC74C. `mimi-seed init` \uC2E4\uD589."));
1433
- process.exit(1);
1434
- }
1435
- if (args.setupJenkins) {
1436
- const jenkins = await promptJenkinsSetup();
1437
- await writeConfig({ ...cfg, jenkins });
1438
- log2(kleur6.green("\u2705 Jenkins \uC124\uC815 \uC800\uC7A5\uB428"));
1439
- return;
1440
- }
1441
- if (args.setupGithub) {
1442
- const ciCfg = await promptGitProviderSetup("github");
1443
- saveCiProviderConfig(ciCfg);
1444
- log2(kleur6.green(`\u2705 GitHub Actions \uC124\uC815 \uC800\uC7A5\uB428 \u2192 ~/.mimi-seed/ci.json`));
1445
- return;
1446
- }
1447
- if (args.setupGitlab) {
1448
- const ciCfg = await promptGitProviderSetup("gitlab");
1449
- saveCiProviderConfig(ciCfg);
1450
- log2(kleur6.green(`\u2705 GitLab CI \uC124\uC815 \uC800\uC7A5\uB428 \u2192 ~/.mimi-seed/ci.json`));
1451
- return;
1452
- }
1453
- log2(kleur6.bold(`mimi-seed deploy \u2014 ${args.platform}`));
1454
- if (args.dryRun) log2(kleur6.yellow(" [dry-run \uBAA8\uB4DC] \uC2E4\uC81C \uBC30\uD3EC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4"));
1455
- log2("");
1456
- let versionCode = args.versionCode;
1457
- if (!args.skipBuild) {
1458
- const ciProvider = loadCiProviderConfig();
1459
- const kind = resolveCi(args.ci, cfg.jenkins, ciProvider);
1460
- log2(kleur6.dim(` CI: ${kind}`));
1461
- if (kind === "jenkins") {
1462
- if (!cfg.jenkins?.url || !cfg.jenkins?.token) {
1463
- log2(kleur6.yellow("Jenkins \uC124\uC815 \uC5C6\uC74C. `mimi-seed deploy setup-jenkins` \uB85C \uC124\uC815\uD558\uAC70\uB098 --skip-build \uC0AC\uC6A9."));
1464
- process.exit(1);
1465
- }
1466
- const jenkins = cfg.jenkins;
1467
- const jobName = args.platform === "android" ? jenkins.jobAndroid : jenkins.jobIos;
1468
- if (!jobName) {
1469
- log2(kleur6.red(`${args.platform} Jenkins job\uC774 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. setup-jenkins \uC2E4\uD589.`));
1470
- process.exit(1);
1471
- }
1472
- log2(`\u{1F528} Jenkins \uBE4C\uB4DC \uD2B8\uB9AC\uAC70: ${kleur6.cyan(jobName)}`);
1473
- const buildParams = {};
1474
- if (args.appId) buildParams.MIMI_APP_ID = args.appId;
1475
- const queueItemId = await triggerBuild(jenkins, jobName, buildParams);
1476
- if (!queueItemId) {
1477
- log2(kleur6.yellow(" \u26A0 Queue item ID\uB97C \uAC00\uC838\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uBE4C\uB4DC\uB294 \uC2DC\uC791\uB410\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4."));
1478
- } else {
1479
- log2(kleur6.dim(` Queue item: ${queueItemId}`));
1480
- }
1481
- let buildNumber = null;
1482
- if (queueItemId) {
1483
- log2(" \uBE4C\uB4DC \uBC88\uD638 \uB300\uAE30 \uC911...");
1484
- for (let i = 0; i < 6; i++) {
1485
- await new Promise((r) => setTimeout(r, 5e3));
1486
- buildNumber = await getQueueBuildNumber(jenkins, queueItemId).catch(() => null);
1487
- if (buildNumber) break;
1488
- }
1489
- }
1490
- if (!buildNumber) {
1491
- log2(kleur6.yellow(" \uBE4C\uB4DC \uBC88\uD638\uB97C \uAC00\uC838\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. --skip-build + --version-code \uB85C \uC7AC\uC2DC\uB3C4 \uAC00\uB2A5."));
1492
- process.exit(1);
1493
- }
1494
- log2(` \uBE4C\uB4DC #${buildNumber} \uC2DC\uC791\uB428. \uC644\uB8CC \uB300\uAE30 \uC911...`);
1495
- const result = await pollBuildComplete(jenkins, jobName, buildNumber);
1496
- if (result !== "SUCCESS") {
1497
- log2(kleur6.red(`\uBE4C\uB4DC \uC2E4\uD328: ${result}`));
1498
- log2(kleur6.dim(` Jenkins: ${jenkins.url}/job/${encodeURIComponent(jobName)}/${buildNumber}/`));
1499
- log2(kleur6.dim(` \uBE4C\uB4DC\uAC00 \uC774\uBBF8 \uC644\uB8CC\uB410\uB2E4\uBA74: mimi-seed deploy --skip-build --version-code ${buildNumber} --platform ${args.platform}`));
1500
- process.exit(1);
1501
- }
1502
- log2(kleur6.green(`\u2705 \uBE4C\uB4DC #${buildNumber} \uC131\uACF5`));
1503
- if (!versionCode) {
1504
- versionCode = buildNumber;
1505
- log2(kleur6.dim(` versionCode = buildNumber (${versionCode})`));
1506
- }
1507
- } else {
1508
- if (!ciProvider) {
1509
- log2(kleur6.red(`${kind} \uC124\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. setup-${kind} \uC2E4\uD589.`));
1510
- process.exit(1);
1511
- }
1512
- const buildId = await runGitProviderBuild(ciProvider, args);
1513
- if (!versionCode) {
1514
- log2(kleur6.red(`\u2717 versionCode \uBBF8\uC9C0\uC815 (${kind} run_id ${buildId}\uB294 versionCode\uB85C \uBD80\uC801\uD569)`));
1515
- log2(kleur6.dim(" \uAD8C\uC7A5 \uC0AC\uD56D:"));
1516
- log2(kleur6.dim(" \u2022 CI \uC6CC\uD06C\uD50C\uB85C \uC548\uC5D0\uC11C versionCode\uB97C \uACB0\uC815\uD558\uACE0 \uACB0\uACFC\uB97C \uCD9C\uB825"));
1517
- log2(kleur6.dim(" \u2022 \uB2E4\uC74C \uC2E4\uD589: mimi-seed deploy --skip-build --version-code <N>"));
1518
- process.exit(1);
1519
- }
1520
- }
1521
- }
1522
- if (!versionCode) {
1523
- log2(kleur6.red("versionCode\uB97C \uC54C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. --version-code <N> \uC73C\uB85C \uC9C0\uC815\uD558\uC138\uC694."));
1524
- process.exit(1);
1525
- }
1526
- let appId = args.appId;
1527
- if (!appId) {
1528
- const { mcpCall: mcpCall2 } = await import("./mcp-client-26VCPY4L.js");
1529
- const r = await mcpCall2(cfg.endpoint, cfg.token, "list_apps", {});
1530
- if (!r.isError) {
1531
- try {
1532
- const apps = JSON.parse(r.text);
1533
- if (apps.length > 0) {
1534
- appId = apps[0].id;
1535
- log2(kleur6.dim(` \uC571: ${apps[0].name} (${appId})`));
1536
- }
1537
- } catch {
1538
- }
1539
- }
1540
- }
1541
- if (!appId) {
1542
- log2(kleur6.red("appId\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. --app <id> \uB85C \uC9C0\uC815\uD558\uAC70\uB098 `mimi-seed init` \uC73C\uB85C \uC571 \uB4F1\uB85D."));
1543
- process.exit(1);
1544
- }
1545
- const needsConfirm = !args.dryRun && !args.yes && process.stdout.isTTY && !process.env.MIMI_SEED_TOKEN;
1546
- if (needsConfirm) {
1547
- const target = args.platform === "ios" ? "App Store \uC2EC\uC0AC \uC81C\uCD9C" : "Play Store production \uD2B8\uB799";
1548
- log2("");
1549
- log2(kleur6.yellow(`\u26A0 \uC2E4\uC81C \uBC30\uD3EC: ${args.platform} \xB7 versionCode ${versionCode} \u2192 ${target}`));
1550
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1551
- const answer = await new Promise(
1552
- (resolve) => rl.question(kleur6.bold("\uACC4\uC18D \uC9C4\uD589\uD560\uAE4C\uC694? [y/N]: "), (a) => resolve(a.trim().toLowerCase()))
1553
- );
1554
- rl.close();
1555
- if (answer !== "y" && answer !== "yes") {
1556
- log2(kleur6.dim("\uCDE8\uC18C\uB428. (--yes \uB85C \uD655\uC778 \uC0DD\uB7B5 \uAC00\uB2A5)"));
1557
- return;
1558
- }
1559
- }
1560
- log2("");
1561
- log2("\u{1F4E1} \uC11C\uBC84 \uBC30\uD3EC \uD30C\uC774\uD504\uB77C\uC778 \uC2DC\uC791...");
1562
- log2("");
1563
- const deployBuildNumber = !args.skipBuild ? versionCode : void 0;
1564
- await streamDeploy(cfg.webBase, cfg.token, {
1565
- appId,
1566
- platform: args.platform,
1567
- versionCode,
1568
- ...deployBuildNumber ? { buildNumber: deployBuildNumber } : {},
1569
- fromRef: args.fromRef,
1570
- toRef: args.toRef,
1571
- language: args.language,
1572
- dryRun: args.dryRun
1573
- });
1574
- log2("");
1575
- log2(kleur6.bold("\uC644\uB8CC. Play Console\uC5D0\uC11C \uBC30\uD3EC \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694."));
1576
- log2(kleur6.dim(" https://play.google.com/console/developers"));
1577
- }
1578
-
1579
825
  // src/mcp-restart.ts
1580
826
  import { execSync as execSync2 } from "child_process";
1581
- import fs7 from "fs";
1582
- import os5 from "os";
1583
- import path7 from "path";
1584
- import kleur7 from "kleur";
1585
- function log3(msg) {
827
+ import fs2 from "fs";
828
+ import os from "os";
829
+ import path2 from "path";
830
+ import kleur6 from "kleur";
831
+ function log2(msg) {
1586
832
  process.stdout.write(msg + "\n");
1587
833
  }
1588
834
  function readClaudeJson() {
1589
- const p = path7.join(os5.homedir(), ".claude.json");
835
+ const p = path2.join(os.homedir(), ".claude.json");
1590
836
  try {
1591
- return JSON.parse(fs7.readFileSync(p, "utf8"));
837
+ return JSON.parse(fs2.readFileSync(p, "utf8"));
1592
838
  } catch {
1593
839
  return {};
1594
840
  }
@@ -1604,7 +850,7 @@ function findProcessMarker(cfg) {
1604
850
  return meaningful.at(-1) ?? null;
1605
851
  }
1606
852
  function killByMarker(marker) {
1607
- const isWin = os5.platform() === "win32";
853
+ const isWin = os.platform() === "win32";
1608
854
  if (isWin) {
1609
855
  const escaped = marker.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
1610
856
  let pids = [];
@@ -1621,7 +867,7 @@ function killByMarker(marker) {
1621
867
  for (const pid of pids) {
1622
868
  try {
1623
869
  execSync2(`taskkill /F /PID ${pid}`, { stdio: "pipe" });
1624
- log3(kleur7.dim(` PID ${pid} \uC885\uB8CC`));
870
+ log2(kleur6.dim(` PID ${pid} \uC885\uB8CC`));
1625
871
  killed++;
1626
872
  } catch {
1627
873
  }
@@ -1638,47 +884,47 @@ function killByMarker(marker) {
1638
884
  }
1639
885
  async function cmdRestart(args) {
1640
886
  const serverName = args[0] ?? "mimi-seed";
1641
- log3(kleur7.bold(`MCP \uC11C\uBC84 \uC7AC\uC2DC\uC791: ${serverName}`));
1642
- log3("");
887
+ log2(kleur6.bold(`MCP \uC11C\uBC84 \uC7AC\uC2DC\uC791: ${serverName}`));
888
+ log2("");
1643
889
  const d = readClaudeJson();
1644
890
  const servers = d.mcpServers;
1645
891
  const cfg = servers?.[serverName];
1646
892
  if (!cfg) {
1647
893
  const available = servers ? Object.keys(servers).join(", ") : "(\uC5C6\uC74C)";
1648
- log3(kleur7.red(`'${serverName}' \uC11C\uBC84\uB97C ~/.claude/.claude.json\uC5D0\uC11C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.`));
1649
- log3(kleur7.dim(` \uB4F1\uB85D\uB41C \uC11C\uBC84: ${available}`));
894
+ log2(kleur6.red(`'${serverName}' \uC11C\uBC84\uB97C ~/.claude/.claude.json\uC5D0\uC11C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.`));
895
+ log2(kleur6.dim(` \uB4F1\uB85D\uB41C \uC11C\uBC84: ${available}`));
1650
896
  process.exit(1);
1651
897
  }
1652
898
  if (cfg.type === "http" || cfg.type === "sse") {
1653
- log3(kleur7.yellow(`'${serverName}'\uB294 HTTP/SSE \uC11C\uBC84\uC785\uB2C8\uB2E4. \uD504\uB85C\uC138\uC2A4 \uC7AC\uC2DC\uC791\uC774 \uD544\uC694\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`));
1654
- log3(kleur7.dim(" Claude Code\uC5D0\uC11C /mcp \uB97C \uC2E4\uD589\uD574 \uC5F0\uACB0 \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694."));
899
+ log2(kleur6.yellow(`'${serverName}'\uB294 HTTP/SSE \uC11C\uBC84\uC785\uB2C8\uB2E4. \uD504\uB85C\uC138\uC2A4 \uC7AC\uC2DC\uC791\uC774 \uD544\uC694\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`));
900
+ log2(kleur6.dim(" Claude Code\uC5D0\uC11C /mcp \uB97C \uC2E4\uD589\uD574 \uC5F0\uACB0 \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694."));
1655
901
  return;
1656
902
  }
1657
903
  const marker = findProcessMarker(cfg);
1658
904
  if (!marker) {
1659
- log3(kleur7.yellow("\uD504\uB85C\uC138\uC2A4 \uC2DD\uBCC4\uC790(\uC2A4\uD06C\uB9BD\uD2B8 \uACBD\uB85C)\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."));
1660
- log3(kleur7.dim(` \uC124\uC815: ${JSON.stringify(cfg)}`));
905
+ log2(kleur6.yellow("\uD504\uB85C\uC138\uC2A4 \uC2DD\uBCC4\uC790(\uC2A4\uD06C\uB9BD\uD2B8 \uACBD\uB85C)\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."));
906
+ log2(kleur6.dim(` \uC124\uC815: ${JSON.stringify(cfg)}`));
1661
907
  process.exit(1);
1662
908
  }
1663
- log3(kleur7.dim(` \uC2DD\uBCC4\uC790: ${marker}`));
909
+ log2(kleur6.dim(` \uC2DD\uBCC4\uC790: ${marker}`));
1664
910
  const { killed } = killByMarker(marker);
1665
911
  if (killed === 0) {
1666
- log3(kleur7.yellow("\u26A0 \uC2E4\uD589 \uC911\uC778 \uD504\uB85C\uC138\uC2A4\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."));
1667
- log3(kleur7.dim(" \uC774\uBBF8 \uC885\uB8CC\uB410\uAC70\uB098, Claude Code\uAC00 \uC544\uC9C1 \uC11C\uBC84\uB97C \uC2DC\uC791\uD558\uC9C0 \uC54A\uC740 \uC0C1\uD0DC\uC77C \uC218 \uC788\uC2B5\uB2C8\uB2E4."));
912
+ log2(kleur6.yellow("\u26A0 \uC2E4\uD589 \uC911\uC778 \uD504\uB85C\uC138\uC2A4\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."));
913
+ log2(kleur6.dim(" \uC774\uBBF8 \uC885\uB8CC\uB410\uAC70\uB098, Claude Code\uAC00 \uC544\uC9C1 \uC11C\uBC84\uB97C \uC2DC\uC791\uD558\uC9C0 \uC54A\uC740 \uC0C1\uD0DC\uC77C \uC218 \uC788\uC2B5\uB2C8\uB2E4."));
1668
914
  } else {
1669
- log3("");
1670
- log3(kleur7.green(`\u2713 ${serverName} \uC885\uB8CC\uB428 (${killed}\uAC1C \uD504\uB85C\uC138\uC2A4)`));
1671
- log3(kleur7.dim(" Claude Code\uAC00 \uB2E4\uC74C \uB3C4\uAD6C \uD638\uCD9C \uC2DC \uC790\uB3D9\uC73C\uB85C \uC7AC\uC5F0\uACB0\uD569\uB2C8\uB2E4."));
915
+ log2("");
916
+ log2(kleur6.green(`\u2713 ${serverName} \uC885\uB8CC\uB428 (${killed}\uAC1C \uD504\uB85C\uC138\uC2A4)`));
917
+ log2(kleur6.dim(" Claude Code\uAC00 \uB2E4\uC74C \uB3C4\uAD6C \uD638\uCD9C \uC2DC \uC790\uB3D9\uC73C\uB85C \uC7AC\uC5F0\uACB0\uD569\uB2C8\uB2E4."));
1672
918
  }
1673
- log3("");
1674
- log3(kleur7.cyan(" \uC5F0\uACB0 \uD655\uC778: Claude Code\uC5D0\uC11C /mcp \uC2E4\uD589"));
919
+ log2("");
920
+ log2(kleur6.cyan(" \uC5F0\uACB0 \uD655\uC778: Claude Code\uC5D0\uC11C /mcp \uC2E4\uD589"));
1675
921
  }
1676
922
 
1677
923
  // src/mcp-config.ts
1678
- import fs8 from "fs/promises";
1679
- import path8 from "path";
1680
- import os6 from "os";
1681
- import kleur8 from "kleur";
924
+ import fs3 from "fs/promises";
925
+ import path3 from "path";
926
+ import os2 from "os";
927
+ import kleur7 from "kleur";
1682
928
  var SERVER_NAME = "mimi-seed";
1683
929
  function tomlString(value) {
1684
930
  return JSON.stringify(value);
@@ -1710,35 +956,35 @@ function claudeMcpAddCommand(cfg) {
1710
956
  function printMcpSetup(cfg) {
1711
957
  process.stdout.write(
1712
958
  [
1713
- kleur8.bold("Claude Code MCP \uB4F1\uB85D \u2014 \uC544\uB798 \uD55C \uC904\uC744 \uADF8\uB300\uB85C \uC2E4\uD589\uD558\uC138\uC694:"),
1714
- kleur8.cyan(` ${claudeMcpAddCommand(cfg)}`),
1715
- kleur8.dim(` \u26A0 \uC2E4\uC81C \uD1A0\uD070\uC774 \uD3EC\uD568\uB41C \uBA85\uB839\uC785\uB2C8\uB2E4 (\uC178 \uD788\uC2A4\uD1A0\uB9AC\uC5D0 \uB0A8\uC74C). \uD1A0\uD070\uC740 ${CONFIG_LOCATION_HINT} \uC5D0\uB3C4 \uC800\uC7A5\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.`),
959
+ kleur7.bold("Claude Code MCP \uB4F1\uB85D \u2014 \uC544\uB798 \uD55C \uC904\uC744 \uADF8\uB300\uB85C \uC2E4\uD589\uD558\uC138\uC694:"),
960
+ kleur7.cyan(` ${claudeMcpAddCommand(cfg)}`),
961
+ kleur7.dim(` \u26A0 \uC2E4\uC81C \uD1A0\uD070\uC774 \uD3EC\uD568\uB41C \uBA85\uB839\uC785\uB2C8\uB2E4 (\uC178 \uD788\uC2A4\uD1A0\uB9AC\uC5D0 \uB0A8\uC74C). \uD1A0\uD070\uC740 ${CONFIG_LOCATION_HINT} \uC5D0\uB3C4 \uC800\uC7A5\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.`),
1716
962
  "",
1717
- kleur8.dim("Codex MCP \uB4F1\uB85D:"),
1718
- kleur8.dim(" mimi-seed mcp codex --write"),
1719
- kleur8.dim(" # \uB610\uB294 \uC218\uB3D9\uC73C\uB85C ~/.codex/config.toml \uC5D0 [mcp_servers.mimi-seed] \uCD94\uAC00")
963
+ kleur7.dim("Codex MCP \uB4F1\uB85D:"),
964
+ kleur7.dim(" mimi-seed mcp codex --write"),
965
+ kleur7.dim(" # \uB610\uB294 \uC218\uB3D9\uC73C\uB85C ~/.codex/config.toml \uC5D0 [mcp_servers.mimi-seed] \uCD94\uAC00")
1720
966
  ].join("\n") + "\n"
1721
967
  );
1722
968
  }
1723
969
  async function writeCodexMcpConfig(cfg) {
1724
- const configDir = path8.join(os6.homedir(), ".codex");
1725
- const configPath = path8.join(configDir, "config.toml");
1726
- await fs8.mkdir(configDir, { recursive: true });
970
+ const configDir = path3.join(os2.homedir(), ".codex");
971
+ const configPath = path3.join(configDir, "config.toml");
972
+ await fs3.mkdir(configDir, { recursive: true });
1727
973
  let current = "";
1728
974
  try {
1729
- current = await fs8.readFile(configPath, "utf8");
975
+ current = await fs3.readFile(configPath, "utf8");
1730
976
  } catch {
1731
977
  current = "";
1732
978
  }
1733
979
  const next = replaceTomlBlock(current, `mcp_servers.${SERVER_NAME}`, codexBlock(cfg));
1734
- await fs8.writeFile(configPath, next);
980
+ await fs3.writeFile(configPath, next);
1735
981
  return configPath;
1736
982
  }
1737
983
 
1738
984
  // src/release-manifest.ts
1739
- import fs9 from "fs/promises";
1740
- import path9 from "path";
1741
- var RELEASE_MANIFEST_RELATIVE_PATH = path9.join("docs", "releases.json");
985
+ import fs4 from "fs/promises";
986
+ import path4 from "path";
987
+ var RELEASE_MANIFEST_RELATIVE_PATH = path4.join("docs", "releases.json");
1742
988
  var RELEASE_MANIFEST_TEMPLATE = {
1743
989
  $schema: {
1744
990
  _comment: "Mimi Seed release notes SSOT. Store notes, App Store What's New, in-app announcements, and app-version rollout metadata can share this file.",
@@ -1764,14 +1010,14 @@ var RELEASE_MANIFEST_TEMPLATE = {
1764
1010
  versions: {}
1765
1011
  };
1766
1012
  async function ensureReleaseManifest(cwd) {
1767
- const filePath = path9.join(cwd, RELEASE_MANIFEST_RELATIVE_PATH);
1013
+ const filePath = path4.join(cwd, RELEASE_MANIFEST_RELATIVE_PATH);
1768
1014
  try {
1769
- await fs9.access(filePath);
1015
+ await fs4.access(filePath);
1770
1016
  return { path: filePath, created: false };
1771
1017
  } catch {
1772
1018
  }
1773
- await fs9.mkdir(path9.dirname(filePath), { recursive: true });
1774
- await fs9.writeFile(filePath, `${JSON.stringify(RELEASE_MANIFEST_TEMPLATE, null, 2)}
1019
+ await fs4.mkdir(path4.dirname(filePath), { recursive: true });
1020
+ await fs4.writeFile(filePath, `${JSON.stringify(RELEASE_MANIFEST_TEMPLATE, null, 2)}
1775
1021
  `, {
1776
1022
  mode: 420
1777
1023
  });
@@ -1781,62 +1027,62 @@ async function ensureReleaseManifest(cwd) {
1781
1027
  // src/index.ts
1782
1028
  var DEFAULT_WEB_BASE = process.env.MIMI_SEED_WEB_BASE ?? "https://mimi-seed.pryzm.gg";
1783
1029
  var DEFAULT_MCP_ENDPOINT = `${DEFAULT_WEB_BASE}/api/mcp`;
1784
- function log4(msg) {
1030
+ function log3(msg) {
1785
1031
  process.stdout.write(msg + "\n");
1786
1032
  }
1787
1033
  async function cmdInit(args) {
1788
1034
  const local = args.includes("--local");
1789
1035
  const cwd = process.cwd();
1790
- log4(kleur9.bold("Mimi Seed CLI \u2014 init"));
1791
- log4(kleur9.dim(cwd));
1792
- log4("");
1036
+ log3(kleur8.bold("Mimi Seed CLI \u2014 init"));
1037
+ log3(kleur8.dim(cwd));
1038
+ log3("");
1793
1039
  if (!await hasAnyProjectSignal(cwd)) {
1794
- log4(kleur9.yellow("\u26A0 package.json / app.json / android / ios \uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uADF8\uB798\uB3C4 \uC9C4\uD589\uD569\uB2C8\uB2E4."));
1040
+ log3(kleur8.yellow("\u26A0 package.json / app.json / android / ios \uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uADF8\uB798\uB3C4 \uC9C4\uD589\uD569\uB2C8\uB2E4."));
1795
1041
  }
1796
- log4("\u{1F50D} \uC571 \uAC10\uC9C0 \uC911...");
1042
+ log3("\u{1F50D} \uC571 \uAC10\uC9C0 \uC911...");
1797
1043
  const hints = await detectHints(cwd);
1798
1044
  if (hints.length === 0) {
1799
- log4(kleur9.yellow("\uAC10\uC9C0\uB41C \uC571 \uC5C6\uC74C. \uC6F9\uC5D0\uC11C \uC218\uB3D9 \uB4F1\uB85D \uAC00\uB2A5."));
1045
+ log3(kleur8.yellow("\uAC10\uC9C0\uB41C \uC571 \uC5C6\uC74C. \uC6F9\uC5D0\uC11C \uC218\uB3D9 \uB4F1\uB85D \uAC00\uB2A5."));
1800
1046
  } else {
1801
1047
  for (const h of hints) {
1802
1048
  const tag = [h.packageName && `android:${h.packageName}`, h.bundleId && `ios:${h.bundleId}`].filter(Boolean).join(" ");
1803
- log4(` \u2022 ${h.name ?? "(\uC774\uB984 \uBBF8\uC0C1)"} ${kleur9.dim(tag)}`);
1049
+ log3(` \u2022 ${h.name ?? "(\uC774\uB984 \uBBF8\uC0C1)"} ${kleur8.dim(tag)}`);
1804
1050
  }
1805
1051
  }
1806
- log4("");
1052
+ log3("");
1807
1053
  if (process.env.MIMI_SEED_TOKEN) {
1808
1054
  const cfg2 = await getEffectiveConfig();
1809
1055
  if (cfg2 && hints.length > 0) {
1810
- log4(kleur9.dim("CI \uBAA8\uB4DC: MIMI_SEED_TOKEN \uC0AC\uC6A9"));
1056
+ log3(kleur8.dim("CI \uBAA8\uB4DC: MIMI_SEED_TOKEN \uC0AC\uC6A9"));
1811
1057
  const payload = hints.map((h) => ({ name: h.name, packageName: h.packageName, bundleId: h.bundleId }));
1812
1058
  const result = await mcpCall(cfg2.endpoint, cfg2.token, "sync_apps", { hints: payload });
1813
1059
  if (result.isError) {
1814
- log4(kleur9.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
1060
+ log3(kleur8.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
1815
1061
  } else {
1816
- for (const line of result.text.split("\n")) log4(" " + line);
1062
+ for (const line of result.text.split("\n")) log3(" " + line);
1817
1063
  }
1818
1064
  }
1819
1065
  const manifest2 = await ensureReleaseManifest(cwd);
1820
- log4(kleur9.dim(` \uB9B4\uB9AC\uC988 \uB178\uD2B8 SSOT: ${manifest2.created ? "\uC0DD\uC131" : "\uD655\uC778"} docs/releases.json`));
1821
- log4(kleur9.bold("\u2713 \uC644\uB8CC (CI \uBAA8\uB4DC)"));
1066
+ log3(kleur8.dim(` \uB9B4\uB9AC\uC988 \uB178\uD2B8 SSOT: ${manifest2.created ? "\uC0DD\uC131" : "\uD655\uC778"} docs/releases.json`));
1067
+ log3(kleur8.bold("\u2713 \uC644\uB8CC (CI \uBAA8\uB4DC)"));
1822
1068
  return;
1823
1069
  }
1824
- log4("\u{1F510} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778 \uB300\uAE30...");
1825
- const hostName = os7.hostname().slice(0, 32);
1070
+ log3("\u{1F510} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778 \uB300\uAE30...");
1071
+ const hostName = os3.hostname().slice(0, 32);
1826
1072
  const name = `cli-${hostName}`;
1827
1073
  const { port, promise } = await awaitHandshake(5 * 60 * 1e3);
1828
1074
  const callback = `http://127.0.0.1:${port}/cb`;
1829
1075
  const connectUrl = `${DEFAULT_WEB_BASE}/cli/connect?callback=${encodeURIComponent(callback)}&name=${encodeURIComponent(name)}`;
1830
- log4(kleur9.dim(` ${connectUrl}`));
1076
+ log3(kleur8.dim(` ${connectUrl}`));
1831
1077
  await open(connectUrl);
1832
1078
  let handshake;
1833
1079
  try {
1834
1080
  handshake = await promise;
1835
1081
  } catch (e) {
1836
- log4(kleur9.red("\uC5F0\uACB0 \uC2E4\uD328: " + e.message));
1082
+ log3(kleur8.red("\uC5F0\uACB0 \uC2E4\uD328: " + e.message));
1837
1083
  process.exit(1);
1838
1084
  }
1839
- log4(kleur9.green("\u2713 \uD1A0\uD070 \uC218\uC2E0"));
1085
+ log3(kleur8.green("\u2713 \uD1A0\uD070 \uC218\uC2E0"));
1840
1086
  const cfg = {
1841
1087
  token: handshake.token,
1842
1088
  prefix: handshake.prefix,
@@ -1845,18 +1091,18 @@ async function cmdInit(args) {
1845
1091
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
1846
1092
  };
1847
1093
  await writeConfig(cfg);
1848
- log4(kleur9.dim(` \uC800\uC7A5\uB428: ${CONFIG_LOCATION}`));
1849
- log4("");
1094
+ log3(kleur8.dim(` \uC800\uC7A5\uB428: ${CONFIG_LOCATION}`));
1095
+ log3("");
1850
1096
  if (hints.length > 0) {
1851
- log4("\u{1F504} \uC571 \uB4F1\uB85D \uC911...");
1097
+ log3("\u{1F504} \uC571 \uB4F1\uB85D \uC911...");
1852
1098
  const payload = hints.map((h) => ({ name: h.name, packageName: h.packageName, bundleId: h.bundleId }));
1853
1099
  const result = await mcpCall(cfg.endpoint, cfg.token, "sync_apps", { hints: payload });
1854
1100
  if (result.isError) {
1855
- log4(kleur9.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
1101
+ log3(kleur8.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
1856
1102
  } else {
1857
- for (const line of result.text.split("\n")) log4(" " + line);
1103
+ for (const line of result.text.split("\n")) log3(" " + line);
1858
1104
  }
1859
- log4("");
1105
+ log3("");
1860
1106
  }
1861
1107
  const appLines = hints.flatMap((h) => {
1862
1108
  const parts = [
@@ -1900,118 +1146,137 @@ async function cmdInit(args) {
1900
1146
  "- `/mimi-seed:health` \u2014 \uC5F0\uACB0 \uC0C1\uD0DC \uBE60\uB978 \uD655\uC778",
1901
1147
  "- `/mimi-seed:review-inbox` \u2014 \uBBF8\uB2F5\uBCC0 \uB9AC\uBDF0 \uB2F5\uBCC0"
1902
1148
  ].join("\n");
1903
- const claudeDir = path10.join(cwd, ".claude");
1904
- const claudeAgentPath = path10.join(claudeDir, "mimi-seed.md");
1905
- if (!fs10.existsSync(claudeAgentPath)) {
1906
- fs10.mkdirSync(claudeDir, { recursive: true });
1907
- fs10.writeFileSync(claudeAgentPath, agentMd, { mode: 420 });
1908
- log4(kleur9.dim(` Claude \uC5D0\uC774\uC804\uD2B8 \uC124\uC815: .claude/mimi-seed.md`));
1909
- }
1910
- const codexAgentPath = path10.join(cwd, "AGENTS.md");
1911
- if (!fs10.existsSync(codexAgentPath)) {
1912
- fs10.writeFileSync(codexAgentPath, agentMd, { mode: 420 });
1913
- log4(kleur9.dim(` Codex \uC5D0\uC774\uC804\uD2B8 \uC124\uC815: AGENTS.md`));
1149
+ const claudeDir = path5.join(cwd, ".claude");
1150
+ const claudeAgentPath = path5.join(claudeDir, "mimi-seed.md");
1151
+ if (!fs5.existsSync(claudeAgentPath)) {
1152
+ fs5.mkdirSync(claudeDir, { recursive: true });
1153
+ fs5.writeFileSync(claudeAgentPath, agentMd, { mode: 420 });
1154
+ log3(kleur8.dim(` Claude \uC5D0\uC774\uC804\uD2B8 \uC124\uC815: .claude/mimi-seed.md`));
1155
+ }
1156
+ const codexAgentPath = path5.join(cwd, "AGENTS.md");
1157
+ if (!fs5.existsSync(codexAgentPath)) {
1158
+ fs5.writeFileSync(codexAgentPath, agentMd, { mode: 420 });
1159
+ log3(kleur8.dim(` Codex \uC5D0\uC774\uC804\uD2B8 \uC124\uC815: AGENTS.md`));
1914
1160
  }
1915
1161
  const manifest = await ensureReleaseManifest(cwd);
1916
- log4(kleur9.dim(` \uB9B4\uB9AC\uC988 \uB178\uD2B8 SSOT: ${manifest.created ? "\uC0DD\uC131" : "\uD655\uC778"} docs/releases.json`));
1917
- log4(kleur9.bold("\u2713 \uC900\uBE44 \uC644\uB8CC."));
1918
- log4("");
1919
- log4("Claude Code \uB610\uB294 Codex\uC5D0\uC11C \uC774\uB807\uAC8C \uBB3C\uC5B4\uBCF4\uC138\uC694:");
1920
- log4(kleur9.cyan(' "\uB0B4 \uC571 \uCD9C\uC2DC \uC900\uBE44\uB410\uC5B4?"'));
1921
- log4(kleur9.cyan(' "\uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC368\uC918"'));
1922
- log4(kleur9.cyan(' "\uB4F1\uB85D\uB41C \uC571 \uBAA9\uB85D \uBCF4\uC5EC\uC918"'));
1923
- log4("");
1924
- log4(`\uB300\uC2DC\uBCF4\uB4DC: ${kleur9.underline(DEFAULT_WEB_BASE + "/apps")}`);
1925
- log4("");
1162
+ log3(kleur8.dim(` \uB9B4\uB9AC\uC988 \uB178\uD2B8 SSOT: ${manifest.created ? "\uC0DD\uC131" : "\uD655\uC778"} docs/releases.json`));
1163
+ log3(kleur8.bold("\u2713 \uC900\uBE44 \uC644\uB8CC."));
1164
+ log3("");
1165
+ log3("Claude Code \uB610\uB294 Codex\uC5D0\uC11C \uC774\uB807\uAC8C \uBB3C\uC5B4\uBCF4\uC138\uC694:");
1166
+ log3(kleur8.cyan(' "\uB0B4 \uC571 \uCD9C\uC2DC \uC900\uBE44\uB410\uC5B4?"'));
1167
+ log3(kleur8.cyan(' "\uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC368\uC918"'));
1168
+ log3(kleur8.cyan(' "\uB4F1\uB85D\uB41C \uC571 \uBAA9\uB85D \uBCF4\uC5EC\uC918"'));
1169
+ log3("");
1170
+ log3(`\uB300\uC2DC\uBCF4\uB4DC: ${kleur8.underline(DEFAULT_WEB_BASE + "/apps")}`);
1171
+ log3("");
1926
1172
  printMcpSetup(cfg);
1927
1173
  if (local) {
1928
- log4("");
1929
- log4(kleur9.bold("\u2500\u2500 \uB85C\uCEEC MCP \uCD94\uAC00 \uC124\uC815 (--local) \u2500\u2500"));
1930
- log4(kleur9.dim("\uC6D0\uACA9 MCP(PAT, \uC77D\uAE30\xB7\uC9C4\uB2E8)\uB294 \uC704\uC5D0\uC11C \uB05D. \uB85C\uCEEC MCP\uB294 Google OAuth\uB85C \uC2A4\uD1A0\uC5B4 \uC4F0\uAE30 \uB3C4\uAD6C \uC804\uCCB4\uB97C \uC9C1\uC811 \uC2E4\uD589\uD569\uB2C8\uB2E4 (Node 20+)."));
1931
- log4("");
1932
- log4("1) Google \uB85C\uADF8\uC778 (Firebase / AdMob / Play / Ads):");
1174
+ log3("");
1175
+ log3(kleur8.bold("\u2500\u2500 \uB85C\uCEEC MCP \uCD94\uAC00 \uC124\uC815 (--local) \u2500\u2500"));
1176
+ log3(kleur8.dim("\uC6D0\uACA9 MCP(PAT, \uC77D\uAE30\xB7\uC9C4\uB2E8)\uB294 \uC704\uC5D0\uC11C \uB05D. \uB85C\uCEEC MCP\uB294 Google OAuth\uB85C \uC2A4\uD1A0\uC5B4 \uC4F0\uAE30 \uB3C4\uAD6C \uC804\uCCB4\uB97C \uC9C1\uC811 \uC2E4\uD589\uD569\uB2C8\uB2E4 (Node 20+)."));
1177
+ log3("");
1178
+ log3("1) Google \uB85C\uADF8\uC778 (Firebase / AdMob / Play / Ads):");
1933
1179
  await cmdAuth(["login"]);
1934
- log4("");
1935
- log4("2) \uB85C\uCEEC MCP \uC11C\uBC84 \uB4F1\uB85D (\uC6D0\uACA9 'mimi-seed' \uC640 \uBCC4\uAC1C):");
1936
- log4(kleur9.cyan(" claude mcp add mimi-seed-local -- npx -y @yoonion/mimi-seed-mcp"));
1937
- log4(kleur9.dim(' Codex: ~/.codex/config.toml \uC5D0 [mcp_servers.mimi-seed-local] command="npx", args=["-y","@yoonion/mimi-seed-mcp"]'));
1180
+ log3("");
1181
+ log3("2) \uB85C\uCEEC MCP \uC11C\uBC84 \uB4F1\uB85D (\uC6D0\uACA9 'mimi-seed' \uC640 \uBCC4\uAC1C):");
1182
+ log3(kleur8.cyan(" claude mcp add mimi-seed-local -- npx -y @yoonion/mimi-seed-mcp"));
1183
+ log3(kleur8.dim(' Codex: ~/.codex/config.toml \uC5D0 [mcp_servers.mimi-seed-local] command="npx", args=["-y","@yoonion/mimi-seed-mcp"]'));
1184
+ log3("");
1185
+ log3("3) \uB098\uBA38\uC9C0 \uACC4\uC815 \uC5F0\uACB0 (App Store / Play / Jenkins / CI / \uC18C\uC15C \u2026):");
1186
+ log3(kleur8.cyan(" mimi-seed setup"));
1187
+ log3(kleur8.dim(" \uAC01 \uD56D\uBAA9\uC5D0\uC11C [?] \uB97C \uB204\uB974\uBA74 \uD1A0\uD070 \uBC1C\uAE09 \uBC29\uBC95\uC744 \uC54C\uB824\uC90D\uB2C8\uB2E4."));
1938
1188
  }
1939
1189
  }
1940
1190
  async function cmdStatus() {
1941
1191
  const cfg = await getEffectiveConfig();
1942
1192
  if (!cfg) {
1943
- log4(kleur9.yellow("\uC5F0\uACB0\uB41C Mimi Seed \uACC4\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uC2E4\uD589."));
1193
+ log3(kleur8.yellow("\uC5F0\uACB0\uB41C Mimi Seed \uACC4\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uC2E4\uD589."));
1944
1194
  process.exit(1);
1945
1195
  }
1946
- log4(kleur9.bold("Mimi Seed \uC5F0\uACB0 \uC0C1\uD0DC"));
1947
- log4(` \uD1A0\uD070: ${cfg.prefix}\u2026 (${cfg.createdAt.slice(0, 10)})`);
1948
- log4(` \uC5D4\uB4DC\uD3EC\uC778\uD2B8: ${cfg.endpoint}`);
1949
- log4("");
1950
- log4("\u{1F4CB} \uC571 \uBAA9\uB85D:");
1196
+ log3(kleur8.bold("Mimi Seed \uC5F0\uACB0 \uC0C1\uD0DC"));
1197
+ log3(` \uD1A0\uD070: ${cfg.prefix}\u2026 (${cfg.createdAt.slice(0, 10)})`);
1198
+ log3(` \uC5D4\uB4DC\uD3EC\uC778\uD2B8: ${cfg.endpoint}`);
1199
+ log3("");
1200
+ log3("\u{1F4CB} \uC571 \uBAA9\uB85D:");
1951
1201
  const r = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
1952
1202
  if (r.isError) {
1953
- log4(kleur9.red("\uC870\uD68C \uC2E4\uD328: " + r.text));
1203
+ log3(kleur8.red("\uC870\uD68C \uC2E4\uD328: " + r.text));
1954
1204
  process.exit(1);
1955
1205
  }
1956
- for (const line of r.text.split("\n")) log4(" " + line);
1206
+ for (const line of r.text.split("\n")) log3(" " + line);
1957
1207
  }
1958
1208
  async function cmdLogout() {
1959
1209
  await deleteConfig();
1960
- log4(kleur9.green("\u2713 \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C \uC644\uB8CC."));
1961
- log4(kleur9.dim("\uC6F9\uC5D0\uC11C \uD1A0\uD070 \uD574\uC9C0: /workspace/api-tokens"));
1210
+ log3(kleur8.green("\u2713 \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C \uC644\uB8CC."));
1211
+ log3(kleur8.dim("\uC6F9\uC5D0\uC11C \uD1A0\uD070 \uD574\uC9C0: /workspace/api-tokens"));
1962
1212
  }
1963
1213
  async function cmdMcp(args) {
1964
1214
  const target = args[0] ?? "help";
1965
1215
  const cfg = await getEffectiveConfig();
1966
1216
  if (!cfg) {
1967
- log4(kleur9.yellow("\uC5F0\uACB0\uB41C Mimi Seed \uACC4\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uC2E4\uD589."));
1217
+ log3(kleur8.yellow("\uC5F0\uACB0\uB41C Mimi Seed \uACC4\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uC2E4\uD589."));
1968
1218
  process.exit(1);
1969
1219
  }
1970
1220
  if (target === "codex") {
1971
1221
  if (args.includes("--write")) {
1972
1222
  const configPath = await writeCodexMcpConfig(cfg);
1973
- log4(kleur9.green("\u2713 Codex MCP \uC124\uC815 \uC644\uB8CC"));
1974
- log4(kleur9.dim(` ${configPath}`));
1975
- log4(kleur9.dim(" \u26A0 config.toml\uC5D0 \uC2E4\uC81C \uD1A0\uD070\uC774 \uD3C9\uBB38\uC73C\uB85C \uC800\uC7A5\uB429\uB2C8\uB2E4 (\uD30C\uC77C \uAD8C\uD55C \uD655\uC778 \uAD8C\uC7A5)."));
1976
- log4("");
1977
- log4("Codex\uB97C \uC0C8\uB85C \uC5F4\uACE0 `/mcp` \uB610\uB294 `codex mcp list`\uB85C \uD655\uC778\uD558\uC138\uC694.");
1223
+ log3(kleur8.green("\u2713 Codex MCP \uC124\uC815 \uC644\uB8CC"));
1224
+ log3(kleur8.dim(` ${configPath}`));
1225
+ log3(kleur8.dim(" \u26A0 config.toml\uC5D0 \uC2E4\uC81C \uD1A0\uD070\uC774 \uD3C9\uBB38\uC73C\uB85C \uC800\uC7A5\uB429\uB2C8\uB2E4 (\uD30C\uC77C \uAD8C\uD55C \uD655\uC778 \uAD8C\uC7A5)."));
1226
+ log3("");
1227
+ log3("Codex\uB97C \uC0C8\uB85C \uC5F4\uACE0 `/mcp` \uB610\uB294 `codex mcp list`\uB85C \uD655\uC778\uD558\uC138\uC694.");
1978
1228
  return;
1979
1229
  }
1980
- log4(kleur9.bold("Codex MCP \uB4F1\uB85D"));
1981
- log4("");
1982
- log4("\uC790\uB3D9 \uB4F1\uB85D:");
1983
- log4(kleur9.cyan(" mimi-seed mcp codex --write"));
1984
- log4("");
1985
- log4("\uC218\uB3D9 \uB4F1\uB85D \uC608\uC2DC (~/.codex/config.toml) \u2014 \uC2E4\uC81C \uD1A0\uD070 \uD3EC\uD568:");
1986
- log4(`[mcp_servers.mimi-seed]
1230
+ log3(kleur8.bold("Codex MCP \uB4F1\uB85D"));
1231
+ log3("");
1232
+ log3("\uC790\uB3D9 \uB4F1\uB85D:");
1233
+ log3(kleur8.cyan(" mimi-seed mcp codex --write"));
1234
+ log3("");
1235
+ log3("\uC218\uB3D9 \uB4F1\uB85D \uC608\uC2DC (~/.codex/config.toml) \u2014 \uC2E4\uC81C \uD1A0\uD070 \uD3EC\uD568:");
1236
+ log3(`[mcp_servers.mimi-seed]
1987
1237
  url = "${cfg.endpoint}"
1988
1238
  enabled = true
1989
1239
  http_headers = { Authorization = "Bearer ${cfg.token}" }`);
1990
1240
  return;
1991
1241
  }
1992
1242
  if (target === "claude") {
1993
- log4(kleur9.bold("Claude Code MCP \uB4F1\uB85D \u2014 \uC544\uB798 \uD55C \uC904\uC744 \uADF8\uB300\uB85C \uC2E4\uD589\uD558\uC138\uC694:"));
1994
- log4(kleur9.cyan(` ${claudeMcpAddCommand(cfg)}`));
1995
- log4(kleur9.dim(" \u26A0 \uC2E4\uC81C \uD1A0\uD070\uC774 \uD3EC\uD568\uB41C \uBA85\uB839\uC785\uB2C8\uB2E4 (\uC178 \uD788\uC2A4\uD1A0\uB9AC\uC5D0 \uB0A8\uC74C). \uD1A0\uD070\uC740 ~/.mimi-seed/config.json \uC5D0\uB3C4 \uC800\uC7A5\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4."));
1243
+ log3(kleur8.bold("Claude Code MCP \uB4F1\uB85D \u2014 \uC544\uB798 \uD55C \uC904\uC744 \uADF8\uB300\uB85C \uC2E4\uD589\uD558\uC138\uC694:"));
1244
+ log3(kleur8.cyan(` ${claudeMcpAddCommand(cfg)}`));
1245
+ log3(kleur8.dim(" \u26A0 \uC2E4\uC81C \uD1A0\uD070\uC774 \uD3EC\uD568\uB41C \uBA85\uB839\uC785\uB2C8\uB2E4 (\uC178 \uD788\uC2A4\uD1A0\uB9AC\uC5D0 \uB0A8\uC74C). \uD1A0\uD070\uC740 ~/.mimi-seed/config.json \uC5D0\uB3C4 \uC800\uC7A5\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4."));
1996
1246
  return;
1997
1247
  }
1998
1248
  printMcpSetup(cfg);
1999
1249
  }
2000
1250
  var CMD_USAGE = {
2001
- init: `${kleur9.bold("mimi-seed init")} \u2014 \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
1251
+ init: `${kleur8.bold("mimi-seed init")} \u2014 \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
2002
1252
 
2003
1253
  \uC571 \uC790\uB3D9 \uAC10\uC9C0(Expo/Gradle/Info.plist/pbxproj) \u2192 \uBE0C\uB77C\uC6B0\uC800 PAT \uBC1C\uAE09(\uC6D0\uACA9 MCP) \u2192 \uC571 \uB4F1\uB85D
2004
1254
  + .claude/mimi-seed.md, AGENTS.md, docs/releases.json \uC0DD\uC131/\uD655\uC778.
2005
1255
 
2006
1256
  \uC635\uC158:
2007
1257
  --local \uCD94\uAC00\uB85C Google OAuth \uB85C\uADF8\uC778 + \uB85C\uCEEC MCP(\uC2A4\uD1A0\uC5B4 \uC4F0\uAE30 \uB3C4\uAD6C \uC804\uCCB4) \uB4F1\uB85D \uC548\uB0B4`,
2008
- status: `${kleur9.bold("mimi-seed status")} \u2014 \uC5F0\uACB0 \uC0C1\uD0DC + \uB4F1\uB85D \uC571 \uBAA9\uB85D. \uC635\uC158 \uC5C6\uC74C.`,
2009
- doctor: `${kleur9.bold("mimi-seed doctor")} \u2014 \uD658\uACBD \uC9C4\uB2E8 (\uD1A0\uD070\xB7Node\xB7Git\xB7\uD504\uB85C\uC81D\uD2B8\xB7CI). \uC635\uC158 \uC5C6\uC74C.`,
2010
- logout: `${kleur9.bold("mimi-seed logout")} \u2014 \uB85C\uCEEC \uC124\uC815(config.json) \uC0AD\uC81C. \uC635\uC158 \uC5C6\uC74C.`,
2011
- restart: `${kleur9.bold("mimi-seed restart")} \u2014 MCP \uC11C\uBC84 \uD504\uB85C\uC138\uC2A4 \uC7AC\uC2DC\uC791 (\uAE30\uBCF8: mimi-seed)
1258
+ setup: `${kleur8.bold("mimi-seed setup")} \u2014 \uAC00\uC9C4 \uACC4\uC815\uC744 \uD55C \uBC88\uC5D0 \uC5F0\uACB0 (\uC548\uB0B4\uD615 \uB9C8\uBC95\uC0AC)
1259
+
1260
+ \uC5F0\uACB0 \uC0C1\uD0DC\uB97C \uBA3C\uC800 \uBCF4\uC5EC\uC8FC\uACE0, \uC544\uC9C1 \uC5F0\uACB0\uB418\uC9C0 \uC54A\uC740 \uAC83\uB9CC \uC21C\uC11C\uB300\uB85C \uBB3C\uC5B4\uBCF8\uB2E4.
1261
+ \uAC01 \uD56D\uBAA9\uC5D0\uC11C [?] \uB97C \uB204\uB974\uBA74 "\uADF8 \uD1A0\uD070\uC744 \uC5B4\uB514\uC11C \uC5B4\uB5BB\uAC8C \uBC1C\uAE09\uBC1B\uB294\uC9C0"\uB97C \uC54C\uB824\uC900\uB2E4.
1262
+ \uC774\uBBF8 \uC5F0\uACB0\uB41C \uD56D\uBAA9\uC740 \uAC74\uB108\uB6F0\uBBC0\uB85C, \uC911\uAC04\uC5D0 \uADF8\uB9CC\uB450\uACE0 \uB098\uC911\uC5D0 \uB2E4\uC2DC \uC2E4\uD589\uD574\uB3C4 \uB41C\uB2E4.
1263
+
1264
+ \uC635\uC158:
1265
+ --only <ids> \uC9C0\uC815\uD55C \uC790\uACA9\uC99D\uBA85\uB9CC (\uC27C\uD45C \uAD6C\uBD84: oauth,appstore,jenkins \u2026)
1266
+ --reconnect <ids> \uC774\uBBF8 \uC5F0\uACB0\uB3FC \uC788\uC5B4\uB3C4 \uB2E4\uC2DC \uC124\uC815
1267
+ --platform android,ios \uD50C\uB7AB\uD3FC \uAC15\uC81C \uC9C0\uC815 (\uAE30\uBCF8: \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C \uC790\uB3D9 \uAC10\uC9C0)
1268
+ --yes, -y \uC544\uBB34\uAC83\uB3C4 \uBB3B\uC9C0 \uC54A\uACE0 \uC0C1\uD0DC\uD45C\uB9CC \uCD9C\uB825
1269
+ --non-interactive \uC704\uC640 \uB3D9\uC77C (CI \uC6A9)
1270
+ --fail-on-missing \uD544\uC218 \uC790\uACA9\uC99D\uBA85\uC774 \uC5C6\uC73C\uBA74 exit 1 (CI \uAC8C\uC774\uD2B8)
1271
+
1272
+ ${kleur8.dim("\uBE44TTY / CI \uD658\uACBD\uC5D0\uC11C\uB294 \uD504\uB86C\uD504\uD2B8 \uC5C6\uC774 \uC0C1\uD0DC\uD45C\uB9CC \uCD9C\uB825\uD55C\uB2E4.")}`,
1273
+ status: `${kleur8.bold("mimi-seed status")} \u2014 \uC5F0\uACB0 \uC0C1\uD0DC + \uB4F1\uB85D \uC571 \uBAA9\uB85D. \uC635\uC158 \uC5C6\uC74C.`,
1274
+ doctor: `${kleur8.bold("mimi-seed doctor")} \u2014 \uD658\uACBD \uC9C4\uB2E8 (\uD1A0\uD070\xB7Node\xB7Git\xB7\uD504\uB85C\uC81D\uD2B8\xB7CI). \uC635\uC158 \uC5C6\uC74C.`,
1275
+ logout: `${kleur8.bold("mimi-seed logout")} \u2014 \uB85C\uCEEC \uC124\uC815(config.json) \uC0AD\uC81C. \uC635\uC158 \uC5C6\uC74C.`,
1276
+ restart: `${kleur8.bold("mimi-seed restart")} \u2014 MCP \uC11C\uBC84 \uD504\uB85C\uC138\uC2A4 \uC7AC\uC2DC\uC791 (\uAE30\uBCF8: mimi-seed)
2012
1277
 
2013
1278
  mimi-seed restart [server-name]`,
2014
- notes: `${kleur9.bold("mimi-seed notes")} \u2014 \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 (git log \u2192 AI \u2192 \uB9C8\uCF13 \uC801\uC6A9)
1279
+ notes: `${kleur8.bold("mimi-seed notes")} \u2014 \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 (git log \u2192 AI \u2192 \uB9C8\uCF13 \uC801\uC6A9)
2015
1280
 
2016
1281
  \uC635\uC158:
2017
1282
  --from <ref> \uC2DC\uC791 \uCEE4\uBC0B/\uD0DC\uADF8 (\uAE30\uBCF8: \uCD5C\uC2E0 \uD0DC\uADF8)
@@ -2020,12 +1285,12 @@ var CMD_USAGE = {
2020
1285
  --apply \uC0DD\uC131 \uD6C4 \uC2A4\uD1A0\uC5B4\uC5D0 \uBC14\uB85C \uC801\uC6A9
2021
1286
  --no-interactive CI \uBAA8\uB4DC (\uD504\uB86C\uD504\uD2B8 \uC5C6\uC74C)
2022
1287
  --limit <n> \uCD5C\uB300 \uCEE4\uBC0B \uC218 (\uAE30\uBCF8: 30)`,
2023
- check: `${kleur9.bold("mimi-seed check")} \u2014 \uCD9C\uC2DC \uC804 Readiness \uC810\uAC80
1288
+ check: `${kleur8.bold("mimi-seed check")} \u2014 \uCD9C\uC2DC \uC804 Readiness \uC810\uAC80
2024
1289
 
2025
1290
  \uC635\uC158:
2026
1291
  --app <id> \uC571 ID \uC9C0\uC815
2027
1292
  --fail-on-blocker \uBE14\uB85C\uCEE4 \uC788\uC73C\uBA74 exit 1 (CI\uC6A9)`,
2028
- review: `${kleur9.bold("mimi-seed review")} \u2014 \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
1293
+ review: `${kleur8.bold("mimi-seed review")} \u2014 \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
2029
1294
 
2030
1295
  \uC635\uC158:
2031
1296
  --text <\uB0B4\uC6A9> \uB9AC\uBDF0 \uC6D0\uBB38 (\uBBF8\uC785\uB825 \uC2DC \uB300\uD654\uD615 \uD504\uB86C\uD504\uD2B8)
@@ -2037,7 +1302,7 @@ var CMD_USAGE = {
2037
1302
  --review-id <id> \uB9AC\uBDF0 ID (--apply \uC2DC \uD544\uC694)
2038
1303
  --package-name <p> \uD328\uD0A4\uC9C0\uBA85 (--apply \uC2DC \uD544\uC694)
2039
1304
  --no-interactive CI \uBAA8\uB4DC`,
2040
- deploy: `${kleur9.bold("mimi-seed deploy")} \u2014 \uC571 \uC790\uB3D9 \uBC30\uD3EC (CI \uBE4C\uB4DC \u2192 Play Store/App Store)
1305
+ deploy: `${kleur8.bold("mimi-seed deploy")} \u2014 \uC571 \uC790\uB3D9 \uBC30\uD3EC (CI \uBE4C\uB4DC \u2192 Play Store/App Store)
2041
1306
 
2042
1307
  \uC635\uC158:
2043
1308
  --platform android|ios \uBC30\uD3EC \uD50C\uB7AB\uD3FC (\uAE30\uBCF8: android)
@@ -2053,7 +1318,7 @@ var CMD_USAGE = {
2053
1318
  --workflow <file> GitHub workflow \uD30C\uC77C (\uC608: deploy.yml)
2054
1319
  --ref <branch|tag> GitHub/GitLab \uBE0C\uB79C\uCE58/\uD0DC\uADF8 (\uAE30\uBCF8: main)
2055
1320
  setup-jenkins / setup-github / setup-gitlab CI \uC124\uC815 \uB300\uD654\uD615 \uB4F1\uB85D`,
2056
- mcp: `${kleur9.bold("mimi-seed mcp")} \u2014 Claude/Codex MCP \uC5F0\uACB0
1321
+ mcp: `${kleur8.bold("mimi-seed mcp")} \u2014 Claude/Codex MCP \uC5F0\uACB0
2057
1322
 
2058
1323
  \uC11C\uBE0C\uBA85\uB839:
2059
1324
  mimi-seed mcp \uD604\uC7AC \uC124\uC815 + \uB4F1\uB85D \uC548\uB0B4
@@ -2064,34 +1329,37 @@ var CMD_USAGE = {
2064
1329
  function printCommandHelp(cmd) {
2065
1330
  const usage = CMD_USAGE[cmd];
2066
1331
  if (!usage) return false;
2067
- log4(usage);
1332
+ log3(usage);
2068
1333
  return true;
2069
1334
  }
2070
1335
  function printHelp() {
2071
- log4(`${kleur9.bold("mimi-seed")} \u2014 Claude Code/Codex\uC5D0\uC11C \uC571 \uCD9C\uC2DC \uC6B4\uC601
1336
+ log3(`${kleur8.bold("mimi-seed")} \u2014 Claude Code/Codex\uC5D0\uC11C \uC571 \uCD9C\uC2DC \uC6B4\uC601
2072
1337
 
2073
- ${kleur9.bold("\uBA85\uB839\uC5B4:")}
2074
- ${kleur9.cyan("mimi-seed init")} \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
2075
- ${kleur9.cyan("mimi-seed status")} \uC5F0\uACB0 \uC0C1\uD0DC + \uB4F1\uB85D \uC571 \uBAA9\uB85D
2076
- ${kleur9.cyan("mimi-seed auth")} \uB85C\uCEEC \uC778\uC99D (Google OAuth / App Store / Play / BigQuery)
2077
- ${kleur9.cyan("mimi-seed firebase")} Firebase \uC571 \uC0DD\uC131\xB7config \uB2E4\uC6B4\uB85C\uB4DC\xB7GA4 \uB9C1\uD06C
2078
- ${kleur9.cyan("mimi-seed admob")} AdMob \uACC4\uC815\xB7\uC571\xB7\uAD11\uACE0\uB2E8\uC704 \uC870\uD68C \uBC0F \uC0DD\uC131
2079
- ${kleur9.cyan("mimi-seed ga4")} GA4 property\xB7data stream \uC0DD\uC131\xB7\uC870\uD68C
2080
- ${kleur9.cyan("mimi-seed doctor")} \uD658\uACBD \uC9C4\uB2E8 (\uD1A0\uD070\xB7Git\xB7\uD504\uB85C\uC81D\uD2B8\xB7CI \uCCB4\uD06C)
2081
- ${kleur9.cyan("mimi-seed check")} \uCD9C\uC2DC \uC804 Readiness \uC810\uAC80
2082
- ${kleur9.cyan("mimi-seed notes")} \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 (git log \u2192 AI \u2192 \uB9C8\uCF13 \uC801\uC6A9)
2083
- ${kleur9.cyan("mimi-seed review")} \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
2084
- ${kleur9.cyan("mimi-seed deploy")} \uC571 \uC790\uB3D9 \uBC30\uD3EC (CI \u2192 Play Store/App Store)
2085
- ${kleur9.cyan("mimi-seed mcp")} Claude/Codex MCP \uC5F0\uACB0 \uC548\uB0B4 \uBC0F Codex \uC124\uC815 \uC4F0\uAE30
2086
- ${kleur9.cyan("mimi-seed restart")} MCP \uC11C\uBC84 \uD504\uB85C\uC138\uC2A4 \uC7AC\uC2DC\uC791 (\uAE30\uBCF8: mimi-seed)
2087
- ${kleur9.cyan("mimi-seed logout")} \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C
1338
+ ${kleur8.bold("\uBA85\uB839\uC5B4:")}
1339
+ ${kleur8.cyan("mimi-seed init")} \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
1340
+ ${kleur8.cyan("mimi-seed setup")} \uAC00\uC9C4 \uACC4\uC815\uC744 \uD55C \uBC88\uC5D0 \uC5F0\uACB0 (\uC548\uB0B4\uD615 \uB9C8\uBC95\uC0AC)
1341
+ ${kleur8.cyan("mimi-seed status")} \uC5F0\uACB0 \uC0C1\uD0DC + \uB4F1\uB85D \uC571 \uBAA9\uB85D
1342
+ ${kleur8.cyan("mimi-seed auth")} \uC790\uACA9\uC99D\uBA85 \uAC1C\uBCC4 \uC778\uC99D (Google / App Store / Play / Jenkins / CI \u2026)
1343
+ ${kleur8.cyan("mimi-seed firebase")} Firebase \uC571 \uC0DD\uC131\xB7config \uB2E4\uC6B4\uB85C\uB4DC\xB7GA4 \uB9C1\uD06C
1344
+ ${kleur8.cyan("mimi-seed admob")} AdMob \uACC4\uC815\xB7\uC571\xB7\uAD11\uACE0\uB2E8\uC704 \uC870\uD68C \uBC0F \uC0DD\uC131
1345
+ ${kleur8.cyan("mimi-seed ga4")} GA4 property\xB7data stream \uC0DD\uC131\xB7\uC870\uD68C
1346
+ ${kleur8.cyan("mimi-seed doctor")} \uD658\uACBD \uC9C4\uB2E8 (\uD1A0\uD070\xB7Git\xB7\uD504\uB85C\uC81D\uD2B8\xB7CI \uCCB4\uD06C)
1347
+ ${kleur8.cyan("mimi-seed check")} \uCD9C\uC2DC \uC804 Readiness \uC810\uAC80
1348
+ ${kleur8.cyan("mimi-seed notes")} \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 (git log \u2192 AI \u2192 \uB9C8\uCF13 \uC801\uC6A9)
1349
+ ${kleur8.cyan("mimi-seed review")} \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
1350
+ ${kleur8.cyan("mimi-seed deploy")} \uC571 \uC790\uB3D9 \uBC30\uD3EC (CI \u2192 Play Store/App Store)
1351
+ ${kleur8.cyan("mimi-seed mcp")} Claude/Codex MCP \uC5F0\uACB0 \uC548\uB0B4 \uBC0F Codex \uC124\uC815 \uC4F0\uAE30
1352
+ ${kleur8.cyan("mimi-seed restart")} MCP \uC11C\uBC84 \uD504\uB85C\uC138\uC2A4 \uC7AC\uC2DC\uC791 (\uAE30\uBCF8: mimi-seed)
1353
+ ${kleur8.cyan("mimi-seed logout")} \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C
2088
1354
 
2089
- ${kleur9.dim("\uAC01 \uBA85\uB839 \uC0C1\uC138 \uC635\uC158:")} ${kleur9.cyan("mimi-seed <command> --help")}
1355
+ ${kleur8.dim("\uAC01 \uBA85\uB839 \uC0C1\uC138 \uC635\uC158:")} ${kleur8.cyan("mimi-seed <command> --help")}
2090
1356
 
2091
- ${kleur9.bold("\uD658\uACBD\uBCC0\uC218:")}
1357
+ ${kleur8.bold("\uD658\uACBD\uBCC0\uC218:")}
2092
1358
  MIMI_SEED_TOKEN PAT \uD1A0\uD070 (CI/CD \uBB34\uC778\uC99D \uBAA8\uB4DC)
2093
1359
  MIMI_SEED_WEB_BASE \uC11C\uBC84 \uC8FC\uC18C (\uAE30\uBCF8: https://mimi-seed.pryzm.gg)
2094
1360
  ANTHROPIC_API_KEY AI \uB178\uD2B8 \uC0DD\uC131 \uD65C\uC131\uD654 (\uC120\uD0DD)
1361
+ MIMI_SEED_GOOGLE_CLIENT_ID / _SECRET
1362
+ \uC9C1\uC811 \uB9CC\uB4E0 Google OAuth \uD074\uB77C\uC774\uC5B8\uD2B8 \uC0AC\uC6A9 (\uBBF8\uC9C0\uC815 \uC2DC \uB85C\uADF8\uC778 \uB54C \uC6F9 \uCF58\uC194\uC5D0\uC11C \uBC1B\uC544\uC634)
2095
1363
  `);
2096
1364
  }
2097
1365
  async function main() {
@@ -2105,6 +1373,9 @@ async function main() {
2105
1373
  case "init":
2106
1374
  await cmdInit(restArgs);
2107
1375
  break;
1376
+ case "setup":
1377
+ await cmdSetup(restArgs);
1378
+ break;
2108
1379
  case "status":
2109
1380
  await cmdStatus();
2110
1381
  break;
@@ -2150,13 +1421,13 @@ async function main() {
2150
1421
  printHelp();
2151
1422
  break;
2152
1423
  default:
2153
- log4(kleur9.red(`\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839: ${cmd}`));
1424
+ log3(kleur8.red(`\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839: ${cmd}`));
2154
1425
  printHelp();
2155
1426
  process.exit(1);
2156
1427
  }
2157
1428
  } catch (e) {
2158
- log4(kleur9.red(`\uC624\uB958: ${e.message}`));
2159
- if (process.env.DEBUG) log4(e.stack ?? "");
1429
+ log3(kleur8.red(`\uC624\uB958: ${e.message}`));
1430
+ if (process.env.DEBUG) log3(e.stack ?? "");
2160
1431
  process.exit(1);
2161
1432
  }
2162
1433
  }