@wooojin/forgen 0.5.5 → 0.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +129 -0
  3. package/README.ja.md +5 -1
  4. package/README.ko.md +6 -2
  5. package/README.md +6 -2
  6. package/README.zh.md +5 -1
  7. package/assets/claude/skills/verify/SKILL.md +66 -0
  8. package/assets/shared/hook-registry.json +10 -3
  9. package/dist/cli.js +7 -3
  10. package/dist/core/doctor.js +9 -1
  11. package/dist/core/health-cli.js +11 -12
  12. package/dist/core/paths.d.ts +1 -1
  13. package/dist/core/settings-lock.d.ts +7 -1
  14. package/dist/core/settings-lock.js +19 -4
  15. package/dist/core/status-cli.d.ts +1 -1
  16. package/dist/core/trust-layer-intent.d.ts +2 -2
  17. package/dist/core/uninstall.d.ts +12 -0
  18. package/dist/core/uninstall.js +83 -4
  19. package/dist/engine/solution-fixup.js +2 -2
  20. package/dist/engine/solution-format.js +4 -4
  21. package/dist/engine/solution-quarantine.js +2 -2
  22. package/dist/hooks/context-guard.d.ts +3 -0
  23. package/dist/hooks/context-guard.js +9 -5
  24. package/dist/hooks/hook-registry.d.ts +11 -0
  25. package/dist/hooks/hooks-generator.d.ts +2 -0
  26. package/dist/hooks/hooks-generator.js +5 -1
  27. package/dist/hooks/session-end.d.ts +10 -3
  28. package/dist/hooks/session-end.js +17 -4
  29. package/dist/host/codex-adapter.js +5 -0
  30. package/dist/host/codex-hook-alive.d.ts +23 -0
  31. package/dist/host/codex-hook-alive.js +51 -0
  32. package/dist/host/codex-notify.d.ts +55 -0
  33. package/dist/host/codex-notify.js +153 -0
  34. package/dist/host/codex-rollout.d.ts +21 -0
  35. package/dist/host/codex-rollout.js +82 -0
  36. package/dist/host/exec-host.js +5 -1
  37. package/dist/host/install-claude.d.ts +14 -0
  38. package/dist/host/install-claude.js +52 -0
  39. package/dist/host/install-codex.d.ts +100 -7
  40. package/dist/host/install-codex.js +391 -63
  41. package/dist/host/install-orchestrator.d.ts +4 -0
  42. package/dist/host/install-orchestrator.js +23 -3
  43. package/dist/host/managed-marker.d.ts +23 -0
  44. package/dist/host/managed-marker.js +78 -0
  45. package/dist/host/opencode/plugin/forgen.d.ts +1 -1
  46. package/dist/host/uninstall-codex.d.ts +58 -0
  47. package/dist/host/uninstall-codex.js +246 -0
  48. package/package.json +5 -6
  49. package/plugin.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://claude.ai/schemas/claude-plugin.json",
3
3
  "name": "forgen",
4
- "version": "0.5.5",
4
+ "version": "0.5.7",
5
5
  "description": "Claude Code harness — the more you use Claude, the better it gets",
