mimi-seed 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -3
- package/dist/index.js +201 -38
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -21,6 +21,7 @@ npx mimi-seed init
|
|
|
21
21
|
| `mimi-seed doctor` | 환경 진단 (토큰·Git·앱·CI 한 번에 체크) |
|
|
22
22
|
| `mimi-seed check` | 출시 전 Readiness 점검 (점수 + 블로커) |
|
|
23
23
|
| `mimi-seed notes` | AI 릴리즈 노트 생성 (git log → 3 톤 → 다국어 → 적용) |
|
|
24
|
+
| `mimi-seed review` | AI 리뷰 답변 초안 생성 및 Play Store 게시 |
|
|
24
25
|
| `mimi-seed logout` | 로컬 설정 삭제 |
|
|
25
26
|
|
|
26
27
|
---
|
|
@@ -108,13 +109,73 @@ mimi-seed doctor
|
|
|
108
109
|
|
|
109
110
|
---
|
|
110
111
|
|
|
112
|
+
## mimi-seed review
|
|
113
|
+
|
|
114
|
+
스토어 리뷰에 대한 AI 답변 초안을 생성합니다. `ANTHROPIC_API_KEY` 필요.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# 대화형 — 리뷰 입력 후 답변 생성
|
|
118
|
+
mimi-seed review
|
|
119
|
+
|
|
120
|
+
# 리뷰 텍스트 직접 지정
|
|
121
|
+
mimi-seed review --text "앱이 자꾸 튕겨요" --rating 2
|
|
122
|
+
|
|
123
|
+
# 톤·언어 지정
|
|
124
|
+
mimi-seed review --text "Great app!" --rating 5 --tone professional --language en
|
|
125
|
+
|
|
126
|
+
# 생성 후 Play Store 바로 게시
|
|
127
|
+
mimi-seed review --text "버그 있어요" --rating 2 \
|
|
128
|
+
--apply --package-name com.example.app --review-id <reviewId>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### 옵션
|
|
132
|
+
|
|
133
|
+
| 옵션 | 기본값 | 설명 |
|
|
134
|
+
|------|--------|------|
|
|
135
|
+
| `--text <내용>` | (프롬프트) | 리뷰 원문 |
|
|
136
|
+
| `--rating <1-5>` | — | 별점 (맥락용) |
|
|
137
|
+
| `--tone <tone>` | `friendly` | `friendly` / `professional` / `empathetic` / `brief` |
|
|
138
|
+
| `--language <코드>` | `ko` | 답변 언어 (`ko`, `en`, `ja` 등) |
|
|
139
|
+
| `--app-name <이름>` | — | 앱 이름 (프롬프트 맥락용) |
|
|
140
|
+
| `--apply` | false | 답변을 Play Store에 바로 게시 |
|
|
141
|
+
| `--review-id <id>` | — | 게시 대상 리뷰 ID (`--apply` 시 필요) |
|
|
142
|
+
| `--package-name <p>` | — | Android 패키지명 (`--apply` 시 필요) |
|
|
143
|
+
| `--no-interactive` | false | CI 모드 (프롬프트 없음) |
|
|
144
|
+
|
|
145
|
+
> AI 생성 답변은 초안입니다. 게시 전 반드시 검토하세요.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
111
149
|
## CI/CD 사용
|
|
112
150
|
|
|
113
|
-
|
|
151
|
+
### 방법 1 — 재사용 가능 워크플로우 (권장)
|
|
152
|
+
|
|
153
|
+
`.github/workflows/release.yml` 파일 하나만 추가하면 끝납니다.
|
|
154
|
+
|
|
155
|
+
```yaml
|
|
156
|
+
name: App Release
|
|
157
|
+
|
|
158
|
+
on:
|
|
159
|
+
push:
|
|
160
|
+
tags: ['v*'] # v1.2.3 태그 push 시 자동 실행
|
|
161
|
+
workflow_dispatch: # 수동 실행 버튼
|
|
162
|
+
|
|
163
|
+
jobs:
|
|
164
|
+
release:
|
|
165
|
+
uses: jeonghwanko/app-gen/.github/workflows/mimi-seed-release.yml@master
|
|
166
|
+
with:
|
|
167
|
+
locales: 'ko,en-US' # 다국어 릴리즈 노트
|
|
168
|
+
apply-notes: true # 스토어에 자동 적용
|
|
169
|
+
fail-on-blocker: true # 블로커 있으면 실패
|
|
170
|
+
secrets:
|
|
171
|
+
MIMI_SEED_TOKEN: ${{ secrets.MIMI_SEED_TOKEN }}
|
|
172
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} # 선택: AI 생성
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### 방법 2 — 기존 워크플로우에 스텝 추가
|
|
114
176
|
|
|
115
177
|
```yaml
|
|
116
|
-
|
|
117
|
-
- name: Release notes & readiness check
|
|
178
|
+
- name: 릴리즈 노트 생성 및 적용
|
|
118
179
|
env:
|
|
119
180
|
MIMI_SEED_TOKEN: ${{ secrets.MIMI_SEED_TOKEN }}
|
|
120
181
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import os2 from "os";
|
|
5
|
-
import
|
|
5
|
+
import kleur5 from "kleur";
|
|
6
6
|
import open from "open";
|
|
7
7
|
|
|
8
8
|
// src/detect.ts
|
|
@@ -659,6 +659,154 @@ async function cmdNotes(argv) {
|
|
|
659
659
|
}
|
|
660
660
|
}
|
|
661
661
|
|
|
662
|
+
// src/review.ts
|
|
663
|
+
import { createInterface as createInterface2 } from "readline/promises";
|
|
664
|
+
import Anthropic2 from "@anthropic-ai/sdk";
|
|
665
|
+
import kleur4 from "kleur";
|
|
666
|
+
function parseArgs3(argv) {
|
|
667
|
+
const args = { tone: "friendly", language: "ko", apply: false, noInteractive: false };
|
|
668
|
+
for (let i = 0; i < argv.length; i++) {
|
|
669
|
+
if (argv[i] === "--text" && argv[i + 1]) args.text = argv[++i];
|
|
670
|
+
if (argv[i] === "--rating" && argv[i + 1]) args.rating = parseInt(argv[++i], 10);
|
|
671
|
+
if (argv[i] === "--tone" && argv[i + 1]) args.tone = argv[++i];
|
|
672
|
+
if (argv[i] === "--language" && argv[i + 1]) args.language = argv[++i];
|
|
673
|
+
if (argv[i] === "--app-name" && argv[i + 1]) args.appName = argv[++i];
|
|
674
|
+
if (argv[i] === "--developer-name" && argv[i + 1]) args.developerName = argv[++i];
|
|
675
|
+
if (argv[i] === "--apply") args.apply = true;
|
|
676
|
+
if (argv[i] === "--review-id" && argv[i + 1]) args.reviewId = argv[++i];
|
|
677
|
+
if (argv[i] === "--package-name" && argv[i + 1]) args.packageName = argv[++i];
|
|
678
|
+
if (argv[i] === "--no-interactive") args.noInteractive = true;
|
|
679
|
+
}
|
|
680
|
+
return args;
|
|
681
|
+
}
|
|
682
|
+
async function promptUser2(question) {
|
|
683
|
+
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
684
|
+
const answer = await rl.question(question);
|
|
685
|
+
rl.close();
|
|
686
|
+
return answer.trim();
|
|
687
|
+
}
|
|
688
|
+
var TONE_GUIDES = {
|
|
689
|
+
friendly: "\uCE5C\uADFC\uD558\uACE0 \uB530\uB73B\uD558\uAC8C. \uC774\uBAA8\uC9C0 1~2\uAC1C \uC0AC\uC6A9. \uAC10\uC0AC \uC778\uC0AC\uB85C \uC2DC\uC791.",
|
|
690
|
+
professional: "\uC815\uC911\uD558\uACE0 \uACF5\uC2DD\uC801\uC73C\uB85C. \uBB38\uC81C\uB97C \uC778\uC815\uD558\uACE0 \uD574\uACB0\uCC45\uC744 \uC81C\uC2DC.",
|
|
691
|
+
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.",
|
|
692
|
+
brief: "2~3\uBB38\uC7A5\uC73C\uB85C \uAC04\uACB0\uD558\uAC8C. \uD575\uC2EC \uC751\uB2F5\uB9CC."
|
|
693
|
+
};
|
|
694
|
+
var SENTIMENT_PROMPTS = {
|
|
695
|
+
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.",
|
|
696
|
+
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.",
|
|
697
|
+
neutral: "\uC911\uB9BD\uC801\uC778 \uB9AC\uBDF0. \uD53C\uB4DC\uBC31\uC5D0 \uAC10\uC0AC\uD558\uACE0 \uAC1C\uC120 \uB178\uB825\uC744 \uC57D\uC18D.",
|
|
698
|
+
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.",
|
|
699
|
+
feature_request: "\uAE30\uB2A5 \uC694\uCCAD. \uD53C\uB4DC\uBC31\uC5D0 \uAC10\uC0AC\uD558\uACE0, \uAC80\uD1A0\uD558\uACA0\uB2E4\uACE0 \uC57D\uC18D."
|
|
700
|
+
};
|
|
701
|
+
function detectSentiment(text) {
|
|
702
|
+
const lower = text.toLowerCase();
|
|
703
|
+
if (["\uBC84\uADF8", "\uC624\uB958", "\uC548\uB428", "crash", "bug", "error", "broken"].some((w) => lower.includes(w))) return "bug_report";
|
|
704
|
+
if (["\uCD94\uAC00", "\uC6D0\uD574", "\uC788\uC73C\uBA74", "wish", "feature", "add", "would like"].some((w) => lower.includes(w))) return "feature_request";
|
|
705
|
+
if (["\uBCC4\uB85C", "\uC2E4\uB9DD", "\uC9DC\uC99D", "terrible", "worst", "awful"].some((w) => lower.includes(w))) return "negative";
|
|
706
|
+
if (["\uC88B\uC544", "\uCD5C\uACE0", "\uD6CC\uB96D", "great", "excellent", "love", "perfect"].some((w) => lower.includes(w))) return "positive";
|
|
707
|
+
return "neutral";
|
|
708
|
+
}
|
|
709
|
+
async function generateReply(opts) {
|
|
710
|
+
const client = new Anthropic2({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
711
|
+
const sentiment = detectSentiment(opts.text);
|
|
712
|
+
const toneGuide = TONE_GUIDES[opts.tone] ?? TONE_GUIDES.friendly;
|
|
713
|
+
const sentimentPrompt = SENTIMENT_PROMPTS[sentiment] ?? SENTIMENT_PROMPTS.neutral;
|
|
714
|
+
const stars = opts.rating !== void 0 ? `\uBCC4\uC810: ${"\u2605".repeat(opts.rating)}${"\u2606".repeat(5 - opts.rating)} (${opts.rating}/5)
|
|
715
|
+
` : "";
|
|
716
|
+
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" };
|
|
717
|
+
const langName = LOCALE_NAMES[opts.language] ?? opts.language;
|
|
718
|
+
const response = await client.messages.create({
|
|
719
|
+
model: "claude-haiku-4-5-20251001",
|
|
720
|
+
max_tokens: 500,
|
|
721
|
+
system: `\uC571 \uAC1C\uBC1C\uC790\uB97C \uB300\uC2E0\uD574 \uC2A4\uD1A0\uC5B4 \uB9AC\uBDF0\uC5D0 \uB2F5\uBCC0\uD558\uB294 \uC804\uBB38\uAC00. ${langName}\uB85C 150\uC790 \uC774\uB0B4\uB85C \uB2F5\uBCC0. ${toneGuide} \uAC1C\uBC1C\uC790 \uC774\uB984: ${opts.developerName ?? "\uAC1C\uBC1C\uD300"}`,
|
|
722
|
+
messages: [{
|
|
723
|
+
role: "user",
|
|
724
|
+
content: `\uC571: ${opts.appName ?? "\uC571"}
|
|
725
|
+
${stars}\uB9AC\uBDF0: "${opts.text}"
|
|
726
|
+
|
|
727
|
+
\uC9C0\uCE68: ${sentimentPrompt}
|
|
728
|
+
|
|
729
|
+
JSON\uC73C\uB85C\uB9CC \uC751\uB2F5: { "reply": "\uB2F5\uBCC0 \uD14D\uC2A4\uD2B8" }`
|
|
730
|
+
}]
|
|
731
|
+
});
|
|
732
|
+
const raw = response.content[0].type === "text" ? response.content[0].text : "";
|
|
733
|
+
const match = raw.match(/\{[\s\S]*\}/);
|
|
734
|
+
if (!match) throw new Error("AI \uC751\uB2F5 \uD30C\uC2F1 \uC2E4\uD328");
|
|
735
|
+
const parsed = JSON.parse(match[0]);
|
|
736
|
+
return parsed.reply;
|
|
737
|
+
}
|
|
738
|
+
async function cmdReview(argv) {
|
|
739
|
+
const args = parseArgs3(argv);
|
|
740
|
+
process.stdout.write(kleur4.bold("mimi-seed review \u2014 \uB9AC\uBDF0 \uB2F5\uBCC0 \uC0DD\uC131\n\n"));
|
|
741
|
+
if (!process.env.ANTHROPIC_API_KEY) {
|
|
742
|
+
process.stdout.write(kleur4.red("ANTHROPIC_API_KEY\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.\n"));
|
|
743
|
+
process.stdout.write(kleur4.dim("\uD65C\uC131\uD654: export ANTHROPIC_API_KEY=sk-ant-...\n"));
|
|
744
|
+
process.exit(1);
|
|
745
|
+
}
|
|
746
|
+
let reviewText = args.text;
|
|
747
|
+
if (!reviewText) {
|
|
748
|
+
if (args.noInteractive) {
|
|
749
|
+
process.stdout.write(kleur4.red("--text <\uB9AC\uBDF0 \uB0B4\uC6A9> \uC774 \uD544\uC694\uD569\uB2C8\uB2E4.\n"));
|
|
750
|
+
process.exit(1);
|
|
751
|
+
}
|
|
752
|
+
process.stdout.write(kleur4.dim("\uB9AC\uBDF0 \uB0B4\uC6A9\uC744 \uC785\uB825\uD558\uC138\uC694 (\uC5EC\uB7EC \uC904: Ctrl+D\uB85C \uC644\uB8CC):\n"));
|
|
753
|
+
reviewText = await promptUser2("> ");
|
|
754
|
+
if (!reviewText) {
|
|
755
|
+
process.stdout.write(kleur4.yellow("\uC785\uB825 \uC5C6\uC74C. \uC885\uB8CC.\n"));
|
|
756
|
+
process.exit(0);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
if (args.rating !== void 0) {
|
|
760
|
+
const stars = "\u2605".repeat(args.rating) + "\u2606".repeat(5 - args.rating);
|
|
761
|
+
process.stdout.write(kleur4.dim(`\uBCC4\uC810: ${stars} tone: ${args.tone} \uC5B8\uC5B4: ${args.language}
|
|
762
|
+
|
|
763
|
+
`));
|
|
764
|
+
}
|
|
765
|
+
process.stdout.write("\u{1F916} Claude AI\uB85C \uB2F5\uBCC0 \uC0DD\uC131 \uC911...\n");
|
|
766
|
+
const reply = await generateReply({ ...args, text: reviewText });
|
|
767
|
+
process.stdout.write("\n" + kleur4.bold("\u2500\u2500\u2500 \uC81C\uC548 \uB2F5\uBCC0 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
|
|
768
|
+
process.stdout.write(reply + "\n");
|
|
769
|
+
process.stdout.write(kleur4.dim("\n\u26A0 \uC774 \uB2F5\uBCC0\uC740 AI\uAC00 \uC0DD\uC131\uD55C \uCD08\uC548\uC785\uB2C8\uB2E4. \uAC8C\uC2DC \uC804 \uBC18\uB4DC\uC2DC \uAC80\uD1A0\uD558\uC138\uC694.\n\n"));
|
|
770
|
+
const shouldPrompt = !args.apply && !args.noInteractive && process.stdout.isTTY;
|
|
771
|
+
if (!args.apply && !shouldPrompt) return;
|
|
772
|
+
let doApply = args.apply;
|
|
773
|
+
if (shouldPrompt) {
|
|
774
|
+
const ans = await promptUser2("\uC774 \uB2F5\uBCC0\uC744 Play Store\uC5D0 \uAC8C\uC2DC\uD560\uAE4C\uC694? [y/N]: ");
|
|
775
|
+
doApply = ans.toLowerCase() === "y";
|
|
776
|
+
}
|
|
777
|
+
if (!doApply) {
|
|
778
|
+
process.stdout.write(kleur4.dim("\uAC74\uB108\uB700.\n"));
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
const cfg = await getEffectiveConfig();
|
|
782
|
+
if (!cfg) {
|
|
783
|
+
process.stdout.write(kleur4.yellow("Mimi Seed \uACC4\uC815 \uC5F0\uACB0 \uD544\uC694. `mimi-seed init` \uC2E4\uD589.\n"));
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
let reviewId = args.reviewId;
|
|
787
|
+
let packageName = args.packageName;
|
|
788
|
+
if (!reviewId || !packageName) {
|
|
789
|
+
if (args.noInteractive) {
|
|
790
|
+
process.stdout.write(kleur4.red("--review-id \uC640 --package-name \uC774 \uD544\uC694\uD569\uB2C8\uB2E4.\n"));
|
|
791
|
+
process.exit(1);
|
|
792
|
+
}
|
|
793
|
+
if (!packageName) packageName = await promptUser2("\uD328\uD0A4\uC9C0\uBA85 (\uC608: com.example.app): ");
|
|
794
|
+
if (!reviewId) reviewId = await promptUser2("\uB9AC\uBDF0 ID: ");
|
|
795
|
+
}
|
|
796
|
+
if (!reviewId || !packageName) {
|
|
797
|
+
process.stdout.write(kleur4.yellow("\uC815\uBCF4 \uBD80\uC871. \uCDE8\uC18C.\n"));
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
process.stdout.write("Play Store\uC5D0 \uAC8C\uC2DC \uC911...\n");
|
|
801
|
+
const r = await mcpCall(cfg.endpoint, cfg.token, "playstore_reply_review", {
|
|
802
|
+
package_name: packageName,
|
|
803
|
+
review_id: reviewId,
|
|
804
|
+
reply_text: reply
|
|
805
|
+
});
|
|
806
|
+
process.stdout.write(r.isError ? kleur4.red(`\uAC8C\uC2DC \uC2E4\uD328: ${r.text}
|
|
807
|
+
`) : kleur4.green("\u2713 \uB2F5\uBCC0 \uAC8C\uC2DC \uC644\uB8CC\n"));
|
|
808
|
+
}
|
|
809
|
+
|
|
662
810
|
// src/index.ts
|
|
663
811
|
var DEFAULT_WEB_BASE = process.env.MIMI_SEED_WEB_BASE ?? "https://mimi-seed.pryzm.gg";
|
|
664
812
|
var DEFAULT_MCP_ENDPOINT = `${DEFAULT_WEB_BASE}/api/mcp`;
|
|
@@ -667,36 +815,36 @@ function log(msg) {
|
|
|
667
815
|
}
|
|
668
816
|
async function cmdInit() {
|
|
669
817
|
const cwd = process.cwd();
|
|
670
|
-
log(
|
|
671
|
-
log(
|
|
818
|
+
log(kleur5.bold("Mimi Seed CLI \u2014 init"));
|
|
819
|
+
log(kleur5.dim(cwd));
|
|
672
820
|
log("");
|
|
673
821
|
if (!await hasAnyProjectSignal(cwd)) {
|
|
674
|
-
log(
|
|
822
|
+
log(kleur5.yellow("\u26A0 package.json / app.json / android / ios \uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uADF8\uB798\uB3C4 \uC9C4\uD589\uD569\uB2C8\uB2E4."));
|
|
675
823
|
}
|
|
676
824
|
log("\u{1F50D} \uC571 \uAC10\uC9C0 \uC911...");
|
|
677
825
|
const hints = await detectHints(cwd);
|
|
678
826
|
if (hints.length === 0) {
|
|
679
|
-
log(
|
|
827
|
+
log(kleur5.yellow("\uAC10\uC9C0\uB41C \uC571 \uC5C6\uC74C. \uC6F9\uC5D0\uC11C \uC218\uB3D9 \uB4F1\uB85D \uAC00\uB2A5."));
|
|
680
828
|
} else {
|
|
681
829
|
for (const h of hints) {
|
|
682
830
|
const tag = [h.packageName && `android:${h.packageName}`, h.bundleId && `ios:${h.bundleId}`].filter(Boolean).join(" ");
|
|
683
|
-
log(` \u2022 ${h.name ?? "(\uC774\uB984 \uBBF8\uC0C1)"} ${
|
|
831
|
+
log(` \u2022 ${h.name ?? "(\uC774\uB984 \uBBF8\uC0C1)"} ${kleur5.dim(tag)}`);
|
|
684
832
|
}
|
|
685
833
|
}
|
|
686
834
|
log("");
|
|
687
835
|
if (process.env.MIMI_SEED_TOKEN) {
|
|
688
836
|
const cfg2 = await getEffectiveConfig();
|
|
689
837
|
if (cfg2 && hints.length > 0) {
|
|
690
|
-
log(
|
|
838
|
+
log(kleur5.dim("CI \uBAA8\uB4DC: MIMI_SEED_TOKEN \uC0AC\uC6A9"));
|
|
691
839
|
const payload = hints.map((h) => ({ name: h.name, packageName: h.packageName, bundleId: h.bundleId }));
|
|
692
840
|
const result = await mcpCall(cfg2.endpoint, cfg2.token, "sync_apps", { hints: payload });
|
|
693
841
|
if (result.isError) {
|
|
694
|
-
log(
|
|
842
|
+
log(kleur5.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
|
|
695
843
|
} else {
|
|
696
844
|
for (const line of result.text.split("\n")) log(" " + line);
|
|
697
845
|
}
|
|
698
846
|
}
|
|
699
|
-
log(
|
|
847
|
+
log(kleur5.bold("\u2713 \uC644\uB8CC (CI \uBAA8\uB4DC)"));
|
|
700
848
|
return;
|
|
701
849
|
}
|
|
702
850
|
log("\u{1F510} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778 \uB300\uAE30...");
|
|
@@ -705,16 +853,16 @@ async function cmdInit() {
|
|
|
705
853
|
const { port, promise } = await awaitHandshake(5 * 60 * 1e3);
|
|
706
854
|
const callback = `http://127.0.0.1:${port}/cb`;
|
|
707
855
|
const connectUrl = `${DEFAULT_WEB_BASE}/cli/connect?callback=${encodeURIComponent(callback)}&name=${encodeURIComponent(name)}`;
|
|
708
|
-
log(
|
|
856
|
+
log(kleur5.dim(` ${connectUrl}`));
|
|
709
857
|
await open(connectUrl);
|
|
710
858
|
let handshake;
|
|
711
859
|
try {
|
|
712
860
|
handshake = await promise;
|
|
713
861
|
} catch (e) {
|
|
714
|
-
log(
|
|
862
|
+
log(kleur5.red("\uC5F0\uACB0 \uC2E4\uD328: " + e.message));
|
|
715
863
|
process.exit(1);
|
|
716
864
|
}
|
|
717
|
-
log(
|
|
865
|
+
log(kleur5.green("\u2713 \uD1A0\uD070 \uC218\uC2E0"));
|
|
718
866
|
const cfg = {
|
|
719
867
|
token: handshake.token,
|
|
720
868
|
prefix: handshake.prefix,
|
|
@@ -723,30 +871,30 @@ async function cmdInit() {
|
|
|
723
871
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
724
872
|
};
|
|
725
873
|
await writeConfig(cfg);
|
|
726
|
-
log(
|
|
874
|
+
log(kleur5.dim(` \uC800\uC7A5\uB428: ${CONFIG_LOCATION}`));
|
|
727
875
|
log("");
|
|
728
876
|
if (hints.length > 0) {
|
|
729
877
|
log("\u{1F504} \uC571 \uB4F1\uB85D \uC911...");
|
|
730
878
|
const payload = hints.map((h) => ({ name: h.name, packageName: h.packageName, bundleId: h.bundleId }));
|
|
731
879
|
const result = await mcpCall(cfg.endpoint, cfg.token, "sync_apps", { hints: payload });
|
|
732
880
|
if (result.isError) {
|
|
733
|
-
log(
|
|
881
|
+
log(kleur5.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
|
|
734
882
|
} else {
|
|
735
883
|
for (const line of result.text.split("\n")) log(" " + line);
|
|
736
884
|
}
|
|
737
885
|
log("");
|
|
738
886
|
}
|
|
739
|
-
log(
|
|
887
|
+
log(kleur5.bold("\u2713 \uC900\uBE44 \uC644\uB8CC."));
|
|
740
888
|
log("");
|
|
741
889
|
log("Claude Code\uC5D0\uC11C \uC774\uB807\uAC8C \uBB3C\uC5B4\uBCF4\uC138\uC694:");
|
|
742
|
-
log(
|
|
743
|
-
log(
|
|
744
|
-
log(
|
|
890
|
+
log(kleur5.cyan(' "\uB0B4 \uC571 \uCD9C\uC2DC \uC900\uBE44\uB410\uC5B4?"'));
|
|
891
|
+
log(kleur5.cyan(' "\uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC368\uC918"'));
|
|
892
|
+
log(kleur5.cyan(' "\uB4F1\uB85D\uB41C \uC571 \uBAA9\uB85D \uBCF4\uC5EC\uC918"'));
|
|
745
893
|
log("");
|
|
746
|
-
log(`\uB300\uC2DC\uBCF4\uB4DC: ${
|
|
894
|
+
log(`\uB300\uC2DC\uBCF4\uB4DC: ${kleur5.underline(DEFAULT_WEB_BASE + "/apps")}`);
|
|
747
895
|
log("");
|
|
748
896
|
log(
|
|
749
|
-
|
|
897
|
+
kleur5.dim(
|
|
750
898
|
`Claude Code MCP \uB4F1\uB85D:
|
|
751
899
|
claude mcp add --transport http mimi-seed ${DEFAULT_MCP_ENDPOINT} \\
|
|
752
900
|
--header "Authorization: Bearer ${cfg.prefix}..."`
|
|
@@ -756,38 +904,39 @@ async function cmdInit() {
|
|
|
756
904
|
async function cmdStatus() {
|
|
757
905
|
const cfg = await getEffectiveConfig();
|
|
758
906
|
if (!cfg) {
|
|
759
|
-
log(
|
|
907
|
+
log(kleur5.yellow("\uC5F0\uACB0\uB41C Mimi Seed \uACC4\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uC2E4\uD589."));
|
|
760
908
|
process.exit(1);
|
|
761
909
|
}
|
|
762
|
-
log(
|
|
910
|
+
log(kleur5.bold("Mimi Seed \uC5F0\uACB0 \uC0C1\uD0DC"));
|
|
763
911
|
log(` \uD1A0\uD070: ${cfg.prefix}\u2026 (${cfg.createdAt.slice(0, 10)})`);
|
|
764
912
|
log(` \uC5D4\uB4DC\uD3EC\uC778\uD2B8: ${cfg.endpoint}`);
|
|
765
913
|
log("");
|
|
766
914
|
log("\u{1F4CB} \uC571 \uBAA9\uB85D:");
|
|
767
915
|
const r = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
|
|
768
916
|
if (r.isError) {
|
|
769
|
-
log(
|
|
917
|
+
log(kleur5.red("\uC870\uD68C \uC2E4\uD328: " + r.text));
|
|
770
918
|
process.exit(1);
|
|
771
919
|
}
|
|
772
920
|
for (const line of r.text.split("\n")) log(" " + line);
|
|
773
921
|
}
|
|
774
922
|
async function cmdLogout() {
|
|
775
923
|
await deleteConfig();
|
|
776
|
-
log(
|
|
777
|
-
log(
|
|
924
|
+
log(kleur5.green("\u2713 \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C \uC644\uB8CC."));
|
|
925
|
+
log(kleur5.dim("\uC6F9\uC5D0\uC11C \uD1A0\uD070 \uD574\uC9C0: /workspace/api-tokens"));
|
|
778
926
|
}
|
|
779
927
|
function printHelp() {
|
|
780
|
-
log(`${
|
|
928
|
+
log(`${kleur5.bold("mimi-seed")} \u2014 Claude Code\uC5D0\uC11C \uC571 \uCD9C\uC2DC \uC6B4\uC601
|
|
781
929
|
|
|
782
|
-
${
|
|
783
|
-
${
|
|
784
|
-
${
|
|
785
|
-
${
|
|
786
|
-
${
|
|
787
|
-
${
|
|
788
|
-
${
|
|
930
|
+
${kleur5.bold("\uBA85\uB839\uC5B4:")}
|
|
931
|
+
${kleur5.cyan("mimi-seed init")} \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
|
|
932
|
+
${kleur5.cyan("mimi-seed status")} \uC5F0\uACB0 \uC0C1\uD0DC + \uB4F1\uB85D \uC571 \uBAA9\uB85D
|
|
933
|
+
${kleur5.cyan("mimi-seed doctor")} \uD658\uACBD \uC9C4\uB2E8 (\uD1A0\uD070\xB7Git\xB7\uD504\uB85C\uC81D\uD2B8\xB7CI \uCCB4\uD06C)
|
|
934
|
+
${kleur5.cyan("mimi-seed check")} \uCD9C\uC2DC \uC804 Readiness \uC810\uAC80
|
|
935
|
+
${kleur5.cyan("mimi-seed notes")} \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 (git log \u2192 AI \u2192 \uB9C8\uCF13 \uC801\uC6A9)
|
|
936
|
+
${kleur5.cyan("mimi-seed review")} \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
|
|
937
|
+
${kleur5.cyan("mimi-seed logout")} \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C
|
|
789
938
|
|
|
790
|
-
${
|
|
939
|
+
${kleur5.bold("mimi-seed notes \uC635\uC158:")}
|
|
791
940
|
--from <ref> \uC2DC\uC791 \uCEE4\uBC0B/\uD0DC\uADF8 (\uAE30\uBCF8: \uCD5C\uC2E0 \uD0DC\uADF8)
|
|
792
941
|
--to <ref> \uB05D \uCEE4\uBC0B (\uAE30\uBCF8: HEAD)
|
|
793
942
|
--locale ko,en-US \uB300\uC0C1 \uB85C\uCF00\uC77C (\uC27C\uD45C \uAD6C\uBD84)
|
|
@@ -795,11 +944,22 @@ ${kleur4.bold("mimi-seed notes \uC635\uC158:")}
|
|
|
795
944
|
--no-interactive CI \uBAA8\uB4DC (\uD504\uB86C\uD504\uD2B8 \uC5C6\uC74C)
|
|
796
945
|
--limit <n> \uCD5C\uB300 \uCEE4\uBC0B \uC218 (\uAE30\uBCF8: 30)
|
|
797
946
|
|
|
798
|
-
${
|
|
947
|
+
${kleur5.bold("mimi-seed check \uC635\uC158:")}
|
|
799
948
|
--app <id> \uC571 ID \uC9C0\uC815
|
|
800
949
|
--fail-on-blocker \uBE14\uB85C\uCEE4 \uC788\uC73C\uBA74 exit 1 (CI\uC6A9)
|
|
801
950
|
|
|
802
|
-
${
|
|
951
|
+
${kleur5.bold("mimi-seed review \uC635\uC158:")}
|
|
952
|
+
--text <\uB0B4\uC6A9> \uB9AC\uBDF0 \uC6D0\uBB38 (\uBBF8\uC785\uB825 \uC2DC \uB300\uD654\uD615 \uD504\uB86C\uD504\uD2B8)
|
|
953
|
+
--rating <1-5> \uBCC4\uC810
|
|
954
|
+
--tone <tone> friendly / professional / empathetic / brief (\uAE30\uBCF8: friendly)
|
|
955
|
+
--language <\uCF54\uB4DC> \uB2F5\uBCC0 \uC5B8\uC5B4 (\uAE30\uBCF8: ko)
|
|
956
|
+
--app-name <\uC774\uB984> \uC571 \uC774\uB984 (\uB9E5\uB77D\uC6A9)
|
|
957
|
+
--apply \uB2F5\uBCC0\uC744 Play Store\uC5D0 \uAC8C\uC2DC
|
|
958
|
+
--review-id <id> \uB9AC\uBDF0 ID (--apply \uC2DC \uD544\uC694)
|
|
959
|
+
--package-name <p> \uD328\uD0A4\uC9C0\uBA85 (--apply \uC2DC \uD544\uC694)
|
|
960
|
+
--no-interactive CI \uBAA8\uB4DC
|
|
961
|
+
|
|
962
|
+
${kleur5.bold("\uD658\uACBD\uBCC0\uC218:")}
|
|
803
963
|
MIMI_SEED_TOKEN PAT \uD1A0\uD070 (CI/CD \uBB34\uC778\uC99D \uBAA8\uB4DC)
|
|
804
964
|
MIMI_SEED_WEB_BASE \uC11C\uBC84 \uC8FC\uC18C (\uAE30\uBCF8: https://mimi-seed.pryzm.gg)
|
|
805
965
|
ANTHROPIC_API_KEY AI \uB178\uD2B8 \uC0DD\uC131 \uD65C\uC131\uD654 (\uC120\uD0DD)
|
|
@@ -825,6 +985,9 @@ async function main() {
|
|
|
825
985
|
case "notes":
|
|
826
986
|
await cmdNotes(restArgs);
|
|
827
987
|
break;
|
|
988
|
+
case "review":
|
|
989
|
+
await cmdReview(restArgs);
|
|
990
|
+
break;
|
|
828
991
|
case "logout":
|
|
829
992
|
await cmdLogout();
|
|
830
993
|
break;
|
|
@@ -834,12 +997,12 @@ async function main() {
|
|
|
834
997
|
printHelp();
|
|
835
998
|
break;
|
|
836
999
|
default:
|
|
837
|
-
log(
|
|
1000
|
+
log(kleur5.red(`\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839: ${cmd}`));
|
|
838
1001
|
printHelp();
|
|
839
1002
|
process.exit(1);
|
|
840
1003
|
}
|
|
841
1004
|
} catch (e) {
|
|
842
|
-
log(
|
|
1005
|
+
log(kleur5.red(`\uC624\uB958: ${e.message}`));
|
|
843
1006
|
if (process.env.DEBUG) log(e.stack ?? "");
|
|
844
1007
|
process.exit(1);
|
|
845
1008
|
}
|
package/package.json
CHANGED