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