6
6
  "author": {
7
7
  "name": "jang-ujin",
package/CHANGELOG.md CHANGED
@@ -7,6 +7,135 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.7] — 2026-10-02 — `forgen uninstall` 의 Codex 정리 · 의존성 메이저 업그레이드
11
+
12
+ ### Added
13
+ - **`forgen uninstall` 이 Codex 등록분도 되돌린다 (ADR-016 D4).** hooks.json 의 forgen 훅, config.toml 의 MCP/notify 블록,
14
+ forgen-managed `skills/` · `agents/ch-*.toml`, cwd 의 AGENTS.md 블록을 제거한다. 이전엔 Codex 쪽이 그대로 남아
15
+ 패키지를 지우면 Codex 가 매 훅 이벤트마다 사라진 스크립트를 실행하려 했다.
16
+ - **다른 도구의 훅 신뢰를 지킨다.** Codex 의 trust 키는 그룹 인덱스 기반이라 forgen 그룹을 지워 인덱스가 당겨지면
17
+ 뒤따르는 훅이 재승인 전까지 조용히 skip 된다. 그런 위치에는 빈 그룹(`{"hooks": []}`)을 남겨 인덱스를 유지한다
18
+ (Codex 0.153.4 `hooks/list` 로 확인: 경고 없음, 뒤 그룹 trusted 유지). forgen 은 `[hooks.state]` 를 고쳐 쓰지 않는다.
19
+ - 사용자가 만든 스킬/에이전트/마커 없는 MCP 테이블/자기 `notify`, Codex 가 블록 사이에 써 넣은 설정은 보존.
20
+ forgen notify 뒤에 체인해 둔 사용자 notifier 는 그 argv 만으로 `notify` 를 되돌려 놓는다.
21
+ - uninstall 후 재설치하면 빈 그룹 자리를 다시 채워 forgen 훅이 원래 인덱스(= 이미 승인된 trust 키)로 돌아간다.
22
+ - 각 단계는 독립 — 하나가 실패(읽기 전용 파일, 깨진 hooks.json)해도 나머지는 진행하고 실패를 출력한다.
23
+ - 한계: 다른 프로젝트의 AGENTS.md 블록(실행한 cwd 것만 정리), `forgen install opencode` 산출물, 낡은 `[hooks.state]`
24
+ 항목, 빈 디렉토리/빈 config.toml 은 남는다.
25
+ - `forgen uninstall` 의 Claude 쪽 누락 보강: `~/.claude.json` 의 `forgen-compound` MCP 등록(install 은 여기에 쓰는데 uninstall 은
26
+ settings.json 만 정리했다), 패키지가 제공한 dev-guide 스킬(`~/.claude/skills/forgen-<stack>-<skill>`).
27
+
28
+ ### Fixed
29
+ - **손으로 여러 줄로 고친 forgen notify 블록을 제거(`--no-notify`, uninstall)하면 config.toml 이 깨지던 결함 (0.5.6).**
30
+ 배열의 첫 줄만 지우고 나머지를 남겨 Codex 가 기동하지 못했다. 이제 그런 블록은 건드리지 않고 알린다.
31
+ - **훅 소유 판정이 너무 넓던 것.** command 에 `dist/hooks/<아무이름>.js` 가 있거나 forgen 설치 경로를 *부분문자열* 로
32
+ 포함하면 forgen 훅으로 봤다 — 다른 프로젝트의 `…/dist/hooks/pre-commit.js` 나 `<pkgRoot>-fork/…` 가 재설치 시 교체
33
+ 대상이 될 수 있었다. 이제 forgen 설치 경로의 `dist/` 아래, codex-adapter 경유, registry 에 있는 forgen 훅 이름일 때만.
34
+ - 소유 마커 판정 강화: SKILL.md 는 frontmatter *바로 뒤* 의 마커만 인정(본문의 `---` 뒤 마커 인용을 오인하던 것),
35
+ agent TOML 은 첫 줄이 정확히 `# forgen-managed` 일 때만.
36
+ - `~/.claude.json` / settings.json 을 다시 쓸 때 기존 파일 권한(0600)과 심링크를 보존 (이전엔 0644 로 넓어지고 심링크가
37
+ 일반 파일로 바뀌었다).
38
+ - `forgen status` 점수: 총점을 표시되는 항목 점수의 합으로 계산 (반올림 순서 때문에 항목 합과 총점이 1 어긋날 수 있었다).
39
+
40
+ ### Changed — dependencies
41
+ - **js-yaml 4 → 5** (런타임 의존성). v5 ESM 빌드는 default export 가 없어 namespace import 로 전환, dump 옵션
42
+ `quotingType` → `quoteStyle`, 타입 내장으로 `@types/js-yaml` 제거. **스키마를 `JSON_SCHEMA` → `CORE_SCHEMA` 로 변경**:
43
+ v5 의 `JSON_SCHEMA` 는 strict JSON 이라 v4 가 읽던 `confidence: .5`, `supersedes: ~`, `True` 등을 문자열로 읽어
44
+ 솔루션이 조용히 탈락한다. `CORE_SCHEMA` 가 v4 와 같은 해석을 준다 (회귀 테스트 추가). 실 솔루션/룰 frontmatter 87개에서
45
+ v4 와 파싱·직렬화 결과 바이트 동일. 남는 차이: `1_000`, `0b11` 형태는 v5 에서 문자열로 읽히고 따옴표 없이 직렬화된다
46
+ (forgen 이 쓰는 값에는 나타나지 않는 형태).
47
+ - **TypeScript 5.9 → 7.0** (dev). TS 6+ 는 `@types/*` 를 자동 포함하지 않아 tsconfig 에 `"types": ["node"]` 명시.
48
+ TS 7 의 `tsc` 는 Node 20.0 에서 실행되지 않으므로 CI 의 훅 포터빌리티 잡을 "Node 22 로 빌드 → 대상 Node 로 로드" 로 분리
49
+ (배포물은 빌드된 dist 이므로 사용자 영향 없음).
50
+ - **vitest 4 → 5** + `@vitest/coverage-v8` 5 (dev, 함께 올려야 함).
51
+ - `@modelcontextprotocol/sdk` 1.31.0, `zod` 4.6.5, `@types/node` 26.6.4, `@biomejs/biome` 2.5.15, `npm audit fix`
52
+ (advisory 13 → 0), `actions/setup-node` v7.
53
+
54
+ ### Docs
55
+ - `packages/forgen-eval/reports/persistence/` 원시 리포트 6건 커밋 — `docs/release/v0.5.0-persistence-delta.md` 가 재현
56
+ 근거로 인용하던 파일이 저장소에 없었다.
57
+
58
+ ### Verified
59
+ - vitest 3204 통과 (신규: Codex uninstall 왕복·신뢰 인덱스 보존·config 보존·소유 판정 오탐·손편집 notify 블록,
60
+ Claude 쪽 MCP/dev-guide 정리·권한/심링크 보존, js-yaml 스칼라 해석).
61
+ - 격리 `HOME` 에서 CLI `install both` → `uninstall --force` 왕복: forgen 이 등록한 훅/블록/스킬/에이전트가 제거되고, 다른
62
+ 도구 훅은 실 Codex `hooks/list` 에서 uninstall 후에도 `trusted`, `codex mcp list` 가 config 를 정상 파싱 (손편집된
63
+ 여러 줄 notify 블록이 있는 경우 포함).
64
+ - fresh-context critic 리뷰: MAJOR 2 · MINOR 12 → 위 Fixed/Changed 에 반영 또는 한계로 명시. 리뷰어가 확인한 것: TS 7 빌드의
65
+ `.js` 227개가 TS 5.9 빌드와 바이트 동일, 실 `~/.codex` 사본에서 uninstall 후 다른 도구 훅 8개 trusted 유지.
66
+ - CI: ubuntu/macOS/arm 테스트 + Node 20.0.0~22 훅 로드 14개 검사 통과.
67
+ - 0.5.6 을 이 머신의 실 `~/.codex` 에 설치한 뒤 실 Codex 세션 1회: Stop 훅 발화·alive 마커 갱신, silent 플래그 없음.
68
+
69
+
70
+ ## [0.5.6] — 2026-10-02 — Codex notify 폴백 · 훅 번들(재승인 2건) · Claude verify 스킬 (ADR-016)
71
+
72
+ > **Codex 사용자 — 재승인 필요**: 업그레이드 후 `forgen install codex` 를 다시 실행하면 훅 2개
73
+ > (`session_start` 변경, `session_end` 신규)가 Codex `/hooks` 승인 전까지 skip 된다. 그동안 Codex 세션에
74
+ > `<forgen-rules>` 블록이 주입되지 않는다. install 출력과 `forgen doctor` 가 대상 훅을 표시한다.
75
+
76
+ ### Added
77
+ - **Codex `notify` 폴백** (`dist/host/codex-notify.js`). `forgen install codex` 가 config.toml 최상단에 마커 블록으로
78
+ `notify` 를 등록한다. Codex 의 notify 는 훅 신뢰와 무관하게 턴 완료마다 detached 로 실행되므로, forgen 훅이
79
+ 미승인/변경 상태로 조용히 skip 되는 동안에도 (a) `state/codex-hooks-silent.json` 에 관측을 남겨 `forgen doctor`
80
+ [Codex Hooks] 가 보여주고 (b) 프롬프트 ≥10 인 세션은 Stop 훅과 같은 디바운스 경로로 auto-compound 를 띄운다.
81
+ 훅이 정상 실행 중이면(codex-adapter 의 alive 마커) 아무것도 하지 않는다. 사용자가 이미 `notify` 를 정의했으면
82
+ **건드리지 않는다** (수동 체인: argv 뒤에 `"--", "<program>", …`). 끄기: `--no-notify` (기존 블록도 제거).
83
+ `forgen uninstall` 도 블록을 제거한다. Codex 가 블록 사이에 써 넣은 root 키(`model` 등)와 BOM/CRLF 는 보존한다.
84
+ - **Codex `SessionEnd` 훅** — `session-end` 를 Codex 에도 등록. Stop 없이 끝나는 세션의 학습 추출 트리거.
85
+ Codex rollout 의 실제 사용자 프롬프트(`event_msg`/`user_message`)를 raw 바이트 스캔으로 센다.
86
+ - **Claude `verify` 스킬** — `forgen install claude` 가 `~/.claude/skills/verify/SKILL.md` 를 설치한다. Claude Code
87
+ 2.1.286+ 는 project/user 스킬에 `verify` 가 있으면 코드 커밋 직전에 실행하도록 모델에 안내한다 (플러그인 스킬
88
+ `forgen:verify` 는 대상이 아님). 본문은 "프로젝트 자체 레시피 우선 → 실제 build/type-check/lint/test 실행 →
89
+ confirmed / refuted / unverified 판정, mock 통과는 증거 아님". 사용자가 만든 `verify` 스킬은 덮어쓰지 않고,
90
+ `forgen uninstall` 은 forgen 이 설치한 것만 제거한다. 끄기: `--no-verify-skill`. (npm postinstall 은 설치하지 않음 —
91
+ 명시적 `forgen install claude` 에서만.)
92
+ - `forgen install` 플래그 `--no-notify`, `--no-verify-skill`.
93
+
94
+ ### Fixed
95
+ - **`forgen install codex` 재실행이 Codex 훅 신뢰를 전부 지우던 결함 (0.5.3~).** 신규 설치는 MCP 마커 블록을
96
+ config.toml 끝에 붙이는데, Codex 는 `/hooks` 승인 시 `[hooks.state]` 테이블을 파일 끝 주석(= forgen 의 END 마커)
97
+ *앞* 에 써 넣는다 — 즉 블록 안. 재설치가 블록을 통째로 교체하면서 22개 신뢰 기록, `[features]`, MCP 서버의
98
+ `enabled = false` 등이 사라졌다. 이제 forgen 이 쓴 줄만 다시 쓰고 사이에 끼어든 내용은 블록 뒤로 옮겨 보존한다.
99
+ (실 Codex 0.153.4 app-server 로 재현·수정 확인: 재설치 후 22/22 유지.)
100
+ - Codex 추출 run(`codex exec --ephemeral`)에 `FORGEN_NESTED_RUN=1` 이 전달되지 않던 것 — forgen 훅이 추출 세션에서
101
+ 발화하지 않도록 Claude 분기와 동일하게 표식.
102
+ - `[mcp_servers.forgen-compound]` 가 마커 없이 이미 있으면 중복 테이블을 append 하지 않는다.
103
+
104
+ ### Changed
105
+ - **Codex 훅 신뢰 감사가 해시를 대조한다.** `auditCodexHookTrust` 가 Codex 0.153.4 의 핸들러 단위 trust 해시를
106
+ 계산해 `trusted` / `modified` / `untrusted` 를 구분한다. 이전엔 `hooks.state` 키의 존재만 봐서, 핸들러가 바뀌어
107
+ Codex 가 skip 하는 훅을 "trusted" 로 표시했다. `/hooks` 에서 끈 훅(`enabled = false`)은 `disabled` 로 따로 센다.
108
+ 읽기 전용 대조이며 신뢰 기록은 쓰지 않는다.
109
+ - **`session-recovery` 의 Codex 핸들러에 `additionalContextLimit: 0`.** Codex 는 약 10KB 를 넘는
110
+ `additionalContext` 를 임시 파일로 스필하고 모델에는 잘린 미리보기만 준다 — 한국어 룰 블록(상한 15,000자)은 이를
111
+ 쉽게 넘는다.
112
+ - `post-tool-failure` 를 Codex hooks.json 에서 제외 (`PostToolUseFailure` 는 Codex 이벤트가 아니라 무시되던 죽은 엔트리).
113
+ - `forgen install codex` 는 config.toml 내용이 바뀔 때만 파일을 쓴다.
114
+ - CI: 태그 푸시 발행을 npm Trusted Publishing(OIDC) 으로 전환, `release.yml`/`compat.yml` 에 누락됐던
115
+ `hooks/hooks.json` 생성 단계 추가 (v0.5.0 이후 태그 발행이 계속 실패하던 원인).
116
+
117
+ ### Not done (정직 표기 — ADR-016)
118
+ - `async: true` 훅: Codex 는 같은 이벤트의 핸들러를 이미 동시 실행한다. 어댑터 경유 훅 1회 91~137ms 실측 →
119
+ 이득 상한 ≈130ms/이벤트. 반면 async 는 block/deny 가 적용되지 않고 핸들러마다 재승인이 든다.
120
+ - `PostCompact` / `Interrupt` 등록: 둘 다 컨텍스트 주입이 불가능하고 forgen 이 거기서 할 일이 없다.
121
+ - 사용자 `notify` 자동 체인: TOML 임의 배열 재작성 + 원복 보장 불가로 수동 체인 안내만 제공.
122
+
123
+ ### Verified
124
+ - vitest 3161 통과 (신규: trust 해시·notify 블록 upsert·notify 폴백 분기·rollout 카운터·verify 스킬 install/uninstall).
125
+ - trust 해시: 실머신 `~/.codex/config.toml` 의 trusted_hash 20건과 일치 (fixture 8건 vendoring), 격리 `CODEX_HOME` 에서
126
+ forgen 계산 해시를 기록한 뒤 Codex `hooks/list` 가 22/22 `trusted` 로 판정 (`additionalContextLimit:0`·SessionEnd 포함).
127
+ - 격리 Codex 0.153.4 실세션 (`codex exec`):
128
+ - 훅 미승인: notify 발화 → silent 기록, `forgen doctor` 가 "forgen 훅 미발화" 표시.
129
+ - 훅 승인: alive 마커(Stop) 기록 → notify 가 silent 를 지움, hook-timing 에 `SessionEnd:session-end (rt: codex)`.
130
+ - 스필: 21KB SessionStart 컨텍스트 — 기본값은 "Full hook output saved to" + 중간 내용 미전달, `additionalContextLimit:0`
131
+ 은 전문 전달.
132
+ - notify 폴백의 auto-compound 트리거: 실 바이너리 + 12-프롬프트 rollout 으로 러너 인자(cwd, rollout, session id, 12)와
133
+ `FORGEN_RUNTIME=codex` 전달, 재호출 시 in-flight 게이트로 skip 확인. (러너 자체는 스텁으로 대체 — LLM 추출은 이 검증 범위 밖.)
134
+ - fresh-context critic 리뷰 1라운드: CRITICAL 2 · MAJOR 1 · MINOR 10 → 위 Fixed 항목 포함 전부 반영 또는 한계로 명시
135
+ (ADR-016 Review 절). 리뷰어의 실 Codex 재현 스크립트를 수정 후 빌드에 다시 돌려 통과 확인.
136
+ - 이 머신의 실 `~/.codex` 에는 아직 설치하지 않았다 (재승인 전까지 룰 주입이 멈추므로 오너 결정 후).
137
+
138
+
10
139
  ## [0.5.5] — 2026-10-01 — Codex Stop 분기 복구 (Stop 트리거 auto-compound·finalizeSession)
