commitgate 0.4.0 → 0.8.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/AGENTS.template.md +8 -0
- package/CHANGELOG.md +154 -0
- package/README.en.md +187 -20
- package/README.md +187 -20
- package/bin/commitgate.mjs +15 -6
- package/bin/dispatch.d.mts +6 -0
- package/bin/dispatch.mjs +38 -0
- package/bin/init.ts +965 -84
- package/bin/migrate.ts +244 -0
- package/bin/uninstall.ts +48 -1
- package/package.json +73 -69
- package/req.config.json.sample +3 -0
- package/scripts/req/lib/adapters.ts +56 -8
- package/scripts/req/lib/config.ts +55 -0
- package/scripts/req/lib/porcelain.ts +104 -0
- package/scripts/req/lib/scratch.ts +104 -0
- package/scripts/req/req-commit.ts +21 -7
- package/scripts/req/req-doctor.ts +135 -31
- package/scripts/req/req-new.ts +94 -15
- package/scripts/req/req-next.ts +94 -17
- package/scripts/req/review-codex.ts +851 -94
- package/scripts/verify-review-overrides.mjs +96 -0
- package/skills/ATTRIBUTION.md +85 -0
- package/skills/commitgate-diagnosing-bugs/SKILL.md +149 -0
- package/skills/commitgate-discovery/SKILL.md +93 -0
- package/skills/commitgate-research/SKILL.md +85 -0
- package/skills/commitgate-tdd/SKILL.md +113 -0
- package/templates/CLAUDE.template.md +2 -1
- package/templates/claude-command.md +5 -2
- package/templates/claude-skill.md +4 -1
- package/templates/cursor-rule.mdc +5 -2
- package/templates/workflow.gitignore +8 -0
- package/workflow/machine.schema.json +10 -1
- package/workflow/req.config.schema.json +90 -10
- package/workflow/review-persona.md +56 -1
package/README.md
CHANGED
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
AI 에이전트가 코드를 빠르게 만들더라도, 리뷰 없이 바로 커밋되면 위험합니다. CommitGate는 변경을 티켓 단위로 묶고, Codex가 승인한 staged tree만 커밋되게 합니다. 승인 후 코드가 바뀌거나 증거가 부족하면 기본적으로 막습니다.
|
|
8
8
|
|
|
9
|
+
> **⚠️ 시작하기 전에 두 가지를 알아 두세요.**
|
|
10
|
+
>
|
|
11
|
+
> 1. **리뷰는 staged diff를 외부로 전송합니다.** `req:review-codex`는 `git diff --cached` **전문**을 Codex(OpenAI)로 보냅니다. codex는 `--sandbox read-only`로 저장소 루트를 읽으므로 diff에 없는 파일도 읽힐 수 있습니다. 마스킹·필터·길이 상한은 **없습니다.** 리뷰 전에 staged 내용에 자격증명·토큰·개인정보가 없는지 확인하세요.
|
|
12
|
+
> 2. **git hook을 설치하지 않습니다.** `req:commit` 대신 `git commit`을 직접 치면 게이트·승인 바인딩·증거 기록이 전부 우회됩니다. CommitGate의 강제력은 **협조하는 에이전트를 계약 궤도에 유지하는 것**에 있지, 사람의 우회를 막는 데 있지 않습니다.
|
|
13
|
+
|
|
9
14
|
[](https://github.com/sol5288/commitgate/actions/workflows/ci.yml)
|
|
10
15
|
[](https://www.npmjs.com/package/commitgate)
|
|
11
16
|
[](./LICENSE)
|
|
@@ -21,19 +26,38 @@ AI 에이전트가 코드를 빠르게 만들더라도, 리뷰 없이 바로 커
|
|
|
21
26
|
git init
|
|
22
27
|
npm init -y
|
|
23
28
|
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
# 1) CommitGate를 devDependency로 설치합니다 — 실행 코드가 여기 들어옵니다:
|
|
30
|
+
npm install -D commitgate
|
|
31
|
+
|
|
32
|
+
# 2) 프로젝트에 설정·계약·스키마와 req:* 스크립트를 깝니다:
|
|
33
|
+
npx commitgate init
|
|
34
|
+
|
|
27
35
|
codex --version
|
|
28
36
|
codex login status
|
|
29
37
|
```
|
|
30
38
|
|
|
39
|
+
> **왜 두 단계인가요?** CommitGate는 실행 코드를 프로젝트에 **복사하지 않습니다**. 1단계가 런타임을 `node_modules/commitgate`에 넣고, 2단계는 프로젝트에 **거버넌스 자산**(설정·계약·스키마·persona)과 `req:* = commitgate <verb>` 스크립트만 깝니다.
|
|
40
|
+
> 그래서 업데이트는 `npm update commitgate` 한 번이고, 런타임 제거는 `npm uninstall -D commitgate`입니다.
|
|
41
|
+
> `init`은 `devDependencies.commitgate` 선언이 없으면 **중단**합니다 — `req:*`가 가리킬 런타임이 없기 때문입니다.
|
|
42
|
+
|
|
43
|
+
설치는 파일을 놓기만 하고 커밋하지 않습니다. `req:new`는 **clean 워킹트리를 요구**하므로, 설치분을 먼저 커밋하세요. 설치 출력의 `다음:` 안내가 stage할 정확한 경로 목록을 알려 줍니다.
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
git add -- <설치 출력이 알려 준 경로들>
|
|
47
|
+
git status # 의도한 것만 staged 인지 눈으로 확인
|
|
48
|
+
git commit -m "chore: install commitgate"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> **전체를 담는 stage(`-A` / `.`)를 쓰지 마세요.** 기존 프로젝트의 무관한 변경과 `.env` 같은 미추적 파일이 함께 커밋되고, 이어지는 `req:review-codex`가 그 staged diff 전문을 외부로 전송합니다.
|
|
52
|
+
> 설치 전부터 있던 무관한 변경은 설치 커밋 뒤에 **경로를 명시해** 치우세요: `git stash push -u -- <경로들>`.
|
|
53
|
+
> `-u` 없이는 untracked가 남아 `req:new`가 막히고, 경로 없이 `git stash -u`만 쓰면 `node_modules/`처럼 무시되지 않은 디렉터리까지 딸려 갑니다. 설치 출력이 그 경로 목록도 알려 줍니다.
|
|
54
|
+
|
|
31
55
|
**긴 프롬프트를 붙여넣을 필요가 없습니다.** 설치가 에이전트 진입점을 함께 깝니다.
|
|
32
56
|
|
|
33
57
|
| 파일 | 읽는 도구 |
|
|
34
58
|
|---|---|
|
|
35
59
|
| `AGENTS.md` | Codex CLI, Cursor — **계약 정본** |
|
|
36
|
-
| `.claude/skills/commitgate/SKILL.md` | Claude Code (
|
|
60
|
+
| `.claude/skills/commitgate/SKILL.md` | Claude Code (자동 발견 — 호출은 모델 판단) |
|
|
37
61
|
| `.claude/commands/req.md` | Claude Code (`/req` 명시 호출) |
|
|
38
62
|
| `.cursor/rules/commitgate.mdc` | Cursor (`alwaysApply`) |
|
|
39
63
|
| `CLAUDE.md` | Claude Code (항상 로드) — 부재 시에만 생성 |
|
|
@@ -93,7 +117,7 @@ npm run req:next -- 2026-002
|
|
|
93
117
|
|
|
94
118
|
`req:review-codex`는 `workflow/review-persona.md`를 프롬프트 **첫 블록**으로 넣습니다. 사람이 직접 실행하든, Cursor가 실행하든, Claude가 실행하든 동일합니다 — 에이전트가 잊을 수 있는 자리에 두지 않습니다. 파일이 없거나 비어 있으면 리뷰가 fail-closed로 멈춥니다.
|
|
95
119
|
|
|
96
|
-
내용을 프로젝트에 맞게 고치거나, `req.config.json`의 `reviewPersonaPath`로 다른 파일을 지정할 수 있습니다. `null`로 두면
|
|
120
|
+
내용을 프로젝트에 맞게 고치거나, `req.config.json`의 `reviewPersonaPath`로 다른 파일을 지정할 수 있습니다. `null`로 두면 비활성화됩니다 — 다만 **delta design 리뷰에는 내장 delta 계약이 주입된다**(승인 baseline 이후 변경분만 재검토하도록 리뷰어에게 거는 계약이라, 설정 persona와 무관하게 붙습니다).
|
|
97
121
|
|
|
98
122
|
main에 반영하는 경로는 **PR 경유(선택)**와 **direct push** 둘 다 유효합니다. PR은 의무가 아닙니다. 다만 protected branch로 직접 push하면 required checks를 **우회**하므로 "branch protection bypass를 사용한 direct push 승인"을 따로 받아야 합니다 — bypass 권한이 있다는 사실은 승인이 아닙니다. 그리고 이때 CI는 push **이후에** 도는 **사후 검증**이라, 그 사실을 보고에서 생략하지 않습니다. tag, npm publish, GitHub release는 반영과 묶이지 않는 별도 통제점이고 CI green 이후에 요청합니다. 자세한 계약은 [AGENTS.template.md](AGENTS.template.md)와 [docs/RELEASING.md](docs/RELEASING.md)를 참고하세요.
|
|
99
123
|
|
|
@@ -114,24 +138,88 @@ CommitGate가 막는 것은 단순한 명령 실수가 아니라 **리뷰받지
|
|
|
114
138
|
|
|
115
139
|
한 줄로 말하면, **확실히 승인된 변경만 통과하고 애매하면 멈추는 방식**입니다.
|
|
116
140
|
|
|
141
|
+
### 보장하지 않는 것
|
|
142
|
+
|
|
143
|
+
방어선을 잘못 계산하지 않도록, 이 도구가 **하지 않는 일**을 분명히 해 둡니다.
|
|
144
|
+
|
|
145
|
+
- **하드 강제가 아닙니다.** git hook을 설치하지 않으므로 `req:commit` 대신 `git commit`을 직접 치면 doctor·승인 바인딩·증거 기록이 전부 우회됩니다. 운영 반영의 실제 방어선은 여전히 CI와 배포 파이프라인입니다.
|
|
146
|
+
- **staged 내용의 비밀을 지켜 주지 않습니다.** `req:review-codex`는 `git diff --cached` 전문을 Codex(OpenAI)로 전송하고, codex는 `--sandbox read-only`로 저장소 루트를 읽습니다. 마스킹·스크러빙·길이 상한이 없습니다. 결제·자격증명처럼 민감한 코드베이스라면 리뷰 전 staged diff를 육안으로 확인하는 절차를 계약(`AGENTS.md`)에 명문화하세요.
|
|
147
|
+
- **커밋 이후를 보장하지 않습니다.** 승인은 커밋 시점의 staged tree에 대한 것이고, 머지·태그·publish는 각각 별도 통제점입니다.
|
|
148
|
+
|
|
117
149
|
---
|
|
118
150
|
|
|
119
151
|
## 설치가 하는 일
|
|
120
152
|
|
|
121
|
-
`npx commitgate
|
|
153
|
+
`npx commitgate init`은 대상 프로젝트에 아래 파일과 설정을 추가합니다. 기존 파일은 기본적으로 덮어쓰지 않습니다.
|
|
122
154
|
|
|
123
155
|
| 추가 항목 | 설명 |
|
|
124
156
|
|---|---|
|
|
125
|
-
| `scripts/req/` | `req:new`, `req:next`, `req:review-codex`, `req:doctor`, `req:commit` 스크립트 |
|
|
126
157
|
| `workflow/*.schema.json` | Codex 응답과 설정 검증 스키마 |
|
|
127
|
-
| `workflow/review-persona.md` | Codex 리뷰 프롬프트에 주입되는 리뷰어 페르소나 |
|
|
158
|
+
| `workflow/review-persona.md` | Codex 리뷰 프롬프트에 주입되는 리뷰어 페르소나 (없을 때만 생성) |
|
|
128
159
|
| `req.config.json` | 프로젝트별 설정 |
|
|
129
160
|
| `AGENTS.md` | 계약 정본 (없을 때만 생성) |
|
|
130
161
|
| `CLAUDE.md` | Claude Code 지침 포인터 (없을 때만 생성) |
|
|
131
162
|
| `.claude/skills/commitgate/SKILL.md` | Claude Code 스킬 (포인터) |
|
|
132
163
|
| `.claude/commands/req.md` | `/req` 슬래시 커맨드 (포인터) |
|
|
133
164
|
| `.cursor/rules/commitgate.mdc` | Cursor 규칙 (포인터) |
|
|
134
|
-
|
|
|
165
|
+
| `.claude/skills/commitgate-*/SKILL.md` | **Companion Skills** 4종 — 아래 참조 (기존 파일 보존) |
|
|
166
|
+
| `package.json` 스크립트 | `req:new`·`req:next`·`req:review-codex`·`req:doctor`·`req:commit` = `commitgate <verb>` (없는 키만) |
|
|
167
|
+
|
|
168
|
+
### Companion Skills
|
|
169
|
+
|
|
170
|
+
CommitGate는 **거버넌스 레이어**입니다 — `req:next`가 다음 행동을 계산하고, 리뷰·승인·증거가 커밋을 게이트합니다.
|
|
171
|
+
그런데 "무엇을 만들지 정리하는 법", "테스트를 어떻게 먼저 쓰는지" 같은 **방법론**은 비어 있었습니다.
|
|
172
|
+
Matt Pocock의 공개 skills(MIT)를 CommitGate의 권한 경계에 맞게 적응해 4종을 함께 설치합니다.
|
|
173
|
+
|
|
174
|
+
| 스킬 | 언제 |
|
|
175
|
+
|---|---|
|
|
176
|
+
| `commitgate-discovery` | `req:new` **전** — 모호한 요구를 REQ Brief로 정리. **사용자 호출형** |
|
|
177
|
+
| `commitgate-tdd` | `req:next`가 `AGENT`일 때 — Red → Green → Refactor → stage |
|
|
178
|
+
| `commitgate-diagnosing-bugs` | 버그·회귀·성능 — 피드백 루프 → 재현·최소화 → 가설 → 계측 → 수정 |
|
|
179
|
+
| `commitgate-research` | 외부 기술 선택 — 1차 출처 조사, 결론·출처·한계 |
|
|
180
|
+
|
|
181
|
+
**자동 발견 · 모델 판단 호출.** harness가 스킬을 자동으로 **발견**하지만, 쓸지 **판단하는 건 모델**입니다 —
|
|
182
|
+
확률적이며 항상 뜬다고 기대하면 안 됩니다. Claude Code에서는 `/commitgate-<이름>`으로 **직접 호출**할 수도 있습니다.
|
|
183
|
+
다른 harness에서는 그 harness가 제공하는 호출 방식을 쓰거나, `AGENTS.md`의 진입 흐름을 따르세요.
|
|
184
|
+
|
|
185
|
+
**권장 흐름**: `commitgate-discovery`로 요구 정리 → `/req`(Claude Code) 또는 `AGENTS.md` 진입 → `req:new` → `req:next` 반복.
|
|
186
|
+
|
|
187
|
+
#### 경계 — 반드시 알아 두세요
|
|
188
|
+
|
|
189
|
+
- 🔴 **`AGENTS.md`가 계약 정본입니다.** 스킬은 **방법론**이지 계약이 아닙니다.
|
|
190
|
+
스킬을 설치하지 않아도 **핵심 워크플로는 완전히 동일하게** 동작합니다.
|
|
191
|
+
- 🔴 **스킬 결과는 승인 증거가 아닙니다.** companion skills의 산출물도, 외부 Matt skills를 따로 돌린 결과도
|
|
192
|
+
CommitGate·Codex의 **승인 근거가 되지 않습니다**. 리뷰 실행·승인 판정·상태 전이·커밋은 **CommitGate만** 담당하며,
|
|
193
|
+
다음 행동은 `req:next`가 정본입니다.
|
|
194
|
+
- 스킬은 **협조적 텍스트**입니다 — 스킬이 커밋을 막는 게 아니라, 막는 건 CommitGate의 게이트입니다.
|
|
195
|
+
|
|
196
|
+
#### 설치·보존·옵션
|
|
197
|
+
|
|
198
|
+
- **`--no-agent-entrypoints`**: `.claude/` 계층 전체를 건너뜁니다(companion 4종 포함).
|
|
199
|
+
- **기존 파일 보존(seed-once)**: 스킬은 **고치라고 만든 자산**입니다. 수정한 스킬은 **`--force`로도 덮어쓰지 않습니다.**
|
|
200
|
+
`AGENTS.md`·`CLAUDE.md`·`workflow/.gitignore`도 같은 정책입니다.
|
|
201
|
+
- **gitignore 경고**: `.claude/`를 gitignore하면 팀원의 fresh clone에 스킬이 전달되지 않습니다.
|
|
202
|
+
설치는 진행하되 **경고**하고 추적 방법을 안내합니다. **`--strict`면 설치 전에 중단**합니다.
|
|
203
|
+
- **타사 skill과 공존**: 타사 `tdd`·`grill-me` 등은 `.claude/skills/<이름>/`, companion은 `.claude/skills/commitgate-<이름>/` —
|
|
204
|
+
**경로가 달라 서로 건드리지 않습니다.**
|
|
205
|
+
|
|
206
|
+
#### 출처
|
|
207
|
+
|
|
208
|
+
Matt Pocock의 MIT 공개 skills를 기준 SHA `d574778f94cf620fcc8ce741584093bc650a61d3`에서 적응해
|
|
209
|
+
**패키지 payload로 포함**합니다. **외부 skill installer를 실행하거나 런타임 의존하지 않습니다** —
|
|
210
|
+
패키지 안에 고정된 사본입니다. 각 SKILL.md에 MIT 고지 전문이 동행하며, 자세한 출처는 패키지의
|
|
211
|
+
`skills/ATTRIBUTION.md`에 있습니다.
|
|
212
|
+
|
|
213
|
+
### 설치하지 **않는** 것
|
|
214
|
+
|
|
215
|
+
| 항목 | 어디에 있나 |
|
|
216
|
+
|---|---|
|
|
217
|
+
| `scripts/req/**` 실행 코드 | `node_modules/commitgate` — 프로젝트에 복사하지 않습니다 |
|
|
218
|
+
| `tsx` · `ajv` · `cross-spawn` | `commitgate` 패키지의 runtime dependency — 대상 `package.json`에 주입하지 않습니다 |
|
|
219
|
+
|
|
220
|
+
프로젝트에 남는 것은 **거버넌스·감사 데이터**(설정·계약·스키마·persona·`workflow/REQ-*` 증거)뿐입니다. 실행 코드는 패키지에만 있으므로 `npm update commitgate` 한 번으로 갱신되고, 복사본 버전이 갈라지지 않습니다.
|
|
221
|
+
|
|
222
|
+
`req:*` 스크립트는 설치된 패키지 bin을 호출합니다 — `npm run req:new -- <slug>` → `commitgate req:new <slug>` → `node_modules/.bin/commitgate`.
|
|
135
223
|
|
|
136
224
|
진입점 파일들은 **얇은 포인터**입니다. 계약 본문은 `AGENTS.md` 하나에만 있습니다.
|
|
137
225
|
|
|
@@ -149,21 +237,90 @@ npx commitgate --no-agent-entrypoints
|
|
|
149
237
|
npx commitgate --dry-run
|
|
150
238
|
```
|
|
151
239
|
|
|
152
|
-
|
|
240
|
+
정합성 경고를 설치 실패로 취급하려면:
|
|
153
241
|
|
|
154
242
|
```sh
|
|
155
243
|
npx commitgate --strict
|
|
156
244
|
```
|
|
157
245
|
|
|
158
|
-
|
|
246
|
+
**파일을 하나도 쓰기 전에** 중단합니다. 대상은 다음과 같습니다.
|
|
247
|
+
|
|
248
|
+
- 계약 포인터(`.claude/`·`.cursor/`·`AGENTS.md`·`CLAUDE.md`)가 `.gitignore`에 걸려 팀·CI에 공유되지 않을 때
|
|
249
|
+
- `workflow/.gitignore` 정책 파일이 무시돼 fresh clone·CI에 scratch 규칙이 전달되지 않을 때
|
|
250
|
+
- 설치 전 워킹트리에 staged 변경이 있거나 설치 산출물과 겹치는 수정이 있어, 설치분만 담은 커밋을 만들 수 없을 때
|
|
251
|
+
- 기존 `cross-spawn`이 검증 하한보다 낮을 때(프로젝트가 그 패키지를 이미 쓰는 경우)
|
|
252
|
+
|
|
253
|
+
> `--strict`는 **선행 `npm install -D commitgate`가 남긴 `package.json`·lockfile 변경도** preexisting-dirty로 봅니다. 권장 순서: `npm i -D commitgate` → **커밋** → `npx commitgate init --strict` → 설치분 커밋.
|
|
159
254
|
|
|
160
255
|
> `workflow/machine.schema.json`과 `workflow/req.config.schema.json`은 `req.config.json`의 `ticketRoot` 설정과 무관하게 **항상 `workflow/` 아래**에 복사됩니다.
|
|
161
256
|
|
|
162
257
|
---
|
|
163
258
|
|
|
259
|
+
## 예전 설치본에서 옮겨오기 (`migrate`)
|
|
260
|
+
|
|
261
|
+
`scripts/req/`가 프로젝트에 복사돼 있고 `req:*`가 `tsx scripts/req/*.ts`를 가리킨다면 **예전(vendored) 설치본**입니다. `init`은 이 상태를 감지하면 조용히 섞이지 않도록 **중단하고** 이 명령을 안내합니다.
|
|
262
|
+
|
|
263
|
+
```sh
|
|
264
|
+
npm install -D commitgate # 아직 devDependency가 아니라면 먼저
|
|
265
|
+
npx commitgate migrate # 계획만 출력 — 아무것도 쓰지 않습니다
|
|
266
|
+
npx commitgate migrate --apply # package.json 의 req:* 만 전환
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
`migrate`가 하는 일은 **하나**입니다: `req:*` 중 **현재 값이 정확히 예전 주입값인 키만** `commitgate <verb>`로 바꿉니다.
|
|
270
|
+
|
|
271
|
+
- **아무것도 삭제하지 않습니다.** `scripts/req/`·스키마·persona·설정·진입점·`workflow/REQ-*` 증거를 전부 그대로 둡니다. 남은 `scripts/req/`는 더 이상 실행되지 않으니, 정리하려면 `npx commitgate uninstall` 계획을 먼저 확인하세요.
|
|
272
|
+
- **직접 고친 스크립트는 덮어쓰지 않습니다.** 값이 한 글자라도 다르면 사용자 값으로 보고 보존한 뒤 수동 조치를 안내합니다.
|
|
273
|
+
- **커밋하지 않습니다.** `package.json` 한 파일만 쓰고, 검토는 사용자 몫입니다.
|
|
274
|
+
|
|
275
|
+
`req:doctor`도 설치 모드(예전/현재/혼합)를 진단해 알려 줍니다.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## 지원 범위
|
|
280
|
+
|
|
281
|
+
| 환경 | 상태 |
|
|
282
|
+
|---|---|
|
|
283
|
+
| **npm** | 완전 지원 — 매 릴리스 packed tarball smoke로 검증합니다 |
|
|
284
|
+
| **pnpm · yarn** (`node_modules` linker) | 지원 — `node_modules/.bin/commitgate`로 해소되는 표준 경로를 씁니다 |
|
|
285
|
+
| **Yarn PnP** | **이번 릴리스 미지원**(검증하지 않았습니다). `nodeLinker: node-modules`를 쓰세요 |
|
|
286
|
+
| **workspace/monorepo** | **워크스페이스 root 설치**를 지원합니다(root에 `req.config.json`·`workflow/`). 하위 패키지에 독립 설치하는 배치는 미지원 |
|
|
287
|
+
|
|
288
|
+
**재현성**: `req.config.json`의 리뷰 모델·추론강도 핀과 스키마·persona가 프로젝트에 남아 과거 리뷰 입력이 git 이력으로 재현됩니다. 런타임 버전은 **lockfile이 고정**하므로 `package-lock.json`(pnpm/yarn은 각 lockfile)을 **커밋하세요**.
|
|
289
|
+
|
|
290
|
+
### Companion Skills 발견 범위
|
|
291
|
+
|
|
292
|
+
**설치는 모든 환경에서 동일합니다.** 아래는 harness가 그 파일을 **발견하는지**에 대한 것입니다.
|
|
293
|
+
|
|
294
|
+
| harness | 발견 |
|
|
295
|
+
|---|---|
|
|
296
|
+
| **Claude Code** | `.claude/skills/<이름>/SKILL.md`를 native로 읽습니다 |
|
|
297
|
+
| **Cursor (editor)** | `.claude/skills`를 호환 경로로 읽습니다 |
|
|
298
|
+
| **Cursor (CLI)** | ⚠️ **버전·실행 모드별 동작 차이 가능 — 보장하지 않습니다** |
|
|
299
|
+
| **Codex** | **제품 범위 밖** — companion entrypoint를 설치하지 않습니다. CommitGate에서 Codex는 **Reviewer**이고 이 4종은 **Builder 보조**입니다 |
|
|
300
|
+
|
|
301
|
+
⚠️ **근거는 벤더 1차 문서입니다 — CommitGate 팀이 실측한 것이 아닙니다.** 확인 시점 **2026-07-17**, 확인 환경 win32 x64 / Node v20.19.5.
|
|
302
|
+
벤더가 동작을 바꾸면 이 표는 낡을 수 있습니다.
|
|
303
|
+
|
|
304
|
+
⚠️ **Cursor CLI를 "지원"·"미지원" 어느 쪽으로도 단정하지 않습니다.** Cursor는 editor·CLI 양쪽의 Agent Skills 지원을
|
|
305
|
+
발표했지만, `.claude/skills` 호환 경로의 CLI 발견은 버전·모드에 따라 차이가 보고돼 있고 우리는 검증하지 못했습니다.
|
|
306
|
+
발견되지 않아도 **핵심 워크플로에는 영향이 없습니다** — 스킬은 품질 보조 레이어이고 계약 정본은 `AGENTS.md`입니다.
|
|
307
|
+
|
|
308
|
+
우회를 위해 `.cursor/skills`에 **이중 설치하지 않습니다** — 그 경로도 CLI에서 동작이 불확실하고, 같은 내용이 두 곳에
|
|
309
|
+
깔리면 drift 위험이 생깁니다. 벤더가 고치면 **우리 변경 없이** 동작합니다(같은 경로를 읽으므로).
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
164
313
|
## 제거하려면
|
|
165
314
|
|
|
166
|
-
|
|
315
|
+
CommitGate는 두 곳에 있습니다. **런타임**(`node_modules/commitgate`)과 **프로젝트에 깔린 거버넌스 파일**입니다.
|
|
316
|
+
|
|
317
|
+
런타임은 package manager가 지웁니다:
|
|
318
|
+
|
|
319
|
+
```sh
|
|
320
|
+
npm uninstall -D commitgate # pnpm remove -D commitgate · yarn remove commitgate
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
프로젝트 파일은 아래 계획을 보고 직접 정리하세요. 먼저 알아둘 것: **`npx commitgate`는 전역 설치가 아닙니다.** npx는 패키지를 npm 캐시(`_npx/<hash>/`)에 받아 한 번 실행할 뿐이고, 전역 `node_modules`에도 PATH에도 아무것도 남기지 않습니다.
|
|
167
324
|
|
|
168
325
|
제거 계획을 먼저 확인하세요. 이 명령은 **아무것도 지우지 않고** 계획만 출력합니다:
|
|
169
326
|
|
|
@@ -302,11 +459,14 @@ npm run req:commit -- 2026-001 --run --message-file commit-message.txt
|
|
|
302
459
|
|
|
303
460
|
| 명령 | 용도 |
|
|
304
461
|
|---|---|
|
|
305
|
-
| `
|
|
306
|
-
| `npx commitgate
|
|
307
|
-
| `npx commitgate --
|
|
308
|
-
| `npx commitgate --
|
|
462
|
+
| `npm install -D commitgate` | **런타임 설치 (선행 필수)** — 실행 코드가 `node_modules/commitgate`에 들어옵니다 |
|
|
463
|
+
| `npx commitgate init` | 프로젝트에 설정·계약·스키마와 `req:*` 스크립트 설치 |
|
|
464
|
+
| `npx commitgate init --dry-run` | 파일을 쓰지 않고 설치 계획 확인 |
|
|
465
|
+
| `npx commitgate init --strict` | 정합성 경고를 설치 실패로 처리 (gitignore된 계약 포인터, 설치 커밋을 안전하게 만들 수 없는 워킹트리 등) — 파일을 하나도 쓰기 전에 중단 |
|
|
466
|
+
| `npx commitgate init --no-agent-entrypoints` | `.claude/`·`.cursor/`·`CLAUDE.md` 설치 건너뛰기 |
|
|
467
|
+
| `npx commitgate migrate [--apply]` | 예전 vendored 설치본 → 런타임 패키지 전환 (기본: 계획만, 비파괴) |
|
|
309
468
|
| `npx commitgate uninstall` | 제거 계획 확인 (읽기 전용 — 아무것도 지우지 않음) |
|
|
469
|
+
| `npm uninstall -D commitgate` | 런타임 제거 |
|
|
310
470
|
| `npm run req:new -- <slug> --run` | REQ 티켓, 브랜치, 설계문서 생성 |
|
|
311
471
|
| `npm run req:next -- <id> [--json]` | **다음 행동 계산** (읽기 전용) |
|
|
312
472
|
| `npm run req:review-codex -- <id> --kind design --run` | 설계 리뷰 |
|
|
@@ -334,10 +494,16 @@ yarn req:next 2026-002 # yarn
|
|
|
334
494
|
| `ticketRoot` | `"workflow"` | REQ 티켓 폴더 |
|
|
335
495
|
| `packageManager` | 자동 감지 | `npm`, `pnpm`, `yarn` |
|
|
336
496
|
| `designDocs` | `00/01/02` 문서 | 설계 문서 파일명 |
|
|
337
|
-
| `reviewPersonaPath` | `"workflow/review-persona.md"` | 리뷰 프롬프트 첫 블록. `null`이면 비활성 |
|
|
497
|
+
| `reviewPersonaPath` | `"workflow/review-persona.md"` | 리뷰 프롬프트 첫 블록. `null`이면 비활성 — 단 delta design 리뷰에는 내장 delta 계약이 주입된다 |
|
|
498
|
+
| `reviewModel` | `"gpt-5.6-terra"` | codex 리뷰 모델(`-c model=`로 고정). `null`이면 codex 전역 설정을 상속 |
|
|
499
|
+
| `reviewReasoningEffort` | `"high"` | codex 리뷰 추론강도. `none`·`minimal`·`low`·`medium`·`high`·`xhigh` 중 하나. `null`이면 전역 상속 |
|
|
338
500
|
|
|
339
501
|
빈 `branchPrefix`나 프로젝트 밖으로 나가는 경로는 거부됩니다.
|
|
340
502
|
|
|
503
|
+
**리뷰 모델·추론강도 고정**: `req:review-codex`는 codex 인자에 `-c model=`·`-c model_reasoning_effort=`를 주입해 **모델과 추론강도를 고정**합니다. 고정하지 않으면 리뷰가 사용자 전역 `~/.codex/config.toml`(예: `model_reasoning_effort="ultra"`)을 상속해 리뷰 1회가 수 분·토큰 과다가 됩니다. 기본값은 `gpt-5.6-terra`/`high`이고, 프로젝트의 codex가 그 모델을 지원하지 않으면 `req.config.json`에서 바꾸거나 `null`로 두어 전역 설정을 상속시킵니다. override가 실제로 존중되는지는 `npm run verify:overrides`(codex CLI 필요)로 확인할 수 있습니다.
|
|
504
|
+
|
|
505
|
+
**재리뷰는 stateless**: 재리뷰는 매번 **새 codex 스레드**로 시작합니다(이전 대화를 resume해 누적하지 않음 — 토큰 증가와 findings 심화·이동을 막습니다). 직전 같은 대상의 NEEDS_FIX findings만 참고용으로 프롬프트에 담겨 해소 여부(closure)를 확인합니다.
|
|
506
|
+
|
|
341
507
|
---
|
|
342
508
|
|
|
343
509
|
## FAQ
|
|
@@ -361,17 +527,18 @@ yarn req:next 2026-002 # yarn
|
|
|
361
527
|
|
|
362
528
|
## 현재 범위
|
|
363
529
|
|
|
364
|
-
현재 버전은
|
|
530
|
+
현재 버전은 **런타임 패키지 모델**입니다. 실행 코드와 런타임 의존성은 `node_modules/commitgate`에만 있고, 프로젝트에는 거버넌스·감사 데이터와 `req:* = commitgate <verb>` 스크립트만 남습니다. (예전 vendored scaffold 설치본은 [`migrate`](#예전-설치본에서-옮겨오기-migrate)로 전환합니다.)
|
|
365
531
|
|
|
366
532
|
현재 운영 중인 검증입니다.
|
|
367
533
|
|
|
368
534
|
- GitHub Actions에서 `ubuntu-latest`, `macos-latest`, `windows-latest` × Node 18/20/22 매트릭스를 실행합니다.
|
|
369
|
-
- `npm run smoke`는 pack tarball
|
|
535
|
+
- `npm run smoke`는 pack tarball을 임시 프로젝트에 실제로 설치해, 대상에 `scripts/req/`가 **없고** `tsx`·`ajv`·`cross-spawn`이 **주입되지 않으며** 다섯 `req:*`가 패키지 bin을 가리키는지, 그리고 `npm run req:doctor`가 실제로 패키지 안의 모듈까지 dispatch되는지 확인합니다. `migrate` 비파괴성도 같은 방식으로 검증합니다.
|
|
370
536
|
- Windows `.cmd` 래퍼 주입 회귀 테스트가 패키지 매니저와 Codex wrapper 경로를 보호합니다.
|
|
371
537
|
|
|
372
538
|
아래는 후속 범위입니다.
|
|
373
539
|
|
|
374
|
-
-
|
|
540
|
+
- Yarn PnP 지원, 워크스페이스 하위 패키지 독립 설치
|
|
541
|
+
- 자산↔런타임 버전 드리프트 탐지
|
|
375
542
|
- 비-git VCS 지원
|
|
376
543
|
- 더 다양한 설계문서 템플릿
|
|
377
544
|
|
package/bin/commitgate.mjs
CHANGED
|
@@ -10,20 +10,29 @@
|
|
|
10
10
|
* (node:module의 register('tsx/esm')는 tsx v4가 deprecated --loader로 간주해 거부 → tsx 자체 API 사용.)
|
|
11
11
|
* (Stage B에서 init.ts를 JS로 빌드하면 이 런처는 제거 가능.)
|
|
12
12
|
*
|
|
13
|
-
* verb dispatch(REQ-2026-
|
|
14
|
-
*
|
|
13
|
+
* verb dispatch(REQ-2026-014 Stage B, 설계 D3): 로컬 패키지 bin이 `req:*`를 dispatch한다.
|
|
14
|
+
* - 알려진 verb(`req:new`/`req:next`/`req:review-codex`/`req:doctor`/`req:commit`/`uninstall`/`init`) → 해당 모듈(verb 토큰 소비).
|
|
15
|
+
* - argv 없음 **또는 첫 인자가 `-` 옵션**(`--dry-run`·`--dir`·`--strict`·`--force`·`--no-agent-entrypoints`·`-h`) → **init에 argv 전체 전달**(하위호환).
|
|
16
|
+
* - 그 외 비-옵션 미지 토큰 → fail-closed(오타를 조용히 init으로 보내지 않는다). `migrate`는 Phase 3에서 등록.
|
|
17
|
+
* 각 대상 모듈은 `runCli(argv)`(예외→친절한 1줄+exit1 경계)를 export한다. import되면 대상의 `if (isMain)` 가드는 발화하지 않으므로 중복 실행 없음.
|
|
15
18
|
*/
|
|
16
19
|
import { register } from 'tsx/esm/api'
|
|
17
20
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
18
21
|
import { dirname, join } from 'node:path'
|
|
22
|
+
import { resolveDispatch } from './dispatch.mjs'
|
|
19
23
|
|
|
20
24
|
register()
|
|
21
25
|
|
|
22
26
|
const binDir = dirname(fileURLToPath(import.meta.url))
|
|
23
27
|
const argv = process.argv.slice(2)
|
|
24
|
-
const entry = argv[0] === 'uninstall' ? 'uninstall.ts' : 'init.ts'
|
|
25
|
-
const rest = argv[0] === 'uninstall' ? argv.slice(1) : argv
|
|
26
28
|
|
|
27
|
-
const
|
|
29
|
+
const decision = resolveDispatch(argv)
|
|
30
|
+
if ('unknown' in decision) {
|
|
31
|
+
// 비-옵션 미지 토큰: fail-closed(스택트레이스 없이 한 줄).
|
|
32
|
+
console.error(`commitgate: 알 수 없는 명령: ${decision.unknown}`)
|
|
33
|
+
process.exit(1)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const mod = await import(pathToFileURL(join(binDir, decision.entry)).href)
|
|
28
37
|
// runCli = 예외를 친절한 한 줄 메시지 + exit 1로 변환하는 CLI 경계(스택트레이스 노출 방지).
|
|
29
|
-
mod.runCli(rest)
|
|
38
|
+
mod.runCli(decision.rest)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** 타입 선언 — `bin/dispatch.mjs`(런타임은 tsx 없이 로드돼야 하므로 순수 .mjs로 유지)의 타입. */
|
|
2
|
+
export declare const VERB_MODULES: Record<string, string>
|
|
3
|
+
|
|
4
|
+
export type DispatchDecision = { entry: string; rest: string[] } | { unknown: string }
|
|
5
|
+
|
|
6
|
+
export declare function resolveDispatch(argv: string[]): DispatchDecision
|
package/bin/dispatch.mjs
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commitgate bin verb dispatch — 순수 결정 로직(부작용 없음, 테스트 가능).
|
|
3
|
+
*
|
|
4
|
+
* `bin/commitgate.mjs`(register tsx·동적 import 부작용 포함)와 `tests/unit/dispatch.test.ts`가 공유한다.
|
|
5
|
+
* 설계 D3(REQ-2026-014 Stage B):
|
|
6
|
+
* - 알려진 verb → 해당 모듈(verb 토큰 소비).
|
|
7
|
+
* - argv 없음 **또는 첫 인자가 `-` 옵션** → init에 argv 전체 전달(하위호환: `npx commitgate --dry-run` 등).
|
|
8
|
+
* - 그 외 비-옵션 미지 토큰 → `unknown`(호출부가 fail-closed).
|
|
9
|
+
*
|
|
10
|
+
* `migrate`는 **파일 생성과 동시에**(Phase 3) 등록했다. Phase 1이 미리 등록하지 않은 이유는, 등록만 하고 모듈이 없으면
|
|
11
|
+
* 깨진 동적 import가 raw unhandled rejection으로 터지기 때문이다(`unknown` 분기의 친절한 1줄 오류가 낫다).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** verb → 대상 모듈(binDir 기준 상대). req:* 는 패키지의 scripts/req/*.ts 에서 실행(Stage B: 대상 프로젝트에 복사하지 않음). */
|
|
15
|
+
export const VERB_MODULES = {
|
|
16
|
+
'req:new': '../scripts/req/req-new.ts',
|
|
17
|
+
'req:next': '../scripts/req/req-next.ts',
|
|
18
|
+
'req:review-codex': '../scripts/req/review-codex.ts',
|
|
19
|
+
'req:doctor': '../scripts/req/req-doctor.ts',
|
|
20
|
+
'req:commit': '../scripts/req/req-commit.ts',
|
|
21
|
+
uninstall: 'uninstall.ts',
|
|
22
|
+
migrate: 'migrate.ts',
|
|
23
|
+
init: 'init.ts',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* argv → { entry, rest } 또는 { unknown }.
|
|
28
|
+
* @param {string[]} argv process.argv.slice(2)
|
|
29
|
+
* @returns {{ entry: string, rest: string[] } | { unknown: string }}
|
|
30
|
+
*/
|
|
31
|
+
export function resolveDispatch(argv) {
|
|
32
|
+
const verb = argv[0]
|
|
33
|
+
// verb 없음 / init 옵션(`-`로 시작) → init에 argv 전체 전달(D3).
|
|
34
|
+
if (verb === undefined || verb.startsWith('-')) return { entry: 'init.ts', rest: argv }
|
|
35
|
+
if (Object.prototype.hasOwnProperty.call(VERB_MODULES, verb)) return { entry: VERB_MODULES[verb], rest: argv.slice(1) }
|
|
36
|
+
// 비-옵션 미지 토큰: 호출부가 fail-closed 처리(오타를 조용히 init으로 보내지 않는다).
|
|
37
|
+
return { unknown: verb }
|
|
38
|
+
}
|