mimi-seed 0.3.0 → 0.4.1

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.
Files changed (2) hide show
  1. package/dist/index.js +192 -57
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5,8 +5,8 @@ import {
5
5
 
6
6
  // src/index.ts
7
7
  import os7 from "os";
8
- import fs8 from "fs";
9
- import path8 from "path";
8
+ import fs10 from "fs";
9
+ import path10 from "path";
10
10
  import kleur9 from "kleur";
11
11
  import open from "open";
12
12
 
@@ -255,9 +255,9 @@ async function getEffectiveConfig() {
255
255
  }
256
256
 
257
257
  // src/doctor.ts
258
- import fs3 from "fs";
258
+ import fs4 from "fs";
259
259
  import os2 from "os";
260
- import path3 from "path";
260
+ import path4 from "path";
261
261
  import kleur from "kleur";
262
262
 
263
263
  // src/git.ts
@@ -300,6 +300,33 @@ function formatCommitsForPrompt(commits) {
300
300
  return commits.map((c) => `- ${c.message} (${c.author}, ${c.date.slice(0, 10)})`).join("\n");
301
301
  }
302
302
 
303
+ // src/project-manifest.ts
304
+ import fs3 from "fs";
305
+ import path3 from "path";
306
+ var MANIFEST_FILENAME = ".mimi-seed.json";
307
+ function findProjectManifest(startDir = process.cwd(), maxDepth = 8) {
308
+ let dir = path3.resolve(startDir);
309
+ for (let i = 0; i <= maxDepth; i++) {
310
+ const candidate = path3.join(dir, MANIFEST_FILENAME);
311
+ if (fs3.existsSync(candidate)) {
312
+ try {
313
+ const obj = JSON.parse(fs3.readFileSync(candidate, "utf-8"));
314
+ if (obj && typeof obj === "object") return { manifest: obj, filePath: candidate };
315
+ } catch {
316
+ }
317
+ return null;
318
+ }
319
+ const parent = path3.dirname(dir);
320
+ if (parent === dir) break;
321
+ dir = parent;
322
+ }
323
+ return null;
324
+ }
325
+ function manifestServiceEntries(m) {
326
+ const svc = m.services ?? {};
327
+ return Object.keys(svc).filter((k) => svc[k] != null).map((k) => [k, svc[k]]);
328
+ }
329
+
303
330
  // src/doctor.ts
304
331
  function ok(label, detail = "") {
305
332
  process.stdout.write(` ${kleur.green("\u2713")} ${label}${detail ? kleur.dim(" " + detail) : ""}
@@ -317,6 +344,20 @@ function section(title) {
317
344
  process.stdout.write("\n" + kleur.dim(`\u2500\u2500 ${title} \u2500\u2500
318
345
  `));
319
346
  }
347
+ function manifestDetail(id, svc) {
348
+ const parts = [];
349
+ if (id === "bigquery") {
350
+ if (svc.projectId) parts.push(svc.projectId);
351
+ if (svc.dataset) parts.push(svc.dataset);
352
+ } else if (id === "playstore" && svc.packageName) {
353
+ parts.push(svc.packageName);
354
+ } else if (id === "appstore" && svc.keyId) {
355
+ parts.push(`keyId ${svc.keyId}`);
356
+ } else if (id === "jenkins" && svc.url) {
357
+ parts.push(svc.url);
358
+ }
359
+ return parts.join(" / ");
360
+ }
320
361
  async function cmdDoctor() {
321
362
  const cwd = process.cwd();
322
363
  process.stdout.write(kleur.bold("mimi-seed doctor\n\n"));
@@ -339,33 +380,64 @@ async function cmdDoctor() {
339
380
  }
340
381
  }
341
382
  section("\uB85C\uCEEC MCP \uC790\uACA9\uC99D\uBA85 (~/.mimi-seed)");
342
- const credDir = path3.join(os2.homedir(), ".mimi-seed");
343
- const hasFile = (name) => fs3.existsSync(path3.join(credDir, name));
383
+ const credDir = path4.join(os2.homedir(), ".mimi-seed");
384
+ const hasFile = (name) => fs4.existsSync(path4.join(credDir, name));
344
385
  const hasPrefix = (prefix) => {
345
386
  try {
346
- return fs3.readdirSync(credDir).some((f) => f.startsWith(prefix));
387
+ return fs4.readdirSync(credDir).some((f) => f.startsWith(prefix));
347
388
  } catch {
348
389
  return false;
349
390
  }
350
391
  };
351
392
  const hasPlaySa = hasFile("play-service-account.json") || (() => {
352
393
  try {
353
- return fs3.readdirSync(path3.join(credDir, "play-service-accounts")).some((f) => f.endsWith(".json"));
394
+ return fs4.readdirSync(path4.join(credDir, "play-service-accounts")).some((f) => f.endsWith(".json"));
354
395
  } catch {
355
396
  return false;
356
397
  }
357
398
  })();
399
+ const oauthPresent = hasFile("tokens.json");
400
+ const appstorePresent = hasFile("appstore.json");
401
+ const bigquerySaPresent = hasPrefix("bigquery");
358
402
  const creds = [
359
- ["Google OAuth (Firebase/AdMob/Play/Ads)", hasFile("tokens.json"), "mimi-seed auth login"],
360
- ["App Store Connect", hasFile("appstore.json"), "mimi-seed auth appstore"],
403
+ ["Google OAuth (Firebase/AdMob/Play/Ads)", oauthPresent, "mimi-seed auth login"],
404
+ ["App Store Connect", appstorePresent, "mimi-seed auth appstore"],
361
405
  ["Play \uC11C\uBE44\uC2A4 \uACC4\uC815 (\uC120\uD0DD \u2014 OAuth\uB85C\uB3C4 \uAC00\uB2A5)", hasPlaySa, "mimi-seed auth playstore"],
362
- ["BigQuery \uC11C\uBE44\uC2A4 \uACC4\uC815", hasPrefix("bigquery"), "mimi-seed auth bigquery"]
406
+ ["BigQuery \uC11C\uBE44\uC2A4 \uACC4\uC815", bigquerySaPresent, "mimi-seed auth bigquery"]
363
407
  ];
364
408
  for (const [label, present, cmd] of creds) {
365
409
  if (present) ok(label);
366
410
  else warn(label, `\u2192 ${cmd}`);
367
411
  }
368
412
  process.stdout.write(kleur.dim(" OAuth \uD1A0\uD070 \uC2E0\uC120\uB3C4 \uD655\uC778: mimi-seed auth status\n"));
413
+ const loaded = findProjectManifest(cwd);
414
+ if (loaded) {
415
+ const projName = loaded.manifest.displayName ?? loaded.manifest.project ?? "\uC774 \uD504\uB85C\uC81D\uD2B8";
416
+ section(`${projName} \uC694\uAD6C\uC0AC\uD56D (.mimi-seed.json)`);
417
+ const connectedOf = {
418
+ oauth: oauthPresent,
419
+ bigquery: bigquerySaPresent || oauthPresent,
420
+ playstore: hasPlaySa || oauthPresent,
421
+ appstore: appstorePresent,
422
+ jenkins: false
423
+ // mimi-seed 로컬 자격증명 대상 아님(별도 MCP) — note 로만 안내
424
+ };
425
+ const fixOf = {
426
+ oauth: "mimi-seed auth login",
427
+ bigquery: "mimi-seed auth bigquery",
428
+ playstore: "mimi-seed auth playstore",
429
+ appstore: "mimi-seed auth appstore",
430
+ jenkins: "claude mcp add virgm-jenkins -s user"
431
+ };
432
+ for (const [id, svc] of manifestServiceEntries(loaded.manifest)) {
433
+ const required = svc.required !== false;
434
+ const connected = connectedOf[id];
435
+ const detail = manifestDetail(id, svc);
436
+ if (connected) ok(id, detail);
437
+ else if (!required) warn(`${id} (\uC120\uD0DD)`, svc.note ?? detail);
438
+ else fail(id, `\u2192 ${fixOf[id]}${detail ? " " + detail : ""}`);
439
+ }
440
+ }
369
441
  section("\uB85C\uCEEC \uD658\uACBD");
370
442
  const nodeVer = process.version;
371
443
  const [, major] = nodeVer.match(/v(\d+)/) ?? [];
@@ -837,12 +909,12 @@ async function cmdReview(argv) {
837
909
 
838
910
  // src/auth.ts
839
911
  import { spawn } from "child_process";
840
- import fs4 from "fs";
912
+ import fs5 from "fs";
841
913
  import os3 from "os";
842
- import path4 from "path";
914
+ import path5 from "path";
843
915
  import kleur5 from "kleur";
844
916
  var MCP_PKG = "@yoonion/mimi-seed-mcp";
845
- var MIMI_DIR = path4.join(os3.homedir(), ".mimi-seed");
917
+ var MIMI_DIR = path5.join(os3.homedir(), ".mimi-seed");
846
918
  function log(msg) {
847
919
  process.stdout.write(msg + "\n");
848
920
  }
@@ -886,11 +958,11 @@ async function runMcpBin(bin, extraArgs) {
886
958
  });
887
959
  }
888
960
  function fileExists(name) {
889
- return fs4.existsSync(path4.join(MIMI_DIR, name));
961
+ return fs5.existsSync(path5.join(MIMI_DIR, name));
890
962
  }
891
963
  function anyFileStarting(prefix) {
892
964
  try {
893
- return fs4.readdirSync(MIMI_DIR).some((f) => f.startsWith(prefix));
965
+ return fs5.readdirSync(MIMI_DIR).some((f) => f.startsWith(prefix));
894
966
  } catch {
895
967
  return false;
896
968
  }
@@ -985,25 +1057,25 @@ import kleur6 from "kleur";
985
1057
  import * as readline from "readline";
986
1058
 
987
1059
  // src/ci-providers.ts
988
- import fs5 from "fs";
989
- import path5 from "path";
1060
+ import fs6 from "fs";
1061
+ import path6 from "path";
990
1062
  import os4 from "os";
991
- var CI_CONFIG_PATH = path5.join(os4.homedir(), ".mimi-seed", "ci.json");
1063
+ var CI_CONFIG_PATH = path6.join(os4.homedir(), ".mimi-seed", "ci.json");
992
1064
  function loadCiProviderConfig() {
993
1065
  try {
994
- return JSON.parse(fs5.readFileSync(CI_CONFIG_PATH, "utf-8"));
1066
+ return JSON.parse(fs6.readFileSync(CI_CONFIG_PATH, "utf-8"));
995
1067
  } catch {
996
1068
  return null;
997
1069
  }
998
1070
  }
999
1071
  function saveCiProviderConfig(cfg) {
1000
- const dir = path5.dirname(CI_CONFIG_PATH);
1001
- if (!fs5.existsSync(dir)) {
1002
- fs5.mkdirSync(dir, { recursive: true, mode: 448 });
1072
+ const dir = path6.dirname(CI_CONFIG_PATH);
1073
+ if (!fs6.existsSync(dir)) {
1074
+ fs6.mkdirSync(dir, { recursive: true, mode: 448 });
1003
1075
  }
1004
- fs5.writeFileSync(CI_CONFIG_PATH, JSON.stringify(cfg, null, 2));
1076
+ fs6.writeFileSync(CI_CONFIG_PATH, JSON.stringify(cfg, null, 2));
1005
1077
  if (process.platform !== "win32") {
1006
- fs5.chmodSync(CI_CONFIG_PATH, 384);
1078
+ fs6.chmodSync(CI_CONFIG_PATH, 384);
1007
1079
  }
1008
1080
  }
1009
1081
  function ghBase(cfg) {
@@ -1504,17 +1576,17 @@ async function cmdDeploy(argv) {
1504
1576
 
1505
1577
  // src/mcp-restart.ts
1506
1578
  import { execSync as execSync2 } from "child_process";
1507
- import fs6 from "fs";
1579
+ import fs7 from "fs";
1508
1580
  import os5 from "os";
1509
- import path6 from "path";
1581
+ import path7 from "path";
1510
1582
  import kleur7 from "kleur";
1511
1583
  function log3(msg) {
1512
1584
  process.stdout.write(msg + "\n");
1513
1585
  }
1514
1586
  function readClaudeJson() {
1515
- const p = path6.join(os5.homedir(), ".claude.json");
1587
+ const p = path7.join(os5.homedir(), ".claude.json");
1516
1588
  try {
1517
- return JSON.parse(fs6.readFileSync(p, "utf8"));
1589
+ return JSON.parse(fs7.readFileSync(p, "utf8"));
1518
1590
  } catch {
1519
1591
  return {};
1520
1592
  }
@@ -1601,8 +1673,8 @@ async function cmdRestart(args) {
1601
1673
  }
1602
1674
 
1603
1675
  // src/mcp-config.ts
1604
- import fs7 from "fs/promises";
1605
- import path7 from "path";
1676
+ import fs8 from "fs/promises";
1677
+ import path8 from "path";
1606
1678
  import os6 from "os";
1607
1679
  import kleur8 from "kleur";
1608
1680
  var SERVER_NAME = "mimi-seed";
@@ -1629,12 +1701,16 @@ function replaceTomlBlock(input, tableName, block) {
1629
1701
  const end = next < 0 ? input.length : start + header.length + next + 1;
1630
1702
  return `${input.slice(0, start)}${block}${input.slice(end).replace(/^\n+/, "\n")}`;
1631
1703
  }
1704
+ var CONFIG_LOCATION_HINT = "~/.mimi-seed/config.json";
1705
+ function claudeMcpAddCommand(cfg) {
1706
+ return `claude mcp add --transport http ${SERVER_NAME} ${cfg.endpoint} --header "Authorization: Bearer ${cfg.token}"`;
1707
+ }
1632
1708
  function printMcpSetup(cfg) {
1633
1709
  process.stdout.write(
1634
1710
  [
1635
- kleur8.dim("Claude Code MCP \uB4F1\uB85D:"),
1636
- kleur8.dim(` claude mcp add --transport http ${SERVER_NAME} ${cfg.endpoint} \\`),
1637
- kleur8.dim(` --header "Authorization: Bearer ${cfg.prefix}..."`),
1711
+ kleur8.bold("Claude Code MCP \uB4F1\uB85D \u2014 \uC544\uB798 \uD55C \uC904\uC744 \uADF8\uB300\uB85C \uC2E4\uD589\uD558\uC138\uC694:"),
1712
+ kleur8.cyan(` ${claudeMcpAddCommand(cfg)}`),
1713
+ 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.`),
1638
1714
  "",
1639
1715
  kleur8.dim("Codex MCP \uB4F1\uB85D:"),
1640
1716
  kleur8.dim(" mimi-seed mcp codex --write"),
@@ -1643,20 +1719,63 @@ function printMcpSetup(cfg) {
1643
1719
  );
1644
1720
  }
1645
1721
  async function writeCodexMcpConfig(cfg) {
1646
- const configDir = path7.join(os6.homedir(), ".codex");
1647
- const configPath = path7.join(configDir, "config.toml");
1648
- await fs7.mkdir(configDir, { recursive: true });
1722
+ const configDir = path8.join(os6.homedir(), ".codex");
1723
+ const configPath = path8.join(configDir, "config.toml");
1724
+ await fs8.mkdir(configDir, { recursive: true });
1649
1725
  let current = "";
1650
1726
  try {
1651
- current = await fs7.readFile(configPath, "utf8");
1727
+ current = await fs8.readFile(configPath, "utf8");
1652
1728
  } catch {
1653
1729
  current = "";
1654
1730
  }
1655
1731
  const next = replaceTomlBlock(current, `mcp_servers.${SERVER_NAME}`, codexBlock(cfg));
1656
- await fs7.writeFile(configPath, next);
1732
+ await fs8.writeFile(configPath, next);
1657
1733
  return configPath;
1658
1734
  }
1659
1735
 
1736
+ // src/release-manifest.ts
1737
+ import fs9 from "fs/promises";
1738
+ import path9 from "path";
1739
+ var RELEASE_MANIFEST_RELATIVE_PATH = path9.join("docs", "releases.json");
1740
+ var RELEASE_MANIFEST_TEMPLATE = {
1741
+ $schema: {
1742
+ _comment: "Mimi Seed release notes SSOT. Store notes, App Store What's New, in-app announcements, and app-version rollout metadata can share this file.",
1743
+ fields: {
1744
+ date: "ISO 8601 release/build date.",
1745
+ android: "Google Play release notes, 500 characters or fewer per locale.",
1746
+ ios: "App Store What's New, 4000 characters or fewer.",
1747
+ appVersion: {
1748
+ minVersion: "Optional minimum supported app version.",
1749
+ latestVersion: "Optional latest recommended app version.",
1750
+ android: "Optional Android override: { minVersion?, latestVersion? }.",
1751
+ ios: "Optional iOS override: { minVersion?, latestVersion? }."
1752
+ },
1753
+ announcement: {
1754
+ title: "Optional in-app announcement title.",
1755
+ content: "Optional in-app announcement body.",
1756
+ type: "info | update | event | maintenance",
1757
+ pinned: "Optional boolean."
1758
+ },
1759
+ highlights: "Optional internal changelog bullets."
1760
+ }
1761
+ },
1762
+ versions: {}
1763
+ };
1764
+ async function ensureReleaseManifest(cwd) {
1765
+ const filePath = path9.join(cwd, RELEASE_MANIFEST_RELATIVE_PATH);
1766
+ try {
1767
+ await fs9.access(filePath);
1768
+ return { path: filePath, created: false };
1769
+ } catch {
1770
+ }
1771
+ await fs9.mkdir(path9.dirname(filePath), { recursive: true });
1772
+ await fs9.writeFile(filePath, `${JSON.stringify(RELEASE_MANIFEST_TEMPLATE, null, 2)}
1773
+ `, {
1774
+ mode: 420
1775
+ });
1776
+ return { path: filePath, created: true };
1777
+ }
1778
+
1660
1779
  // src/index.ts
1661
1780
  var DEFAULT_WEB_BASE = process.env.MIMI_SEED_WEB_BASE ?? "https://mimi-seed.pryzm.gg";
1662
1781
  var DEFAULT_MCP_ENDPOINT = `${DEFAULT_WEB_BASE}/api/mcp`;
@@ -1695,6 +1814,8 @@ async function cmdInit(args) {
1695
1814
  for (const line of result.text.split("\n")) log4(" " + line);
1696
1815
  }
1697
1816
  }
