mimi-seed 0.19.11 → 0.19.13

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,13 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- mcpCall
4
- } from "./chunk-2FKWHED2.js";
5
2
  import {
6
3
  CONFIG_LOCATION,
7
4
  CREDENTIALS,
8
5
  MCP_PKG,
6
+ catalog,
9
7
  cmdDeploy,
10
8
  cmdSetup,
9
+ cmdTelemetry,
11
10
  credLabel,
12
11
  credNote,
13
12
  deleteConfig,
@@ -16,20 +15,19 @@ import {
16
15
  findProjectManifest,
17
16
  getEffectiveConfig,
18
17
  hasAnyProjectSignal,
18
+ isLang,
19
19
  isSatisfied,
20
20
  manifestServiceEntries,
21
21
  migrateLegacyJenkins,
22
- runMcpBin,
23
- tryCredById,
24
- writeConfig
25
- } from "./chunk-UKNCSE4V.js";
26
- import {
27
- catalog,
28
- isLang,
29
22
  resolveLang,
23
+ runMcpBin,
30
24
  t,
25
+ telemetryNotice,
26
+ tryCredById,
27
+ usageRun,
28
+ writeConfig,
31
29
  writeSettings
32
- } from "./chunk-TQHLWSFR.js";
30
+ } from "./chunk-YIVIN2XR.js";
33
31
 
34
32
  // src/index.ts
35
33
  import os3 from "os";
@@ -102,6 +100,54 @@ async function awaitHandshake(timeoutMs) {
102
100
  return { port, promise };
103
101
  }
104
102
 
103
+ // src/mcp-client.ts
104
+ var M2 = catalog(
105
+ { noSseData: "MCP SSE \uC751\uB2F5\uC5D0 data \uC5C6\uC74C" },
106
+ { noSseData: "No data in the MCP SSE response" }
107
+ );
108
+ async function mcpCall(endpoint, token, name, args) {
109
+ const res = await fetch(endpoint, {
110
+ method: "POST",
111
+ headers: {
112
+ "Content-Type": "application/json",
113
+ Accept: "application/json, text/event-stream",
114
+ Authorization: `Bearer ${token}`
115
+ },
116
+ body: JSON.stringify({
117
+ jsonrpc: "2.0",
118
+ id: 1,
119
+ method: "tools/call",
120
+ params: { name, arguments: args }
121
+ })
122
+ });
123
+ if (!res.ok) {
124
+ const raw = await res.text();
125
+ let msg = raw;
126
+ try {
127
+ const parsed = JSON.parse(raw);
128
+ if (typeof parsed.error === "string") msg = parsed.error;
129
+ } catch {
130
+ }
131
+ return { text: `HTTP ${res.status}: ${msg}`, isError: true };
132
+ }
133
+ const contentType = res.headers.get("content-type") ?? "";
134
+ let payload;
135
+ if (contentType.includes("text/event-stream")) {
136
+ const text2 = await res.text();
137
+ const line = text2.split("\n").map((l) => l.trim()).find((l) => l.startsWith("data:"));
138
+ if (!line) throw new Error(M2().noSseData);
139
+ payload = JSON.parse(line.slice(5).trim());
140
+ } else {
141
+ payload = await res.json();
142
+ }
143
+ if (payload.error) {
144
+ return { text: payload.error.message, isError: true };
145
+ }
146
+ const content = payload.result?.content ?? [];
147
+ const text = content.filter((c) => c.type === "text").map((c) => c.text ?? "").join("\n");
148
+ return { text, isError: payload.result?.isError ?? false };
149
+ }
150
+
105
151
  // src/doctor.ts
106
152
  import kleur from "kleur";
107
153
 
@@ -1251,7 +1297,7 @@ async function scanReleaseDoctor(projectPath, now = /* @__PURE__ */ new Date())
1251
1297
  }
1252
1298
 
1253
1299
  // src/check.ts
