mimi-seed 0.6.0 → 0.7.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,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  mcpCall
4
- } from "./chunk-DW3LOLPJ.js";
4
+ } from "./chunk-QU2IDWJ2.js";
5
5
  import {
6
6
  CONFIG_LOCATION,
7
7
  CREDENTIALS,
@@ -15,16 +15,19 @@ import {
15
15
  detectHints,
16
16
  getEffectiveConfig,
17
17
  hasAnyProjectSignal,
18
- isLang,
19
18
  isSatisfied,
20
19
  migrateLegacyJenkins,
21
- resolveLang,
22
20
  runMcpBin,
23
- t,
24
21
  tryCredById,
25
- writeConfig,
22
+ writeConfig
23
+ } from "./chunk-47TS72IP.js";
24
+ import {
25
+ catalog,
26
+ isLang,
27
+ resolveLang,
28
+ t,
26
29
  writeSettings
27
- } from "./chunk-R3LS4FY2.js";
30
+ } from "./chunk-SZQFVTEW.js";
28
31
 
29
32
  // src/index.ts
30
33
  import os3 from "os";
@@ -35,6 +38,20 @@ import open from "open";
35
38
 
36
39
  // src/handshake.ts
37
40
  import http from "http";
41
+ var M = catalog(
42
+ {
43
+ htmlLang: "ko",
44
+ connected: "\u2713 Mimi Seed \uC5F0\uACB0 \uC644\uB8CC",
45
+ backToTerminal: "\uD130\uBBF8\uB110\uB85C \uB3CC\uC544\uAC00\uC138\uC694.",
46
+ timeout: (seconds) => `${seconds}\uCD08 \uC548\uC5D0 \uC5F0\uACB0\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`
47
+ },
48
+ {
49
+ htmlLang: "en",
50
+ connected: "\u2713 Mimi Seed connected",
51
+ backToTerminal: "You can go back to your terminal.",
52
+ timeout: (seconds) => `Not connected within ${seconds}s.`
53
+ }
54
+ );
38
55
  async function awaitHandshake(timeoutMs) {
39
56
  let resolve;
40
57
  let reject;
@@ -63,9 +80,9 @@ async function awaitHandshake(timeoutMs) {
63
80
  }
64
81
  res.setHeader("Content-Type", "text/html; charset=utf-8");
65
82
  res.end(`<!doctype html>
66
- <html lang="ko"><body style="font-family:system-ui;padding:40px;max-width:480px;margin:auto">
67
- <h2>\u2713 Mimi Seed \uC5F0\uACB0 \uC644\uB8CC</h2>
68
- <p>\uD130\uBBF8\uB110\uB85C \uB3CC\uC544\uAC00\uC138\uC694.</p>
83
+ <html lang="${M().htmlLang}"><body style="font-family:system-ui;padding:40px;max-width:480px;margin:auto">
84
+ <h2>${M().connected}</h2>
85
+ <p>${M().backToTerminal}</p>
69
86
  <script>setTimeout(()=>window.close(),1000)</script>
70
87
  </body></html>`);
71
88
  resolve({ token, prefix });
@@ -76,7 +93,7 @@ async function awaitHandshake(timeoutMs) {
76
93
  const port = server.address().port;
77
94
  const timer = setTimeout(() => {
78
95
  server.close();
79
- reject(new Error(`${timeoutMs / 1e3}\uCD08 \uC548\uC5D0 \uC5F0\uACB0\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`));
96
+ reject(new Error(M().timeout(timeoutMs / 1e3)));
80
97
  }, timeoutMs);
81
98
  promise.finally(() => clearTimeout(timer));
82
99
  return { port, promise };
@@ -267,6 +284,70 @@ async function cmdDoctor() {
267
284
 
268
285
  // src/check.ts
269
286
  import kleur2 from "kleur";
287
+ var M2 = catalog(
288
+ {
289
+ noAccount: "\uC5F0\uACB0\uB41C \uACC4\uC815 \uC5C6\uC74C. `mimi-seed init` \uC2E4\uD589.\n",
290
+ title: "mimi-seed check \u2014 \uCD9C\uC2DC \uC804 \uC810\uAC80\n\n",
291
+ appsFailed: (msg) => `\uC571 \uBAA9\uB85D \uC870\uD68C \uC2E4\uD328: ${msg}
292
+ `,
293
+ noApps: "\uB4F1\uB85D\uB41C \uC571\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uD6C4 \uC571\uC744 \uB4F1\uB85D\uD558\uC138\uC694.\n",
294
+ app: (name) => `\uC571: ${name}
295
+
296
+ `,
297
+ appsParseFailed: (raw) => `\uC571 \uBAA9\uB85D \uD30C\uC2F1 \uC2E4\uD328: ${raw}
298
+ `,
299
+ scoring: "\u{1F4CA} Readiness \uC810\uC218 \uACC4\uC0B0 \uC911...\n",
300
+ scoreFailed: (msg) => `\uC810\uC218 \uC870\uD68C \uC2E4\uD328: ${msg}
301
+ `,
302
+ score: (bar) => `
303
+ \uC810\uC218: ${bar}
304
+
305
+ `,
306
+ byModule: "\u2500\u2500 \uBAA8\uB4C8\uBCC4 \u2500\u2500\n",
307
+ blockers: "\u{1F6AB} \uBE14\uB85C\uCEE4:\n",
308
+ warnings: "\u26A0 \uACBD\uACE0:\n",
309
+ stepIntegration: (url) => `\uC5F0\uACB0 \uC9C4\uB2E8 ${url}`,
310
+ stepCopy: (url) => `\uBB38\uAD6C \uBCF4\uAC15 mimi-seed notes \uB610\uB294 ${url}`,
311
+ stepScreenshot: (url) => `\uC2A4\uD06C\uB9B0\uC0F7 ${url}`,
312
+ stepChecklist: (url) => `\uCCB4\uD06C\uB9AC\uC2A4\uD2B8 ${url}`,
313
+ nextSteps: "\u2192 \uB2E4\uC74C \uB2E8\uACC4:\n",
314
+ preview: " \uBBF8\uB9AC\uBCF4\uAE30: mimi-seed deploy --dry-run\n\n",
315
+ ready: "\u2713 \uCD9C\uC2DC \uC900\uBE44 \uC644\uB8CC!\n",
316
+ notReady: (score) => `\uC544\uC9C1 ${score}/100 \u2014 \uC704 \uB2E4\uC74C \uB2E8\uACC4\uB97C \uC9C4\uD589\uD558\uC138\uC694.
317
+ `
318
+ },
319
+ {
320
+ noAccount: "No account connected. Run `mimi-seed init`.\n",
321
+ title: "mimi-seed check \u2014 pre-launch check\n\n",
322
+ appsFailed: (msg) => `Failed to list apps: ${msg}
323
+ `,
324
+ noApps: "No apps registered. Run `mimi-seed init`, then register an app.\n",
325
+ app: (name) => `App: ${name}
326
+
327
+ `,
328
+ appsParseFailed: (raw) => `Failed to parse the app list: ${raw}
329
+ `,
330
+ scoring: "\u{1F4CA} Computing the readiness score...\n",
331
+ scoreFailed: (msg) => `Failed to fetch the score: ${msg}
332
+ `,
333
+ score: (bar) => `
334
+ Score: ${bar}
335
+
336
+ `,
337
+ byModule: "\u2500\u2500 By module \u2500\u2500\n",
338
+ blockers: "\u{1F6AB} Blockers:\n",
339
+ warnings: "\u26A0 Warnings:\n",
340
+ stepIntegration: (url) => `Connections ${url}`,
341
+ stepCopy: (url) => `Copy mimi-seed notes or ${url}`,
342
+ stepScreenshot: (url) => `Screenshots ${url}`,
343
+ stepChecklist: (url) => `Checklist ${url}`,
344
+ nextSteps: "\u2192 Next steps:\n",
345
+ preview: " Preview: mimi-seed deploy --dry-run\n\n",
346
+ ready: "\u2713 Ready to launch!\n",
347
+ notReady: (score) => `Still ${score}/100 \u2014 work through the next steps above.
348
+ `
349
+ }
350
+ );
270
351
  function parseArgs(argv) {
271
352
  const args = { failOnBlocker: false };
272
353
  for (let i = 0; i < argv.length; i++) {
@@ -291,14 +372,13 @@ async function cmdCheck(argv) {
291
372
  const args = parseArgs(argv);
292
373
  const cfg = await getEffectiveConfig();
293
374
  if (!cfg) {
294
- process.stdout.write(kleur2.red("\uC5F0\uACB0\uB41C \uACC4\uC815 \uC5C6\uC74C. `mimi-seed init` \uC2E4\uD589.\n"));
375
+ process.stdout.write(kleur2.red(M2().noAccount));
295
376
  process.exit(1);
296
377
  }
297
- process.stdout.write(kleur2.bold("mimi-seed check \u2014 \uCD9C\uC2DC \uC804 \uC810\uAC80\n\n"));
378
+ process.stdout.write(kleur2.bold(M2().title));
298
379
  const appsResult = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
299
380
  if (appsResult.isError) {
300
- process.stdout.write(kleur2.red(`\uC571 \uBAA9\uB85D \uC870\uD68C \uC2E4\uD328: ${appsResult.text}
301
- `));
381
+ process.stdout.write(kleur2.red(M2().appsFailed(appsResult.text)));
302
382
  process.exit(1);
303
383
  }
304
384
  let appId = args.appId;
@@ -306,36 +386,29 @@ async function cmdCheck(argv) {
306
386
  try {
307
387
  const apps = JSON.parse(appsResult.text);
308
388
  if (!Array.isArray(apps) || apps.length === 0) {
309
- process.stdout.write(kleur2.yellow("\uB4F1\uB85D\uB41C \uC571\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uD6C4 \uC571\uC744 \uB4F1\uB85D\uD558\uC138\uC694.\n"));
389
+ process.stdout.write(kleur2.yellow(M2().noApps));
310
390
  process.exit(0);
311
391
  }
312
392
  appId = apps[0].id;
313
- process.stdout.write(kleur2.dim(`\uC571: ${apps[0].name ?? appId}
314
-
315
- `));
393
+ process.stdout.write(kleur2.dim(M2().app(apps[0].name ?? appId)));
316
394
  } catch {
317
- process.stdout.write(kleur2.red(`\uC571 \uBAA9\uB85D \uD30C\uC2F1 \uC2E4\uD328: ${appsResult.text.slice(0, 80)}
318
- `));
395
+ process.stdout.write(kleur2.red(M2().appsParseFailed(appsResult.text.slice(0, 80))));
319
396
  process.exit(1);
320
397
  }
321
398
  }
322
- process.stdout.write("\u{1F4CA} Readiness \uC810\uC218 \uACC4\uC0B0 \uC911...\n");
399
+ process.stdout.write(M2().scoring);
323
400
  const readinessResult = await mcpCall(cfg.endpoint, cfg.token, "get_readiness", { app_id: appId });
324
401
  if (readinessResult.isError) {
325
- process.stdout.write(kleur2.red(`\uC810\uC218 \uC870\uD68C \uC2E4\uD328: ${readinessResult.text}
326
- `));
402
+ process.stdout.write(kleur2.red(M2().scoreFailed(readinessResult.text)));
327
403
  process.exit(1);
328
404
  }
329
405
  let hasBlocker = false;
330
406
  try {
331
407
  const data = JSON.parse(readinessResult.text);
332
408
  const score = data.score ?? 0;
333
- process.stdout.write(`
334
- \uC810\uC218: ${renderScore(score)}
335
-
336
- `);
409
+ process.stdout.write(M2().score(renderScore(score)));
337
410
  if (data.modules) {
338
- process.stdout.write(kleur2.dim("\u2500\u2500 \uBAA8\uB4C8\uBCC4 \u2500\u2500\n"));
411
+ process.stdout.write(kleur2.dim(M2().byModule));
339
412
  for (const [key, val] of Object.entries(data.modules)) {
340
413
  const label = MODULE_LABELS[key] ?? key;
341
414
  const color = val >= 25 ? kleur2.green : val >= 10 ? kleur2.yellow : kleur2.red;
@@ -346,13 +419,13 @@ async function cmdCheck(argv) {
346
419
  }
347
420
  if (data.blockers?.length) {
348
421
  hasBlocker = true;
349
- process.stdout.write(kleur2.bold("\u{1F6AB} \uBE14\uB85C\uCEE4:\n"));
422
+ process.stdout.write(kleur2.bold(M2().blockers));
350
423
  for (const b of data.blockers) process.stdout.write(` ${kleur2.red("\u2022")} ${b}
351
424
  `);
352
425
  process.stdout.write("\n");
353
426
  }
354
427
  if (data.warnings?.length) {
355
- process.stdout.write(kleur2.bold("\u26A0 \uACBD\uACE0:\n"));
428
+ process.stdout.write(kleur2.bold(M2().warnings));
356
429
  for (const w of data.warnings) process.stdout.write(` ${kleur2.yellow("\u2022")} ${w}
357
430
  `);
358
431
  process.stdout.write("\n");
@@ -360,22 +433,19 @@ async function cmdCheck(argv) {
360
433
  const base = `${cfg.webBase}/apps/${appId}`;
361
434
  const nextSteps = [];
362
435
  if (data.modules) {
363
- if ((data.modules.integration ?? 25) < 25) nextSteps.push(`\uC5F0\uACB0 \uC9C4\uB2E8 ${base}/integration`);
364
- if ((data.modules.copy ?? 25) < 25) nextSteps.push(`\uBB38\uAD6C \uBCF4\uAC15 mimi-seed notes \uB610\uB294 ${base}/copy`);
365
- if ((data.modules.screenshot ?? 25) < 25) nextSteps.push(`\uC2A4\uD06C\uB9B0\uC0F7 ${base}/screenshots`);
366
- if ((data.modules.checklist ?? 25) < 25) nextSteps.push(`\uCCB4\uD06C\uB9AC\uC2A4\uD2B8 ${base}/launch`);
436
+ if ((data.modules.integration ?? 25) < 25) nextSteps.push(M2().stepIntegration(`${base}/integration`));
437
+ if ((data.modules.copy ?? 25) < 25) nextSteps.push(M2().stepCopy(`${base}/copy`));
438
+ if ((data.modules.screenshot ?? 25) < 25) nextSteps.push(M2().stepScreenshot(`${base}/screenshots`));
439
+ if ((data.modules.checklist ?? 25) < 25) nextSteps.push(M2().stepChecklist(`${base}/launch`));
367
440
  }
368
441
  if (nextSteps.length) {
369
- process.stdout.write(kleur2.bold("\u2192 \uB2E4\uC74C \uB2E8\uACC4:\n"));
442
+ process.stdout.write(kleur2.bold(M2().nextSteps));
370
443
  for (const s of nextSteps) process.stdout.write(` ${kleur2.cyan("\u2022")} ${s}
371
444
  `);
372
- process.stdout.write(kleur2.dim(` \uBBF8\uB9AC\uBCF4\uAE30: mimi-seed deploy --dry-run
373
-
374
- `));
445
+ process.stdout.write(kleur2.dim(M2().preview));
375
446
  }
376
447
  if (!hasBlocker) {
377
- process.stdout.write(score >= 80 ? kleur2.green("\u2713 \uCD9C\uC2DC \uC900\uBE44 \uC644\uB8CC!\n") : kleur2.yellow(`\uC544\uC9C1 ${score}/100 \u2014 \uC704 \uB2E4\uC74C \uB2E8\uACC4\uB97C \uC9C4\uD589\uD558\uC138\uC694.
378
- `));
448
+ process.stdout.write(score >= 80 ? kleur2.green(M2().ready) : kleur2.yellow(M2().notReady(score)));
379
449
  }
380
450
  } catch {
381
451
  for (const line of readinessResult.text.split("\n")) process.stdout.write(" " + line + "\n");
@@ -387,6 +457,122 @@ async function cmdCheck(argv) {
387
457
  import { createInterface } from "readline/promises";
388
458
  import Anthropic from "@anthropic-ai/sdk";
389
459
  import kleur3 from "kleur";
460
+ var M3 = catalog(
461
+ {
462
+ // Claude 프롬프트
463
+ localeHint: (l) => `"${l}": "\uD574\uB2F9 \uC5B8\uC5B4\uB85C \uBC88\uC5ED\uB41C \uAC04\uACB0\uD55C \uBC84\uC804"`,
464
+ system: "\uC571 \uC2A4\uD1A0\uC5B4 \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC804\uBB38 \uCE74\uD53C\uB77C\uC774\uD130\uC785\uB2C8\uB2E4. \uCEE4\uBC0B \uB0B4\uC5ED\uC744 \uC0AC\uC6A9\uC790 \uCE5C\uD654\uC801\uC778 \uC5B8\uC5B4\uB85C \uBCC0\uD658\uD569\uB2C8\uB2E4. \uD56D\uC0C1 \uC720\uD6A8\uD55C JSON\uC73C\uB85C\uB9CC \uC751\uB2F5\uD558\uC138\uC694.",
465
+ userPrompt: (commitsText, localeList) => `\uB2E4\uC74C \uCEE4\uBC0B \uB0B4\uC5ED\uC73C\uB85C \uB9B4\uB9AC\uC988 \uB178\uD2B8\uB97C 3\uAC00\uC9C0 \uD1A4\uC73C\uB85C \uC791\uC131\uD558\uC138\uC694:
466
+
467
+ ${commitsText}
468
+
469
+ JSON:
470
+ {
471
+ "concise": "\uAC04\uACB0\uD55C \uBC84\uC804 (3\uC904 \uC774\uB0B4, \uBD88\uB9BF)",
472
+ "detailed": "\uC0C1\uC138 \uBC84\uC804 (5\uAC1C \uC774\uB0B4, \uBD88\uB9BF)",
473
+ "marketing": "\uB9C8\uCF00\uD305 \uBC84\uC804 (\uC5F4\uC815\uC801 \uD1A4)",
474
+ "localized": {
475
+ ${localeList}
476
+ }
477
+ }`,
478
+ parseFailed: "AI \uC751\uB2F5 \uD30C\uC2F1 \uC2E4\uD328",
479
+ // 템플릿 폴백
480
+ marketingTemplate: (items) => `\uC0C8\uB85C\uC6B4 \uC5C5\uB370\uC774\uD2B8\uAC00 \uC900\uBE44\uB410\uC2B5\uB2C8\uB2E4!
481
+
482
+ ${items}
483
+
484
+ \uC9C0\uAE08 \uBC14\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.`,
485
+ // cmdNotes
486
+ title: "mimi-seed notes \u2014 \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131\n\n",
487
+ notGitRepo: "Git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4.\n",
488
+ range: (from, to) => `\uBC94\uC704: ${from} \u2192 ${to}
489
+ `,
490
+ recentCommits: (limit) => `\uCD5C\uADFC ${limit}\uAC1C \uCEE4\uBC0B
491
+ `,
492
+ noCommits: "\uCEE4\uBC0B\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\n",
493
+ analyzing: (n) => `\uCEE4\uBC0B ${n}\uAC1C \uBD84\uC11D \uC911...
494
+
495
+ `,
496
+ generating: "\u{1F916} Claude AI\uB85C \uC0DD\uC131 \uC911...\n",
497
+ aiFailed: (msg) => `AI \uC0DD\uC131 \uC2E4\uD328, \uD15C\uD50C\uB9BF \uC0AC\uC6A9: ${msg}
498
+ `,
499
+ noApiKey: "ANTHROPIC_API_KEY \uC5C6\uC74C \u2014 \uC790\uB3D9 \uD3EC\uB9F7\uD305 \uC0AC\uC6A9\n",
500
+ noApiKeyHint: "AI \uC0DD\uC131 \uD65C\uC131\uD654: export ANTHROPIC_API_KEY=sk-ant-...\n\n",
501
+ hdrConcise: "\u2500\u2500\u2500 \uAC04\uACB0\uD55C \uBC84\uC804 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
502
+ hdrDetailed: "\u2500\u2500\u2500 \uC0C1\uC138 \uBC84\uC804 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
503
+ hdrMarketing: "\u2500\u2500\u2500 \uB9C8\uCF00\uD305 \uBC84\uC804 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
504
+ hdrLocalized: "\u2500\u2500\u2500 \uB2E4\uAD6D\uC5B4 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
505
+ needAccount: "Mimi Seed \uACC4\uC815 \uC5F0\uACB0 \uD544\uC694. `mimi-seed init` \uC2E4\uD589.\n",
506
+ choosePrompt: "\uC801\uC6A9\uD560 \uBC84\uC804 [1=\uAC04\uACB0/2=\uC0C1\uC138/3=\uB9C8\uCF00\uD305/Enter=\uAC74\uB108\uB700]: ",
507
+ skipped: "\uAC74\uB108\uB700.\n",
508
+ listAppsFailed: (msg) => `\uC571 \uBAA9\uB85D \uC870\uD68C \uC2E4\uD328: ${msg}
509
+ `,
510
+ noApps: "\uB4F1\uB85D\uB41C \uC571\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.\n",
511
+ toneToAllLocales: "\uC120\uD0DD\uD55C \uD1A4\uC744 \uBAA8\uB4E0 \uB85C\uCF00\uC77C\uC5D0 \uC801\uC6A9\uD569\uB2C8\uB2E4 (\uC790\uB3D9 \uBC88\uC5ED \uBBF8\uC801\uC6A9).\n",
512
+ applying: "Play Store\uC5D0 \uC801\uC6A9 \uC911...\n",
513
+ applyFailed: (locale, msg) => `${locale} \uC801\uC6A9 \uC2E4\uD328: ${msg}
514
+ `,
515
+ applied: (locale) => `\u2713 ${locale} \uC801\uC6A9\uB428
516
+ `
517
+ },
518
+ {
519
+ // Claude prompt
520
+ localeHint: (l) => `"${l}": "the concise version, translated into that language"`,
521
+ system: "You are an expert app store release-notes copywriter. You turn commit history into user-friendly language. Always respond with valid JSON only.",
522
+ userPrompt: (commitsText, localeList) => `Write release notes in 3 tones from the following commit history:
523
+
524
+ ${commitsText}
525
+
526
+ JSON:
527
+ {
528
+ "concise": "concise version (3 bullets max)",
529
+ "detailed": "detailed version (5 bullets max)",
530
+ "marketing": "marketing version (enthusiastic tone)",
531
+ "localized": {
532
+ ${localeList}
533
+ }
534
+ }`,
535
+ parseFailed: "Failed to parse the AI response",
536
+ // Template fallback
537
+ marketingTemplate: (items) => `A new update is here!
538
+
539
+ ${items}
540
+
541
+ Update now.`,
542
+ // cmdNotes
543
+ title: "mimi-seed notes \u2014 generate release notes\n\n",
544
+ notGitRepo: "Not a git repository.\n",
545
+ range: (from, to) => `Range: ${from} \u2192 ${to}
546
+ `,
547
+ recentCommits: (limit) => `Last ${limit} commit(s)
548
+ `,
549
+ noCommits: "No commits found.\n",
550
+ analyzing: (n) => `Analyzing ${n} commit(s)...
551
+
552
+ `,
553
+ generating: "\u{1F916} Generating with Claude AI...\n",
554
+ aiFailed: (msg) => `AI generation failed, falling back to the template: ${msg}
555
+ `,
556
+ noApiKey: "No ANTHROPIC_API_KEY \u2014 using automatic formatting\n",
557
+ noApiKeyHint: "Enable AI generation: export ANTHROPIC_API_KEY=sk-ant-...\n\n",
558
+ hdrConcise: "\u2500\u2500\u2500 Concise \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
559
+ hdrDetailed: "\u2500\u2500\u2500 Detailed \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
560
+ hdrMarketing: "\u2500\u2500\u2500 Marketing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
561
+ hdrLocalized: "\u2500\u2500\u2500 Localized \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
562
+ needAccount: "A Mimi Seed account is required. Run `mimi-seed init`.\n",
563
+ choosePrompt: "Which version? [1=concise/2=detailed/3=marketing/Enter=skip]: ",
564
+ skipped: "Skipped.\n",
565
+ listAppsFailed: (msg) => `Failed to list apps: ${msg}
566
+ `,
567
+ noApps: "No apps registered.\n",
568
+ toneToAllLocales: "Applying the selected tone to every locale (no automatic translation).\n",
569
+ applying: "Applying to Play Store...\n",
570
+ applyFailed: (locale, msg) => `${locale} failed to apply: ${msg}
571
+ `,
572
+ applied: (locale) => `\u2713 ${locale} applied
573
+ `
574
+ }
575
+ );
390
576
  function parseArgs2(argv) {
391
577
  const args = { to: "HEAD", locales: ["ko", "en-US"], apply: false, noInteractive: false, limit: 30 };
392
578
  for (let i = 0; i < argv.length; i++) {
@@ -407,31 +593,19 @@ async function promptUser(question) {
407
593
  }
408
594
  async function generateWithClaude(commitsText, locales) {
409
595
  const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
410
- const localeList = locales.map((l) => `"${l}": "\uD574\uB2F9 \uC5B8\uC5B4\uB85C \uBC88\uC5ED\uB41C \uAC04\uACB0\uD55C \uBC84\uC804"`).join(",\n ");
596
+ const localeList = locales.map((l) => M3().localeHint(l)).join(",\n ");
411
597
  const response = await client.messages.create({
412
598
  model: "claude-haiku-4-5-20251001",
413
599
  max_tokens: 1500,
414
- system: "\uC571 \uC2A4\uD1A0\uC5B4 \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC804\uBB38 \uCE74\uD53C\uB77C\uC774\uD130\uC785\uB2C8\uB2E4. \uCEE4\uBC0B \uB0B4\uC5ED\uC744 \uC0AC\uC6A9\uC790 \uCE5C\uD654\uC801\uC778 \uC5B8\uC5B4\uB85C \uBCC0\uD658\uD569\uB2C8\uB2E4. \uD56D\uC0C1 \uC720\uD6A8\uD55C JSON\uC73C\uB85C\uB9CC \uC751\uB2F5\uD558\uC138\uC694.",
600
+ system: M3().system,
415
601
  messages: [{
416
602
  role: "user",
417
- content: `\uB2E4\uC74C \uCEE4\uBC0B \uB0B4\uC5ED\uC73C\uB85C \uB9B4\uB9AC\uC988 \uB178\uD2B8\uB97C 3\uAC00\uC9C0 \uD1A4\uC73C\uB85C \uC791\uC131\uD558\uC138\uC694:
418
-
419
- ${commitsText}
420
-
421
- JSON:
422
- {
423
- "concise": "\uAC04\uACB0\uD55C \uBC84\uC804 (3\uC904 \uC774\uB0B4, \uBD88\uB9BF)",
424
- "detailed": "\uC0C1\uC138 \uBC84\uC804 (5\uAC1C \uC774\uB0B4, \uBD88\uB9BF)",
425
- "marketing": "\uB9C8\uCF00\uD305 \uBC84\uC804 (\uC5F4\uC815\uC801 \uD1A4)",
426
- "localized": {
427
- ${localeList}
428
- }
429
- }`
603
+ content: M3().userPrompt(commitsText, localeList)
430
604
  }]
431
605
  });
432
606
  const text = response.content[0].type === "text" ? response.content[0].text : "";
433
607
  const match = text.match(/\{[\s\S]*\}/);
434
- if (!match) throw new Error("AI \uC751\uB2F5 \uD30C\uC2F1 \uC2E4\uD328");
608
+ if (!match) throw new Error(M3().parseFailed);
435
609
  return JSON.parse(match[0]);
436
610
  }
437
611
  function generateTemplate(commits, locales) {
@@ -441,11 +615,7 @@ function generateTemplate(commits, locales) {
441
615
  });
442
616
  const concise = items.slice(0, 3).join("\n");
443
617
  const detailed = items.join("\n");
444
- const marketing = `\uC0C8\uB85C\uC6B4 \uC5C5\uB370\uC774\uD2B8\uAC00 \uC900\uBE44\uB410\uC2B5\uB2C8\uB2E4!
445
-
446
- ${items.slice(0, 5).join("\n")}
447
-
448
- \uC9C0\uAE08 \uBC14\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.`;
618
+ const marketing = M3().marketingTemplate(items.slice(0, 5).join("\n"));
449
619
  const localized = Object.fromEntries(locales.map((l) => [l, concise]));
450
620
  return { concise, detailed, marketing, localized };
451
621
  }
@@ -460,46 +630,43 @@ function parseFirstApp(text) {
460
630
  async function cmdNotes(argv) {
461
631
  const args = parseArgs2(argv);
462
632
  const cwd = process.cwd();
463
- process.stdout.write(kleur3.bold("mimi-seed notes \u2014 \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131\n\n"));
633
+ process.stdout.write(kleur3.bold(M3().title));
464
634
  if (!isGitRepo(cwd)) {
465
- process.stdout.write(kleur3.red("Git \uC800\uC7A5\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4.\n"));
635
+ process.stdout.write(kleur3.red(M3().notGitRepo));
466
636
  process.exit(1);
467
637
  }
468
638
  const latestTag = getLatestTag(cwd);
469
639
  const fromRef = args.from ?? latestTag ?? void 0;
470
- process.stdout.write(kleur3.dim(fromRef ? `\uBC94\uC704: ${fromRef} \u2192 ${args.to}
471
- ` : `\uCD5C\uADFC ${args.limit}\uAC1C \uCEE4\uBC0B
472
- `));
640
+ process.stdout.write(
641
+ kleur3.dim(fromRef ? M3().range(fromRef, args.to) : M3().recentCommits(args.limit))
642
+ );
473
643
  const commits = getGitLog(cwd, { from: fromRef, to: args.to, limit: args.limit });
474
644
  if (commits.length === 0) {
475
- process.stdout.write(kleur3.yellow("\uCEE4\uBC0B\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\n"));
645
+ process.stdout.write(kleur3.yellow(M3().noCommits));
476
646
  process.exit(0);
477
647
  }
478
- process.stdout.write(kleur3.dim(`\uCEE4\uBC0B ${commits.length}\uAC1C \uBD84\uC11D \uC911...
479
-
480
- `));
648
+ process.stdout.write(kleur3.dim(M3().analyzing(commits.length)));
481
649
  let result;
482
650
  if (process.env.ANTHROPIC_API_KEY) {
483
- process.stdout.write("\u{1F916} Claude AI\uB85C \uC0DD\uC131 \uC911...\n");
651
+ process.stdout.write(M3().generating);
484
652
  try {
485
653
  result = await generateWithClaude(formatCommitsForPrompt(commits), args.locales);
486
654
  } catch (e) {
487
- process.stdout.write(kleur3.yellow(`AI \uC0DD\uC131 \uC2E4\uD328, \uD15C\uD50C\uB9BF \uC0AC\uC6A9: ${e.message}
488
- `));
655
+ process.stdout.write(kleur3.yellow(M3().aiFailed(e.message)));
489
656
  result = generateTemplate(commits, args.locales);
490
657
  }
491
658
  } else {
492
- process.stdout.write(kleur3.dim("ANTHROPIC_API_KEY \uC5C6\uC74C \u2014 \uC790\uB3D9 \uD3EC\uB9F7\uD305 \uC0AC\uC6A9\n") + kleur3.dim("AI \uC0DD\uC131 \uD65C\uC131\uD654: export ANTHROPIC_API_KEY=sk-ant-...\n\n"));
659
+ process.stdout.write(kleur3.dim(M3().noApiKey) + kleur3.dim(M3().noApiKeyHint));
493
660
  result = generateTemplate(commits, args.locales);
494
661
  }
495
- process.stdout.write(kleur3.bold("\u2500\u2500\u2500 \uAC04\uACB0\uD55C \uBC84\uC804 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
662
+ process.stdout.write(kleur3.bold(M3().hdrConcise));
496
663
  process.stdout.write(result.concise + "\n\n");
497
- process.stdout.write(kleur3.bold("\u2500\u2500\u2500 \uC0C1\uC138 \uBC84\uC804 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
664
+ process.stdout.write(kleur3.bold(M3().hdrDetailed));
498
665
  process.stdout.write(result.detailed + "\n\n");
499
- process.stdout.write(kleur3.bold("\u2500\u2500\u2500 \uB9C8\uCF00\uD305 \uBC84\uC804 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
666
+ process.stdout.write(kleur3.bold(M3().hdrMarketing));
500
667
  process.stdout.write(result.marketing + "\n\n");
501
668
  if (Object.keys(result.localized).length > 0) {
502
- process.stdout.write(kleur3.bold("\u2500\u2500\u2500 \uB2E4\uAD6D\uC5B4 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
669
+ process.stdout.write(kleur3.bold(M3().hdrLocalized));
503
670
  for (const [locale, text] of Object.entries(result.localized)) {
504
671
  process.stdout.write(kleur3.dim(`[${locale}]
505
672
  `) + text + "\n\n");
@@ -509,15 +676,15 @@ async function cmdNotes(argv) {
509
676
  if (!args.apply && !shouldPrompt) return;
510
677
  const cfg = await getEffectiveConfig();
511
678
  if (!cfg) {
512
- process.stdout.write(kleur3.yellow("Mimi Seed \uACC4\uC815 \uC5F0\uACB0 \uD544\uC694. `mimi-seed init` \uC2E4\uD589.\n"));
679
+ process.stdout.write(kleur3.yellow(M3().needAccount));
513
680
  return;
514
681
  }
515
682
  let selectedText = result.concise;
516
683
  let userSelectedTone = false;
517
684
  if (shouldPrompt) {
518
- const choice = await promptUser("\uC801\uC6A9\uD560 \uBC84\uC804 [1=\uAC04\uACB0/2=\uC0C1\uC138/3=\uB9C8\uCF00\uD305/Enter=\uAC74\uB108\uB700]: ");
685
+ const choice = await promptUser(M3().choosePrompt);
519
686
  if (!choice) {
520
- process.stdout.write(kleur3.dim("\uAC74\uB108\uB700.\n"));
687
+ process.stdout.write(kleur3.dim(M3().skipped));
521
688
  return;
522
689
  }
523
690
  if (choice === "2") selectedText = result.detailed;
@@ -526,19 +693,18 @@ async function cmdNotes(argv) {
526
693
  }
527
694
  const appsResult = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
528
695
  if (appsResult.isError) {
529
- process.stdout.write(kleur3.red(`\uC571 \uBAA9\uB85D \uC870\uD68C \uC2E4\uD328: ${appsResult.text}
530
- `));
696
+ process.stdout.write(kleur3.red(M3().listAppsFailed(appsResult.text)));
531
697
  return;
532
698
  }
533
699
  const app = parseFirstApp(appsResult.text);
534
700
  if (!app) {
535
- process.stdout.write(kleur3.yellow("\uB4F1\uB85D\uB41C \uC571\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.\n"));
701
+ process.stdout.write(kleur3.yellow(M3().noApps));
536
702
  return;
537
703
  }
538
704
  if (userSelectedTone && args.locales.length > 1) {
539
- process.stdout.write(kleur3.dim("\uC120\uD0DD\uD55C \uD1A4\uC744 \uBAA8\uB4E0 \uB85C\uCF00\uC77C\uC5D0 \uC801\uC6A9\uD569\uB2C8\uB2E4 (\uC790\uB3D9 \uBC88\uC5ED \uBBF8\uC801\uC6A9).\n"));
705
+ process.stdout.write(kleur3.dim(M3().toneToAllLocales));
540
706
  }
541
- process.stdout.write("Play Store\uC5D0 \uC801\uC6A9 \uC911...\n");
707
+ process.stdout.write(M3().applying);
542
708
  for (const locale of args.locales) {
543
709
  const text = userSelectedTone ? selectedText : result.localized[locale] ?? selectedText;
544
710
  const r = await mcpCall(cfg.endpoint, cfg.token, "apply_release_notes", {
@@ -547,9 +713,9 @@ async function cmdNotes(argv) {
547
713
  locale,
548
714
  text
549
715
  });
550
- process.stdout.write(r.isError ? kleur3.red(`${locale} \uC801\uC6A9 \uC2E4\uD328: ${r.text}
551
- `) : kleur3.green(`\u2713 ${locale} \uC801\uC6A9\uB428
552
- `));
716
+ process.stdout.write(
717
+ r.isError ? kleur3.red(M3().applyFailed(locale, r.text)) : kleur3.green(M3().applied(locale))
718
+ );
553
719
  }
554
720
  }
555
721
 
@@ -557,6 +723,120 @@ async function cmdNotes(argv) {
557
723
  import { createInterface as createInterface2 } from "readline/promises";
558
724
  import Anthropic2 from "@anthropic-ai/sdk";
559
725
  import kleur4 from "kleur";
726
+ var M4 = catalog(
727
+ {
728
+ toneGuides: {
729
+ friendly: "\uCE5C\uADFC\uD558\uACE0 \uB530\uB73B\uD558\uAC8C. \uC774\uBAA8\uC9C0 1~2\uAC1C \uC0AC\uC6A9. \uAC10\uC0AC \uC778\uC0AC\uB85C \uC2DC\uC791.",
730
+ professional: "\uC815\uC911\uD558\uACE0 \uACF5\uC2DD\uC801\uC73C\uB85C. \uBB38\uC81C\uB97C \uC778\uC815\uD558\uACE0 \uD574\uACB0\uCC45\uC744 \uC81C\uC2DC.",
731
+ empathetic: "\uACF5\uAC10\uC744 \uBA3C\uC800 \uD45C\uD604. \uBD88\uD3B8\uC744 \uCDA9\uBD84\uD788 \uC778\uC815\uD55C \uD6C4 \uD574\uACB0 \uC758\uC9C0\uB97C \uBCF4\uC5EC\uC90C.",
732
+ brief: "2~3\uBB38\uC7A5\uC73C\uB85C \uAC04\uACB0\uD558\uAC8C. \uD575\uC2EC \uC751\uB2F5\uB9CC."
733
+ },
734
+ sentimentPrompts: {
735
+ positive: "\uAE0D\uC815\uC801\uC778 \uB9AC\uBDF0. \uAC10\uC0AC \uC778\uC0AC\uC640 \uD568\uAED8 \uC55E\uC73C\uB85C\uB3C4 \uC88B\uC740 \uACBD\uD5D8\uC744 \uC81C\uACF5\uD558\uACA0\uB2E4\uB294 \uC758\uC9C0\uB97C \uD45C\uD604.",
736
+ negative: "\uBD80\uC815\uC801\uC778 \uB9AC\uBDF0. \uBA3C\uC800 \uBD88\uD3B8\uC744 \uC0AC\uACFC\uD558\uACE0, \uBB38\uC81C \uD574\uACB0 \uC758\uC9C0\uB97C \uBCF4\uC5EC\uC918. \uAC00\uB2A5\uD558\uBA74 \uC9C0\uC6D0 \uCC44\uB110\uB85C \uC720\uB3C4.",
737
+ neutral: "\uC911\uB9BD\uC801\uC778 \uB9AC\uBDF0. \uD53C\uB4DC\uBC31\uC5D0 \uAC10\uC0AC\uD558\uACE0 \uAC1C\uC120 \uB178\uB825\uC744 \uC57D\uC18D.",
738
+ bug_report: "\uBC84\uADF8 \uB9AC\uD3EC\uD2B8. \uBB38\uC81C\uB97C \uD655\uC778\uD588\uC74C\uC744 \uC54C\uB9AC\uACE0, \uC218\uC815 \uC608\uC815\uC784\uC744 \uC804\uB2EC.",
739
+ feature_request: "\uAE30\uB2A5 \uC694\uCCAD. \uD53C\uB4DC\uBC31\uC5D0 \uAC10\uC0AC\uD558\uACE0, \uAC80\uD1A0\uD558\uACA0\uB2E4\uACE0 \uC57D\uC18D."
740
+ },
741
+ localeNames: { ko: "\uD55C\uAD6D\uC5B4", en: "\uC601\uC5B4", ja: "\uC77C\uBCF8\uC5B4", "en-US": "\uC601\uC5B4", "zh-TW": "\uBC88\uCCB4\uC911\uAD6D\uC5B4" },
742
+ // Claude 프롬프트
743
+ starsLabel: (stars, rating) => `\uBCC4\uC810: ${stars} (${rating}/5)
744
+ `,
745
+ defaultDeveloper: "\uAC1C\uBC1C\uD300",
746
+ defaultApp: "\uC571",
747
+ system: (langName, toneGuide, developer) => `\uC571 \uAC1C\uBC1C\uC790\uB97C \uB300\uC2E0\uD574 \uC2A4\uD1A0\uC5B4 \uB9AC\uBDF0\uC5D0 \uB2F5\uBCC0\uD558\uB294 \uC804\uBB38\uAC00. ${langName}\uB85C 150\uC790 \uC774\uB0B4\uB85C \uB2F5\uBCC0. ${toneGuide} \uAC1C\uBC1C\uC790 \uC774\uB984: ${developer}`,
748
+ userPrompt: (app, stars, text, sentimentPrompt) => `\uC571: ${app}
749
+ ${stars}\uB9AC\uBDF0: "${text}"
750
+
751
+ \uC9C0\uCE68: ${sentimentPrompt}
752
+
753
+ JSON\uC73C\uB85C\uB9CC \uC751\uB2F5: { "reply": "\uB2F5\uBCC0 \uD14D\uC2A4\uD2B8" }`,
754
+ parseFailed: "AI \uC751\uB2F5 \uD30C\uC2F1 \uC2E4\uD328",
755
+ // cmdReview
756
+ title: "mimi-seed review \u2014 \uB9AC\uBDF0 \uB2F5\uBCC0 \uC0DD\uC131\n\n",
757
+ noApiKey: "ANTHROPIC_API_KEY\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.\n",
758
+ noApiKeyHint: "\uD65C\uC131\uD654: export ANTHROPIC_API_KEY=sk-ant-...\n",
759
+ textRequired: "--text <\uB9AC\uBDF0 \uB0B4\uC6A9> \uC774 \uD544\uC694\uD569\uB2C8\uB2E4.\n",
760
+ enterReview: "\uB9AC\uBDF0 \uB0B4\uC6A9\uC744 \uC785\uB825\uD558\uC138\uC694 (\uC5EC\uB7EC \uC904: Ctrl+D\uB85C \uC644\uB8CC):\n",
761
+ noInput: "\uC785\uB825 \uC5C6\uC74C. \uC885\uB8CC.\n",
762
+ meta: (stars, tone, language) => `\uBCC4\uC810: ${stars} tone: ${tone} \uC5B8\uC5B4: ${language}
763
+
764
+ `,
765
+ generating: "\u{1F916} Claude AI\uB85C \uB2F5\uBCC0 \uC0DD\uC131 \uC911...\n",
766
+ hdrReply: "\u2500\u2500\u2500 \uC81C\uC548 \uB2F5\uBCC0 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
767
+ draftWarning: "\n\u26A0 \uC774 \uB2F5\uBCC0\uC740 AI\uAC00 \uC0DD\uC131\uD55C \uCD08\uC548\uC785\uB2C8\uB2E4. \uAC8C\uC2DC \uC804 \uBC18\uB4DC\uC2DC \uAC80\uD1A0\uD558\uC138\uC694.\n\n",
768
+ postPrompt: "\uC774 \uB2F5\uBCC0\uC744 Play Store\uC5D0 \uAC8C\uC2DC\uD560\uAE4C\uC694? [y/N]: ",
769
+ skipped: "\uAC74\uB108\uB700.\n",
770
+ needAccount: "Mimi Seed \uACC4\uC815 \uC5F0\uACB0 \uD544\uC694. `mimi-seed init` \uC2E4\uD589.\n",
771
+ idsRequired: "--review-id \uC640 --package-name \uC774 \uD544\uC694\uD569\uB2C8\uB2E4.\n",
772
+ packagePrompt: "\uD328\uD0A4\uC9C0\uBA85 (\uC608: com.example.app): ",
773
+ reviewIdPrompt: "\uB9AC\uBDF0 ID: ",
774
+ notEnoughInfo: "\uC815\uBCF4 \uBD80\uC871. \uCDE8\uC18C.\n",
775
+ posting: "Play Store\uC5D0 \uAC8C\uC2DC \uC911...\n",
776
+ postFailed: (msg) => `\uAC8C\uC2DC \uC2E4\uD328: ${msg}
777
+ `,
778
+ posted: "\u2713 \uB2F5\uBCC0 \uAC8C\uC2DC \uC644\uB8CC\n"
779
+ },
780
+ {
781
+ toneGuides: {
782
+ friendly: "Warm and friendly. Use 1-2 emoji. Open with a thank-you.",
783
+ professional: "Polite and formal. Acknowledge the issue and offer a solution.",
784
+ empathetic: "Lead with empathy. Fully acknowledge the frustration, then show a commitment to fix it.",
785
+ brief: "Keep it to 2-3 sentences. Only the essential response."
786
+ },
787
+ sentimentPrompts: {
788
+ positive: "A positive review. Say thank you and commit to keeping the experience great.",
789
+ negative: "A negative review. Apologize for the trouble first, then show a commitment to fixing it. Point to a support channel if you can.",
790
+ neutral: "A neutral review. Thank them for the feedback and promise to keep improving.",
791
+ bug_report: "A bug report. Confirm the issue is known and say a fix is coming.",
792
+ feature_request: "A feature request. Thank them for the feedback and promise to consider it."
793
+ },
794
+ localeNames: {
795
+ ko: "Korean",
796
+ en: "English",
797
+ ja: "Japanese",
798
+ "en-US": "English",
799
+ "zh-TW": "Traditional Chinese"
800
+ },
801
+ // Claude prompt
802
+ starsLabel: (stars, rating) => `Rating: ${stars} (${rating}/5)
803
+ `,
804
+ defaultDeveloper: "the dev team",
805
+ defaultApp: "the app",
806
+ system: (langName, toneGuide, developer) => `You reply to app store reviews on behalf of the app's developer. Reply in ${langName}, in 150 characters or fewer. ${toneGuide} Developer name: ${developer}`,
807
+ userPrompt: (app, stars, text, sentimentPrompt) => `App: ${app}
808
+ ${stars}Review: "${text}"
809
+
810
+ Guidance: ${sentimentPrompt}
811
+
812
+ Respond with JSON only: { "reply": "the reply text" }`,
813
+ parseFailed: "Failed to parse the AI response",
814
+ // cmdReview
815
+ title: "mimi-seed review \u2014 generate a review reply\n\n",
816
+ noApiKey: "ANTHROPIC_API_KEY is not set.\n",
817
+ noApiKeyHint: "Enable it: export ANTHROPIC_API_KEY=sk-ant-...\n",
818
+ textRequired: "--text <review body> is required.\n",
819
+ enterReview: "Paste the review (multi-line: finish with Ctrl+D):\n",
820
+ noInput: "Nothing entered. Exiting.\n",
821
+ meta: (stars, tone, language) => `Rating: ${stars} tone: ${tone} language: ${language}
822
+
823
+ `,
824
+ generating: "\u{1F916} Generating a reply with Claude AI...\n",
825
+ hdrReply: "\u2500\u2500\u2500 Suggested reply \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
826
+ draftWarning: "\n\u26A0 This reply is an AI-generated draft. Always review it before posting.\n\n",
827
+ postPrompt: "Post this reply to Play Store? [y/N]: ",
828
+ skipped: "Skipped.\n",
829
+ needAccount: "A Mimi Seed account is required. Run `mimi-seed init`.\n",
830
+ idsRequired: "--review-id and --package-name are required.\n",
831
+ packagePrompt: "Package name (e.g. com.example.app): ",
832
+ reviewIdPrompt: "Review ID: ",
833
+ notEnoughInfo: "Not enough information. Cancelled.\n",
834
+ posting: "Posting to Play Store...\n",
835
+ postFailed: (msg) => `Failed to post: ${msg}
836
+ `,
837
+ posted: "\u2713 Reply posted\n"
838
+ }
839
+ );
560
840
  function parseArgs3(argv) {
561
841
  const args = { tone: "friendly", language: "ko", apply: false, noInteractive: false };
562
842
  for (let i = 0; i < argv.length; i++) {
@@ -579,19 +859,6 @@ async function promptUser2(question) {
579
859
  rl.close();
580
860
  return answer.trim();
581
861
  }
582
- var TONE_GUIDES = {
583
- friendly: "\uCE5C\uADFC\uD558\uACE0 \uB530\uB73B\uD558\uAC8C. \uC774\uBAA8\uC9C0 1~2\uAC1C \uC0AC\uC6A9. \uAC10\uC0AC \uC778\uC0AC\uB85C \uC2DC\uC791.",
584
- professional: "\uC815\uC911\uD558\uACE0 \uACF5\uC2DD\uC801\uC73C\uB85C. \uBB38\uC81C\uB97C \uC778\uC815\uD558\uACE0 \uD574\uACB0\uCC45\uC744 \uC81C\uC2DC.",
585
- empathetic: "\uACF5\uAC10\uC744 \uBA3C\uC800 \uD45C\uD604. \uBD88\uD3B8\uC744 \uCDA9\uBD84\uD788 \uC778\uC815\uD55C \uD6C4 \uD574\uACB0 \uC758\uC9C0\uB97C \uBCF4\uC5EC\uC90C.",
586
- brief: "2~3\uBB38\uC7A5\uC73C\uB85C \uAC04\uACB0\uD558\uAC8C. \uD575\uC2EC \uC751\uB2F5\uB9CC."
587
- };
588
- var SENTIMENT_PROMPTS = {
589
- positive: "\uAE0D\uC815\uC801\uC778 \uB9AC\uBDF0. \uAC10\uC0AC \uC778\uC0AC\uC640 \uD568\uAED8 \uC55E\uC73C\uB85C\uB3C4 \uC88B\uC740 \uACBD\uD5D8\uC744 \uC81C\uACF5\uD558\uACA0\uB2E4\uB294 \uC758\uC9C0\uB97C \uD45C\uD604.",
590
- negative: "\uBD80\uC815\uC801\uC778 \uB9AC\uBDF0. \uBA3C\uC800 \uBD88\uD3B8\uC744 \uC0AC\uACFC\uD558\uACE0, \uBB38\uC81C \uD574\uACB0 \uC758\uC9C0\uB97C \uBCF4\uC5EC\uC918. \uAC00\uB2A5\uD558\uBA74 \uC9C0\uC6D0 \uCC44\uB110\uB85C \uC720\uB3C4.",
591
- neutral: "\uC911\uB9BD\uC801\uC778 \uB9AC\uBDF0. \uD53C\uB4DC\uBC31\uC5D0 \uAC10\uC0AC\uD558\uACE0 \uAC1C\uC120 \uB178\uB825\uC744 \uC57D\uC18D.",
592
- bug_report: "\uBC84\uADF8 \uB9AC\uD3EC\uD2B8. \uBB38\uC81C\uB97C \uD655\uC778\uD588\uC74C\uC744 \uC54C\uB9AC\uACE0, \uC218\uC815 \uC608\uC815\uC784\uC744 \uC804\uB2EC.",
593
- feature_request: "\uAE30\uB2A5 \uC694\uCCAD. \uD53C\uB4DC\uBC31\uC5D0 \uAC10\uC0AC\uD558\uACE0, \uAC80\uD1A0\uD558\uACA0\uB2E4\uACE0 \uC57D\uC18D."
594
- };
595
862
  function detectSentiment(text) {
596
863
  const lower = text.toLowerCase();
597
864
  if (["\uBC84\uADF8", "\uC624\uB958", "\uC548\uB428", "crash", "bug", "error", "broken"].some((w) => lower.includes(w))) return "bug_report";
@@ -602,103 +869,97 @@ function detectSentiment(text) {
602
869
  }
603
870
  async function generateReply(opts) {
604
871
  const client = new Anthropic2({ apiKey: process.env.ANTHROPIC_API_KEY });
872
+ const m = M4();
873
+ const toneGuides = m.toneGuides;
874
+ const sentimentPrompts = m.sentimentPrompts;
875
+ const localeNames = m.localeNames;
605
876
  const sentiment = detectSentiment(opts.text);
606
- const toneGuide = TONE_GUIDES[opts.tone] ?? TONE_GUIDES.friendly;
607
- const sentimentPrompt = SENTIMENT_PROMPTS[sentiment] ?? SENTIMENT_PROMPTS.neutral;
608
- const stars = opts.rating !== void 0 ? `\uBCC4\uC810: ${"\u2605".repeat(opts.rating)}${"\u2606".repeat(5 - opts.rating)} (${opts.rating}/5)
609
- ` : "";
610
- const LOCALE_NAMES = { ko: "\uD55C\uAD6D\uC5B4", en: "\uC601\uC5B4", ja: "\uC77C\uBCF8\uC5B4", "en-US": "\uC601\uC5B4", "zh-TW": "\uBC88\uCCB4\uC911\uAD6D\uC5B4" };
611
- const langName = LOCALE_NAMES[opts.language] ?? opts.language;
877
+ const toneGuide = toneGuides[opts.tone] ?? toneGuides.friendly;
878
+ const sentimentPrompt = sentimentPrompts[sentiment] ?? sentimentPrompts.neutral;
879
+ const stars = opts.rating !== void 0 ? m.starsLabel(`${"\u2605".repeat(opts.rating)}${"\u2606".repeat(5 - opts.rating)}`, opts.rating) : "";
880
+ const langName = localeNames[opts.language] ?? opts.language;
612
881
  const response = await client.messages.create({
613
882
  model: "claude-haiku-4-5-20251001",
614
883
  max_tokens: 500,
615
- system: `\uC571 \uAC1C\uBC1C\uC790\uB97C \uB300\uC2E0\uD574 \uC2A4\uD1A0\uC5B4 \uB9AC\uBDF0\uC5D0 \uB2F5\uBCC0\uD558\uB294 \uC804\uBB38\uAC00. ${langName}\uB85C 150\uC790 \uC774\uB0B4\uB85C \uB2F5\uBCC0. ${toneGuide} \uAC1C\uBC1C\uC790 \uC774\uB984: ${opts.developerName ?? "\uAC1C\uBC1C\uD300"}`,
884
+ system: m.system(langName, toneGuide, opts.developerName ?? m.defaultDeveloper),
616
885
  messages: [{
617
886
  role: "user",
618
- content: `\uC571: ${opts.appName ?? "\uC571"}
619
- ${stars}\uB9AC\uBDF0: "${opts.text}"
620
-
621
- \uC9C0\uCE68: ${sentimentPrompt}
622
-
623
- JSON\uC73C\uB85C\uB9CC \uC751\uB2F5: { "reply": "\uB2F5\uBCC0 \uD14D\uC2A4\uD2B8" }`
887
+ content: m.userPrompt(opts.appName ?? m.defaultApp, stars, opts.text, sentimentPrompt)
624
888
  }]
625
889
  });
626
890
  const raw = response.content[0].type === "text" ? response.content[0].text : "";
627
891
  const match = raw.match(/\{[\s\S]*\}/);
628
- if (!match) throw new Error("AI \uC751\uB2F5 \uD30C\uC2F1 \uC2E4\uD328");
892
+ if (!match) throw new Error(m.parseFailed);
629
893
  const parsed = JSON.parse(match[0]);
630
894
  return parsed.reply;
631
895
  }
632
896
  async function cmdReview(argv) {
633
897
  const args = parseArgs3(argv);
634
- process.stdout.write(kleur4.bold("mimi-seed review \u2014 \uB9AC\uBDF0 \uB2F5\uBCC0 \uC0DD\uC131\n\n"));
898
+ process.stdout.write(kleur4.bold(M4().title));
635
899
  if (!process.env.ANTHROPIC_API_KEY) {
636
- process.stdout.write(kleur4.red("ANTHROPIC_API_KEY\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.\n"));
637
- process.stdout.write(kleur4.dim("\uD65C\uC131\uD654: export ANTHROPIC_API_KEY=sk-ant-...\n"));
900
+ process.stdout.write(kleur4.red(M4().noApiKey));
901
+ process.stdout.write(kleur4.dim(M4().noApiKeyHint));
638
902
  process.exit(1);
639
903
  }
640
904
  let reviewText = args.text;
641
905
  if (!reviewText) {
642
906
  if (args.noInteractive) {
643
- process.stdout.write(kleur4.red("--text <\uB9AC\uBDF0 \uB0B4\uC6A9> \uC774 \uD544\uC694\uD569\uB2C8\uB2E4.\n"));
907
+ process.stdout.write(kleur4.red(M4().textRequired));
644
908
  process.exit(1);
645
909
  }
646
- process.stdout.write(kleur4.dim("\uB9AC\uBDF0 \uB0B4\uC6A9\uC744 \uC785\uB825\uD558\uC138\uC694 (\uC5EC\uB7EC \uC904: Ctrl+D\uB85C \uC644\uB8CC):\n"));
910
+ process.stdout.write(kleur4.dim(M4().enterReview));
647
911
  reviewText = await promptUser2("> ");
648
912
  if (!reviewText) {
649
- process.stdout.write(kleur4.yellow("\uC785\uB825 \uC5C6\uC74C. \uC885\uB8CC.\n"));
913
+ process.stdout.write(kleur4.yellow(M4().noInput));
650
914
  process.exit(0);
651
915
  }
652
916
  }
653
917
  if (args.rating !== void 0) {
654
918
  const stars = "\u2605".repeat(args.rating) + "\u2606".repeat(5 - args.rating);
655
- process.stdout.write(kleur4.dim(`\uBCC4\uC810: ${stars} tone: ${args.tone} \uC5B8\uC5B4: ${args.language}
656
-
657
- `));
919
+ process.stdout.write(kleur4.dim(M4().meta(stars, args.tone, args.language)));
658
920
  }
659
- process.stdout.write("\u{1F916} Claude AI\uB85C \uB2F5\uBCC0 \uC0DD\uC131 \uC911...\n");
921
+ process.stdout.write(M4().generating);
660
922
  const reply = await generateReply({ ...args, text: reviewText });
661
- process.stdout.write("\n" + kleur4.bold("\u2500\u2500\u2500 \uC81C\uC548 \uB2F5\uBCC0 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
923
+ process.stdout.write("\n" + kleur4.bold(M4().hdrReply));
662
924
  process.stdout.write(reply + "\n");
663
- process.stdout.write(kleur4.dim("\n\u26A0 \uC774 \uB2F5\uBCC0\uC740 AI\uAC00 \uC0DD\uC131\uD55C \uCD08\uC548\uC785\uB2C8\uB2E4. \uAC8C\uC2DC \uC804 \uBC18\uB4DC\uC2DC \uAC80\uD1A0\uD558\uC138\uC694.\n\n"));
925
+ process.stdout.write(kleur4.dim(M4().draftWarning));
664
926
  const shouldPrompt = !args.apply && !args.noInteractive && process.stdout.isTTY;
665
927
  if (!args.apply && !shouldPrompt) return;
666
928
  let doApply = args.apply;
667
929
  if (shouldPrompt) {
668
- const ans = await promptUser2("\uC774 \uB2F5\uBCC0\uC744 Play Store\uC5D0 \uAC8C\uC2DC\uD560\uAE4C\uC694? [y/N]: ");
930
+ const ans = await promptUser2(M4().postPrompt);
669
931
  doApply = ans.toLowerCase() === "y";
670
932
  }
671
933
  if (!doApply) {
672
- process.stdout.write(kleur4.dim("\uAC74\uB108\uB700.\n"));
934
+ process.stdout.write(kleur4.dim(M4().skipped));
673
935
  return;
674
936
  }
675
937
  const cfg = await getEffectiveConfig();
676
938
  if (!cfg) {
677
- process.stdout.write(kleur4.yellow("Mimi Seed \uACC4\uC815 \uC5F0\uACB0 \uD544\uC694. `mimi-seed init` \uC2E4\uD589.\n"));
939
+ process.stdout.write(kleur4.yellow(M4().needAccount));
678
940
  return;
679
941
  }
680
942
  let reviewId = args.reviewId;
681
943
  let packageName = args.packageName;
682
944
  if (!reviewId || !packageName) {
683
945
  if (args.noInteractive) {
684
- process.stdout.write(kleur4.red("--review-id \uC640 --package-name \uC774 \uD544\uC694\uD569\uB2C8\uB2E4.\n"));
946
+ process.stdout.write(kleur4.red(M4().idsRequired));
685
947
  process.exit(1);
686
948
  }
687
- if (!packageName) packageName = await promptUser2("\uD328\uD0A4\uC9C0\uBA85 (\uC608: com.example.app): ");
688
- if (!reviewId) reviewId = await promptUser2("\uB9AC\uBDF0 ID: ");
949
+ if (!packageName) packageName = await promptUser2(M4().packagePrompt);
950
+ if (!reviewId) reviewId = await promptUser2(M4().reviewIdPrompt);
689
951
  }
690
952
  if (!reviewId || !packageName) {
691
- process.stdout.write(kleur4.yellow("\uC815\uBCF4 \uBD80\uC871. \uCDE8\uC18C.\n"));
953
+ process.stdout.write(kleur4.yellow(M4().notEnoughInfo));
692
954
  return;
693
955
  }
694
- process.stdout.write("Play Store\uC5D0 \uAC8C\uC2DC \uC911...\n");
956
+ process.stdout.write(M4().posting);
695
957
  const r = await mcpCall(cfg.endpoint, cfg.token, "playstore_reply_review", {
696
958
  package_name: packageName,
697
959
  review_id: reviewId,
698
960
  reply_text: reply
699
961
  });
700
- process.stdout.write(r.isError ? kleur4.red(`\uAC8C\uC2DC \uC2E4\uD328: ${r.text}
701
- `) : kleur4.green("\u2713 \uB2F5\uBCC0 \uAC8C\uC2DC \uC644\uB8CC\n"));
962
+ process.stdout.write(r.isError ? kleur4.red(M4().postFailed(r.text)) : kleur4.green(M4().posted));
702
963
  }
703
964
 
704
965
  // src/auth.ts
@@ -706,8 +967,9 @@ import kleur5 from "kleur";
706
967
  function log(msg) {
707
968
  process.stdout.write(msg + "\n");
708
969
  }
709
- function printAuthHelp() {
710
- log(`${kleur5.bold("mimi-seed auth")} \u2014 \uB85C\uCEEC \uC790\uACA9\uC99D\uBA85 \uC778\uC99D/\uAD00\uB9AC
970
+ var M5 = catalog(
971
+ {
972
+ help: `${kleur5.bold("mimi-seed auth")} \u2014 \uB85C\uCEEC \uC790\uACA9\uC99D\uBA85 \uC778\uC99D/\uAD00\uB9AC
711
973
 
712
974
  ${kleur5.bold("Google OAuth (Firebase / AdMob / Play):")}
713
975
  ${kleur5.cyan("mimi-seed auth login")} \uBE0C\uB77C\uC6B0\uC800\uB85C \uB85C\uADF8\uC778 (\uC774\uBBF8 \uC788\uC73C\uBA74 \uC790\uB3D9 refresh \uC2DC\uB3C4)
@@ -738,7 +1000,47 @@ ${kleur5.bold("login \uC635\uC158:")}
738
1000
  --timeout <\uCD08> \uCF5C\uBC31 \uB300\uAE30 \uC2DC\uAC04 (\uAE30\uBCF8 600)
739
1001
  --force \uAE30\uC874 \uD1A0\uD070 \uBB34\uC2DC\uD558\uACE0 \uAC15\uC81C \uC7AC\uB85C\uADF8\uC778
740
1002
 
741
- ${kleur5.dim(`\uB0B4\uBD80\uC801\uC73C\uB85C ${MCP_PKG} \uC758 mimi-seed-*-auth CLI\uB97C \uD638\uCD9C\uD569\uB2C8\uB2E4.`)}`);
1003
+ ${kleur5.dim(`\uB0B4\uBD80\uC801\uC73C\uB85C ${MCP_PKG} \uC758 mimi-seed-*-auth CLI\uB97C \uD638\uCD9C\uD569\uB2C8\uB2E4.`)}`,
1004
+ connectAll: "\n \uD55C \uBC88\uC5D0 \uC5F0\uACB0: mimi-seed setup"
1005
+ },
1006
+ {
1007
+ help: `${kleur5.bold("mimi-seed auth")} \u2014 local credential setup
1008
+
1009
+ ${kleur5.bold("Google OAuth (Firebase / AdMob / Play):")}
1010
+ ${kleur5.cyan("mimi-seed auth login")} sign in via browser (refreshes an existing token if it can)
1011
+ ${kleur5.cyan("mimi-seed auth status")} current OAuth token status
1012
+ ${kleur5.cyan("mimi-seed auth refresh")} refresh only, using refresh_token (no browser)
1013
+ ${kleur5.cyan("mimi-seed auth logout")} delete the OAuth token
1014
+
1015
+ ${kleur5.bold("Per-platform credentials:")}
1016
+ ${kleur5.cyan("mimi-seed auth appstore")} App Store Connect API key (appstore.json)
1017
+ ${kleur5.cyan("mimi-seed auth playstore")} register a Play service-account JSON
1018
+ ${kleur5.cyan("mimi-seed auth bigquery")} BigQuery service account (Crashlytics export, etc.)
1019
+
1020
+ ${kleur5.bold("Build / marketing:")}
1021
+ ${kleur5.cyan("mimi-seed auth jenkins")} Jenkins connection (jenkins.json)
1022
+ ${kleur5.cyan("mimi-seed auth ci")} GitHub Actions / GitLab CI (ci.json)
1023
+ ${kleur5.cyan("mimi-seed auth googleads")} Google Ads (google-ads.json)
1024
+ ${kleur5.cyan("mimi-seed auth facebook")} Facebook Page
1025
+ ${kleur5.cyan("mimi-seed auth instagram")} Instagram
1026
+
1027
+ ${kleur5.bold("Everything at a glance:")}
1028
+ ${kleur5.cyan("mimi-seed auth status --all")} which credentials you have
1029
+
1030
+ ${kleur5.bold("Connect them all in one pass:")}
1031
+ ${kleur5.cyan("mimi-seed setup")} guided, one credential at a time (recommended)
1032
+
1033
+ ${kleur5.bold("login options:")}
1034
+ --no-browser don't open the URL automatically (paste it yourself)
1035
+ --timeout <sec> how long to wait for the callback (default 600)
1036
+ --force ignore the existing token and sign in again
1037
+
1038
+ ${kleur5.dim(`Under the hood this calls the mimi-seed-*-auth CLIs from ${MCP_PKG}.`)}`,
1039
+ connectAll: "\n Connect everything at once: mimi-seed setup"
1040
+ }
1041
+ );
1042
+ function printAuthHelp() {
1043
+ log(M5().help);
742
1044
  }
743
1045
  function printAllCredStatus() {
744
1046
  log(kleur5.bold(t().auth.statusTitle + " ") + kleur5.dim("(~/.mimi-seed)"));
@@ -749,7 +1051,7 @@ function printAllCredStatus() {
749
1051
  const tail = d.present ? kleur5.dim(d.detail ?? "") : kleur5.dim(`\u2192 ${spec.fix}`);
750
1052
  log(` ${mark} ${credLabel(spec).padEnd(24)} ${tail}`);
751
1053
  }
752
- log(kleur5.dim("\n \uD55C \uBC88\uC5D0 \uC5F0\uACB0: mimi-seed setup"));
1054
+ log(kleur5.dim(M5().connectAll));
753
1055
  }
754
1056
  async function cmdAuth(args) {
755
1057
  const sub = args[0];
@@ -770,7 +1072,7 @@ async function cmdAuth(args) {
770
1072
  if (sub === "facebook") return void exitWith(await runMcpBin("mimi-seed-social-auth", ["facebook"]));
771
1073
  if (sub === "instagram") return void exitWith(await runMcpBin("mimi-seed-social-auth", ["instagram"]));
772
1074
  if (sub === "ci") {
773
- const { cmdSetup: cmdSetup2 } = await import("./setup-G2JY35UW.js");
1075
+ const { cmdSetup: cmdSetup2 } = await import("./setup-NQHLA3AJ.js");
774
1076
  await cmdSetup2(["--only", "github,gitlab", "--reconnect", "github,gitlab"]);
775
1077
  return;
776
1078
  }
@@ -837,9 +1139,7 @@ async function runMcpBin2(bin, extraArgs) {
837
1139
  shell: true
838
1140
  });
839
1141
  child.on("error", (e) => {
840
- process.stderr.write(`
841
- \u274C npx \uC2E4\uD589 \uC2E4\uD328: ${e.message}
842
- `);
1142
+ process.stderr.write(t().auth.npxFailed("npx", e.message));
843
1143
  resolve(1);
844
1144
  });
845
1145
  child.on("exit", (code) => resolve(code ?? 0));
@@ -867,6 +1167,42 @@ import kleur7 from "kleur";
867
1167
  function log3(msg) {
868
1168
  process.stdout.write(msg + "\n");
869
1169
  }
1170
+ var M6 = catalog(
1171
+ {
1172
+ killedPid: (pid) => ` PID ${pid} \uC885\uB8CC`,
1173
+ title: (server) => `MCP \uC11C\uBC84 \uC7AC\uC2DC\uC791: ${server}`,
1174
+ none: "(\uC5C6\uC74C)",
1175
+ notFound: (server) => `'${server}' \uC11C\uBC84\uB97C ~/.claude/.claude.json\uC5D0\uC11C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.`,
1176
+ registered: (list) => ` \uB4F1\uB85D\uB41C \uC11C\uBC84: ${list}`,
1177
+ httpServer: (server) => `'${server}'\uB294 HTTP/SSE \uC11C\uBC84\uC785\uB2C8\uB2E4. \uD504\uB85C\uC138\uC2A4 \uC7AC\uC2DC\uC791\uC774 \uD544\uC694\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`,
1178
+ httpHint: " Claude Code\uC5D0\uC11C /mcp \uB97C \uC2E4\uD589\uD574 \uC5F0\uACB0 \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694.",
1179
+ noMarker: "\uD504\uB85C\uC138\uC2A4 \uC2DD\uBCC4\uC790(\uC2A4\uD06C\uB9BD\uD2B8 \uACBD\uB85C)\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
1180
+ configLine: (cfg) => ` \uC124\uC815: ${cfg}`,
1181
+ markerLine: (marker) => ` \uC2DD\uBCC4\uC790: ${marker}`,
1182
+ noProcess: "\u26A0 \uC2E4\uD589 \uC911\uC778 \uD504\uB85C\uC138\uC2A4\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
1183
+ noProcessHint: " \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.",
1184
+ killed: (server, n) => `\u2713 ${server} \uC885\uB8CC\uB428 (${n}\uAC1C \uD504\uB85C\uC138\uC2A4)`,
1185
+ killedHint: " Claude Code\uAC00 \uB2E4\uC74C \uB3C4\uAD6C \uD638\uCD9C \uC2DC \uC790\uB3D9\uC73C\uB85C \uC7AC\uC5F0\uACB0\uD569\uB2C8\uB2E4.",
1186
+ verify: " \uC5F0\uACB0 \uD655\uC778: Claude Code\uC5D0\uC11C /mcp \uC2E4\uD589"
1187
+ },
1188
+ {
1189
+ killedPid: (pid) => ` PID ${pid} killed`,
1190
+ title: (server) => `Restarting MCP server: ${server}`,
1191
+ none: "(none)",
1192
+ registered: (list) => ` Registered servers: ${list}`,
1193
+ notFound: (server) => `Could not find the '${server}' server in ~/.claude/.claude.json.`,
1194
+ httpServer: (server) => `'${server}' is an HTTP/SSE server. It does not need a process restart.`,
1195
+ httpHint: " Run /mcp in Claude Code to check the connection.",
1196
+ noMarker: "Could not find a process marker (script path).",
1197
+ configLine: (cfg) => ` Config: ${cfg}`,
1198
+ markerLine: (marker) => ` Marker: ${marker}`,
1199
+ noProcess: "\u26A0 No running process found.",
1200
+ noProcessHint: " It may have already exited, or Claude Code may not have started the server yet.",
1201
+ killed: (server, n) => `\u2713 ${server} killed (${n} process(es))`,
1202
+ killedHint: " Claude Code will reconnect automatically on the next tool call.",
1203
+ verify: " Verify the connection: run /mcp in Claude Code"
1204
+ }
1205
+ );
870
1206
  function readClaudeJson() {
871
1207
  const p = path2.join(os.homedir(), ".claude.json");
872
1208
  try {
@@ -903,7 +1239,7 @@ function killByMarker(marker) {
903
1239
  for (const pid of pids) {
904
1240
  try {
905
1241
  execSync2(`taskkill /F /PID ${pid}`, { stdio: "pipe" });
906
- log3(kleur7.dim(` PID ${pid} \uC885\uB8CC`));
1242
+ log3(kleur7.dim(M6().killedPid(pid)));
907
1243
  killed++;
908
1244
  } catch {
909
1245
  }
@@ -920,40 +1256,40 @@ function killByMarker(marker) {
920
1256
  }
921
1257
  async function cmdRestart(args) {
922
1258
  const serverName = args[0] ?? "mimi-seed";
923
- log3(kleur7.bold(`MCP \uC11C\uBC84 \uC7AC\uC2DC\uC791: ${serverName}`));
1259
+ log3(kleur7.bold(M6().title(serverName)));
924
1260
  log3("");
925
1261
  const d = readClaudeJson();
926
1262
  const servers = d.mcpServers;
927
1263
  const cfg = servers?.[serverName];
928
1264
  if (!cfg) {
929
- const available = servers ? Object.keys(servers).join(", ") : "(\uC5C6\uC74C)";
930
- log3(kleur7.red(`'${serverName}' \uC11C\uBC84\uB97C ~/.claude/.claude.json\uC5D0\uC11C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.`));
931
- log3(kleur7.dim(` \uB4F1\uB85D\uB41C \uC11C\uBC84: ${available}`));
1265
+ const available = servers ? Object.keys(servers).join(", ") : M6().none;
1266
+ log3(kleur7.red(M6().notFound(serverName)));
1267
+ log3(kleur7.dim(M6().registered(available)));
932
1268
  process.exit(1);
933
1269
  }
934
1270
  if (cfg.type === "http" || cfg.type === "sse") {
935
- 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.`));
936
- log3(kleur7.dim(" Claude Code\uC5D0\uC11C /mcp \uB97C \uC2E4\uD589\uD574 \uC5F0\uACB0 \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694."));
1271
+ log3(kleur7.yellow(M6().httpServer(serverName)));
1272
+ log3(kleur7.dim(M6().httpHint));
937
1273
  return;
938
1274
  }
939
1275
  const marker = findProcessMarker(cfg);
940
1276
  if (!marker) {
941
- log3(kleur7.yellow("\uD504\uB85C\uC138\uC2A4 \uC2DD\uBCC4\uC790(\uC2A4\uD06C\uB9BD\uD2B8 \uACBD\uB85C)\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."));
942
- log3(kleur7.dim(` \uC124\uC815: ${JSON.stringify(cfg)}`));
1277
+ log3(kleur7.yellow(M6().noMarker));
1278
+ log3(kleur7.dim(M6().configLine(JSON.stringify(cfg))));
943
1279
  process.exit(1);
944
1280
  }
945
- log3(kleur7.dim(` \uC2DD\uBCC4\uC790: ${marker}`));
1281
+ log3(kleur7.dim(M6().markerLine(marker)));
946
1282
  const { killed } = killByMarker(marker);
947
1283
  if (killed === 0) {
948
- log3(kleur7.yellow("\u26A0 \uC2E4\uD589 \uC911\uC778 \uD504\uB85C\uC138\uC2A4\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."));
949
- 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."));
1284
+ log3(kleur7.yellow(M6().noProcess));
1285
+ log3(kleur7.dim(M6().noProcessHint));
950
1286
  } else {
951
1287
  log3("");
952
- log3(kleur7.green(`\u2713 ${serverName} \uC885\uB8CC\uB428 (${killed}\uAC1C \uD504\uB85C\uC138\uC2A4)`));
953
- log3(kleur7.dim(" Claude Code\uAC00 \uB2E4\uC74C \uB3C4\uAD6C \uD638\uCD9C \uC2DC \uC790\uB3D9\uC73C\uB85C \uC7AC\uC5F0\uACB0\uD569\uB2C8\uB2E4."));
1288
+ log3(kleur7.green(M6().killed(serverName, killed)));
1289
+ log3(kleur7.dim(M6().killedHint));
954
1290
  }
955
1291
  log3("");
956
- log3(kleur7.cyan(" \uC5F0\uACB0 \uD655\uC778: Claude Code\uC5D0\uC11C /mcp \uC2E4\uD589"));
1292
+ log3(kleur7.cyan(M6().verify));
957
1293
  }
958
1294
 
959
1295
  // src/mcp-config.ts
@@ -962,6 +1298,20 @@ import path3 from "path";
962
1298
  import os2 from "os";
963
1299
  import kleur8 from "kleur";
964
1300
  var SERVER_NAME = "mimi-seed";
1301
+ var M7 = catalog(
1302
+ {
1303
+ claudeTitle: "Claude Code MCP \uB4F1\uB85D \u2014 \uC544\uB798 \uD55C \uC904\uC744 \uADF8\uB300\uB85C \uC2E4\uD589\uD558\uC138\uC694:",
1304
+ tokenWarning: (loc) => ` \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 ${loc} \uC5D0\uB3C4 \uC800\uC7A5\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.`,
1305
+ codexTitle: "Codex MCP \uB4F1\uB85D:",
1306
+ codexManual: " # \uB610\uB294 \uC218\uB3D9\uC73C\uB85C ~/.codex/config.toml \uC5D0 [mcp_servers.mimi-seed] \uCD94\uAC00"
1307
+ },
1308
+ {
1309
+ claudeTitle: "Register the MCP server with Claude Code \u2014 run this single line as-is:",
1310
+ tokenWarning: (loc) => ` \u26A0 This command contains your real token (it lands in your shell history). The token is also stored in ${loc}.`,
1311
+ codexTitle: "Register with Codex:",
1312
+ codexManual: " # or add [mcp_servers.mimi-seed] to ~/.codex/config.toml by hand"
1313
+ }
1314
+ );
965
1315
  function tomlString(value) {
966
1316
  return JSON.stringify(value);
967
1317
  }
@@ -992,13 +1342,13 @@ function claudeMcpAddCommand(cfg) {
992
1342
  function printMcpSetup(cfg) {
993
1343
  process.stdout.write(
994
1344
  [
995
- kleur8.bold("Claude Code MCP \uB4F1\uB85D \u2014 \uC544\uB798 \uD55C \uC904\uC744 \uADF8\uB300\uB85C \uC2E4\uD589\uD558\uC138\uC694:"),
1345
+ kleur8.bold(M7().claudeTitle),
996
1346
  kleur8.cyan(` ${claudeMcpAddCommand(cfg)}`),
997
- 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.`),
1347
+ kleur8.dim(M7().tokenWarning(CONFIG_LOCATION_HINT)),
998
1348
  "",
999
- kleur8.dim("Codex MCP \uB4F1\uB85D:"),
1349
+ kleur8.dim(M7().codexTitle),
1000
1350
  kleur8.dim(" mimi-seed mcp codex --write"),
1001
- kleur8.dim(" # \uB610\uB294 \uC218\uB3D9\uC73C\uB85C ~/.codex/config.toml \uC5D0 [mcp_servers.mimi-seed] \uCD94\uAC00")
1351
+ kleur8.dim(M7().codexManual)
1002
1352
  ].join("\n") + "\n"
1003
1353
  );
1004
1354
  }
@@ -1063,6 +1413,335 @@ async function ensureReleaseManifest(cwd) {
1063
1413
  // src/index.ts
1064
1414
  var DEFAULT_WEB_BASE = process.env.MIMI_SEED_WEB_BASE ?? "https://mimi-seed.pryzm.gg";
1065
1415
  var DEFAULT_MCP_ENDPOINT = `${DEFAULT_WEB_BASE}/api/mcp`;
1416
+ var M8 = catalog(
1417
+ {
1418
+ noProjectSignal: "\u26A0 package.json / app.json / android / ios \uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uADF8\uB798\uB3C4 \uC9C4\uD589\uD569\uB2C8\uB2E4.",
1419
+ detecting: "\u{1F50D} \uC571 \uAC10\uC9C0 \uC911...",
1420
+ noAppDetected: "\uAC10\uC9C0\uB41C \uC571 \uC5C6\uC74C. \uC6F9\uC5D0\uC11C \uC218\uB3D9 \uB4F1\uB85D \uAC00\uB2A5.",
1421
+ unnamed: "(\uC774\uB984 \uBBF8\uC0C1)",
1422
+ ciMode: "CI \uBAA8\uB4DC: MIMI_SEED_TOKEN \uC0AC\uC6A9",
1423
+ syncFailed: (text) => "\uB4F1\uB85D \uC2E4\uD328: " + text,
1424
+ manifest: (created) => ` \uB9B4\uB9AC\uC988 \uB178\uD2B8 SSOT: ${created ? "\uC0DD\uC131" : "\uD655\uC778"} docs/releases.json`,
1425
+ doneCi: "\u2713 \uC644\uB8CC (CI \uBAA8\uB4DC)",
1426
+ waitingLogin: "\u{1F510} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778 \uB300\uAE30...",
1427
+ connectFailed: (msg) => "\uC5F0\uACB0 \uC2E4\uD328: " + msg,
1428
+ tokenReceived: "\u2713 \uD1A0\uD070 \uC218\uC2E0",
1429
+ saved: (location) => ` \uC800\uC7A5\uB428: ${location}`,
1430
+ registering: "\u{1F504} \uC571 \uB4F1\uB85D \uC911...",
1431
+ claudeAgent: " Claude \uC5D0\uC774\uC804\uD2B8 \uC124\uC815: .claude/mimi-seed.md",
1432
+ codexAgent: " Codex \uC5D0\uC774\uC804\uD2B8 \uC124\uC815: AGENTS.md",
1433
+ ready: "\u2713 \uC900\uBE44 \uC644\uB8CC.",
1434
+ askLike: "Claude Code \uB610\uB294 Codex\uC5D0\uC11C \uC774\uB807\uAC8C \uBB3C\uC5B4\uBCF4\uC138\uC694:",
1435
+ ask1: ' "\uB0B4 \uC571 \uCD9C\uC2DC \uC900\uBE44\uB410\uC5B4?"',
1436
+ ask2: ' "\uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC368\uC918"',
1437
+ ask3: ' "\uB4F1\uB85D\uB41C \uC571 \uBAA9\uB85D \uBCF4\uC5EC\uC918"',
1438
+ dashboard: (url) => `\uB300\uC2DC\uBCF4\uB4DC: ${url}`,
1439
+ localTitle: "\u2500\u2500 \uB85C\uCEEC MCP \uCD94\uAC00 \uC124\uC815 (--local) \u2500\u2500",
1440
+ localIntro: "\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+).",
1441
+ localStep1: "1) Google \uB85C\uADF8\uC778 (Firebase / AdMob / Play / Ads):",
1442
+ localStep2: "2) \uB85C\uCEEC MCP \uC11C\uBC84 \uB4F1\uB85D (\uC6D0\uACA9 'mimi-seed' \uC640 \uBCC4\uAC1C):",
1443
+ localCodexHint: ' Codex: ~/.codex/config.toml \uC5D0 [mcp_servers.mimi-seed-local] command="npx", args=["-y","@yoonion/mimi-seed-mcp"]',
1444
+ localStep3: "3) \uB098\uBA38\uC9C0 \uACC4\uC815 \uC5F0\uACB0 (App Store / Play / Jenkins / CI / \uC18C\uC15C \u2026):",
1445
+ localSetupHint: " \uAC01 \uD56D\uBAA9\uC5D0\uC11C [?] \uB97C \uB204\uB974\uBA74 \uD1A0\uD070 \uBC1C\uAE09 \uBC29\uBC95\uC744 \uC54C\uB824\uC90D\uB2C8\uB2E4.",
1446
+ notConnected: "\uC5F0\uACB0\uB41C Mimi Seed \uACC4\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uC2E4\uD589.",
1447
+ statusTitle: "Mimi Seed \uC5F0\uACB0 \uC0C1\uD0DC",
1448
+ statusToken: (prefix, date) => ` \uD1A0\uD070: ${prefix}\u2026 (${date})`,
1449
+ statusEndpoint: (endpoint) => ` \uC5D4\uB4DC\uD3EC\uC778\uD2B8: ${endpoint}`,
1450
+ appList: "\u{1F4CB} \uC571 \uBAA9\uB85D:",
1451
+ listFailed: (text) => "\uC870\uD68C \uC2E4\uD328: " + text,
1452
+ logoutDone: "\u2713 \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C \uC644\uB8CC.",
1453
+ logoutRevoke: "\uC6F9\uC5D0\uC11C \uD1A0\uD070 \uD574\uC9C0: /workspace/api-tokens",
1454
+ codexWritten: "\u2713 Codex MCP \uC124\uC815 \uC644\uB8CC",
1455
+ codexWriteWarn: " \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).",
1456
+ codexVerify: "Codex\uB97C \uC0C8\uB85C \uC5F4\uACE0 `/mcp` \uB610\uB294 `codex mcp list`\uB85C \uD655\uC778\uD558\uC138\uC694.",
1457
+ codexTitle: "Codex MCP \uB4F1\uB85D",
1458
+ codexAuto: "\uC790\uB3D9 \uB4F1\uB85D:",
1459
+ codexManual: "\uC218\uB3D9 \uB4F1\uB85D \uC608\uC2DC (~/.codex/config.toml) \u2014 \uC2E4\uC81C \uD1A0\uD070 \uD3EC\uD568:",
1460
+ claudeTitle: "Claude Code MCP \uB4F1\uB85D \u2014 \uC544\uB798 \uD55C \uC904\uC744 \uADF8\uB300\uB85C \uC2E4\uD589\uD558\uC138\uC694:",
1461
+ claudeWarn: " \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.",
1462
+ // 명령별 상세 사용법 (SSOT). `mimi-seed <command> --help` 와 overview 가 공유.
1463
+ usage: {
1464
+ init: `${kleur9.bold("mimi-seed init")} \u2014 \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
1465
+
1466
+ \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
1467
+ + .claude/mimi-seed.md, AGENTS.md, docs/releases.json \uC0DD\uC131/\uD655\uC778.
1468
+
1469
+ \uC635\uC158:
1470
+ --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`,
1471
+ setup: `${kleur9.bold("mimi-seed setup")} \u2014 \uAC00\uC9C4 \uACC4\uC815\uC744 \uD55C \uBC88\uC5D0 \uC5F0\uACB0 (\uC548\uB0B4\uD615 \uB9C8\uBC95\uC0AC)
1472
+
1473
+ \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.
1474
+ \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.
1475
+ \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.
1476
+
1477
+ \uC635\uC158:
1478
+ --only <ids> \uC9C0\uC815\uD55C \uC790\uACA9\uC99D\uBA85\uB9CC (\uC27C\uD45C \uAD6C\uBD84: oauth,appstore,jenkins \u2026)
1479
+ --reconnect <ids> \uC774\uBBF8 \uC5F0\uACB0\uB3FC \uC788\uC5B4\uB3C4 \uB2E4\uC2DC \uC124\uC815
1480
+ --platform android,ios \uD50C\uB7AB\uD3FC \uAC15\uC81C \uC9C0\uC815 (\uAE30\uBCF8: \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C \uC790\uB3D9 \uAC10\uC9C0)
1481
+ --yes, -y \uC544\uBB34\uAC83\uB3C4 \uBB3B\uC9C0 \uC54A\uACE0 \uC0C1\uD0DC\uD45C\uB9CC \uCD9C\uB825
1482
+ --non-interactive \uC704\uC640 \uB3D9\uC77C (CI \uC6A9)
1483
+ --fail-on-missing \uD544\uC218 \uC790\uACA9\uC99D\uBA85\uC774 \uC5C6\uC73C\uBA74 exit 1 (CI \uAC8C\uC774\uD2B8)
1484
+
1485
+ ${kleur9.dim("\uBE44TTY / CI \uD658\uACBD\uC5D0\uC11C\uB294 \uD504\uB86C\uD504\uD2B8 \uC5C6\uC774 \uC0C1\uD0DC\uD45C\uB9CC \uCD9C\uB825\uD55C\uB2E4.")}`,
1486
+ lang: `${kleur9.bold("mimi-seed lang")} \u2014 CLI \uCD9C\uB825 \uC5B8\uC5B4 (\uD55C\uAD6D\uC5B4 / English)
1487
+
1488
+ mimi-seed lang \uD604\uC7AC \uC5B8\uC5B4 \uD45C\uC2DC
1489
+ mimi-seed lang ko \uD55C\uAD6D\uC5B4 (\uAE30\uBCF8)
1490
+ mimi-seed lang en English
1491
+
1492
+ ${kleur9.dim("~/.mimi-seed/settings.json \uC5D0 \uC800\uC7A5\uB429\uB2C8\uB2E4. \uD658\uACBD\uBCC0\uC218 MIMI_SEED_LANG \uAC00 \uC788\uC73C\uBA74 \uADF8\uAC8C \uC6B0\uC120\uD569\uB2C8\uB2E4.")}
1493
+ ${kleur9.dim("setup \uB9C8\uBC95\uC0AC\uAC00 \uCCAB \uC2E4\uD589 \uB54C \uBB3C\uC5B4\uBCF4\uBBC0\uB85C \uBCF4\uD1B5\uC740 \uC9C1\uC811 \uCE60 \uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.")}`,
1494
+ status: `${kleur9.bold("mimi-seed status")} \u2014 \uC5F0\uACB0 \uC0C1\uD0DC + \uB4F1\uB85D \uC571 \uBAA9\uB85D. \uC635\uC158 \uC5C6\uC74C.`,
1495
+ 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.`,
1496
+ logout: `${kleur9.bold("mimi-seed logout")} \u2014 \uB85C\uCEEC \uC124\uC815(config.json) \uC0AD\uC81C. \uC635\uC158 \uC5C6\uC74C.`,
1497
+ restart: `${kleur9.bold("mimi-seed restart")} \u2014 MCP \uC11C\uBC84 \uD504\uB85C\uC138\uC2A4 \uC7AC\uC2DC\uC791 (\uAE30\uBCF8: mimi-seed)
1498
+
1499
+ mimi-seed restart [server-name]`,
1500
+ notes: `${kleur9.bold("mimi-seed notes")} \u2014 \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 (git log \u2192 AI \u2192 \uB9C8\uCF13 \uC801\uC6A9)
1501
+
1502
+ \uC635\uC158:
1503
+ --from <ref> \uC2DC\uC791 \uCEE4\uBC0B/\uD0DC\uADF8 (\uAE30\uBCF8: \uCD5C\uC2E0 \uD0DC\uADF8)
1504
+ --to <ref> \uB05D \uCEE4\uBC0B (\uAE30\uBCF8: HEAD)
1505
+ --locale ko,en-US \uB300\uC0C1 \uB85C\uCF00\uC77C (\uC27C\uD45C \uAD6C\uBD84)
1506
+ --apply \uC0DD\uC131 \uD6C4 \uC2A4\uD1A0\uC5B4\uC5D0 \uBC14\uB85C \uC801\uC6A9
1507
+ --no-interactive CI \uBAA8\uB4DC (\uD504\uB86C\uD504\uD2B8 \uC5C6\uC74C)
1508
+ --limit <n> \uCD5C\uB300 \uCEE4\uBC0B \uC218 (\uAE30\uBCF8: 30)`,
1509
+ check: `${kleur9.bold("mimi-seed check")} \u2014 \uCD9C\uC2DC \uC804 Readiness \uC810\uAC80
1510
+
1511
+ \uC635\uC158:
1512
+ --app <id> \uC571 ID \uC9C0\uC815
1513
+ --fail-on-blocker \uBE14\uB85C\uCEE4 \uC788\uC73C\uBA74 exit 1 (CI\uC6A9)`,
1514
+ review: `${kleur9.bold("mimi-seed review")} \u2014 \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
1515
+
1516
+ \uC635\uC158:
1517
+ --text <\uB0B4\uC6A9> \uB9AC\uBDF0 \uC6D0\uBB38 (\uBBF8\uC785\uB825 \uC2DC \uB300\uD654\uD615 \uD504\uB86C\uD504\uD2B8)
1518
+ --rating <1-5> \uBCC4\uC810
1519
+ --tone <tone> friendly / professional / empathetic / brief (\uAE30\uBCF8: friendly)
1520
+ --language <\uCF54\uB4DC> \uB2F5\uBCC0 \uC5B8\uC5B4 (\uAE30\uBCF8: ko)
1521
+ --app-name <\uC774\uB984> \uC571 \uC774\uB984 (\uB9E5\uB77D\uC6A9)
1522
+ --apply \uB2F5\uBCC0\uC744 Play Store\uC5D0 \uAC8C\uC2DC
1523
+ --review-id <id> \uB9AC\uBDF0 ID (--apply \uC2DC \uD544\uC694)
1524
+ --package-name <p> \uD328\uD0A4\uC9C0\uBA85 (--apply \uC2DC \uD544\uC694)
1525
+ --no-interactive CI \uBAA8\uB4DC`,
1526
+ deploy: `${kleur9.bold("mimi-seed deploy")} \u2014 \uC571 \uC790\uB3D9 \uBC30\uD3EC (CI \uBE4C\uB4DC \u2192 Play Store/App Store)
1527
+
1528
+ \uC635\uC158:
1529
+ --platform android|ios \uBC30\uD3EC \uD50C\uB7AB\uD3FC (\uAE30\uBCF8: android)
1530
+ --app <id> \uC571 ID \uC9C0\uC815
1531
+ --version-code <n> \uBE4C\uB4DC \uBC88\uD638 \uC9C1\uC811 \uC9C0\uC815 (--skip-build \uC640 \uD568\uAED8)
1532
+ --from <ref> \uCEE4\uBC0B \uBC94\uC704 \uC2DC\uC791 (\uB9B4\uB9AC\uC988 \uB178\uD2B8\uC6A9)
1533
+ --to <ref> \uCEE4\uBC0B \uBC94\uC704 \uB05D (\uAE30\uBCF8: HEAD)
1534
+ --language <\uCF54\uB4DC> \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC5B8\uC5B4 (\uAE30\uBCF8: ko-KR)
1535
+ --dry-run \uC2E4\uC81C \uBC30\uD3EC \uC5C6\uC774 \uD30C\uC774\uD504\uB77C\uC778 \uD14C\uC2A4\uD2B8
1536
+ --yes, -y \uBC30\uD3EC \uD655\uC778 \uD504\uB86C\uD504\uD2B8 \uC0DD\uB7B5 (\uC2A4\uD06C\uB9BD\uD2B8/\uC790\uB3D9\uD654\uC6A9)
1537
+ --skip-build CI \uBE4C\uB4DC \uAC74\uB108\uB700 (--version-code \uD544\uC218)
1538
+ --ci jenkins|github|gitlab CI \uAC15\uC81C \uC120\uD0DD (\uAE30\uBCF8: auto)
1539
+ --workflow <file> GitHub workflow \uD30C\uC77C (\uC608: deploy.yml)
1540
+ --ref <branch|tag> GitHub/GitLab \uBE0C\uB79C\uCE58/\uD0DC\uADF8 (\uAE30\uBCF8: main)
1541
+ setup-jenkins / setup-github / setup-gitlab CI \uC124\uC815 \uB300\uD654\uD615 \uB4F1\uB85D`,
1542
+ mcp: `${kleur9.bold("mimi-seed mcp")} \u2014 Claude/Codex MCP \uC5F0\uACB0
1543
+
1544
+ \uC11C\uBE0C\uBA85\uB839:
1545
+ mimi-seed mcp \uD604\uC7AC \uC124\uC815 + \uB4F1\uB85D \uC548\uB0B4
1546
+ mimi-seed mcp claude Claude Code \uB4F1\uB85D \uBA85\uB839 \uCD9C\uB825
1547
+ mimi-seed mcp codex Codex \uB4F1\uB85D \uC548\uB0B4
1548
+ mimi-seed mcp codex --write ~/.codex/config.toml\uC5D0 \uC9C1\uC811 \uAE30\uB85D (\u26A0 \uC2E4\uC81C \uD1A0\uD070 \uD3C9\uBB38 \uC800\uC7A5)`
1549
+ },
1550
+ help: `${kleur9.bold("mimi-seed")} \u2014 Claude Code/Codex\uC5D0\uC11C \uC571 \uCD9C\uC2DC \uC6B4\uC601
1551
+
1552
+ ${kleur9.bold("\uBA85\uB839\uC5B4:")}
1553
+ ${kleur9.cyan("mimi-seed init")} \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
1554
+ ${kleur9.cyan("mimi-seed setup")} \uAC00\uC9C4 \uACC4\uC815\uC744 \uD55C \uBC88\uC5D0 \uC5F0\uACB0 (\uC548\uB0B4\uD615 \uB9C8\uBC95\uC0AC)
1555
+ ${kleur9.cyan("mimi-seed lang")} \uCD9C\uB825 \uC5B8\uC5B4 (ko / en)
1556
+ ${kleur9.cyan("mimi-seed status")} \uC5F0\uACB0 \uC0C1\uD0DC + \uB4F1\uB85D \uC571 \uBAA9\uB85D
1557
+ ${kleur9.cyan("mimi-seed auth")} \uC790\uACA9\uC99D\uBA85 \uAC1C\uBCC4 \uC778\uC99D (Google / App Store / Play / Jenkins / CI \u2026)
1558
+ ${kleur9.cyan("mimi-seed firebase")} Firebase \uC571 \uC0DD\uC131\xB7config \uB2E4\uC6B4\uB85C\uB4DC\xB7GA4 \uB9C1\uD06C
1559
+ ${kleur9.cyan("mimi-seed admob")} AdMob \uACC4\uC815\xB7\uC571\xB7\uAD11\uACE0\uB2E8\uC704 \uC870\uD68C \uBC0F \uC0DD\uC131
1560
+ ${kleur9.cyan("mimi-seed ga4")} GA4 property\xB7data stream \uC0DD\uC131\xB7\uC870\uD68C
1561
+ ${kleur9.cyan("mimi-seed doctor")} \uD658\uACBD \uC9C4\uB2E8 (\uD1A0\uD070\xB7Git\xB7\uD504\uB85C\uC81D\uD2B8\xB7CI \uCCB4\uD06C)
1562
+ ${kleur9.cyan("mimi-seed check")} \uCD9C\uC2DC \uC804 Readiness \uC810\uAC80
1563
+ ${kleur9.cyan("mimi-seed notes")} \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 (git log \u2192 AI \u2192 \uB9C8\uCF13 \uC801\uC6A9)
1564
+ ${kleur9.cyan("mimi-seed review")} \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
1565
+ ${kleur9.cyan("mimi-seed deploy")} \uC571 \uC790\uB3D9 \uBC30\uD3EC (CI \u2192 Play Store/App Store)
1566
+ ${kleur9.cyan("mimi-seed mcp")} Claude/Codex MCP \uC5F0\uACB0 \uC548\uB0B4 \uBC0F Codex \uC124\uC815 \uC4F0\uAE30
1567
+ ${kleur9.cyan("mimi-seed restart")} MCP \uC11C\uBC84 \uD504\uB85C\uC138\uC2A4 \uC7AC\uC2DC\uC791 (\uAE30\uBCF8: mimi-seed)
1568
+ ${kleur9.cyan("mimi-seed logout")} \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C
1569
+
1570
+ ${kleur9.dim("\uAC01 \uBA85\uB839 \uC0C1\uC138 \uC635\uC158:")} ${kleur9.cyan("mimi-seed <command> --help")}
1571
+
1572
+ ${kleur9.bold("\uD658\uACBD\uBCC0\uC218:")}
1573
+ MIMI_SEED_TOKEN PAT \uD1A0\uD070 (CI/CD \uBB34\uC778\uC99D \uBAA8\uB4DC)
1574
+ MIMI_SEED_WEB_BASE \uC11C\uBC84 \uC8FC\uC18C (\uAE30\uBCF8: https://mimi-seed.pryzm.gg)
1575
+ ANTHROPIC_API_KEY AI \uB178\uD2B8 \uC0DD\uC131 \uD65C\uC131\uD654 (\uC120\uD0DD)
1576
+ MIMI_SEED_LANG \uCD9C\uB825 \uC5B8\uC5B4 \uAC15\uC81C (ko / en) \u2014 settings.json \uBCF4\uB2E4 \uC6B0\uC120
1577
+ MIMI_SEED_GOOGLE_CLIENT_ID / _SECRET
1578
+ \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)
1579
+ `
1580
+ },
1581
+ {
1582
+ noProjectSignal: "\u26A0 No package.json / app.json / android / ios found. Continuing anyway.",
1583
+ detecting: "\u{1F50D} Detecting apps...",
1584
+ noAppDetected: "No app detected. You can register one manually on the web.",
1585
+ unnamed: "(unnamed)",
1586
+ ciMode: "CI mode: using MIMI_SEED_TOKEN",
1587
+ syncFailed: (text) => "Registration failed: " + text,
1588
+ manifest: (created) => ` Release notes SSOT: ${created ? "created" : "verified"} docs/releases.json`,
1589
+ doneCi: "\u2713 Done (CI mode)",
1590
+ waitingLogin: "\u{1F510} Waiting for browser sign-in...",
1591
+ connectFailed: (msg) => "Connection failed: " + msg,
1592
+ tokenReceived: "\u2713 Token received",
1593
+ saved: (location) => ` Saved: ${location}`,
1594
+ registering: "\u{1F504} Registering apps...",
1595
+ claudeAgent: " Claude agent config: .claude/mimi-seed.md",
1596
+ codexAgent: " Codex agent config: AGENTS.md",
1597
+ ready: "\u2713 Ready.",
1598
+ askLike: "Try asking this in Claude Code or Codex:",
1599
+ ask1: ' "Is my app ready to ship?"',
1600
+ ask2: ' "Write the release notes"',
1601
+ ask3: ' "Show my registered apps"',
1602
+ dashboard: (url) => `Dashboard: ${url}`,
1603
+ localTitle: "\u2500\u2500 Extra local MCP setup (--local) \u2500\u2500",
1604
+ localIntro: "The remote MCP (PAT, read + diagnostics) is done above. The local MCP runs every store write tool directly via Google OAuth (Node 20+).",
1605
+ localStep1: "1) Sign in with Google (Firebase / AdMob / Play / Ads):",
1606
+ localStep2: "2) Register the local MCP server (separate from the remote 'mimi-seed'):",
1607
+ localCodexHint: ' Codex: add [mcp_servers.mimi-seed-local] command="npx", args=["-y","@yoonion/mimi-seed-mcp"] to ~/.codex/config.toml',
1608
+ localStep3: "3) Connect the remaining accounts (App Store / Play / Jenkins / CI / social \u2026):",
1609
+ localSetupHint: " Press [?] on any item to see how to obtain that token.",
1610
+ notConnected: "No Mimi Seed account connected. Run `mimi-seed init`.",
1611
+ statusTitle: "Mimi Seed connection status",
1612
+ statusToken: (prefix, date) => ` Token: ${prefix}\u2026 (${date})`,
1613
+ statusEndpoint: (endpoint) => ` Endpoint: ${endpoint}`,
1614
+ appList: "\u{1F4CB} Apps:",
1615
+ listFailed: (text) => "Lookup failed: " + text,
1616
+ logoutDone: "\u2713 Local config deleted.",
1617
+ logoutRevoke: "Revoke the token on the web: /workspace/api-tokens",
1618
+ codexWritten: "\u2713 Codex MCP configured",
1619
+ codexWriteWarn: " \u26A0 The real token is stored in plain text in config.toml (check the file permissions).",
1620
+ codexVerify: "Reopen Codex and verify with `/mcp` or `codex mcp list`.",
1621
+ codexTitle: "Codex MCP registration",
1622
+ codexAuto: "Automatic:",
1623
+ codexManual: "Manual example (~/.codex/config.toml) \u2014 contains the real token:",
1624
+ claudeTitle: "Claude Code MCP registration \u2014 run this one line as-is:",
1625
+ claudeWarn: " \u26A0 This command contains the real token (it stays in your shell history). The token is also stored in ~/.mimi-seed/config.json.",
1626
+ usage: {
1627
+ init: `${kleur9.bold("mimi-seed init")} \u2014 connect the current project to Mimi Seed
1628
+
1629
+ Auto-detects apps (Expo/Gradle/Info.plist/pbxproj) \u2192 issues a PAT in the browser (remote MCP) \u2192 registers the apps
1630
+ + creates/verifies .claude/mimi-seed.md, AGENTS.md, docs/releases.json.
1631
+
1632
+ Options:
1633
+ --local also sign in with Google OAuth and show local MCP (all store write tools) setup`,
1634
+ setup: `${kleur9.bold("mimi-seed setup")} \u2014 connect the accounts you have, in one pass (guided wizard)
1635
+
1636
+ Shows the connection status first, then asks only about what is not connected yet.
1637
+ Press [?] on any item to learn where and how to obtain that token.
1638
+ Already-connected items are skipped, so you can quit halfway and rerun it later.
1639
+
1640
+ Options:
1641
+ --only <ids> only the given credentials (comma-separated: oauth,appstore,jenkins \u2026)
1642
+ --reconnect <ids> set up again even if already connected
1643
+ --platform android,ios force the platforms (default: auto-detected from the project)
1644
+ --yes, -y ask nothing, just print the status table
1645
+ --non-interactive same as above (for CI)
1646
+ --fail-on-missing exit 1 if a required credential is missing (CI gate)
1647
+
1648
+ ${kleur9.dim("In non-TTY / CI environments it prints the status table only, with no prompts.")}`,
1649
+ lang: `${kleur9.bold("mimi-seed lang")} \u2014 CLI output language (\uD55C\uAD6D\uC5B4 / English)
1650
+
1651
+ mimi-seed lang show current language
1652
+ mimi-seed lang ko \uD55C\uAD6D\uC5B4 (default)
1653
+ mimi-seed lang en English
1654
+
1655
+ ${kleur9.dim("Stored in ~/.mimi-seed/settings.json. MIMI_SEED_LANG takes precedence when set.")}
1656
+ ${kleur9.dim("The setup wizard asks on first run, so you rarely need to type this.")}`,
1657
+ status: `${kleur9.bold("mimi-seed status")} \u2014 connection status + registered apps. No options.`,
1658
+ doctor: `${kleur9.bold("mimi-seed doctor")} \u2014 environment check (token \xB7 Node \xB7 Git \xB7 project \xB7 CI). No options.`,
1659
+ logout: `${kleur9.bold("mimi-seed logout")} \u2014 delete the local config (config.json). No options.`,
1660
+ restart: `${kleur9.bold("mimi-seed restart")} \u2014 restart the MCP server process (default: mimi-seed)
1661
+
1662
+ mimi-seed restart [server-name]`,
1663
+ notes: `${kleur9.bold("mimi-seed notes")} \u2014 generate release notes (git log \u2192 AI \u2192 push to stores)
1664
+
1665
+ Options:
1666
+ --from <ref> start commit/tag (default: latest tag)
1667
+ --to <ref> end commit (default: HEAD)
1668
+ --locale ko,en-US target locales (comma-separated)
1669
+ --apply push to the stores right after generating
1670
+ --no-interactive CI mode (no prompts)
1671
+ --limit <n> max commits (default: 30)`,
1672
+ check: `${kleur9.bold("mimi-seed check")} \u2014 pre-release readiness check
1673
+
1674
+ Options:
1675
+ --app <id> app ID
1676
+ --fail-on-blocker exit 1 if a blocker is found (for CI)`,
1677
+ review: `${kleur9.bold("mimi-seed review")} \u2014 draft a review reply with AI and post it to the Play Store
1678
+
1679
+ Options:
1680
+ --text <content> the review text (interactive prompt if omitted)
1681
+ --rating <1-5> star rating
1682
+ --tone <tone> friendly / professional / empathetic / brief (default: friendly)
1683
+ --language <code> reply language (default: ko)
1684
+ --app-name <name> app name (for context)
1685
+ --apply post the reply to the Play Store
1686
+ --review-id <id> review ID (required with --apply)
1687
+ --package-name <p> package name (required with --apply)
1688
+ --no-interactive CI mode`,
1689
+ deploy: `${kleur9.bold("mimi-seed deploy")} \u2014 automated app release (CI build \u2192 Play Store/App Store)
1690
+
1691
+ Options:
1692
+ --platform android|ios target platform (default: android)
1693
+ --app <id> app ID
1694
+ --version-code <n> set the build number explicitly (with --skip-build)
1695
+ --from <ref> commit range start (for release notes)
1696
+ --to <ref> commit range end (default: HEAD)
1697
+ --language <code> release notes language (default: ko-KR)
1698
+ --dry-run exercise the pipeline without releasing
1699
+ --yes, -y skip the release confirmation prompt (scripts/automation)
1700
+ --skip-build skip the CI build (--version-code required)
1701
+ --ci jenkins|github|gitlab force the CI provider (default: auto)
1702
+ --workflow <file> GitHub workflow file (e.g. deploy.yml)
1703
+ --ref <branch|tag> GitHub/GitLab branch/tag (default: main)
1704
+ setup-jenkins / setup-github / setup-gitlab interactive CI setup`,
1705
+ mcp: `${kleur9.bold("mimi-seed mcp")} \u2014 Claude/Codex MCP connection
1706
+
1707
+ Subcommands:
1708
+ mimi-seed mcp current config + setup instructions
1709
+ mimi-seed mcp claude print the Claude Code registration command
1710
+ mimi-seed mcp codex Codex setup instructions
1711
+ mimi-seed mcp codex --write write straight into ~/.codex/config.toml (\u26A0 stores the real token in plain text)`
1712
+ },
1713
+ help: `${kleur9.bold("mimi-seed")} \u2014 app release ops from Claude Code/Codex
1714
+
1715
+ ${kleur9.bold("Commands:")}
1716
+ ${kleur9.cyan("mimi-seed init")} connect the current project to Mimi Seed
1717
+ ${kleur9.cyan("mimi-seed setup")} connect the accounts you have, in one pass (guided wizard)
1718
+ ${kleur9.cyan("mimi-seed lang")} output language (ko / en)
1719
+ ${kleur9.cyan("mimi-seed status")} connection status + registered apps
1720
+ ${kleur9.cyan("mimi-seed auth")} connect credentials one by one (Google / App Store / Play / Jenkins / CI \u2026)
1721
+ ${kleur9.cyan("mimi-seed firebase")} create Firebase apps, download configs, link GA4
1722
+ ${kleur9.cyan("mimi-seed admob")} list and create AdMob accounts, apps, ad units
1723
+ ${kleur9.cyan("mimi-seed ga4")} create and list GA4 properties and data streams
1724
+ ${kleur9.cyan("mimi-seed doctor")} environment check (token \xB7 Git \xB7 project \xB7 CI)
1725
+ ${kleur9.cyan("mimi-seed check")} pre-release readiness check
1726
+ ${kleur9.cyan("mimi-seed notes")} generate release notes (git log \u2192 AI \u2192 push to stores)
1727
+ ${kleur9.cyan("mimi-seed review")} draft a review reply with AI and post it to the Play Store
1728
+ ${kleur9.cyan("mimi-seed deploy")} automated app release (CI \u2192 Play Store/App Store)
1729
+ ${kleur9.cyan("mimi-seed mcp")} Claude/Codex MCP setup instructions and Codex config writing
1730
+ ${kleur9.cyan("mimi-seed restart")} restart the MCP server process (default: mimi-seed)
1731
+ ${kleur9.cyan("mimi-seed logout")} delete the local config
1732
+
1733
+ ${kleur9.dim("Per-command options:")} ${kleur9.cyan("mimi-seed <command> --help")}
1734
+
1735
+ ${kleur9.bold("Environment variables:")}
1736
+ MIMI_SEED_TOKEN PAT token (headless CI/CD mode)
1737
+ MIMI_SEED_WEB_BASE server address (default: https://mimi-seed.pryzm.gg)
1738
+ ANTHROPIC_API_KEY enable AI note generation (optional)
1739
+ MIMI_SEED_LANG force the output language (ko / en) \u2014 beats settings.json
1740
+ MIMI_SEED_GOOGLE_CLIENT_ID / _SECRET
1741
+ use your own Google OAuth client (otherwise fetched from the web console at sign-in)
1742
+ `
1743
+ }
1744
+ );
1066
1745
  function log4(msg) {
1067
1746
  process.stdout.write(msg + "\n");
1068
1747
  }
@@ -1073,37 +1752,37 @@ async function cmdInit(args) {
1073
1752
  log4(kleur9.dim(cwd));
1074
1753
  log4("");
1075
1754
  if (!await hasAnyProjectSignal(cwd)) {
1076
- 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."));
1755
+ log4(kleur9.yellow(M8().noProjectSignal));
1077
1756
  }
1078
- log4("\u{1F50D} \uC571 \uAC10\uC9C0 \uC911...");
1757
+ log4(M8().detecting);
1079
1758
  const hints = await detectHints(cwd);
1080
1759
  if (hints.length === 0) {
1081
- log4(kleur9.yellow("\uAC10\uC9C0\uB41C \uC571 \uC5C6\uC74C. \uC6F9\uC5D0\uC11C \uC218\uB3D9 \uB4F1\uB85D \uAC00\uB2A5."));
1760
+ log4(kleur9.yellow(M8().noAppDetected));
1082
1761
  } else {
1083
1762
  for (const h of hints) {
1084
1763
  const tag = [h.packageName && `android:${h.packageName}`, h.bundleId && `ios:${h.bundleId}`].filter(Boolean).join(" ");
1085
- log4(` \u2022 ${h.name ?? "(\uC774\uB984 \uBBF8\uC0C1)"} ${kleur9.dim(tag)}`);
1764
+ log4(` \u2022 ${h.name ?? M8().unnamed} ${kleur9.dim(tag)}`);
1086
1765
  }
1087
1766
  }
1088
1767
  log4("");
1089
1768
  if (process.env.MIMI_SEED_TOKEN) {
1090
1769
  const cfg2 = await getEffectiveConfig();
1091
1770
  if (cfg2 && hints.length > 0) {
1092
- log4(kleur9.dim("CI \uBAA8\uB4DC: MIMI_SEED_TOKEN \uC0AC\uC6A9"));
1771
+ log4(kleur9.dim(M8().ciMode));
1093
1772
  const payload = hints.map((h) => ({ name: h.name, packageName: h.packageName, bundleId: h.bundleId }));
1094
1773
  const result = await mcpCall(cfg2.endpoint, cfg2.token, "sync_apps", { hints: payload });
1095
1774
  if (result.isError) {
1096
- log4(kleur9.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
1775
+ log4(kleur9.red(M8().syncFailed(result.text)));
1097
1776
  } else {
1098
1777
  for (const line of result.text.split("\n")) log4(" " + line);
1099
1778
  }
1100
1779
  }
1101
1780
  const manifest2 = await ensureReleaseManifest(cwd);
1102
- log4(kleur9.dim(` \uB9B4\uB9AC\uC988 \uB178\uD2B8 SSOT: ${manifest2.created ? "\uC0DD\uC131" : "\uD655\uC778"} docs/releases.json`));
1103
- log4(kleur9.bold("\u2713 \uC644\uB8CC (CI \uBAA8\uB4DC)"));
1781
+ log4(kleur9.dim(M8().manifest(manifest2.created)));
1782
+ log4(kleur9.bold(M8().doneCi));
1104
1783
  return;
1105
1784
  }
1106
- log4("\u{1F510} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778 \uB300\uAE30...");
1785
+ log4(M8().waitingLogin);
1107
1786
  const hostName = os3.hostname().slice(0, 32);
1108
1787
  const name = `cli-${hostName}`;
1109
1788
  const { port, promise } = await awaitHandshake(5 * 60 * 1e3);
@@ -1115,10 +1794,10 @@ async function cmdInit(args) {
1115
1794
  try {
1116
1795
  handshake = await promise;
1117
1796
  } catch (e) {
1118
- log4(kleur9.red("\uC5F0\uACB0 \uC2E4\uD328: " + e.message));
1797
+ log4(kleur9.red(M8().connectFailed(e.message)));
1119
1798
  process.exit(1);
1120
1799
  }
1121
- log4(kleur9.green("\u2713 \uD1A0\uD070 \uC218\uC2E0"));
1800
+ log4(kleur9.green(M8().tokenReceived));
1122
1801
  const cfg = {
1123
1802
  token: handshake.token,
1124
1803
  prefix: handshake.prefix,
@@ -1127,14 +1806,14 @@ async function cmdInit(args) {
1127
1806
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
1128
1807
  };
1129
1808
  await writeConfig(cfg);
1130
- log4(kleur9.dim(` \uC800\uC7A5\uB428: ${CONFIG_LOCATION}`));
1809
+ log4(kleur9.dim(M8().saved(CONFIG_LOCATION)));
1131
1810
  log4("");
1132
1811
  if (hints.length > 0) {
1133
- log4("\u{1F504} \uC571 \uB4F1\uB85D \uC911...");
1812
+ log4(M8().registering);
1134
1813
  const payload = hints.map((h) => ({ name: h.name, packageName: h.packageName, bundleId: h.bundleId }));
1135
1814
  const result = await mcpCall(cfg.endpoint, cfg.token, "sync_apps", { hints: payload });
1136
1815
  if (result.isError) {
1137
- log4(kleur9.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
1816
+ log4(kleur9.red(M8().syncFailed(result.text)));
1138
1817
  } else {
1139
1818
  for (const line of result.text.split("\n")) log4(" " + line);
1140
1819
  }
@@ -1187,88 +1866,88 @@ async function cmdInit(args) {
1187
1866
  if (!fs5.existsSync(claudeAgentPath)) {
1188
1867
  fs5.mkdirSync(claudeDir, { recursive: true });
1189
1868
  fs5.writeFileSync(claudeAgentPath, agentMd, { mode: 420 });
1190
- log4(kleur9.dim(` Claude \uC5D0\uC774\uC804\uD2B8 \uC124\uC815: .claude/mimi-seed.md`));
1869
+ log4(kleur9.dim(M8().claudeAgent));
1191
1870
  }
1192
1871
  const codexAgentPath = path5.join(cwd, "AGENTS.md");
1193
1872
  if (!fs5.existsSync(codexAgentPath)) {
1194
1873
  fs5.writeFileSync(codexAgentPath, agentMd, { mode: 420 });
1195
- log4(kleur9.dim(` Codex \uC5D0\uC774\uC804\uD2B8 \uC124\uC815: AGENTS.md`));
1874
+ log4(kleur9.dim(M8().codexAgent));
1196
1875
  }
1197
1876
  const manifest = await ensureReleaseManifest(cwd);
1198
- log4(kleur9.dim(` \uB9B4\uB9AC\uC988 \uB178\uD2B8 SSOT: ${manifest.created ? "\uC0DD\uC131" : "\uD655\uC778"} docs/releases.json`));
1199
- log4(kleur9.bold("\u2713 \uC900\uBE44 \uC644\uB8CC."));
1877
+ log4(kleur9.dim(M8().manifest(manifest.created)));
1878
+ log4(kleur9.bold(M8().ready));
1200
1879
  log4("");
1201
- log4("Claude Code \uB610\uB294 Codex\uC5D0\uC11C \uC774\uB807\uAC8C \uBB3C\uC5B4\uBCF4\uC138\uC694:");
1202
- log4(kleur9.cyan(' "\uB0B4 \uC571 \uCD9C\uC2DC \uC900\uBE44\uB410\uC5B4?"'));
1203
- log4(kleur9.cyan(' "\uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC368\uC918"'));
1204
- log4(kleur9.cyan(' "\uB4F1\uB85D\uB41C \uC571 \uBAA9\uB85D \uBCF4\uC5EC\uC918"'));
1880
+ log4(M8().askLike);
1881
+ log4(kleur9.cyan(M8().ask1));
1882
+ log4(kleur9.cyan(M8().ask2));
1883
+ log4(kleur9.cyan(M8().ask3));
1205
1884
  log4("");
1206
- log4(`\uB300\uC2DC\uBCF4\uB4DC: ${kleur9.underline(DEFAULT_WEB_BASE + "/apps")}`);
1885
+ log4(M8().dashboard(kleur9.underline(DEFAULT_WEB_BASE + "/apps")));
1207
1886
  log4("");
1208
1887
  printMcpSetup(cfg);
1209
1888
  if (local) {
1210
1889
  log4("");
1211
- log4(kleur9.bold("\u2500\u2500 \uB85C\uCEEC MCP \uCD94\uAC00 \uC124\uC815 (--local) \u2500\u2500"));
1212
- 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+)."));
1890
+ log4(kleur9.bold(M8().localTitle));
1891
+ log4(kleur9.dim(M8().localIntro));
1213
1892
  log4("");
1214
- log4("1) Google \uB85C\uADF8\uC778 (Firebase / AdMob / Play / Ads):");
1893
+ log4(M8().localStep1);
1215
1894
  await cmdAuth(["login"]);
1216
1895
  log4("");
1217
- log4("2) \uB85C\uCEEC MCP \uC11C\uBC84 \uB4F1\uB85D (\uC6D0\uACA9 'mimi-seed' \uC640 \uBCC4\uAC1C):");
1896
+ log4(M8().localStep2);
1218
1897
  log4(kleur9.cyan(" claude mcp add mimi-seed-local -- npx -y @yoonion/mimi-seed-mcp"));
1219
- log4(kleur9.dim(' Codex: ~/.codex/config.toml \uC5D0 [mcp_servers.mimi-seed-local] command="npx", args=["-y","@yoonion/mimi-seed-mcp"]'));
1898
+ log4(kleur9.dim(M8().localCodexHint));
1220
1899
  log4("");
1221
- log4("3) \uB098\uBA38\uC9C0 \uACC4\uC815 \uC5F0\uACB0 (App Store / Play / Jenkins / CI / \uC18C\uC15C \u2026):");
1900
+ log4(M8().localStep3);
1222
1901
  log4(kleur9.cyan(" mimi-seed setup"));
1223
- log4(kleur9.dim(" \uAC01 \uD56D\uBAA9\uC5D0\uC11C [?] \uB97C \uB204\uB974\uBA74 \uD1A0\uD070 \uBC1C\uAE09 \uBC29\uBC95\uC744 \uC54C\uB824\uC90D\uB2C8\uB2E4."));
1902
+ log4(kleur9.dim(M8().localSetupHint));
1224
1903
  }
1225
1904
  }
1226
1905
  async function cmdStatus() {
1227
1906
  const cfg = await getEffectiveConfig();
1228
1907
  if (!cfg) {
1229
- log4(kleur9.yellow("\uC5F0\uACB0\uB41C Mimi Seed \uACC4\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uC2E4\uD589."));
1908
+ log4(kleur9.yellow(M8().notConnected));
1230
1909
  process.exit(1);
1231
1910
  }
1232
- log4(kleur9.bold("Mimi Seed \uC5F0\uACB0 \uC0C1\uD0DC"));
1233
- log4(` \uD1A0\uD070: ${cfg.prefix}\u2026 (${cfg.createdAt.slice(0, 10)})`);
1234
- log4(` \uC5D4\uB4DC\uD3EC\uC778\uD2B8: ${cfg.endpoint}`);
1911
+ log4(kleur9.bold(M8().statusTitle));
1912
+ log4(M8().statusToken(cfg.prefix, cfg.createdAt.slice(0, 10)));
1913
+ log4(M8().statusEndpoint(cfg.endpoint));
1235
1914
  log4("");
1236
- log4("\u{1F4CB} \uC571 \uBAA9\uB85D:");
1915
+ log4(M8().appList);
1237
1916
  const r = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
1238
1917
  if (r.isError) {
1239
- log4(kleur9.red("\uC870\uD68C \uC2E4\uD328: " + r.text));
1918
+ log4(kleur9.red(M8().listFailed(r.text)));
1240
1919
  process.exit(1);
1241
1920
  }
1242
1921
  for (const line of r.text.split("\n")) log4(" " + line);
1243
1922
  }
1244
1923
  async function cmdLogout() {
1245
1924
  await deleteConfig();
1246
- log4(kleur9.green("\u2713 \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C \uC644\uB8CC."));
1247
- log4(kleur9.dim("\uC6F9\uC5D0\uC11C \uD1A0\uD070 \uD574\uC9C0: /workspace/api-tokens"));
1925
+ log4(kleur9.green(M8().logoutDone));
1926
+ log4(kleur9.dim(M8().logoutRevoke));
1248
1927
  }
1249
1928
  async function cmdMcp(args) {
1250
1929
  const target = args[0] ?? "help";
1251
1930
  const cfg = await getEffectiveConfig();
1252
1931
  if (!cfg) {
1253
- log4(kleur9.yellow("\uC5F0\uACB0\uB41C Mimi Seed \uACC4\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uC2E4\uD589."));
1932
+ log4(kleur9.yellow(M8().notConnected));
1254
1933
  process.exit(1);
1255
1934
  }
1256
1935
  if (target === "codex") {
1257
1936
  if (args.includes("--write")) {
1258
1937
  const configPath = await writeCodexMcpConfig(cfg);
1259
- log4(kleur9.green("\u2713 Codex MCP \uC124\uC815 \uC644\uB8CC"));
1938
+ log4(kleur9.green(M8().codexWritten));
1260
1939
  log4(kleur9.dim(` ${configPath}`));
1261
- 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)."));
1940
+ log4(kleur9.dim(M8().codexWriteWarn));
1262
1941
  log4("");
1263
- log4("Codex\uB97C \uC0C8\uB85C \uC5F4\uACE0 `/mcp` \uB610\uB294 `codex mcp list`\uB85C \uD655\uC778\uD558\uC138\uC694.");
1942
+ log4(M8().codexVerify);
1264
1943
  return;
1265
1944
  }
1266
- log4(kleur9.bold("Codex MCP \uB4F1\uB85D"));
1945
+ log4(kleur9.bold(M8().codexTitle));
1267
1946
  log4("");
1268
- log4("\uC790\uB3D9 \uB4F1\uB85D:");
1947
+ log4(M8().codexAuto);
1269
1948
  log4(kleur9.cyan(" mimi-seed mcp codex --write"));
1270
1949
  log4("");
1271
- log4("\uC218\uB3D9 \uB4F1\uB85D \uC608\uC2DC (~/.codex/config.toml) \u2014 \uC2E4\uC81C \uD1A0\uD070 \uD3EC\uD568:");
1950
+ log4(M8().codexManual);
1272
1951
  log4(`[mcp_servers.mimi-seed]
1273
1952
  url = "${cfg.endpoint}"
1274
1953
  enabled = true
@@ -1276,137 +1955,21 @@ http_headers = { Authorization = "Bearer ${cfg.token}" }`);
1276
1955
  return;
1277
1956
  }
1278
1957
  if (target === "claude") {
1279
- log4(kleur9.bold("Claude Code MCP \uB4F1\uB85D \u2014 \uC544\uB798 \uD55C \uC904\uC744 \uADF8\uB300\uB85C \uC2E4\uD589\uD558\uC138\uC694:"));
1958
+ log4(kleur9.bold(M8().claudeTitle));
1280
1959
  log4(kleur9.cyan(` ${claudeMcpAddCommand(cfg)}`));
1281
- 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."));
1960
+ log4(kleur9.dim(M8().claudeWarn));
1282
1961
  return;
1283
1962
  }
1284
1963
  printMcpSetup(cfg);
1285
1964
  }
1286
- var CMD_USAGE = {
1287
- init: `${kleur9.bold("mimi-seed init")} \u2014 \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
1288
-
1289
- \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
1290
- + .claude/mimi-seed.md, AGENTS.md, docs/releases.json \uC0DD\uC131/\uD655\uC778.
1291
-
1292
- \uC635\uC158:
1293
- --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`,
1294
- setup: `${kleur9.bold("mimi-seed setup")} \u2014 \uAC00\uC9C4 \uACC4\uC815\uC744 \uD55C \uBC88\uC5D0 \uC5F0\uACB0 (\uC548\uB0B4\uD615 \uB9C8\uBC95\uC0AC)
1295
-
1296
- \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.
1297
- \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.
1298
- \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.
1299
-
1300
- \uC635\uC158:
1301
- --only <ids> \uC9C0\uC815\uD55C \uC790\uACA9\uC99D\uBA85\uB9CC (\uC27C\uD45C \uAD6C\uBD84: oauth,appstore,jenkins \u2026)
1302
- --reconnect <ids> \uC774\uBBF8 \uC5F0\uACB0\uB3FC \uC788\uC5B4\uB3C4 \uB2E4\uC2DC \uC124\uC815
1303
- --platform android,ios \uD50C\uB7AB\uD3FC \uAC15\uC81C \uC9C0\uC815 (\uAE30\uBCF8: \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C \uC790\uB3D9 \uAC10\uC9C0)
1304
- --yes, -y \uC544\uBB34\uAC83\uB3C4 \uBB3B\uC9C0 \uC54A\uACE0 \uC0C1\uD0DC\uD45C\uB9CC \uCD9C\uB825
1305
- --non-interactive \uC704\uC640 \uB3D9\uC77C (CI \uC6A9)
1306
- --fail-on-missing \uD544\uC218 \uC790\uACA9\uC99D\uBA85\uC774 \uC5C6\uC73C\uBA74 exit 1 (CI \uAC8C\uC774\uD2B8)
1307
-
1308
- ${kleur9.dim("\uBE44TTY / CI \uD658\uACBD\uC5D0\uC11C\uB294 \uD504\uB86C\uD504\uD2B8 \uC5C6\uC774 \uC0C1\uD0DC\uD45C\uB9CC \uCD9C\uB825\uD55C\uB2E4.")}`,
1309
- lang: `${kleur9.bold("mimi-seed lang")} \u2014 CLI \uCD9C\uB825 \uC5B8\uC5B4 (\uD55C\uAD6D\uC5B4 / English)
1310
-
1311
- mimi-seed lang \uD604\uC7AC \uC5B8\uC5B4 \uD45C\uC2DC
1312
- mimi-seed lang ko \uD55C\uAD6D\uC5B4 (\uAE30\uBCF8)
1313
- mimi-seed lang en English
1314
-
1315
- ${kleur9.dim("~/.mimi-seed/settings.json \uC5D0 \uC800\uC7A5\uB429\uB2C8\uB2E4. \uD658\uACBD\uBCC0\uC218 MIMI_SEED_LANG \uAC00 \uC788\uC73C\uBA74 \uADF8\uAC8C \uC6B0\uC120\uD569\uB2C8\uB2E4.")}
1316
- ${kleur9.dim("setup \uB9C8\uBC95\uC0AC\uAC00 \uCCAB \uC2E4\uD589 \uB54C \uBB3C\uC5B4\uBCF4\uBBC0\uB85C \uBCF4\uD1B5\uC740 \uC9C1\uC811 \uCE60 \uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.")}`,
1317
- status: `${kleur9.bold("mimi-seed status")} \u2014 \uC5F0\uACB0 \uC0C1\uD0DC + \uB4F1\uB85D \uC571 \uBAA9\uB85D. \uC635\uC158 \uC5C6\uC74C.`,
1318
- 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.`,
1319
- logout: `${kleur9.bold("mimi-seed logout")} \u2014 \uB85C\uCEEC \uC124\uC815(config.json) \uC0AD\uC81C. \uC635\uC158 \uC5C6\uC74C.`,
1320
- restart: `${kleur9.bold("mimi-seed restart")} \u2014 MCP \uC11C\uBC84 \uD504\uB85C\uC138\uC2A4 \uC7AC\uC2DC\uC791 (\uAE30\uBCF8: mimi-seed)
1321
-
1322
- mimi-seed restart [server-name]`,
1323
- notes: `${kleur9.bold("mimi-seed notes")} \u2014 \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 (git log \u2192 AI \u2192 \uB9C8\uCF13 \uC801\uC6A9)
1324
-
1325
- \uC635\uC158:
1326
- --from <ref> \uC2DC\uC791 \uCEE4\uBC0B/\uD0DC\uADF8 (\uAE30\uBCF8: \uCD5C\uC2E0 \uD0DC\uADF8)
1327
- --to <ref> \uB05D \uCEE4\uBC0B (\uAE30\uBCF8: HEAD)
1328
- --locale ko,en-US \uB300\uC0C1 \uB85C\uCF00\uC77C (\uC27C\uD45C \uAD6C\uBD84)
1329
- --apply \uC0DD\uC131 \uD6C4 \uC2A4\uD1A0\uC5B4\uC5D0 \uBC14\uB85C \uC801\uC6A9
1330
- --no-interactive CI \uBAA8\uB4DC (\uD504\uB86C\uD504\uD2B8 \uC5C6\uC74C)
1331
- --limit <n> \uCD5C\uB300 \uCEE4\uBC0B \uC218 (\uAE30\uBCF8: 30)`,
1332
- check: `${kleur9.bold("mimi-seed check")} \u2014 \uCD9C\uC2DC \uC804 Readiness \uC810\uAC80
1333
-
1334
- \uC635\uC158:
1335
- --app <id> \uC571 ID \uC9C0\uC815
1336
- --fail-on-blocker \uBE14\uB85C\uCEE4 \uC788\uC73C\uBA74 exit 1 (CI\uC6A9)`,
1337
- review: `${kleur9.bold("mimi-seed review")} \u2014 \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
1338
-
1339
- \uC635\uC158:
1340
- --text <\uB0B4\uC6A9> \uB9AC\uBDF0 \uC6D0\uBB38 (\uBBF8\uC785\uB825 \uC2DC \uB300\uD654\uD615 \uD504\uB86C\uD504\uD2B8)
1341
- --rating <1-5> \uBCC4\uC810
1342
- --tone <tone> friendly / professional / empathetic / brief (\uAE30\uBCF8: friendly)
1343
- --language <\uCF54\uB4DC> \uB2F5\uBCC0 \uC5B8\uC5B4 (\uAE30\uBCF8: ko)
1344
- --app-name <\uC774\uB984> \uC571 \uC774\uB984 (\uB9E5\uB77D\uC6A9)
1345
- --apply \uB2F5\uBCC0\uC744 Play Store\uC5D0 \uAC8C\uC2DC
1346
- --review-id <id> \uB9AC\uBDF0 ID (--apply \uC2DC \uD544\uC694)
1347
- --package-name <p> \uD328\uD0A4\uC9C0\uBA85 (--apply \uC2DC \uD544\uC694)
1348
- --no-interactive CI \uBAA8\uB4DC`,
1349
- deploy: `${kleur9.bold("mimi-seed deploy")} \u2014 \uC571 \uC790\uB3D9 \uBC30\uD3EC (CI \uBE4C\uB4DC \u2192 Play Store/App Store)
1350
-
1351
- \uC635\uC158:
1352
- --platform android|ios \uBC30\uD3EC \uD50C\uB7AB\uD3FC (\uAE30\uBCF8: android)
1353
- --app <id> \uC571 ID \uC9C0\uC815
1354
- --version-code <n> \uBE4C\uB4DC \uBC88\uD638 \uC9C1\uC811 \uC9C0\uC815 (--skip-build \uC640 \uD568\uAED8)
1355
- --from <ref> \uCEE4\uBC0B \uBC94\uC704 \uC2DC\uC791 (\uB9B4\uB9AC\uC988 \uB178\uD2B8\uC6A9)
1356
- --to <ref> \uCEE4\uBC0B \uBC94\uC704 \uB05D (\uAE30\uBCF8: HEAD)
1357
- --language <\uCF54\uB4DC> \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC5B8\uC5B4 (\uAE30\uBCF8: ko-KR)
1358
- --dry-run \uC2E4\uC81C \uBC30\uD3EC \uC5C6\uC774 \uD30C\uC774\uD504\uB77C\uC778 \uD14C\uC2A4\uD2B8
1359
- --yes, -y \uBC30\uD3EC \uD655\uC778 \uD504\uB86C\uD504\uD2B8 \uC0DD\uB7B5 (\uC2A4\uD06C\uB9BD\uD2B8/\uC790\uB3D9\uD654\uC6A9)
1360
- --skip-build CI \uBE4C\uB4DC \uAC74\uB108\uB700 (--version-code \uD544\uC218)
1361
- --ci jenkins|github|gitlab CI \uAC15\uC81C \uC120\uD0DD (\uAE30\uBCF8: auto)
1362
- --workflow <file> GitHub workflow \uD30C\uC77C (\uC608: deploy.yml)
1363
- --ref <branch|tag> GitHub/GitLab \uBE0C\uB79C\uCE58/\uD0DC\uADF8 (\uAE30\uBCF8: main)
1364
- setup-jenkins / setup-github / setup-gitlab CI \uC124\uC815 \uB300\uD654\uD615 \uB4F1\uB85D`,
1365
- mcp: `${kleur9.bold("mimi-seed mcp")} \u2014 Claude/Codex MCP \uC5F0\uACB0
1366
-
1367
- \uC11C\uBE0C\uBA85\uB839:
1368
- mimi-seed mcp \uD604\uC7AC \uC124\uC815 + \uB4F1\uB85D \uC548\uB0B4
1369
- mimi-seed mcp claude Claude Code \uB4F1\uB85D \uBA85\uB839 \uCD9C\uB825
1370
- mimi-seed mcp codex Codex \uB4F1\uB85D \uC548\uB0B4
1371
- mimi-seed mcp codex --write ~/.codex/config.toml\uC5D0 \uC9C1\uC811 \uAE30\uB85D (\u26A0 \uC2E4\uC81C \uD1A0\uD070 \uD3C9\uBB38 \uC800\uC7A5)`
1372
- };
1373
1965
  function printCommandHelp(cmd) {
1374
- const usage = CMD_USAGE[cmd];
1966
+ const usage = M8().usage[cmd];
1375
1967
  if (!usage) return false;
1376
1968
  log4(usage);
1377
1969
  return true;
1378
1970
  }
1379
1971
  function printHelp() {
1380
- log4(`${kleur9.bold("mimi-seed")} \u2014 Claude Code/Codex\uC5D0\uC11C \uC571 \uCD9C\uC2DC \uC6B4\uC601
1381
-
1382
- ${kleur9.bold("\uBA85\uB839\uC5B4:")}
1383
- ${kleur9.cyan("mimi-seed init")} \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
1384
- ${kleur9.cyan("mimi-seed setup")} \uAC00\uC9C4 \uACC4\uC815\uC744 \uD55C \uBC88\uC5D0 \uC5F0\uACB0 (\uC548\uB0B4\uD615 \uB9C8\uBC95\uC0AC)
1385
- ${kleur9.cyan("mimi-seed lang")} \uCD9C\uB825 \uC5B8\uC5B4 (ko / en)
1386
- ${kleur9.cyan("mimi-seed status")} \uC5F0\uACB0 \uC0C1\uD0DC + \uB4F1\uB85D \uC571 \uBAA9\uB85D
1387
- ${kleur9.cyan("mimi-seed auth")} \uC790\uACA9\uC99D\uBA85 \uAC1C\uBCC4 \uC778\uC99D (Google / App Store / Play / Jenkins / CI \u2026)
1388
- ${kleur9.cyan("mimi-seed firebase")} Firebase \uC571 \uC0DD\uC131\xB7config \uB2E4\uC6B4\uB85C\uB4DC\xB7GA4 \uB9C1\uD06C
1389
- ${kleur9.cyan("mimi-seed admob")} AdMob \uACC4\uC815\xB7\uC571\xB7\uAD11\uACE0\uB2E8\uC704 \uC870\uD68C \uBC0F \uC0DD\uC131
1390
- ${kleur9.cyan("mimi-seed ga4")} GA4 property\xB7data stream \uC0DD\uC131\xB7\uC870\uD68C
1391
- ${kleur9.cyan("mimi-seed doctor")} \uD658\uACBD \uC9C4\uB2E8 (\uD1A0\uD070\xB7Git\xB7\uD504\uB85C\uC81D\uD2B8\xB7CI \uCCB4\uD06C)
1392
- ${kleur9.cyan("mimi-seed check")} \uCD9C\uC2DC \uC804 Readiness \uC810\uAC80
1393
- ${kleur9.cyan("mimi-seed notes")} \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 (git log \u2192 AI \u2192 \uB9C8\uCF13 \uC801\uC6A9)
1394
- ${kleur9.cyan("mimi-seed review")} \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
1395
- ${kleur9.cyan("mimi-seed deploy")} \uC571 \uC790\uB3D9 \uBC30\uD3EC (CI \u2192 Play Store/App Store)
1396
- ${kleur9.cyan("mimi-seed mcp")} Claude/Codex MCP \uC5F0\uACB0 \uC548\uB0B4 \uBC0F Codex \uC124\uC815 \uC4F0\uAE30
1397
- ${kleur9.cyan("mimi-seed restart")} MCP \uC11C\uBC84 \uD504\uB85C\uC138\uC2A4 \uC7AC\uC2DC\uC791 (\uAE30\uBCF8: mimi-seed)
1398
- ${kleur9.cyan("mimi-seed logout")} \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C
1399
-
1400
- ${kleur9.dim("\uAC01 \uBA85\uB839 \uC0C1\uC138 \uC635\uC158:")} ${kleur9.cyan("mimi-seed <command> --help")}
1401
-
1402
- ${kleur9.bold("\uD658\uACBD\uBCC0\uC218:")}
1403
- MIMI_SEED_TOKEN PAT \uD1A0\uD070 (CI/CD \uBB34\uC778\uC99D \uBAA8\uB4DC)
1404
- MIMI_SEED_WEB_BASE \uC11C\uBC84 \uC8FC\uC18C (\uAE30\uBCF8: https://mimi-seed.pryzm.gg)
1405
- ANTHROPIC_API_KEY AI \uB178\uD2B8 \uC0DD\uC131 \uD65C\uC131\uD654 (\uC120\uD0DD)
1406
- MIMI_SEED_LANG \uCD9C\uB825 \uC5B8\uC5B4 \uAC15\uC81C (ko / en) \u2014 settings.json \uBCF4\uB2E4 \uC6B0\uC120
1407
- MIMI_SEED_GOOGLE_CLIENT_ID / _SECRET
1408
- \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)
1409
- `);
1972
+ log4(M8().help);
1410
1973
  }
1411
1974
  async function main() {
1412
1975
  const cmd = process.argv[2];
@@ -1470,12 +2033,12 @@ async function main() {
1470
2033
  printHelp();
1471
2034
  break;
1472
2035
  default:
1473
- log4(kleur9.red(`\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839: ${cmd}`));
2036
+ log4(kleur9.red(t().common.unknownCommand(cmd)));
1474
2037
  printHelp();
1475
2038
  process.exit(1);
1476
2039
  }
1477
2040
  } catch (e) {
1478
- log4(kleur9.red(`\uC624\uB958: ${e.message}`));
2041
+ log4(kleur9.red(t().common.error(e.message)));
1479
2042
  if (process.env.DEBUG) log4(e.stack ?? "");
1480
2043
  process.exit(1);
1481
2044
  }