11
140
 
12
141
  ### Fixed
package/README.ja.md CHANGED
@@ -192,6 +192,10 @@ forgen install both # 3択インタラクティブ: claude / codex / bo
192
192
  forgen install claude
193
193
  forgen install codex
194
194
  # Codex のみ: codex 内で `/hooks` から forgen フックを一度承認 — Codex は未承認フックをスキップ (forgen doctor の [Codex Hooks] で確認)
195
+ # v0.5.6 へのアップグレード: `forgen install codex` を再実行するとフック 2 件 (session_start, session_end) が変わる — `/hooks` でもう一度承認。
196
+ # config.toml に `notify` フォールバックも登録 (既に `notify` を定義していれば変更しない; 無効化: --no-notify)。
197
+ # Claude のみ: `verify` スキルを ~/.claude/skills/verify にインストール — Claude Code がコードのコミット直前に実行
198
+ # (自作の `verify` スキルは上書きしない; 無効化: --no-verify-skill)。
195
199
 
196
200
  # 3. 初回実行 — 4問オンボーディング (英語/韓国語選択)
197
201
  forgen # デフォルト: Claude
@@ -560,7 +564,7 @@ forgen mcp list # インストール済み MCP サーバーを
560
564
  forgen mcp add <名前> # テンプレートから MCP サーバーを追加