1817
+ const manifest2 = await ensureReleaseManifest(cwd);
1818
+ log4(kleur9.dim(` \uB9B4\uB9AC\uC988 \uB178\uD2B8 SSOT: ${manifest2.created ? "\uC0DD\uC131" : "\uD655\uC778"} docs/releases.json`));
1698
1819
  log4(kleur9.bold("\u2713 \uC644\uB8CC (CI \uBAA8\uB4DC)"));
1699
1820
  return;
1700
1821
  }
@@ -1749,12 +1870,24 @@ async function cmdInit(args) {
1749
1870
  "Mimi Seed MCP\uAC00 \uC774 \uD504\uB85C\uC81D\uD2B8\uC5D0 \uC5F0\uACB0\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.",
1750
1871
  "Google Play \xB7 App Store \xB7 Firebase \xB7 AdMob\uC744 \uB3C4\uAD6C\uB85C \uC9C1\uC811 \uC81C\uC5B4\uD569\uB2C8\uB2E4.",
1751
1872
  "",
1873
+ "## \uC138\uC158 \uC2DC\uC791",
1874
+ "",
1875
+ "1. \uCD9C\uC2DC/\uC2A4\uD1A0\uC5B4/Firebase/AdMob \uC694\uCCAD\uC740 \uBA3C\uC800 `mimi_seed_status`\uB85C \uC5F0\uACB0 \uC0C1\uD0DC\uB97C \uD655\uC778",
1876
+ "2. \uC778\uC99D \uB204\uB77D\uC774\uBA74 `mimi_seed_auth_start` \uB610\uB294 \uC544\uB798 \uB85C\uCEEC \uC778\uC99D \uBA85\uB839\uC744 \uC548\uB0B4",
1877
+ '3. Claude Code\uC5D0\uC11C \uB3C4\uAD6C schema\uAC00 deferred \uC0C1\uD0DC\uB77C\uBA74 \uD544\uC694\uD55C \uB3C4\uAD6C\uB97C `ToolSearch(query="select:<tool>[,<tool>...]")`\uB85C \uBA3C\uC800 \uB85C\uB4DC',
1878
+ "",
1752
1879
  "## \uCD9C\uC2DC \uC694\uCCAD \uCC98\uB9AC \uC21C\uC11C",
1753
1880
  "",
1754
- "1. \uD56D\uC0C1 `playstore_check_submission_risks` / `appstore_check_submission_risks` \uB85C \uBE14\uB85C\uCEE4 \uBA3C\uC800 \uD655\uC778",
1755
- "2. \uB9B4\uB9AC\uC988 \uB178\uD2B8: `generate_release_notes_from_commits` \u2192 \uC0AC\uC6A9\uC790 \uD655\uC778 \uD6C4 \uC801\uC6A9",
1756
- "3. \uC2A4\uD1A0\uC5B4 **\uC4F0\uAE30** \uC791\uC5C5(submit, apply, reply)\uC740 \uBC18\uB4DC\uC2DC \uC0AC\uC6A9\uC790 \uBA85\uC2DC \uB3D9\uC758 \uD6C4 \uC2E4\uD589",
1757
- "4. \uC644\uB8CC \uD6C4 \uACB0\uACFC \uC694\uC57D \uC81C\uACF5",
1881
+ "1. \uD56D\uC0C1 `playstore_check_submission_risks` / `appstore_check_submission_risks` \uB85C \uBE14\uB85C\uCEE4 \uD655\uC778",
1882
+ "2. \uB9B4\uB9AC\uC988 \uB178\uD2B8\uB294 `docs/releases.json`\uC744 SSOT\uB85C \uD655\uC778/\uC791\uC131 \u2192 \uC0AC\uC6A9\uC790 \uD655\uC778 \uD6C4 \uC801\uC6A9",
1883
+ "3. \uC2A4\uD1A0\uC5B4 **\uC4F0\uAE30** \uC791\uC5C5(submit, apply, reply, delete)\uC740 \uBC18\uB4DC\uC2DC \uC0AC\uC6A9\uC790 \uBA85\uC2DC \uB3D9\uC758 \uD6C4 \uC2E4\uD589",
1884
+ "4. \uCD9C\uC2DC \uC644\uB8CC \uD6C4 \uC801\uC6A9 \uACB0\uACFC\uC640 \uC2E4\uD328 \uC9C0\uC810 \uC694\uC57D",
1885
+ "",
1886
+ "## \uC778\uC99D \uBCF5\uAD6C",
1887
+ "",
1888
+ "- Google/Firebase/AdMob/Play OAuth: `npx -y @yoonion/mimi-seed-mcp mimi-seed-auth`",
1889
+ "- App Store Connect: `npx -y @yoonion/mimi-seed-mcp mimi-seed-appstore-auth`",
1890
+ "- Play service account: `npx -y @yoonion/mimi-seed-mcp mimi-seed-playstore-auth`",
1758
1891
  "",
1759
1892
  "## \uC571 \uC815\uBCF4",
1760
1893
  ...appLines.length > 0 ? appLines : [" (mimi-seed status \uB85C \uD655\uC778)"],
@@ -1765,18 +1898,20 @@ async function cmdInit(args) {
1765
1898
  "- `/mimi-seed:health` \u2014 \uC5F0\uACB0 \uC0C1\uD0DC \uBE60\uB978 \uD655\uC778",
1766
1899
  "- `/mimi-seed:review-inbox` \u2014 \uBBF8\uB2F5\uBCC0 \uB9AC\uBDF0 \uB2F5\uBCC0"
1767
1900
  ].join("\n");
1768
- const claudeDir = path8.join(cwd, ".claude");
1769
- const claudeAgentPath = path8.join(claudeDir, "mimi-seed.md");
1770
- if (!fs8.existsSync(claudeAgentPath)) {
1771
- fs8.mkdirSync(claudeDir, { recursive: true });
1772
- fs8.writeFileSync(claudeAgentPath, agentMd, { mode: 420 });
1901
+ const claudeDir = path10.join(cwd, ".claude");
1902
+ const claudeAgentPath = path10.join(claudeDir, "mimi-seed.md");
1903
+ if (!fs10.existsSync(claudeAgentPath)) {
1904
+ fs10.mkdirSync(claudeDir, { recursive: true });
1905
+ fs10.writeFileSync(claudeAgentPath, agentMd, { mode: 420 });
1773
1906
  log4(kleur9.dim(` Claude \uC5D0\uC774\uC804\uD2B8 \uC124\uC815: .claude/mimi-seed.md`));
1774
1907
  }
1775
- const codexAgentPath = path8.join(cwd, "AGENTS.md");
1776
- if (!fs8.existsSync(codexAgentPath)) {
1777
- fs8.writeFileSync(codexAgentPath, agentMd, { mode: 420 });
1908
+ const codexAgentPath = path10.join(cwd, "AGENTS.md");
1909
+ if (!fs10.existsSync(codexAgentPath)) {
1910
+ fs10.writeFileSync(codexAgentPath, agentMd, { mode: 420 });
1778
1911
  log4(kleur9.dim(` Codex \uC5D0\uC774\uC804\uD2B8 \uC124\uC815: AGENTS.md`));
1779
1912
  }
1913
+ const manifest = await ensureReleaseManifest(cwd);
1914
+ log4(kleur9.dim(` \uB9B4\uB9AC\uC988 \uB178\uD2B8 SSOT: ${manifest.created ? "\uC0DD\uC131" : "\uD655\uC778"} docs/releases.json`));
1780
1915
  log4(kleur9.bold("\u2713 \uC900\uBE44 \uC644\uB8CC."));
1781
1916
  log4("");
1782
1917
  log4("Claude Code \uB610\uB294 Codex\uC5D0\uC11C \uC774\uB807\uAC8C \uBB3C\uC5B4\uBCF4\uC138\uC694:");
@@ -1845,17 +1980,17 @@ async function cmdMcp(args) {
1845
1980
  log4("\uC790\uB3D9 \uB4F1\uB85D:");
1846
1981
  log4(kleur9.cyan(" mimi-seed mcp codex --write"));
1847
1982
  log4("");
1848
- log4("\uC218\uB3D9 \uB4F1\uB85D \uC608\uC2DC (~/.codex/config.toml):");
1983
+ log4("\uC218\uB3D9 \uB4F1\uB85D \uC608\uC2DC (~/.codex/config.toml) \u2014 \uC2E4\uC81C \uD1A0\uD070 \uD3EC\uD568:");
1849
1984
  log4(`[mcp_servers.mimi-seed]
1850
1985
  url = "${cfg.endpoint}"
1851
1986
  enabled = true
1852
- http_headers = { Authorization = "Bearer ${cfg.prefix}..." }`);
1987
+ http_headers = { Authorization = "Bearer ${cfg.token}" }`);
1853
1988
  return;
1854
1989
  }
1855
1990
  if (target === "claude") {
1856
- log4(kleur9.bold("Claude Code MCP \uB4F1\uB85D"));
1857
- log4(kleur9.cyan(` claude mcp add --transport http mimi-seed ${cfg.endpoint} \\`));
1858
- log4(kleur9.cyan(` --header "Authorization: Bearer ${cfg.prefix}..."`));
1991
+ log4(kleur9.bold("Claude Code MCP \uB4F1\uB85D \u2014 \uC544\uB798 \uD55C \uC904\uC744 \uADF8\uB300\uB85C \uC2E4\uD589\uD558\uC138\uC694:"));
1992
+ log4(kleur9.cyan(` ${claudeMcpAddCommand(cfg)}`));
1993
+ 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."));
1859
1994
  return;
1860
1995
  }
1861
1996
  printMcpSetup(cfg);
@@ -1864,7 +1999,7 @@ var CMD_USAGE = {
1864
1999
  init: `${kleur9.bold("mimi-seed init")} \u2014 \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
1865
2000
 
1866
2001
  \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
1867
- + .claude/mimi-seed.md, AGENTS.md \uC0DD\uC131.
2002
+ + .claude/mimi-seed.md, AGENTS.md, docs/releases.json \uC0DD\uC131/\uD655\uC778.
1868
2003
 
1869
2004
  \uC635\uC158:
1870
2005
  --local \uCD94\uAC00\uB85C Google OAuth \uB85C\uADF8\uC778 + \uB85C\uCEEC 110+ tool MCP \uB4F1\uB85D \uC548\uB0B4`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mimi-seed",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Mimi Seed CLI — Claude Code와 Codex에서 앱 출시 운영을 관리합니다.",
5
5
  "bin": {
6
6
  "mimi-seed": "dist/index.js"