commitgate 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.template.md +9 -0
- package/README.en.md +85 -35
- package/README.md +87 -37
- package/bin/init.ts +156 -7
- package/bin/uninstall.ts +54 -12
- package/package.json +5 -2
- package/req.config.json.sample +14 -13
- package/scripts/req/lib/config.ts +47 -6
- package/scripts/req/req-commit.ts +1 -1
- package/scripts/req/req-doctor.ts +0 -1
- package/scripts/req/req-new.ts +1 -1
- package/scripts/req/req-next.ts +638 -0
- package/scripts/req/review-codex.ts +154 -10
- package/templates/CLAUDE.template.md +15 -0
- package/templates/claude-command.md +36 -0
- package/templates/claude-skill.md +59 -0
- package/templates/cursor-rule.mdc +55 -0
- package/workflow/req.config.schema.json +1 -0
- package/workflow/review-persona.md +45 -0
package/AGENTS.template.md
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
<!-- commitgate:contract -->
|
|
1
2
|
# AGENTS.md (AI REQ workflow — Codex/Builder 계약)
|
|
2
3
|
|
|
3
4
|
> 이 파일은 `commitgate`(init)가 대상 repo에 `AGENTS.md`가 없을 때 생성한 **템플릿**이다.
|
|
4
5
|
> 프로젝트 고유 규칙(SSOT 경로·포트·참조 프로젝트·phase 문서 등)은 여기에 직접 채워 넣어라.
|
|
5
6
|
> Codex CLI는 repo 루트의 `AGENTS.md`를 리뷰 컨텍스트로 읽는다.
|
|
7
|
+
>
|
|
8
|
+
> ⚠️ 첫 줄의 `<!-- commitgate:contract -->` 마커를 지우지 마라. `.claude/`·`.cursor/` 진입점 파일이
|
|
9
|
+
> "이 `AGENTS.md`가 CommitGate 계약인가"를 그 마커로 판별하고, `npx commitgate`도 부재 시 경고한다.
|
|
6
10
|
|
|
7
11
|
## 역할
|
|
8
12
|
|
|
@@ -78,8 +82,13 @@ protected branch에 변경을 넣는 경로는 **두 가지**이고 **둘 다
|
|
|
78
82
|
| 명령 | 용도 |
|
|
79
83
|
|---|---|
|
|
80
84
|
| `req:new <slug> --run` | REQ 티켓 생성(채번·브랜치·`00/01/02` 스캐폴드) |
|
|
85
|
+
| `req:next <id> [--json]` | **다음 행동 계산**(읽기 전용). `RUN`/`AGENT`/`AWAIT_HUMAN`/`DONE`/`BLOCKED` |
|
|
81
86
|
| `req:review-codex <id> --kind design\|phase [--phase <p>] --run` | Codex 리뷰 조립·호출·승인 반영 |
|
|
82
87
|
| `req:doctor <id>` | 일관성 게이트(D-체크) |
|
|
83
88
|
| `req:commit <id> [--run] [--message-file <f>]` | 승인된 phase 커밋 + evidence-finalize |
|
|
84
89
|
|
|
90
|
+
**다음 행동을 추측하지 마라.** `req:next`가 state와 git에서 계산해 준다. `RUN`이면 그 명령을 그대로 실행하고, `AGENT`면 그 작업을 한 뒤 `git add` 하고, `AWAIT_HUMAN`이면 멈춰서 출력된 승인 문장을 그대로 받는다. 이 루프를 끊지 말고 반복한다.
|
|
91
|
+
|
|
92
|
+
**리뷰어 페르소나는 도구가 주입한다.** `req:review-codex`가 `workflow/review-persona.md`를 프롬프트 첫 블록으로 넣는다(`req.config.json`의 `reviewPersonaPath`). 사람이 직접 실행하든 다른 에이전트가 실행하든 동일하다 — 프롬프트에 손으로 붙여넣지 마라. 파일이 없거나 비어 있으면 fail-closed로 멈춘다.
|
|
93
|
+
|
|
85
94
|
설정은 repo 루트의 `req.config.json`(선택). 자세한 계약은 `README.md` 참조.
|
package/README.en.md
CHANGED
|
@@ -28,36 +28,29 @@ codex --version
|
|
|
28
28
|
codex login status
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
**You do not paste a long prompt.** Installation lays down agent entrypoints for you.
|
|
32
|
+
|
|
33
|
+
| File | Read by |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `AGENTS.md` | Codex CLI, Cursor — **the contract** |
|
|
36
|
+
| `.claude/skills/commitgate/SKILL.md` | Claude Code (auto-invoked when it matches) |
|
|
37
|
+
| `.claude/commands/req.md` | Claude Code (`/req` explicit call) |
|
|
38
|
+
| `.cursor/rules/commitgate.mdc` | Cursor (`alwaysApply`) |
|
|
39
|
+
| `CLAUDE.md` | Claude Code (always loaded) — created only if absent |
|
|
40
|
+
|
|
41
|
+
Just give the agent a requirement.
|
|
32
42
|
|
|
33
43
|
```text
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- If `req:review-codex` returns NEEDS_FIX/exit 3, fix the findings and rerun review.
|
|
41
|
-
- If it returns BLOCKED/exit 2, do not retry the same review; escalate or change the review target. If a stuck thread is suspected, you may retry once with `--fresh-thread`.
|
|
42
|
-
- The review target is only what has been staged with git add.
|
|
43
|
-
- Do not manually git add state.json or responses/.
|
|
44
|
-
|
|
45
|
-
Stop for human confirmation only (each item must be approved by that exact sentence; one approval never carries over to the next step):
|
|
46
|
-
- Right before req:commit --run
|
|
47
|
-
- [Path A · optional] [I1] Before pushing the feature branch and opening a PR / [I2] Before merging the PR, after confirming the required status checks are green
|
|
48
|
-
- [Path B] [B1] Before a direct push to a protected branch — get a separate "branch protection bypass를 사용한 direct push 승인". This push bypasses the required status checks, and CI runs after it
|
|
49
|
-
- [R1/R2/R3] tag creation and push / npm publish / GitHub release — each approved separately, after CI is green
|
|
50
|
-
- Before destructive actions such as reset, clean, or force push
|
|
51
|
-
- When the requested scope must change
|
|
52
|
-
- When Codex review returns BLOCKED or remains unclear after bounded retries
|
|
53
|
-
|
|
54
|
-
Requirement:
|
|
55
|
-
- What:
|
|
56
|
-
- Why:
|
|
57
|
-
- Constraints:
|
|
58
|
-
- Done when:
|
|
44
|
+
/req Add a profile edit API
|
|
45
|
+
|
|
46
|
+
- What: PATCH /profile to change nickname and bio
|
|
47
|
+
- Why: users cannot change their profile after signup
|
|
48
|
+
- Constraints: reuse the existing auth middleware, no schema change
|
|
49
|
+
- Done when: unit tests pass, unauthorized users get 403
|
|
59
50
|
```
|
|
60
51
|
|
|
52
|
+
Outside Claude Code you can skip the slash command and state the requirement directly — `.cursor/rules` and `AGENTS.md` load the rules. If the four fields are missing, the agent asks first.
|
|
53
|
+
|
|
61
54
|
The agent's first reply should look roughly like this.
|
|
62
55
|
|
|
63
56
|
```text
|
|
@@ -69,7 +62,38 @@ Phases:
|
|
|
69
62
|
Control points: before req:commit --run / [B1] before a direct push to main (or [I1] open PR → [I2] merge)
|
|
70
63
|
```
|
|
71
64
|
|
|
72
|
-
|
|
65
|
+
### The agent follows whatever `req:next` says
|
|
66
|
+
|
|
67
|
+
The agent never guesses the next action. The tool computes it from `state.json` and git state.
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
npm run req:next -- 2026-002
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
[req:next] RUN REQ-2026-002
|
|
75
|
+
phase `phase-1`의 staged 변경을 리뷰받는다.
|
|
76
|
+
|
|
77
|
+
$ npm run req:review-codex -- 2026-002 --kind phase --phase phase-1 --run
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
| kind | Meaning | exit |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `RUN` | Run the printed command verbatim, then `req:next` again | 0 |
|
|
83
|
+
| `AGENT` | Work the tool cannot do (implement, write docs, `git add`) | 0 |
|
|
84
|
+
| `AWAIT_HUMAN` | **Control point** — do not proceed without the exact approval sentence | 10 |
|
|
85
|
+
| `DONE` | Nothing left for the tool. Integration is a separate control point | 11 |
|
|
86
|
+
| `BLOCKED` | Escalate to a human. Do not retry the same review | 2 |
|
|
87
|
+
|
|
88
|
+
Use `--json` for machine-readable output. It is **read-only** and changes no state.
|
|
89
|
+
|
|
90
|
+
Repeat this loop without stopping and it drives design → Codex review → implementation → re-review → commit. You only confirm at `AWAIT_HUMAN`.
|
|
91
|
+
|
|
92
|
+
### The reviewer persona is injected by the tool
|
|
93
|
+
|
|
94
|
+
`req:review-codex` puts `workflow/review-persona.md` in as the **first block** of the prompt. It is identical whether a human, Cursor, or Claude runs the command — it does not live where an agent can forget it. If the file is missing or empty, the review stops fail-closed.
|
|
95
|
+
|
|
96
|
+
Edit it for your project, or point `reviewPersonaPath` in `req.config.json` at a different file. Set it to `null` to disable.
|
|
73
97
|
|
|
74
98
|
Both integration paths are valid: **through a PR (optional)** and **direct push**. A PR is not mandatory. But a direct push to a protected branch **bypasses the required status checks**, so it needs a separate "branch protection bypass를 사용한 direct push 승인" — holding bypass permission is not approval. In that case CI runs **after** the push, so its green is post-hoc verification, and the agent must not omit that from its report. tag, npm publish, and GitHub release are control points of their own, requested after CI is green and never bundled with the integration approval. See [AGENTS.template.md](AGENTS.template.md) and [docs/RELEASING.md](docs/RELEASING.md) for the full contract.
|
|
75
99
|
|
|
@@ -98,12 +122,27 @@ In short: **approved changes pass, ambiguous changes stop.**
|
|
|
98
122
|
|
|
99
123
|
| Added item | Purpose |
|
|
100
124
|
|---|---|
|
|
101
|
-
| `scripts/req/` | `req:new`, `req:review-codex`, `req:doctor`, `req:commit` scripts |
|
|
125
|
+
| `scripts/req/` | `req:new`, `req:next`, `req:review-codex`, `req:doctor`, `req:commit` scripts |
|
|
102
126
|
| `workflow/*.schema.json` | Schemas for Codex responses and config |
|
|
127
|
+
| `workflow/review-persona.md` | Reviewer persona injected into the Codex review prompt |
|
|
103
128
|
| `req.config.json` | Project-level configuration |
|
|
104
|
-
| `AGENTS.md` |
|
|
129
|
+
| `AGENTS.md` | The contract (created only if absent) |
|
|
130
|
+
| `CLAUDE.md` | Claude Code pointer (created only if absent) |
|
|
131
|
+
| `.claude/skills/commitgate/SKILL.md` | Claude Code skill (pointer) |
|
|
132
|
+
| `.claude/commands/req.md` | `/req` slash command (pointer) |
|
|
133
|
+
| `.cursor/rules/commitgate.mdc` | Cursor rule (pointer) |
|
|
105
134
|
| `package.json` scripts | `req:*` commands and required devDependencies |
|
|
106
135
|
|
|
136
|
+
The entrypoint files are **thin pointers**. The contract itself lives only in `AGENTS.md`.
|
|
137
|
+
|
|
138
|
+
If another tool already owns `.claude/` or `.cursor/`, skip that layer.
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
npx commitgate --no-agent-entrypoints
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
If an `AGENTS.md` already exists without the CommitGate contract marker (`<!-- commitgate:contract -->`), the contract template is installed alongside it as `AGENTS.commitgate.md` and you are told to merge it. Your existing file is never touched.
|
|
145
|
+
|
|
107
146
|
Preview without writing files:
|
|
108
147
|
|
|
109
148
|
```sh
|
|
@@ -163,7 +202,7 @@ git checkout HEAD -- package.json
|
|
|
163
202
|
|
|
164
203
|
Delete only the paths `npx commitgate uninstall` listed. Removing `scripts/req/` or `workflow/` as whole directories would also take your own files and your ticket evidence with them.
|
|
165
204
|
|
|
166
|
-
> git does not track empty directories. After deleting the files, `git status` can report a clean tree while empty `scripts
|
|
205
|
+
> git does not track empty directories. After deleting the files, `git status` can report a clean tree while empty `scripts/`, `workflow/`, `.claude/`, and `.cursor/` directories remain on disk.
|
|
167
206
|
|
|
168
207
|
### If you already committed the scaffold
|
|
169
208
|
|
|
@@ -266,12 +305,22 @@ npm run req:commit -- 2026-001 --run --message-file commit-message.txt
|
|
|
266
305
|
| `npx commitgate` | Install CommitGate into a project |
|
|
267
306
|
| `npx commitgate --dry-run` | Preview the install plan without writing files |
|
|
268
307
|
| `npx commitgate --strict` | Treat low `cross-spawn` version warnings as install failures |
|
|
308
|
+
| `npx commitgate --no-agent-entrypoints` | Skip `.claude/`, `.cursor/`, and `CLAUDE.md` |
|
|
269
309
|
| `npx commitgate uninstall` | Preview the removal plan (read-only — deletes nothing) |
|
|
270
|
-
| `req:new <slug> --run` | Create a REQ ticket, branch, and design docs |
|
|
271
|
-
| `req:
|
|
272
|
-
| `req:review-codex <id> --kind
|
|
273
|
-
| `req:
|
|
274
|
-
| `req:
|
|
310
|
+
| `npm run req:new -- <slug> --run` | Create a REQ ticket, branch, and design docs |
|
|
311
|
+
| `npm run req:next -- <id> [--json]` | **Compute the next action** (read-only) |
|
|
312
|
+
| `npm run req:review-codex -- <id> --kind design --run` | Review the design |
|
|
313
|
+
| `npm run req:review-codex -- <id> --kind phase --phase <p> --run` | Review the implementation |
|
|
314
|
+
| `npm run req:doctor -- <id>` | Check gate status |
|
|
315
|
+
| `npm run req:commit -- <id> --run -m "message"` | Commit approved changes |
|
|
316
|
+
|
|
317
|
+
`req:*` are **`package.json` scripts**, not executables on your PATH. npm needs the `--` separator to pass arguments.
|
|
318
|
+
|
|
319
|
+
```sh
|
|
320
|
+
npm run req:next -- 2026-002 # npm
|
|
321
|
+
pnpm req:next 2026-002 # pnpm
|
|
322
|
+
yarn req:next 2026-002 # yarn
|
|
323
|
+
```
|
|
275
324
|
|
|
276
325
|
---
|
|
277
326
|
|
|
@@ -285,6 +334,7 @@ Defaults are enough for most projects. If needed, edit `req.config.json` in the
|
|
|
285
334
|
| `ticketRoot` | `"workflow"` | REQ ticket directory |
|
|
286
335
|
| `packageManager` | auto-detected | `npm`, `pnpm`, or `yarn` |
|
|
287
336
|
| `designDocs` | `00/01/02` docs | Design document filenames |
|
|
337
|
+
| `reviewPersonaPath` | `"workflow/review-persona.md"` | First block of the review prompt. `null` disables it |
|
|
288
338
|
|
|
289
339
|
Empty `branchPrefix` values and paths that escape the project root are rejected.
|
|
290
340
|
|
package/README.md
CHANGED
|
@@ -28,37 +28,30 @@ codex --version
|
|
|
28
28
|
codex login status
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
**긴 프롬프트를 붙여넣을 필요가 없습니다.** 설치가 에이전트 진입점을 함께 깝니다.
|
|
32
|
+
|
|
33
|
+
| 파일 | 읽는 도구 |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `AGENTS.md` | Codex CLI, Cursor — **계약 정본** |
|
|
36
|
+
| `.claude/skills/commitgate/SKILL.md` | Claude Code (요청에 맞으면 자동 발동) |
|
|
37
|
+
| `.claude/commands/req.md` | Claude Code (`/req` 명시 호출) |
|
|
38
|
+
| `.cursor/rules/commitgate.mdc` | Cursor (`alwaysApply`) |
|
|
39
|
+
| `CLAUDE.md` | Claude Code (항상 로드) — 부재 시에만 생성 |
|
|
40
|
+
|
|
41
|
+
에이전트에게 요구사항만 주면 됩니다.
|
|
32
42
|
|
|
33
43
|
```text
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- `req:review-codex`가 NEEDS_FIX/exit 3을 반환하면 findings를 수정하고 재리뷰한다.
|
|
41
|
-
- BLOCKED/exit 2를 반환하면 같은 리뷰를 재시도하지 말고 사람에게 보고하거나 리뷰 대상을 바꾼다. 스레드 고착이 의심되면 `--fresh-thread`로 한 번만 회복을 시도할 수 있다.
|
|
42
|
-
- 리뷰 대상은 git add 한 파일만이다.
|
|
43
|
-
- `state.json`과 `responses/`는 직접 `git add`하지 않는다.
|
|
44
|
-
|
|
45
|
-
멈춰서 확인받을 때(각 항목은 그 문장 그대로 승인받아야 하며, 한 승인은 다음 단계로 이월되지 않는다):
|
|
46
|
-
- req:commit --run 직전
|
|
47
|
-
- [경로 A · 선택] [I1] feature branch push + PR 생성 직전 / [I2] required checks green 확인 후 PR merge 직전
|
|
48
|
-
- [경로 B] [B1] protected branch에 direct push 직전 — "branch protection bypass를 사용한 direct push 승인"을 따로 받는다. 이 push는 required checks를 우회하고, CI는 사후에 돈다
|
|
49
|
-
- [R1/R2/R3] tag 생성·push / npm publish / GitHub release — CI green 확인 후 각각 별도 승인
|
|
50
|
-
- reset, clean, force push 같은 destructive 작업 전
|
|
51
|
-
- 요구사항 범위를 바꿔야 할 때
|
|
52
|
-
- Codex 리뷰가 BLOCKED를 반환하거나 제한된 재시도 후에도 판단이 불명확할 때
|
|
53
|
-
|
|
54
|
-
요구사항:
|
|
55
|
-
- 무엇을:
|
|
56
|
-
- 왜:
|
|
57
|
-
- 제약:
|
|
58
|
-
- 완료 기준:
|
|
44
|
+
/req 프로필 수정 API를 추가해줘
|
|
45
|
+
|
|
46
|
+
- 무엇을: PATCH /profile 로 닉네임·소개글 수정
|
|
47
|
+
- 왜: 지금은 가입 후 프로필을 바꿀 방법이 없다
|
|
48
|
+
- 제약: 기존 인증 미들웨어 재사용, 스키마 변경 없음
|
|
49
|
+
- 완료 기준: 단위 테스트 통과, 권한 없는 사용자는 403
|
|
59
50
|
```
|
|
60
51
|
|
|
61
|
-
|
|
52
|
+
Claude Code가 아니면 슬래시 커맨드 없이 요구사항만 주어도 됩니다(`.cursor/rules`·`AGENTS.md`가 규칙을 로드합니다). 네 칸이 비어 있으면 에이전트가 먼저 물어봅니다.
|
|
53
|
+
|
|
54
|
+
첫 응답은 보통 이렇게 나옵니다.
|
|
62
55
|
|
|
63
56
|
```text
|
|
64
57
|
REQ-2026-002 발행
|
|
@@ -69,7 +62,38 @@ phase:
|
|
|
69
62
|
통제점: req:commit --run 직전 / [B1] main direct push 직전 (또는 [I1] PR 생성 → [I2] merge)
|
|
70
63
|
```
|
|
71
64
|
|
|
72
|
-
|
|
65
|
+
### 에이전트는 `req:next`가 시키는 대로 진행합니다
|
|
66
|
+
|
|
67
|
+
다음 행동을 에이전트가 추측하지 않습니다. 도구가 `state.json`과 git 상태에서 계산합니다.
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
npm run req:next -- 2026-002
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
[req:next] RUN REQ-2026-002
|
|
75
|
+
phase `phase-1`의 staged 변경을 리뷰받는다.
|
|
76
|
+
|
|
77
|
+
$ npm run req:review-codex -- 2026-002 --kind phase --phase phase-1 --run
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
| kind | 뜻 | exit |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `RUN` | 출력된 명령을 그대로 실행하고 다시 `req:next` | 0 |
|
|
83
|
+
| `AGENT` | 도구가 대신 못 하는 작업(구현·문서 작성·`git add`) | 0 |
|
|
84
|
+
| `AWAIT_HUMAN` | **통제점** — 출력된 승인 문장을 그대로 받기 전엔 진행 금지 | 10 |
|
|
85
|
+
| `DONE` | 이 티켓에서 도구가 할 일 없음. 통합은 별도 통제점 | 11 |
|
|
86
|
+
| `BLOCKED` | 사람에게 보고. 같은 리뷰 재시도 금지 | 2 |
|
|
87
|
+
|
|
88
|
+
`--json`으로 기계 판독할 수 있습니다. **읽기 전용**이라 어떤 상태도 바꾸지 않습니다.
|
|
89
|
+
|
|
90
|
+
이 루프를 끊지 말고 반복하면 설계 → Codex 리뷰 → 구현 → 재리뷰 → 커밋이 진행됩니다. 사용자는 `AWAIT_HUMAN`에서만 확인하면 됩니다.
|
|
91
|
+
|
|
92
|
+
### 리뷰어 페르소나는 도구가 주입합니다
|
|
93
|
+
|
|
94
|
+
`req:review-codex`는 `workflow/review-persona.md`를 프롬프트 **첫 블록**으로 넣습니다. 사람이 직접 실행하든, Cursor가 실행하든, Claude가 실행하든 동일합니다 — 에이전트가 잊을 수 있는 자리에 두지 않습니다. 파일이 없거나 비어 있으면 리뷰가 fail-closed로 멈춥니다.
|
|
95
|
+
|
|
96
|
+
내용을 프로젝트에 맞게 고치거나, `req.config.json`의 `reviewPersonaPath`로 다른 파일을 지정할 수 있습니다. `null`로 두면 비활성화됩니다.
|
|
73
97
|
|
|
74
98
|
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)를 참고하세요.
|
|
75
99
|
|
|
@@ -98,12 +122,27 @@ CommitGate가 막는 것은 단순한 명령 실수가 아니라 **리뷰받지
|
|
|
98
122
|
|
|
99
123
|
| 추가 항목 | 설명 |
|
|
100
124
|
|---|---|
|
|
101
|
-
| `scripts/req/` | `req:new`, `req:review-codex`, `req:doctor`, `req:commit` 스크립트 |
|
|
125
|
+
| `scripts/req/` | `req:new`, `req:next`, `req:review-codex`, `req:doctor`, `req:commit` 스크립트 |
|
|
102
126
|
| `workflow/*.schema.json` | Codex 응답과 설정 검증 스키마 |
|
|
127
|
+
| `workflow/review-persona.md` | Codex 리뷰 프롬프트에 주입되는 리뷰어 페르소나 |
|
|
103
128
|
| `req.config.json` | 프로젝트별 설정 |
|
|
104
|
-
| `AGENTS.md` |
|
|
129
|
+
| `AGENTS.md` | 계약 정본 (없을 때만 생성) |
|
|
130
|
+
| `CLAUDE.md` | Claude Code 지침 포인터 (없을 때만 생성) |
|
|
131
|
+
| `.claude/skills/commitgate/SKILL.md` | Claude Code 스킬 (포인터) |
|
|
132
|
+
| `.claude/commands/req.md` | `/req` 슬래시 커맨드 (포인터) |
|
|
133
|
+
| `.cursor/rules/commitgate.mdc` | Cursor 규칙 (포인터) |
|
|
105
134
|
| `package.json` 스크립트 | `req:*` 명령과 필요한 devDependencies |
|
|
106
135
|
|
|
136
|
+
진입점 파일들은 **얇은 포인터**입니다. 계약 본문은 `AGENTS.md` 하나에만 있습니다.
|
|
137
|
+
|
|
138
|
+
`.claude/`·`.cursor/`를 다른 도구가 쓰고 있다면 건너뛸 수 있습니다.
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
npx commitgate --no-agent-entrypoints
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
기존 `AGENTS.md`가 있는데 CommitGate 계약 마커(`<!-- commitgate:contract -->`)가 없으면, 계약 템플릿을 `AGENTS.commitgate.md`로 함께 놓고 병합을 안내합니다. 기존 파일은 건드리지 않습니다.
|
|
145
|
+
|
|
107
146
|
미리보기만 하려면:
|
|
108
147
|
|
|
109
148
|
```sh
|
|
@@ -163,7 +202,7 @@ git checkout HEAD -- package.json
|
|
|
163
202
|
|
|
164
203
|
파일 삭제는 `npx commitgate uninstall`이 나열해 준 경로만 지우세요. `scripts/req/`나 `workflow/`를 디렉터리째 지우면 그 안의 사용자 파일이나 티켓 증거가 함께 사라집니다.
|
|
165
204
|
|
|
166
|
-
> git은 빈 디렉터리를 추적하지 않습니다. 파일을 다 지운 뒤 `git status`가 clean이어도 빈 `scripts/`·`workflow/`가 파일시스템에 남을 수 있습니다.
|
|
205
|
+
> git은 빈 디렉터리를 추적하지 않습니다. 파일을 다 지운 뒤 `git status`가 clean이어도 빈 `scripts/`·`workflow/`·`.claude/`·`.cursor/`가 파일시스템에 남을 수 있습니다.
|
|
167
206
|
|
|
168
207
|
### 이미 커밋했다면
|
|
169
208
|
|
|
@@ -224,7 +263,7 @@ Windows에서 설치 직후 `codex` 명령을 못 찾으면 새 터미널을 열
|
|
|
224
263
|
|
|
225
264
|
## 수동 명령
|
|
226
265
|
|
|
227
|
-
대부분의 사용자는
|
|
266
|
+
대부분의 사용자는 `req:next`가 시키는 대로만 하면 됩니다. 아래는 내부에서 어떤 명령이 실행되는지 이해하거나 직접 디버깅할 때만 보면 됩니다.
|
|
228
267
|
|
|
229
268
|
```sh
|
|
230
269
|
# 1. 티켓과 브랜치 생성
|
|
@@ -266,12 +305,22 @@ npm run req:commit -- 2026-001 --run --message-file commit-message.txt
|
|
|
266
305
|
| `npx commitgate` | 프로젝트에 CommitGate 설치 |
|
|
267
306
|
| `npx commitgate --dry-run` | 파일을 쓰지 않고 설치 계획 확인 |
|
|
268
307
|
| `npx commitgate --strict` | 낮은 `cross-spawn` 버전 경고를 설치 실패로 처리 |
|
|
308
|
+
| `npx commitgate --no-agent-entrypoints` | `.claude/`·`.cursor/`·`CLAUDE.md` 설치 건너뛰기 |
|
|
269
309
|
| `npx commitgate uninstall` | 제거 계획 확인 (읽기 전용 — 아무것도 지우지 않음) |
|
|
270
|
-
| `req:new <slug> --run` | REQ 티켓, 브랜치, 설계문서 생성 |
|
|
271
|
-
| `req:
|
|
272
|
-
| `req:review-codex <id> --kind
|
|
273
|
-
| `req:
|
|
274
|
-
| `req:
|
|
310
|
+
| `npm run req:new -- <slug> --run` | REQ 티켓, 브랜치, 설계문서 생성 |
|
|
311
|
+
| `npm run req:next -- <id> [--json]` | **다음 행동 계산** (읽기 전용) |
|
|
312
|
+
| `npm run req:review-codex -- <id> --kind design --run` | 설계 리뷰 |
|
|
313
|
+
| `npm run req:review-codex -- <id> --kind phase --phase <p> --run` | 구현 리뷰 |
|
|
314
|
+
| `npm run req:doctor -- <id>` | 게이트 상태 확인 |
|
|
315
|
+
| `npm run req:commit -- <id> --run -m "message"` | 승인된 변경 커밋 |
|
|
316
|
+
|
|
317
|
+
`req:*`는 PATH에 잡히는 실행 파일이 아니라 **`package.json` 스크립트**입니다. npm은 인자 전달에 `--` 구분자가 필요합니다.
|
|
318
|
+
|
|
319
|
+
```sh
|
|
320
|
+
npm run req:next -- 2026-002 # npm
|
|
321
|
+
pnpm req:next 2026-002 # pnpm
|
|
322
|
+
yarn req:next 2026-002 # yarn
|
|
323
|
+
```
|
|
275
324
|
|
|
276
325
|
---
|
|
277
326
|
|
|
@@ -285,6 +334,7 @@ npm run req:commit -- 2026-001 --run --message-file commit-message.txt
|
|
|
285
334
|
| `ticketRoot` | `"workflow"` | REQ 티켓 폴더 |
|
|
286
335
|
| `packageManager` | 자동 감지 | `npm`, `pnpm`, `yarn` |
|
|
287
336
|
| `designDocs` | `00/01/02` 문서 | 설계 문서 파일명 |
|
|
337
|
+
| `reviewPersonaPath` | `"workflow/review-persona.md"` | 리뷰 프롬프트 첫 블록. `null`이면 비활성 |
|
|
288
338
|
|
|
289
339
|
빈 `branchPrefix`나 프로젝트 밖으로 나가는 경로는 거부됩니다.
|
|
290
340
|
|
package/bin/init.ts
CHANGED
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
*
|
|
5
5
|
* 동작(멱등·비파괴):
|
|
6
6
|
* 1. 대상 repo 감사(git repo·package.json 필수 → 없으면 fail-closed throw)
|
|
7
|
-
* 2. `scripts/req/**` + `
|
|
7
|
+
* 2. `scripts/req/**` + `KIT_COPY_RELPATHS`(스키마 2종 + review-persona.md) 복사(기존 파일은 --force 없으면 스킵)
|
|
8
8
|
* 3. `req.config.json` 시드(부재 시): 감지한 packageManager + handoffPath:null(프로젝트별 값은 코어 DEFAULTS가 아니라 config에서 흡수)
|
|
9
9
|
* 4. 대상 `package.json`에 req:* 스크립트·devDeps(ajv/tsx) 주입(기존 키 미덮어씀)
|
|
10
10
|
* 5. `AGENTS.md` 부재 시 템플릿 생성(있으면 스킵 — Codex 계약 보존)
|
|
11
|
+
* 6. 에이전트 진입점(.claude/skills·.claude/commands·.cursor/rules) 복사 + `CLAUDE.md` 부재 시 생성 (--no-agent-entrypoints로 생략)
|
|
11
12
|
*
|
|
12
13
|
* 코어 승인 바인딩·staged tree 검증은 건드리지 않는다(복사만). 프로젝트 차이는 req.config.json에서만 흡수.
|
|
13
14
|
*/
|
|
@@ -24,7 +25,7 @@ import {
|
|
|
24
25
|
import { execFileSync } from 'node:child_process'
|
|
25
26
|
import { resolve, join, dirname, relative } from 'node:path'
|
|
26
27
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
27
|
-
import { loadConfig, stripBom, type PackageManager } from '../scripts/req/lib/config'
|
|
28
|
+
import { loadConfig, stripBom, DEFAULT_REVIEW_PERSONA_RELPATH, type PackageManager } from '../scripts/req/lib/config'
|
|
28
29
|
import { createGitAdapter, type GitRunner } from '../scripts/req/lib/adapters'
|
|
29
30
|
import * as semver from 'semver'
|
|
30
31
|
|
|
@@ -41,11 +42,58 @@ export const KIT_SOURCE_DIR_REL = 'scripts/req'
|
|
|
41
42
|
*/
|
|
42
43
|
export const KIT_SCHEMA_RELPATHS = ['workflow/machine.schema.json', 'workflow/req.config.schema.json'] as const
|
|
43
44
|
|
|
45
|
+
/**
|
|
46
|
+
* init이 `scripts/req/**` 외에 **실제로 복사하는** 파일 목록(패키지-상대 = 대상-상대). 복사기와 uninstall planner의 SSOT.
|
|
47
|
+
*
|
|
48
|
+
* ⚠️ `KIT_SCHEMA_RELPATHS`와 **의미가 다르다** — 그 상수는 "설정된 `schemaPath`가 init이 깐 스키마인가"를
|
|
49
|
+
* 판정하는 **스키마 축**이고(`bin/uninstall.ts`), 이 상수는 **복사 축**이다. 여기 persona를 넣되 저기엔 넣지 않는다.
|
|
50
|
+
*
|
|
51
|
+
* ⚠️ `package.json`의 `files[]`는 또 다른 축(npm tarball)이다. **셋을 혼동하면**
|
|
52
|
+
* tarball엔 실렸는데 대상 repo엔 안 깔리는 파일이 생기고, phase-1b의 persona fail-closed와 만나
|
|
53
|
+
* 신규 설치본의 모든 리뷰가 멈춘다(REQ-2026-010 design R1 P1).
|
|
54
|
+
*/
|
|
55
|
+
export const KIT_COPY_RELPATHS = [...KIT_SCHEMA_RELPATHS, DEFAULT_REVIEW_PERSONA_RELPATH] as const
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 에이전트 진입점 (REQ-2026-010 D7·D8). init 복사기와 uninstall planner가 공유하는 SSOT.
|
|
59
|
+
*
|
|
60
|
+
* ⚠️ `KIT_COPY_RELPATHS`와 달리 **`src !== dest`**다. `copyInto`는 `relative(PACKAGE_ROOT, src)`로 레이아웃을
|
|
61
|
+
* 재현하므로 쓸 수 없고, `uninstall`의 `tool` 분류도 `join(PACKAGE_ROOT, rel)`로 원본을 찾을 수 없다.
|
|
62
|
+
* 두 곳 모두 `src`/`dest`를 분리해 다뤄야 한다.
|
|
63
|
+
*
|
|
64
|
+
* 본문 SSOT는 `AGENTS.md`다. 여기 깔리는 파일은 **얇은 포인터**이며 계약 본문을 복제하지 않는다 — 복제하면 drift 부채가 된다.
|
|
65
|
+
*/
|
|
66
|
+
export const KIT_AGENT_ENTRYPOINTS = [
|
|
67
|
+
{ src: 'templates/claude-skill.md', dest: '.claude/skills/commitgate/SKILL.md' },
|
|
68
|
+
{ src: 'templates/claude-command.md', dest: '.claude/commands/req.md' },
|
|
69
|
+
{ src: 'templates/cursor-rule.mdc', dest: '.cursor/rules/commitgate.mdc' },
|
|
70
|
+
] as const
|
|
71
|
+
|
|
72
|
+
/** `CLAUDE.md`는 `AGENTS.md`와 같은 취급 — **부재 시에만** 생성하고, uninstall에서 `ambiguous`(자동 제거 금지). */
|
|
73
|
+
export const KIT_CLAUDE_TEMPLATE_REL = 'templates/CLAUDE.template.md'
|
|
74
|
+
export const KIT_CLAUDE_DEST_REL = 'CLAUDE.md'
|
|
75
|
+
|
|
76
|
+
/** `AGENTS.md`가 CommitGate 계약인지 판별하는 마커. 진입점 포인터들이 이 마커로 SSOT를 확인한다. */
|
|
77
|
+
export const AGENTS_CONTRACT_MARKER = '<!-- commitgate:contract -->'
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 기존 `AGENTS.md`에 계약 마커가 없을 때 **대상 repo에 함께 놓는** 계약 템플릿 사본 (phase-3a R1 P2).
|
|
81
|
+
*
|
|
82
|
+
* ⚠️ 이게 없으면 포인터의 복구 지시가 **막다른 길**이 된다. 진입점 파일들은 "마커가 없으면 계약 템플릿을
|
|
83
|
+
* 참조해 병합하라"고 하는데, `AGENTS.template.md`는 **패키지 안에만** 있고 대상 repo에는 복사되지 않는다.
|
|
84
|
+
* `npx commitgate`는 전역/로컬 설치가 아니라 npm 캐시에서 한 번 실행될 뿐이라 `node_modules/commitgate/`도
|
|
85
|
+
* 남지 않는다. 사용자는 참조할 파일을 찾을 수 없다.
|
|
86
|
+
*
|
|
87
|
+
* 그래서 마커가 없을 때만 이 경로로 사본을 놓는다. 마커가 있으면(정상) 이 파일은 만들지 않는다.
|
|
88
|
+
*/
|
|
89
|
+
export const KIT_AGENTS_CONTRACT_COPY_REL = 'AGENTS.commitgate.md'
|
|
90
|
+
|
|
44
91
|
/** 대상 package.json에 주입할 req:* 스크립트. */
|
|
45
92
|
export const REQ_SCRIPTS: Record<string, string> = {
|
|
46
93
|
'req:new': 'tsx scripts/req/req-new.ts',
|
|
47
94
|
'req:review-codex': 'tsx scripts/req/review-codex.ts',
|
|
48
95
|
'req:doctor': 'tsx scripts/req/req-doctor.ts',
|
|
96
|
+
'req:next': 'tsx scripts/req/req-next.ts',
|
|
49
97
|
'req:commit': 'tsx scripts/req/req-commit.ts',
|
|
50
98
|
}
|
|
51
99
|
|
|
@@ -64,6 +112,8 @@ export interface InitOptions {
|
|
|
64
112
|
force: boolean
|
|
65
113
|
dryRun: boolean
|
|
66
114
|
strict: boolean // cross-spawn 하한 미만이면 WARN 대신 throw(#1)
|
|
115
|
+
/** `.claude/`·`.cursor/`·`CLAUDE.md`를 건너뛴다. 다른 도구가 그 디렉터리를 쓰는 repo를 위한 opt-out(D7). */
|
|
116
|
+
noAgentEntrypoints?: boolean
|
|
67
117
|
}
|
|
68
118
|
|
|
69
119
|
export interface InitResult {
|
|
@@ -77,6 +127,10 @@ export interface InitResult {
|
|
|
77
127
|
packageManager: PackageManager
|
|
78
128
|
crossSpawnFloorWarned: boolean // 기존 cross-spawn이 보안 하한 미만이라 경고(#1)
|
|
79
129
|
dryRun: boolean
|
|
130
|
+
claudeMdCreated: boolean // CLAUDE.md를 새로 만들었는가(있으면 미덮어씀)
|
|
131
|
+
agentsMarkerMissing: boolean // 기존 AGENTS.md에 commitgate 계약 마커가 없어 경고했는가
|
|
132
|
+
agentsContractCopyCreated: boolean // 마커 부재 시 AGENTS.commitgate.md(계약 템플릿 사본)를 놓았는가
|
|
133
|
+
agentEntrypointsSkipped: boolean // --no-agent-entrypoints
|
|
80
134
|
}
|
|
81
135
|
|
|
82
136
|
/**
|
|
@@ -164,6 +218,54 @@ function copyInto(
|
|
|
164
218
|
}
|
|
165
219
|
}
|
|
166
220
|
|
|
221
|
+
/**
|
|
222
|
+
* 진입점 dest 경로들이 **실제로 만들어질 수 있는지** preflight에서 확인한다 (D8).
|
|
223
|
+
*
|
|
224
|
+
* `mkdirSync(recursive)`는 경로 중간 컴포넌트가 **파일**이면 ENOTDIR로 죽는다. apply 단계에서 그러면
|
|
225
|
+
* 앞의 파일들은 이미 복사된 뒤라 **부분 설치**가 된다. 쓰기 전에 걸러서 preflight→apply 계약을 지킨다.
|
|
226
|
+
*/
|
|
227
|
+
function assertEntrypointPathsUsable(targetRoot: string): void {
|
|
228
|
+
const dests = [...KIT_AGENT_ENTRYPOINTS.map((e) => e.dest), KIT_CLAUDE_DEST_REL, KIT_AGENTS_CONTRACT_COPY_REL]
|
|
229
|
+
for (const dest of dests) {
|
|
230
|
+
const parts = dest.split('/')
|
|
231
|
+
// 마지막(파일명) 제외한 각 디렉터리 컴포넌트가 파일로 존재하면 mkdir 불가.
|
|
232
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
233
|
+
const sub = join(targetRoot, ...parts.slice(0, i + 1))
|
|
234
|
+
if (existsSync(sub) && !statSync(sub).isDirectory())
|
|
235
|
+
throw new Error(
|
|
236
|
+
`진입점 설치 불가: ${parts.slice(0, i + 1).join('/')} 가 디렉터리가 아니라 파일입니다(${dest} 를 만들 수 없음).\n` +
|
|
237
|
+
` → 해당 파일을 옮기거나, --no-agent-entrypoints 로 이 계층을 건너뛰세요.`,
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
const abs = join(targetRoot, dest)
|
|
241
|
+
if (existsSync(abs) && statSync(abs).isDirectory())
|
|
242
|
+
throw new Error(`진입점 설치 불가: ${dest} 가 디렉터리로 존재합니다(파일이어야 함).`)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* `src → dest`(경로가 다름) 복사. 기존 파일은 `--force` 없으면 스킵. 중첩 디렉터리를 만든다.
|
|
248
|
+
* `copyInto`(레이아웃 재현)와 달리 매핑이 명시적이다.
|
|
249
|
+
*/
|
|
250
|
+
function copyEntrypoints(
|
|
251
|
+
targetRoot: string,
|
|
252
|
+
opts: InitOptions,
|
|
253
|
+
copied: string[],
|
|
254
|
+
skipped: string[],
|
|
255
|
+
): void {
|
|
256
|
+
for (const { src, dest } of KIT_AGENT_ENTRYPOINTS) {
|
|
257
|
+
const destAbs = join(targetRoot, dest)
|
|
258
|
+
if (existsSync(destAbs) && !opts.force) {
|
|
259
|
+
skipped.push(dest)
|
|
260
|
+
continue
|
|
261
|
+
}
|
|
262
|
+
copied.push(dest)
|
|
263
|
+
if (opts.dryRun) continue
|
|
264
|
+
mkdirSync(dirname(destAbs), { recursive: true })
|
|
265
|
+
copyFileSync(join(PACKAGE_ROOT, src), destAbs)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
167
269
|
/** JSON 파일을 객체로 파싱(fail-closed). 파싱 실패·비-객체(배열/원시)면 throw. */
|
|
168
270
|
function parseJsonObject(path: string, label: string): Record<string, unknown> {
|
|
169
271
|
let parsed: unknown
|
|
@@ -310,7 +412,7 @@ export function runInit(opts: InitOptions): InitResult {
|
|
|
310
412
|
const packageManager = detectPackageManager(targetRoot)
|
|
311
413
|
|
|
312
414
|
// req.config.json 계획(쓰기 없음). handoffPath:null·packageManager를 항상 보장 —
|
|
313
|
-
// 코어
|
|
415
|
+
// handoffPath는 프로젝트별 값이라 코어 기본이 비활성(null)이다 — 그 비활성을 config에 **명시 기록**한다(암묵 < 명시). 기존 키 보존.
|
|
314
416
|
let configAction: 'created' | 'merged' | 'unchanged' = 'unchanged'
|
|
315
417
|
const configKeysAdded: string[] = []
|
|
316
418
|
let configToWrite: Record<string, unknown> | null = null
|
|
@@ -354,13 +456,29 @@ export function runInit(opts: InitOptions): InitResult {
|
|
|
354
456
|
const agentsPath = join(targetRoot, 'AGENTS.md')
|
|
355
457
|
const agentsCreated = !existsSync(agentsPath)
|
|
356
458
|
|
|
459
|
+
const agentEntrypointsSkipped = opts.noAgentEntrypoints === true
|
|
460
|
+
if (!agentEntrypointsSkipped) assertEntrypointPathsUsable(targetRoot)
|
|
461
|
+
|
|
462
|
+
// 기존 AGENTS.md에 계약 마커가 없으면 진입점 포인터가 **엉뚱한 SSOT**를 가리키게 된다(design R1 observation).
|
|
463
|
+
// 설치는 계속한다(비파괴 원칙) — 사용자가 병합하도록 알릴 뿐.
|
|
464
|
+
const agentsMarkerMissing =
|
|
465
|
+
!agentEntrypointsSkipped && !agentsCreated && !readFileSync(agentsPath, 'utf8').includes(AGENTS_CONTRACT_MARKER)
|
|
466
|
+
|
|
467
|
+
const claudeMdPath = join(targetRoot, KIT_CLAUDE_DEST_REL)
|
|
468
|
+
const claudeMdCreated = !agentEntrypointsSkipped && !existsSync(claudeMdPath)
|
|
469
|
+
|
|
470
|
+
// 마커가 없으면 포인터가 참조할 계약 템플릿을 **대상 repo에** 놓는다 — 그러지 않으면 복구 지시가 막다른 길이다.
|
|
471
|
+
const contractCopyPath = join(targetRoot, KIT_AGENTS_CONTRACT_COPY_REL)
|
|
472
|
+
const agentsContractCopyCreated = agentsMarkerMissing && (!existsSync(contractCopyPath) || opts.force)
|
|
473
|
+
|
|
357
474
|
// ══ Apply: 여기부터 쓰기(preflight 전부 통과 후에만) ═════════════════
|
|
358
475
|
const copied: string[] = []
|
|
359
476
|
const skipped: string[] = []
|
|
360
477
|
copyInto(walkFiles(join(PACKAGE_ROOT, KIT_SOURCE_DIR_REL)), PACKAGE_ROOT, targetRoot, opts, copied, skipped)
|
|
361
|
-
// ⚠️
|
|
362
|
-
const
|
|
363
|
-
copyInto(
|
|
478
|
+
// ⚠️ KIT_COPY_RELPATHS는 패키지-상대 = 대상-상대(리터럴 `workflow/`). ticketRoot/schemaPath 설정과 무관 — uninstall planner와 공유하는 SSOT.
|
|
479
|
+
const kitFiles = KIT_COPY_RELPATHS.map((rel) => join(PACKAGE_ROOT, rel))
|
|
480
|
+
copyInto(kitFiles, PACKAGE_ROOT, targetRoot, opts, copied, skipped)
|
|
481
|
+
if (!agentEntrypointsSkipped) copyEntrypoints(targetRoot, opts, copied, skipped)
|
|
364
482
|
|
|
365
483
|
if (!opts.dryRun) {
|
|
366
484
|
if (configToWrite) writeFileSync(cfgPath, JSON.stringify(configToWrite, null, 2) + '\n', 'utf8')
|
|
@@ -370,6 +488,23 @@ export function runInit(opts: InitOptions): InitResult {
|
|
|
370
488
|
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8')
|
|
371
489
|
}
|
|
372
490
|
if (agentsCreated) copyFileSync(join(PACKAGE_ROOT, 'AGENTS.template.md'), agentsPath)
|
|
491
|
+
// CLAUDE.md는 AGENTS.md와 동일 정책: **부재 시에만** 생성(--force로도 덮어쓰지 않는다 — 사용자 파일일 수 있다).
|
|
492
|
+
if (claudeMdCreated) copyFileSync(join(PACKAGE_ROOT, KIT_CLAUDE_TEMPLATE_REL), claudeMdPath)
|
|
493
|
+
if (agentsContractCopyCreated) copyFileSync(join(PACKAGE_ROOT, 'AGENTS.template.md'), contractCopyPath)
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (agentsMarkerMissing) {
|
|
497
|
+
// phase-3a R1 observation: 사본을 **새로 놓았을 때**와 **기존 것을 보존했을 때**의 문구가 달라야 한다.
|
|
498
|
+
// "설치했습니다"가 두 경우 모두에 나오면, 편집된 사본을 그대로 둔 사용자가 새 템플릿을 받았다고 오해한다.
|
|
499
|
+
const copyNote = opts.dryRun
|
|
500
|
+
? `계약 템플릿을 ${KIT_AGENTS_CONTRACT_COPY_REL} 로 설치할 예정입니다.`
|
|
501
|
+
: agentsContractCopyCreated
|
|
502
|
+
? `계약 템플릿을 ${KIT_AGENTS_CONTRACT_COPY_REL} 로 함께 설치했습니다.`
|
|
503
|
+
: `${KIT_AGENTS_CONTRACT_COPY_REL} 가 이미 있어 보존했습니다(덮어쓰려면 --force).`
|
|
504
|
+
console.warn(
|
|
505
|
+
`⚠️ 기존 AGENTS.md에 ${AGENTS_CONTRACT_MARKER} 마커가 없습니다 — .claude/·.cursor/ 진입점이 가리킬 CommitGate 계약이 그 파일에 없습니다.\n` +
|
|
506
|
+
` ${copyNote} 그 내용을 AGENTS.md에 병합한 뒤 ${KIT_AGENTS_CONTRACT_COPY_REL} 를 지우세요(설치는 계속됩니다).`,
|
|
507
|
+
)
|
|
373
508
|
}
|
|
374
509
|
|
|
375
510
|
return {
|
|
@@ -383,6 +518,10 @@ export function runInit(opts: InitOptions): InitResult {
|
|
|
383
518
|
packageManager,
|
|
384
519
|
crossSpawnFloorWarned,
|
|
385
520
|
dryRun: opts.dryRun,
|
|
521
|
+
claudeMdCreated,
|
|
522
|
+
agentsMarkerMissing,
|
|
523
|
+
agentsContractCopyCreated,
|
|
524
|
+
agentEntrypointsSkipped,
|
|
386
525
|
}
|
|
387
526
|
}
|
|
388
527
|
|
|
@@ -391,6 +530,7 @@ export function parseArgs(argv: string[]): InitOptions {
|
|
|
391
530
|
let force = false
|
|
392
531
|
let dryRun = false
|
|
393
532
|
let strict = false
|
|
533
|
+
let noAgentEntrypoints = false
|
|
394
534
|
for (let i = 0; i < argv.length; i++) {
|
|
395
535
|
const a = argv[i]
|
|
396
536
|
if (a === '--dir') {
|
|
@@ -404,6 +544,8 @@ export function parseArgs(argv: string[]): InitOptions {
|
|
|
404
544
|
dryRun = true
|
|
405
545
|
} else if (a === '--strict') {
|
|
406
546
|
strict = true
|
|
547
|
+
} else if (a === '--no-agent-entrypoints') {
|
|
548
|
+
noAgentEntrypoints = true
|
|
407
549
|
} else if (a === '-h' || a === '--help') {
|
|
408
550
|
printHelp()
|
|
409
551
|
process.exit(0)
|
|
@@ -411,7 +553,7 @@ export function parseArgs(argv: string[]): InitOptions {
|
|
|
411
553
|
throw new Error(`알 수 없는 인자: ${a}`)
|
|
412
554
|
}
|
|
413
555
|
}
|
|
414
|
-
return { dir: resolve(dir), force, dryRun, strict }
|
|
556
|
+
return { dir: resolve(dir), force, dryRun, strict, noAgentEntrypoints }
|
|
415
557
|
}
|
|
416
558
|
|
|
417
559
|
function printHelp(): void {
|
|
@@ -426,6 +568,8 @@ function printHelp(): void {
|
|
|
426
568
|
--force 기존 kit 파일 덮어쓰기(기본: 스킵)
|
|
427
569
|
--dry-run 변경 없이 수행 예정 목록만 출력
|
|
428
570
|
--strict 기존 cross-spawn이 보안 하한(>=7.0.6) 미만이면 경고 대신 중단(fail-closed)
|
|
571
|
+
--no-agent-entrypoints
|
|
572
|
+
.claude/·.cursor/·CLAUDE.md 진입점 설치를 건너뛴다
|
|
429
573
|
-h, --help 도움말
|
|
430
574
|
|
|
431
575
|
설치 후:
|
|
@@ -458,6 +602,11 @@ export function main(argv: string[]): void {
|
|
|
458
602
|
`${tag} package.json: ${r.packageJsonAdded.length > 0 ? '추가 ' + r.packageJsonAdded.join(', ') : '변경 없음'}`,
|
|
459
603
|
)
|
|
460
604
|
console.log(`${tag} AGENTS.md: ${r.agentsCreated ? '템플릿 생성' : '이미 존재(유지)'}`)
|
|
605
|
+
if (r.agentEntrypointsSkipped) console.log(`${tag} 에이전트 진입점: 건너뜀(--no-agent-entrypoints)`)
|
|
606
|
+
else {
|
|
607
|
+
console.log(`${tag} CLAUDE.md: ${r.claudeMdCreated ? '템플릿 생성' : '이미 존재(유지)'}`)
|
|
608
|
+
if (r.agentsContractCopyCreated) console.log(`${tag} ${KIT_AGENTS_CONTRACT_COPY_REL}: 계약 템플릿 사본 생성(AGENTS.md에 병합 후 삭제)`)
|
|
609
|
+
}
|
|
461
610
|
if (r.crossSpawnFloorWarned) console.log(`${tag} ⚠️ cross-spawn 버전 하한 경고(위 참조) — 강제 중단은 --strict`)
|
|
462
611
|
if (!r.dryRun) {
|
|
463
612
|
console.log(`\n다음:`)
|