561
565
  forgen mcp templates # 利用可能なテンプレートを表示
562
566
  forgen notepad show # セッションノートパッドを表示
563
- forgen uninstall # forgen をきれいに削除
567
+ forgen uninstall # forgen をきれいに削除 (Claude Code + Codex の登録分)
564
568
  ```
565
569
 
566
570
  ### MCP ツール(セッション中に Claude が使用可能)
package/README.ko.md CHANGED
@@ -152,6 +152,10 @@ forgen install both # 3지선다 인터랙티브: claude / codex / both
152
152
  forgen install claude
153
153
  forgen install codex
154
154
  # Codex 만: codex 안에서 `/hooks` 로 forgen 훅을 한 번 승인 — Codex 는 미승인 훅을 skip (forgen doctor 의 [Codex Hooks] 로 확인)
155
+ # v0.5.6 업그레이드: `forgen install codex` 를 다시 돌리면 훅 2개(session_start, session_end)가 바뀜 — `/hooks` 에서 한 번 더 승인.
156
+ # config.toml 에 `notify` 폴백도 등록됨 (이미 `notify` 를 쓰고 있으면 건드리지 않음; 끄기: --no-notify).
157
+ # Claude 만: `verify` 스킬을 ~/.claude/skills/verify 에 설치 — Claude Code 가 코드 커밋 직전에 실행
158
+ # (직접 만든 `verify` 스킬은 덮어쓰지 않음; 끄기: --no-verify-skill).
155
159
 
156
160
  # 3. 첫 실행 — 4문항 온보딩 (영어/한국어 선택)
157
161
  forgen # 기본: Claude
@@ -545,7 +549,7 @@ forgen mcp list # 설치된 MCP 서버 목록
545
549
  forgen mcp add <이름> # 템플릿에서 MCP 서버 추가
546
550
  forgen mcp templates # 사용 가능한 템플릿 목록
547
551
  forgen notepad show # 세션 노트패드 보기
548
- forgen uninstall # forgen 깔끔하게 제거
552
+ forgen uninstall # forgen 깔끔하게 제거 (Claude Code + Codex 등록분)
549
553
  ```
