commitgate 0.4.0 → 0.7.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 +115 -0
- package/README.en.md +112 -17
- package/README.md +117 -17
- package/bin/commitgate.mjs +15 -6
- package/bin/dispatch.d.mts +6 -0
- package/bin/dispatch.mjs +38 -0
- package/bin/init.ts +788 -78
- package/bin/migrate.ts +244 -0
- package/bin/uninstall.ts +43 -1
- package/package.json +6 -3
- package/req.config.json.sample +2 -0
- package/scripts/req/lib/adapters.ts +56 -8
- package/scripts/req/lib/config.ts +25 -0
- package/scripts/req/lib/porcelain.ts +104 -0
- package/scripts/req/lib/scratch.ts +104 -0
- package/scripts/req/req-commit.ts +16 -3
- package/scripts/req/req-doctor.ts +135 -31
- package/scripts/req/req-new.ts +60 -10
- package/scripts/req/req-next.ts +33 -17
- package/scripts/req/review-codex.ts +198 -68
- package/scripts/verify-review-overrides.mjs +96 -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 +5 -1
- package/workflow/req.config.schema.json +5 -0
- package/workflow/review-persona.md +22 -1
package/AGENTS.template.md
CHANGED
|
@@ -46,6 +46,14 @@
|
|
|
46
46
|
|
|
47
47
|
> PR을 생략할 수 있다는 것과, 우회를 보고하지 않아도 된다는 것은 다르다. 경로는 선택이지만 **투명성은 선택이 아니다.**
|
|
48
48
|
|
|
49
|
+
### 6. 리뷰 전 staged 내용 확인 (외부 전송)
|
|
50
|
+
|
|
51
|
+
`req:review-codex`는 `git diff --cached` **전문**을 Codex(OpenAI)로 전송한다. codex는 `--sandbox read-only`로 저장소 루트를 읽으므로 diff에 없는 파일(`.env` 등)도 읽힐 수 있다. 마스킹·스크러빙·길이 상한은 **없다.**
|
|
52
|
+
|
|
53
|
+
- 리뷰를 실행하기 전에 staged 내용에 자격증명·토큰·개인정보가 없는지 확인한다.
|
|
54
|
+
- 민감 코드베이스(결제·인증·개인정보)라면 이 확인을 생략하지 않는다. 확인 없이 리뷰를 돌리지 않는다.
|
|
55
|
+
- 이 절은 도구가 강제하지 않는다. **Builder의 의무다.**
|
|
56
|
+
|
|
49
57
|
## 사람에게 보고해야 할 때
|
|
50
58
|
|
|
51
59
|
각 통제점은 **고유한 승인 문장**을 가진다. 그 문장 그대로 승인받지 못했으면 실행하지 않는다.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
이 프로젝트는 [Semantic Versioning](https://semver.org/lang/ko/)을 따릅니다.
|
|
4
|
+
|
|
5
|
+
## 0.7.0
|
|
6
|
+
|
|
7
|
+
**설치 모델이 바뀝니다 — 기존 사용자는 조치가 필요합니다.** 실행 코드와 런타임 의존성을 대상 프로젝트에 복사·주입하지 않고, `commitgate` 패키지에서 실행합니다. 프로젝트에는 거버넌스·감사 데이터만 남습니다.
|
|
8
|
+
|
|
9
|
+
> **npm 배포 이력**: `0.5.0`·`0.6.0`은 npm에 배포되지 않았습니다. `0.4.0` 다음 릴리스가 `0.7.0`이며, 아래 `0.5.0`·`0.6.0` 항목의 변경도 **전부 `0.7.0`에 포함**됩니다.
|
|
10
|
+
|
|
11
|
+
### ⚠️ Breaking
|
|
12
|
+
|
|
13
|
+
- **설치가 2단계가 됩니다.** `npx commitgate` 단독 실행으로는 더 이상 설치되지 않습니다.
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npm install -D commitgate # 1) 런타임이 node_modules/commitgate 에 들어옵니다
|
|
17
|
+
npx commitgate init # 2) 설정·계약·스키마와 req:* 스크립트를 깝니다
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`init`은 대상 `package.json`에 `devDependencies.commitgate` 선언이 없으면 **중단**합니다 — `req:*`가 가리킬 런타임이 없기 때문입니다. 선언의 **존재만** 확인하고 값 형태는 검증하지 않습니다(`file:`·`link:`·`workspace:`·git URL 전부 정당한 설치 형태입니다).
|
|
21
|
+
|
|
22
|
+
- **`scripts/req/**`를 복사하지 않습니다.** 실행 코드는 `node_modules/commitgate`에만 있습니다.
|
|
23
|
+
- **`tsx`·`ajv`·`cross-spawn`을 대상 `package.json`에 주입하지 않습니다.** 이들은 `commitgate` 패키지의 runtime dependency로 전이 설치됩니다.
|
|
24
|
+
- **`req:*` 스크립트 값이 `commitgate <verb>`가 됩니다**(예: `req:new` → `commitgate req:new`). `npm run req:new -- <slug>` UX와 인자 전달은 그대로입니다.
|
|
25
|
+
|
|
26
|
+
### 기존 설치본(0.6.0 이하)에서 옮겨오기
|
|
27
|
+
|
|
28
|
+
기존 프로젝트에는 `scripts/req/`가 복사돼 있고 `req:*`가 `tsx scripts/req/*.ts`를 가리킵니다. `init`은 이 상태를 감지하면 조용히 섞이지 않도록 **중단하고** `migrate`를 안내합니다.
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
npm install -D commitgate
|
|
32
|
+
npx commitgate migrate # 계획만 출력 — 아무것도 쓰지 않습니다
|
|
33
|
+
npx commitgate migrate --apply # package.json 의 req:* 만 전환
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- **아무것도 삭제하지 않습니다.** `scripts/req/`·스키마·persona·설정·진입점·`workflow/REQ-*` 증거를 전부 그대로 둡니다. 남은 `scripts/req/`는 더 이상 실행되지 않으니, 정리하려면 `npx commitgate uninstall` 계획을 먼저 확인하세요.
|
|
37
|
+
- **직접 고친 스크립트는 덮어쓰지 않습니다.** 값이 **정확히** 기존 주입값일 때만 전환하고, 한 글자라도 다르면 사용자 값으로 보아 보존한 뒤 수동 조치를 안내합니다.
|
|
38
|
+
- **커밋하지 않습니다.** `package.json` 한 파일만 쓰고, 검토는 사용자 몫입니다.
|
|
39
|
+
|
|
40
|
+
### 추가
|
|
41
|
+
|
|
42
|
+
- **`commitgate migrate`** — 위 비파괴 전환 명령. 기본 dry-run.
|
|
43
|
+
- **`req:doctor` D19 — 설치 모드 진단.** `req:*` 값의 **형태만**으로 예전(vendored)/현재(런타임 패키지)/혼합/없음/사용자정의를 분류합니다. 혼합일 때만 WARN하며 **FAIL하지 않습니다** — 예전 설치 형태는 결함이 아니라 지원되는 상태이고, `req:commit`이 doctor를 하드 게이트로 실행하므로 FAIL로 두면 정당한 프로젝트의 커밋이 막힙니다. manifest·lockfile·`node_modules`·버전은 검증하지 않습니다.
|
|
44
|
+
- **verb dispatch** — `commitgate <verb>`가 패키지 내부 모듈로 라우팅됩니다. `npx commitgate --dry-run` 같은 기존 옵션 형태는 그대로 `init`으로 갑니다(하위호환).
|
|
45
|
+
- **`uninstall`에 런타임 제거 안내 추가** — `npm uninstall -D commitgate`. 이 명령은 여전히 **읽기 전용**이며 안내를 문자열로 출력만 합니다(npm을 실행하지 않습니다).
|
|
46
|
+
|
|
47
|
+
### 지원 범위
|
|
48
|
+
|
|
49
|
+
- **npm** — 완전 지원. 매 릴리스 packed tarball smoke로 검증합니다.
|
|
50
|
+
- **pnpm·yarn**(`node_modules` linker) — 지원. 표준 `node_modules/.bin/commitgate` 해소를 씁니다.
|
|
51
|
+
- **Yarn PnP** — **이번 릴리스 미지원**(검증하지 않았습니다). `nodeLinker: node-modules`를 쓰세요.
|
|
52
|
+
- **workspace/monorepo** — 워크스페이스 root 설치를 지원합니다. 하위 패키지 독립 설치는 미지원.
|
|
53
|
+
- 런타임 버전은 lockfile이 고정하므로 `package-lock.json`(pnpm/yarn은 각 lockfile)을 **커밋하세요**.
|
|
54
|
+
|
|
55
|
+
### 알려진 한계
|
|
56
|
+
|
|
57
|
+
- **관리 자산(스키마·persona)과 런타임 패키지의 버전 skew를 자동으로 감지하지 못합니다.** `npm update commitgate`는 런타임만 올리고 자산은 그대로 둡니다. D19는 스크립트 형태만 보므로 이 축을 잡지 못합니다. 자산 업그레이드·3-way merge는 이번 범위가 아닙니다.
|
|
58
|
+
|
|
59
|
+
## 0.6.0
|
|
60
|
+
|
|
61
|
+
리뷰 codex 호출을 도구가 통제합니다 — **모델·추론강도를 고정**하고 **재리뷰를 stateless**로. 다운스트림에서 리뷰가 사용자 전역 프로필을 상속해 느리고(11~13분) 토큰이 많던 문제를 해결합니다. 기존 `req.config.json`은 그대로 동작합니다(새 키는 기본값으로 병합).
|
|
62
|
+
|
|
63
|
+
### 추가
|
|
64
|
+
|
|
65
|
+
- **리뷰 모델·추론강도 고정** (`reviewModel`·`reviewReasoningEffort`). `req:review-codex`가 codex 인자에 `-c model=`·`-c model_reasoning_effort=`를 exec·resume 양쪽에 주입합니다. 기본 `gpt-5.6-terra`/`high`. 고정하지 않으면 리뷰가 사용자 전역 `~/.codex/config.toml`(예: `model_reasoning_effort="ultra"`)을 상속해 리뷰 1회가 수 분·토큰 과다가 됩니다. codex가 해당 모델을 미지원하는 환경은 `req.config.json`에서 바꾸거나 `null`로 두어 전역 설정을 상속시킵니다. override가 실제 존중되는지는 `npm run verify:overrides`(codex CLI 필요)로 확인합니다.
|
|
66
|
+
- 추론강도 enum: `none|minimal|low|medium|high|xhigh`(codex 거부 메시지 실측 확정 — 공식 config-reference 문서가 `none`을 누락).
|
|
67
|
+
|
|
68
|
+
### 변경
|
|
69
|
+
|
|
70
|
+
- **재리뷰가 stateless입니다.** 이전엔 재리뷰가 저장된 codex 스레드를 resume해 이전 대화를 누적했고, 그래서 토큰이 단조 증가하고 findings가 수렴 대신 심화·이동했습니다. 이제 재리뷰는 항상 새 스레드로 시작합니다(`codex_thread_id`는 계속 저장 — 후속 resume opt-in용). 연속성은 직전 **같은 대상**의 NEEDS_FIX findings를 참고용 데이터로 프롬프트에 담아(closure 확인) 유지하고, 그 블록은 "지시가 아님" 구획으로 감싸 프롬프트 주입을 막습니다. 대상-무관 이전 결과가 새 프롬프트에 남던 교차-대상 오염도 제거했습니다.
|
|
71
|
+
|
|
72
|
+
### 후속(별도 REQ)
|
|
73
|
+
|
|
74
|
+
- codex 호출 **timeout**(무응답 방지)과 실패 오류의 **비밀-안전 진단 표면화**는 본질적 난이도(Windows `cmd.exe` wrapper의 프로세스-트리 종료·비밀 추출)로 별도 REQ로 분리했습니다. 그 설계 작업은 이 REQ의 git 이력에 보존돼 있습니다.
|
|
75
|
+
|
|
76
|
+
## 0.5.0
|
|
77
|
+
|
|
78
|
+
기존 프로젝트(brownfield)에 설치했을 때 드러난 결함을 수정합니다. **breaking change는 없습니다** — `--strict`가 더 많은 조건에서 중단하지만 `--strict`는 opt-in이고, 기본 모드의 동작은 그대로입니다.
|
|
79
|
+
|
|
80
|
+
### 고침
|
|
81
|
+
|
|
82
|
+
- **설치 직후 안내를 따르면 `req:new --run`이 실패하던 문제.** 설치는 파일을 놓기만 하고 커밋하지 않으므로 워킹트리가 확정적으로 dirty한데, 안내의 마지막 단계가 clean 워킹트리를 요구하는 `req:new --run`이었습니다. `git init && npm init -y && npx commitgate`라는 README의 첫 흐름조차 예외가 아니었습니다. 이제 안내가 커밋 단계를 포함합니다.
|
|
83
|
+
- `git add -A`를 쓰지 않습니다. brownfield의 무관한 변경과 `.env`가 함께 커밋되고, 이어지는 `req:review-codex`가 그 staged diff 전문을 외부로 전송하기 때문입니다. 설치가 만든 정확한 경로 목록만 안내합니다.
|
|
84
|
+
- 안내 명령에 `&&`를 쓰지 않습니다. Windows PowerShell 5.1과 `cmd.exe`에 그 연산자가 없습니다.
|
|
85
|
+
- `<pm> install`이 갱신하는 lockfile과, 계약 마커가 없을 때 생성되는 `AGENTS.commitgate.md`를 stage 목록에 포함합니다. 빠뜨리면 커밋 뒤에도 워킹트리가 dirty로 남습니다.
|
|
86
|
+
- 설치 전부터 있던 무관한 변경은 `git stash push -u -- <경로>`로 안내합니다. 경로 없는 `git stash -u`는 gitignore되지 않은 `node_modules/`까지 쓸어 갑니다.
|
|
87
|
+
- 설치 전에 **staged 변경**이 있거나 **산출물과 겹치는 tracked 수정**이 있으면 `git add` 목록을 내지 않습니다. 전자는 커밋이 삼키고 후자는 사후 분리가 불가능합니다. 잘못된 안내보다 안내 없음이 낫습니다.
|
|
88
|
+
- `node_modules`가 무시되지 않으면 `.gitignore` 추가를 안내하고 그 파일을 설치 커밋에 담습니다. 무시 규칙은 **tracked 저장소 `.gitignore`**에서 온 것만 인정합니다 — `.git/info/exclude`와 전역 ignore는 clone에 따라오지 않습니다.
|
|
89
|
+
- 경로에 공백이 있으면 큰따옴표로 묶습니다. 큰따옴표·백틱·`$`·`%`·`!`가 든 경로는 어떤 셸 인용으로도 안전하지 않으므로 복붙 명령을 아예 내지 않습니다 — `cmd.exe`는 큰따옴표 안에서도 `%VAR%`와 `!VAR!`를 치환합니다.
|
|
90
|
+
- `git status --porcelain`이 C-인용해 주는 경로(`"notes today.txt"`)를 되돌립니다. 되돌리지 않으면 산출물 매칭이 실패하고 안내가 이중 인용을 냅니다.
|
|
91
|
+
|
|
92
|
+
- **`.claude`를 통짜로 무시하는 repo에서 진입점이 조용히 추적 제외되던 문제.** 설치는 "성공"을 출력했지만 팀원의 fresh clone과 CI에는 계약 포인터가 없었습니다. 이제 `git check-ignore`로 감지해 경고하고, 동작하는 `.gitignore` 패턴을 제시합니다(부모 디렉터리가 제외되면 하위 부정 패턴이 무효라는 함정 포함). `--strict`에서는 파일을 하나도 쓰기 전에 중단합니다.
|
|
93
|
+
|
|
94
|
+
- **진입점 템플릿이 `npm run …`을 하드코딩하던 문제.** pnpm/yarn 프로젝트에 틀린 명령이 깔렸습니다. 이제 계약(`AGENTS.md`)과 같은 pm-중립 표기를 씁니다. 치환 렌더링은 쓰지 않습니다 — `uninstall`이 설치본과 패키지 원본의 sha256을 비교하므로, 렌더하면 자기가 깐 파일을 지우지 못합니다.
|
|
95
|
+
|
|
96
|
+
- **런타임 문구의 pm 리터럴.** `req:next`는 npm을, `req:new`·`req:doctor`·`req:review-codex`는 pnpm을 박아 두어 어느 프로젝트에서든 최소 하나는 틀렸습니다. config 로드 이후의 안내·에러는 이제 감지한 패키지매니저로 렌더합니다.
|
|
97
|
+
|
|
98
|
+
- **`--`를 "알 수 없는 옵션"으로 거부하던 문제.** npm은 `npm run x -- a`에서 `--`를 제거하지만 pnpm/yarn은 그대로 전달합니다. 이제 POSIX end-of-options 구분자로 흡수합니다. `--` 이후 인자도 계속 옵션으로 파싱하므로 `req:commit <id> -- --run`이 조용히 dry-run이 되지 않습니다.
|
|
99
|
+
|
|
100
|
+
- **`uninstall`이 skip한 사용자 파일을 소유물로 오인하던 경로.** stage 목록은 패키지 원본과 byte-identical한 파일만 CommitGate 소유로 봅니다.
|
|
101
|
+
|
|
102
|
+
### 새로 알림
|
|
103
|
+
|
|
104
|
+
- `README`와 `AGENTS.template.md`에 **`req:review-codex`가 `git diff --cached` 전문을 Codex(OpenAI)로 전송한다**는 사실을 명시했습니다. codex는 `--sandbox read-only`로 저장소 루트를 읽으며, 마스킹·필터·길이 상한이 없습니다.
|
|
105
|
+
- **git hook을 설치하지 않으므로 `git commit`을 직접 치면 게이트가 우회된다**는 점도 README 상단에 명시했습니다. 이 도구의 강제력은 협조하는 에이전트를 계약 궤도에 유지하는 데 있습니다.
|
|
106
|
+
|
|
107
|
+
### 아직 하지 않은 것
|
|
108
|
+
|
|
109
|
+
- 트렁크 브랜치가 `'main'`으로 하드코딩되어 있습니다(`trunkBranch` config 없음).
|
|
110
|
+
- `req:review-codex`에 타임아웃이 없습니다.
|
|
111
|
+
- 리뷰 전 시크릿 스캔 훅(`preReviewCommand`)이 없습니다.
|
|
112
|
+
|
|
113
|
+
## 0.4.0 이전
|
|
114
|
+
|
|
115
|
+
`git log`를 참조하세요.
|
package/README.en.md
CHANGED
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
AI coding agents can move quickly, but unreviewed changes should not go straight into your history. CommitGate wraps each change in a REQ ticket and only allows the staged tree approved by Codex to be committed. If the code changes after approval, or if evidence is missing, it fails closed.
|
|
8
8
|
|
|
9
|
+
> **⚠️ Two things to know before you start.**
|
|
10
|
+
>
|
|
11
|
+
> 1. **Review sends your staged diff off-machine.** `req:review-codex` passes the **entire** `git diff --cached` to Codex (OpenAI). Codex reads your repository root under `--sandbox read-only`, so files outside the diff can be read too. There is **no** masking, filtering, or size cap. Check the staged content for credentials, tokens, and personal data before running a review.
|
|
12
|
+
> 2. **No git hook is installed.** Running `git commit` directly instead of `req:commit` bypasses the gate, the approval binding, and the evidence trail. CommitGate's enforcement keeps a **cooperating agent on the contract's rails** — it is not a barrier against a human going around it.
|
|
13
|
+
|
|
9
14
|
[](https://github.com/sol5288/commitgate/actions/workflows/ci.yml)
|
|
10
15
|
[](https://www.npmjs.com/package/commitgate)
|
|
11
16
|
[](./LICENSE)
|
|
@@ -21,13 +26,32 @@ Run this from your project root. The project must be a **git repository with a `
|
|
|
21
26
|
git init
|
|
22
27
|
npm init -y
|
|
23
28
|
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
# 1) Install CommitGate as a devDependency — this is where the runtime lives:
|
|
30
|
+
npm install -D commitgate
|
|
31
|
+
|
|
32
|
+
# 2) Add config, contract, schemas, and the req:* scripts to your project:
|
|
33
|
+
npx commitgate init
|
|
34
|
+
|
|
27
35
|
codex --version
|
|
28
36
|
codex login status
|
|
29
37
|
```
|
|
30
38
|
|
|
39
|
+
> **Why two steps?** CommitGate does **not** copy its runtime code into your project. Step 1 puts the runtime in `node_modules/commitgate`; step 2 adds only **governance assets** (config, contract, schemas, persona) plus `req:* = commitgate <verb>` scripts.
|
|
40
|
+
> That means updating is a single `npm update commitgate`, and removing the runtime is `npm uninstall -D commitgate`.
|
|
41
|
+
> `init` **stops** if `devDependencies.commitgate` is not declared — there would be no runtime for `req:*` to point at.
|
|
42
|
+
|
|
43
|
+
Installation writes files but never commits them. `req:new` **requires a clean working tree**, so commit the scaffold first. The installer's `다음:` (next steps) output prints the exact paths to stage.
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
git add -- <the paths the installer printed>
|
|
47
|
+
git status # confirm only what you intended is staged
|
|
48
|
+
git commit -m "chore: install commitgate"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> **Do not stage everything (`-A` / `.`).** Unrelated changes and untracked files such as `.env` would be swept into the commit, and the next `req:review-codex` transmits that staged diff in full to an external service.
|
|
52
|
+
> Park any changes that predate the install **by pathspec** after the install commit: `git stash push -u -- <paths>`.
|
|
53
|
+
> Without `-u`, untracked files remain and `req:new` stays blocked; without the pathspec, a bare `git stash -u` also sweeps up directories that are not ignored, such as `node_modules/`. The installer prints that path list too.
|
|
54
|
+
|
|
31
55
|
**You do not paste a long prompt.** Installation lays down agent entrypoints for you.
|
|
32
56
|
|
|
33
57
|
| File | Read by |
|
|
@@ -114,24 +138,42 @@ CommitGate is designed to block **unreviewed changes from being committed**, not
|
|
|
114
138
|
|
|
115
139
|
In short: **approved changes pass, ambiguous changes stop.**
|
|
116
140
|
|
|
141
|
+
### What It Does *Not* Enforce
|
|
142
|
+
|
|
143
|
+
So that you do not miscalculate where your real defenses are:
|
|
144
|
+
|
|
145
|
+
- **This is not hard enforcement.** No git hook is installed, so running `git commit` directly instead of `req:commit` bypasses doctor, the approval binding, and the evidence trail. Your real defense for production is still CI and the deployment pipeline.
|
|
146
|
+
- **It does not keep your staged content secret.** `req:review-codex` transmits the full `git diff --cached` to Codex (OpenAI), and codex reads the repository root under `--sandbox read-only`. There is no masking, scrubbing, or size cap. For payment or credential-bearing codebases, write a "inspect the staged diff before review" step into your contract (`AGENTS.md`).
|
|
147
|
+
- **It does not guarantee anything after the commit.** Approval binds the staged tree at commit time; merge, tag, and publish are each separate control points.
|
|
148
|
+
|
|
117
149
|
---
|
|
118
150
|
|
|
119
151
|
## What Installation Adds
|
|
120
152
|
|
|
121
|
-
`npx commitgate` adds the following to the target project. Existing files are not overwritten by default.
|
|
153
|
+
`npx commitgate init` adds the following to the target project. Existing files are not overwritten by default.
|
|
122
154
|
|
|
123
155
|
| Added item | Purpose |
|
|
124
156
|
|---|---|
|
|
125
|
-
| `scripts/req/` | `req:new`, `req:next`, `req:review-codex`, `req:doctor`, `req:commit` scripts |
|
|
126
157
|
| `workflow/*.schema.json` | Schemas for Codex responses and config |
|
|
127
|
-
| `workflow/review-persona.md` | Reviewer persona injected into the Codex review prompt |
|
|
158
|
+
| `workflow/review-persona.md` | Reviewer persona injected into the Codex review prompt (created only if absent) |
|
|
128
159
|
| `req.config.json` | Project-level configuration |
|
|
129
160
|
| `AGENTS.md` | The contract (created only if absent) |
|
|
130
161
|
| `CLAUDE.md` | Claude Code pointer (created only if absent) |
|
|
131
162
|
| `.claude/skills/commitgate/SKILL.md` | Claude Code skill (pointer) |
|
|
132
163
|
| `.claude/commands/req.md` | `/req` slash command (pointer) |
|
|
133
164
|
| `.cursor/rules/commitgate.mdc` | Cursor rule (pointer) |
|
|
134
|
-
| `package.json` scripts | `req
|
|
165
|
+
| `package.json` scripts | `req:new`·`req:next`·`req:review-codex`·`req:doctor`·`req:commit` = `commitgate <verb>` (missing keys only) |
|
|
166
|
+
|
|
167
|
+
### What it does **not** install
|
|
168
|
+
|
|
169
|
+
| Item | Where it lives instead |
|
|
170
|
+
|---|---|
|
|
171
|
+
| `scripts/req/**` runtime code | `node_modules/commitgate` — never copied into your project |
|
|
172
|
+
| `tsx` · `ajv` · `cross-spawn` | runtime dependencies of the `commitgate` package — never injected into your `package.json` |
|
|
173
|
+
|
|
174
|
+
What stays in your project is **governance and audit data** only: config, contract, schemas, persona, and `workflow/REQ-*` evidence. Because the runtime lives in the package, `npm update commitgate` updates it in one step and vendored copies cannot drift.
|
|
175
|
+
|
|
176
|
+
The `req:*` scripts call the installed package bin — `npm run req:new -- <slug>` → `commitgate req:new <slug>` → `node_modules/.bin/commitgate`.
|
|
135
177
|
|
|
136
178
|
The entrypoint files are **thin pointers**. The contract itself lives only in `AGENTS.md`.
|
|
137
179
|
|
|
@@ -149,21 +191,64 @@ Preview without writing files:
|
|
|
149
191
|
npx commitgate --dry-run
|
|
150
192
|
```
|
|
151
193
|
|
|
152
|
-
Treat
|
|
194
|
+
Treat integrity warnings as an install failure:
|
|
153
195
|
|
|
154
196
|
```sh
|
|
155
197
|
npx commitgate --strict
|
|
156
198
|
```
|
|
157
199
|
|
|
158
|
-
|
|
200
|
+
CommitGate stops **before writing any file** if a contract pointer would be swallowed by `.gitignore`, if the `workflow/.gitignore` policy file would not reach a fresh clone, if the working tree makes a safe install commit impossible, or if an existing `cross-spawn` is below the verified floor.
|
|
201
|
+
|
|
202
|
+
> `--strict` also treats the `package.json`/lockfile changes left by the required `npm install -D commitgate` as pre-existing dirt. Recommended order: `npm i -D commitgate` → **commit** → `npx commitgate init --strict` → commit the scaffold.
|
|
159
203
|
|
|
160
204
|
> `workflow/machine.schema.json` and `workflow/req.config.schema.json` are always copied under `workflow/`, regardless of the `ticketRoot` setting in `req.config.json`.
|
|
161
205
|
|
|
162
206
|
---
|
|
163
207
|
|
|
208
|
+
## Migrating from an older install (`migrate`)
|
|
209
|
+
|
|
210
|
+
If `scripts/req/` is copied into your project and `req:*` points at `tsx scripts/req/*.ts`, you have an **older (vendored) install**. `init` detects this and **stops** rather than creating a silent mix, pointing you here.
|
|
211
|
+
|
|
212
|
+
```sh
|
|
213
|
+
npm install -D commitgate # first, if it is not a devDependency yet
|
|
214
|
+
npx commitgate migrate # plan only — writes nothing
|
|
215
|
+
npx commitgate migrate --apply # rewrites only the req:* scripts in package.json
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`migrate` does exactly **one** thing: it rewrites the `req:*` keys **whose current value is byte-for-byte the old injected value** to `commitgate <verb>`.
|
|
219
|
+
|
|
220
|
+
- **It deletes nothing.** `scripts/req/`, schemas, persona, config, entrypoints, and `workflow/REQ-*` evidence are all left in place. The leftover `scripts/req/` is no longer executed; run `npx commitgate uninstall` to see a cleanup plan first.
|
|
221
|
+
- **It never overwrites scripts you edited.** Any value that differs — even by one character — is treated as yours, preserved, and reported for manual action.
|
|
222
|
+
- **It does not commit.** It writes `package.json` only; reviewing is up to you.
|
|
223
|
+
|
|
224
|
+
`req:doctor` also reports the install mode (old / current / mixed).
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Support scope
|
|
229
|
+
|
|
230
|
+
| Environment | Status |
|
|
231
|
+
|---|---|
|
|
232
|
+
| **npm** | Fully supported — verified on every release by a packed-tarball smoke test |
|
|
233
|
+
| **pnpm · yarn** (`node_modules` linker) | Supported — uses the standard `node_modules/.bin/commitgate` resolution |
|
|
234
|
+
| **Yarn PnP** | **Not supported in this release** (untested). Use `nodeLinker: node-modules` |
|
|
235
|
+
| **workspaces / monorepo** | **Workspace-root installs** are supported (`req.config.json` and `workflow/` at the root). Installing independently in a sub-package is not supported |
|
|
236
|
+
|
|
237
|
+
**Reproducibility**: the review model/effort pins in `req.config.json`, plus the schemas and persona, stay in your project, so past review inputs are reproducible from git history. Runtime versions are pinned by your lockfile — **commit `package-lock.json`** (or the pnpm/yarn equivalent).
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
164
241
|
## Removing CommitGate
|
|
165
242
|
|
|
166
|
-
|
|
243
|
+
CommitGate lives in two places: the **runtime** (`node_modules/commitgate`) and the **governance files installed into your project**.
|
|
244
|
+
|
|
245
|
+
The package manager removes the runtime:
|
|
246
|
+
|
|
247
|
+
```sh
|
|
248
|
+
npm uninstall -D commitgate # pnpm remove -D commitgate · yarn remove commitgate
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
For the project files, review the plan below and clean up yourself. First, the important part: **`npx commitgate` is not a global install.** npx downloads the package into the npm cache (`_npx/<hash>/`) and runs it once; it leaves nothing in your global `node_modules` and nothing on your PATH.
|
|
167
252
|
|
|
168
253
|
Start by previewing the removal plan. This command **deletes nothing**:
|
|
169
254
|
|
|
@@ -302,11 +387,14 @@ npm run req:commit -- 2026-001 --run --message-file commit-message.txt
|
|
|
302
387
|
|
|
303
388
|
| Command | Purpose |
|
|
304
389
|
|---|---|
|
|
305
|
-
| `
|
|
306
|
-
| `npx commitgate
|
|
307
|
-
| `npx commitgate --
|
|
308
|
-
| `npx commitgate --
|
|
390
|
+
| `npm install -D commitgate` | **Install the runtime (required first)** — the executable code lives in `node_modules/commitgate` |
|
|
391
|
+
| `npx commitgate init` | Install config, contract, schemas, and the `req:*` scripts into a project |
|
|
392
|
+
| `npx commitgate init --dry-run` | Preview the install plan without writing files |
|
|
393
|
+
| `npx commitgate init --strict` | Treat integrity warnings as install failures — stops before writing any file |
|
|
394
|
+
| `npx commitgate init --no-agent-entrypoints` | Skip `.claude/`, `.cursor/`, and `CLAUDE.md` |
|
|
395
|
+
| `npx commitgate migrate [--apply]` | Move an older vendored install to the runtime package (plan-only by default, non-destructive) |
|
|
309
396
|
| `npx commitgate uninstall` | Preview the removal plan (read-only — deletes nothing) |
|
|
397
|
+
| `npm uninstall -D commitgate` | Remove the runtime |
|
|
310
398
|
| `npm run req:new -- <slug> --run` | Create a REQ ticket, branch, and design docs |
|
|
311
399
|
| `npm run req:next -- <id> [--json]` | **Compute the next action** (read-only) |
|
|
312
400
|
| `npm run req:review-codex -- <id> --kind design --run` | Review the design |
|
|
@@ -335,9 +423,15 @@ Defaults are enough for most projects. If needed, edit `req.config.json` in the
|
|
|
335
423
|
| `packageManager` | auto-detected | `npm`, `pnpm`, or `yarn` |
|
|
336
424
|
| `designDocs` | `00/01/02` docs | Design document filenames |
|
|
337
425
|
| `reviewPersonaPath` | `"workflow/review-persona.md"` | First block of the review prompt. `null` disables it |
|
|
426
|
+
| `reviewModel` | `"gpt-5.6-terra"` | codex review model (pinned via `-c model=`). `null` inherits your global codex config |
|
|
427
|
+
| `reviewReasoningEffort` | `"high"` | codex review reasoning effort. One of `none`, `minimal`, `low`, `medium`, `high`, `xhigh`. `null` inherits the global setting |
|
|
338
428
|
|
|
339
429
|
Empty `branchPrefix` values and paths that escape the project root are rejected.
|
|
340
430
|
|
|
431
|
+
**Pinned review model & effort**: `req:review-codex` injects `-c model=` and `-c model_reasoning_effort=` into the codex arguments to **pin the model and reasoning effort**. Without pinning, a review inherits your global `~/.codex/config.toml` (e.g. `model_reasoning_effort="ultra"`), making a single review take minutes and burn tokens. The defaults are `gpt-5.6-terra`/`high`; if your codex doesn't support that model, change it in `req.config.json` or set it to `null` to inherit the global config. Whether the overrides are actually honored can be checked with `npm run verify:overrides` (requires the codex CLI).
|
|
432
|
+
|
|
433
|
+
**Stateless re-reviews**: each re-review starts a **fresh codex thread** (it does not resume/accumulate the prior conversation — which drove token growth and goalpost drift). Only the previous same-target NEEDS_FIX findings are carried into the prompt as reference data, to confirm closure.
|
|
434
|
+
|
|
341
435
|
---
|
|
342
436
|
|
|
343
437
|
## FAQ
|
|
@@ -361,17 +455,18 @@ No. Existing files are skipped. Use `--force` if you intentionally want to refre
|
|
|
361
455
|
|
|
362
456
|
## Current Scope
|
|
363
457
|
|
|
364
|
-
The current release is **
|
|
458
|
+
The current release is a **runtime package model**. Executable code and runtime dependencies live only in `node_modules/commitgate`; your project keeps governance/audit data and the `req:* = commitgate <verb>` scripts. (Older vendored installs move over with [`migrate`](#migrating-from-an-older-install-migrate).)
|
|
365
459
|
|
|
366
460
|
Current verification:
|
|
367
461
|
|
|
368
462
|
- GitHub Actions runs a `ubuntu-latest`, `macos-latest`, `windows-latest` × Node 18/20/22 matrix.
|
|
369
|
-
- `npm run smoke` installs the packed tarball and
|
|
463
|
+
- `npm run smoke` installs the packed tarball into a throwaway project and asserts that the target has **no** `scripts/req/`, that `tsx`/`ajv`/`cross-spawn` are **not** injected, that all five `req:*` scripts point at the package bin, and that `npm run req:doctor` actually dispatches into the module inside the package. It verifies `migrate`'s non-destructiveness the same way.
|
|
370
464
|
- A Windows `.cmd` wrapper injection regression test protects package-manager and Codex wrapper paths.
|
|
371
465
|
|
|
372
466
|
Future scope:
|
|
373
467
|
|
|
374
|
-
-
|
|
468
|
+
- Yarn PnP support; independent installs in workspace sub-packages
|
|
469
|
+
- Asset↔runtime version drift detection
|
|
375
470
|
- Non-git VCS support
|
|
376
471
|
- More design document templates
|
|
377
472
|
|
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,13 +26,32 @@ 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
|
| 파일 | 읽는 도구 |
|
|
@@ -114,24 +138,42 @@ 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
|
-
| `package.json` 스크립트 | `req
|
|
165
|
+
| `package.json` 스크립트 | `req:new`·`req:next`·`req:review-codex`·`req:doctor`·`req:commit` = `commitgate <verb>` (없는 키만) |
|
|
166
|
+
|
|
167
|
+
### 설치하지 **않는** 것
|
|
168
|
+
|
|
169
|
+
| 항목 | 어디에 있나 |
|
|
170
|
+
|---|---|
|
|
171
|
+
| `scripts/req/**` 실행 코드 | `node_modules/commitgate` — 프로젝트에 복사하지 않습니다 |
|
|
172
|
+
| `tsx` · `ajv` · `cross-spawn` | `commitgate` 패키지의 runtime dependency — 대상 `package.json`에 주입하지 않습니다 |
|
|
173
|
+
|
|
174
|
+
프로젝트에 남는 것은 **거버넌스·감사 데이터**(설정·계약·스키마·persona·`workflow/REQ-*` 증거)뿐입니다. 실행 코드는 패키지에만 있으므로 `npm update commitgate` 한 번으로 갱신되고, 복사본 버전이 갈라지지 않습니다.
|
|
175
|
+
|
|
176
|
+
`req:*` 스크립트는 설치된 패키지 bin을 호출합니다 — `npm run req:new -- <slug>` → `commitgate req:new <slug>` → `node_modules/.bin/commitgate`.
|
|
135
177
|
|
|
136
178
|
진입점 파일들은 **얇은 포인터**입니다. 계약 본문은 `AGENTS.md` 하나에만 있습니다.
|
|
137
179
|
|
|
@@ -149,21 +191,69 @@ npx commitgate --no-agent-entrypoints
|
|
|
149
191
|
npx commitgate --dry-run
|
|
150
192
|
```
|
|
151
193
|
|
|
152
|
-
|
|
194
|
+
정합성 경고를 설치 실패로 취급하려면:
|
|
153
195
|
|
|
154
196
|
```sh
|
|
155
197
|
npx commitgate --strict
|
|
156
198
|
```
|
|
157
199
|
|
|
158
|
-
|
|
200
|
+
**파일을 하나도 쓰기 전에** 중단합니다. 대상은 다음과 같습니다.
|
|
201
|
+
|
|
202
|
+
- 계약 포인터(`.claude/`·`.cursor/`·`AGENTS.md`·`CLAUDE.md`)가 `.gitignore`에 걸려 팀·CI에 공유되지 않을 때
|
|
203
|
+
- `workflow/.gitignore` 정책 파일이 무시돼 fresh clone·CI에 scratch 규칙이 전달되지 않을 때
|
|
204
|
+
- 설치 전 워킹트리에 staged 변경이 있거나 설치 산출물과 겹치는 수정이 있어, 설치분만 담은 커밋을 만들 수 없을 때
|
|
205
|
+
- 기존 `cross-spawn`이 검증 하한보다 낮을 때(프로젝트가 그 패키지를 이미 쓰는 경우)
|
|
206
|
+
|
|
207
|
+
> `--strict`는 **선행 `npm install -D commitgate`가 남긴 `package.json`·lockfile 변경도** preexisting-dirty로 봅니다. 권장 순서: `npm i -D commitgate` → **커밋** → `npx commitgate init --strict` → 설치분 커밋.
|
|
159
208
|
|
|
160
209
|
> `workflow/machine.schema.json`과 `workflow/req.config.schema.json`은 `req.config.json`의 `ticketRoot` 설정과 무관하게 **항상 `workflow/` 아래**에 복사됩니다.
|
|
161
210
|
|
|
162
211
|
---
|
|
163
212
|
|
|
213
|
+
## 예전 설치본에서 옮겨오기 (`migrate`)
|
|
214
|
+
|
|
215
|
+
`scripts/req/`가 프로젝트에 복사돼 있고 `req:*`가 `tsx scripts/req/*.ts`를 가리킨다면 **예전(vendored) 설치본**입니다. `init`은 이 상태를 감지하면 조용히 섞이지 않도록 **중단하고** 이 명령을 안내합니다.
|
|
216
|
+
|
|
217
|
+
```sh
|
|
218
|
+
npm install -D commitgate # 아직 devDependency가 아니라면 먼저
|
|
219
|
+
npx commitgate migrate # 계획만 출력 — 아무것도 쓰지 않습니다
|
|
220
|
+
npx commitgate migrate --apply # package.json 의 req:* 만 전환
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
`migrate`가 하는 일은 **하나**입니다: `req:*` 중 **현재 값이 정확히 예전 주입값인 키만** `commitgate <verb>`로 바꿉니다.
|
|
224
|
+
|
|
225
|
+
- **아무것도 삭제하지 않습니다.** `scripts/req/`·스키마·persona·설정·진입점·`workflow/REQ-*` 증거를 전부 그대로 둡니다. 남은 `scripts/req/`는 더 이상 실행되지 않으니, 정리하려면 `npx commitgate uninstall` 계획을 먼저 확인하세요.
|
|
226
|
+
- **직접 고친 스크립트는 덮어쓰지 않습니다.** 값이 한 글자라도 다르면 사용자 값으로 보고 보존한 뒤 수동 조치를 안내합니다.
|
|
227
|
+
- **커밋하지 않습니다.** `package.json` 한 파일만 쓰고, 검토는 사용자 몫입니다.
|
|
228
|
+
|
|
229
|
+
`req:doctor`도 설치 모드(예전/현재/혼합)를 진단해 알려 줍니다.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 지원 범위
|
|
234
|
+
|
|
235
|
+
| 환경 | 상태 |
|
|
236
|
+
|---|---|
|
|
237
|
+
| **npm** | 완전 지원 — 매 릴리스 packed tarball smoke로 검증합니다 |
|
|
238
|
+
| **pnpm · yarn** (`node_modules` linker) | 지원 — `node_modules/.bin/commitgate`로 해소되는 표준 경로를 씁니다 |
|
|
239
|
+
| **Yarn PnP** | **이번 릴리스 미지원**(검증하지 않았습니다). `nodeLinker: node-modules`를 쓰세요 |
|
|
240
|
+
| **workspace/monorepo** | **워크스페이스 root 설치**를 지원합니다(root에 `req.config.json`·`workflow/`). 하위 패키지에 독립 설치하는 배치는 미지원 |
|
|
241
|
+
|
|
242
|
+
**재현성**: `req.config.json`의 리뷰 모델·추론강도 핀과 스키마·persona가 프로젝트에 남아 과거 리뷰 입력이 git 이력으로 재현됩니다. 런타임 버전은 **lockfile이 고정**하므로 `package-lock.json`(pnpm/yarn은 각 lockfile)을 **커밋하세요**.
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
164
246
|
## 제거하려면
|
|
165
247
|
|
|
166
|
-
|
|
248
|
+
CommitGate는 두 곳에 있습니다. **런타임**(`node_modules/commitgate`)과 **프로젝트에 깔린 거버넌스 파일**입니다.
|
|
249
|
+
|
|
250
|
+
런타임은 package manager가 지웁니다:
|
|
251
|
+
|
|
252
|
+
```sh
|
|
253
|
+
npm uninstall -D commitgate # pnpm remove -D commitgate · yarn remove commitgate
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
프로젝트 파일은 아래 계획을 보고 직접 정리하세요. 먼저 알아둘 것: **`npx commitgate`는 전역 설치가 아닙니다.** npx는 패키지를 npm 캐시(`_npx/<hash>/`)에 받아 한 번 실행할 뿐이고, 전역 `node_modules`에도 PATH에도 아무것도 남기지 않습니다.
|
|
167
257
|
|
|
168
258
|
제거 계획을 먼저 확인하세요. 이 명령은 **아무것도 지우지 않고** 계획만 출력합니다:
|
|
169
259
|
|
|
@@ -302,11 +392,14 @@ npm run req:commit -- 2026-001 --run --message-file commit-message.txt
|
|
|
302
392
|
|
|
303
393
|
| 명령 | 용도 |
|
|
304
394
|
|---|---|
|
|
305
|
-
| `
|
|
306
|
-
| `npx commitgate
|
|
307
|
-
| `npx commitgate --
|
|
308
|
-
| `npx commitgate --
|
|
395
|
+
| `npm install -D commitgate` | **런타임 설치 (선행 필수)** — 실행 코드가 `node_modules/commitgate`에 들어옵니다 |
|
|
396
|
+
| `npx commitgate init` | 프로젝트에 설정·계약·스키마와 `req:*` 스크립트 설치 |
|
|
397
|
+
| `npx commitgate init --dry-run` | 파일을 쓰지 않고 설치 계획 확인 |
|
|
398
|
+
| `npx commitgate init --strict` | 정합성 경고를 설치 실패로 처리 (gitignore된 계약 포인터, 설치 커밋을 안전하게 만들 수 없는 워킹트리 등) — 파일을 하나도 쓰기 전에 중단 |
|
|
399
|
+
| `npx commitgate init --no-agent-entrypoints` | `.claude/`·`.cursor/`·`CLAUDE.md` 설치 건너뛰기 |
|
|
400
|
+
| `npx commitgate migrate [--apply]` | 예전 vendored 설치본 → 런타임 패키지 전환 (기본: 계획만, 비파괴) |
|
|
309
401
|
| `npx commitgate uninstall` | 제거 계획 확인 (읽기 전용 — 아무것도 지우지 않음) |
|
|
402
|
+
| `npm uninstall -D commitgate` | 런타임 제거 |
|
|
310
403
|
| `npm run req:new -- <slug> --run` | REQ 티켓, 브랜치, 설계문서 생성 |
|
|
311
404
|
| `npm run req:next -- <id> [--json]` | **다음 행동 계산** (읽기 전용) |
|
|
312
405
|
| `npm run req:review-codex -- <id> --kind design --run` | 설계 리뷰 |
|
|
@@ -335,9 +428,15 @@ yarn req:next 2026-002 # yarn
|
|
|
335
428
|
| `packageManager` | 자동 감지 | `npm`, `pnpm`, `yarn` |
|
|
336
429
|
| `designDocs` | `00/01/02` 문서 | 설계 문서 파일명 |
|
|
337
430
|
| `reviewPersonaPath` | `"workflow/review-persona.md"` | 리뷰 프롬프트 첫 블록. `null`이면 비활성 |
|
|
431
|
+
| `reviewModel` | `"gpt-5.6-terra"` | codex 리뷰 모델(`-c model=`로 고정). `null`이면 codex 전역 설정을 상속 |
|
|
432
|
+
| `reviewReasoningEffort` | `"high"` | codex 리뷰 추론강도. `none`·`minimal`·`low`·`medium`·`high`·`xhigh` 중 하나. `null`이면 전역 상속 |
|
|
338
433
|
|
|
339
434
|
빈 `branchPrefix`나 프로젝트 밖으로 나가는 경로는 거부됩니다.
|
|
340
435
|
|
|
436
|
+
**리뷰 모델·추론강도 고정**: `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 필요)로 확인할 수 있습니다.
|
|
437
|
+
|
|
438
|
+
**재리뷰는 stateless**: 재리뷰는 매번 **새 codex 스레드**로 시작합니다(이전 대화를 resume해 누적하지 않음 — 토큰 증가와 findings 심화·이동을 막습니다). 직전 같은 대상의 NEEDS_FIX findings만 참고용으로 프롬프트에 담겨 해소 여부(closure)를 확인합니다.
|
|
439
|
+
|
|
341
440
|
---
|
|
342
441
|
|
|
343
442
|
## FAQ
|
|
@@ -361,17 +460,18 @@ yarn req:next 2026-002 # yarn
|
|
|
361
460
|
|
|
362
461
|
## 현재 범위
|
|
363
462
|
|
|
364
|
-
현재 버전은
|
|
463
|
+
현재 버전은 **런타임 패키지 모델**입니다. 실행 코드와 런타임 의존성은 `node_modules/commitgate`에만 있고, 프로젝트에는 거버넌스·감사 데이터와 `req:* = commitgate <verb>` 스크립트만 남습니다. (예전 vendored scaffold 설치본은 [`migrate`](#예전-설치본에서-옮겨오기-migrate)로 전환합니다.)
|
|
365
464
|
|
|
366
465
|
현재 운영 중인 검증입니다.
|
|
367
466
|
|
|
368
467
|
- GitHub Actions에서 `ubuntu-latest`, `macos-latest`, `windows-latest` × Node 18/20/22 매트릭스를 실행합니다.
|
|
369
|
-
- `npm run smoke`는 pack tarball
|
|
468
|
+
- `npm run smoke`는 pack tarball을 임시 프로젝트에 실제로 설치해, 대상에 `scripts/req/`가 **없고** `tsx`·`ajv`·`cross-spawn`이 **주입되지 않으며** 다섯 `req:*`가 패키지 bin을 가리키는지, 그리고 `npm run req:doctor`가 실제로 패키지 안의 모듈까지 dispatch되는지 확인합니다. `migrate` 비파괴성도 같은 방식으로 검증합니다.
|
|
370
469
|
- Windows `.cmd` 래퍼 주입 회귀 테스트가 패키지 매니저와 Codex wrapper 경로를 보호합니다.
|
|
371
470
|
|
|
372
471
|
아래는 후속 범위입니다.
|
|
373
472
|
|
|
374
|
-
-
|
|
473
|
+
- Yarn PnP 지원, 워크스페이스 하위 패키지 독립 설치
|
|
474
|
+
- 자산↔런타임 버전 드리프트 탐지
|
|
375
475
|
- 비-git VCS 지원
|
|
376
476
|
- 더 다양한 설계문서 템플릿
|
|
377
477
|
|
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)
|