project-auto-wizard 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +24 -21
  2. package/package.json +2 -2
  3. package/payload/config/breaking-changes.json +1 -1
  4. package/payload/scripts/__pycache__/changelog_manager.cpython-314.pyc +0 -0
  5. package/payload/scripts/__pycache__/issue_helper.cpython-314.pyc +0 -0
  6. package/payload/scripts/__pycache__/version_manager.cpython-314.pyc +0 -0
  7. package/payload/scripts/changelog_manager.py +84 -31
  8. package/payload/scripts/version_manager.py +2 -2
  9. package/payload/version.yml.template +4 -2
  10. package/payload/workflows/common/PROJECT-COMMON-AI-PR-SUMMARY.yaml +30 -10
  11. package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +37 -12
  12. package/payload/workflows/common/PROJECT-COMMON-ISSUE-HELPER.yaml +3 -6
  13. package/payload/workflows/common/PROJECT-COMMON-README-VERSION-UPDATE.yaml +0 -12
  14. package/payload/workflows/common/PROJECT-COMMON-RELEASE-PUBLISH.yaml +27 -6
  15. package/payload/workflows/common/PROJECT-COMMON-VERSION-CONTROL.yaml +3 -7
  16. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +7 -34
  17. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +2 -25
  18. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +2 -20
  19. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +5 -30
  20. package/payload/workflows/flutter/PROJECT-FLUTTER-APP-BUILD-TRIGGER.yaml +0 -4
  21. package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +7 -51
  22. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +5 -29
  23. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +9 -29
  24. package/payload/workflows/go/PROJECT-GO-CI.yaml +5 -28
  25. package/payload/workflows/go/PROJECT-GO-PR-PREVIEW.yaml +19 -99
  26. package/payload/workflows/go/PROJECT-GO-SIMPLE-CICD.yaml +18 -89
  27. package/payload/workflows/next/PROJECT-NEXT-CI.yaml +11 -41
  28. package/payload/workflows/next/PROJECT-NEXT-CICD.yaml +9 -45
  29. package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +1 -27
  30. package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +14 -97
  31. package/payload/workflows/python/PROJECT-PYTHON-SIMPLE-CICD.yaml +14 -92
  32. package/payload/workflows/react/PROJECT-REACT-CI.yaml +11 -40
  33. package/payload/workflows/react/PROJECT-REACT-CICD.yaml +9 -43
  34. package/payload/workflows/spring/{nexus/PROJECT-SPRING-NEXUS-CI.yml → PROJECT-SPRING-CI.yml} +2 -25
  35. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-NGINX-CICD.yaml +19 -63
  36. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-TRAEFIK-CICD.yaml +19 -59
  37. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +23 -86
  38. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-SIMPLE-CICD.yaml +20 -80
  39. package/src/cli/args.js +8 -15
  40. package/src/cli/help.js +2 -3
  41. package/src/commands/doctor.js +9 -8
  42. package/src/commands/dry-run.js +1 -1
  43. package/src/commands/full.js +9 -10
  44. package/src/commands/interactive-flutter.js +1 -1
  45. package/src/commands/interactive.js +34 -46
  46. package/src/commands/purge.js +2 -2
  47. package/src/commands/status.js +5 -6
  48. package/src/commands/uninstall.js +3 -4
  49. package/src/context.js +5 -6
  50. package/src/core/assets.js +2 -2
  51. package/src/core/baseline.js +1 -1
  52. package/src/core/branches.js +3 -3
  53. package/src/core/breaking-check.js +1 -1
  54. package/src/core/breaking.js +1 -1
  55. package/src/core/copy/flutter-app.js +2 -2
  56. package/src/core/copy/gitignore.js +8 -8
  57. package/src/core/copy/workflows.js +24 -69
  58. package/src/core/deploy-style.js +2 -2
  59. package/src/core/detect-fs.js +11 -11
  60. package/src/core/detect.js +7 -7
  61. package/src/core/flutter-options.js +1 -1
  62. package/src/core/installed-stores.js +1 -1
  63. package/src/core/logger.js +1 -1
  64. package/src/core/paths-resolve.js +1 -1
  65. package/src/core/removal-plan.js +4 -4
  66. package/src/core/verify.js +4 -4
  67. package/src/core/version-yml.js +27 -34
  68. package/src/core/wizard-env.js +4 -4
  69. package/src/index.js +14 -14
  70. package/src/ui/banner.js +2 -2
  71. package/src/ui/env-plan.js +10 -16
  72. package/src/ui/prompts.js +9 -14
  73. package/src/ui/status-cards.js +10 -12
  74. package/src/ui/summary.js +13 -13
  75. package/payload/workflows/common/secret-backup/PROJECT-COMMON-SECRET-FILE-UPLOAD.yaml +0 -210
  76. package/payload/workflows/spring/nexus/PROJECT-SPRING-GITHUB-PACKAGES-PUBLISH.yml +0 -92
  77. package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-PUBLISH.yml +0 -70
  78. package/src/core/options-ask.js +0 -102
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # project-auto-wizard
2
2
 
3
- > **One command DevOps** — `npx` 한 줄로 어떤 프로젝트든 GitHub-native AI 릴리스 자동화를 설치하는 마법사
3
+ > **One command DevOps** — `npx` 한 줄로 어떤 프로젝트든 GitHub-native 릴리스 자동화(GitHub Copilot AI 요약은 선택)를 설치하는 마법사
4
4
 
5
5
  - 새 프로젝트를 시작하면 코드를 작성하기 전부터 버전 관리, 배포 자동화, 변경 기록 작성처럼 먼저 정해야 할 일이 많다. project-auto-wizard는 이 준비 과정을 명령 한 번으로 구성하고, 프로젝트 상태 관리가 GitHub 안에서 자동으로 동작하도록 해 개발자가 기능 구현에 집중하도록 돕는 오픈소스 도구
6
6
 
@@ -8,8 +8,6 @@
8
8
  npx project-auto-wizard