550
554
 
551
555
  ### MCP 도구 (세션 중 Claude가 사용)
@@ -670,7 +674,7 @@ forgen는 설치 시 다른 Claude Code 플러그인(oh-my-claudecode, superpowe
670
674
 
671
675
  | 문서 | 설명 |
672
676
  |------|------|
673
- | [훅 레퍼런스](docs/reference/hooks-reference.md) | 3개 계층의 19개 훅 — 이벤트, 타임아웃, 동작 |
677
+ | [훅 레퍼런스](docs/reference/hooks-reference.md) | 3개 계층의 23개 훅 — 이벤트, 타임아웃, 동작 |
674
678
  | [공존 가이드](docs/guides/with-omc.md) | oh-my-claudecode와 forgen 함께 사용하기 |
675
679
  | [CHANGELOG](CHANGELOG.md) | 버전 히스토리 및 릴리즈 노트 |
676
680
 
package/README.md CHANGED
@@ -245,6 +245,10 @@ forgen install both # 3-choice interactive: claude / codex / both
245
245
  forgen install claude
246
246
  forgen install codex
247
247
  # Codex only: trust the forgen hooks once with `/hooks` inside codex — Codex skips untrusted hooks (forgen doctor shows [Codex Hooks])
248
+ # Upgrading to v0.5.6: re-running `forgen install codex` changes 2 hooks (session_start, session_end) — approve them in `/hooks` once more.
249
+ # It also registers a `notify` fallback in config.toml (left alone if you already define `notify`; opt out: --no-notify).
250
+ # Claude only: installs a `verify` skill to ~/.claude/skills/verify — Claude Code runs it right before code commits
251
+ # (your own `verify` skill is never overwritten; opt out: --no-verify-skill).
248
252
 
249
253
  # 3. First run — 4-question onboarding (English or Korean)
250
254
  forgen # default: Claude
@@ -746,7 +750,7 @@ forgen mcp list # List installed MCP servers
746
750
  forgen mcp add <name> # Add MCP server from template
747
751
  forgen mcp templates # Show available templates
748
752
  forgen notepad show # View session notepad
749
- forgen uninstall # Remove forgen cleanly
753
+ forgen uninstall # Remove forgen cleanly (Claude Code + Codex registrations)
750
754
  ```
751
755
 
752
756
  ### Rule lifecycle (v0.4.0, ADR-001/002)
@@ -924,7 +928,7 @@ See [Coexistence Guide](docs/guides/with-omc.md) for the full plugin-detection m
924
928
 
925
929
  | Document | Description |
926
930
  |----------|-------------|
927
- | [Hooks Reference](docs/reference/hooks-reference.md) | 19 hooks across 3 tiers — events, timeouts, behavior |
931
+ | [Hooks Reference](docs/reference/hooks-reference.md) | 23 hooks across 3 tiers — events, timeouts, behavior |
928
932
  | [Coexistence Guide](docs/guides/with-omc.md) | Using forgen alongside oh-my-claudecode |
929
933
  | [forgen-eval testbed](packages/forgen-eval/) | Alpha self-measurement package — multi-host parity, 7-axis metrics, drift detection (private workspace, v0.4.3+) |
930
934
  | [Multi-host core design](docs/superpowers/specs/2026-04-27-forgen-multi-host-core-design.md) | Codex/Claude symmetric host adapter spec |
package/README.zh.md CHANGED
@@ -192,6 +192,10 @@ forgen install both # 三选交互: claude / codex / both
192
192
  forgen install claude
193
193
  forgen install codex
194
194
  # 仅 Codex: 在 codex 内用 `/hooks` 信任一次 forgen 钩子 — Codex 会跳过未信任的钩子 (用 forgen doctor 的 [Codex Hooks] 查看)
195
+ # 升级到 v0.5.6: 重新运行 `forgen install codex` 会改动 2 个钩子 (session_start, session_end) — 在 `/hooks` 中再信任一次。
196
+ # 同时在 config.toml 注册 `notify` 兜底 (如果你已定义 `notify` 则不改动; 关闭: --no-notify)。
197
+ # 仅 Claude: 将 `verify` 技能安装到 ~/.claude/skills/verify — Claude Code 在提交代码前运行它
198
+ # (不会覆盖你自己的 `verify` 技能; 关闭: --no-verify-skill)。
195
199
 
196
200
  # 3. 首次运行 — 4题引导问卷 (英语/韩语选择)
197
201
  forgen # 默认: Claude
@@ -529,7 +533,7 @@ forgen mcp list # 列出已安装的 MCP 服务器
529
533
  forgen mcp add <名称> # 从模板添加 MCP 服务器
530
534
  forgen mcp templates # 显示可用模板
531
535
  forgen notepad show # 查看会话记事本
532
- forgen uninstall # 干净地卸载 forgen
536
+ forgen uninstall # 干净地卸载 forgen (Claude Code + Codex 的注册项)
533
537
  ```
534
538
 
535
539
  ### MCP 工具(会话中 Claude 可使用)
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: verify
3
+ description: Confirm a code change actually works using real execution evidence — run the project's own build, type-check, lint and tests, exercise the change itself, and report a confirmed / refuted / unverified verdict. Use right before committing a code change, or when asked to verify that something works.
4
+ ---
5
+
6
+ <!-- forgen-managed -->
7
+
8
+ # verify — prove the change works before it is committed
9
+
10
+ "Saying it is done and proving it is done are different things."
11
+
12
+ This is the forgen default verification recipe. Claude Code runs a skill named `verify` right before
13
+ commits that touch code (docs-only and tests-only commits are skipped).
14
+
15
+ ## 0. Prefer the repository's own recipe
16
+
17
+ If this repository documents how to verify changes — its own `.claude/skills/verify/SKILL.md`, or
18
+ verification/test commands in `CLAUDE.md`, `AGENTS.md`, `CONTRIBUTING.md` or the README — follow that
19
+ recipe first. It knows the project better than this generic one. Use the steps below for whatever it
20
+ does not cover, and keep the evidence rules in step 3 either way.
21
+
22
+ ## 1. Scope the change
23
+
24
+ - `git status --short` and `git diff --stat` (staged and unstaged) to see what is about to be committed.
25
+ - State in one sentence what the change is supposed to do. That sentence is the claim you are verifying.
26
+
27
+ ## 2. Run the project's real checks
28
+
29
+ Find the commands the project actually uses (`package.json` scripts, `Makefile`, `justfile`,
30
+ `pyproject.toml`, `Cargo.toml`, `go.mod`, CI workflow files) and run the ones relevant to the change:
31
+
32
+ 1. build / compile
33
+ 2. type-check
34
+ 3. lint
35
+ 4. tests — the tests covering the changed code first, then the full suite when it is reasonably fast
36
+
37
+ Keep it proportional: a one-line fix needs its targeted test and a build, not a 20-minute end-to-end run.
38
+ Say which checks you skipped and why.
39
+
40
+ ## 3. Evidence rules
41
+
42
+ - Only output from commands you ran **now** counts. "It should pass" and results quoted from earlier
43
+ in the session are not evidence.
44
+ - A passing test that mocks or stubs the unit you changed does not show the change works. Look for,
45
+ or add, a check that executes the real code path.
46
+ - Exercise the change itself where feasible: run the CLI command, call the endpoint, load the page,
47
+ import and call the function. A green test suite that never touches the new behaviour is not enough.
48
+ - Do not attach confidence percentages you did not measure.
49
+
50
+ ## 4. Verdict
51
+
52
+ Report exactly one of:
53
+
54
+ - **confirmed** — the commands ran and their output supports the claim. Quote each command and the
55
+ 1–3 lines of output that matter.
56
+ - **refuted** — something failed or the behaviour is wrong. Do **not** commit. Show the failing output,
57
+ fix the problem, then run this skill again.
58
+ - **unverified** — you could not run what was needed (missing dependency, no test covers it, needs
59
+ credentials). Say precisely what could not be checked and ask before committing; never round
60
+ unverified up to confirmed.
61
+
62
+ ## 5. Riskier changes
63
+
64
+ For changes that touch persistence, authentication, money, concurrency, or public interfaces, hand the
65
+ claim to a fresh verifier sub-agent (`forgen-verify` or `ch-verifier` when available) and ask it to try
66
+ to break the claim rather than confirm it. Include its verdict in your report.
@@ -24,7 +24,10 @@
24
24
  "matcher": "*",
25
25
  "script": "hooks/session-recovery.js",
26
26
  "timeout": 3,
27
- "compoundCritical": true
27
+ "compoundCritical": true,
28
+ "codex": {
29
+ "additionalContextLimit": 0
30
+ }
28
31
  },
