mimi-seed 0.5.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/{chunk-T757WVSG.js → chunk-47TS72IP.js} +533 -204
- package/dist/{chunk-DW3LOLPJ.js → chunk-QU2IDWJ2.js} +8 -1
- package/dist/chunk-SZQFVTEW.js +252 -0
- package/dist/index.js +997 -385
- package/dist/{mcp-client-SLIKR42C.js → mcp-client-PVWDCYJN.js} +2 -1
- package/dist/{setup-ENJIMGJF.js → setup-NQHLA3AJ.js} +2 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
mcpCall
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-QU2IDWJ2.js";
|
|
5
5
|
import {
|
|
6
6
|
CONFIG_LOCATION,
|
|
7
7
|
CREDENTIALS,
|
|
8
8
|
MCP_PKG,
|
|
9
9
|
cmdDeploy,
|
|
10
10
|
cmdSetup,
|
|
11
|
+
credLabel,
|
|
12
|
+
credNote,
|
|
11
13
|
deleteConfig,
|
|
12
14
|
detectAll,
|
|
13
15
|
detectHints,
|
|
@@ -18,17 +20,38 @@ import {
|
|
|
18
20
|
runMcpBin,
|
|
19
21
|
tryCredById,
|
|
20
22
|
writeConfig
|
|
21
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-47TS72IP.js";
|
|
24
|
+
import {
|
|
25
|
+
catalog,
|
|
26
|
+
isLang,
|
|
27
|
+
resolveLang,
|
|
28
|
+
t,
|
|
29
|
+
writeSettings
|
|
30
|
+
} from "./chunk-SZQFVTEW.js";
|
|
22
31
|
|
|
23
32
|
// src/index.ts
|
|
24
33
|
import os3 from "os";
|
|
25
34
|
import fs5 from "fs";
|
|
26
35
|
import path5 from "path";
|
|
27
|
-
import
|
|
36
|
+
import kleur9 from "kleur";
|
|
28
37
|
import open from "open";
|
|
29
38
|
|
|
30
39
|
// src/handshake.ts
|
|
31
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
|
+
);
|
|
32
55
|
async function awaitHandshake(timeoutMs) {
|
|
33
56
|
let resolve;
|
|
34
57
|
let reject;
|
|
@@ -57,9 +80,9 @@ async function awaitHandshake(timeoutMs) {
|
|
|
57
80
|
}
|
|
58
81
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
59
82
|
res.end(`<!doctype html>
|
|
60
|
-
<html lang="
|
|
61
|
-
<h2
|
|
62
|
-
<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>
|
|
63
86
|
<script>setTimeout(()=>window.close(),1000)</script>
|
|
64
87
|
</body></html>`);
|
|
65
88
|
resolve({ token, prefix });
|
|
@@ -70,7 +93,7 @@ async function awaitHandshake(timeoutMs) {
|
|
|
70
93
|
const port = server.address().port;
|
|
71
94
|
const timer = setTimeout(() => {
|
|
72
95
|
server.close();
|
|
73
|
-
reject(new Error(
|
|
96
|
+
reject(new Error(M().timeout(timeoutMs / 1e3)));
|
|
74
97
|
}, timeoutMs);
|
|
75
98
|
promise.finally(() => clearTimeout(timer));
|
|
76
99
|
return { port, promise };
|
|
@@ -179,78 +202,81 @@ function manifestDetail(id, svc) {
|
|
|
179
202
|
}
|
|
180
203
|
async function cmdDoctor() {
|
|
181
204
|
const cwd = process.cwd();
|
|
182
|
-
|
|
183
|
-
|
|
205
|
+
const m = t().doctor;
|
|
206
|
+
process.stdout.write(kleur.bold(m.title + "\n\n"));
|
|
207
|
+
section(m.secAuth);
|
|
184
208
|
const cfg = await getEffectiveConfig();
|
|
185
209
|
if (!cfg) {
|
|
186
|
-
fail(
|
|
210
|
+
fail(m.noToken, m.noTokenFix);
|
|
187
211
|
} else {
|
|
188
|
-
ok(
|
|
189
|
-
ok(
|
|
212
|
+
ok(m.tokenSaved, `${cfg.prefix}\u2026 (${cfg.createdAt.slice(0, 10)})`);
|
|
213
|
+
ok(m.endpoint, cfg.endpoint);
|
|
190
214
|
if (process.env.MIMI_SEED_TOKEN) {
|
|
191
|
-
ok(
|
|
215
|
+
ok(m.ciMode, m.ciModeDetail);
|
|
192
216
|
}
|
|
193
217
|
const r = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
|
|
194
218
|
if (r.isError) {
|
|
195
|
-
fail(
|
|
219
|
+
fail(m.tokenInvalid, r.text.slice(0, 80));
|
|
196
220
|
} else {
|
|
197
221
|
const lines = r.text.split("\n").filter(Boolean);
|
|
198
|
-
ok(
|
|
222
|
+
ok(m.serverOk, m.appCount(lines.length));
|
|
199
223
|
}
|
|
200
224
|
}
|
|
201
|
-
section(
|
|
225
|
+
section(m.secCreds);
|
|
202
226
|
migrateLegacyJenkins();
|
|
203
227
|
const detected = detectAll();
|
|
204
228
|
for (const spec of CREDENTIALS) {
|
|
205
229
|
const d = detected.get(spec.id);
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
230
|
+
const base = credLabel(spec);
|
|
231
|
+
const note = credNote(spec);
|
|
232
|
+
const label = note ? `${base} (${note})` : base;
|
|
233
|
+
if (d.present) ok(base, d.detail);
|
|
234
|
+
else if (isSatisfied(spec, detected)) warn(label, t().setup.fallbackWorking);
|
|
209
235
|
else if (spec.requirement === "optional") warn(`${label}`, `\u2192 ${spec.fix}`);
|
|
210
|
-
else fail(
|
|
236
|
+
else fail(base, `\u2192 ${spec.fix}`);
|
|
211
237
|
}
|
|
212
|
-
process.stdout.write(kleur.dim(
|
|
238
|
+
process.stdout.write(kleur.dim(m.credsHint));
|
|
213
239
|
const loaded = findProjectManifest(cwd);
|
|
214
240
|
if (loaded) {
|
|
215
|
-
const projName = loaded.manifest.displayName ?? loaded.manifest.project ??
|
|
216
|
-
section(
|
|
241
|
+
const projName = loaded.manifest.displayName ?? loaded.manifest.project ?? m.thisProject;
|
|
242
|
+
section(m.requirements(projName));
|
|
217
243
|
for (const [id, svc] of manifestServiceEntries(loaded.manifest)) {
|
|
218
244
|
const required = svc.required !== false;
|
|
219
245
|
const detail = manifestDetail(id, svc);
|
|
220
246
|
const spec = tryCredById(id);
|
|
221
247
|
if (!spec) {
|
|
222
|
-
warn(
|
|
248
|
+
warn(m.unknownService(id), svc.note ?? detail);
|
|
223
249
|
continue;
|
|
224
250
|
}
|
|
225
251
|
const connected = isSatisfied(spec, detected);
|
|
226
252
|
if (connected) ok(id, detail);
|
|
227
|
-
else if (!required) warn(`${id} (
|
|
253
|
+
else if (!required) warn(`${id} (${t().common.optional})`, svc.note ?? detail);
|
|
228
254
|
else fail(id, `\u2192 ${spec.fix}${detail ? " " + detail : ""}`);
|
|
229
255
|
}
|
|
230
256
|
}
|
|
231
|
-
section(
|
|
257
|
+
section(m.secEnv);
|
|
232
258
|
const nodeVer = process.version;
|
|
233
259
|
const [, major] = nodeVer.match(/v(\d+)/) ?? [];
|
|
234
260
|
if (Number(major) >= 20) {
|
|
235
261
|
ok("Node.js", nodeVer);
|
|
236
262
|
} else {
|
|
237
|
-
fail("Node.js",
|
|
263
|
+
fail("Node.js", m.nodeTooOld(nodeVer));
|
|
238
264
|
}
|
|
239
265
|
if (isGitRepo(cwd)) {
|
|
240
266
|
const latestTag = getLatestTag(cwd);
|
|
241
267
|
const commits = getGitLog(cwd, { limit: 5 });
|
|
242
|
-
ok(
|
|
268
|
+
ok(m.gitRepo, latestTag ? m.gitTag(latestTag) : m.gitCommits(commits.length));
|
|
243
269
|
} else {
|
|
244
|
-
warn(
|
|
270
|
+
warn(m.noGit, m.noGitDetail);
|
|
245
271
|
}
|
|
246
|
-
section(
|
|
272
|
+
section(m.secApps);
|
|
247
273
|
const hints = await detectHints(cwd);
|
|
248
274
|
if (hints.length === 0) {
|
|
249
|
-
warn(
|
|
275
|
+
warn(m.noApp, m.noAppDetail);
|
|
250
276
|
} else {
|
|
251
277
|
for (const h of hints) {
|
|
252
278
|
const ids = [h.packageName && `android:${h.packageName}`, h.bundleId && `ios:${h.bundleId}`].filter(Boolean).join(" ");
|
|
253
|
-
ok(h.name ??
|
|
279
|
+
ok(h.name ?? m.unnamed, ids);
|
|
254
280
|
}
|
|
255
281
|
}
|
|
256
282
|
process.stdout.write("\n");
|
|
@@ -258,6 +284,70 @@ async function cmdDoctor() {
|
|
|
258
284
|
|
|
259
285
|
// src/check.ts
|
|
260
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
|
+
);
|
|
261
351
|
function parseArgs(argv) {
|
|
262
352
|
const args = { failOnBlocker: false };
|
|
263
353
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -282,14 +372,13 @@ async function cmdCheck(argv) {
|
|
|
282
372
|
const args = parseArgs(argv);
|
|
283
373
|
const cfg = await getEffectiveConfig();
|
|
284
374
|
if (!cfg) {
|
|
285
|
-
process.stdout.write(kleur2.red(
|
|
375
|
+
process.stdout.write(kleur2.red(M2().noAccount));
|
|
286
376
|
process.exit(1);
|
|
287
377
|
}
|
|
288
|
-
process.stdout.write(kleur2.bold(
|
|
378
|
+
process.stdout.write(kleur2.bold(M2().title));
|
|
289
379
|
const appsResult = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
|
|
290
380
|
if (appsResult.isError) {
|
|
291
|
-
process.stdout.write(kleur2.red(
|
|
292
|
-
`));
|
|
381
|
+
process.stdout.write(kleur2.red(M2().appsFailed(appsResult.text)));
|
|
293
382
|
process.exit(1);
|
|
294
383
|
}
|
|
295
384
|
let appId = args.appId;
|
|
@@ -297,36 +386,29 @@ async function cmdCheck(argv) {
|
|
|
297
386
|
try {
|
|
298
387
|
const apps = JSON.parse(appsResult.text);
|
|
299
388
|
if (!Array.isArray(apps) || apps.length === 0) {
|
|
300
|
-
process.stdout.write(kleur2.yellow(
|
|
389
|
+
process.stdout.write(kleur2.yellow(M2().noApps));
|
|
301
390
|
process.exit(0);
|
|
302
391
|
}
|
|
303
392
|
appId = apps[0].id;
|
|
304
|
-
process.stdout.write(kleur2.dim(
|
|
305
|
-
|
|
306
|
-
`));
|
|
393
|
+
process.stdout.write(kleur2.dim(M2().app(apps[0].name ?? appId)));
|
|
307
394
|
} catch {
|
|
308
|
-
process.stdout.write(kleur2.red(
|
|
309
|
-
`));
|
|
395
|
+
process.stdout.write(kleur2.red(M2().appsParseFailed(appsResult.text.slice(0, 80))));
|
|
310
396
|
process.exit(1);
|
|
311
397
|
}
|
|
312
398
|
}
|
|
313
|
-
process.stdout.write(
|
|
399
|
+
process.stdout.write(M2().scoring);
|
|
314
400
|
const readinessResult = await mcpCall(cfg.endpoint, cfg.token, "get_readiness", { app_id: appId });
|
|
315
401
|
if (readinessResult.isError) {
|
|
316
|
-
process.stdout.write(kleur2.red(
|
|
317
|
-
`));
|
|
402
|
+
process.stdout.write(kleur2.red(M2().scoreFailed(readinessResult.text)));
|
|
318
403
|
process.exit(1);
|
|
319
404
|
}
|
|
320
405
|
let hasBlocker = false;
|
|
321
406
|
try {
|
|
322
407
|
const data = JSON.parse(readinessResult.text);
|
|
323
408
|
const score = data.score ?? 0;
|
|
324
|
-
process.stdout.write(
|
|
325
|
-
\uC810\uC218: ${renderScore(score)}
|
|
326
|
-
|
|
327
|
-
`);
|
|
409
|
+
process.stdout.write(M2().score(renderScore(score)));
|
|
328
410
|
if (data.modules) {
|
|
329
|
-
process.stdout.write(kleur2.dim(
|
|
411
|
+
process.stdout.write(kleur2.dim(M2().byModule));
|
|
330
412
|
for (const [key, val] of Object.entries(data.modules)) {
|
|
331
413
|
const label = MODULE_LABELS[key] ?? key;
|
|
332
414
|
const color = val >= 25 ? kleur2.green : val >= 10 ? kleur2.yellow : kleur2.red;
|
|
@@ -337,13 +419,13 @@ async function cmdCheck(argv) {
|
|
|
337
419
|
}
|
|
338
420
|
if (data.blockers?.length) {
|
|
339
421
|
hasBlocker = true;
|
|
340
|
-
process.stdout.write(kleur2.bold(
|
|
422
|
+
process.stdout.write(kleur2.bold(M2().blockers));
|
|
341
423
|
for (const b of data.blockers) process.stdout.write(` ${kleur2.red("\u2022")} ${b}
|
|
342
424
|
`);
|
|
343
425
|
process.stdout.write("\n");
|
|
344
426
|
}
|
|
345
427
|
if (data.warnings?.length) {
|
|
346
|
-
process.stdout.write(kleur2.bold(
|
|
428
|
+
process.stdout.write(kleur2.bold(M2().warnings));
|
|
347
429
|
for (const w of data.warnings) process.stdout.write(` ${kleur2.yellow("\u2022")} ${w}
|
|
348
430
|
`);
|
|
349
431
|
process.stdout.write("\n");
|
|
@@ -351,22 +433,19 @@ async function cmdCheck(argv) {
|
|
|
351
433
|
const base = `${cfg.webBase}/apps/${appId}`;
|
|
352
434
|
const nextSteps = [];
|
|
353
435
|
if (data.modules) {
|
|
354
|
-
if ((data.modules.integration ?? 25) < 25) nextSteps.push(
|
|
355
|
-
if ((data.modules.copy ?? 25) < 25) nextSteps.push(
|
|
356
|
-
if ((data.modules.screenshot ?? 25) < 25) nextSteps.push(
|
|
357
|
-
if ((data.modules.checklist ?? 25) < 25) nextSteps.push(
|
|
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`));
|
|
358
440
|
}
|
|
359
441
|
if (nextSteps.length) {
|
|
360
|
-
process.stdout.write(kleur2.bold(
|
|
442
|
+
process.stdout.write(kleur2.bold(M2().nextSteps));
|
|
361
443
|
for (const s of nextSteps) process.stdout.write(` ${kleur2.cyan("\u2022")} ${s}
|
|
362
444
|
`);
|
|
363
|
-
process.stdout.write(kleur2.dim(
|
|
364
|
-
|
|
365
|
-
`));
|
|
445
|
+
process.stdout.write(kleur2.dim(M2().preview));
|
|
366
446
|
}
|
|
367
447
|
if (!hasBlocker) {
|
|
368
|
-
process.stdout.write(score >= 80 ? kleur2.green(
|
|
369
|
-
`));
|
|
448
|
+
process.stdout.write(score >= 80 ? kleur2.green(M2().ready) : kleur2.yellow(M2().notReady(score)));
|
|
370
449
|
}
|
|
371
450
|
} catch {
|
|
372
451
|
for (const line of readinessResult.text.split("\n")) process.stdout.write(" " + line + "\n");
|
|
@@ -378,6 +457,122 @@ async function cmdCheck(argv) {
|
|
|
378
457
|
import { createInterface } from "readline/promises";
|
|
379
458
|
import Anthropic from "@anthropic-ai/sdk";
|
|
380
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
|
+
);
|
|
381
576
|
function parseArgs2(argv) {
|
|
382
577
|
const args = { to: "HEAD", locales: ["ko", "en-US"], apply: false, noInteractive: false, limit: 30 };
|
|
383
578
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -398,31 +593,19 @@ async function promptUser(question) {
|
|
|
398
593
|
}
|
|
399
594
|
async function generateWithClaude(commitsText, locales) {
|
|
400
595
|
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
401
|
-
const localeList = locales.map((l) =>
|
|
596
|
+
const localeList = locales.map((l) => M3().localeHint(l)).join(",\n ");
|
|
402
597
|
const response = await client.messages.create({
|
|
403
598
|
model: "claude-haiku-4-5-20251001",
|
|
404
599
|
max_tokens: 1500,
|
|
405
|
-
system:
|
|
600
|
+
system: M3().system,
|
|
406
601
|
messages: [{
|
|
407
602
|
role: "user",
|
|
408
|
-
content:
|
|
409
|
-
|
|
410
|
-
${commitsText}
|
|
411
|
-
|
|
412
|
-
JSON:
|
|
413
|
-
{
|
|
414
|
-
"concise": "\uAC04\uACB0\uD55C \uBC84\uC804 (3\uC904 \uC774\uB0B4, \uBD88\uB9BF)",
|
|
415
|
-
"detailed": "\uC0C1\uC138 \uBC84\uC804 (5\uAC1C \uC774\uB0B4, \uBD88\uB9BF)",
|
|
416
|
-
"marketing": "\uB9C8\uCF00\uD305 \uBC84\uC804 (\uC5F4\uC815\uC801 \uD1A4)",
|
|
417
|
-
"localized": {
|
|
418
|
-
${localeList}
|
|
419
|
-
}
|
|
420
|
-
}`
|
|
603
|
+
content: M3().userPrompt(commitsText, localeList)
|
|
421
604
|
}]
|
|
422
605
|
});
|
|
423
606
|
const text = response.content[0].type === "text" ? response.content[0].text : "";
|
|
424
607
|
const match = text.match(/\{[\s\S]*\}/);
|
|
425
|
-
if (!match) throw new Error(
|
|
608
|
+
if (!match) throw new Error(M3().parseFailed);
|
|
426
609
|
return JSON.parse(match[0]);
|
|
427
610
|
}
|
|
428
611
|
function generateTemplate(commits, locales) {
|
|
@@ -432,11 +615,7 @@ function generateTemplate(commits, locales) {
|
|
|
432
615
|
});
|
|
433
616
|
const concise = items.slice(0, 3).join("\n");
|
|
434
617
|
const detailed = items.join("\n");
|
|
435
|
-
const marketing =
|
|
436
|
-
|
|
437
|
-
${items.slice(0, 5).join("\n")}
|
|
438
|
-
|
|
439
|
-
\uC9C0\uAE08 \uBC14\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.`;
|
|
618
|
+
const marketing = M3().marketingTemplate(items.slice(0, 5).join("\n"));
|
|
440
619
|
const localized = Object.fromEntries(locales.map((l) => [l, concise]));
|
|
441
620
|
return { concise, detailed, marketing, localized };
|
|
442
621
|
}
|
|
@@ -451,46 +630,43 @@ function parseFirstApp(text) {
|
|
|
451
630
|
async function cmdNotes(argv) {
|
|
452
631
|
const args = parseArgs2(argv);
|
|
453
632
|
const cwd = process.cwd();
|
|
454
|
-
process.stdout.write(kleur3.bold(
|
|
633
|
+
process.stdout.write(kleur3.bold(M3().title));
|
|
455
634
|
if (!isGitRepo(cwd)) {
|
|
456
|
-
process.stdout.write(kleur3.red(
|
|
635
|
+
process.stdout.write(kleur3.red(M3().notGitRepo));
|
|
457
636
|
process.exit(1);
|
|
458
637
|
}
|
|
459
638
|
const latestTag = getLatestTag(cwd);
|
|
460
639
|
const fromRef = args.from ?? latestTag ?? void 0;
|
|
461
|
-
process.stdout.write(
|
|
462
|
-
|
|
463
|
-
|
|
640
|
+
process.stdout.write(
|
|
641
|
+
kleur3.dim(fromRef ? M3().range(fromRef, args.to) : M3().recentCommits(args.limit))
|
|
642
|
+
);
|
|
464
643
|
const commits = getGitLog(cwd, { from: fromRef, to: args.to, limit: args.limit });
|
|
465
644
|
if (commits.length === 0) {
|
|
466
|
-
process.stdout.write(kleur3.yellow(
|
|
645
|
+
process.stdout.write(kleur3.yellow(M3().noCommits));
|
|
467
646
|
process.exit(0);
|
|
468
647
|
}
|
|
469
|
-
process.stdout.write(kleur3.dim(
|
|
470
|
-
|
|
471
|
-
`));
|
|
648
|
+
process.stdout.write(kleur3.dim(M3().analyzing(commits.length)));
|
|
472
649
|
let result;
|
|
473
650
|
if (process.env.ANTHROPIC_API_KEY) {
|
|
474
|
-
process.stdout.write(
|
|
651
|
+
process.stdout.write(M3().generating);
|
|
475
652
|
try {
|
|
476
653
|
result = await generateWithClaude(formatCommitsForPrompt(commits), args.locales);
|
|
477
654
|
} catch (e) {
|
|
478
|
-
process.stdout.write(kleur3.yellow(
|
|
479
|
-
`));
|
|
655
|
+
process.stdout.write(kleur3.yellow(M3().aiFailed(e.message)));
|
|
480
656
|
result = generateTemplate(commits, args.locales);
|
|
481
657
|
}
|
|
482
658
|
} else {
|
|
483
|
-
process.stdout.write(kleur3.dim(
|
|
659
|
+
process.stdout.write(kleur3.dim(M3().noApiKey) + kleur3.dim(M3().noApiKeyHint));
|
|
484
660
|
result = generateTemplate(commits, args.locales);
|
|
485
661
|
}
|
|
486
|
-
process.stdout.write(kleur3.bold(
|
|
662
|
+
process.stdout.write(kleur3.bold(M3().hdrConcise));
|
|
487
663
|
process.stdout.write(result.concise + "\n\n");
|
|
488
|
-
process.stdout.write(kleur3.bold(
|
|
664
|
+
process.stdout.write(kleur3.bold(M3().hdrDetailed));
|
|
489
665
|
process.stdout.write(result.detailed + "\n\n");
|
|
490
|
-
process.stdout.write(kleur3.bold(
|
|
666
|
+
process.stdout.write(kleur3.bold(M3().hdrMarketing));
|
|
491
667
|
process.stdout.write(result.marketing + "\n\n");
|
|
492
668
|
if (Object.keys(result.localized).length > 0) {
|
|
493
|
-
process.stdout.write(kleur3.bold(
|
|
669
|
+
process.stdout.write(kleur3.bold(M3().hdrLocalized));
|
|
494
670
|
for (const [locale, text] of Object.entries(result.localized)) {
|
|
495
671
|
process.stdout.write(kleur3.dim(`[${locale}]
|
|
496
672
|
`) + text + "\n\n");
|
|
@@ -500,15 +676,15 @@ async function cmdNotes(argv) {
|
|
|
500
676
|
if (!args.apply && !shouldPrompt) return;
|
|
501
677
|
const cfg = await getEffectiveConfig();
|
|
502
678
|
if (!cfg) {
|
|
503
|
-
process.stdout.write(kleur3.yellow(
|
|
679
|
+
process.stdout.write(kleur3.yellow(M3().needAccount));
|
|
504
680
|
return;
|
|
505
681
|
}
|
|
506
682
|
let selectedText = result.concise;
|
|
507
683
|
let userSelectedTone = false;
|
|
508
684
|
if (shouldPrompt) {
|
|
509
|
-
const choice = await promptUser(
|
|
685
|
+
const choice = await promptUser(M3().choosePrompt);
|
|
510
686
|
if (!choice) {
|
|
511
|
-
process.stdout.write(kleur3.dim(
|
|
687
|
+
process.stdout.write(kleur3.dim(M3().skipped));
|
|
512
688
|
return;
|
|
513
689
|
}
|
|
514
690
|
if (choice === "2") selectedText = result.detailed;
|
|
@@ -517,19 +693,18 @@ async function cmdNotes(argv) {
|
|
|
517
693
|
}
|
|
518
694
|
const appsResult = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
|
|
519
695
|
if (appsResult.isError) {
|
|
520
|
-
process.stdout.write(kleur3.red(
|
|
521
|
-
`));
|
|
696
|
+
process.stdout.write(kleur3.red(M3().listAppsFailed(appsResult.text)));
|
|
522
697
|
return;
|
|
523
698
|
}
|
|
524
699
|
const app = parseFirstApp(appsResult.text);
|
|
525
700
|
if (!app) {
|
|
526
|
-
process.stdout.write(kleur3.yellow(
|
|
701
|
+
process.stdout.write(kleur3.yellow(M3().noApps));
|
|
527
702
|
return;
|
|
528
703
|
}
|
|
529
704
|
if (userSelectedTone && args.locales.length > 1) {
|
|
530
|
-
process.stdout.write(kleur3.dim(
|
|
705
|
+
process.stdout.write(kleur3.dim(M3().toneToAllLocales));
|
|
531
706
|
}
|
|
532
|
-
process.stdout.write(
|
|
707
|
+
process.stdout.write(M3().applying);
|
|
533
708
|
for (const locale of args.locales) {
|
|
534
709
|
const text = userSelectedTone ? selectedText : result.localized[locale] ?? selectedText;
|
|
535
710
|
const r = await mcpCall(cfg.endpoint, cfg.token, "apply_release_notes", {
|
|
@@ -538,9 +713,9 @@ async function cmdNotes(argv) {
|
|
|
538
713
|
locale,
|
|
539
714
|
text
|
|
540
715
|
});
|
|
541
|
-
process.stdout.write(
|
|
542
|
-
|
|
543
|
-
|
|
716
|
+
process.stdout.write(
|
|
717
|
+
r.isError ? kleur3.red(M3().applyFailed(locale, r.text)) : kleur3.green(M3().applied(locale))
|
|
718
|
+
);
|
|
544
719
|
}
|
|
545
720
|
}
|
|
546
721
|
|
|
@@ -548,6 +723,120 @@ async function cmdNotes(argv) {
|
|
|
548
723
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
549
724
|
import Anthropic2 from "@anthropic-ai/sdk";
|
|
550
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
|
+
);
|
|
551
840
|
function parseArgs3(argv) {
|
|
552
841
|
const args = { tone: "friendly", language: "ko", apply: false, noInteractive: false };
|
|
553
842
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -570,19 +859,6 @@ async function promptUser2(question) {
|
|
|
570
859
|
rl.close();
|
|
571
860
|
return answer.trim();
|
|
572
861
|
}
|
|
573
|
-
var TONE_GUIDES = {
|
|
574
|
-
friendly: "\uCE5C\uADFC\uD558\uACE0 \uB530\uB73B\uD558\uAC8C. \uC774\uBAA8\uC9C0 1~2\uAC1C \uC0AC\uC6A9. \uAC10\uC0AC \uC778\uC0AC\uB85C \uC2DC\uC791.",
|
|
575
|
-
professional: "\uC815\uC911\uD558\uACE0 \uACF5\uC2DD\uC801\uC73C\uB85C. \uBB38\uC81C\uB97C \uC778\uC815\uD558\uACE0 \uD574\uACB0\uCC45\uC744 \uC81C\uC2DC.",
|
|
576
|
-
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.",
|
|
577
|
-
brief: "2~3\uBB38\uC7A5\uC73C\uB85C \uAC04\uACB0\uD558\uAC8C. \uD575\uC2EC \uC751\uB2F5\uB9CC."
|
|
578
|
-
};
|
|
579
|
-
var SENTIMENT_PROMPTS = {
|
|
580
|
-
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.",
|
|
581
|
-
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.",
|
|
582
|
-
neutral: "\uC911\uB9BD\uC801\uC778 \uB9AC\uBDF0. \uD53C\uB4DC\uBC31\uC5D0 \uAC10\uC0AC\uD558\uACE0 \uAC1C\uC120 \uB178\uB825\uC744 \uC57D\uC18D.",
|
|
583
|
-
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.",
|
|
584
|
-
feature_request: "\uAE30\uB2A5 \uC694\uCCAD. \uD53C\uB4DC\uBC31\uC5D0 \uAC10\uC0AC\uD558\uACE0, \uAC80\uD1A0\uD558\uACA0\uB2E4\uACE0 \uC57D\uC18D."
|
|
585
|
-
};
|
|
586
862
|
function detectSentiment(text) {
|
|
587
863
|
const lower = text.toLowerCase();
|
|
588
864
|
if (["\uBC84\uADF8", "\uC624\uB958", "\uC548\uB428", "crash", "bug", "error", "broken"].some((w) => lower.includes(w))) return "bug_report";
|
|
@@ -593,103 +869,97 @@ function detectSentiment(text) {
|
|
|
593
869
|
}
|
|
594
870
|
async function generateReply(opts) {
|
|
595
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;
|
|
596
876
|
const sentiment = detectSentiment(opts.text);
|
|
597
|
-
const toneGuide =
|
|
598
|
-
const sentimentPrompt =
|
|
599
|
-
const stars = opts.rating !== void 0 ?
|
|
600
|
-
|
|
601
|
-
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" };
|
|
602
|
-
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;
|
|
603
881
|
const response = await client.messages.create({
|
|
604
882
|
model: "claude-haiku-4-5-20251001",
|
|
605
883
|
max_tokens: 500,
|
|
606
|
-
system:
|
|
884
|
+
system: m.system(langName, toneGuide, opts.developerName ?? m.defaultDeveloper),
|
|
607
885
|
messages: [{
|
|
608
886
|
role: "user",
|
|
609
|
-
content:
|
|
610
|
-
${stars}\uB9AC\uBDF0: "${opts.text}"
|
|
611
|
-
|
|
612
|
-
\uC9C0\uCE68: ${sentimentPrompt}
|
|
613
|
-
|
|
614
|
-
JSON\uC73C\uB85C\uB9CC \uC751\uB2F5: { "reply": "\uB2F5\uBCC0 \uD14D\uC2A4\uD2B8" }`
|
|
887
|
+
content: m.userPrompt(opts.appName ?? m.defaultApp, stars, opts.text, sentimentPrompt)
|
|
615
888
|
}]
|
|
616
889
|
});
|
|
617
890
|
const raw = response.content[0].type === "text" ? response.content[0].text : "";
|
|
618
891
|
const match = raw.match(/\{[\s\S]*\}/);
|
|
619
|
-
if (!match) throw new Error(
|
|
892
|
+
if (!match) throw new Error(m.parseFailed);
|
|
620
893
|
const parsed = JSON.parse(match[0]);
|
|
621
894
|
return parsed.reply;
|
|
622
895
|
}
|
|
623
896
|
async function cmdReview(argv) {
|
|
624
897
|
const args = parseArgs3(argv);
|
|
625
|
-
process.stdout.write(kleur4.bold(
|
|
898
|
+
process.stdout.write(kleur4.bold(M4().title));
|
|
626
899
|
if (!process.env.ANTHROPIC_API_KEY) {
|
|
627
|
-
process.stdout.write(kleur4.red(
|
|
628
|
-
process.stdout.write(kleur4.dim(
|
|
900
|
+
process.stdout.write(kleur4.red(M4().noApiKey));
|
|
901
|
+
process.stdout.write(kleur4.dim(M4().noApiKeyHint));
|
|
629
902
|
process.exit(1);
|
|
630
903
|
}
|
|
631
904
|
let reviewText = args.text;
|
|
632
905
|
if (!reviewText) {
|
|
633
906
|
if (args.noInteractive) {
|
|
634
|
-
process.stdout.write(kleur4.red(
|
|
907
|
+
process.stdout.write(kleur4.red(M4().textRequired));
|
|
635
908
|
process.exit(1);
|
|
636
909
|
}
|
|
637
|
-
process.stdout.write(kleur4.dim(
|
|
910
|
+
process.stdout.write(kleur4.dim(M4().enterReview));
|
|
638
911
|
reviewText = await promptUser2("> ");
|
|
639
912
|
if (!reviewText) {
|
|
640
|
-
process.stdout.write(kleur4.yellow(
|
|
913
|
+
process.stdout.write(kleur4.yellow(M4().noInput));
|
|
641
914
|
process.exit(0);
|
|
642
915
|
}
|
|
643
916
|
}
|
|
644
917
|
if (args.rating !== void 0) {
|
|
645
918
|
const stars = "\u2605".repeat(args.rating) + "\u2606".repeat(5 - args.rating);
|
|
646
|
-
process.stdout.write(kleur4.dim(
|
|
647
|
-
|
|
648
|
-
`));
|
|
919
|
+
process.stdout.write(kleur4.dim(M4().meta(stars, args.tone, args.language)));
|
|
649
920
|
}
|
|
650
|
-
process.stdout.write(
|
|
921
|
+
process.stdout.write(M4().generating);
|
|
651
922
|
const reply = await generateReply({ ...args, text: reviewText });
|
|
652
|
-
process.stdout.write("\n" + kleur4.bold(
|
|
923
|
+
process.stdout.write("\n" + kleur4.bold(M4().hdrReply));
|
|
653
924
|
process.stdout.write(reply + "\n");
|
|
654
|
-
process.stdout.write(kleur4.dim(
|
|
925
|
+
process.stdout.write(kleur4.dim(M4().draftWarning));
|
|
655
926
|
const shouldPrompt = !args.apply && !args.noInteractive && process.stdout.isTTY;
|
|
656
927
|
if (!args.apply && !shouldPrompt) return;
|
|
657
928
|
let doApply = args.apply;
|
|
658
929
|
if (shouldPrompt) {
|
|
659
|
-
const ans = await promptUser2(
|
|
930
|
+
const ans = await promptUser2(M4().postPrompt);
|
|
660
931
|
doApply = ans.toLowerCase() === "y";
|
|
661
932
|
}
|
|
662
933
|
if (!doApply) {
|
|
663
|
-
process.stdout.write(kleur4.dim(
|
|
934
|
+
process.stdout.write(kleur4.dim(M4().skipped));
|
|
664
935
|
return;
|
|
665
936
|
}
|
|
666
937
|
const cfg = await getEffectiveConfig();
|
|
667
938
|
if (!cfg) {
|
|
668
|
-
process.stdout.write(kleur4.yellow(
|
|
939
|
+
process.stdout.write(kleur4.yellow(M4().needAccount));
|
|
669
940
|
return;
|
|
670
941
|
}
|
|
671
942
|
let reviewId = args.reviewId;
|
|
672
943
|
let packageName = args.packageName;
|
|
673
944
|
if (!reviewId || !packageName) {
|
|
674
945
|
if (args.noInteractive) {
|
|
675
|
-
process.stdout.write(kleur4.red(
|
|
946
|
+
process.stdout.write(kleur4.red(M4().idsRequired));
|
|
676
947
|
process.exit(1);
|
|
677
948
|
}
|
|
678
|
-
if (!packageName) packageName = await promptUser2(
|
|
679
|
-
if (!reviewId) reviewId = await promptUser2(
|
|
949
|
+
if (!packageName) packageName = await promptUser2(M4().packagePrompt);
|
|
950
|
+
if (!reviewId) reviewId = await promptUser2(M4().reviewIdPrompt);
|
|
680
951
|
}
|
|
681
952
|
if (!reviewId || !packageName) {
|
|
682
|
-
process.stdout.write(kleur4.yellow(
|
|
953
|
+
process.stdout.write(kleur4.yellow(M4().notEnoughInfo));
|
|
683
954
|
return;
|
|
684
955
|
}
|
|
685
|
-
process.stdout.write(
|
|
956
|
+
process.stdout.write(M4().posting);
|
|
686
957
|
const r = await mcpCall(cfg.endpoint, cfg.token, "playstore_reply_review", {
|
|
687
958
|
package_name: packageName,
|
|
688
959
|
review_id: reviewId,
|
|
689
960
|
reply_text: reply
|
|
690
961
|
});
|
|
691
|
-
process.stdout.write(r.isError ? kleur4.red(
|
|
692
|
-
`) : 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));
|
|
693
963
|
}
|
|
694
964
|
|
|
695
965
|
// src/auth.ts
|
|
@@ -697,8 +967,9 @@ import kleur5 from "kleur";
|
|
|
697
967
|
function log(msg) {
|
|
698
968
|
process.stdout.write(msg + "\n");
|
|
699
969
|
}
|
|
700
|
-
|
|
701
|
-
|
|
970
|
+
var M5 = catalog(
|
|
971
|
+
{
|
|
972
|
+
help: `${kleur5.bold("mimi-seed auth")} \u2014 \uB85C\uCEEC \uC790\uACA9\uC99D\uBA85 \uC778\uC99D/\uAD00\uB9AC
|
|
702
973
|
|
|
703
974
|
${kleur5.bold("Google OAuth (Firebase / AdMob / Play):")}
|
|
704
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)
|
|
@@ -729,18 +1000,58 @@ ${kleur5.bold("login \uC635\uC158:")}
|
|
|
729
1000
|
--timeout <\uCD08> \uCF5C\uBC31 \uB300\uAE30 \uC2DC\uAC04 (\uAE30\uBCF8 600)
|
|
730
1001
|
--force \uAE30\uC874 \uD1A0\uD070 \uBB34\uC2DC\uD558\uACE0 \uAC15\uC81C \uC7AC\uB85C\uADF8\uC778
|
|
731
1002
|
|
|
732
|
-
${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);
|
|
733
1044
|
}
|
|
734
1045
|
function printAllCredStatus() {
|
|
735
|
-
log(kleur5.bold(
|
|
1046
|
+
log(kleur5.bold(t().auth.statusTitle + " ") + kleur5.dim("(~/.mimi-seed)"));
|
|
736
1047
|
const detected = detectAll();
|
|
737
1048
|
for (const spec of CREDENTIALS) {
|
|
738
1049
|
const d = detected.get(spec.id);
|
|
739
1050
|
const mark = d.present ? kleur5.green("\u2713") : isSatisfied(spec, detected) ? kleur5.yellow("~") : spec.requirement === "optional" ? kleur5.dim("\xB7") : kleur5.red("\u2717");
|
|
740
1051
|
const tail = d.present ? kleur5.dim(d.detail ?? "") : kleur5.dim(`\u2192 ${spec.fix}`);
|
|
741
|
-
log(` ${mark} ${spec.
|
|
1052
|
+
log(` ${mark} ${credLabel(spec).padEnd(24)} ${tail}`);
|
|
742
1053
|
}
|
|
743
|
-
log(kleur5.dim(
|
|
1054
|
+
log(kleur5.dim(M5().connectAll));
|
|
744
1055
|
}
|
|
745
1056
|
async function cmdAuth(args) {
|
|
746
1057
|
const sub = args[0];
|
|
@@ -761,7 +1072,7 @@ async function cmdAuth(args) {
|
|
|
761
1072
|
if (sub === "facebook") return void exitWith(await runMcpBin("mimi-seed-social-auth", ["facebook"]));
|
|
762
1073
|
if (sub === "instagram") return void exitWith(await runMcpBin("mimi-seed-social-auth", ["instagram"]));
|
|
763
1074
|
if (sub === "ci") {
|
|
764
|
-
const { cmdSetup: cmdSetup2 } = await import("./setup-
|
|
1075
|
+
const { cmdSetup: cmdSetup2 } = await import("./setup-NQHLA3AJ.js");
|
|
765
1076
|
await cmdSetup2(["--only", "github,gitlab", "--reconnect", "github,gitlab"]);
|
|
766
1077
|
return;
|
|
767
1078
|
}
|
|
@@ -781,7 +1092,7 @@ async function cmdAuth(args) {
|
|
|
781
1092
|
mcpArgs = ["--logout", ...rest];
|
|
782
1093
|
break;
|
|
783
1094
|
default:
|
|
784
|
-
log(kleur5.red(
|
|
1095
|
+
log(kleur5.red(t().auth.unknownSub(sub ?? "")));
|
|
785
1096
|
printAuthHelp();
|
|
786
1097
|
process.exit(1);
|
|
787
1098
|
}
|
|
@@ -791,6 +1102,33 @@ function exitWith(code) {
|
|
|
791
1102
|
if (code !== 0) process.exit(code);
|
|
792
1103
|
}
|
|
793
1104
|
|
|
1105
|
+
// src/lang.ts
|
|
1106
|
+
import kleur6 from "kleur";
|
|
1107
|
+
function log2(msg) {
|
|
1108
|
+
process.stdout.write(msg + "\n");
|
|
1109
|
+
}
|
|
1110
|
+
async function cmdLang(args) {
|
|
1111
|
+
const arg = args[0]?.toLowerCase();
|
|
1112
|
+
if (!arg) {
|
|
1113
|
+
log2(t().lang.current(resolveLang()));
|
|
1114
|
+
if (process.env.MIMI_SEED_LANG) {
|
|
1115
|
+
log2(kleur6.dim(" (MIMI_SEED_LANG)"));
|
|
1116
|
+
}
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
if (arg === "--help" || arg === "-h") {
|
|
1120
|
+
log2(t().lang.usage);
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
if (!isLang(arg)) {
|
|
1124
|
+
log2(kleur6.red(t().lang.invalid(arg)));
|
|
1125
|
+
process.exit(1);
|
|
1126
|
+
}
|
|
1127
|
+
writeSettings({ lang: arg });
|
|
1128
|
+
process.env.MIMI_SEED_LANG = arg;
|
|
1129
|
+
log2(kleur6.green(t().lang.saved(arg)));
|
|
1130
|
+
}
|
|
1131
|
+
|
|
794
1132
|
// src/cloud.ts
|
|
795
1133
|
import { spawn } from "child_process";
|
|
796
1134
|
var MCP_PKG2 = "@yoonion/mimi-seed-mcp";
|
|
@@ -801,9 +1139,7 @@ async function runMcpBin2(bin, extraArgs) {
|
|
|
801
1139
|
shell: true
|
|
802
1140
|
});
|
|
803
1141
|
child.on("error", (e) => {
|
|
804
|
-
process.stderr.write(
|
|
805
|
-
\u274C npx \uC2E4\uD589 \uC2E4\uD328: ${e.message}
|
|
806
|
-
`);
|
|
1142
|
+
process.stderr.write(t().auth.npxFailed("npx", e.message));
|
|
807
1143
|
resolve(1);
|
|
808
1144
|
});
|
|
809
1145
|
child.on("exit", (code) => resolve(code ?? 0));
|
|
@@ -827,10 +1163,46 @@ import { execSync as execSync2 } from "child_process";
|
|
|
827
1163
|
import fs2 from "fs";
|
|
828
1164
|
import os from "os";
|
|
829
1165
|
import path2 from "path";
|
|
830
|
-
import
|
|
831
|
-
function
|
|
1166
|
+
import kleur7 from "kleur";
|
|
1167
|
+
function log3(msg) {
|
|
832
1168
|
process.stdout.write(msg + "\n");
|
|
833
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
|
+
);
|
|
834
1206
|
function readClaudeJson() {
|
|
835
1207
|
const p = path2.join(os.homedir(), ".claude.json");
|
|
836
1208
|
try {
|
|
@@ -867,7 +1239,7 @@ function killByMarker(marker) {
|
|
|
867
1239
|
for (const pid of pids) {
|
|
868
1240
|
try {
|
|
869
1241
|
execSync2(`taskkill /F /PID ${pid}`, { stdio: "pipe" });
|
|
870
|
-
|
|
1242
|
+
log3(kleur7.dim(M6().killedPid(pid)));
|
|
871
1243
|
killed++;
|
|
872
1244
|
} catch {
|
|
873
1245
|
}
|
|
@@ -884,48 +1256,62 @@ function killByMarker(marker) {
|
|
|
884
1256
|
}
|
|
885
1257
|
async function cmdRestart(args) {
|
|
886
1258
|
const serverName = args[0] ?? "mimi-seed";
|
|
887
|
-
|
|
888
|
-
|
|
1259
|
+
log3(kleur7.bold(M6().title(serverName)));
|
|
1260
|
+
log3("");
|
|
889
1261
|
const d = readClaudeJson();
|
|
890
1262
|
const servers = d.mcpServers;
|
|
891
1263
|
const cfg = servers?.[serverName];
|
|
892
1264
|
if (!cfg) {
|
|
893
|
-
const available = servers ? Object.keys(servers).join(", ") :
|
|
894
|
-
|
|
895
|
-
|
|
1265
|
+
const available = servers ? Object.keys(servers).join(", ") : M6().none;
|
|
1266
|
+
log3(kleur7.red(M6().notFound(serverName)));
|
|
1267
|
+
log3(kleur7.dim(M6().registered(available)));
|
|
896
1268
|
process.exit(1);
|
|
897
1269
|
}
|
|
898
1270
|
if (cfg.type === "http" || cfg.type === "sse") {
|
|
899
|
-
|
|
900
|
-
|
|
1271
|
+
log3(kleur7.yellow(M6().httpServer(serverName)));
|
|
1272
|
+
log3(kleur7.dim(M6().httpHint));
|
|
901
1273
|
return;
|
|
902
1274
|
}
|
|
903
1275
|
const marker = findProcessMarker(cfg);
|
|
904
1276
|
if (!marker) {
|
|
905
|
-
|
|
906
|
-
|
|
1277
|
+
log3(kleur7.yellow(M6().noMarker));
|
|
1278
|
+
log3(kleur7.dim(M6().configLine(JSON.stringify(cfg))));
|
|
907
1279
|
process.exit(1);
|
|
908
1280
|
}
|
|
909
|
-
|
|
1281
|
+
log3(kleur7.dim(M6().markerLine(marker)));
|
|
910
1282
|
const { killed } = killByMarker(marker);
|
|
911
1283
|
if (killed === 0) {
|
|
912
|
-
|
|
913
|
-
|
|
1284
|
+
log3(kleur7.yellow(M6().noProcess));
|
|
1285
|
+
log3(kleur7.dim(M6().noProcessHint));
|
|
914
1286
|
} else {
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1287
|
+
log3("");
|
|
1288
|
+
log3(kleur7.green(M6().killed(serverName, killed)));
|
|
1289
|
+
log3(kleur7.dim(M6().killedHint));
|
|
918
1290
|
}
|
|
919
|
-
|
|
920
|
-
|
|
1291
|
+
log3("");
|
|
1292
|
+
log3(kleur7.cyan(M6().verify));
|
|
921
1293
|
}
|
|
922
1294
|
|
|
923
1295
|
// src/mcp-config.ts
|
|
924
1296
|
import fs3 from "fs/promises";
|
|
925
1297
|
import path3 from "path";
|
|
926
1298
|
import os2 from "os";
|
|
927
|
-
import
|
|
1299
|
+
import kleur8 from "kleur";
|
|
928
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
|
+
);
|
|
929
1315
|
function tomlString(value) {
|
|
930
1316
|
return JSON.stringify(value);
|
|
931
1317
|
}
|
|
@@ -956,13 +1342,13 @@ function claudeMcpAddCommand(cfg) {
|
|
|
956
1342
|
function printMcpSetup(cfg) {
|
|
957
1343
|
process.stdout.write(
|
|
958
1344
|
[
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1345
|
+
kleur8.bold(M7().claudeTitle),
|
|
1346
|
+
kleur8.cyan(` ${claudeMcpAddCommand(cfg)}`),
|
|
1347
|
+
kleur8.dim(M7().tokenWarning(CONFIG_LOCATION_HINT)),
|
|
962
1348
|
"",
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1349
|
+
kleur8.dim(M7().codexTitle),
|
|
1350
|
+
kleur8.dim(" mimi-seed mcp codex --write"),
|
|
1351
|
+
kleur8.dim(M7().codexManual)
|
|
966
1352
|
].join("\n") + "\n"
|
|
967
1353
|
);
|
|
968
1354
|
}
|
|
@@ -1027,62 +1413,391 @@ async function ensureReleaseManifest(cwd) {
|
|
|
1027
1413
|
// src/index.ts
|
|
1028
1414
|
var DEFAULT_WEB_BASE = process.env.MIMI_SEED_WEB_BASE ?? "https://mimi-seed.pryzm.gg";
|
|
1029
1415
|
var DEFAULT_MCP_ENDPOINT = `${DEFAULT_WEB_BASE}/api/mcp`;
|
|
1030
|
-
|
|
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
|
+
);
|
|
1745
|
+
function log4(msg) {
|
|
1031
1746
|
process.stdout.write(msg + "\n");
|
|
1032
1747
|
}
|
|
1033
1748
|
async function cmdInit(args) {
|
|
1034
1749
|
const local = args.includes("--local");
|
|
1035
1750
|
const cwd = process.cwd();
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1751
|
+
log4(kleur9.bold("Mimi Seed CLI \u2014 init"));
|
|
1752
|
+
log4(kleur9.dim(cwd));
|
|
1753
|
+
log4("");
|
|
1039
1754
|
if (!await hasAnyProjectSignal(cwd)) {
|
|
1040
|
-
|
|
1755
|
+
log4(kleur9.yellow(M8().noProjectSignal));
|
|
1041
1756
|
}
|
|
1042
|
-
|
|
1757
|
+
log4(M8().detecting);
|
|
1043
1758
|
const hints = await detectHints(cwd);
|
|
1044
1759
|
if (hints.length === 0) {
|
|
1045
|
-
|
|
1760
|
+
log4(kleur9.yellow(M8().noAppDetected));
|
|
1046
1761
|
} else {
|
|
1047
1762
|
for (const h of hints) {
|
|
1048
1763
|
const tag = [h.packageName && `android:${h.packageName}`, h.bundleId && `ios:${h.bundleId}`].filter(Boolean).join(" ");
|
|
1049
|
-
|
|
1764
|
+
log4(` \u2022 ${h.name ?? M8().unnamed} ${kleur9.dim(tag)}`);
|
|
1050
1765
|
}
|
|
1051
1766
|
}
|
|
1052
|
-
|
|
1767
|
+
log4("");
|
|
1053
1768
|
if (process.env.MIMI_SEED_TOKEN) {
|
|
1054
1769
|
const cfg2 = await getEffectiveConfig();
|
|
1055
1770
|
if (cfg2 && hints.length > 0) {
|
|
1056
|
-
|
|
1771
|
+
log4(kleur9.dim(M8().ciMode));
|
|
1057
1772
|
const payload = hints.map((h) => ({ name: h.name, packageName: h.packageName, bundleId: h.bundleId }));
|
|
1058
1773
|
const result = await mcpCall(cfg2.endpoint, cfg2.token, "sync_apps", { hints: payload });
|
|
1059
1774
|
if (result.isError) {
|
|
1060
|
-
|
|
1775
|
+
log4(kleur9.red(M8().syncFailed(result.text)));
|
|
1061
1776
|
} else {
|
|
1062
|
-
for (const line of result.text.split("\n"))
|
|
1777
|
+
for (const line of result.text.split("\n")) log4(" " + line);
|
|
1063
1778
|
}
|
|
1064
1779
|
}
|
|
1065
1780
|
const manifest2 = await ensureReleaseManifest(cwd);
|
|
1066
|
-
|
|
1067
|
-
|
|
1781
|
+
log4(kleur9.dim(M8().manifest(manifest2.created)));
|
|
1782
|
+
log4(kleur9.bold(M8().doneCi));
|
|
1068
1783
|
return;
|
|
1069
1784
|
}
|
|
1070
|
-
|
|
1785
|
+
log4(M8().waitingLogin);
|
|
1071
1786
|
const hostName = os3.hostname().slice(0, 32);
|
|
1072
1787
|
const name = `cli-${hostName}`;
|
|
1073
1788
|
const { port, promise } = await awaitHandshake(5 * 60 * 1e3);
|
|
1074
1789
|
const callback = `http://127.0.0.1:${port}/cb`;
|
|
1075
1790
|
const connectUrl = `${DEFAULT_WEB_BASE}/cli/connect?callback=${encodeURIComponent(callback)}&name=${encodeURIComponent(name)}`;
|
|
1076
|
-
|
|
1791
|
+
log4(kleur9.dim(` ${connectUrl}`));
|
|
1077
1792
|
await open(connectUrl);
|
|
1078
1793
|
let handshake;
|
|
1079
1794
|
try {
|
|
1080
1795
|
handshake = await promise;
|
|
1081
1796
|
} catch (e) {
|
|
1082
|
-
|
|
1797
|
+
log4(kleur9.red(M8().connectFailed(e.message)));
|
|
1083
1798
|
process.exit(1);
|
|
1084
1799
|
}
|
|
1085
|
-
|
|
1800
|
+
log4(kleur9.green(M8().tokenReceived));
|
|
1086
1801
|
const cfg = {
|
|
1087
1802
|
token: handshake.token,
|
|
1088
1803
|
prefix: handshake.prefix,
|
|
@@ -1091,18 +1806,18 @@ async function cmdInit(args) {
|
|
|
1091
1806
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1092
1807
|
};
|
|
1093
1808
|
await writeConfig(cfg);
|
|
1094
|
-
|
|
1095
|
-
|
|
1809
|
+
log4(kleur9.dim(M8().saved(CONFIG_LOCATION)));
|
|
1810
|
+
log4("");
|
|
1096
1811
|
if (hints.length > 0) {
|
|
1097
|
-
|
|
1812
|
+
log4(M8().registering);
|
|
1098
1813
|
const payload = hints.map((h) => ({ name: h.name, packageName: h.packageName, bundleId: h.bundleId }));
|
|
1099
1814
|
const result = await mcpCall(cfg.endpoint, cfg.token, "sync_apps", { hints: payload });
|
|
1100
1815
|
if (result.isError) {
|
|
1101
|
-
|
|
1816
|
+
log4(kleur9.red(M8().syncFailed(result.text)));
|
|
1102
1817
|
} else {
|
|
1103
|
-
for (const line of result.text.split("\n"))
|
|
1818
|
+
for (const line of result.text.split("\n")) log4(" " + line);
|
|
1104
1819
|
}
|
|
1105
|
-
|
|
1820
|
+
log4("");
|
|
1106
1821
|
}
|
|
1107
1822
|
const appLines = hints.flatMap((h) => {
|
|
1108
1823
|
const parts = [
|
|
@@ -1151,216 +1866,110 @@ async function cmdInit(args) {
|
|
|
1151
1866
|
if (!fs5.existsSync(claudeAgentPath)) {
|
|
1152
1867
|
fs5.mkdirSync(claudeDir, { recursive: true });
|
|
1153
1868
|
fs5.writeFileSync(claudeAgentPath, agentMd, { mode: 420 });
|
|
1154
|
-
|
|
1869
|
+
log4(kleur9.dim(M8().claudeAgent));
|
|
1155
1870
|
}
|
|
1156
1871
|
const codexAgentPath = path5.join(cwd, "AGENTS.md");
|
|
1157
1872
|
if (!fs5.existsSync(codexAgentPath)) {
|
|
1158
1873
|
fs5.writeFileSync(codexAgentPath, agentMd, { mode: 420 });
|
|
1159
|
-
|
|
1874
|
+
log4(kleur9.dim(M8().codexAgent));
|
|
1160
1875
|
}
|
|
1161
1876
|
const manifest = await ensureReleaseManifest(cwd);
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1877
|
+
log4(kleur9.dim(M8().manifest(manifest.created)));
|
|
1878
|
+
log4(kleur9.bold(M8().ready));
|
|
1879
|
+
log4("");
|
|
1880
|
+
log4(M8().askLike);
|
|
1881
|
+
log4(kleur9.cyan(M8().ask1));
|
|
1882
|
+
log4(kleur9.cyan(M8().ask2));
|
|
1883
|
+
log4(kleur9.cyan(M8().ask3));
|
|
1884
|
+
log4("");
|
|
1885
|
+
log4(M8().dashboard(kleur9.underline(DEFAULT_WEB_BASE + "/apps")));
|
|
1886
|
+
log4("");
|
|
1172
1887
|
printMcpSetup(cfg);
|
|
1173
1888
|
if (local) {
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1889
|
+
log4("");
|
|
1890
|
+
log4(kleur9.bold(M8().localTitle));
|
|
1891
|
+
log4(kleur9.dim(M8().localIntro));
|
|
1892
|
+
log4("");
|
|
1893
|
+
log4(M8().localStep1);
|
|
1179
1894
|
await cmdAuth(["login"]);
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1895
|
+
log4("");
|
|
1896
|
+
log4(M8().localStep2);
|
|
1897
|
+
log4(kleur9.cyan(" claude mcp add mimi-seed-local -- npx -y @yoonion/mimi-seed-mcp"));
|
|
1898
|
+
log4(kleur9.dim(M8().localCodexHint));
|
|
1899
|
+
log4("");
|
|
1900
|
+
log4(M8().localStep3);
|
|
1901
|
+
log4(kleur9.cyan(" mimi-seed setup"));
|
|
1902
|
+
log4(kleur9.dim(M8().localSetupHint));
|
|
1188
1903
|
}
|
|
1189
1904
|
}
|
|
1190
1905
|
async function cmdStatus() {
|
|
1191
1906
|
const cfg = await getEffectiveConfig();
|
|
1192
1907
|
if (!cfg) {
|
|
1193
|
-
|
|
1908
|
+
log4(kleur9.yellow(M8().notConnected));
|
|
1194
1909
|
process.exit(1);
|
|
1195
1910
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1911
|
+
log4(kleur9.bold(M8().statusTitle));
|
|
1912
|
+
log4(M8().statusToken(cfg.prefix, cfg.createdAt.slice(0, 10)));
|
|
1913
|
+
log4(M8().statusEndpoint(cfg.endpoint));
|
|
1914
|
+
log4("");
|
|
1915
|
+
log4(M8().appList);
|
|
1201
1916
|
const r = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
|
|
1202
1917
|
if (r.isError) {
|
|
1203
|
-
|
|
1918
|
+
log4(kleur9.red(M8().listFailed(r.text)));
|
|
1204
1919
|
process.exit(1);
|
|
1205
1920
|
}
|
|
1206
|
-
for (const line of r.text.split("\n"))
|
|
1921
|
+
for (const line of r.text.split("\n")) log4(" " + line);
|
|
1207
1922
|
}
|
|
1208
1923
|
async function cmdLogout() {
|
|
1209
1924
|
await deleteConfig();
|
|
1210
|
-
|
|
1211
|
-
|
|
1925
|
+
log4(kleur9.green(M8().logoutDone));
|
|
1926
|
+
log4(kleur9.dim(M8().logoutRevoke));
|
|
1212
1927
|
}
|
|
1213
1928
|
async function cmdMcp(args) {
|
|
1214
1929
|
const target = args[0] ?? "help";
|
|
1215
1930
|
const cfg = await getEffectiveConfig();
|
|
1216
1931
|
if (!cfg) {
|
|
1217
|
-
|
|
1932
|
+
log4(kleur9.yellow(M8().notConnected));
|
|
1218
1933
|
process.exit(1);
|
|
1219
1934
|
}
|
|
1220
1935
|
if (target === "codex") {
|
|
1221
1936
|
if (args.includes("--write")) {
|
|
1222
1937
|
const configPath = await writeCodexMcpConfig(cfg);
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1938
|
+
log4(kleur9.green(M8().codexWritten));
|
|
1939
|
+
log4(kleur9.dim(` ${configPath}`));
|
|
1940
|
+
log4(kleur9.dim(M8().codexWriteWarn));
|
|
1941
|
+
log4("");
|
|
1942
|
+
log4(M8().codexVerify);
|
|
1228
1943
|
return;
|
|
1229
1944
|
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1945
|
+
log4(kleur9.bold(M8().codexTitle));
|
|
1946
|
+
log4("");
|
|
1947
|
+
log4(M8().codexAuto);
|
|
1948
|
+
log4(kleur9.cyan(" mimi-seed mcp codex --write"));
|
|
1949
|
+
log4("");
|
|
1950
|
+
log4(M8().codexManual);
|
|
1951
|
+
log4(`[mcp_servers.mimi-seed]
|
|
1237
1952
|
url = "${cfg.endpoint}"
|
|
1238
1953
|
enabled = true
|
|
1239
1954
|
http_headers = { Authorization = "Bearer ${cfg.token}" }`);
|
|
1240
1955
|
return;
|
|
1241
1956
|
}
|
|
1242
1957
|
if (target === "claude") {
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1958
|
+
log4(kleur9.bold(M8().claudeTitle));
|
|
1959
|
+
log4(kleur9.cyan(` ${claudeMcpAddCommand(cfg)}`));
|
|
1960
|
+
log4(kleur9.dim(M8().claudeWarn));
|
|
1246
1961
|
return;
|
|
1247
1962
|
}
|
|
1248
1963
|
printMcpSetup(cfg);
|
|
1249
1964
|
}
|
|
1250
|
-
var CMD_USAGE = {
|
|
1251
|
-
init: `${kleur8.bold("mimi-seed init")} \u2014 \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
|
|
1252
|
-
|
|
1253
|
-
\uC571 \uC790\uB3D9 \uAC10\uC9C0(Expo/Gradle/Info.plist/pbxproj) \u2192 \uBE0C\uB77C\uC6B0\uC800 PAT \uBC1C\uAE09(\uC6D0\uACA9 MCP) \u2192 \uC571 \uB4F1\uB85D
|
|
1254
|
-
+ .claude/mimi-seed.md, AGENTS.md, docs/releases.json \uC0DD\uC131/\uD655\uC778.
|
|
1255
|
-
|
|
1256
|
-
\uC635\uC158:
|
|
1257
|
-
--local \uCD94\uAC00\uB85C Google OAuth \uB85C\uADF8\uC778 + \uB85C\uCEEC MCP(\uC2A4\uD1A0\uC5B4 \uC4F0\uAE30 \uB3C4\uAD6C \uC804\uCCB4) \uB4F1\uB85D \uC548\uB0B4`,
|
|
1258
|
-
setup: `${kleur8.bold("mimi-seed setup")} \u2014 \uAC00\uC9C4 \uACC4\uC815\uC744 \uD55C \uBC88\uC5D0 \uC5F0\uACB0 (\uC548\uB0B4\uD615 \uB9C8\uBC95\uC0AC)
|
|
1259
|
-
|
|
1260
|
-
\uC5F0\uACB0 \uC0C1\uD0DC\uB97C \uBA3C\uC800 \uBCF4\uC5EC\uC8FC\uACE0, \uC544\uC9C1 \uC5F0\uACB0\uB418\uC9C0 \uC54A\uC740 \uAC83\uB9CC \uC21C\uC11C\uB300\uB85C \uBB3C\uC5B4\uBCF8\uB2E4.
|
|
1261
|
-
\uAC01 \uD56D\uBAA9\uC5D0\uC11C [?] \uB97C \uB204\uB974\uBA74 "\uADF8 \uD1A0\uD070\uC744 \uC5B4\uB514\uC11C \uC5B4\uB5BB\uAC8C \uBC1C\uAE09\uBC1B\uB294\uC9C0"\uB97C \uC54C\uB824\uC900\uB2E4.
|
|
1262
|
-
\uC774\uBBF8 \uC5F0\uACB0\uB41C \uD56D\uBAA9\uC740 \uAC74\uB108\uB6F0\uBBC0\uB85C, \uC911\uAC04\uC5D0 \uADF8\uB9CC\uB450\uACE0 \uB098\uC911\uC5D0 \uB2E4\uC2DC \uC2E4\uD589\uD574\uB3C4 \uB41C\uB2E4.
|
|
1263
|
-
|
|
1264
|
-
\uC635\uC158:
|
|
1265
|
-
--only <ids> \uC9C0\uC815\uD55C \uC790\uACA9\uC99D\uBA85\uB9CC (\uC27C\uD45C \uAD6C\uBD84: oauth,appstore,jenkins \u2026)
|
|
1266
|
-
--reconnect <ids> \uC774\uBBF8 \uC5F0\uACB0\uB3FC \uC788\uC5B4\uB3C4 \uB2E4\uC2DC \uC124\uC815
|
|
1267
|
-
--platform android,ios \uD50C\uB7AB\uD3FC \uAC15\uC81C \uC9C0\uC815 (\uAE30\uBCF8: \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C \uC790\uB3D9 \uAC10\uC9C0)
|
|
1268
|
-
--yes, -y \uC544\uBB34\uAC83\uB3C4 \uBB3B\uC9C0 \uC54A\uACE0 \uC0C1\uD0DC\uD45C\uB9CC \uCD9C\uB825
|
|
1269
|
-
--non-interactive \uC704\uC640 \uB3D9\uC77C (CI \uC6A9)
|
|
1270
|
-
--fail-on-missing \uD544\uC218 \uC790\uACA9\uC99D\uBA85\uC774 \uC5C6\uC73C\uBA74 exit 1 (CI \uAC8C\uC774\uD2B8)
|
|
1271
|
-
|
|
1272
|
-
${kleur8.dim("\uBE44TTY / CI \uD658\uACBD\uC5D0\uC11C\uB294 \uD504\uB86C\uD504\uD2B8 \uC5C6\uC774 \uC0C1\uD0DC\uD45C\uB9CC \uCD9C\uB825\uD55C\uB2E4.")}`,
|
|
1273
|
-
status: `${kleur8.bold("mimi-seed status")} \u2014 \uC5F0\uACB0 \uC0C1\uD0DC + \uB4F1\uB85D \uC571 \uBAA9\uB85D. \uC635\uC158 \uC5C6\uC74C.`,
|
|
1274
|
-
doctor: `${kleur8.bold("mimi-seed doctor")} \u2014 \uD658\uACBD \uC9C4\uB2E8 (\uD1A0\uD070\xB7Node\xB7Git\xB7\uD504\uB85C\uC81D\uD2B8\xB7CI). \uC635\uC158 \uC5C6\uC74C.`,
|
|
1275
|
-
logout: `${kleur8.bold("mimi-seed logout")} \u2014 \uB85C\uCEEC \uC124\uC815(config.json) \uC0AD\uC81C. \uC635\uC158 \uC5C6\uC74C.`,
|
|
1276
|
-
restart: `${kleur8.bold("mimi-seed restart")} \u2014 MCP \uC11C\uBC84 \uD504\uB85C\uC138\uC2A4 \uC7AC\uC2DC\uC791 (\uAE30\uBCF8: mimi-seed)
|
|
1277
|
-
|
|
1278
|
-
mimi-seed restart [server-name]`,
|
|
1279
|
-
notes: `${kleur8.bold("mimi-seed notes")} \u2014 \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 (git log \u2192 AI \u2192 \uB9C8\uCF13 \uC801\uC6A9)
|
|
1280
|
-
|
|
1281
|
-
\uC635\uC158:
|
|
1282
|
-
--from <ref> \uC2DC\uC791 \uCEE4\uBC0B/\uD0DC\uADF8 (\uAE30\uBCF8: \uCD5C\uC2E0 \uD0DC\uADF8)
|
|
1283
|
-
--to <ref> \uB05D \uCEE4\uBC0B (\uAE30\uBCF8: HEAD)
|
|
1284
|
-
--locale ko,en-US \uB300\uC0C1 \uB85C\uCF00\uC77C (\uC27C\uD45C \uAD6C\uBD84)
|
|
1285
|
-
--apply \uC0DD\uC131 \uD6C4 \uC2A4\uD1A0\uC5B4\uC5D0 \uBC14\uB85C \uC801\uC6A9
|
|
1286
|
-
--no-interactive CI \uBAA8\uB4DC (\uD504\uB86C\uD504\uD2B8 \uC5C6\uC74C)
|
|
1287
|
-
--limit <n> \uCD5C\uB300 \uCEE4\uBC0B \uC218 (\uAE30\uBCF8: 30)`,
|
|
1288
|
-
check: `${kleur8.bold("mimi-seed check")} \u2014 \uCD9C\uC2DC \uC804 Readiness \uC810\uAC80
|
|
1289
|
-
|
|
1290
|
-
\uC635\uC158:
|
|
1291
|
-
--app <id> \uC571 ID \uC9C0\uC815
|
|
1292
|
-
--fail-on-blocker \uBE14\uB85C\uCEE4 \uC788\uC73C\uBA74 exit 1 (CI\uC6A9)`,
|
|
1293
|
-
review: `${kleur8.bold("mimi-seed review")} \u2014 \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
|
|
1294
|
-
|
|
1295
|
-
\uC635\uC158:
|
|
1296
|
-
--text <\uB0B4\uC6A9> \uB9AC\uBDF0 \uC6D0\uBB38 (\uBBF8\uC785\uB825 \uC2DC \uB300\uD654\uD615 \uD504\uB86C\uD504\uD2B8)
|
|
1297
|
-
--rating <1-5> \uBCC4\uC810
|
|
1298
|
-
--tone <tone> friendly / professional / empathetic / brief (\uAE30\uBCF8: friendly)
|
|
1299
|
-
--language <\uCF54\uB4DC> \uB2F5\uBCC0 \uC5B8\uC5B4 (\uAE30\uBCF8: ko)
|
|
1300
|
-
--app-name <\uC774\uB984> \uC571 \uC774\uB984 (\uB9E5\uB77D\uC6A9)
|
|
1301
|
-
--apply \uB2F5\uBCC0\uC744 Play Store\uC5D0 \uAC8C\uC2DC
|
|
1302
|
-
--review-id <id> \uB9AC\uBDF0 ID (--apply \uC2DC \uD544\uC694)
|
|
1303
|
-
--package-name <p> \uD328\uD0A4\uC9C0\uBA85 (--apply \uC2DC \uD544\uC694)
|
|
1304
|
-
--no-interactive CI \uBAA8\uB4DC`,
|
|
1305
|
-
deploy: `${kleur8.bold("mimi-seed deploy")} \u2014 \uC571 \uC790\uB3D9 \uBC30\uD3EC (CI \uBE4C\uB4DC \u2192 Play Store/App Store)
|
|
1306
|
-
|
|
1307
|
-
\uC635\uC158:
|
|
1308
|
-
--platform android|ios \uBC30\uD3EC \uD50C\uB7AB\uD3FC (\uAE30\uBCF8: android)
|
|
1309
|
-
--app <id> \uC571 ID \uC9C0\uC815
|
|
1310
|
-
--version-code <n> \uBE4C\uB4DC \uBC88\uD638 \uC9C1\uC811 \uC9C0\uC815 (--skip-build \uC640 \uD568\uAED8)
|
|
1311
|
-
--from <ref> \uCEE4\uBC0B \uBC94\uC704 \uC2DC\uC791 (\uB9B4\uB9AC\uC988 \uB178\uD2B8\uC6A9)
|
|
1312
|
-
--to <ref> \uCEE4\uBC0B \uBC94\uC704 \uB05D (\uAE30\uBCF8: HEAD)
|
|
1313
|
-
--language <\uCF54\uB4DC> \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC5B8\uC5B4 (\uAE30\uBCF8: ko-KR)
|
|
1314
|
-
--dry-run \uC2E4\uC81C \uBC30\uD3EC \uC5C6\uC774 \uD30C\uC774\uD504\uB77C\uC778 \uD14C\uC2A4\uD2B8
|
|
1315
|
-
--yes, -y \uBC30\uD3EC \uD655\uC778 \uD504\uB86C\uD504\uD2B8 \uC0DD\uB7B5 (\uC2A4\uD06C\uB9BD\uD2B8/\uC790\uB3D9\uD654\uC6A9)
|
|
1316
|
-
--skip-build CI \uBE4C\uB4DC \uAC74\uB108\uB700 (--version-code \uD544\uC218)
|
|
1317
|
-
--ci jenkins|github|gitlab CI \uAC15\uC81C \uC120\uD0DD (\uAE30\uBCF8: auto)
|
|
1318
|
-
--workflow <file> GitHub workflow \uD30C\uC77C (\uC608: deploy.yml)
|
|
1319
|
-
--ref <branch|tag> GitHub/GitLab \uBE0C\uB79C\uCE58/\uD0DC\uADF8 (\uAE30\uBCF8: main)
|
|
1320
|
-
setup-jenkins / setup-github / setup-gitlab CI \uC124\uC815 \uB300\uD654\uD615 \uB4F1\uB85D`,
|
|
1321
|
-
mcp: `${kleur8.bold("mimi-seed mcp")} \u2014 Claude/Codex MCP \uC5F0\uACB0
|
|
1322
|
-
|
|
1323
|
-
\uC11C\uBE0C\uBA85\uB839:
|
|
1324
|
-
mimi-seed mcp \uD604\uC7AC \uC124\uC815 + \uB4F1\uB85D \uC548\uB0B4
|
|
1325
|
-
mimi-seed mcp claude Claude Code \uB4F1\uB85D \uBA85\uB839 \uCD9C\uB825
|
|
1326
|
-
mimi-seed mcp codex Codex \uB4F1\uB85D \uC548\uB0B4
|
|
1327
|
-
mimi-seed mcp codex --write ~/.codex/config.toml\uC5D0 \uC9C1\uC811 \uAE30\uB85D (\u26A0 \uC2E4\uC81C \uD1A0\uD070 \uD3C9\uBB38 \uC800\uC7A5)`
|
|
1328
|
-
};
|
|
1329
1965
|
function printCommandHelp(cmd) {
|
|
1330
|
-
const usage =
|
|
1966
|
+
const usage = M8().usage[cmd];
|
|
1331
1967
|
if (!usage) return false;
|
|
1332
|
-
|
|
1968
|
+
log4(usage);
|
|
1333
1969
|
return true;
|
|
1334
1970
|
}
|
|
1335
1971
|
function printHelp() {
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
${kleur8.bold("\uBA85\uB839\uC5B4:")}
|
|
1339
|
-
${kleur8.cyan("mimi-seed init")} \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
|
|
1340
|
-
${kleur8.cyan("mimi-seed setup")} \uAC00\uC9C4 \uACC4\uC815\uC744 \uD55C \uBC88\uC5D0 \uC5F0\uACB0 (\uC548\uB0B4\uD615 \uB9C8\uBC95\uC0AC)
|
|
1341
|
-
${kleur8.cyan("mimi-seed status")} \uC5F0\uACB0 \uC0C1\uD0DC + \uB4F1\uB85D \uC571 \uBAA9\uB85D
|
|
1342
|
-
${kleur8.cyan("mimi-seed auth")} \uC790\uACA9\uC99D\uBA85 \uAC1C\uBCC4 \uC778\uC99D (Google / App Store / Play / Jenkins / CI \u2026)
|
|
1343
|
-
${kleur8.cyan("mimi-seed firebase")} Firebase \uC571 \uC0DD\uC131\xB7config \uB2E4\uC6B4\uB85C\uB4DC\xB7GA4 \uB9C1\uD06C
|
|
1344
|
-
${kleur8.cyan("mimi-seed admob")} AdMob \uACC4\uC815\xB7\uC571\xB7\uAD11\uACE0\uB2E8\uC704 \uC870\uD68C \uBC0F \uC0DD\uC131
|
|
1345
|
-
${kleur8.cyan("mimi-seed ga4")} GA4 property\xB7data stream \uC0DD\uC131\xB7\uC870\uD68C
|
|
1346
|
-
${kleur8.cyan("mimi-seed doctor")} \uD658\uACBD \uC9C4\uB2E8 (\uD1A0\uD070\xB7Git\xB7\uD504\uB85C\uC81D\uD2B8\xB7CI \uCCB4\uD06C)
|
|
1347
|
-
${kleur8.cyan("mimi-seed check")} \uCD9C\uC2DC \uC804 Readiness \uC810\uAC80
|
|
1348
|
-
${kleur8.cyan("mimi-seed notes")} \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 (git log \u2192 AI \u2192 \uB9C8\uCF13 \uC801\uC6A9)
|
|
1349
|
-
${kleur8.cyan("mimi-seed review")} \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
|
|
1350
|
-
${kleur8.cyan("mimi-seed deploy")} \uC571 \uC790\uB3D9 \uBC30\uD3EC (CI \u2192 Play Store/App Store)
|
|
1351
|
-
${kleur8.cyan("mimi-seed mcp")} Claude/Codex MCP \uC5F0\uACB0 \uC548\uB0B4 \uBC0F Codex \uC124\uC815 \uC4F0\uAE30
|
|
1352
|
-
${kleur8.cyan("mimi-seed restart")} MCP \uC11C\uBC84 \uD504\uB85C\uC138\uC2A4 \uC7AC\uC2DC\uC791 (\uAE30\uBCF8: mimi-seed)
|
|
1353
|
-
${kleur8.cyan("mimi-seed logout")} \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C
|
|
1354
|
-
|
|
1355
|
-
${kleur8.dim("\uAC01 \uBA85\uB839 \uC0C1\uC138 \uC635\uC158:")} ${kleur8.cyan("mimi-seed <command> --help")}
|
|
1356
|
-
|
|
1357
|
-
${kleur8.bold("\uD658\uACBD\uBCC0\uC218:")}
|
|
1358
|
-
MIMI_SEED_TOKEN PAT \uD1A0\uD070 (CI/CD \uBB34\uC778\uC99D \uBAA8\uB4DC)
|
|
1359
|
-
MIMI_SEED_WEB_BASE \uC11C\uBC84 \uC8FC\uC18C (\uAE30\uBCF8: https://mimi-seed.pryzm.gg)
|
|
1360
|
-
ANTHROPIC_API_KEY AI \uB178\uD2B8 \uC0DD\uC131 \uD65C\uC131\uD654 (\uC120\uD0DD)
|
|
1361
|
-
MIMI_SEED_GOOGLE_CLIENT_ID / _SECRET
|
|
1362
|
-
\uC9C1\uC811 \uB9CC\uB4E0 Google OAuth \uD074\uB77C\uC774\uC5B8\uD2B8 \uC0AC\uC6A9 (\uBBF8\uC9C0\uC815 \uC2DC \uB85C\uADF8\uC778 \uB54C \uC6F9 \uCF58\uC194\uC5D0\uC11C \uBC1B\uC544\uC634)
|
|
1363
|
-
`);
|
|
1972
|
+
log4(M8().help);
|
|
1364
1973
|
}
|
|
1365
1974
|
async function main() {
|
|
1366
1975
|
const cmd = process.argv[2];
|
|
@@ -1376,6 +1985,9 @@ async function main() {
|
|
|
1376
1985
|
case "setup":
|
|
1377
1986
|
await cmdSetup(restArgs);
|
|
1378
1987
|
break;
|
|
1988
|
+
case "lang":
|
|
1989
|
+
await cmdLang(restArgs);
|
|
1990
|
+
break;
|
|
1379
1991
|
case "status":
|
|
1380
1992
|
await cmdStatus();
|
|
1381
1993
|
break;
|
|
@@ -1421,13 +2033,13 @@ async function main() {
|
|
|
1421
2033
|
printHelp();
|
|
1422
2034
|
break;
|
|
1423
2035
|
default:
|
|
1424
|
-
|
|
2036
|
+
log4(kleur9.red(t().common.unknownCommand(cmd)));
|
|
1425
2037
|
printHelp();
|
|
1426
2038
|
process.exit(1);
|
|
1427
2039
|
}
|
|
1428
2040
|
} catch (e) {
|
|
1429
|
-
|
|
1430
|
-
if (process.env.DEBUG)
|
|
2041
|
+
log4(kleur9.red(t().common.error(e.message)));
|
|
2042
|
+
if (process.env.DEBUG) log4(e.stack ?? "");
|
|
1431
2043
|
process.exit(1);
|
|
1432
2044
|
}
|
|
1433
2045
|
}
|