9
9
  ```
10
10
 
11
- <!-- TODO: 30초 데모 GIF (docs/assets/demo.gif) -->
12
- <!-- TODO: 3분 데모 YouTube 링크 -->
13
11
 
14
12
  [![CI](https://github.com/Twin-Fang/project-auto-wizard/actions/workflows/CI.yaml/badge.svg)](https://github.com/Twin-Fang/project-auto-wizard/actions/workflows/CI.yaml)
15
13
  [![npm](https://img.shields.io/npm/v/project-auto-wizard)](https://www.npmjs.com/package/project-auto-wizard)
@@ -17,7 +15,7 @@ npx project-auto-wizard
17
15
  [![node](https://img.shields.io/badge/node-%3E%3D20.12-brightgreen)](package.json)
18
16
 
19
17
  <!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->
20
- ## 최신 버전 : v0.10.0 (2026-09-21)
18
+ ## 최신 버전 : v0.12.0 (2026-09-24)
21
19
 
22
20
  [전체 버전 기록 보기](CHANGELOG.md)
23
21
 
@@ -38,7 +36,7 @@ npx project-auto-wizard --mode full --force --type spring,react # CI에서 비
38
36
  | 축 | 내용 |
39
37
  |---|---|
40
38
  | ① **npx 마법사** | 마커 파일로 프로젝트 타입 자동 감지 — **10타입 + 멀티타입 + 모노레포 경로**까지. 질문은 최소한만 |
41
- | ② **GitHub-native AI Release Automation** | 릴리스 PR을 열면: 버전 확정 → **AI가 릴리스 노트 작성** → CHANGELOG 갱신 → automerge → tag + GitHub Release. **API 키 0개** (GitHub Models) |
39
+ | ② **GitHub-native Release Automation** | 릴리스 PR을 열면: 버전 확정 → **릴리스 노트 작성**(기본은 규칙 기반, GitHub Copilot AI는 선택) → CHANGELOG 갱신 → automerge → tag + GitHub Release. **API 키 0개** |
42
40
  | ③ **타입별 CI/CD 워크플로우** | Spring(무중단 배포 포함)·Flutter(스토어 배포)·React·Next·Python·Go 등 타입에 맞는 GitHub Actions 자동 배치 |
43
41
 
44
42
  ### 지원 프로젝트 타입
@@ -67,7 +65,7 @@ flutter.APP_ARTIFACT_NAME:
67
65
  `spring`/`flutter`는 아래처럼 단일 CI 이상으로 깊게 구성되어 있습니다:
68
66
 
69
67
  - **flutter**: Android(Firebase/Playstore/Selfhosted/TestAPK 배포), iOS(TestFlight/Test-TestFlight), CI, Lab 트리거까지 8종 — 스토어 배포(Play Store·TestFlight)는 고른 플랫폼만 설치되고 fastlane 파일도 함께 생성됩니다. 환경변수 방식·배포 모드 등 자세한 내용은 아래 "Flutter 워크플로우 상세"를 참고하세요.
70
- - **spring**: 서버 배포 1종(단일 서버 / 무중단 Nginx / 무중단 Traefik / 배포 안 함 중 택1) + PR 프리뷰 + 라이브러리 publish 2종(Nexus·GitHub Packages, `--nexus` opt-in)
68
+ - **spring**: 서버 배포 1종(단일 서버 / 무중단 Nginx / 무중단 Traefik / 배포 안 함 중 택1) + PR 프리뷰 + CI
71
69
  - 서버 배포 워크플로우는 **서로 대체재**라 하나만 설치합니다. 대화형에서 고르면 그것만 깔리고 **`push` 트리거까지 켜진 채로** 설치됩니다. 비대화형은 `--deploy-style simple|nginx|traefik|none` (기본: `simple`).
72
70
  - 고른 방식은 `version.yml`에 기록되므로 다시 실행해도 묻지 않습니다. 방식을 바꾸면 **이전 워크플로우를 마법사가 정리합니다** — 손대지 않은 파일은 삭제하고, 수정한 파일은 `.bak`으로 옮겨 내용을 보존합니다. 남겨두면 배포가 두 번 돕니다.
73
71
  - PR 프리뷰는 배포 방식과 무관한 별개 축이라 선택과 관계없이 함께 설치됩니다 (단, `none`을 고르면 PR 프리뷰도 함께 제외됩니다 — 서버 배포 자체를 하지 않는 프로젝트를 위한 선택지입니다).
@@ -144,7 +142,7 @@ flutter.APP_ARTIFACT_NAME:
144
142
  **모노레포와 `ci-gate`**
145
143
 
146
144
  - `--paths flutter=app`처럼 Flutter 하위 폴더를 지정하면 모든 Flutter 워크플로우가 그 폴더(`FLUTTER_PROJECT_DIR`)를 기준으로 동작하고, main push로 도는 배포 워크플로우(`PLAYSTORE`·`IOS-TESTFLIGHT`·`SELFHOSTED`·`FIREBASE`)는 `app/**`가 바뀔 때만 실행됩니다(Spring 등 다른 타입과 같은 `paths` 필터).
147
- - CI 워크플로우(Flutter·Go·Next·Python·React·Spring NEXUS-CI)는 `push`·`pull_request`에서 항상 실행되고, 첫 job `changes`가 변경 파일을 판별해 나머지 job을 건너뜁니다(건너뛴 job은 Success). 마지막 job `ci-gate`는 항상 실행되어 필수 job이 모두 success 또는 skipped이면 통과하고 failure·cancelled가 하나라도 있으면 실패합니다.
145
+ - CI 워크플로우(Flutter·Go·Next·Python·React·Spring CI)는 `push`·`pull_request`에서 항상 실행되고, 첫 job `changes`가 변경 파일을 판별해 나머지 job을 건너뜁니다(건너뛴 job은 Success). 마지막 job `ci-gate`는 항상 실행되어 필수 job이 모두 success 또는 skipped이면 통과하고 failure·cancelled가 하나라도 있으면 실패합니다.
148
146
  - 브랜치 보호 규칙의 required status check에는 개별 job이 아니라 **`CI Gate`(`ci-gate`) 하나만** 등록하세요. 경로 필터로 워크플로우 자체를 건너뛰면 required check가 Pending에 머물러 머지가 막히지만, job을 건너뛰는 방식은 그렇지 않기 때문입니다.
149
147
  - 워크플로우 `paths` 필터는 태그 push에 적용되지 않습니다(GitHub 문서).
150
148
 
@@ -183,18 +181,23 @@ npx project-auto-wizard --mode uninstall --force # 워크플로우·스
183
181
  npx project-auto-wizard --mode uninstall --force --purge-readme --purge-gitignore --purge-version # 완전 삭제
184
182
  ```
185
183
 
186
- ## API 키 0개 AI — 요약 엔진 체인
184
+ ## 요약 엔진 체인
187
185
 
188
- 릴리스 노트는 3단 엔진 체인으로 생성됩니다. **상용 서드파티 서비스에 전혀 의존하지 않으며, 어떤 단계가 실패해도 릴리스는 절대 막히지 않습니다.**
186
+ 릴리스 노트는 3단 엔진 체인으로 생성됩니다. **어떤 단계가 실패해도 릴리스는 절대 막히지 않습니다.**
187
+
188
+ > GitHub Models는 2026-07-30에 종료되어 더 이상 사용하지 않습니다.
189
189
 
190
190
  ```mermaid
191
191
  flowchart LR
192
- B["사용자 지정 AI<br/>(AI_API_KEY)"] -->|"키 없음/실패"| C["GitHub Models<br/>(GITHUB_TOKEN만, 무료)"]
193
- C -->|"rate limit/실패"| D["규칙 기반 fallback<br/>(항상 성공)"]
192
+ B["사용자 지정 AI<br/>(AI_API_KEY + AI_API_BASE_URL + AI_MODEL)"] -->|"미설정/실패"| C["GitHub Copilot CLI<br/>(선택, GITHUB_TOKEN)"]
193
+ C -->|"꺼짐/사용 불가/실패"| D["규칙 기반 fallback<br/>(항상 성공)"]
194
194
  ```
195
195
 
196
- - 기본값은 **GitHub Models** — Actions의 `GITHUB_TOKEN` + `permissions: models: read`만으로 동작. **비용 0, 설정 0.**
197
- - `AI_API_KEY`/`AI_API_BASE_URL`/`AI_MODEL` secret으로 OpenAI-호환 엔드포인트(Groq, Gemini 호환 모드, Ollama 등) 교체 가능.
196
+ - 기본값은 **규칙 기반 요약**입니다. 설치 마법사에서 Copilot을 켜면(`--copilot`, `version.yml`의 `copilot_ai: true`) Actions의 `GITHUB_TOKEN` + `permissions: copilot-requests: write`로 Copilot CLI가 요약을 생성합니다 — 별도 API 키는 필요 없습니다.
197
+ - **Copilot은 GitHub Copilot AI Credits를 소비합니다.** 개인 저장소는 저장소 소유자의 Copilot 좌석에, 조직 저장소는 조직에 과금되며 조직은 "Allow use of Copilot CLI billed to the organization" 정책을 켜야 합니다. 사용할 수 없으면 자동으로 규칙 기반 요약으로 전환됩니다. PR에 푸시할 때마다 요약이 새로 생성되므로 그만큼 크레딧이 소비됩니다.
198
+ - Copilot 모델은 저비용 소형 모델(`claude-haiku-4.5`)로 고정되어 있고, 저장소 변수 `COPILOT_MODEL`로 바꿀 수 있습니다.
199
+ - GitHub은 Copilot CLI를 `run` 스텝에서 직접 호출하기보다 Agentic Workflows를 쓰라고 권고하지만, 이 프로젝트는 직접 호출을 택했습니다. 프롬프트 입력이 PR 제목·커밋 메시지·`git diff --stat`뿐이고, 빈 임시 디렉터리에서 shell/write/url 도구와 내장 MCP를 모두 막은 텍스트 생성 전용으로 호출하며, 포크 PR은 기존 가드로 건너뛰어 프롬프트 인젝션 위험을 낮췄기 때문입니다.
200
+ - `AI_API_KEY`(**Secret**)와 `AI_API_BASE_URL`·`AI_MODEL`(**Variables**)을 **모두** 설정하면 OpenAI-호환 엔드포인트(Groq, Gemini 호환 모드, Ollama 등)를 최우선으로 사용합니다. 셋 중 하나라도 없으면 이 단계는 건너뜁니다.
198
201
  - 규칙 fallback 3단: 프로젝트 컨벤션 → Conventional Commits → 무형식 bullet. 커밋 컨벤션이 없어도 동작.
199
202
 
200
203
  ## 릴리스 흐름
@@ -204,7 +207,7 @@ flowchart LR
204
207
  subgraph pr-flow ["pr-flow (기본)"]
205
208
  D1[develop push] --> PR[develop→main 릴리스 PR]
206
209
  PR --> V["버전 확정 (patch+1)"]
207
- V --> AI[AI 릴리스 노트]
210
+ V --> AI[릴리스 노트]
208
211
  AI --> CL[CHANGELOG.json/md 갱신]
209
212
  CL --> AM[automerge]
210
213
  AM --> TAG["tag vX.Y.Z + GitHub Release"]
@@ -233,9 +236,8 @@ npx project-auto-wizard [옵션]
233
236
  --flutter-store CSV Flutter 스토어 배포 대상: android,ios,none (미지정 시 둘 다 설치)
234
237
  --android-deploy-mode M Play Store 배포 모드: store_only | store_prepare | store_submit (기본: store_only)
235
238
  --ios-deploy-mode M iOS 배포 모드: store_only | store_prepare | store_submit (기본: store_only)
236
- --nexus 라이브러리 publish 워크플로우 포함 (Nexus + GitHub Packages)
237
- --secret-backup Secret 서버 백업 워크플로우 포함
238
239
  --semver-auto 커밋 타입 기반 자동 major/minor/patch 승격 (기본: 사용함, --no-semver-auto로 끔)
240
+ --copilot Copilot으로 AI 요약 생성 (기본: 사용 안 함, GitHub Copilot AI Credits 소비, --no-copilot으로 끔)
239
241
  --dry-run 실제 파일 변경 없이 무엇이 바뀔지만 미리 보여줌
240
242
  --purge-readme --mode uninstall --force 시 README.md 버전 섹션도 제거
241
243
  --purge-gitignore --mode uninstall --force 시 .gitignore 자동 추가 항목도 제거
@@ -270,9 +272,10 @@ npx project-auto-wizard --mode doctor # 환경 진단 (읽기 전용, 규칙
270
272
  secret이 없어도 폴백이 자동으로 이어받아 태그·Release까지 진행됩니다 — 실제 병합 후 최대 ~20초 정도 더 걸릴 뿐입니다.
271
273
  속도를 더 원한다면 PAT을 등록할 수 있습니다 — 반드시 개인 계정이 아닌 조직 bot/machine 계정으로 발급하세요 (scopes: repo, workflow).
272
274
  등록: 레포 Settings → Secrets and variables → Actions → New repository secret · 이름은 WORKFLOW_PAT
273
- [i] GitHub Models — AI 릴리스 노트 생성
274
- 조직 정책으로 차단됐는지는 자동으로 확인할 수 없습니다 (Settings → Models).
275
- 차단돼 있어도 규칙 기반 요약으로 자동 전환되므로 그대로 두셔도 됩니다.
275
+ [i] Copilot AI 요약 — AI 릴리스 노트 생성(선택)
276
+ 기본은 꺼져 있습니다 (version.yml의 copilot_ai: false).
277
+ 켜면 GitHub Copilot AI Credits가 소비됩니다 — 조직은 'Allow use of Copilot CLI billed to the organization' 정책이 필요합니다.
278
+ 꺼져 있거나 사용할 수 없으면 규칙 기반 요약으로 자동 전환되므로 그대로 두셔도 됩니다.
276
279
 
277
280
  ✓ 문제를 찾지 못했습니다.
278
281
  ```
@@ -296,7 +299,7 @@ npx project-auto-wizard --no-semver-auto # 항상 patch+1 (레거시 동작)
296
299
 
297
300
  ### 자체 AI PR 요약봇
298
301
 
299
- 상용 PR 리뷰 SaaS 없이 동작하는 자체 요약봇입니다. 릴리스 브랜치(`--main-branch`)를 대상으로 하는 PR이 열릴 때 API 키 0개 AI 엔진 체인으로 요약 코멘트를 자동으로 답니다.
302
+ 상용 PR 리뷰 SaaS 없이 동작하는 자체 요약봇입니다. 릴리스 브랜치(`--main-branch`)를 대상으로 하는 PR이 열릴 때 요약 엔진 체인(기본은 규칙 기반, Copilot은 선택)으로 요약 코멘트를 자동으로 답니다.
300
303
 
301
304
  기본 설치(pr-flow) 기준으로 일상적인 기능 PR은 `develop`을 대상으로 열리므로, 이 봇은 develop→main 릴리스 PR에서만 실제로 동작합니다 — 해당 PR에서는 `AUTO-CHANGELOG-CONTROL`이 이미 같은 엔진으로 체인지로그 요약을 생성하므로, 이 봇은 그 요약을 PR 코멘트 형태로도 남겨주는 보조 역할입니다. 릴리스 브랜치 = 개발 브랜치인 trunk-based 모드에서는 모든 PR이 곧 릴리스 대상 브랜치를 향하므로 매 PR마다 동작합니다.
302
305
 
@@ -308,7 +311,7 @@ npx project-auto-wizard --no-semver-auto # 항상 patch+1 (레거시 동작)
308
311
  |---|---|
309
312
  | **`WORKFLOW_PAT` secret** (선택 — 속도 최적화용) | 없어도 `GITHUB_TOKEN` 폴백이 automerge부터 Release 발행까지 자동으로 이어갑니다(실제 병합 후 최대 ~20초 추가). 더 빠르게 하고 싶다면 Settings → Secrets → Actions에 `WORKFLOW_PAT` (scopes: `repo`, `workflow`) 등록 — 반드시 개인 계정이 아닌 조직 bot/machine 계정으로 발급하세요 |
310
313
  | **Workflow permissions** | Settings → Actions → Workflow permissions: **Read and write** |
311
- | **GitHub Models** | 기본 활성 — 별도 설정 불필요. 조직 정책으로 차단된 경우 자동으로 규칙 fallback |
314
+ | **Copilot AI 요약** (선택) | 기본 꺼짐. 켜려면 마법사에서 선택하거나 `version.yml`의 `copilot_ai`를 `true`로 — AI Credits가 소비되며 조직은 "Allow use of Copilot CLI billed to the organization" 정책이 필요합니다. 사용할 수 없으면 자동으로 규칙 fallback |
312
315
 
313
316
  ## 설계 원칙
314
317
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "project-auto-wizard",
3
- "version": "0.11.0",
4
- "description": "One command DevOps: npx wizard that installs GitHub-native AI Release Automation into any project",
3
+ "version": "0.12.1",
4
+ "description": "One command DevOps: npx wizard that installs GitHub-native Release Automation (optional GitHub Copilot AI summaries) into any project",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {
@@ -18,7 +18,7 @@
18
18
  {
19
19
  "severity": "warning",
20
20
  "title": "모노레포 CI required check는 ci-gate 하나만 등록",
21
- "message": "CI 워크플로우(Flutter·Go·Next·Python·React·Spring NEXUS-CI)는 항상 실행되고 변경이 없으면 job을 건너뜁니다. required check로 쓰려면 개별 job이 아닌 CI Gate(ci-gate) 하나만 등록하세요."
21
+ "message": "CI 워크플로우(Flutter·Go·Next·Python·React·Spring CI)는 항상 실행되고 변경이 없으면 job을 건너뜁니다. required check로 쓰려면 개별 job이 아닌 CI Gate(ci-gate) 하나만 등록하세요."
22
22
  }
23
23
  ]
24
24
  }
@@ -27,7 +27,9 @@ import html
27
27
  import json
28
28
  import os
29
29
  import re
30
+ import subprocess
30
31
  import sys
32
+ import tempfile
31
33
  import traceback
32
34
  import urllib.error
33
35
  import urllib.request
@@ -123,7 +125,7 @@ def _parse_markdown_sections(md_content: str) -> dict:
123
125
  """
124
126
  섹션 파서: `### 카테고리` 헤딩 + `- 항목` 불릿 형식.
125
127
 
126
- AI 엔진 체인(사용자 지정 API → GitHub Models)과 규칙 기반 폴백이
128
+ AI 엔진 체인(사용자 지정 API → Copilot CLI)과 규칙 기반 폴백이
127
129
  동일하게 생성하는 형식이므로 1순위로 시도한다.
128
130
  """
129
131
  detected: dict[str, dict] = {}
@@ -389,21 +391,19 @@ def _ai_assisted_minor_upgrade(unclassified_lines: list[str]) -> bool:
389
391
  return False
390
392
  prompt = _BUMP_AI_PROMPT_PREFIX + "\n".join(f"- {line}" for line in unclassified_lines)
391
393
 
392
- ai_api_key = os.environ.get('AI_API_KEY')
393
- github_token = os.environ.get('GITHUB_TOKEN')
394
- candidates = [
395
- (ai_api_key, os.environ.get('AI_API_BASE_URL') or _AI_DEFAULT_BASE_URL, os.environ.get('AI_MODEL') or _AI_DEFAULT_MODEL),
396
- (github_token, _AI_DEFAULT_BASE_URL, _AI_DEFAULT_MODEL),
397
- ]
398
- for token, base_url, model in candidates:
399
- if not token:
400
- continue
394
+ settings = _user_api_settings()
395
+ if settings:
396
+ api_key, base_url, model = settings
401
397
  try:
402
- response = call_openai_compatible(base_url, token, model, prompt)
403
- return response.strip() == 'MINOR'
398
+ return call_openai_compatible(base_url, api_key, model, prompt).strip() == 'MINOR'
404
399
  except Exception as e:
405
400
  print(f"[warn] bump AI assist failed: {e}", file=sys.stderr)
406
- continue
401
+
402
+ if _copilot_enabled():
403
+ try:
404
+ return call_copilot_cli(prompt).strip() == 'MINOR'
405
+ except Exception as e:
406
+ print(f"[warn] bump AI assist (copilot) failed: {e}", file=sys.stderr)
407
407
  return False
408
408
 
409
409
 
@@ -464,7 +464,7 @@ def cmd_classify_bump(commits_file: str) -> int:
464
464
  def cmd_update_from_summary() -> int:
465
465
  """pr_body.md에서 Markdown을 파싱하여 CHANGELOG.json 갱신."""
466
466
  version = os.environ.get('VERSION')
467
- # PROJECT_TYPES(csv)가 유일한 입력 — 단수 PROJECT_TYPE 폴백은 제거됐다 (issue #62)
467
+ # PROJECT_TYPES(csv)가 유일한 입력 — 단수 PROJECT_TYPE 폴백은 제거됐다
468
468
  project_types_csv = os.environ.get('PROJECT_TYPES', '')
469
469
  project_types = [t.strip() for t in project_types_csv.split(',') if t.strip()]
470
470
  today = os.environ.get('TODAY')
@@ -694,8 +694,67 @@ def cmd_export_release_notes(version: str, output_path: str | None) -> int:
694
694
 
695
695
  # ------------------------ ai-summary 엔진 체인 ------------------------
696
696
 
697
- _AI_DEFAULT_BASE_URL = "https://models.github.ai/inference"
698
- _AI_DEFAULT_MODEL = "openai/gpt-4o-mini"
697
+ _COPILOT_DEFAULT_MODEL = "claude-haiku-4.5"
698
+ _COPILOT_TIMEOUT_SECONDS = 90
699
+
700
+
701
+ def _user_api_settings() -> tuple[str, str, str] | None:
702
+ """사용자 지정 AI 티어 설정. AI_API_KEY와 AI_API_BASE_URL, AI_MODEL이 모두 있어야 한다.
703
+
704
+ 키만 있고 URL·모델이 비어 있으면 그 키를 어디로도 보내지 않고 경고 후 건너뛴다
705
+ (종료된 기본 엔드포인트으로 사용자 키가 흘러가던 문제 방지)."""
706
+ api_key = os.environ.get('AI_API_KEY')
707
+ if not api_key:
708
+ return None
709
+ base_url = os.environ.get('AI_API_BASE_URL')
710
+ model = os.environ.get('AI_MODEL')
711
+ if not base_url or not model:
712
+ print(
713
+ "::warning::AI_API_KEY가 설정됐지만 AI_API_BASE_URL/AI_MODEL이 없어 사용자 API 티어를 건너뜁니다",
714
+ file=sys.stderr,
715
+ )
716
+ return None
717
+ return api_key, base_url, model
718
+
719
+
720
+ def _copilot_enabled() -> bool:
721
+ """version.yml의 copilot_ai가 켜져 있고(워크플로우가 COPILOT_AI로 전달) 토큰이 있을 때만 True."""
722
+ return os.environ.get('COPILOT_AI', '').strip().lower() == 'true' and bool(os.environ.get('GITHUB_TOKEN'))
723
+
724
+
725
+ def call_copilot_cli(prompt: str) -> str:
726
+ """Copilot CLI를 텍스트 생성 전용으로 호출해 응답 텍스트를 반환.
727
+
728
+ 프롬프트에 필요한 정보가 이미 다 들어 있으므로 에이전트 기능은 전부 막는다:
729
+ 빈 임시 디렉터리에서 실행하고, shell/write/url 도구를 거부하며, 내장 MCP와
730
+ 커스텀 지침 로딩을 끈다.
731
+ 실패(비정상 종료·타임아웃·CLI 없음)는 예외로 올려 호출부가 fallback한다."""
732
+ model = os.environ.get('COPILOT_MODEL') or _COPILOT_DEFAULT_MODEL
733
+ with tempfile.TemporaryDirectory() as workdir:
734
+ result = subprocess.run(
735
+ [
736
+ 'copilot', '-p', prompt, '-s',
737
+ '--no-ask-user', '--no-color', '--no-custom-instructions', '--disable-builtin-mcps',
738
+ '--deny-tool=shell', '--deny-tool=write', '--deny-tool=url',
739
+ '--model', model,
740
+ ],
741
+ cwd=workdir, stdin=subprocess.DEVNULL,
742
+ capture_output=True, text=True, timeout=_COPILOT_TIMEOUT_SECONDS,
743
+ )
744
+ if result.returncode != 0:
745
+ raise RuntimeError(f"copilot exited with {result.returncode}: {result.stderr.strip()[:200]}")
746
+ return result.stdout
747
+
748
+
749
+ def _is_valid_copilot_summary(text: str) -> bool:
750
+ """프롬프트가 요구한 Markdown 형식인지 최소한만 검사한다.
751
+
752
+ 섹션 헤딩('### ')이 하나도 없거나 코드펜스로 시작하는(통째로 감싼) 응답은 릴리스 노트로
753
+ 쓰지 않는다."""
754
+ stripped = text.strip()
755
+ if not stripped or stripped.startswith('```'):
756
+ return False
757
+ return any(line.startswith('### ') for line in stripped.splitlines())
699
758
 
700
759
 
701
760
  def _build_ai_prompt(commit_lines: list[str], pr_title: str | None, version: str, diff_stat: str | None = None) -> str:
@@ -760,16 +819,13 @@ def cmd_ai_summary(commits_file: str, version: str, output_path: str, pr_title:
760
819
  except Exception:
761
820
  diff_stat = None
762
821
 
763
- ai_api_key = os.environ.get('AI_API_KEY')
764
- ai_base_url = os.environ.get('AI_API_BASE_URL') or _AI_DEFAULT_BASE_URL
765
- ai_model = os.environ.get('AI_MODEL') or _AI_DEFAULT_MODEL
766
- github_token = os.environ.get('GITHUB_TOKEN')
767
-
768
822
  engine = None
769
823
  summary_text = None
770
824
  prompt = _build_ai_prompt(commit_lines, pr_title, version, diff_stat)
771
825
 
772
- if ai_api_key:
826
+ settings = _user_api_settings()
827
+ if settings:
828
+ ai_api_key, ai_base_url, ai_model = settings
773
829
  try:
774
830
  candidate = call_openai_compatible(ai_base_url, ai_api_key, ai_model, prompt)
775
831
  if candidate and candidate.strip():
@@ -780,19 +836,16 @@ def cmd_ai_summary(commits_file: str, version: str, output_path: str, pr_title:
780
836
  except Exception as e:
781
837
  print(f"[warn] user-api failed: {e}", file=sys.stderr)
782
838
 
783
- if summary_text is None and github_token:
839
+ if summary_text is None and _copilot_enabled():
784
840
  try:
785
- # GitHub Models는 자체 모델 카탈로그만 서빙한다 — 사용자 API용으로
786
- # AI_MODEL이 오버라이드돼 있어도 여기서는 기본 모델을 쓴다
787
- # (커스텀 모델명은 models.github.ai에서 404).
788
- candidate = call_openai_compatible(_AI_DEFAULT_BASE_URL, github_token, _AI_DEFAULT_MODEL, prompt)
789
- if candidate and candidate.strip():
841
+ candidate = call_copilot_cli(prompt)
842
+ if _is_valid_copilot_summary(candidate):
790
843
  summary_text = candidate
791
- engine = "github-models"
844
+ engine = "copilot"
792
845
  else:
793
- print("[warn] github-models failed: empty content in response", file=sys.stderr)
846
+ print("[warn] copilot failed: empty or not in the requested Markdown format", file=sys.stderr)
794
847
  except Exception as e:
795
- print(f"[warn] github-models failed: {e}", file=sys.stderr)
848
+ print(f"[warn] copilot failed: {e}", file=sys.stderr)
796
849
 
797
850
  if summary_text is None:
798
851
  classified = classify_commits(commit_lines)
@@ -160,7 +160,7 @@ def get_project_types_csv():
160
160
  # Inline array form: project_types: ["a", "b"] # trailing comment allowed
161
161
  # The template always appends "# first entry is primary", so anchoring at
162
162
  # end-of-line made this branch never match — every install silently fell
163
- # through to the singular key instead (issue #62).
163
+ # through to the singular key instead.
164
164
  m = re.search(r'^project_types:[ \t]*\[(.*?)\][ \t]*(?:#.*)?$', text, re.MULTILINE)
165
165
  if m:
166
166
  inner = m.group(1)
@@ -435,7 +435,7 @@ def sync_all_project_files(new_version):
435
435
  types = get_project_types_csv()
436
436
  if not types:
437
437
  # No silent fallback: an unreadable project_types used to degrade to
438
- # "basic" and skip every sync without a word (issue #62).
438
+ # "basic" and skip every sync without a word.
439
439
  raise SystemExit("ERROR: version.yml has no readable project_types — cannot sync project files")
440
440
  for t in types:
441
441
  sync_for_type(t, new_version, get_version_code)
@@ -6,6 +6,9 @@
6
6
  # (default) — feat: commits bump minor, a `!` breaking marker on any commit
7
7
  # type bumps major. Set semver_auto: false to always patch-bump instead
8
8
  # (then edit major/minor manually, e.g., 1.0.5 -> 1.1.0 or 2.0.0).
9
+ # - copilot_ai: opt-in (default false) — true lets the release/PR summary
10
+ # workflows call GitHub Copilot CLI, which consumes GitHub Copilot AI Credits.
11
+ # Falls back to rule-based summaries whenever Copilot is off or unavailable.
9
12
  # ===
10
13
  # Supported project types: spring, flutter, next, react, react-native,
11
14
  # react-native-expo, node, python, basic, go
@@ -44,9 +47,8 @@ metadata:
44
47
  develop: "{{DEVELOP_BRANCH}}"
45
48
  mode: "{{BRANCH_MODE}}" # pr-flow | trunk-based
46
49
  options:
47
- nexus: {{OPT_NEXUS}}
48
- secret_backup: {{OPT_SECRET_BACKUP}}
49
50
  semver_auto: {{OPT_SEMVER_AUTO}}
51
+ copilot_ai: {{OPT_COPILOT_AI}}
50
52
  deploy_style: "{{OPT_DEPLOY_STYLE}}" # simple | nginx | traefik | none (서버 배포 워크플로우)
51
53
  {{FLUTTER_OPTIONS}}
52
54
  {{DEPLOY}}
@@ -1,12 +1,10 @@
1
1
  # project-auto-wizard:managed-workflow
2
- # ===================================================================
3
2
  # PROJECT-COMMON-AI-PR-SUMMARY.yaml
4
3
  # Self-hosted AI PR summary bot — no third-party service required.
5
- # ===================================================================
6
4
  #
7
- # Posts an AI-generated summary comment on PRs targeting the default
8
- # branch, using the same zero-API-key engine chain as the release
9
- # changelog (user API key -> GitHub Models -> rule-based fallback).
5
+ # Posts a summary comment on PRs targeting the default branch, using the
6
+ # same engine chain as the release changelog (user API key -> Copilot CLI,
7
+ # opt-in via version.yml copilot_ai -> rule-based fallback).
10
8
  # The prompt includes commit subjects plus a capped `git diff --stat`.
11
9
  #
12
10
  # On release PRs (head branch == {{DEVELOP_BRANCH}}) this job is skipped.
@@ -18,7 +16,6 @@
18
16
  # that commit is pushed with GITHUB_TOKEN, which GitHub does not use
19
17
  # to retrigger pull_request events, so there would be no later run to
20
18
  # correct it.
21
- # ===================================================================
22
19
 
23
20
  name: PROJECT-AI-PR-SUMMARY
24
21
 
@@ -30,7 +27,11 @@ on:
30
27
  permissions:
31
28
  contents: read
32
29
  pull-requests: write
33
- models: read
30
+ copilot-requests: write
31
+
32
+ concurrency:
33
+ group: ai-pr-summary-${{ github.event.pull_request.number }}
34
+ cancel-in-progress: true
34
35
 
35
36
  jobs:
36
37
  ai-pr-summary:
@@ -46,6 +47,20 @@ jobs:
46
47
  fetch-depth: 0
47
48
  ref: ${{ github.event.pull_request.head.ref }}
48
49
 
50
+ - name: Read copilot_ai option from version.yml
51
+ id: copilot_options
52
+ run: |
53
+ # scoped to the metadata: -> template: -> options: chain so an
54
+ # unrelated "copilot_ai:" key can never hijack the value
55
+ COPILOT_AI=$(python3 -c 'import re; t=open("version.yml",encoding="utf-8").read(); m=re.search(r"metadata:.*?template:.*?options:.*?copilot_ai:\s*\"?(true|false)", t, re.S); print(m.group(1) if m else "false")' 2>/dev/null || echo "false")
56
+ echo "copilot_ai=$COPILOT_AI" >> $GITHUB_OUTPUT
57
+ echo "copilot_ai option: $COPILOT_AI"
58
+
59
+ - name: Install Copilot CLI
60
+ if: steps.copilot_options.outputs.copilot_ai == 'true'
61
+ continue-on-error: true
62
+ run: npm install -g @github/copilot@1.0.88
63
+
49
64
  - name: Generate and post AI summary
50
65
  env:
51
66
  PR_TITLE: ${{ github.event.pull_request.title }}
@@ -53,6 +68,8 @@ jobs:
53
68
  AI_API_BASE_URL: ${{ vars.AI_API_BASE_URL }}
54
69
  AI_MODEL: ${{ vars.AI_MODEL }}
55
70
  GITHUB_TOKEN: ${{ github.token }}
71
+ COPILOT_AI: ${{ steps.copilot_options.outputs.copilot_ai }}
72
+ COPILOT_MODEL: ${{ vars.COPILOT_MODEL }}
56
73
  run: |
57
74
  PR_NUMBER=${{ github.event.pull_request.number }}
58
75
  VERSION=$(python3 .github/scripts/version_manager.py get 2>/dev/null | tail -n 1)
@@ -68,17 +85,20 @@ jobs:
68
85
  fi
69
86
  rm -f _full_diff_stat.txt
70
87
 
71
- python3 .github/scripts/changelog_manager.py ai-summary \
88
+ SUMMARY_RESULT=$(python3 .github/scripts/changelog_manager.py ai-summary \
72
89
  --commits-file commits.txt \
73
90
  --version "$VERSION" \
74
91
  --output summary.md \
75
92
  --pr-title "$PR_TITLE" \
76
- --diff-stat-file diff_stat.txt
93
+ --diff-stat-file diff_stat.txt | tail -n 1)
94
+ ENGINE=$(python3 -c 'import json,sys; print(json.loads(sys.argv[1]).get("engine") or "unknown")' "$SUMMARY_RESULT" 2>/dev/null || echo "unknown")
77
95
 
78
96
  {
79
- echo "🤖 **AI Summary (project-auto-wizard)**"
97
+ echo "📋 **PR Summary (project-auto-wizard)**"
80
98
  echo ""
81
99
  cat summary.md
100
+ echo ""
101
+ echo "<sub>engine: ${ENGINE}</sub>"
82
102
  } > comment_body.md
83
103
 
84
104
  python3 -c 'import json; print(json.dumps({"body": open("comment_body.md", encoding="utf-8").read()}))' > comment_payload.json
@@ -1,8 +1,6 @@
1
1
  # project-auto-wizard:managed-workflow
2
- # ===================================================================
3
2
  # PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml
4
3
  # Release PR pipeline: version confirm + AI changelog + automerge
5
- # ===================================================================
6
4
  #
7
5
  # Runs on release PRs ({{DEVELOP_BRANCH}} -> {{MAIN_BRANCH}}) and:
8
6
  # 1. Confirms the release version and syncs every version file:
@@ -12,8 +10,8 @@
12
10
  # commits) and bumps accordingly.
13
11
  # - Otherwise: always patch+1 (legacy behavior).
14
12
  # 2. Generates the release summary locally with changelog_manager.py
15
- # ai-summary (engine chain: user API key -> GitHub Models -> rule-based
16
- # fallback; always succeeds, no third-party service involved)
13
+ # ai-summary (engine chain: user API key -> Copilot CLI, opt-in via
14
+ # version.yml copilot_ai -> rule-based fallback; always succeeds)
17
15
  # - The AI prompt includes commit subjects plus a capped
18
16
  # `git diff --stat` (file-level change summary, no diff body)
19
17
  # for richer context without token-cost blowup.
@@ -34,14 +32,15 @@
34
32
  # - secrets.WORKFLOW_PAT: PAT used for the merge. Without it the merge is
35
33
  # performed with GITHUB_TOKEN, and GitHub will NOT trigger downstream
36
34
  # {{MAIN_BRANCH}}-push workflows (e.g. RELEASE-PUBLISH) for that merge.
37
- # - secrets.AI_API_KEY (+ vars.AI_API_BASE_URL / vars.AI_MODEL): preferred
38
- # summary engine. Falls back to GitHub Models, then to rule-based.
35
+ # - secrets.AI_API_KEY (+ vars.AI_API_BASE_URL / vars.AI_MODEL, all three
36
+ # required together): preferred summary engine. Falls back to Copilot CLI
37
+ # (only when version.yml copilot_ai is true; optional vars.COPILOT_MODEL),
38
+ # then to rule-based.
39
39
  #
40
- # Issue #90: without WORKFLOW_PAT, a wait-for-merge-and-trigger-release job
40
+ # Without WORKFLOW_PAT, a wait-for-merge-and-trigger-release job
41
41
  # polls until the automerge actually lands, then wakes RELEASE-PUBLISH via
42
42
  # workflow_dispatch — GITHUB_TOKEN can always trigger that event, unlike
43
43
  # the push/release events blocked by GitHub's anti-recursion policy.
44
- # ===================================================================
45
44
 
46
45
  name: PROJECT-AUTO-CHANGELOG-CONTROL
47
46
 
@@ -60,7 +59,7 @@ on:
60
59
  permissions:
61
60
  contents: write
62
61
  pull-requests: write
63
- models: read
62
+ copilot-requests: write
64
63
 
65
64
  jobs:
66
65
  changelog-and-merge:
@@ -94,6 +93,20 @@ jobs:
94
93
  echo "semver_auto=$SEMVER_AUTO" >> $GITHUB_OUTPUT
95
94
  echo "semver_auto option: $SEMVER_AUTO"
96
95
 
96
+ - name: Read copilot_ai option from version.yml
97
+ id: copilot_options
98
+ run: |
99
+ # scoped to the metadata: -> template: -> options: chain so an
100
+ # unrelated "copilot_ai:" key can never hijack the value
101
+ COPILOT_AI=$(python3 -c 'import re; t=open("version.yml",encoding="utf-8").read(); m=re.search(r"metadata:.*?template:.*?options:.*?copilot_ai:\s*\"?(true|false)", t, re.S); print(m.group(1) if m else "false")' 2>/dev/null || echo "false")
102
+ echo "copilot_ai=$COPILOT_AI" >> $GITHUB_OUTPUT
103
+ echo "copilot_ai option: $COPILOT_AI"
104
+
105
+ - name: Install Copilot CLI
106
+ if: steps.copilot_options.outputs.copilot_ai == 'true'
107
+ continue-on-error: true
108
+ run: npm install -g @github/copilot@1.0.88
109
+
97
110
  - name: Collect commits since last release
98
111
  id: collect
99
112
  run: |
@@ -149,6 +162,8 @@ jobs:
149
162
  AI_API_BASE_URL: ${{ vars.AI_API_BASE_URL }}
150
163
  AI_MODEL: ${{ vars.AI_MODEL }}
151
164
  GITHUB_TOKEN: ${{ github.token }}
165
+ COPILOT_AI: ${{ steps.copilot_options.outputs.copilot_ai }}
166
+ COPILOT_MODEL: ${{ vars.COPILOT_MODEL }}
152
167
  run: |
153
168
  # Idempotency: if the PR head is already this workflow's own
154
169
  # version-confirm commit (re-run / synchronize retrigger), do not
@@ -175,6 +190,7 @@ jobs:
175
190
  echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
176
191
 
177
192
  - name: Generate summary with the AI engine chain
193
+ id: summary
178
194
  env:
179
195
  # PR title goes through the env block — never inline-interpolate
180
196
  # untrusted text into the shell string (quote injection).
@@ -183,28 +199,37 @@ jobs:
183
199
  AI_API_BASE_URL: ${{ vars.AI_API_BASE_URL }}
184
200
  AI_MODEL: ${{ vars.AI_MODEL }}
185
201
  GITHUB_TOKEN: ${{ github.token }}
202
+ COPILOT_AI: ${{ steps.copilot_options.outputs.copilot_ai }}
203
+ COPILOT_MODEL: ${{ vars.COPILOT_MODEL }}
186
204
  run: |
187
205
  VERSION=$(python3 .github/scripts/version_manager.py get | tail -n 1)
188
206
 
189
- python3 .github/scripts/changelog_manager.py ai-summary \
207
+ SUMMARY_RESULT=$(python3 .github/scripts/changelog_manager.py ai-summary \
190
208
  --commits-file commits.txt \
191
209
  --version "$VERSION" \
192
210
  --output summary.md \
193
211
  --pr-title "$PR_TITLE" \
194
- --diff-stat-file diff_stat.txt
212
+ --diff-stat-file diff_stat.txt | tail -n 1)
213
+ ENGINE=$(python3 -c 'import json,sys; print(json.loads(sys.argv[1]).get("engine") or "unknown")' "$SUMMARY_RESULT" 2>/dev/null || echo "unknown")
214
+ echo "summary engine: $ENGINE"
215
+ echo "engine=$ENGINE" >> $GITHUB_OUTPUT
195
216
 
196
217
  # update-from-summary reads ./pr_body.md
197
218
  cp summary.md pr_body.md
198
219
 
199
220
  - name: Post AI summary comment
200
221
  continue-on-error: true
222
+ env:
223
+ ENGINE: ${{ steps.summary.outputs.engine }}
201
224
  run: |
202
225
  PR_NUMBER=${{ github.event.pull_request.number }}
203
226
 
204
227
  {
205
- echo "🤖 **AI Summary (project-auto-wizard)**"
228
+ echo "📋 **PR Summary (project-auto-wizard)**"
206
229
  echo ""
207
230
  cat summary.md
231
+ echo ""
232
+ echo "<sub>engine: ${ENGINE:-unknown}</sub>"
208
233
  } > comment_body.md
209
234
 
210
235
  python3 -c 'import json; print(json.dumps({"body": open("comment_body.md", encoding="utf-8").read()}))' > comment_payload.json
@@ -1,18 +1,15 @@
1
1
  # project-auto-wizard:managed-workflow
2
- # ===================================================================
3
2
  # PROJECT-COMMON-ISSUE-HELPER.yaml
4
3
  # 이슈 생성/제목 수정 시 브랜치명·커밋 메시지를 댓글로 안내
5
- # ===================================================================
6
4
  #
7
5
  # 이슈가 열리거나(opened) 제목이 수정되면(edited + 제목 변경),
8
6
  # 정규화된 브랜치명과 커밋 메시지를 이슈 댓글로 생성/갱신한다.
9
7
  # ISSUE_HELPER_CREATE_BRANCH=true로 바꾸면 브랜치도 함께 생성한다
10
8
  # (기본값은 false — 이슈를 열 때마다 브랜치가 자동 생성되는 것을
11
- # 막기 위해 옵트인으로 전환됨, project-auto-wizard#68).
9
+ # 막기 위해 옵트인으로 전환됨).
12
10
  #
13
11
  # 로직 본체는 .github/scripts/issue_helper.py (Chuseok22/github-issue-helper의
14
- # Python 재작성, 동일 작성자 — project-auto-wizard#68).
15
- # ===================================================================
12
+ # Python 재작성, 동일 작성자).
16
13
 
17
14
  name: PROJECT-ISSUE-HELPER
18
15
 
@@ -23,7 +20,7 @@ on:
23
20
  # contents는 항상 write로 고정한다. ISSUE_HELPER_CREATE_BRANCH="true"일 때
24
21
  # git/refs API로 브랜치를 생성하려면 write가 필요한데, 이 값은 @wizard ask로
25
22
  # 설치 후에도 사용자가 직접 켤 수 있어(마법사 재실행 없이) 그 시점에 맞춰
26
- # permissions를 조건부로 올릴 방법이 없다 — project-auto-wizard#118.
23
+ # permissions를 조건부로 올릴 방법이 없다.
27
24
  permissions:
28
25
  issues: write
29
26
  contents: write