29
32
  {
30
33
  "name": "post-tool-use",
@@ -168,7 +171,10 @@
168
171
  "matcher": "*",
169
172
  "script": "hooks/post-tool-failure.js",
170
173
  "timeout": 3,
171
- "compoundCritical": false
174
+ "compoundCritical": false,
175
+ "hosts": [
176
+ "claude"
177
+ ]
172
178
  },
173
179
  {
174
180
  "name": "solution-injector",
@@ -206,7 +212,8 @@
206
212
  "timeout": 3,
207
213
  "compoundCritical": true,
208
214
  "hosts": [
209
- "claude"
215
+ "claude",
216
+ "codex"
210
217
  ]
211
218
  }
212
219
  ]
package/dist/cli.js CHANGED
@@ -181,19 +181,23 @@ const commands = [
181
181
  },
182
182
  {
183
183
  name: 'install',
184
- description: 'Install forgen into a host. Usage: forgen install [claude|codex|opencode|both] [--dry-run] [--no-mcp]',
184
+ description: 'Install forgen into a host. Usage: forgen install [claude|codex|opencode|both] [--dry-run] [--no-mcp] [--no-notify] [--no-verify-skill]',
185
185
  handler: async (args) => {
186
186
  const knownSubs = new Set(['claude', 'codex', 'opencode', 'both']);
187
187
  const target = args[0] && knownSubs.has(args[0]) ? args[0] : args[0]?.startsWith('--') ? undefined : args[0];
188
188
  if (target !== undefined && !knownSubs.has(target)) {
189
- console.log('Usage:\n forgen install [claude|codex|opencode|both] [--dry-run] [--no-mcp]\n\n No arg → interactive 3-choice (Claude/Codex/Both). opencode: 명시 타겟(P1 실험적).');
189
+ console.log('Usage:\n forgen install [claude|codex|opencode|both] [--dry-run] [--no-mcp] [--no-notify] [--no-verify-skill]\n\n No arg → interactive 3-choice (Claude/Codex/Both). opencode: 명시 타겟(P1 실험적).\n --no-notify: Codex config.toml 에 notify 폴백을 등록하지 않음.\n --no-verify-skill: Claude ~/.claude/skills/verify 를 설치하지 않음.');
190
190
  return;
191
191
  }
192
192
  const dryRun = args.includes('--dry-run');
193
193
  const registerMcp = !args.includes('--no-mcp');
194
194
  const { runInstall, renderResult, resolvePkgRootFromBinary } = await import('./host/install-orchestrator.js');
195
195
  const pkgRoot = resolvePkgRootFromBinary(import.meta.url);
196
- const result = await runInstall({ target, pkgRoot, dryRun, registerMcp });
196
+ const result = await runInstall({
197
+ target, pkgRoot, dryRun, registerMcp,
198
+ registerNotify: !args.includes('--no-notify'),
199
+ installVerifySkill: !args.includes('--no-verify-skill'),
200
+ });
197
201
  if (result === null) {
198
202
  console.log('\n [forgen] Install skipped.');
199
203
  return;
@@ -74,7 +74,15 @@ async function renderCodexHookTrust() {
74
74
  console.log(` △ ${t.total} forgen hooks registered, Codex trust 기록 없음 — codex 안에서 /hooks 로 승인 (미승인 훅은 skip 됨)`);
75
75
  }
76
76
  else {
77
- console.log(` ✗ ${t.untrusted.length}/${t.total} forgen hooks untrusted (${t.untrusted.slice(0, 4).join(', ')}${t.untrusted.length > 4 ? ', …' : ''}) — codex 안에서 /hooks 로 승인`);
77
+ const pending = [...t.modified.map((k) => `${k} modified`), ...t.untrusted.map((k) => `${k} new`), ...t.disabled.map((k) => `${k} disabled`)];
78
+ console.log(` ✗ ${pending.length}/${t.total} forgen hooks skipped by Codex (${pending.slice(0, 4).join(', ')}${pending.length > 4 ? ', …' : ''}) — codex 안에서 /hooks 로 승인`);
79
+ }
80
+ // ADR-016 D1 — notify 폴백이 관측한 "턴은 끝났는데 forgen 훅이 돌지 않음"
81
+ const { readCodexHooksSilent } = await import('../host/codex-notify.js');
82
+ const silent = readCodexHooksSilent();
83
+ // 1회 관측은 Stop 훅이 돌지 않는 내부 서브세션(/review 등)의 notify 일 수 있다 — 연속 2회부터 표시.
84
+ if (silent && silent.count >= 2) {
85
+ console.log(` ✗ notify 폴백 관측: 최근 Codex 턴 ${silent.count}회에서 forgen 훅 미발화 (마지막 ${silent.detectedAt}, session ${silent.sessionId.slice(0, 8)}) — /hooks 승인 필요`);
78
86
  }
79
87
  }
80
88
  catch { /* fail-open */ }
@@ -44,19 +44,18 @@ export function computeHealth() {
44
44
  if (Object.keys(s.philosophy.axisScores).length >= 4)
45
45
  profile += 3;
46
46
  }
47
- const total = Math.round(utilization + effectiveness + growth + coverage + profile);
48
- const grade = total >= 80 ? 'A' : total >= 60 ? 'B' : total >= 40 ? 'C' : total >= 20 ? 'D' : 'F';
49
- return {
50
- total,
51
- components: {
52
- utilization: Math.round(utilization),
53
- effectiveness: Math.round(effectiveness),
54
- growth: Math.round(growth),
55
- coverage: Math.round(coverage),
56
- profile: Math.round(profile),
57
- },
58
- grade,
47
+ // 총점은 *표시되는* 항목 점수의 합이어야 한다. 이전엔 합을 반올림(round(Σ))하고 항목은 따로 반올림해서
48
+ // 실 상태에 따라 "항목 합 60 ≠ 총점 61" 이 나왔다 (tests/health-cli 가 머신 상태에 따라 간헐 실패).
49
+ const components = {
50
+ utilization: Math.round(utilization),
51
+ effectiveness: Math.round(effectiveness),
52
+ growth: Math.round(growth),
53
+ coverage: Math.round(coverage),
54
+ profile: Math.round(profile),
59
55
  };
56
+ const total = components.utilization + components.effectiveness + components.growth + components.coverage + components.profile;
57
+ const grade = total >= 80 ? 'A' : total >= 60 ? 'B' : total >= 40 ? 'C' : total >= 20 ? 'D' : 'F';
58
+ return { total, components, grade };
60
59
  }
61
60
  function gradeColor(grade) {
62
61
  if (grade === 'A')
@@ -91,7 +91,7 @@ export declare const V1_SESSIONS_DIR: string;
91
91
  /** ~/.forgen/state/raw-logs/ — Raw Log */
92
92
  export declare const V1_RAW_LOGS_DIR: string;
93
93
  /** 모든 실행 모드 이름 (cancel/recovery 시 사용) */
94
- export declare const ALL_MODES: readonly ["ralph", "autopilot", "ultrawork", "team", "pipeline", "ccg", "ralplan", "deep-interview", "forge-loop", "ship", "retro", "learn", "calibrate"];
94
+ export declare const ALL_MODES: readonly ['ralph', 'autopilot', 'ultrawork', 'team', 'pipeline', 'ccg', 'ralplan', 'deep-interview', 'forge-loop', 'ship', 'retro', 'learn', 'calibrate'];
95
95
  /** {repo}/.compound/ — 프로젝트 로컬 디렉토리 */
96
96
  export declare function projectDir(cwd: string): string;
97
97
  /** {repo}/.compound/pack.link — 팀 팩 연결 파일 */
@@ -26,7 +26,13 @@ export declare function acquireLock(): void;
26
26
  * 다른 프로세스의 lock을 존중한다.
27
27
  */
28
28
  export declare function releaseLock(): void;
29
- /** 임시파일에 쓴 후 rename으로 원자적 교체 */
29
+ /**
30
+ * 임시파일에 쓴 후 rename으로 원자적 교체.
31
+ *
32
+ * 0.5.7 (critic): 기존 파일의 권한과 심링크를 보존한다. 이전엔 새 파일을 기본 mode(0644)로 만들어 rename 해서
33
+ * 0600 이던 `~/.claude.json`(MCP env 에 비밀값이 들어갈 수 있다)이 0644 로 넓어졌고, 심링크였으면 링크가
34
+ * 일반 파일로 바뀌고 원본은 갱신되지 않았다.
35
+ */
30
36
  export declare function atomicWriteFileSync(targetPath: string, data: string): void;
31
37
  /**
32
38
  * settings.json 안전 읽기.
@@ -96,11 +96,26 @@ export function releaseLock() {
96
96
  }
97
97
  catch { /* 이미 없으면 무시 */ }
98
98
  }
99
- /** 임시파일에 쓴 후 rename으로 원자적 교체 */
99
+ /**
100
+ * 임시파일에 쓴 후 rename으로 원자적 교체.
101
+ *
102
+ * 0.5.7 (critic): 기존 파일의 권한과 심링크를 보존한다. 이전엔 새 파일을 기본 mode(0644)로 만들어 rename 해서
103
+ * 0600 이던 `~/.claude.json`(MCP env 에 비밀값이 들어갈 수 있다)이 0644 로 넓어졌고, 심링크였으면 링크가
104
+ * 일반 파일로 바뀌고 원본은 갱신되지 않았다.
105
+ */
100
106
  export function atomicWriteFileSync(targetPath, data) {
101
- const tmpPath = `${targetPath}.tmp.${process.pid}`;
102
- fs.writeFileSync(tmpPath, data);
103
- fs.renameSync(tmpPath, targetPath);
107
+ let realTarget = targetPath;
108
+ let mode;
109
+ try {
110
+ realTarget = fs.realpathSync(targetPath);
111
+ mode = fs.statSync(realTarget).mode & 0o777;
112
+ }
113
+ catch { /* 새 파일 — 기본 mode */ }
114
+ const tmpPath = `${realTarget}.tmp.${process.pid}`;
115
+ fs.writeFileSync(tmpPath, data, mode !== undefined ? { mode } : undefined);
116
+ if (mode !== undefined)
117
+ fs.chmodSync(tmpPath, mode); // umask 로 깎였을 수 있어 명시
118
+ fs.renameSync(tmpPath, realTarget);
104
119
  }
105
120
  /**
106
121
  * settings.json 안전 읽기.
@@ -13,7 +13,7 @@
13
13
  * forgen status --blocks [N] 최근 차단 N건 (rule·사유·해결)
14
14
  * forgen status --live 실시간 훅 이벤트 스트림
15
15
  */
16
- declare const VIEWS: readonly ["--compound", "--profile", "--rules", "--blocks", "--live", "--overview"];
16
+ declare const VIEWS: readonly ['--compound', '--profile', '--rules', '--blocks', '--live', '--overview'];
17
17
  type View = (typeof VIEWS)[number];
18
18
  export declare function resolveView(args: string[]): View | null;
19
19
  export declare function handleStatus(args: string[]): Promise<void>;
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * 1원칙: Claude semantics 가 reference. 본 enum 의 의미는 Claude Hook schema 의 행동을 그대로 사용한다.
9
9
  */
10
- export declare const TRUST_LAYER_INTENTS: readonly ["block-completion", "block-tool-use", "inject-context", "observe-only", "secret-filter", "forge-loop-state-inject", "self-evidence-record"];
10
+ export declare const TRUST_LAYER_INTENTS: readonly ['block-completion', 'block-tool-use', 'inject-context', 'observe-only', 'secret-filter', 'forge-loop-state-inject', 'self-evidence-record'];
11
11
  export type TrustLayerIntent = (typeof TRUST_LAYER_INTENTS)[number];
12
12
  export type CapabilityStatus = 'supported' | 'partial' | 'unsupported';
13
13
  export interface CapabilityDeclaration {
@@ -25,7 +25,7 @@ export interface CapabilityDeclaration {
25
25
  * `(HOST_IDS as readonly string[]).includes(host)` 로 "유효 host 인지" 를 판정하라
26
26
  * (W3-3 리뷰 SEV-3 #5: Record<HostId> 는 Record 리터럴만 강제, 자유비교는 미포착).
27
27
  */
28
- export declare const HOST_IDS: readonly ["claude", "codex", "opencode"];
28
+ export declare const HOST_IDS: readonly ['claude', 'codex', 'opencode'];
29
29
  export type HostId = (typeof HOST_IDS)[number];
30
30
  /**
31
31
  * 능력 선언의 검증 수준 (W3-3 리뷰 SEV-3 #1).
@@ -1,3 +1,15 @@
1
+ /** ADR-016 D3 — ~/.claude/skills/verify 제거 (forgen-managed 마커가 있는 것만; 사용자 스킬은 보존) */
2
+ export declare function cleanVerifySkill(homeDir?: string): boolean;
3
+ /**
4
+ * ADR-016 D4 — `~/.claude/skills/forgen-<stack>-<skill>/` dev-guide 스킬 제거.
5
+ * 패키지가 실제로 제공하는 이름만 지운다 (사용자가 만든 `forgen-react-mine` 같은 스킬은 보존).
6
+ */
7
+ export declare function cleanDevGuideSkills(pkgRoot: string, homeDir?: string): number;
8
+ /**
9
+ * ADR-016 D4 — `~/.claude.json` 의 `mcpServers["forgen-compound"]` 제거. install 은 settings.json 이 아니라
10
+ * 여기에 등록하는데 uninstall 은 settings.json 만 정리하고 있었다. forgen 서버 경로를 가리킬 때만 지운다.
11
+ */
12
+ export declare function cleanClaudeJsonMcp(homeDir?: string): boolean;
1
13
  /** forgen uninstall 메인 */
2
14
  export declare function handleUninstall(cwd: string, options: {
3
15
  force?: boolean;