mimi-seed 0.2.7 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/LICENSE +75 -75
  2. package/README.md +309 -230
  3. package/dist/index.js +227 -126
  4. package/package.json +13 -4
package/LICENSE CHANGED
@@ -1,75 +1,75 @@
1
- # PolyForm Noncommercial License 1.0.0
2
-
3
- <https://polyformproject.org/licenses/noncommercial/1.0.0>
4
-
5
- Required Notice: Copyright 2026 Pryzm GG (https://mimi-seed.pryzm.gg)
6
-
7
- ## Acceptance
8
-
9
- In order to get any license under these terms, you must agree to them as both strict obligations and conditions to all your licenses.
10
-
11
- ## Copyright License
12
-
13
- The licensor grants you a copyright license for the software to do everything you might do with the software that would otherwise infringe the licensor's copyright in it for any permitted purpose. However, you may only distribute the software according to [Distribution License](#distribution-license) and make changes or new works based on the software according to [Changes and New Works License](#changes-and-new-works-license).
14
-
15
- ## Distribution License
16
-
17
- The licensor grants you an additional copyright license to distribute copies of the software. Your license to distribute covers distributing the software with changes and new works permitted by [Changes and New Works License](#changes-and-new-works-license).
18
-
19
- ## Notices
20
-
21
- You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms or the URL for them above, as well as copies of any plain-text lines beginning with `Required Notice:` that the licensor provided with the software. For example:
22
-
23
- > Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
24
-
25
- ## Changes and New Works License
26
-
27
- The licensor grants you an additional copyright license to make changes and new works based on the software for any permitted purpose.
28
-
29
- ## Patent License
30
-
31
- The licensor grants you a patent license for the software that covers patent claims the licensor can license, or becomes able to license, that you would infringe by using the software.
32
-
33
- ## Noncommercial Purposes
34
-
35
- Any noncommercial purpose is a permitted purpose.
36
-
37
- ## Personal Uses
38
-
39
- Personal use for research, experiment, and testing for the benefit of public knowledge, personal study, private entertainment, hobby projects, amateur pursuits, or religious observance, without any anticipated commercial application, is use for a permitted purpose.
40
-
41
- ## Noncommercial Organizations
42
-
43
- Use by any charitable organization, educational institution, public research organization, public safety or health organization, environmental protection organization, or government institution is use for a permitted purpose regardless of the source of funding or obligations resulting from the funding.
44
-
45
- ## Fair Use
46
-
47
- You may have "fair use" rights for the software under the law. These terms do not limit them.
48
-
49
- ## No Other Rights
50
-
51
- These terms do not allow you to sublicense or transfer any of your licenses to anyone else, or prevent the licensor from granting licenses to anyone else. These terms do not imply any other licenses.
52
-
53
- ## Patent Defense
54
-
55
- If you make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
56
-
57
- ## Violations
58
-
59
- The first time you are notified in writing that you have violated any of these terms, or done anything with the software not covered by your licenses, your licenses can nonetheless continue if you come into full compliance with these terms, and take practical steps to correct past violations, within 32 days of receiving notice. Otherwise, all your licenses end immediately.
60
-
61
- ## No Liability
62
-
63
- ***As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.***
64
-
65
- ## Definitions
66
-
67
- The **licensor** is the individual or entity offering these terms, and the **software** is the software the licensor makes available under these terms.
68
-
69
- **You** refers to the individual or entity agreeing to these terms.
70
-
71
- **Your company** is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. **Control** means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
72
-
73
- **Your licenses** are all the licenses granted to you for the software under these terms.
74
-
75
- **Use** means anything you do with the software requiring one of your licenses.
1
+ # PolyForm Noncommercial License 1.0.0
2
+
3
+ <https://polyformproject.org/licenses/noncommercial/1.0.0>
4
+
5
+ Required Notice: Copyright 2026 Pryzm GG (https://mimi-seed.pryzm.gg)
6
+
7
+ ## Acceptance
8
+
9
+ In order to get any license under these terms, you must agree to them as both strict obligations and conditions to all your licenses.
10
+
11
+ ## Copyright License
12
+
13
+ The licensor grants you a copyright license for the software to do everything you might do with the software that would otherwise infringe the licensor's copyright in it for any permitted purpose. However, you may only distribute the software according to [Distribution License](#distribution-license) and make changes or new works based on the software according to [Changes and New Works License](#changes-and-new-works-license).
14
+
15
+ ## Distribution License
16
+
17
+ The licensor grants you an additional copyright license to distribute copies of the software. Your license to distribute covers distributing the software with changes and new works permitted by [Changes and New Works License](#changes-and-new-works-license).
18
+
19
+ ## Notices
20
+
21
+ You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms or the URL for them above, as well as copies of any plain-text lines beginning with `Required Notice:` that the licensor provided with the software. For example:
22
+
23
+ > Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
24
+
25
+ ## Changes and New Works License
26
+
27
+ The licensor grants you an additional copyright license to make changes and new works based on the software for any permitted purpose.
28
+
29
+ ## Patent License
30
+
31
+ The licensor grants you a patent license for the software that covers patent claims the licensor can license, or becomes able to license, that you would infringe by using the software.
32
+
33
+ ## Noncommercial Purposes
34
+
35
+ Any noncommercial purpose is a permitted purpose.
36
+
37
+ ## Personal Uses
38
+
39
+ Personal use for research, experiment, and testing for the benefit of public knowledge, personal study, private entertainment, hobby projects, amateur pursuits, or religious observance, without any anticipated commercial application, is use for a permitted purpose.
40
+
41
+ ## Noncommercial Organizations
42
+
43
+ Use by any charitable organization, educational institution, public research organization, public safety or health organization, environmental protection organization, or government institution is use for a permitted purpose regardless of the source of funding or obligations resulting from the funding.
44
+
45
+ ## Fair Use
46
+
47
+ You may have "fair use" rights for the software under the law. These terms do not limit them.
48
+
49
+ ## No Other Rights
50
+
51
+ These terms do not allow you to sublicense or transfer any of your licenses to anyone else, or prevent the licensor from granting licenses to anyone else. These terms do not imply any other licenses.
52
+
53
+ ## Patent Defense
54
+
55
+ If you make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
56
+
57
+ ## Violations
58
+
59
+ The first time you are notified in writing that you have violated any of these terms, or done anything with the software not covered by your licenses, your licenses can nonetheless continue if you come into full compliance with these terms, and take practical steps to correct past violations, within 32 days of receiving notice. Otherwise, all your licenses end immediately.
60
+
61
+ ## No Liability
62
+
63
+ ***As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.***
64
+
65
+ ## Definitions
66
+
67
+ The **licensor** is the individual or entity offering these terms, and the **software** is the software the licensor makes available under these terms.
68
+
69
+ **You** refers to the individual or entity agreeing to these terms.
70
+
71
+ **Your company** is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. **Control** means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
72
+
73
+ **Your licenses** are all the licenses granted to you for the software under these terms.
74
+
75
+ **Use** means anything you do with the software requiring one of your licenses.
package/README.md CHANGED
@@ -1,234 +1,313 @@
1
- # mimi-seed
2
-
3
- **Mimi Seed CLI** — Claude Code에서 앱 출시를 자동화하는 커맨드라인 도구.
4
-
5
- git log에서 릴리즈 노트를 생성하고, 출시 전 위험 요소를 자동 점검하며, Play Store / App Store에 바로 적용합니다.
6
-
7
- ## 빠른 시작
8
-
9
- ```bash
10
- npx mimi-seed init
11
- ```
12
-
13
- 현재 디렉토리에서 Android/iOS 앱을 자동 감지해 Mimi Seed 워크스페이스에 등록하고, PAT를 `~/.mimi-seed/config.json`에 저장합니다.
14
-
15
- ## 명령어
16
-
17
- | 명령어 | 설명 |
18
- |--------|------|
19
- | `mimi-seed init` | 프로젝트를 Mimi Seed에 연결 (PAT 발급 + 앱 자동 등록) |
20
- | `mimi-seed status` | 연결 상태 + 등록 앱 목록 |
21
- | `mimi-seed doctor` | 환경 진단 (토큰·Git·앱·CI 번에 체크) |
22
- | `mimi-seed check` | 출시 Readiness 점검 (점수 + 블로커) |
23
- | `mimi-seed notes` | AI 릴리즈 노트 생성 (git log → 3 톤 → 다국어 → 적용) |
24
- | `mimi-seed review` | AI 리뷰 답변 초안 생성 Play Store 게시 |
25
- | `mimi-seed logout` | 로컬 설정 삭제 |
26
-
27
- ---
28
-
29
- ## mimi-seed notes
30
-
31
- git 커밋 내역으로 앱 스토어 릴리즈 노트를 자동 생성합니다.
32
-
33
- ```bash
34
- # 기본: 최신 태그 이후 커밋 → 간결/상세/마케팅 3 톤 생성
35
- mimi-seed notes
36
-
37
- # 태그 범위 지정
38
- mimi-seed notes --from v1.2.0 --to HEAD
39
-
40
- # 다국어 동시 생성 (AI 필요)
41
- mimi-seed notes --locale ko,en-US,ja
42
-
43
- # 생성 Play Store 바로 적용
44
- mimi-seed notes --apply
45
-
46
- # CI 모드 (프롬프트 없음)
47
- mimi-seed notes --no-interactive --apply
48
- ```
49
-
50
- **AI 생성 활성화** (`ANTHROPIC_API_KEY` 설정 시):
51
- ```bash
52
- export ANTHROPIC_API_KEY=sk-ant-...
53
- mimi-seed notes --locale ko,en-US,ja
54
- ```
55
-
56
- 설정하지 않으면 커밋 메시지 자동 포맷팅으로 동작합니다.
57
-
58
- ### 옵션
59
-
60
- | 옵션 | 기본값 | 설명 |
61
- |------|--------|------|
62
- | `--from <ref>` | 최신 태그 | 시작 커밋 또는 태그 |
63
- | `--to <ref>` | `HEAD` | 끝 커밋 |
64
- | `--locale <list>` | `ko,en-US` | 다국어 로케일 (쉼표 구분) |
65
- | `--apply` | false | 생성 후 스토어에 바로 적용 |
66
- | `--no-interactive` | false | CI 모드 (프롬프트 없음) |
67
- | `--limit <n>` | 30 | 최대 커밋 |
68
-
69
- ---
70
-
71
- ## mimi-seed check
72
-
73
- 출시 전 Readiness 점수와 블로커를 확인합니다.
74
-
75
- ```bash
76
- mimi-seed check
77
-
78
- # CI에서 블로커 있으면 exit 1
79
- mimi-seed check --fail-on-blocker
80
- ```
81
-
82
- ### 옵션
83
-
84
- | 옵션 | 설명 |
85
- |------|------|
86
- | `--app <id>` | 앱 ID 지정 (기본: 첫 번째 등록 앱) |
87
- | `--fail-on-blocker` | 블로커 존재 시 exit 1 (CI/CD용) |
88
-
89
- ---
90
-
91
- ## mimi-seed doctor
92
-
93
- 로컬 환경 전체를 진단합니다.
1
+ # mimi-seed
2
+
3
+ **Mimi Seed CLI** — Claude Code와 Codex에서 앱 출시를 자동화하는 커맨드라인 도구.
4
+
5
+ git log에서 릴리즈 노트를 생성하고, 출시 전 위험 요소를 자동 점검하며, Play Store / App Store에 바로 적용합니다.
6
+
7
+ ## 빠른 시작
8
+
9
+ ```bash
10
+ npx mimi-seed init
11
+ ```
12
+
13
+ 현재 디렉토리에서 Android/iOS 앱을 자동 감지해 Mimi Seed 워크스페이스에 등록하고, PAT를 `~/.mimi-seed/config.json`에 저장합니다. 프로젝트 컨텍스트는 Claude Code용 `.claude/mimi-seed.md`와 Codex용 `AGENTS.md`에 함께 기록됩니다.
14
+
15
+ ## 명령어
16
+
17
+ | 명령어 | 설명 |
18
+ |--------|------|
19
+ | `mimi-seed init` | 프로젝트를 Mimi Seed에 연결 (PAT 발급 + 앱 자동 등록 + `.claude/mimi-seed.md` / `AGENTS.md` 주입) |
20
+ | `mimi-seed status` | 연결 상태 + 등록 앱 목록 |
21
+ | `mimi-seed auth` | Google OAuth 인증 (Firebase / AdMob / Play). `login` / `status` / `refresh` / `logout` 서브명령 |
22
+ | `mimi-seed doctor` | 환경 진단 (토큰·Git·앱·CI 번에 체크) |
23
+ | `mimi-seed check` | 출시 Readiness 점검 (점수 + 블로커) |
24
+ | `mimi-seed notes` | AI 릴리즈 노트 생성 (git log 3 톤 → 다국어 → 적용) |
25
+ | `mimi-seed review` | AI 리뷰 답변 초안 생성 및 Play Store 게시 |
26
+ | `mimi-seed deploy` | 앱 자동 배포 파이프라인 (CI 빌드 → 릴리즈 노트 → 스토어 적용) |
27
+ | `mimi-seed restart` | MCP 서버 프로세스 재시작 |
28
+ | `mimi-seed logout` | 로컬 설정 삭제 |
29
+
30
+ > `init`은 `.claude/mimi-seed.md`와 `AGENTS.md`를 함께 생성해 Claude Code와 Codex 세션마다 에이전트 컨텍스트(출시 워크플로우 + 슬래시 커맨드)를 자동 활성화합니다.
31
+
32
+ ---
33
+
34
+ ## mimi-seed notes
35
+
36
+ git 커밋 내역으로 앱 스토어 릴리즈 노트를 자동 생성합니다.
37
+
38
+ ```bash
39
+ # 기본: 최신 태그 이후 커밋 → 간결/상세/마케팅 3 톤 생성
40
+ mimi-seed notes
41
+
42
+ # 태그 범위 지정
43
+ mimi-seed notes --from v1.2.0 --to HEAD
44
+
45
+ # 다국어 동시 생성 (AI 필요)
46
+ mimi-seed notes --locale ko,en-US,ja
47
+
48
+ # 생성 후 Play Store 바로 적용
49
+ mimi-seed notes --apply
50
+
51
+ # CI 모드 (프롬프트 없음)
52
+ mimi-seed notes --no-interactive --apply
53
+ ```
54
+
55
+ **AI 생성 활성화** (`ANTHROPIC_API_KEY` 설정 시):
56
+ ```bash
57
+ export ANTHROPIC_API_KEY=sk-ant-...
58
+ mimi-seed notes --locale ko,en-US,ja
59
+ ```
60
+
61
+ 설정하지 않으면 커밋 메시지 자동 포맷팅으로 동작합니다.
62
+
63
+ ### 옵션
64
+
65
+ | 옵션 | 기본값 | 설명 |
66
+ |------|--------|------|
67
+ | `--from <ref>` | 최신 태그 | 시작 커밋 또는 태그 |
68
+ | `--to <ref>` | `HEAD` | 끝 커밋 |
69
+ | `--locale <list>` | `ko,en-US` | 다국어 로케일 (쉼표 구분) |
70
+ | `--apply` | false | 생성 후 스토어에 바로 적용 |
71
+ | `--no-interactive` | false | CI 모드 (프롬프트 없음) |
72
+ | `--limit <n>` | 30 | 최대 커밋 수 |
73
+
74
+ ---
75
+
76
+ ## mimi-seed check
77
+
78
+ 출시 Readiness 점수와 블로커를 확인합니다.
79
+
80
+ ```bash
81
+ mimi-seed check
82
+
83
+ # CI에서 블로커 있으면 exit 1
84
+ mimi-seed check --fail-on-blocker
85
+ ```
86
+
87
+ ### 옵션
88
+
89
+ | 옵션 | 설명 |
90
+ |------|------|
91
+ | `--app <id>` | 앱 ID 지정 (기본: 첫 번째 등록 앱) |
92
+ | `--fail-on-blocker` | 블로커 존재 시 exit 1 (CI/CD용) |
93
+
94
+ ---
95
+
96
+ ## mimi-seed doctor
97
+
98
+ 로컬 환경 전체를 진단합니다.
99
+
100
+ ```bash
101
+ mimi-seed doctor
102
+ # ✓ 토큰 저장됨 prs_abc1... (2026-04-25)
103
+ # ✓ 엔드포인트 https://mimi-seed.pryzm.gg/api/mcp
104
+ # ✓ Mimi Seed 서버 연결됨 앱 2개
105
+ #
106
+ # ── 로컬 환경 ──
107
+ # ✓ Node.js v22.17.0
108
+ # ✓ Git 저장소 최신 태그: v1.3.0
109
+ # ⚠ ANTHROPIC_API_KEY 없음 설정 시 AI 릴리즈 노트/리뷰 답변 생성 가능
110
+ #
111
+ # ── 앱 감지 ──
112
+ # ✓ MyApp android:com.example.myapp ios:com.example.myapp
113
+ ```
114
+
115
+ ---
116
+
117
+ ## mimi-seed review
118
+
119
+ 스토어 리뷰에 대한 AI 답변 초안을 생성합니다. `ANTHROPIC_API_KEY` 필요.
120
+
121
+ ```bash
122
+ # 대화형 — 리뷰 입력 후 답변 생성
123
+ mimi-seed review
124
+
125
+ # 리뷰 텍스트 직접 지정
126
+ mimi-seed review --text "앱이 자꾸 튕겨요" --rating 2
127
+
128
+ # 톤·언어 지정
129
+ mimi-seed review --text "Great app!" --rating 5 --tone professional --language en
130
+
131
+ # 생성 후 Play Store 바로 게시
132
+ mimi-seed review --text "버그 있어요" --rating 2 \
133
+ --apply --package-name com.example.app --review-id <reviewId>
134
+ ```
135
+
136
+ ### 옵션
137
+
138
+ | 옵션 | 기본값 | 설명 |
139
+ |------|--------|------|
140
+ | `--text <내용>` | (프롬프트) | 리뷰 원문 |
141
+ | `--rating <1-5>` | — | 별점 (맥락용) |
142
+ | `--tone <tone>` | `friendly` | `friendly` / `professional` / `empathetic` / `brief` |
143
+ | `--language <코드>` | `ko` | 답변 언어 (`ko`, `en`, `ja` 등) |
144
+ | `--app-name <이름>` | — | 앱 이름 (프롬프트 맥락용) |
145
+ | `--apply` | false | 답변을 Play Store에 바로 게시 |
146
+ | `--review-id <id>` | — | 게시 대상 리뷰 ID (`--apply` 시 필요) |
147
+ | `--package-name <p>` | — | Android 패키지명 (`--apply` 시 필요) |
148
+ | `--no-interactive` | false | CI 모드 (프롬프트 없음) |
149
+
150
+ > AI 생성 답변은 초안입니다. 게시 전 반드시 검토하세요.
151
+
152
+ ---
153
+
154
+ ## mimi-seed auth
155
+
156
+ Firebase · AdMob · Play Store 도구에 필요한 Google OAuth 인증을 처리합니다. 내부적으로 `@yoonion/mimi-seed-mcp`의 `mimi-seed-auth` CLI를 호출합니다.
157
+
158
+ ```bash
159
+ mimi-seed auth login # 브라우저로 로그인 (이미 있으면 자동 refresh 시도)
160
+ mimi-seed auth status # 현재 토큰 상태
161
+ mimi-seed auth refresh # refresh_token으로 갱신만 시도 (브라우저 X)
162
+ mimi-seed auth logout # 토큰 삭제
163
+ ```
164
+
165
+ ### login 옵션
166
+
167
+ | 옵션 | 설명 |
168
+ |------|------|
169
+ | `--no-browser` | URL 자동 오픈 안 함 (직접 복붙) |
170
+ | `--timeout <초>` | 콜백 대기 시간 (기본: 600) |
171
+ | `--force` | 기존 토큰 무시하고 강제 재로그인 |
172
+
173
+ 토큰은 `~/.mimi-seed/tokens.json`에 저장되고 자동 갱신됩니다.
174
+
175
+ ---
176
+
177
+ ## mimi-seed deploy
178
+
179
+ CI 빌드부터 스토어 적용까지 출시 파이프라인 전체를 자동화합니다. 단계: **init → verify(블로커 점검) → notes(릴리즈 노트) → apply(스토어 적용) → promote**.
180
+
181
+ ```bash
182
+ # Android 기본 배포 (CI 빌드 자동 감지 → Play Store)
183
+ mimi-seed deploy
184
+
185
+ # iOS 배포
186
+ mimi-seed deploy --platform ios
187
+
188
+ # 이미 빌드된 버전으로 노트만 적용 (CI 빌드 건너뜀)
189
+ mimi-seed deploy --skip-build --version-code 142
190
+
191
+ # 실제 배포 없이 파이프라인만 테스트
192
+ mimi-seed deploy --dry-run
193
+ ```
194
+
195
+ 지원 CI: **Jenkins · GitHub Actions · GitLab CI** (`--ci`로 강제 선택, 기본은 자동 감지).
196
+
197
+ ### CI 설정 등록 (최초 1회)
198
+
199
+ ```bash
200
+ mimi-seed deploy setup-jenkins # Jenkins URL·토큰·잡 이름 대화형 등록
201
+ mimi-seed deploy setup-github # GitHub Actions repo·workflow 등록
202
+ mimi-seed deploy setup-gitlab # GitLab project·trigger 등록
203
+ ```
204
+
205
+ ### 옵션
206
+
207
+ | 옵션 | 기본값 | 설명 |
208
+ |------|--------|------|
209
+ | `--platform android\|ios` | `android` | 배포 플랫폼 |
210
+ | `--app <id>` | 첫 등록 앱 | 앱 ID 지정 |
211
+ | `--version-code <n>` | — | 빌드 번호 직접 지정 (`--skip-build`와 함께) |
212
+ | `--from <ref>` / `--to <ref>` | 최신 태그 / `HEAD` | 릴리즈 노트용 커밋 범위 |
213
+ | `--language <코드>` | `ko-KR` | 릴리즈 노트 언어 |
214
+ | `--dry-run` | false | 실제 배포 없이 파이프라인 테스트 |
215
+ | `--skip-build` | false | CI 빌드 건너뜀 (`--version-code` 필수) |
216
+ | `--ci jenkins\|github\|gitlab` | auto | CI 강제 선택 |
217
+ | `--workflow <file>` | — | GitHub workflow 파일 (예: `deploy.yml`) |
218
+ | `--ref <branch\|tag>` | `main` | GitHub/GitLab 브랜치/태그 |
219
+
220
+ ---
221
+
222
+ ## CI/CD 사용
223
+
224
+ ### 방법 1 — 재사용 가능 워크플로우 (권장)
225
+
226
+ `.github/workflows/release.yml` 파일 하나만 추가하면 끝납니다.
227
+
228
+ ```yaml
229
+ name: App Release
230
+
231
+ on:
232
+ push:
233
+ tags: ['v*'] # v1.2.3 태그 push 시 자동 실행
234
+ workflow_dispatch: # 수동 실행 버튼
235
+
236
+ jobs:
237
+ release:
238
+ uses: jeonghwanko/mimi-seed/.github/workflows/mimi-seed-release.yml@master
239
+ with:
240
+ locales: 'ko,en-US' # 다국어 릴리즈 노트
241
+ apply-notes: true # 스토어에 자동 적용
242
+ fail-on-blocker: true # 블로커 있으면 실패
243
+ secrets:
244
+ MIMI_SEED_TOKEN: ${{ secrets.MIMI_SEED_TOKEN }}
245
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} # 선택: AI 생성
246
+ ```
247
+
248
+ ### 방법 2 — 기존 워크플로우에 스텝 추가
249
+
250
+ ```yaml
251
+ - name: 릴리즈 노트 생성 및 적용
252
+ env:
253
+ MIMI_SEED_TOKEN: ${{ secrets.MIMI_SEED_TOKEN }}
254
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
255
+ run: |
256
+ npx mimi-seed notes --apply --no-interactive --locale ko,en-US
257
+ npx mimi-seed check --fail-on-blocker
258
+ ```
259
+
260
+ `MIMI_SEED_TOKEN`은 [대시보드 → API 토큰](https://mimi-seed.pryzm.gg/workspace/api-tokens)에서 발급하세요.
261
+
262
+ ---
263
+
264
+ ## Claude Code / Codex MCP 등록
265
+
266
+ `init` 후 1회 실행:
267
+
268
+ ```bash
269
+ claude mcp add --transport http mimi-seed https://mimi-seed.pryzm.gg/api/mcp \
270
+ --header "Authorization: Bearer <PAT>"
271
+ ```
272
+
273
+ Codex는 자동 쓰기 명령을 사용할 수 있습니다.
94
274
 
95
275
  ```bash
96
- mimi-seed doctor
97
- # ✓ 토큰 저장됨 prs_abc1... (2026-04-25)
98
- # ✓ 엔드포인트 https://mimi-seed.pryzm.gg/api/mcp
99
- # ✓ Mimi Seed 서버 연결됨 앱 2개
100
- #
101
- # ── 로컬 환경 ──
102
- # ✓ Node.js v22.17.0
103
- # ✓ Git 저장소 최신 태그: v1.3.0
104
- # ⚠ ANTHROPIC_API_KEY 없음 설정 시 AI 릴리즈 노트/리뷰 답변 생성 가능
105
- #
106
- # ── 앱 감지 ──
107
- # ✓ MyApp android:com.example.myapp ios:com.example.myapp
276
+ mimi-seed mcp codex --write
108
277
  ```
109
278
 
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
-
149
- ## CI/CD 사용
150
-
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/mimi-seed/.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 — 기존 워크플로우에 스텝 추가
176
-
177
- ```yaml
178
- - name: 릴리즈 노트 생성 및 적용
179
- env:
180
- MIMI_SEED_TOKEN: ${{ secrets.MIMI_SEED_TOKEN }}
181
- ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
182
- run: |
183
- npx mimi-seed notes --apply --no-interactive --locale ko,en-US
184
- npx mimi-seed check --fail-on-blocker
185
- ```
186
-
187
- `MIMI_SEED_TOKEN`은 [대시보드 → API 토큰](https://mimi-seed.pryzm.gg/workspace/api-tokens)에서 발급하세요.
188
-
189
- ---
190
-
191
- ## Claude Code MCP 등록
192
-
193
- `init` 후 1회 실행:
194
-
195
- ```bash
196
- claude mcp add --transport http mimi-seed https://mimi-seed.pryzm.gg/api/mcp \
197
- --header "Authorization: Bearer <PAT>"
198
- ```
199
-
200
- 등록 후 Claude Code에서 대화로 제어:
201
-
202
- ```
203
- "내 앱 출시 준비됐어?"
204
- "릴리즈 노트 써줘"
205
- "스크린샷 검수해줘"
206
- ```
207
-
208
- ---
209
-
210
- ## 앱 감지 대상
211
-
212
- - `app.json` / `app.config.json` (Expo, React Native)
213
- - `**/build.gradle(.kts)` — `applicationId`
214
- - `**/Info.plist` — `CFBundleIdentifier`
215
- - `**/project.pbxproj` — `PRODUCT_BUNDLE_IDENTIFIER`
216
- - `package.json` — 앱 이름 보충
217
-
218
- ---
219
-
220
- ## 환경변수
221
-
222
- | 변수 | 설명 |
223
- |------|------|
224
- | `MIMI_SEED_TOKEN` | PAT 토큰 — CI/CD 무인증 모드 |
225
- | `MIMI_SEED_WEB_BASE` | 서버 주소 (기본: `https://mimi-seed.pryzm.gg`) |
226
- | `ANTHROPIC_API_KEY` | AI 릴리즈 노트/리뷰 답변 생성 활성화 (선택) |
227
- | `DEBUG` | `1` 설정 시 오류 스택 트레이스 출력 |
228
-
229
- ---
230
-
231
- ## 관련 패키지
232
-
233
- - [`@yoonion/mimi-seed-mcp`](https://www.npmjs.com/package/@yoonion/mimi-seed-mcp) — Claude Desktop / Cursor용 로컬 MCP 서버 (Firebase · Play Store · App Store · AdMob)
234
- - [mimi-seed.pryzm.gg](https://mimi-seed.pryzm.gg) — 웹 콘솔 (스크린샷 검수, Copy Studio, 팀 워크스페이스)
279
+ 등록 후 Claude Code 또는 Codex에서 대화로 제어:
280
+
281
+ ```
282
+ "내 앱 출시 준비됐어?"
283
+ "릴리즈 노트 써줘"
284
+ "스크린샷 검수해줘"
285
+ ```
286
+
287
+ ---
288
+
289
+ ## 감지 대상
290
+
291
+ - `app.json` / `app.config.json` (Expo, React Native)
292
+ - `**/build.gradle(.kts)` — `applicationId`
293
+ - `**/Info.plist` `CFBundleIdentifier`
294
+ - `**/project.pbxproj` — `PRODUCT_BUNDLE_IDENTIFIER`
295
+ - `package.json` 이름 보충
296
+
297
+ ---
298
+
299
+ ## 환경변수
300
+
301
+ | 변수 | 설명 |
302
+ |------|------|
303
+ | `MIMI_SEED_TOKEN` | PAT 토큰 — CI/CD 무인증 모드 |
304
+ | `MIMI_SEED_WEB_BASE` | 서버 주소 (기본: `https://mimi-seed.pryzm.gg`) |
305
+ | `ANTHROPIC_API_KEY` | AI 릴리즈 노트/리뷰 답변 생성 활성화 (선택) |
306
+ | `DEBUG` | `1` 설정 오류 스택 트레이스 출력 |
307
+
308
+ ---
309
+
310
+ ## 관련 패키지
311
+
312
+ - [`@yoonion/mimi-seed-mcp`](https://www.npmjs.com/package/@yoonion/mimi-seed-mcp) Claude Code / Codex / Cursor용 로컬 MCP 서버 (Firebase · Play Store · App Store · AdMob)
313
+ - [mimi-seed.pryzm.gg/tool](https://mimi-seed.pryzm.gg/tool) — 웹 콘솔 (스크린샷 검수, Copy Studio, 팀 워크스페이스)
package/dist/index.js CHANGED
@@ -4,10 +4,10 @@ import {
4
4
  } from "./chunk-Q5YGYAFK.js";
5
5
 
6
6
  // src/index.ts
7
- import os4 from "os";
8
- import fs5 from "fs";
9
- import path5 from "path";
10
- import kleur8 from "kleur";
7
+ import os5 from "os";
8
+ import fs6 from "fs";
9
+ import path6 from "path";
10
+ import kleur9 from "kleur";
11
11
  import open from "open";
12
12
 
13
13
  // src/detect.ts
@@ -913,32 +913,34 @@ async function ghTriggerWorkflow(cfg, workflow, ref, inputs = {}) {
913
913
  if (!run) return null;
914
914
  return { runId: run.id, url: run.html_url };
915
915
  }
916
- async function ghPollRun(cfg, runId, onTick, timeoutMs = 30 * 60 * 1e3) {
916
+ async function ghPollRun(cfg, runId, onTick, timeoutMs = 30 * 60 * 1e3, intervalMs = 15e3) {
917
917
  const start = Date.now();
918
918
  let consecutiveErrors = 0;
919
919
  while (Date.now() - start < timeoutMs) {
920
- await new Promise((r) => setTimeout(r, 15e3));
920
+ await new Promise((r) => setTimeout(r, intervalMs));
921
+ let res;
921
922
  try {
922
- const res = await fetch(
923
+ res = await fetch(
923
924
  `${ghBase(cfg)}/repos/${cfg.owner}/${cfg.repo}/actions/runs/${runId}`,
924
925
  { headers: ghHeaders(cfg.token) }
925
926
  );
926
- if (!res.ok) {
927
- consecutiveErrors++;
928
- if (consecutiveErrors >= 3) throw new Error("GitHub API \uC5F0\uC18D \uC624\uB958");
929
- continue;
930
- }
931
- consecutiveErrors = 0;
932
- const data = await res.json();
933
- onTick?.(data.status);
934
- if (data.status === "completed") {
935
- if (data.conclusion === "success") return "success";
936
- if (data.conclusion === "cancelled") return "cancelled";
937
- return "failure";
938
- }
939
927
  } catch {
940
928
  consecutiveErrors++;
941
929
  if (consecutiveErrors >= 3) throw new Error("GitHub API \uC5F0\uC18D \uC624\uB958 3\uD68C");
930
+ continue;
931
+ }
932
+ if (!res.ok) {
933
+ consecutiveErrors++;
934
+ if (consecutiveErrors >= 3) throw new Error(`GitHub API \uC5F0\uC18D \uC624\uB958 (HTTP ${res.status})`);
935
+ continue;
936
+ }
937
+ consecutiveErrors = 0;
938
+ const data = await res.json();
939
+ onTick?.(data.status);
940
+ if (data.status === "completed") {
941
+ if (data.conclusion === "success") return "success";
942
+ if (data.conclusion === "cancelled") return "cancelled";
943
+ return "failure";
942
944
  }
943
945
  }
944
946
  return "timeout";
@@ -962,31 +964,33 @@ async function glTriggerPipeline(cfg, ref, variables = {}) {
962
964
  const data = await res.json();
963
965
  return { pipelineId: data.id, url: data.web_url };
964
966
  }
965
- async function glPollPipeline(cfg, pipelineId, onTick, timeoutMs = 30 * 60 * 1e3) {
967
+ async function glPollPipeline(cfg, pipelineId, onTick, timeoutMs = 30 * 60 * 1e3, intervalMs = 15e3) {
966
968
  const start = Date.now();
967
969
  let consecutiveErrors = 0;
968
970
  while (Date.now() - start < timeoutMs) {
969
- await new Promise((r) => setTimeout(r, 15e3));
971
+ await new Promise((r) => setTimeout(r, intervalMs));
972
+ let res;
970
973
  try {
971
- const res = await fetch(
974
+ res = await fetch(
972
975
  `${glBase(cfg)}/projects/${glProjectId(cfg)}/pipelines/${pipelineId}`,
973
976
  { headers: { "PRIVATE-TOKEN": cfg.token } }
974
977
  );
975
- if (!res.ok) {
976
- consecutiveErrors++;
977
- if (consecutiveErrors >= 3) throw new Error("GitLab API \uC5F0\uC18D \uC624\uB958");
978
- continue;
979
- }
980
- consecutiveErrors = 0;
981
- const data = await res.json();
982
- onTick?.(data.status);
983
- if (data.status === "success") return "success";
984
- if (data.status === "failed") return "failure";
985
- if (data.status === "canceled" || data.status === "skipped") return "cancelled";
986
978
  } catch {
987
979
  consecutiveErrors++;
988
980
  if (consecutiveErrors >= 3) throw new Error("GitLab API \uC5F0\uC18D \uC624\uB958 3\uD68C");
981
+ continue;
989
982
  }
983
+ if (!res.ok) {
984
+ consecutiveErrors++;
985
+ if (consecutiveErrors >= 3) throw new Error(`GitLab API \uC5F0\uC18D \uC624\uB958 (HTTP ${res.status})`);
986
+ continue;
987
+ }
988
+ consecutiveErrors = 0;
989
+ const data = await res.json();
990
+ onTick?.(data.status);
991
+ if (data.status === "success") return "success";
992
+ if (data.status === "failed") return "failure";
993
+ if (data.status === "canceled" || data.status === "skipped") return "cancelled";
990
994
  }
991
995
  return "timeout";
992
996
  }
@@ -1301,10 +1305,11 @@ async function cmdDeploy(argv) {
1301
1305
  }
1302
1306
  const buildId = await runGitProviderBuild(ciProvider, args);
1303
1307
  if (!versionCode) {
1304
- versionCode = buildId;
1305
- log2(kleur6.dim(` versionCode = build id (${versionCode})`));
1306
- log2(kleur6.dim(` \uC8FC\uC758: GitHub run ID / GitLab pipeline ID\uB294 versionCode\uB85C \uC801\uD569\uD558\uC9C0 \uC54A\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.`));
1307
- log2(kleur6.dim(` --version-code <N> \uB85C \uBA85\uC2DC \uAD8C\uC7A5.`));
1308
+ log2(kleur6.red(`\u2717 versionCode \uBBF8\uC9C0\uC815 (${kind} run_id ${buildId}\uB294 versionCode\uB85C \uBD80\uC801\uD569)`));
1309
+ log2(kleur6.dim(" \uAD8C\uC7A5 \uC0AC\uD56D:"));
1310
+ log2(kleur6.dim(" \u2022 CI \uC6CC\uD06C\uD50C\uB85C \uC548\uC5D0\uC11C versionCode\uB97C \uACB0\uC815\uD558\uACE0 \uACB0\uACFC\uB97C \uCD9C\uB825"));
1311
+ log2(kleur6.dim(" \u2022 \uB2E4\uC74C \uC2E4\uD589: mimi-seed deploy --skip-build --version-code <N>"));
1312
+ process.exit(1);
1308
1313
  }
1309
1314
  }
1310
1315
  }
@@ -1448,6 +1453,63 @@ async function cmdRestart(args) {
1448
1453
  log3(kleur7.cyan(" \uC5F0\uACB0 \uD655\uC778: Claude Code\uC5D0\uC11C /mcp \uC2E4\uD589"));
1449
1454
  }
1450
1455
 
1456
+ // src/mcp-config.ts
1457
+ import fs5 from "fs/promises";
1458
+ import path5 from "path";
1459
+ import os4 from "os";
1460
+ import kleur8 from "kleur";
1461
+ var SERVER_NAME = "mimi-seed";
1462
+ function tomlString(value) {
1463
+ return JSON.stringify(value);
1464
+ }
1465
+ function codexBlock(cfg) {
1466
+ return [
1467
+ `[mcp_servers.${SERVER_NAME}]`,
1468
+ `url = ${tomlString(cfg.endpoint)}`,
1469
+ `enabled = true`,
1470
+ `http_headers = { Authorization = ${tomlString(`Bearer ${cfg.token}`)} }`,
1471
+ ""
1472
+ ].join("\n");
1473
+ }
1474
+ function replaceTomlBlock(input, tableName, block) {
1475
+ const header = `[${tableName}]`;
1476
+ const start = input.indexOf(header);
1477
+ if (start < 0) {
1478
+ const trimmed = input.trimEnd();
1479
+ return `${trimmed}${trimmed ? "\n\n" : ""}${block}`;
1480
+ }
1481
+ const next = input.slice(start + header.length).search(/\n\[[^\]]+\]/);
1482
+ const end = next < 0 ? input.length : start + header.length + next + 1;
1483
+ return `${input.slice(0, start)}${block}${input.slice(end).replace(/^\n+/, "\n")}`;
1484
+ }
1485
+ function printMcpSetup(cfg) {
1486
+ process.stdout.write(
1487
+ [
1488
+ kleur8.dim("Claude Code MCP \uB4F1\uB85D:"),
1489
+ kleur8.dim(` claude mcp add --transport http ${SERVER_NAME} ${cfg.endpoint} \\`),
1490
+ kleur8.dim(` --header "Authorization: Bearer ${cfg.prefix}..."`),
1491
+ "",
1492
+ kleur8.dim("Codex MCP \uB4F1\uB85D:"),
1493
+ kleur8.dim(" mimi-seed mcp codex --write"),
1494
+ kleur8.dim(" # \uB610\uB294 \uC218\uB3D9\uC73C\uB85C ~/.codex/config.toml \uC5D0 [mcp_servers.mimi-seed] \uCD94\uAC00")
1495
+ ].join("\n") + "\n"
1496
+ );
1497
+ }
1498
+ async function writeCodexMcpConfig(cfg) {
1499
+ const configDir = path5.join(os4.homedir(), ".codex");
1500
+ const configPath = path5.join(configDir, "config.toml");
1501
+ await fs5.mkdir(configDir, { recursive: true });
1502
+ let current = "";
1503
+ try {
1504
+ current = await fs5.readFile(configPath, "utf8");
1505
+ } catch {
1506
+ current = "";
1507
+ }
1508
+ const next = replaceTomlBlock(current, `mcp_servers.${SERVER_NAME}`, codexBlock(cfg));
1509
+ await fs5.writeFile(configPath, next);
1510
+ return configPath;
1511
+ }
1512
+
1451
1513
  // src/index.ts
1452
1514
  var DEFAULT_WEB_BASE = process.env.MIMI_SEED_WEB_BASE ?? "https://mimi-seed.pryzm.gg";
1453
1515
  var DEFAULT_MCP_ENDPOINT = `${DEFAULT_WEB_BASE}/api/mcp`;
@@ -1456,54 +1518,54 @@ function log4(msg) {
1456
1518
  }
1457
1519
  async function cmdInit() {
1458
1520
  const cwd = process.cwd();
1459
- log4(kleur8.bold("Mimi Seed CLI \u2014 init"));
1460
- log4(kleur8.dim(cwd));
1521
+ log4(kleur9.bold("Mimi Seed CLI \u2014 init"));
1522
+ log4(kleur9.dim(cwd));
1461
1523
  log4("");
1462
1524
  if (!await hasAnyProjectSignal(cwd)) {
1463
- log4(kleur8.yellow("\u26A0 package.json / app.json / android / ios \uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uADF8\uB798\uB3C4 \uC9C4\uD589\uD569\uB2C8\uB2E4."));
1525
+ log4(kleur9.yellow("\u26A0 package.json / app.json / android / ios \uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uADF8\uB798\uB3C4 \uC9C4\uD589\uD569\uB2C8\uB2E4."));
1464
1526
  }
1465
1527
  log4("\u{1F50D} \uC571 \uAC10\uC9C0 \uC911...");
1466
1528
  const hints = await detectHints(cwd);
1467
1529
  if (hints.length === 0) {
1468
- log4(kleur8.yellow("\uAC10\uC9C0\uB41C \uC571 \uC5C6\uC74C. \uC6F9\uC5D0\uC11C \uC218\uB3D9 \uB4F1\uB85D \uAC00\uB2A5."));
1530
+ log4(kleur9.yellow("\uAC10\uC9C0\uB41C \uC571 \uC5C6\uC74C. \uC6F9\uC5D0\uC11C \uC218\uB3D9 \uB4F1\uB85D \uAC00\uB2A5."));
1469
1531
  } else {
1470
1532
  for (const h of hints) {
1471
1533
  const tag = [h.packageName && `android:${h.packageName}`, h.bundleId && `ios:${h.bundleId}`].filter(Boolean).join(" ");
1472
- log4(` \u2022 ${h.name ?? "(\uC774\uB984 \uBBF8\uC0C1)"} ${kleur8.dim(tag)}`);
1534
+ log4(` \u2022 ${h.name ?? "(\uC774\uB984 \uBBF8\uC0C1)"} ${kleur9.dim(tag)}`);
1473
1535
  }
1474
1536
  }
1475
1537
  log4("");
1476
1538
  if (process.env.MIMI_SEED_TOKEN) {
1477
1539
  const cfg2 = await getEffectiveConfig();
1478
1540
  if (cfg2 && hints.length > 0) {
1479
- log4(kleur8.dim("CI \uBAA8\uB4DC: MIMI_SEED_TOKEN \uC0AC\uC6A9"));
1541
+ log4(kleur9.dim("CI \uBAA8\uB4DC: MIMI_SEED_TOKEN \uC0AC\uC6A9"));
1480
1542
  const payload = hints.map((h) => ({ name: h.name, packageName: h.packageName, bundleId: h.bundleId }));
1481
1543
  const result = await mcpCall(cfg2.endpoint, cfg2.token, "sync_apps", { hints: payload });
1482
1544
  if (result.isError) {
1483
- log4(kleur8.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
1545
+ log4(kleur9.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
1484
1546
  } else {
1485
1547
  for (const line of result.text.split("\n")) log4(" " + line);
1486
1548
  }
1487
1549
  }
1488
- log4(kleur8.bold("\u2713 \uC644\uB8CC (CI \uBAA8\uB4DC)"));
1550
+ log4(kleur9.bold("\u2713 \uC644\uB8CC (CI \uBAA8\uB4DC)"));
1489
1551
  return;
1490
1552
  }
1491
1553
  log4("\u{1F510} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778 \uB300\uAE30...");
1492
- const hostName = os4.hostname().slice(0, 32);
1554
+ const hostName = os5.hostname().slice(0, 32);
1493
1555
  const name = `cli-${hostName}`;
1494
1556
  const { port, promise } = await awaitHandshake(5 * 60 * 1e3);
1495
1557
  const callback = `http://127.0.0.1:${port}/cb`;
1496
1558
  const connectUrl = `${DEFAULT_WEB_BASE}/cli/connect?callback=${encodeURIComponent(callback)}&name=${encodeURIComponent(name)}`;
1497
- log4(kleur8.dim(` ${connectUrl}`));
1559
+ log4(kleur9.dim(` ${connectUrl}`));
1498
1560
  await open(connectUrl);
1499
1561
  let handshake;
1500
1562
  try {
1501
1563
  handshake = await promise;
1502
1564
  } catch (e) {
1503
- log4(kleur8.red("\uC5F0\uACB0 \uC2E4\uD328: " + e.message));
1565
+ log4(kleur9.red("\uC5F0\uACB0 \uC2E4\uD328: " + e.message));
1504
1566
  process.exit(1);
1505
1567
  }
1506
- log4(kleur8.green("\u2713 \uD1A0\uD070 \uC218\uC2E0"));
1568
+ log4(kleur9.green("\u2713 \uD1A0\uD070 \uC218\uC2E0"));
1507
1569
  const cfg = {
1508
1570
  token: handshake.token,
1509
1571
  prefix: handshake.prefix,
@@ -1512,112 +1574,148 @@ async function cmdInit() {
1512
1574
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
1513
1575
  };
1514
1576
  await writeConfig(cfg);
1515
- log4(kleur8.dim(` \uC800\uC7A5\uB428: ${CONFIG_LOCATION}`));
1577
+ log4(kleur9.dim(` \uC800\uC7A5\uB428: ${CONFIG_LOCATION}`));
1516
1578
  log4("");
1517
1579
  if (hints.length > 0) {
1518
1580
  log4("\u{1F504} \uC571 \uB4F1\uB85D \uC911...");
1519
1581
  const payload = hints.map((h) => ({ name: h.name, packageName: h.packageName, bundleId: h.bundleId }));
1520
1582
  const result = await mcpCall(cfg.endpoint, cfg.token, "sync_apps", { hints: payload });
1521
1583
  if (result.isError) {
1522
- log4(kleur8.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
1584
+ log4(kleur9.red("\uB4F1\uB85D \uC2E4\uD328: " + result.text));
1523
1585
  } else {
1524
1586
  for (const line of result.text.split("\n")) log4(" " + line);
1525
1587
  }
1526
1588
  log4("");
1527
1589
  }
1528
- const claudeDir = path5.join(cwd, ".claude");
1529
- const agentMdPath = path5.join(claudeDir, "mimi-seed.md");
1530
- if (!fs5.existsSync(agentMdPath)) {
1531
- fs5.mkdirSync(claudeDir, { recursive: true });
1532
- const appLines = hints.flatMap((h) => {
1533
- const parts = [
1534
- h.name && ` name: ${h.name}`,
1535
- h.packageName && ` packageName: ${h.packageName}`,
1536
- h.bundleId && ` bundleId: ${h.bundleId}`
1537
- ].filter(Boolean);
1538
- return parts;
1539
- });
1540
- const agentMd = [
1541
- "# Mimi Seed Agent",
1542
- "",
1543
- "Mimi Seed MCP\uAC00 \uC774 \uD504\uB85C\uC81D\uD2B8\uC5D0 \uC5F0\uACB0\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.",
1544
- "Google Play \xB7 App Store \xB7 Firebase \xB7 AdMob\uC744 \uB3C4\uAD6C\uB85C \uC9C1\uC811 \uC81C\uC5B4\uD569\uB2C8\uB2E4.",
1545
- "",
1546
- "## \uCD9C\uC2DC \uC694\uCCAD \uCC98\uB9AC \uC21C\uC11C",
1547
- "",
1548
- "1. \uD56D\uC0C1 `playstore_check_submission_risks` / `appstore_check_submission_risks` \uB85C \uBE14\uB85C\uCEE4 \uBA3C\uC800 \uD655\uC778",
1549
- "2. \uB9B4\uB9AC\uC988 \uB178\uD2B8: `generate_release_notes_from_commits` \u2192 \uC0AC\uC6A9\uC790 \uD655\uC778 \uD6C4 \uC801\uC6A9",
1550
- "3. \uC2A4\uD1A0\uC5B4 **\uC4F0\uAE30** \uC791\uC5C5(submit, apply, reply)\uC740 \uBC18\uB4DC\uC2DC \uC0AC\uC6A9\uC790 \uBA85\uC2DC \uB3D9\uC758 \uD6C4 \uC2E4\uD589",
1551
- "4. \uC644\uB8CC \uD6C4 \uACB0\uACFC \uC694\uC57D \uC81C\uACF5",
1552
- "",
1553
- "## \uC571 \uC815\uBCF4",
1554
- ...appLines.length > 0 ? appLines : [" (mimi-seed status \uB85C \uD655\uC778)"],
1555
- "",
1556
- "## \uC2AC\uB798\uC2DC \uCEE4\uB9E8\uB4DC",
1557
- "",
1558
- "- `/mimi-seed:deploy` \u2014 \uC804\uCCB4 \uCD9C\uC2DC \uD30C\uC774\uD504\uB77C\uC778",
1559
- "- `/mimi-seed:health` \u2014 \uC5F0\uACB0 \uC0C1\uD0DC \uBE60\uB978 \uD655\uC778",
1560
- "- `/mimi-seed:review-inbox` \u2014 \uBBF8\uB2F5\uBCC0 \uB9AC\uBDF0 \uB2F5\uBCC0"
1561
- ].join("\n");
1562
- fs5.writeFileSync(agentMdPath, agentMd, { mode: 420 });
1563
- log4(kleur8.dim(` \uC5D0\uC774\uC804\uD2B8 \uC124\uC815: .claude/mimi-seed.md`));
1564
- }
1565
- log4(kleur8.bold("\u2713 \uC900\uBE44 \uC644\uB8CC."));
1590
+ const appLines = hints.flatMap((h) => {
1591
+ const parts = [
1592
+ h.name && ` name: ${h.name}`,
1593
+ h.packageName && ` packageName: ${h.packageName}`,
1594
+ h.bundleId && ` bundleId: ${h.bundleId}`
1595
+ ].filter(Boolean);
1596
+ return parts;
1597
+ });
1598
+ const agentMd = [
1599
+ "# Mimi Seed Agent",
1600
+ "",
1601
+ "Mimi Seed MCP\uAC00 \uC774 \uD504\uB85C\uC81D\uD2B8\uC5D0 \uC5F0\uACB0\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.",
1602
+ "Google Play \xB7 App Store \xB7 Firebase \xB7 AdMob\uC744 \uB3C4\uAD6C\uB85C \uC9C1\uC811 \uC81C\uC5B4\uD569\uB2C8\uB2E4.",
1603
+ "",
1604
+ "## \uCD9C\uC2DC \uC694\uCCAD \uCC98\uB9AC \uC21C\uC11C",
1605
+ "",
1606
+ "1. \uD56D\uC0C1 `playstore_check_submission_risks` / `appstore_check_submission_risks` \uB85C \uBE14\uB85C\uCEE4 \uBA3C\uC800 \uD655\uC778",
1607
+ "2. \uB9B4\uB9AC\uC988 \uB178\uD2B8: `generate_release_notes_from_commits` \u2192 \uC0AC\uC6A9\uC790 \uD655\uC778 \uD6C4 \uC801\uC6A9",
1608
+ "3. \uC2A4\uD1A0\uC5B4 **\uC4F0\uAE30** \uC791\uC5C5(submit, apply, reply)\uC740 \uBC18\uB4DC\uC2DC \uC0AC\uC6A9\uC790 \uBA85\uC2DC \uB3D9\uC758 \uD6C4 \uC2E4\uD589",
1609
+ "4. \uC644\uB8CC \uD6C4 \uACB0\uACFC \uC694\uC57D \uC81C\uACF5",
1610
+ "",
1611
+ "## \uC571 \uC815\uBCF4",
1612
+ ...appLines.length > 0 ? appLines : [" (mimi-seed status \uB85C \uD655\uC778)"],
1613
+ "",
1614
+ "## \uC2AC\uB798\uC2DC \uCEE4\uB9E8\uB4DC",
1615
+ "",
1616
+ "- `/mimi-seed:deploy` \u2014 \uC804\uCCB4 \uCD9C\uC2DC \uD30C\uC774\uD504\uB77C\uC778",
1617
+ "- `/mimi-seed:health` \u2014 \uC5F0\uACB0 \uC0C1\uD0DC \uBE60\uB978 \uD655\uC778",
1618
+ "- `/mimi-seed:review-inbox` \u2014 \uBBF8\uB2F5\uBCC0 \uB9AC\uBDF0 \uB2F5\uBCC0"
1619
+ ].join("\n");
1620
+ const claudeDir = path6.join(cwd, ".claude");
1621
+ const claudeAgentPath = path6.join(claudeDir, "mimi-seed.md");
1622
+ if (!fs6.existsSync(claudeAgentPath)) {
1623
+ fs6.mkdirSync(claudeDir, { recursive: true });
1624
+ fs6.writeFileSync(claudeAgentPath, agentMd, { mode: 420 });
1625
+ log4(kleur9.dim(` Claude \uC5D0\uC774\uC804\uD2B8 \uC124\uC815: .claude/mimi-seed.md`));
1626
+ }
1627
+ const codexAgentPath = path6.join(cwd, "AGENTS.md");
1628
+ if (!fs6.existsSync(codexAgentPath)) {
1629
+ fs6.writeFileSync(codexAgentPath, agentMd, { mode: 420 });
1630
+ log4(kleur9.dim(` Codex \uC5D0\uC774\uC804\uD2B8 \uC124\uC815: AGENTS.md`));
1631
+ }
1632
+ log4(kleur9.bold("\u2713 \uC900\uBE44 \uC644\uB8CC."));
1566
1633
  log4("");
1567
- log4("Claude Code\uC5D0\uC11C \uC774\uB807\uAC8C \uBB3C\uC5B4\uBCF4\uC138\uC694:");
1568
- log4(kleur8.cyan(' "\uB0B4 \uC571 \uCD9C\uC2DC \uC900\uBE44\uB410\uC5B4?"'));
1569
- log4(kleur8.cyan(' "\uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC368\uC918"'));
1570
- log4(kleur8.cyan(' "\uB4F1\uB85D\uB41C \uC571 \uBAA9\uB85D \uBCF4\uC5EC\uC918"'));
1634
+ log4("Claude Code \uB610\uB294 Codex\uC5D0\uC11C \uC774\uB807\uAC8C \uBB3C\uC5B4\uBCF4\uC138\uC694:");
1635
+ log4(kleur9.cyan(' "\uB0B4 \uC571 \uCD9C\uC2DC \uC900\uBE44\uB410\uC5B4?"'));
1636
+ log4(kleur9.cyan(' "\uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC368\uC918"'));
1637
+ log4(kleur9.cyan(' "\uB4F1\uB85D\uB41C \uC571 \uBAA9\uB85D \uBCF4\uC5EC\uC918"'));
1571
1638
  log4("");
1572
- log4(`\uB300\uC2DC\uBCF4\uB4DC: ${kleur8.underline(DEFAULT_WEB_BASE + "/apps")}`);
1639
+ log4(`\uB300\uC2DC\uBCF4\uB4DC: ${kleur9.underline(DEFAULT_WEB_BASE + "/apps")}`);
1573
1640
  log4("");
1574
- log4(
1575
- kleur8.dim(
1576
- `Claude Code MCP \uB4F1\uB85D:
1577
- claude mcp add --transport http mimi-seed ${DEFAULT_MCP_ENDPOINT} \\
1578
- --header "Authorization: Bearer ${cfg.prefix}..."`
1579
- )
1580
- );
1641
+ printMcpSetup(cfg);
1581
1642
  }
1582
1643
  async function cmdStatus() {
1583
1644
  const cfg = await getEffectiveConfig();
1584
1645
  if (!cfg) {
1585
- log4(kleur8.yellow("\uC5F0\uACB0\uB41C Mimi Seed \uACC4\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uC2E4\uD589."));
1646
+ log4(kleur9.yellow("\uC5F0\uACB0\uB41C Mimi Seed \uACC4\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uC2E4\uD589."));
1586
1647
  process.exit(1);
1587
1648
  }
1588
- log4(kleur8.bold("Mimi Seed \uC5F0\uACB0 \uC0C1\uD0DC"));
1649
+ log4(kleur9.bold("Mimi Seed \uC5F0\uACB0 \uC0C1\uD0DC"));
1589
1650
  log4(` \uD1A0\uD070: ${cfg.prefix}\u2026 (${cfg.createdAt.slice(0, 10)})`);
1590
1651
  log4(` \uC5D4\uB4DC\uD3EC\uC778\uD2B8: ${cfg.endpoint}`);
1591
1652
  log4("");
1592
1653
  log4("\u{1F4CB} \uC571 \uBAA9\uB85D:");
1593
1654
  const r = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
1594
1655
  if (r.isError) {
1595
- log4(kleur8.red("\uC870\uD68C \uC2E4\uD328: " + r.text));
1656
+ log4(kleur9.red("\uC870\uD68C \uC2E4\uD328: " + r.text));
1596
1657
  process.exit(1);
1597
1658
  }
1598
1659
  for (const line of r.text.split("\n")) log4(" " + line);
1599
1660
  }
1600
1661
  async function cmdLogout() {
1601
1662
  await deleteConfig();
1602
- log4(kleur8.green("\u2713 \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C \uC644\uB8CC."));
1603
- log4(kleur8.dim("\uC6F9\uC5D0\uC11C \uD1A0\uD070 \uD574\uC9C0: /workspace/api-tokens"));
1663
+ log4(kleur9.green("\u2713 \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C \uC644\uB8CC."));
1664
+ log4(kleur9.dim("\uC6F9\uC5D0\uC11C \uD1A0\uD070 \uD574\uC9C0: /workspace/api-tokens"));
1665
+ }
1666
+ async function cmdMcp(args) {
1667
+ const target = args[0] ?? "help";
1668
+ const cfg = await getEffectiveConfig();
1669
+ if (!cfg) {
1670
+ log4(kleur9.yellow("\uC5F0\uACB0\uB41C Mimi Seed \uACC4\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `mimi-seed init` \uC2E4\uD589."));
1671
+ process.exit(1);
1672
+ }
1673
+ if (target === "codex") {
1674
+ if (args.includes("--write")) {
1675
+ const configPath = await writeCodexMcpConfig(cfg);
1676
+ log4(kleur9.green("\u2713 Codex MCP \uC124\uC815 \uC644\uB8CC"));
1677
+ log4(kleur9.dim(` ${configPath}`));
1678
+ log4("");
1679
+ log4("Codex\uB97C \uC0C8\uB85C \uC5F4\uACE0 `/mcp` \uB610\uB294 `codex mcp list`\uB85C \uD655\uC778\uD558\uC138\uC694.");
1680
+ return;
1681
+ }
1682
+ log4(kleur9.bold("Codex MCP \uB4F1\uB85D"));
1683
+ log4("");
1684
+ log4("\uC790\uB3D9 \uB4F1\uB85D:");
1685
+ log4(kleur9.cyan(" mimi-seed mcp codex --write"));
1686
+ log4("");
1687
+ log4("\uC218\uB3D9 \uB4F1\uB85D \uC608\uC2DC (~/.codex/config.toml):");
1688
+ log4(`[mcp_servers.mimi-seed]
1689
+ url = "${cfg.endpoint}"
1690
+ enabled = true
1691
+ http_headers = { Authorization = "Bearer ${cfg.prefix}..." }`);
1692
+ return;
1693
+ }
1694
+ if (target === "claude") {
1695
+ log4(kleur9.bold("Claude Code MCP \uB4F1\uB85D"));
1696
+ log4(kleur9.cyan(` claude mcp add --transport http mimi-seed ${cfg.endpoint} \\`));
1697
+ log4(kleur9.cyan(` --header "Authorization: Bearer ${cfg.prefix}..."`));
1698
+ return;
1699
+ }
1700
+ printMcpSetup(cfg);
1604
1701
  }
1605
1702
  function printHelp() {
1606
- log4(`${kleur8.bold("mimi-seed")} \u2014 Claude Code\uC5D0\uC11C \uC571 \uCD9C\uC2DC \uC6B4\uC601
1703
+ log4(`${kleur9.bold("mimi-seed")} \u2014 Claude Code/Codex\uC5D0\uC11C \uC571 \uCD9C\uC2DC \uC6B4\uC601
1607
1704
 
1608
- ${kleur8.bold("\uBA85\uB839\uC5B4:")}
1609
- ${kleur8.cyan("mimi-seed init")} \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
1610
- ${kleur8.cyan("mimi-seed status")} \uC5F0\uACB0 \uC0C1\uD0DC + \uB4F1\uB85D \uC571 \uBAA9\uB85D
1611
- ${kleur8.cyan("mimi-seed auth")} Google OAuth \uC778\uC99D (Firebase/AdMob/Play). 'mimi-seed auth --help'
1612
- ${kleur8.cyan("mimi-seed doctor")} \uD658\uACBD \uC9C4\uB2E8 (\uD1A0\uD070\xB7Git\xB7\uD504\uB85C\uC81D\uD2B8\xB7CI \uCCB4\uD06C)
1613
- ${kleur8.cyan("mimi-seed check")} \uCD9C\uC2DC \uC804 Readiness \uC810\uAC80
1614
- ${kleur8.cyan("mimi-seed notes")} \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 (git log \u2192 AI \u2192 \uB9C8\uCF13 \uC801\uC6A9)
1615
- ${kleur8.cyan("mimi-seed review")} \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
1616
- ${kleur8.cyan("mimi-seed deploy")} \uC571 \uC790\uB3D9 \uBC30\uD3EC (Jenkins \u2192 Play Store/App Store)
1617
- ${kleur8.cyan("mimi-seed restart")} MCP \uC11C\uBC84 \uD504\uB85C\uC138\uC2A4 \uC7AC\uC2DC\uC791 (\uAE30\uBCF8: mimi-seed)
1618
- ${kleur8.cyan("mimi-seed logout")} \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C
1705
+ ${kleur9.bold("\uBA85\uB839\uC5B4:")}
1706
+ ${kleur9.cyan("mimi-seed init")} \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uB97C Mimi Seed\uC5D0 \uC5F0\uACB0
1707
+ ${kleur9.cyan("mimi-seed status")} \uC5F0\uACB0 \uC0C1\uD0DC + \uB4F1\uB85D \uC571 \uBAA9\uB85D
1708
+ ${kleur9.cyan("mimi-seed auth")} Google OAuth \uC778\uC99D (Firebase/AdMob/Play). 'mimi-seed auth --help'
1709
+ ${kleur9.cyan("mimi-seed doctor")} \uD658\uACBD \uC9C4\uB2E8 (\uD1A0\uD070\xB7Git\xB7\uD504\uB85C\uC81D\uD2B8\xB7CI \uCCB4\uD06C)
1710
+ ${kleur9.cyan("mimi-seed check")} \uCD9C\uC2DC \uC804 Readiness \uC810\uAC80
1711
+ ${kleur9.cyan("mimi-seed notes")} \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC0DD\uC131 (git log \u2192 AI \u2192 \uB9C8\uCF13 \uC801\uC6A9)
1712
+ ${kleur9.cyan("mimi-seed review")} \uB9AC\uBDF0 \uB2F5\uBCC0 AI \uCD08\uC548 \uC0DD\uC131 \uBC0F Play Store \uAC8C\uC2DC
1713
+ ${kleur9.cyan("mimi-seed deploy")} \uC571 \uC790\uB3D9 \uBC30\uD3EC (Jenkins \u2192 Play Store/App Store)
1714
+ ${kleur9.cyan("mimi-seed mcp")} Claude/Codex MCP \uC5F0\uACB0 \uC548\uB0B4 \uBC0F Codex \uC124\uC815 \uC4F0\uAE30
1715
+ ${kleur9.cyan("mimi-seed restart")} MCP \uC11C\uBC84 \uD504\uB85C\uC138\uC2A4 \uC7AC\uC2DC\uC791 (\uAE30\uBCF8: mimi-seed)
1716
+ ${kleur9.cyan("mimi-seed logout")} \uB85C\uCEEC \uC124\uC815 \uC0AD\uC81C
1619
1717
 
1620
- ${kleur8.bold("mimi-seed notes \uC635\uC158:")}
1718
+ ${kleur9.bold("mimi-seed notes \uC635\uC158:")}
1621
1719
  --from <ref> \uC2DC\uC791 \uCEE4\uBC0B/\uD0DC\uADF8 (\uAE30\uBCF8: \uCD5C\uC2E0 \uD0DC\uADF8)
1622
1720
  --to <ref> \uB05D \uCEE4\uBC0B (\uAE30\uBCF8: HEAD)
1623
1721
  --locale ko,en-US \uB300\uC0C1 \uB85C\uCF00\uC77C (\uC27C\uD45C \uAD6C\uBD84)
@@ -1625,11 +1723,11 @@ ${kleur8.bold("mimi-seed notes \uC635\uC158:")}
1625
1723
  --no-interactive CI \uBAA8\uB4DC (\uD504\uB86C\uD504\uD2B8 \uC5C6\uC74C)
1626
1724
  --limit <n> \uCD5C\uB300 \uCEE4\uBC0B \uC218 (\uAE30\uBCF8: 30)
1627
1725
 
1628
- ${kleur8.bold("mimi-seed check \uC635\uC158:")}
1726
+ ${kleur9.bold("mimi-seed check \uC635\uC158:")}
1629
1727
  --app <id> \uC571 ID \uC9C0\uC815
1630
1728
  --fail-on-blocker \uBE14\uB85C\uCEE4 \uC788\uC73C\uBA74 exit 1 (CI\uC6A9)
1631
1729
 
1632
- ${kleur8.bold("mimi-seed review \uC635\uC158:")}
1730
+ ${kleur9.bold("mimi-seed review \uC635\uC158:")}
1633
1731
  --text <\uB0B4\uC6A9> \uB9AC\uBDF0 \uC6D0\uBB38 (\uBBF8\uC785\uB825 \uC2DC \uB300\uD654\uD615 \uD504\uB86C\uD504\uD2B8)
1634
1732
  --rating <1-5> \uBCC4\uC810
1635
1733
  --tone <tone> friendly / professional / empathetic / brief (\uAE30\uBCF8: friendly)
@@ -1640,7 +1738,7 @@ ${kleur8.bold("mimi-seed review \uC635\uC158:")}
1640
1738
  --package-name <p> \uD328\uD0A4\uC9C0\uBA85 (--apply \uC2DC \uD544\uC694)
1641
1739
  --no-interactive CI \uBAA8\uB4DC
1642
1740
 
1643
- ${kleur8.bold("mimi-seed deploy \uC635\uC158:")}
1741
+ ${kleur9.bold("mimi-seed deploy \uC635\uC158:")}
1644
1742
  --platform android|ios \uBC30\uD3EC \uD50C\uB7AB\uD3FC (\uAE30\uBCF8: android)
1645
1743
  --app <id> \uC571 ID \uC9C0\uC815
1646
1744
  --version-code <n> \uBE4C\uB4DC \uBC88\uD638 \uC9C1\uC811 \uC9C0\uC815 (--skip-build \uC640 \uD568\uAED8)
@@ -1656,7 +1754,7 @@ ${kleur8.bold("mimi-seed deploy \uC635\uC158:")}
1656
1754
  setup-github GitHub Actions \uC124\uC815 \uB300\uD654\uD615 \uB4F1\uB85D
1657
1755
  setup-gitlab GitLab CI \uC124\uC815 \uB300\uD654\uD615 \uB4F1\uB85D
1658
1756
 
1659
- ${kleur8.bold("\uD658\uACBD\uBCC0\uC218:")}
1757
+ ${kleur9.bold("\uD658\uACBD\uBCC0\uC218:")}
1660
1758
  MIMI_SEED_TOKEN PAT \uD1A0\uD070 (CI/CD \uBB34\uC778\uC99D \uBAA8\uB4DC)
1661
1759
  MIMI_SEED_WEB_BASE \uC11C\uBC84 \uC8FC\uC18C (\uAE30\uBCF8: https://mimi-seed.pryzm.gg)
1662
1760
  ANTHROPIC_API_KEY AI \uB178\uD2B8 \uC0DD\uC131 \uD65C\uC131\uD654 (\uC120\uD0DD)
@@ -1691,6 +1789,9 @@ async function main() {
1691
1789
  case "deploy":
1692
1790
  await cmdDeploy(restArgs);
1693
1791
  break;
1792
+ case "mcp":
1793
+ await cmdMcp(restArgs);
1794
+ break;
1694
1795
  case "restart":
1695
1796
  await cmdRestart(restArgs);
1696
1797
  break;
@@ -1703,12 +1804,12 @@ async function main() {
1703
1804
  printHelp();
1704
1805
  break;
1705
1806
  default:
1706
- log4(kleur8.red(`\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839: ${cmd}`));
1807
+ log4(kleur9.red(`\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839: ${cmd}`));
1707
1808
  printHelp();
1708
1809
  process.exit(1);
1709
1810
  }
1710
1811
  } catch (e) {
1711
- log4(kleur8.red(`\uC624\uB958: ${e.message}`));
1812
+ log4(kleur9.red(`\uC624\uB958: ${e.message}`));
1712
1813
  if (process.env.DEBUG) log4(e.stack ?? "");
1713
1814
  process.exit(1);
1714
1815
  }
package/package.json CHANGED
@@ -1,19 +1,27 @@
1
1
  {
2
2
  "name": "mimi-seed",
3
- "version": "0.2.7",
4
- "description": "Mimi Seed CLI — Claude Code에서 앱 출시 운영을 관리합니다.",
3
+ "version": "0.2.9",
4
+ "description": "Mimi Seed CLI — Claude Code와 Codex에서 앱 출시 운영을 관리합니다.",
5
5
  "bin": {
6
6
  "mimi-seed": "dist/index.js"
7
7
  },
8
8
  "type": "module",
9
9
  "license": "PolyForm-Noncommercial-1.0.0",
10
+ "homepage": "https://mimi-seed.pryzm.gg",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/jeonghwanko/mimi-seed-sdk.git",
14
+ "directory": "packages/cli"
15
+ },
10
16
  "files": [
11
17
  "dist",
12
18
  "LICENSE"
13
19
  ],
14
20
  "scripts": {
15
21
  "build": "tsup",
16
- "dev": "tsx src/index.ts"
22
+ "dev": "tsx src/index.ts",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest"
17
25
  },
18
26
  "engines": {
19
27
  "node": ">=18"
@@ -27,7 +35,8 @@
27
35
  "@types/node": "^20.11.0",
28
36
  "tsup": "^8.0.0",
29
37
  "tsx": "^4.7.0",
30
- "typescript": "^5.4.0"
38
+ "typescript": "^5.4.0",
39
+ "vitest": "^4.1.5"
31
40
  },
32
41
  "publishConfig": {
33
42
  "access": "public"