1254
- var M2 = catalog(
1300
+ var M3 = catalog(
1255
1301
  {
1256
1302
  noAccount: "\uC5F0\uACB0\uB41C \uACC4\uC815 \uC5C6\uC74C. `mimi-seed init` \uC2E4\uD589.\n",
1257
1303
  missingOptionValue: (option) => `${option} \uB4A4\uC5D0 \uAC12\uC744 \uC785\uB825\uD558\uC138\uC694.`,
@@ -1261,6 +1307,7 @@ var M2 = catalog(
1261
1307
  localFailed: (message) => `Release Doctor \uC2E4\uD589 \uC2E4\uD328: ${message}
1262
1308
  `,
1263
1309
  title: "mimi-seed check \u2014 \uCD9C\uC2DC \uC804 \uC810\uAC80\n\n",
1310
+ localNext: "\n\uC2A4\uD1A0\uC5B4 \uAC80\uC0AC \uC5F0\uACB0: npx mimi-seed init\nCI\uC5D0\uC11C \uBC18\uBCF5 \uAC80\uC0AC: npx mimi-seed check --local --fail-on-blocker\n",
1264
1311
  appsFailed: (msg) => `\uC571 \uBAA9\uB85D \uC870\uD68C \uC2E4\uD328: ${msg}
1265
1312
  `,
1266
1313
  noApps: "\uB4F1\uB85D\uB41C \uC571\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uD6C4 \uC571\uC744 \uB4F1\uB85D\uD558\uC138\uC694.\n",
@@ -1298,6 +1345,7 @@ var M2 = catalog(
1298
1345
  localFailed: (message) => `Release Doctor failed: ${message}
1299
1346
  `,
1300
1347
  title: "mimi-seed check \u2014 pre-launch check\n\n",
1348
+ localNext: "\nConnect store checks: npx mimi-seed init\nRun in CI: npx mimi-seed check --local --fail-on-blocker\n",
1301
1349
  appsFailed: (msg) => `Failed to list apps: ${msg}
1302
1350
  `,
1303
1351
  noApps: "No apps registered. Run `mimi-seed init`, then register an app.\n",
@@ -1337,7 +1385,7 @@ function parseCheckArgs(argv) {
1337
1385
  };
1338
1386
  const takeValue = (option, index) => {
1339
1387
  const value = argv[index + 1];
1340
- if (!value || value.startsWith("--")) throw new Error(M2().missingOptionValue(option));
1388
+ if (!value || value.startsWith("--")) throw new Error(M3().missingOptionValue(option));
1341
1389
  return value;
1342
1390
  };
1343
1391
  for (let i = 0; i < argv.length; i++) {
@@ -1349,8 +1397,8 @@ function parseCheckArgs(argv) {
1349
1397
  } else if (token === "--fail-on-blocker") args.failOnBlocker = true;
1350
1398
  else if (token === "--local") args.local = true;
1351
1399
  else if (token === "--json") args.json = true;
1352
- else if (token.startsWith("-")) throw new Error(M2().unknownOption(token));
1353
- else throw new Error(M2().unexpectedArgument(token));
1400
+ else if (token.startsWith("-")) throw new Error(M3().unknownOption(token));
1401
+ else throw new Error(M3().unexpectedArgument(token));
1354
1402
  }
1355
1403
  return args;
1356
1404
  }
@@ -1379,31 +1427,35 @@ async function cmdCheck(argv) {
1379
1427
  const cfg = await getEffectiveConfig();
1380
1428
  const localRequested = args.local || args.json || args.projectPathExplicit;
1381
1429
  if (args.appId && localRequested) {
1382
- process.stderr.write(kleur2.red(M2().localAppConflict));
1430
+ process.stderr.write(kleur2.red(M3().localAppConflict));
1383
1431
  process.exitCode = 2;
1384
1432
  return;
1385
1433
  }
1386
1434
  if (args.appId && !cfg) {
1387
- process.stderr.write(kleur2.red(M2().noAccount));
1435
+ process.stderr.write(kleur2.red(M3().noAccount));
1388
1436
  process.exitCode = 1;
1389
1437
  return;
1390
1438
  }
1391
1439
  if (localRequested || !cfg) {
1440
+ const finish = usageRun("check", args.projectPath);
1392
1441
  try {
1393
1442
  const report = await scanReleaseDoctor(args.projectPath);
1394
1443
  process.stdout.write(args.json ? `${JSON.stringify(report, null, 2)}
1395
1444
  ` : renderReleaseDoctor(report, resolveLang()));
1445
+ if (!args.json && !process.env.CI) process.stdout.write(M3().localNext + telemetryNotice());
1396
1446
  if (args.failOnBlocker && report.counts.blocker > 0) process.exitCode = 1;
1447
+ await finish("completed", report);
1397
1448
  } catch (error) {
1398
- process.stderr.write(kleur2.red(M2().localFailed(error instanceof Error ? error.message : String(error))));
1449
+ process.stderr.write(kleur2.red(M3().localFailed(error instanceof Error ? error.message : String(error))));
1399
1450
  process.exitCode = 2;
1451
+ await finish("failed");
1400
1452
  }
1401
1453
  return;
1402
1454
  }
1403
- process.stdout.write(kleur2.bold(M2().title));
1455
+ process.stdout.write(kleur2.bold(M3().title));
1404
1456
  const appsResult = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
1405
1457
  if (appsResult.isError) {
1406
- process.stdout.write(kleur2.red(M2().appsFailed(appsResult.text)));
1458
+ process.stdout.write(kleur2.red(M3().appsFailed(appsResult.text)));
1407
1459
  process.exit(1);
1408
1460
  }
1409
1461
  let appId = args.appId;
@@ -1411,29 +1463,29 @@ async function cmdCheck(argv) {
1411
1463
  try {
1412
1464
  const apps = JSON.parse(appsResult.text);
1413
1465
  if (!Array.isArray(apps) || apps.length === 0) {
1414
- process.stdout.write(kleur2.yellow(M2().noApps));
1466
+ process.stdout.write(kleur2.yellow(M3().noApps));
1415
1467
  process.exit(0);
1416
1468
  }
1417
1469
  appId = apps[0].id;
1418
- process.stdout.write(kleur2.dim(M2().app(apps[0].name ?? appId)));
1470
+ process.stdout.write(kleur2.dim(M3().app(apps[0].name ?? appId)));
1419
1471
  } catch {
1420
- process.stdout.write(kleur2.red(M2().appsParseFailed(appsResult.text.slice(0, 80))));
1472
+ process.stdout.write(kleur2.red(M3().appsParseFailed(appsResult.text.slice(0, 80))));
1421
1473
  process.exit(1);
1422
1474
  }
1423
1475
  }
1424
- process.stdout.write(M2().scoring);
1476
+ process.stdout.write(M3().scoring);
1425
1477
  const readinessResult = await mcpCall(cfg.endpoint, cfg.token, "get_readiness", { app_id: appId });
1426
1478
  if (readinessResult.isError) {
1427
- process.stdout.write(kleur2.red(M2().scoreFailed(readinessResult.text)));
1479
+ process.stdout.write(kleur2.red(M3().scoreFailed(readinessResult.text)));
1428
1480
  process.exit(1);
1429
1481
  }
1430
1482
  let hasBlocker = false;
1431
1483
  try {
1432
1484
  const data = JSON.parse(readinessResult.text);
1433
1485
  const score = data.score ?? 0;
1434
- process.stdout.write(M2().score(renderScore(score)));
1486
+ process.stdout.write(M3().score(renderScore(score)));
1435
1487
  if (data.modules) {
1436
- process.stdout.write(kleur2.dim(M2().byModule));
1488
+ process.stdout.write(kleur2.dim(M3().byModule));
1437
1489
  for (const [key, val] of Object.entries(data.modules)) {
1438
1490
  const label = MODULE_LABELS[key] ?? key;
1439
1491
  const color = val >= 25 ? kleur2.green : val >= 10 ? kleur2.yellow : kleur2.red;
@@ -1444,13 +1496,13 @@ async function cmdCheck(argv) {
1444
1496
  }
1445
1497
  if (data.blockers?.length) {
1446
1498
  hasBlocker = true;
1447
- process.stdout.write(kleur2.bold(M2().blockers));
1499
+ process.stdout.write(kleur2.bold(M3().blockers));
1448
1500
  for (const b of data.blockers) process.stdout.write(` ${kleur2.red("\u2022")} ${b}
1449
1501
  `);
1450
1502
  process.stdout.write("\n");
1451
1503
  }
1452
1504
  if (data.warnings?.length) {
1453
- process.stdout.write(kleur2.bold(M2().warnings));
1505
+ process.stdout.write(kleur2.bold(M3().warnings));
1454
1506
  for (const w of data.warnings) process.stdout.write(` ${kleur2.yellow("\u2022")} ${w}
1455
1507
  `);
1456
1508
  process.stdout.write("\n");
@@ -1458,19 +1510,19 @@ async function cmdCheck(argv) {
1458
1510
  const base = `${cfg.webBase}/apps/${appId}`;
1459
1511
  const nextSteps = [];
1460
1512
  if (data.modules) {
1461
- if ((data.modules.integration ?? 25) < 25) nextSteps.push(M2().stepIntegration(`${base}/integration`));
1462
- if ((data.modules.copy ?? 25) < 25) nextSteps.push(M2().stepCopy(`${base}/copy`));
1463
- if ((data.modules.screenshot ?? 25) < 25) nextSteps.push(M2().stepScreenshot(`${base}/screenshots`));
1464
- if ((data.modules.checklist ?? 25) < 25) nextSteps.push(M2().stepChecklist(`${base}/launch`));
1513
+ if ((data.modules.integration ?? 25) < 25) nextSteps.push(M3().stepIntegration(`${base}/integration`));
1514
+ if ((data.modules.copy ?? 25) < 25) nextSteps.push(M3().stepCopy(`${base}/copy`));
1515
+ if ((data.modules.screenshot ?? 25) < 25) nextSteps.push(M3().stepScreenshot(`${base}/screenshots`));
1516
+ if ((data.modules.checklist ?? 25) < 25) nextSteps.push(M3().stepChecklist(`${base}/launch`));
1465
1517
  }
1466
1518
  if (nextSteps.length) {
1467
- process.stdout.write(kleur2.bold(M2().nextSteps));
1519
+ process.stdout.write(kleur2.bold(M3().nextSteps));
1468
1520
  for (const s of nextSteps) process.stdout.write(` ${kleur2.cyan("\u2022")} ${s}
1469
1521
  `);
1470
- process.stdout.write(kleur2.dim(M2().preview));
1522
+ process.stdout.write(kleur2.dim(M3().preview));
1471
1523
  }
1472
1524
  if (!hasBlocker) {
1473
- process.stdout.write(score >= 80 ? kleur2.green(M2().ready) : kleur2.yellow(M2().notReady(score)));
1525
+ process.stdout.write(score >= 80 ? kleur2.green(M3().ready) : kleur2.yellow(M3().notReady(score)));
1474
1526
  }
1475
1527
  } catch {
1476
1528
  for (const line of readinessResult.text.split("\n")) process.stdout.write(" " + line + "\n");
@@ -1487,7 +1539,7 @@ import kleur3 from "kleur";
1487
1539
  var CLI_AI_MODEL = "claude-haiku-4-5-20251001";
1488
1540
 
1489
1541
  // src/notes.ts
1490
- var M3 = catalog(
1542
+ var M4 = catalog(
1491
1543
  {
1492
1544
  // Claude 프롬프트
1493
1545
  localeHint: (l) => `"${l}": "\uD574\uB2F9 \uC5B8\uC5B4\uB85C \uBC88\uC5ED\uB41C \uAC04\uACB0\uD55C \uBC84\uC804"`,
@@ -1623,21 +1675,21 @@ async function promptUser(question) {
1623
1675
  }
1624
1676
  async function generateWithClaude(commitsText, locales) {
1625
1677
  const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
1626
- const localeList = locales.map((l) => M3().localeHint(l)).join(",\n ");
1678
+ const localeList = locales.map((l) => M4().localeHint(l)).join(",\n ");
1627
1679
  const response = await client.messages.create({
1628
1680
  model: CLI_AI_MODEL,
1629
1681
  // mcp-server 의 generateReleaseNotes 기본값과 같아야 한다 — 낮으면 다국어 JSON 이
1630
1682
  // CLI 경로에서만 잘린다. ai-parity.test.ts 가 두 값의 일치를 강제한다.
1631
1683
  max_tokens: 2e3,
1632
- system: M3().system,
1684
+ system: M4().system,
1633
1685
  messages: [{
1634
1686
  role: "user",
1635
- content: M3().userPrompt(commitsText, localeList)
1687
+ content: M4().userPrompt(commitsText, localeList)
1636
1688
  }]
1637
1689
  });
1638
1690
  const text = response.content[0].type === "text" ? response.content[0].text : "";
1639
1691
  const match = text.match(/\{[\s\S]*\}/);
1640
- if (!match) throw new Error(M3().parseFailed);
1692
+ if (!match) throw new Error(M4().parseFailed);
1641
1693
  return JSON.parse(match[0]);
1642
1694
  }
1643
1695
  function generateTemplate(commits, locales) {
@@ -1647,7 +1699,7 @@ function generateTemplate(commits, locales) {
1647
1699
  });
1648
1700
  const concise = items.slice(0, 3).join("\n");
1649
1701
  const detailed = items.join("\n");
1650
- const marketing = M3().marketingTemplate(items.slice(0, 5).join("\n"));
1702
+ const marketing = M4().marketingTemplate(items.slice(0, 5).join("\n"));
1651
1703
  const localized = Object.fromEntries(locales.map((l) => [l, concise]));
1652
1704
  return { concise, detailed, marketing, localized };
1653
1705
  }
@@ -1662,43 +1714,43 @@ function parseFirstApp(text) {
1662
1714
  async function cmdNotes(argv) {
1663
1715
  const args = parseArgs(argv);
1664
1716
  const cwd = process.cwd();
1665
- process.stdout.write(kleur3.bold(M3().title));
1717
+ process.stdout.write(kleur3.bold(M4().title));
1666
1718
  if (!isGitRepo(cwd)) {
1667
- process.stdout.write(kleur3.red(M3().notGitRepo));
1719
+ process.stdout.write(kleur3.red(M4().notGitRepo));
1668
1720
  process.exit(1);
1669
1721
  }
1670
1722
  const latestTag = getLatestTag(cwd);
1671
1723
  const fromRef = args.from ?? latestTag ?? void 0;
1672
1724
  process.stdout.write(
1673
- kleur3.dim(fromRef ? M3().range(fromRef, args.to) : M3().recentCommits(args.limit))
1725
+ kleur3.dim(fromRef ? M4().range(fromRef, args.to) : M4().recentCommits(args.limit))
1674
1726
  );
1675
1727
  const commits = getGitLog(cwd, { from: fromRef, to: args.to, limit: args.limit });
1676
1728
  if (commits.length === 0) {
1677
- process.stdout.write(kleur3.yellow(M3().noCommits));
1729
+ process.stdout.write(kleur3.yellow(M4().noCommits));
1678
1730
  process.exit(0);
1679
1731
  }
1680
- process.stdout.write(kleur3.dim(M3().analyzing(commits.length)));
1732
+ process.stdout.write(kleur3.dim(M4().analyzing(commits.length)));
1681
1733
  let result;
1682
1734
  if (process.env.ANTHROPIC_API_KEY) {
1683
- process.stdout.write(M3().generating);
1735
+ process.stdout.write(M4().generating);
1684
1736
  try {
1685
1737
  result = await generateWithClaude(formatCommitsForPrompt(commits), args.locales);
1686
1738
  } catch (e) {
1687
- process.stdout.write(kleur3.yellow(M3().aiFailed(e.message)));
1739
+ process.stdout.write(kleur3.yellow(M4().aiFailed(e.message)));
1688
1740
  result = generateTemplate(commits, args.locales);
1689
1741
  }
1690
1742
  } else {
1691
- process.stdout.write(kleur3.dim(M3().noApiKey) + kleur3.dim(M3().noApiKeyHint));
1743
+ process.stdout.write(kleur3.dim(M4().noApiKey) + kleur3.dim(M4().noApiKeyHint));
1692
1744
  result = generateTemplate(commits, args.locales);
1693
1745
  }
1694
- process.stdout.write(kleur3.bold(M3().hdrConcise));
1746
+ process.stdout.write(kleur3.bold(M4().hdrConcise));
1695
1747
  process.stdout.write(result.concise + "\n\n");
1696
- process.stdout.write(kleur3.bold(M3().hdrDetailed));
1748
+ process.stdout.write(kleur3.bold(M4().hdrDetailed));
1697
1749
  process.stdout.write(result.detailed + "\n\n");
1698
- process.stdout.write(kleur3.bold(M3().hdrMarketing));
1750
+ process.stdout.write(kleur3.bold(M4().hdrMarketing));
1699
1751
  process.stdout.write(result.marketing + "\n\n");
1700
1752
  if (Object.keys(result.localized).length > 0) {
1701
- process.stdout.write(kleur3.bold(M3().hdrLocalized));
1753
+ process.stdout.write(kleur3.bold(M4().hdrLocalized));
1702
1754
  for (const [locale, text] of Object.entries(result.localized)) {
1703
1755
  process.stdout.write(kleur3.dim(`[${locale}]
1704
1756
  `) + text + "\n\n");
@@ -1708,15 +1760,15 @@ async function cmdNotes(argv) {
1708
1760
  if (!args.apply && !shouldPrompt) return;
1709
1761
  const cfg = await getEffectiveConfig();
1710
1762
  if (!cfg) {
1711
- process.stdout.write(kleur3.yellow(M3().needAccount));
1763
+ process.stdout.write(kleur3.yellow(M4().needAccount));
1712
1764
  return;
1713
1765
  }
1714
1766
  let selectedText = result.concise;
1715
1767
  let userSelectedTone = false;
1716
1768
  if (shouldPrompt) {
1717
- const choice = await promptUser(M3().choosePrompt);
1769
+ const choice = await promptUser(M4().choosePrompt);
1718
1770
  if (!choice) {
1719
- process.stdout.write(kleur3.dim(M3().skipped));
1771
+ process.stdout.write(kleur3.dim(M4().skipped));
1720
1772
  return;
1721
1773
  }
1722
1774
  if (choice === "2") selectedText = result.detailed;
@@ -1725,18 +1777,18 @@ async function cmdNotes(argv) {
1725
1777
  }
1726
1778
  const appsResult = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
1727
1779
  if (appsResult.isError) {
1728
- process.stdout.write(kleur3.red(M3().listAppsFailed(appsResult.text)));
1780
+ process.stdout.write(kleur3.red(M4().listAppsFailed(appsResult.text)));
1729
1781
  return;
1730
1782
  }
1731
1783
  const app = parseFirstApp(appsResult.text);
1732
1784
  if (!app) {
1733
- process.stdout.write(kleur3.yellow(M3().noApps));
1785
+ process.stdout.write(kleur3.yellow(M4().noApps));
1734
1786
  return;
1735
1787
  }
1736
1788
  if (userSelectedTone && args.locales.length > 1) {
1737
- process.stdout.write(kleur3.dim(M3().toneToAllLocales));
1789
+ process.stdout.write(kleur3.dim(M4().toneToAllLocales));
1738
1790
  }
1739
- process.stdout.write(M3().applying);
1791
+ process.stdout.write(M4().applying);
1740
1792
  for (const locale of args.locales) {
1741
1793
  const text = userSelectedTone ? selectedText : result.localized[locale] ?? selectedText;
1742
1794
  const r = await mcpCall(cfg.endpoint, cfg.token, "apply_release_notes", {
@@ -1746,7 +1798,7 @@ async function cmdNotes(argv) {
1746
1798
  text
1747
1799
  });
1748
1800
  process.stdout.write(
1749
- r.isError ? kleur3.red(M3().applyFailed(locale, r.text)) : kleur3.green(M3().applied(locale))
1801
+ r.isError ? kleur3.red(M4().applyFailed(locale, r.text)) : kleur3.green(M4().applied(locale))
1750
1802
  );
1751
1803
  }
1752
1804
  }
@@ -1755,7 +1807,7 @@ async function cmdNotes(argv) {
1755
1807
  import { createInterface as createInterface2 } from "readline/promises";
1756
1808
  import Anthropic2 from "@anthropic-ai/sdk";
1757
1809
  import kleur4 from "kleur";
1758
- var M4 = catalog(
1810
+ var M5 = catalog(
1759
1811
  {
1760
1812
  toneGuides: {
1761
1813
  friendly: "\uCE5C\uADFC\uD558\uACE0 \uB530\uB73B\uD558\uAC8C. \uC774\uBAA8\uC9C0 1~2\uAC1C \uC0AC\uC6A9. \uAC10\uC0AC \uC778\uC0AC\uB85C \uC2DC\uC791.",
@@ -1901,7 +1953,7 @@ function detectSentiment(text) {
1901
1953
  }
1902
1954
  async function generateReply(opts) {
1903
1955
  const client = new Anthropic2({ apiKey: process.env.ANTHROPIC_API_KEY });
1904
- const m = M4();
1956
+ const m = M5();
1905
1957
  const toneGuides = m.toneGuides;
1906
1958
  const sentimentPrompts = m.sentimentPrompts;
1907
1959
  const localeNames = m.localeNames;
@@ -1927,71 +1979,71 @@ async function generateReply(opts) {
1927
1979
  }
1928
1980
  async function cmdReview(argv) {
1929
1981
  const args = parseArgs2(argv);
1930
- process.stdout.write(kleur4.bold(M4().title));
1982
+ process.stdout.write(kleur4.bold(M5().title));
1931
1983
  if (!process.env.ANTHROPIC_API_KEY) {
1932
- process.stdout.write(kleur4.red(M4().noApiKey));
1933
- process.stdout.write(kleur4.dim(M4().noApiKeyHint));
1984
+ process.stdout.write(kleur4.red(M5().noApiKey));
1985
+ process.stdout.write(kleur4.dim(M5().noApiKeyHint));
1934
1986
  process.exit(1);
1935
1987
  }
1936
1988
  let reviewText = args.text;
1937
1989
  if (!reviewText) {
1938
1990
  if (args.noInteractive) {
1939
- process.stdout.write(kleur4.red(M4().textRequired));
1991
+ process.stdout.write(kleur4.red(M5().textRequired));
1940
1992
  process.exit(1);
1941
1993
  }
1942
- process.stdout.write(kleur4.dim(M4().enterReview));
1994
+ process.stdout.write(kleur4.dim(M5().enterReview));
1943
1995
  reviewText = await promptUser2("> ");
1944
1996
  if (!reviewText) {
1945
- process.stdout.write(kleur4.yellow(M4().noInput));
1997
+ process.stdout.write(kleur4.yellow(M5().noInput));
1946
1998
  process.exit(0);
1947
1999
  }
1948
2000
  }
1949
2001
  if (args.rating !== void 0) {
1950
2002
  const stars = "\u2605".repeat(args.rating) + "\u2606".repeat(5 - args.rating);
1951
- process.stdout.write(kleur4.dim(M4().meta(stars, args.tone, args.language)));
2003
+ process.stdout.write(kleur4.dim(M5().meta(stars, args.tone, args.language)));
1952
2004
  }
1953
- process.stdout.write(M4().generating);
2005
+ process.stdout.write(M5().generating);
1954
2006
  const reply = await generateReply({ ...args, text: reviewText });
1955
- process.stdout.write("\n" + kleur4.bold(M4().hdrReply));
2007
+ process.stdout.write("\n" + kleur4.bold(M5().hdrReply));
1956
2008
  process.stdout.write(reply + "\n");
1957
- process.stdout.write(kleur4.dim(M4().draftWarning));
2009
+ process.stdout.write(kleur4.dim(M5().draftWarning));
1958
2010
  const shouldPrompt = !args.apply && !args.noInteractive && process.stdout.isTTY;
1959
2011
  if (!args.apply && !shouldPrompt) return;
1960
2012
  let doApply = args.apply;
1961
2013
  if (shouldPrompt) {
1962
- const ans = await promptUser2(M4().postPrompt);
2014
+ const ans = await promptUser2(M5().postPrompt);
1963
2015
  doApply = ans.toLowerCase() === "y";
1964
2016
  }
1965
2017
  if (!doApply) {
1966
- process.stdout.write(kleur4.dim(M4().skipped));
2018
+ process.stdout.write(kleur4.dim(M5().skipped));
1967
2019
  return;
1968
2020
  }
1969
2021
  const cfg = await getEffectiveConfig();
1970
2022
  if (!cfg) {
1971
- process.stdout.write(kleur4.yellow(M4().needAccount));
2023
+ process.stdout.write(kleur4.yellow(M5().needAccount));
1972
2024
  return;
1973
2025
  }
1974
2026
  let reviewId = args.reviewId;
1975
2027
  let packageName = args.packageName;
1976
2028
  if (!reviewId || !packageName) {
1977
2029
  if (args.noInteractive) {
1978
- process.stdout.write(kleur4.red(M4().idsRequired));
2030
+ process.stdout.write(kleur4.red(M5().idsRequired));
1979
2031
  process.exit(1);
1980
2032
  }
1981
- if (!packageName) packageName = await promptUser2(M4().packagePrompt);
1982
- if (!reviewId) reviewId = await promptUser2(M4().reviewIdPrompt);
2033
+ if (!packageName) packageName = await promptUser2(M5().packagePrompt);
2034
+ if (!reviewId) reviewId = await promptUser2(M5().reviewIdPrompt);
1983
2035
  }
1984
2036
  if (!reviewId || !packageName) {
1985
- process.stdout.write(kleur4.yellow(M4().notEnoughInfo));
2037
+ process.stdout.write(kleur4.yellow(M5().notEnoughInfo));
1986
2038
  return;
1987
2039
  }
1988
- process.stdout.write(M4().posting);
2040
+ process.stdout.write(M5().posting);
1989
2041
  const r = await mcpCall(cfg.endpoint, cfg.token, "playstore_reply_review", {
1990
2042
  package_name: packageName,
1991
2043
  review_id: reviewId,
1992
2044
  reply_text: reply
1993
2045
  });
1994
- process.stdout.write(r.isError ? kleur4.red(M4().postFailed(r.text)) : kleur4.green(M4().posted));
2046
+ process.stdout.write(r.isError ? kleur4.red(M5().postFailed(r.text)) : kleur4.green(M5().posted));
1995
2047
  }
1996
2048
 
1997
2049
  // src/auth.ts
@@ -1999,7 +2051,7 @@ import kleur5 from "kleur";
1999
2051
  function log(msg) {
2000
2052
  process.stdout.write(msg + "\n");
2001
2053
  }
2002
- var M5 = catalog(
2054
+ var M6 = catalog(
2003
2055
  {
2004
2056
  help: `${kleur5.bold("mimi-seed auth")} \u2014 \uB85C\uCEEC \uC790\uACA9\uC99D\uBA85 \uC778\uC99D/\uAD00\uB9AC
2005
2057
 
@@ -2082,7 +2134,7 @@ ${kleur5.dim(`Under the hood this calls the mimi-seed-*-auth CLIs from ${MCP_PKG
2082
2134
  }
2083
2135
  );
2084
2136
  function printAuthHelp() {
2085
- log(M5().help);
2137
+ log(M6().help);
2086
2138
  }
2087
2139
  function printAllCredStatus() {
2088
2140
  log(kleur5.bold(t().auth.statusTitle + " ") + kleur5.dim("(~/.mimi-seed)"));
@@ -2093,7 +2145,7 @@ function printAllCredStatus() {
2093
2145
  const tail = d.freshness === "expired" ? kleur5.red(`${t().setup.tokenExpired} \u2192 ${spec.fix}`) : d.freshness === "expiring" ? kleur5.yellow(`${t().setup.tokenExpiring(d.daysRemaining ?? 0)} \u2192 ${spec.fix}`) : d.present ? kleur5.dim(d.detail ?? "") : kleur5.dim(`\u2192 ${spec.fix}`);
2094
2146
  log(` ${mark} ${credLabel(spec).padEnd(24)} ${tail}`);
2095
2147
  }
2096
- log(kleur5.dim(M5().connectAll));
2148
+ log(kleur5.dim(M6().connectAll));
2097
2149
  }
2098
2150
  async function cmdAuth(args) {
2099
2151
  const sub = args[0];
@@ -2117,7 +2169,7 @@ async function cmdAuth(args) {
2117
2169
  if (sub === "threads") return void exitWith(await runMcpBin("mimi-seed-social-auth", ["threads", ...rest]));
2118
2170
  if (sub === "tiktok") return void exitWith(await runMcpBin("mimi-seed-tiktok-business-auth", rest));
2119
2171
  if (sub === "ci") {
2120
- const { cmdSetup: cmdSetup2 } = await import("./setup-VO5WN32U.js");
2172
+ const { cmdSetup: cmdSetup2 } = await import("./setup-Z34QP6PO.js");
2121
2173
  await cmdSetup2(["--only", "github,gitlab", "--reconnect", "github,gitlab"]);
2122
2174
  return;
2123
2175
  }
@@ -2200,7 +2252,7 @@ import kleur7 from "kleur";
2200
2252
  function log3(msg) {
2201
2253
  process.stdout.write(msg + "\n");
2202
2254
  }
2203
- var M6 = catalog(
2255
+ var M7 = catalog(
2204
2256
  {
2205
2257
  killedPid: (pid) => ` PID ${pid} \uC885\uB8CC`,
2206
2258
  title: (server) => `MCP \uC11C\uBC84 \uC7AC\uC2DC\uC791: ${server}`,
@@ -2271,7 +2323,7 @@ function detectMcpClient(env = process.env) {
2271
2323
  return "unknown";
2272
2324
  }
2273
2325
  function recoveryMessages(client, killed) {
2274
- const m = M6();
2326
+ const m = M7();
2275
2327
  if (client === "codex") {
2276
2328
  return { hint: killed ? m.codexKilledHint : m.codexNoProcessHint, verify: m.codexVerify };
2277
2329
  }
@@ -2363,7 +2415,7 @@ function killByMarkers(markers) {
2363
2415
  for (const pid of pids) {
2364
2416
  try {
2365
2417
  execSync2(`taskkill /F /PID ${pid}`, { stdio: "pipe" });
2366
- log3(kleur7.dim(M6().killedPid(pid)));
2418
+ log3(kleur7.dim(M7().killedPid(pid)));
2367
2419
  killed++;
2368
2420
  } catch {
2369
2421
  }
@@ -2374,7 +2426,7 @@ function killByMarkers(markers) {
2374
2426
  for (const pid of findPids(markers)) {
2375
2427
  try {
2376
2428
  process.kill(pid, "SIGTERM");
2377
- log3(kleur7.dim(M6().killedPid(String(pid))));
2429
+ log3(kleur7.dim(M7().killedPid(String(pid))));
2378
2430
  killed += 1;
2379
2431
  } catch {
2380
2432
  }
@@ -2384,40 +2436,40 @@ function killByMarkers(markers) {
2384
2436
  }
2385
2437
  async function cmdRestart(args) {
2386
2438
  const serverName = args[0] ?? "mimi-seed";
2387
- log3(kleur7.bold(M6().title(serverName)));
2439
+ log3(kleur7.bold(M7().title(serverName)));
2388
2440
  log3("");
2389
2441
  const { servers, sources } = collectServers();
2390
2442
  const cfg = resolveServerConfig(servers, serverName);
2391
2443
  if (!cfg) {
2392
2444
  const names = Object.keys(servers);
2393
- const available = names.length ? names.join(", ") : M6().none;
2394
- log3(kleur7.red(M6().notFound(serverName)));
2395
- log3(kleur7.dim(M6().searched(sources.length ? sources.join(", ") : M6().noConfig)));
2396
- log3(kleur7.dim(M6().registered(available)));
2445
+ const available = names.length ? names.join(", ") : M7().none;
2446
+ log3(kleur7.red(M7().notFound(serverName)));
2447
+ log3(kleur7.dim(M7().searched(sources.length ? sources.join(", ") : M7().noConfig)));
2448
+ log3(kleur7.dim(M7().registered(available)));
2397
2449
  process.exit(1);
2398
2450
  }
2399
2451
  if (cfg.type === "http" || cfg.type === "sse") {
2400
- log3(kleur7.yellow(M6().httpServer(serverName)));
2401
- log3(kleur7.dim(M6().httpHint));
2452
+ log3(kleur7.yellow(M7().httpServer(serverName)));
2453
+ log3(kleur7.dim(M7().httpHint));
2402
2454
  return;
2403
2455
  }
2404
2456
  const markers = candidateMarkers(cfg);
2405
2457
  const marker = markers[0] ?? null;
2406
2458
  if (!marker) {
2407
- log3(kleur7.yellow(M6().noMarker));
2408
- log3(kleur7.dim(M6().configLine(JSON.stringify(cfg))));
2459
+ log3(kleur7.yellow(M7().noMarker));
2460
+ log3(kleur7.dim(M7().configLine(JSON.stringify(cfg))));
2409
2461
  process.exit(1);
2410
2462
  }
2411
- log3(kleur7.dim(M6().markerLine(marker)));
2463
+ log3(kleur7.dim(M7().markerLine(marker)));
2412
2464
  const { killed } = killByMarkers(markers);
2413
2465
  const recovery = recoveryMessages(detectMcpClient(), killed > 0);
2414
2466
  if (killed === 0) {
2415
- log3(kleur7.yellow(M6().noProcess));
2467
+ log3(kleur7.yellow(M7().noProcess));
2416
2468
  log3(kleur7.dim(recovery.hint));
2417
- log3(kleur7.yellow(M6().unknownWriteOutcome));
2469
+ log3(kleur7.yellow(M7().unknownWriteOutcome));
2418
2470
  } else {
2419
2471
  log3("");
2420
- log3(kleur7.green(M6().killed(serverName, killed)));
2472
+ log3(kleur7.green(M7().killed(serverName, killed)));
2421
2473
  log3(kleur7.dim(recovery.hint));
2422
2474
  }
2423
2475
  log3("");
@@ -2432,7 +2484,7 @@ import kleur8 from "kleur";
2432
2484
  var SERVER_NAME = "mimi-seed";
2433
2485
  var CODEX_REMOTE_NAME = "mimi-seed-remote";
2434
2486
  var CODEX_TOKEN_ENV = "MIMI_SEED_TOKEN";
2435
- var M7 = catalog(
2487
+ var M8 = catalog(
2436
2488
  {
2437
2489
  claudeTitle: "Claude Code MCP \uB4F1\uB85D \u2014 \uC544\uB798 \uD55C \uC904\uC744 \uADF8\uB300\uB85C \uC2E4\uD589\uD558\uC138\uC694:",
2438
2490
  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.`,
@@ -2478,14 +2530,14 @@ function claudeMcpAddCommand(cfg) {
2478
2530
  function printMcpSetup(cfg) {
2479
2531
  process.stdout.write(
2480
2532
  [
2481
- kleur8.bold(M7().claudeTitle),
2533
+ kleur8.bold(M8().claudeTitle),
2482
2534
  kleur8.cyan(` ${claudeMcpAddCommand(cfg)}`),
2483
- kleur8.dim(M7().tokenWarning(CONFIG_LOCATION_HINT)),
2535
+ kleur8.dim(M8().tokenWarning(CONFIG_LOCATION_HINT)),
2484
2536
  "",
2485
- kleur8.dim(M7().codexTitle),
2537
+ kleur8.dim(M8().codexTitle),
2486
2538
  kleur8.dim(" mimi-seed mcp codex --write"),
2487
- kleur8.dim(M7().codexEnvNote),
2488
- kleur8.dim(M7().codexManual)
2539
+ kleur8.dim(M8().codexEnvNote),
2540
+ kleur8.dim(M8().codexManual)
2489
2541
  ].join("\n") + "\n"
2490
2542
  );
2491
2543
  }
@@ -2563,7 +2615,7 @@ async function ensureReleaseManifest(cwd) {
2563
2615
  // src/index.ts
2564
2616
  var DEFAULT_WEB_BASE = process.env.MIMI_SEED_WEB_BASE ?? "https://mimi-seed.pryzm.gg";
2565
2617
  var DEFAULT_MCP_ENDPOINT = `${DEFAULT_WEB_BASE}/api/mcp`;
2566
- var M8 = catalog(
2618
+ var M9 = catalog(
2567
2619
  {
2568
2620
  noProjectSignal: "\u26A0 package.json / app.json / android / ios \uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uADF8\uB798\uB3C4 \uC9C4\uD589\uD569\uB2C8\uB2E4.",
2569
2621
  detecting: "\u{1F50D} \uC571 \uAC10\uC9C0 \uC911...",
@@ -2614,6 +2666,7 @@ var M8 = catalog(
2614
2666
  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.",
2615
2667
  // 명령별 상세 사용법 (SSOT). `mimi-seed <command> --help` 와 overview 가 공유.
2616
2668
  usage: {
2669
+ telemetry: "mimi-seed telemetry on|off|status \u2014 \uC120\uD0DD\uC801 \uC0AC\uC6A9\uB7C9 \uCE21\uC815. \uAE30\uBCF8 \uAEBC\uC9D0. \uC784\uC758 \uC124\uCE58 ID, \uD504\uB85C\uC81D\uD2B8 \uD574\uC2DC, \uBC84\uC804, OS, \uACB0\uACFC \uCF54\uB4DC\uC640 \uC2E4\uD589\uC2DC\uAC04\uC744 \uC804\uC1A1\uD569\uB2C8\uB2E4. \uAC1C\uC778\uC815\uBCF4 \uC548\uB0B4: https://mimi-seed.pryzm.gg/privacy/sdk-usage . MIMI_SEED_TELEMETRY=0\uC740 \uD56D\uC0C1 \uC804\uC1A1\uC744 \uB055\uB2C8\uB2E4.",
2617
2670
  init: `${kleur9.bold("mimi-seed init")} \u2014 \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
2618
2671
 
2619
2672
  \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
@@ -2683,17 +2736,17 @@ ${kleur9.dim("setup \uB9C8\uBC95\uC0AC\uAC00 \uCCAB \uC2E4\uD589 \uB54C \uBB3C\u
2683
2736
 
2684
2737
  \uC635\uC158:
2685
2738
  --platform android|ios \uBC30\uD3EC \uD50C\uB7AB\uD3FC (\uAE30\uBCF8: android)
2686
- --app <id> \uC571 ID \uC9C0\uC815
2687
- --version-code <n> \uBE4C\uB4DC \uBC88\uD638 \uC9C1\uC811 \uC9C0\uC815 (--skip-build \uC640 \uD568\uAED8)
2739
+ --app <id> \uBC30\uD3EC\uD560 \uC6D0\uACA9 \uC571 ID (\uC2E4\uC81C \uC2E4\uD589 \uD544\uC218)
2740
+ --version-code <n> \uC2E4\uC81C \uC2A4\uD1A0\uC5B4 \uBE4C\uB4DC \uBC88\uD638 (CI \uC2E4\uD589 \uBC88\uD638 \uC544\uB2D8)
2688
2741
  --from <ref> \uCEE4\uBC0B \uBC94\uC704 \uC2DC\uC791 (\uB9B4\uB9AC\uC988 \uB178\uD2B8\uC6A9)
2689
2742
  --to <ref> \uCEE4\uBC0B \uBC94\uC704 \uB05D (\uAE30\uBCF8: HEAD)
2690
2743
  --language <\uCF54\uB4DC> \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC5B8\uC5B4 (\uAE30\uBCF8: ko-KR)
2691
- --dry-run \uC2E4\uC81C \uBC30\uD3EC \uC5C6\uC774 \uD30C\uC774\uD504\uB77C\uC778 \uD14C\uC2A4\uD2B8
2692
- --yes, -y \uBC30\uD3EC \uD655\uC778 \uD504\uB86C\uD504\uD2B8 \uC0DD\uB7B5 (\uC2A4\uD06C\uB9BD\uD2B8/\uC790\uB3D9\uD654\uC6A9)
2744
+ --dry-run \uB85C\uCEEC \uACC4\uD68D\uB9CC \uCD9C\uB825 (\uB124\uD2B8\uC6CC\uD06C\xB7\uBE4C\uB4DC\xB7\uC4F0\uAE30 \uC5C6\uC74C)
2745
+ --yes, -y CI \uC2E4\uD589\xB7\uC2A4\uD1A0\uC5B4 \uBC30\uD3EC \uBA85\uC2DC\uC801 \uC2B9\uC778 (\uC2E4\uD589 \uD544\uC218)
2693
2746
  --skip-build CI \uBE4C\uB4DC \uAC74\uB108\uB700 (--version-code \uD544\uC218)
2694
2747
  --ci jenkins|github|gitlab CI \uAC15\uC81C \uC120\uD0DD (\uAE30\uBCF8: auto)
2695
2748
  --workflow <file> GitHub workflow \uD30C\uC77C (\uC608: deploy.yml)
2696
- --ref <branch|tag> GitHub/GitLab \uBE0C\uB79C\uCE58/\uD0DC\uADF8 (\uAE30\uBCF8: main)
2749
+ --ref <branch|tag> CI \uC18C\uC2A4 (\uAE30\uBCF8: main, Jenkins\uB294 \uBE0C\uB79C\uCE58\uB9CC)
2697
2750
  setup-jenkins / setup-github / setup-gitlab CI \uC124\uC815 \uB300\uD654\uD615 \uB4F1\uB85D`,
2698
2751
  mcp: `${kleur9.bold("mimi-seed mcp")} \u2014 Claude/Codex MCP \uC5F0\uACB0
2699
2752
 
@@ -2716,6 +2769,7 @@ ${kleur9.bold("\uBA85\uB839\uC5B4:")}
2716
2769
  ${kleur9.cyan("mimi-seed ga4")} GA4 property\xB7data stream \uC0DD\uC131\xB7\uC870\uD68C
2717
2770
  ${kleur9.cyan("mimi-seed doctor")} \uD658\uACBD \uC9C4\uB2E8 (\uD1A0\uD070\xB7Git\xB7\uD504\uB85C\uC81D\uD2B8\xB7CI \uCCB4\uD06C)
2718
2771
  ${kleur9.cyan("mimi-seed check")} \uCD9C\uC2DC \uC804 Readiness \uC810\uAC80
2772
+ ${kleur9.cyan("mimi-seed telemetry")} \uC120\uD0DD\uC801 \uC0AC\uC6A9\uB7C9 \uCE21\uC815 \uC124\uC815
2719
2773
  ${kleur9.cyan("mimi-seed notes")} \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 (git log \u2192 AI \u2192 \uB9C8\uCF13 \uC801\uC6A9)
2720
2774
  ${kleur9.cyan("mimi-seed review")} \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
2721
2775
  ${kleur9.cyan("mimi-seed deploy")} \uC571 \uC790\uB3D9 \uBC30\uD3EC (CI \u2192 Play Store/App Store)
@@ -2783,6 +2837,7 @@ ${kleur9.bold("\uD658\uACBD\uBCC0\uC218:")}
2783
2837
  claudeTitle: "Claude Code MCP registration \u2014 run this one line as-is:",
2784
2838
  claudeWarn: " \u26A0 This command contains the real token (it stays in your shell history). The token is also stored in ~/.mimi-seed/config.json.",
2785
2839
  usage: {
2840
+ telemetry: "mimi-seed telemetry on|off|status \u2014 optional usage measurement, off by default. Sends a random installation ID, project hash, version, OS, result codes and duration. Privacy: https://mimi-seed.pryzm.gg/privacy/sdk-usage . MIMI_SEED_TELEMETRY=0 always disables transmission.",
2786
2841
  init: `${kleur9.bold("mimi-seed init")} \u2014 connect the current project to Mimi Seed
2787
2842
 
2788
2843
  Auto-detects apps (Expo/Gradle/Info.plist/pbxproj) \u2192 issues a PAT in the browser (remote MCP) \u2192 registers the apps
@@ -2852,17 +2907,17 @@ Options:
2852
2907
 
2853
2908
  Options:
2854
2909
  --platform android|ios target platform (default: android)
2855
- --app <id> app ID
2856
- --version-code <n> set the build number explicitly (with --skip-build)
2910
+ --app <id> remote app ID (required for execution)
2911
+ --version-code <n> actual store build number (not a CI run ID)
2857
2912
  --from <ref> commit range start (for release notes)
2858
2913
  --to <ref> commit range end (default: HEAD)
2859
2914
  --language <code> release notes language (default: ko-KR)
2860
- --dry-run exercise the pipeline without releasing
2861
- --yes, -y skip the release confirmation prompt (scripts/automation)
2915
+ --dry-run print a local plan (no network, builds or writes)
2916
+ --yes, -y explicitly approve CI and store release (required)
2862
2917
  --skip-build skip the CI build (--version-code required)
2863
2918
  --ci jenkins|github|gitlab force the CI provider (default: auto)
2864
2919
  --workflow <file> GitHub workflow file (e.g. deploy.yml)
2865
- --ref <branch|tag> GitHub/GitLab branch/tag (default: main)
2920
+ --ref <branch|tag> CI source (default: main; Jenkins: branches only)
2866
2921
  setup-jenkins / setup-github / setup-gitlab interactive CI setup`,
2867
2922
  mcp: `${kleur9.bold("mimi-seed mcp")} \u2014 Claude/Codex MCP connection
2868
2923
 
@@ -2885,6 +2940,7 @@ ${kleur9.bold("Commands:")}
2885
2940
  ${kleur9.cyan("mimi-seed ga4")} create and list GA4 properties and data streams
2886
2941
  ${kleur9.cyan("mimi-seed doctor")} environment check (token \xB7 Git \xB7 project \xB7 CI)
2887
2942
  ${kleur9.cyan("mimi-seed check")} pre-release readiness check
2943
+ ${kleur9.cyan("mimi-seed telemetry")} optional usage measurement settings
2888
2944
  ${kleur9.cyan("mimi-seed notes")} generate release notes (git log \u2192 AI \u2192 push to stores)
2889
2945
  ${kleur9.cyan("mimi-seed review")} draft a review reply with AI and post it to the Play Store
2890
2946
  ${kleur9.cyan("mimi-seed deploy")} automated app release (CI \u2192 Play Store/App Store)
@@ -2914,37 +2970,37 @@ async function cmdInit(args) {
2914
2970
  log4(kleur9.dim(cwd));
2915
2971
  log4("");
2916
2972
  if (!await hasAnyProjectSignal(cwd)) {
2917
- log4(kleur9.yellow(M8().noProjectSignal));
2973
+ log4(kleur9.yellow(M9().noProjectSignal));
2918
2974
  }
2919
- log4(M8().detecting);
2975
+ log4(M9().detecting);
2920
2976
  const hints = await detectHints(cwd);
2921
2977
  if (hints.length === 0) {
2922
- log4(kleur9.yellow(M8().noAppDetected));
2978
+ log4(kleur9.yellow(M9().noAppDetected));
2923
2979
  } else {
2924
2980
  for (const h of hints) {
2925
2981
  const tag = [h.packageName && `android:${h.packageName}`, h.bundleId && `ios:${h.bundleId}`].filter(Boolean).join(" ");
2926
- log4(` \u2022 ${h.name ?? M8().unnamed} ${kleur9.dim(tag)}`);
2982
+ log4(` \u2022 ${h.name ?? M9().unnamed} ${kleur9.dim(tag)}`);
2927
2983
  }
2928
2984
  }
2929
2985
  log4("");
2930
2986
  if (process.env.MIMI_SEED_TOKEN) {
2931
2987
  const cfg2 = await getEffectiveConfig();
2932
2988
  if (cfg2 && hints.length > 0) {
2933
- log4(kleur9.dim(M8().ciMode));
2989
+ log4(kleur9.dim(M9().ciMode));
2934
2990
  const payload = hints.map((h) => ({ name: h.name, packageName: h.packageName, bundleId: h.bundleId }));
2935
2991
  const result = await mcpCall(cfg2.endpoint, cfg2.token, "sync_apps", { hints: payload });
2936
2992
  if (result.isError) {
2937
- log4(kleur9.red(M8().syncFailed(result.text)));
2993
+ log4(kleur9.red(M9().syncFailed(result.text)));
2938
2994
  } else {
2939
2995
  for (const line of result.text.split("\n")) log4(" " + line);
2940
2996
  }
2941
2997
  }
2942
2998
  const manifest2 = await ensureReleaseManifest(cwd);
2943
- log4(kleur9.dim(M8().manifest(manifest2.created)));
2944
- log4(kleur9.bold(M8().doneCi));
2999
+ log4(kleur9.dim(M9().manifest(manifest2.created)));
3000
+ log4(kleur9.bold(M9().doneCi));
2945
3001
  return;
2946
3002
  }
2947
- log4(M8().waitingLogin);
3003
+ log4(M9().waitingLogin);
2948
3004
  const hostName = os3.hostname().slice(0, 32);
2949
3005
  const name = `cli-${hostName}`;
2950
3006
  const { port, promise } = await awaitHandshake(5 * 60 * 1e3);
@@ -2956,10 +3012,10 @@ async function cmdInit(args) {
2956
3012
  try {
2957
3013
  handshake = await promise;
2958
3014
  } catch (e) {
2959
- log4(kleur9.red(M8().connectFailed(e.message)));
3015
+ log4(kleur9.red(M9().connectFailed(e.message)));
2960
3016
  process.exit(1);
2961
3017
  }
2962
- log4(kleur9.green(M8().tokenReceived));
3018
+ log4(kleur9.green(M9().tokenReceived));
2963
3019
  const cfg = {
2964
3020
  token: handshake.token,
2965
3021
  prefix: handshake.prefix,
@@ -2968,14 +3024,14 @@ async function cmdInit(args) {
2968
3024
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
2969
3025
  };
2970
3026
  await writeConfig(cfg);
2971
- log4(kleur9.dim(M8().saved(CONFIG_LOCATION)));
3027
+ log4(kleur9.dim(M9().saved(CONFIG_LOCATION)));
2972
3028
  log4("");
2973
3029
  if (hints.length > 0) {
2974
- log4(M8().registering);
3030
+ log4(M9().registering);
2975
3031
  const payload = hints.map((h) => ({ name: h.name, packageName: h.packageName, bundleId: h.bundleId }));
2976
3032
  const result = await mcpCall(cfg.endpoint, cfg.token, "sync_apps", { hints: payload });
2977
3033
  if (result.isError) {
2978
- log4(kleur9.red(M8().syncFailed(result.text)));
3034
+ log4(kleur9.red(M9().syncFailed(result.text)));
2979
3035
  } else {
2980
3036
  for (const line of result.text.split("\n")) log4(" " + line);
2981
3037
  }
@@ -3029,94 +3085,94 @@ async function cmdInit(args) {
3029
3085
  if (!fs6.existsSync(claudeAgentPath)) {
3030
3086
  fs6.mkdirSync(claudeDir, { recursive: true });
3031
3087
  fs6.writeFileSync(claudeAgentPath, agentMd, { mode: 420 });
3032
- log4(kleur9.dim(M8().claudeAgent));
3088
+ log4(kleur9.dim(M9().claudeAgent));
3033
3089
  }
3034
3090
  const codexAgentPath = path6.join(cwd, "AGENTS.md");
3035
3091
  if (!fs6.existsSync(codexAgentPath)) {
3036
3092
  fs6.writeFileSync(codexAgentPath, agentMd, { mode: 420 });
3037
- log4(kleur9.dim(M8().codexAgent));
3093
+ log4(kleur9.dim(M9().codexAgent));
3038
3094
  }
3039
3095
  const manifest = await ensureReleaseManifest(cwd);
3040
- log4(kleur9.dim(M8().manifest(manifest.created)));
3041
- log4(kleur9.bold(M8().ready));
3096
+ log4(kleur9.dim(M9().manifest(manifest.created)));
3097
+ log4(kleur9.bold(M9().ready));
3042
3098
  log4("");
3043
- log4(M8().askLike);
3044
- log4(kleur9.cyan(M8().ask1));
3045
- log4(kleur9.cyan(M8().ask2));
3046
- log4(kleur9.cyan(M8().ask3));
3099
+ log4(M9().askLike);
3100
+ log4(kleur9.cyan(M9().ask1));
3101
+ log4(kleur9.cyan(M9().ask2));
3102
+ log4(kleur9.cyan(M9().ask3));
3047
3103
  log4("");
3048
- log4(M8().dashboard(kleur9.underline(DEFAULT_WEB_BASE + "/apps")));
3104
+ log4(M9().dashboard(kleur9.underline(DEFAULT_WEB_BASE + "/apps")));
3049
3105
  log4("");
3050
3106
  printMcpSetup(cfg);
3051
3107
  if (!local) {
3052
3108
  log4("");
3053
- log4(kleur9.bold(M8().nextTitle));
3054
- log4(kleur9.cyan(M8().nextSetup));
3055
- log4(kleur9.cyan(M8().nextDoctor));
3109
+ log4(kleur9.bold(M9().nextTitle));
3110
+ log4(kleur9.cyan(M9().nextSetup));
3111
+ log4(kleur9.cyan(M9().nextDoctor));
3056
3112
  }
3057
3113
  if (local) {
3058
3114
  log4("");
3059
- log4(kleur9.bold(M8().localTitle));
3060
- log4(kleur9.dim(M8().localIntro));
3115
+ log4(kleur9.bold(M9().localTitle));
3116
+ log4(kleur9.dim(M9().localIntro));
3061
3117
  log4("");
3062
- log4(M8().localStep1);
3118
+ log4(M9().localStep1);
3063
3119
  await cmdAuth(["login"]);
3064
3120
  log4("");
3065
- log4(M8().localStep2);
3121
+ log4(M9().localStep2);
3066
3122
  log4(kleur9.cyan(" claude mcp add mimi-seed-local -- npx -y @yoonion/mimi-seed-mcp@latest"));
3067
- log4(kleur9.dim(M8().localCodexHint));
3123
+ log4(kleur9.dim(M9().localCodexHint));
3068
3124
  log4("");
3069
- log4(M8().localStep3);
3125
+ log4(M9().localStep3);
3070
3126
  log4(kleur9.cyan(" mimi-seed setup"));
3071
- log4(kleur9.dim(M8().localSetupHint));
3127
+ log4(kleur9.dim(M9().localSetupHint));
3072
3128
  }
3073
3129
  }
3074
3130
  async function cmdStatus() {
3075
3131
  const cfg = await getEffectiveConfig();
3076
3132
  if (!cfg) {
3077
- log4(kleur9.yellow(M8().notConnected));
3133
+ log4(kleur9.yellow(M9().notConnected));
3078
3134
  process.exit(1);
3079
3135
  }
3080
- log4(kleur9.bold(M8().statusTitle));
3081
- log4(M8().statusToken(cfg.prefix, cfg.createdAt.slice(0, 10)));
3082
- log4(M8().statusEndpoint(cfg.endpoint));
3136
+ log4(kleur9.bold(M9().statusTitle));
3137
+ log4(M9().statusToken(cfg.prefix, cfg.createdAt.slice(0, 10)));
3138
+ log4(M9().statusEndpoint(cfg.endpoint));
3083
3139
  log4("");
3084
- log4(M8().appList);
3140
+ log4(M9().appList);
3085
3141
  const r = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
3086
3142
  if (r.isError) {
3087
- log4(kleur9.red(M8().listFailed(r.text)));
3143
+ log4(kleur9.red(M9().listFailed(r.text)));
3088
3144
  process.exit(1);
3089
3145
  }
3090
3146
  for (const line of r.text.split("\n")) log4(" " + line);
3091
3147
  }
3092
3148
  async function cmdLogout() {
3093
3149
  await deleteConfig();
3094
- log4(kleur9.green(M8().logoutDone));
3095
- log4(kleur9.dim(M8().logoutRevoke));
3150
+ log4(kleur9.green(M9().logoutDone));
3151
+ log4(kleur9.dim(M9().logoutRevoke));
3096
3152
  }
3097
3153
  async function cmdMcp(args) {
3098
3154
  const target = args[0] ?? "help";
3099
3155
  const cfg = await getEffectiveConfig();
3100
3156
  if (!cfg) {
3101
- log4(kleur9.yellow(M8().notConnected));
3157
+ log4(kleur9.yellow(M9().notConnected));
3102
3158
  process.exit(1);
3103
3159
  }
3104
3160
  if (target === "codex") {
3105
3161
  if (args.includes("--write")) {
3106
3162
  const configPath = await writeCodexMcpConfig(cfg);
3107
- log4(kleur9.green(M8().codexWritten));
3163
+ log4(kleur9.green(M9().codexWritten));
3108
3164
  log4(kleur9.dim(` ${configPath}`));
3109
- log4(kleur9.dim(M8().codexWriteWarn));
3165
+ log4(kleur9.dim(M9().codexWriteWarn));
3110
3166
  log4("");
3111
- log4(M8().codexVerify);
3167
+ log4(M9().codexVerify);
3112
3168
  return;
3113
3169
  }
3114
- log4(kleur9.bold(M8().codexTitle));
3170
+ log4(kleur9.bold(M9().codexTitle));
3115
3171
  log4("");
3116
- log4(M8().codexAuto);
3172
+ log4(M9().codexAuto);
3117
3173
  log4(kleur9.cyan(" mimi-seed mcp codex --write"));
3118
3174
  log4("");
3119
- log4(M8().codexManual);
3175
+ log4(M9().codexManual);
3120
3176
  log4(`[mcp_servers.mimi-seed-remote]
3121
3177
  url = "${cfg.endpoint}"
3122
3178
  bearer_token_env_var = "MIMI_SEED_TOKEN"
@@ -3124,21 +3180,21 @@ enabled = true`);
3124
3180
  return;
3125
3181
  }
3126
3182
  if (target === "claude") {
3127
- log4(kleur9.bold(M8().claudeTitle));
3183
+ log4(kleur9.bold(M9().claudeTitle));
3128
3184
  log4(kleur9.cyan(` ${claudeMcpAddCommand(cfg)}`));
3129
- log4(kleur9.dim(M8().claudeWarn));
3185
+ log4(kleur9.dim(M9().claudeWarn));
3130
3186
  return;
3131
3187
  }
3132
3188
  printMcpSetup(cfg);
3133
3189
  }
3134
3190
  function printCommandHelp(cmd) {
3135
- const usage = M8().usage[cmd];
3191
+ const usage = M9().usage[cmd];
3136
3192
  if (!usage) return false;
3137
3193
  log4(usage);
3138
3194
  return true;
3139
3195
  }
3140
3196
  function printHelp() {
3141
- log4(M8().help);
3197
+ log4(M9().help);
3142
3198
  }
3143
3199
  async function main() {
3144
3200
  const cmd = process.argv[2];
@@ -3166,6 +3222,9 @@ async function main() {
3166
3222
  case "check":
3167
3223
  await cmdCheck(restArgs);
3168
3224
  break;
3225
+ case "telemetry":
3226
+ cmdTelemetry(restArgs);
3227
+ break;
3169
3228
  case "notes":
3170
3229
  await cmdNotes(restArgs);
3171
3230
  break;