oh-my-customcode 1.1.51 → 1.1.53
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/dist/cli/index.js +509 -5
- package/dist/index.js +505 -1
- package/package.json +1 -1
- package/templates/.claude/hooks/hooks.json +24 -34
- package/templates/.claude/rules/MUST-agent-design.md +3 -3
- package/templates/.claude/rules/MUST-completion-verification.md +7 -0
- package/templates/.claude/rules/MUST-enforcement-policy.md +9 -3
- package/templates/.claude/settings.json +650 -0
- package/templates/manifest.json +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"command": "bash .claude/hooks/scripts/stage-blocker.sh"
|
|
11
11
|
}
|
|
12
12
|
],
|
|
13
|
-
"description": "Block Write/Edit tools during plan/verify/compound stages
|
|
13
|
+
"description": "Block Write/Edit tools during plan/verify/compound stages — only allow in implement stage"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"matcher": "tool == \"Bash\" && tool_input.command matches \"(npm run dev|pnpm( run)? dev|yarn dev|bun run dev)\"",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"command": "bash .claude/hooks/scripts/content-hash-validator.sh"
|
|
71
71
|
}
|
|
72
72
|
],
|
|
73
|
-
"description": "Validate file content hash before Edit
|
|
73
|
+
"description": "Validate file content hash before Edit — advisory staleness warning"
|
|
74
74
|
},
|
|
75
75
|
{
|
|
76
76
|
"matcher": "tool == \"Write\" || tool == \"Edit\" || tool == \"Bash\"",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"command": "bash .claude/hooks/scripts/schema-validator.sh"
|
|
81
81
|
}
|
|
82
82
|
],
|
|
83
|
-
"description": "Schema-based tool input validation
|
|
83
|
+
"description": "Schema-based tool input validation — Phase 1 advisory only"
|
|
84
84
|
},
|
|
85
85
|
{
|
|
86
86
|
"matcher": "tool == \"Bash\"",
|
|
@@ -110,14 +110,14 @@
|
|
|
110
110
|
"command": "bash .claude/hooks/scripts/rule-deletion-guard.sh"
|
|
111
111
|
}
|
|
112
112
|
],
|
|
113
|
-
"description": "Block rule file deletion
|
|
113
|
+
"description": "Block rule file deletion — requires individual user confirmation per rule (R001 safety)"
|
|
114
114
|
},
|
|
115
115
|
{
|
|
116
116
|
"matcher": "tool == \"Task\" || tool == \"Agent\"",
|
|
117
117
|
"hooks": [
|
|
118
118
|
{
|
|
119
119
|
"type": "command",
|
|
120
|
-
"command": "#!/bin/bash\ninput=$(cat)\nagent_type=$(echo \"$input\" | jq -r '.tool_input.subagent_type // \"unknown\"')\nmodel=$(echo \"$input\" | jq -r '.tool_input.model // \"inherit\"')\ndesc=$(echo \"$input\" | jq -r '.tool_input.description // \"\"' | head -c 40)\nresume=$(echo \"$input\" | jq -r '.tool_input.resume // empty')\nif [ -n \"$resume\" ]; then\n echo \"
|
|
120
|
+
"command": "#!/bin/bash\ninput=$(cat)\nagent_type=$(echo \"$input\" | jq -r '.tool_input.subagent_type // \"unknown\"')\nmodel=$(echo \"$input\" | jq -r '.tool_input.model // \"inherit\"')\ndesc=$(echo \"$input\" | jq -r '.tool_input.description // \"\"' | head -c 40)\nresume=$(echo \"$input\" | jq -r '.tool_input.resume // empty')\nif [ -n \"$resume\" ]; then\n echo \"─── [Resume] ${agent_type}:${model} | ${desc} ───\" >&2\nelse\n echo \"─── [Spawn] ${agent_type}:${model} | ${desc} ───\" >&2\nfi\necho \"$input\""
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
"type": "command",
|
|
@@ -216,7 +216,7 @@
|
|
|
216
216
|
"command": "bash .claude/hooks/scripts/user-prompt-preprocessor.sh"
|
|
217
217
|
}
|
|
218
218
|
],
|
|
219
|
-
"description": "Advisory pre-processing of user input
|
|
219
|
+
"description": "Advisory pre-processing of user input — skill matching hints and intent analysis"
|
|
220
220
|
},
|
|
221
221
|
{
|
|
222
222
|
"matcher": "*",
|
|
@@ -226,7 +226,7 @@
|
|
|
226
226
|
"command": "bash .claude/hooks/scripts/r007-r008-drift-advisor.sh"
|
|
227
227
|
}
|
|
228
228
|
],
|
|
229
|
-
"description": "Proactive R007/R008 drift advisory
|
|
229
|
+
"description": "Proactive R007/R008 drift advisory — checks last assistant turn for identification omission (#1229)"
|
|
230
230
|
},
|
|
231
231
|
{
|
|
232
232
|
"matcher": "*",
|
|
@@ -246,7 +246,7 @@
|
|
|
246
246
|
"command": "bash .claude/hooks/scripts/fail-axis-cause-advisor.sh"
|
|
247
247
|
}
|
|
248
248
|
],
|
|
249
|
-
"description": "Advisory on cause-free progress nudges when this session has logged tool failures
|
|
249
|
+
"description": "Advisory on cause-free progress nudges when this session has logged tool failures — delivers via hookSpecificOutput.additionalContext (#1561)"
|
|
250
250
|
}
|
|
251
251
|
],
|
|
252
252
|
"SubagentStart": [
|
|
@@ -255,7 +255,7 @@
|
|
|
255
255
|
"hooks": [
|
|
256
256
|
{
|
|
257
257
|
"type": "command",
|
|
258
|
-
"command": "#!/bin/bash\ninput=$(cat)\nagent_type=$(echo \"$input\" | jq -r '.agent_type // \"unknown\"')\nmodel=$(echo \"$input\" | jq -r '.model // \"inherit\"')\ndesc=$(echo \"$input\" | jq -r '.description // \"\"' | head -c 40)\necho \"
|
|
258
|
+
"command": "#!/bin/bash\ninput=$(cat)\nagent_type=$(echo \"$input\" | jq -r '.agent_type // \"unknown\"')\nmodel=$(echo \"$input\" | jq -r '.model // \"inherit\"')\ndesc=$(echo \"$input\" | jq -r '.description // \"\"' | head -c 40)\necho \"─── [SubagentStart] ${agent_type}:${model} | ${desc} ───\" >&2\necho \"$input\""
|
|
259
259
|
},
|
|
260
260
|
{
|
|
261
261
|
"type": "command",
|
|
@@ -287,7 +287,7 @@
|
|
|
287
287
|
},
|
|
288
288
|
{
|
|
289
289
|
"type": "prompt",
|
|
290
|
-
"prompt": "A background subagent just completed. If it FAILED, do NOT auto-continue
|
|
290
|
+
"prompt": "A background subagent just completed. If it FAILED, do NOT auto-continue — report the failure and wait for user input."
|
|
291
291
|
}
|
|
292
292
|
],
|
|
293
293
|
"description": "Record agent outcomes + auto-continue workflow + R007/R008 drift advisory on subagent completion (autonomous-loop re-entry, #1545)"
|
|
@@ -314,7 +314,7 @@
|
|
|
314
314
|
"command": "bash .claude/hooks/scripts/file-change-validator.sh"
|
|
315
315
|
}
|
|
316
316
|
],
|
|
317
|
-
"description": "Validate externally modified files
|
|
317
|
+
"description": "Validate externally modified files — advisory staleness warning (v2.1.83+)"
|
|
318
318
|
}
|
|
319
319
|
],
|
|
320
320
|
"Notification": [
|
|
@@ -335,7 +335,7 @@
|
|
|
335
335
|
"hooks": [
|
|
336
336
|
{
|
|
337
337
|
"type": "command",
|
|
338
|
-
"command": "#!/bin/bash\ninput=$(cat)\nteammate=$(echo \"$input\" | jq -r '.teammate_id // \"unknown\"' 2>/dev/null)\necho \"[Hook] TeammateIdle: $teammate
|
|
338
|
+
"command": "#!/bin/bash\ninput=$(cat)\nteammate=$(echo \"$input\" | jq -r '.teammate_id // \"unknown\"' 2>/dev/null)\necho \"[Hook] TeammateIdle: $teammate — available for reassignment\" >&2\necho \"$input\""
|
|
339
339
|
}
|
|
340
340
|
],
|
|
341
341
|
"description": "Track idle Agent Teams members for work reassignment"
|
|
@@ -347,7 +347,7 @@
|
|
|
347
347
|
"hooks": [
|
|
348
348
|
{
|
|
349
349
|
"type": "command",
|
|
350
|
-
"command": "#!/bin/bash\ninput=$(cat)\ntask_id=$(echo \"$input\" | jq -r '.task_id // \"\"' 2>/dev/null)\ndesc=$(echo \"$input\" | jq -r '.description // \"\"' 2>/dev/null | head -c 60)\necho \"[Hook] TaskCreated: $task_id
|
|
350
|
+
"command": "#!/bin/bash\ninput=$(cat)\ntask_id=$(echo \"$input\" | jq -r '.task_id // \"\"' 2>/dev/null)\ndesc=$(echo \"$input\" | jq -r '.description // \"\"' 2>/dev/null | head -c 60)\necho \"[Hook] TaskCreated: $task_id — $desc\" >&2\necho \"$input\""
|
|
351
351
|
}
|
|
352
352
|
],
|
|
353
353
|
"description": "Log task creation events for Agent Teams coordination"
|
|
@@ -359,7 +359,7 @@
|
|
|
359
359
|
"hooks": [
|
|
360
360
|
{
|
|
361
361
|
"type": "command",
|
|
362
|
-
"command": "#!/bin/bash\ninput=$(cat)\ntask_id=$(echo \"$input\" | jq -r '.task_id // \"\"' 2>/dev/null)\nresult=$(echo \"$input\" | jq -r '.result // \"\"' 2>/dev/null | head -c 60)\necho \"[Hook] TaskCompleted: $task_id
|
|
362
|
+
"command": "#!/bin/bash\ninput=$(cat)\ntask_id=$(echo \"$input\" | jq -r '.task_id // \"\"' 2>/dev/null)\nresult=$(echo \"$input\" | jq -r '.result // \"\"' 2>/dev/null | head -c 60)\necho \"[Hook] TaskCompleted: $task_id — $result\" >&2\necho \"$input\""
|
|
363
363
|
}
|
|
364
364
|
],
|
|
365
365
|
"description": "Log task completion events for Agent Teams coordination"
|
|
@@ -374,7 +374,7 @@
|
|
|
374
374
|
"command": "bash .claude/hooks/scripts/task-state-precompact.sh"
|
|
375
375
|
}
|
|
376
376
|
],
|
|
377
|
-
"description": "Serialize active task state before compaction
|
|
377
|
+
"description": "Serialize active task state before compaction — pairs with PostCompact restore (#922)"
|
|
378
378
|
}
|
|
379
379
|
],
|
|
380
380
|
"PostCompact": [
|
|
@@ -383,10 +383,10 @@
|
|
|
383
383
|
"hooks": [
|
|
384
384
|
{
|
|
385
385
|
"type": "prompt",
|
|
386
|
-
"prompt": "Context compacted. RULES ACTIVE.\n\nR007: next response MUST start with
|
|
386
|
+
"prompt": "Context compacted. RULES ACTIVE.\n\nR007: next response MUST start with ┌─ Agent: claude (default) / └─ Task: {task}\nR008: before EVERY tool call → [claude][model] → Tool: Name / Target: path\nR010: write→delegate to subagent; orchestrator read-only (Read/Glob/Grep); git→mgr-gitnerd\nR009: 2+ independent tasks → parallel agents, same message\nR018: 3+ agents OR review cycle → Agent Teams\nR000: user communication in Korean (한국어); code/files in English — NON-NEGOTIABLE\nR001: prohibited actions (no secrets, no system files, no rm -rf); stop on violation → preserve state → report → wait\nR002: tool tiers (Read/Grep=free, Write/Edit=notify, Bash=approval); no writes outside project; no .env/.git/config\n\nAutonomous mode: if /tmp/.claude-autonomous-$PPID exists → simple git (add/commit/push) may run directly; Write/Edit still delegates.\nbypassPermissions does NOT persist across compaction — re-enable if tasks seem blocked.\n\nTask state restore: if /tmp/.claude-task-state-$PPID.json exists, read it to recover active pipeline, dev stage, and autonomous mode state. This was saved by PreCompact hook.\n\nRe-read CLAUDE.md NOW."
|
|
387
387
|
}
|
|
388
388
|
],
|
|
389
|
-
"description": "Reinforce enforced rules after context compaction
|
|
389
|
+
"description": "Reinforce enforced rules after context compaction — prevents rule amnesia (v2.1.76+)"
|
|
390
390
|
}
|
|
391
391
|
],
|
|
392
392
|
"PostToolUse": [
|
|
@@ -473,7 +473,7 @@
|
|
|
473
473
|
"continueOnBlock": true
|
|
474
474
|
}
|
|
475
475
|
],
|
|
476
|
-
"description": "Context budget advisor
|
|
476
|
+
"description": "Context budget advisor — track tool usage patterns and advise ecomode activation"
|
|
477
477
|
},
|
|
478
478
|
{
|
|
479
479
|
"matcher": "tool == \"Edit\" || tool == \"Write\" || tool == \"Bash\" || tool == \"Task\" || tool == \"Agent\" || tool == \"Read\" || tool == \"Glob\" || tool == \"Grep\"",
|
|
@@ -484,7 +484,7 @@
|
|
|
484
484
|
"continueOnBlock": true
|
|
485
485
|
}
|
|
486
486
|
],
|
|
487
|
-
"description": "Proactive R007/R008 drift advisory on tool use
|
|
487
|
+
"description": "Proactive R007/R008 drift advisory on tool use — covers the orchestrator-only stretch before the first subagent spawn, where neither UserPromptSubmit nor SubagentStop fires (#1553). Turn-deduplicated; exit 0 advisory only."
|
|
488
488
|
},
|
|
489
489
|
{
|
|
490
490
|
"matcher": "tool == \"Edit\" || tool == \"Write\" || tool == \"Bash\" || tool == \"Task\" || tool == \"Agent\"",
|
|
@@ -506,7 +506,7 @@
|
|
|
506
506
|
"continueOnBlock": true
|
|
507
507
|
}
|
|
508
508
|
],
|
|
509
|
-
"description": "Advisory cost cap monitoring
|
|
509
|
+
"description": "Advisory cost cap monitoring — warn when session cost approaches configurable limit"
|
|
510
510
|
},
|
|
511
511
|
{
|
|
512
512
|
"matcher": "tool == \"Read\"",
|
|
@@ -516,7 +516,7 @@
|
|
|
516
516
|
"command": "bash .claude/hooks/scripts/content-hash-validator.sh"
|
|
517
517
|
}
|
|
518
518
|
],
|
|
519
|
-
"description": "Store content hashes for Read operations
|
|
519
|
+
"description": "Store content hashes for Read operations — enables Edit staleness detection"
|
|
520
520
|
},
|
|
521
521
|
{
|
|
522
522
|
"matcher": "tool == \"Bash\" || tool == \"Read\" || tool == \"Grep\"",
|
|
@@ -526,7 +526,7 @@
|
|
|
526
526
|
"command": "bash .claude/hooks/scripts/secret-filter.sh"
|
|
527
527
|
}
|
|
528
528
|
],
|
|
529
|
-
"description": "Detect potential secrets in Bash/Read/Grep output
|
|
529
|
+
"description": "Detect potential secrets in Bash/Read/Grep output — advisory warning only"
|
|
530
530
|
},
|
|
531
531
|
{
|
|
532
532
|
"matcher": "tool == \"Edit\" || tool == \"Write\" || tool == \"Bash\" || tool == \"Agent\"",
|
|
@@ -567,7 +567,7 @@
|
|
|
567
567
|
"command": "bash .claude/hooks/scripts/auto-dev-token-tracker.sh"
|
|
568
568
|
}
|
|
569
569
|
],
|
|
570
|
-
"description": "auto-dev token spend tracker
|
|
570
|
+
"description": "auto-dev token spend tracker — records per-phase token estimates during /pipeline auto-dev (Issue #1057, advisory)"
|
|
571
571
|
}
|
|
572
572
|
],
|
|
573
573
|
"Stop": [
|
|
@@ -621,16 +621,6 @@
|
|
|
621
621
|
],
|
|
622
622
|
"description": "Detect reusable skill candidates from session task outcomes (advisory, exit 0)"
|
|
623
623
|
},
|
|
624
|
-
{
|
|
625
|
-
"matcher": "*",
|
|
626
|
-
"hooks": [
|
|
627
|
-
{
|
|
628
|
-
"type": "prompt",
|
|
629
|
-
"prompt": "Session-end memory checkpoint (R011 enforcement). Check if the session-end memory save workflow was completed: 1) sys-memory-keeper was delegated to update MEMORY.md. Note: episodic-memory auto-indexes after session \u2014 no manual action needed. Decision rules (evaluate in order): 1. If session had no explicit session-end signal (quick question, no memory work): APPROVE immediately. 2. If the step was already attempted (success or failure both count): APPROVE. 3. If the step was NOT yet performed: PERFORM IT NOW (maximum 1 attempt): a. Delegate to sys-memory-keeper to update MEMORY.md \u2014 call Agent tool with sys-memory-keeper. b. After completion (success or failure), APPROVE stop. IMPORTANT: Do NOT block indefinitely. After 1 attempt, always APPROVE. IMPORTANT: Do NOT loop \u2014 if you already performed this step in this same stop hook evaluation, APPROVE."
|
|
630
|
-
}
|
|
631
|
-
],
|
|
632
|
-
"description": "Enforce R011 session-end memory saves \u2014 auto-perform if not yet done (episodic-memory auto-indexes)"
|
|
633
|
-
},
|
|
634
624
|
{
|
|
635
625
|
"matcher": "*",
|
|
636
626
|
"hooks": [
|
|
@@ -651,7 +641,7 @@
|
|
|
651
641
|
"command": "bash .claude/hooks/scripts/failure-ledger.sh"
|
|
652
642
|
}
|
|
653
643
|
],
|
|
654
|
-
"description": "FAIL-axis instrumentation
|
|
644
|
+
"description": "FAIL-axis instrumentation — append tool failures to the error ledger (JSONL). Never blocks; feeds fail-axis-cause-advisor.sh (#1561)"
|
|
655
645
|
}
|
|
656
646
|
]
|
|
657
647
|
}
|
|
@@ -194,7 +194,7 @@ Hook JSON output `terminalSequence` field for desktop notifications, window titl
|
|
|
194
194
|
|
|
195
195
|
> **`MessageDisplay`는 표시 전용 — `additionalContext` 미지원**: `MessageDisplay`는 `hookSpecificOutput.displayContent`로 **화면 표시 텍스트만** 교체하며, 트랜스크립트와 Claude가 보는 내용은 원본이 유지된다. 따라서 advisory 훅을 `MessageDisplay`에 배선하면 **모델에 도달하지 않는다**. `additionalContext`(모델 컨텍스트 주입)를 지원하는 이벤트는 SessionStart, Setup, SubagentStart, UserPromptSubmit, UserPromptExpansion, PreToolUse, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop이다. (이전 판이 나열하던 `PostMessage`는 문서화된 이벤트가 아니다 — 실제 이벤트명은 `MessageDisplay`.)
|
|
196
196
|
|
|
197
|
-
> **문서 시차 노트 — `PreModelSwitch`/`PostModelSwitch` 및 `PostCompact`**: 33종 중 `PreModelSwitch`/`PostModelSwitch`(v2.1.251)는 CHANGELOG(2026-08-28)에는 명시되나 공식 hooks.md "Hook events" 카탈로그 페이지에는 실측일(2026-08-29) 기준 헤더가 없다 — changelog→hooks.md 반영 시차로 판단(오류 아님, hook-events-audit 2026-08-29 실측). 또한 `PostCompact`는 공식 문서상 `additionalContext`/decision-control이 정의돼 있지 않다 — 재주입(compact 후 규칙 재주입) 용도로는 `PostCompact`가 아니라 `SessionStart`(matcher `*`)를 쓸 것(cross-ref R021 「Prompt-based」 각주).
|
|
197
|
+
> **문서 시차 노트 — `PreModelSwitch`/`PostModelSwitch` 및 `PostCompact`**: 33종 중 `PreModelSwitch`/`PostModelSwitch`(v2.1.251)는 CHANGELOG(2026-08-28)에는 명시되나 공식 hooks.md "Hook events" 카탈로그 페이지에는 실측일(2026-08-29) 기준 헤더가 없다 — changelog→hooks.md 반영 시차로 판단(오류 아님, hook-events-audit 2026-08-29 실측). 또한 `PostCompact`는 공식 문서상 `additionalContext`/decision-control이 정의돼 있지 않다 — 재주입(compact 후 규칙 재주입) 용도로는 `PostCompact`가 아니라 `SessionStart`(matcher `*`)를 쓸 것(cross-ref R021 「Prompt-based」 각주). PostCompact dispatch 경로는 바이너리 실측으로 실재 확인(2026-08-29 probe) — 상세는 R021 각주.
|
|
198
198
|
|
|
199
199
|
> **신규 이벤트 발동 시점**: `Setup` — `--init-only`, 또는 `-p` 모드에서 `--init`/`--maintenance`로 시작할 때. `UserPromptExpansion` — 사용자가 입력한 커맨드가 프롬프트로 확장될 때(모델 도달 전; 확장 차단 가능). `PostToolUseFailure` — 도구 호출이 실패한 뒤. `PostToolBatch` — 병렬 도구 호출 배치 전체가 끝난 뒤, 다음 모델 호출 전. `MessageDisplay` — assistant 메시지 텍스트가 표시되는 동안(실시간 스트리밍). `DirectoryAdded` (v2.1.219+) — `/add-dir` 또는 SDK `register_repo_root`로 작업 디렉토리가 세션 중 추가될 때. (그 밖의 신규 이벤트 — `PermissionRequest`, `StopFailure`, `InstructionsLoaded`, `ConfigChange`, `WorktreeCreate`, `WorktreeRemove` — 는 발동 시점을 미실측이므로 서술하지 않는다.)
|
|
200
200
|
|
|
@@ -204,8 +204,8 @@ Hook JSON output `terminalSequence` field for desktop notifications, window titl
|
|
|
204
204
|
|-------|---------|---------------|---------------|------------|
|
|
205
205
|
| `PreToolUse` | Before tool execution | tool, tool_input | command, prompt | v2.1.63+ |
|
|
206
206
|
| `PostToolUse` | After tool execution | tool, tool_input, tool_output | command, prompt | v2.1.63+ |
|
|
207
|
-
| `PreCompact` | Before context compaction |
|
|
208
|
-
| `PostCompact` | After context compaction |
|
|
207
|
+
| `PreCompact` | Before context compaction | trigger | command, prompt | v2.1.76+ |
|
|
208
|
+
| `PostCompact` | After context compaction | trigger, compact_summary | command, prompt | v2.1.76+ |
|
|
209
209
|
| `Stop` | Session ending | — | command, prompt | v2.1.63+ |
|
|
210
210
|
| `SessionStart` | Session begins | — | command | v2.1.63+ |
|
|
211
211
|
| `SessionEnd` | Session fully closes | — | command | v2.1.76+ |
|
|
@@ -135,6 +135,13 @@ Cross-reference: R018 (Member Completion Verification), `feedback_release_delega
|
|
|
135
135
|
3. **설명력**: 이것은 **왜 clause 강화가 14회 내내 실패했는지**를 설명한다 — 에이전트에게 "종료하지 말라"고 지시해도 **절단 주체가 에이전트가 아니면 지시가 닿지 않는다**. 역으로 「위임 경계를 Phase 개수로 설계」가 효과적이었던 이유도 설명된다: 작업이 작으면 턴 한도 안에 끝나기 때문이지 에이전트가 더 순종적이어서가 아니다.
|
|
136
136
|
4. **정량 기준 신설**: 위임 크기 판정을 "Phase가 몇 개인가"에서 **"필요 tool call이 20턴 안에 들어가는가"**로 바꾼다. 파일 1개당 Read + Edit + 미러 Edit + diff 확인 = 약 4턴이므로, **파일 편집형 위임은 담당 파일 4~5개가 실질 상한**이다. v1.1.50 세션의 절단 3건은 담당 파일이 각각 4개·3개·7개였고 파일당 신규 노트 추가·은퇴 판정·미러 동기화를 함께 요구했다 — 산술적으로 20턴에 들어갈 수 없는 위임이었다. **이는 에이전트의 실패가 아니라 오케스트레이터의 위임 설계 결함이다.**
|
|
137
137
|
5. **완화책**: 미러 동기화처럼 **후행 필수 작업은 마지막에 몰지 말고 파일 단위로 즉시 수행**한다 — 절단은 항상 마지막 작업을 자르므로, 마지막에 몰린 작업은 절단 시 전량 유실된다.
|
|
138
|
+
6. **정량 기준 정밀화 — 산술 단위는 파일이 아니라 편집 항목 (Origin: #1621 #2a, v1.1.51 세션)**: 위 4항의 "파일 4~5개 상한"은 신설 당일 재절단을 막지 못했다 — R017 그룹은 담당 6파일로 상한 인접이었으나, 실제 초과 원인은 파일 수가 아니라 **조항 2개 신설**이었다. 파일당 편집 항목이 1개라는 암묵 가정이 깨지면 파일 수 기준 산술이 무의미해진다. 위임 크기 산술의 단위는 **편집 항목 수**(신설 조항 1개, 버전 노트 1개, 사본 배선 1개 등)로 바꾼다: **항목당 약 2턴(Edit + 위치 탐색) + 파일당 고정비(Read 1 + 미러 Edit 1 + diff 1 = 3턴)**로 계산한다. 같은 세션에서 4파일·항목 소수 위임들은 완주해 대조를 이룬다.
|
|
139
|
+
7. **리서치형 위임의 산출물 우선 기록 (Origin: #1621 #2b, v1.1.51 세션)**: 파일 편집형뿐 아니라 리서치형(수집 중심) 위임도 절단에 취약하다 — v1.1.51 세션에서 훅 이벤트 감사 위임이 22회 도구 호출(WebFetch/Read)을 전부 수집에 쓰고 **아티팩트를 1회도 Write하지 않은 채 절단**되어, 수집한 산출물 전량이 에이전트 컨텍스트에만 존재하고 파일에는 남지 않았다(재개 지시 "산출물 우선 기록"으로 복구). 리서치형 위임의 표준 문안에는 **"첫 2턴 안에 아티팩트 골격을 Write하고 수집 즉시 증분 Edit하라 — 수집 완료 후 일괄 기록 금지"**를 포함한다. 절단은 항상 마지막 작업을 자르므로, 기록을 마지막에 몰면 절단 시 산출물이 전량 유실된다 — 위 5항(파일 편집형의 "미러 즉시 동기화")의 리서치형 대응이다.
|
|
140
|
+
|
|
141
|
+
| Anti-pattern | Required |
|
|
142
|
+
|--------------|----------|
|
|
143
|
+
| 파일 수만 세어 위임 크기 판정 → 조항 다수 파일에서 절단 | 편집 항목 수 × 2턴 + 파일 고정비(3턴)로 산술 |
|
|
144
|
+
| 리서치 위임이 수집을 끝낸 뒤 일괄 기록 | 첫 2턴 내 골격 Write + 증분 Edit |
|
|
138
145
|
|
|
139
146
|
Cross-reference: R018 (v2.1.246 maxTurns partial-marking 노트), R009 (Member Prompt Size Cap — 프롬프트 토큰 상한과 별개로 턴 수 상한도 위임 크기 설계 변수임을 추가).
|
|
140
147
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
oh-my-customcode uses an **advisory-first enforcement model**. Most rules are enforced through prompt engineering (CLAUDE.md, rules/, `SessionStart` re-injection[^postcompact]) rather than hard-blocking hooks. This is intentional — it preserves agent flexibility while maintaining behavioral standards.
|
|
8
8
|
|
|
9
|
-
[^postcompact]: compact 후 재주입의 문서상 보장 경로는 `SessionStart`(matcher `*`, `claude-md-reinject.sh` — v1.1.50 #1617)이다. 기존 PostCompact prompt 배선은 유지되나 공식 문서상 `additionalContext`가 정의돼 있지 않아 효과 미보장·발동 미검증(hook-events-audit 2026-08-29). Origin: #1619 #7 — 최초 보고는 'PostCompact 공식 부재'였으나 감사 실측 결과 실재하되 additionalContext 미정의로 정정됨. 서브에이전트 보고의 검증 없는 인용이 틀린 전제를 회고 이슈까지 전파시킨 사례 (R020 원인 분석 검증 조항의 실증).
|
|
9
|
+
[^postcompact]: compact 후 재주입의 문서상 보장 경로는 `SessionStart`(matcher `*`, `claude-md-reinject.sh` — v1.1.50 #1617)이다. 기존 PostCompact prompt 배선은 유지되나 공식 문서상 `additionalContext`가 정의돼 있지 않아 효과 미보장·발동 미검증(hook-events-audit 2026-08-29). 후속 바이너리 프로브(postcompact-probe 2026-08-29, CC 2.1.251)에서 dispatch 경로 실재가 확인됨(전용 실행 함수·payload 스키마 `trigger`/`compact_summary`·dispatch map 등록, PreCompact 대비 동형 구조). 따라서 '발동 미검증'은 'dispatch 실재하나 라이브 발동·prompt 핸들러 효과는 미검증'으로 좁혀진다 — `additionalContext` 미정의는 불변이므로 재주입 보장 경로는 여전히 SessionStart다. Origin: #1619 #7 — 최초 보고는 'PostCompact 공식 부재'였으나 감사 실측 결과 실재하되 additionalContext 미정의로 정정됨. 서브에이전트 보고의 검증 없는 인용이 틀린 전제를 회고 이슈까지 전파시킨 사례 (R020 원인 분석 검증 조항의 실증).
|
|
10
10
|
|
|
11
11
|
## Enforcement Tiers
|
|
12
12
|
|
|
@@ -21,9 +21,15 @@ oh-my-customcode uses an **advisory-first enforcement model**. Most rules are en
|
|
|
21
21
|
| Advisory (proactive) | UserPromptSubmit hook | R020 (원인 진단) | `fail-axis-cause-advisor.sh` (#1561, v1.1.44) — 원장에 실패 기록이 있는데 원인 진술 없는 재촉 프롬프트가 오면 `hookSpecificOutput.additionalContext`로 "원인 가설 되묻기" advisory 전달. 원장 부재 시 조용히 통과 |
|
|
22
22
|
| Prompt-based | CLAUDE.md + rules/ + `SessionStart` 재주입(matcher `*`; PostCompact 이벤트 배선은 유지되나 효과 미보장[^postcompact]) | All MUST rules | Behavioral guidance in context |
|
|
23
23
|
|
|
24
|
-
>
|
|
24
|
+
> **훅 배선 경로 (v1.1.53, #1623)**: `.claude/hooks/hooks.json`은 CC가 로드하는 파일이 아니다 — CC 2.1.251 바이너리에 `.claude/hooks` 경로 참조가 0건이고, `settings.json`에 `hooks` 키가 이력상 존재한 적이 없었다. 컴파일레이션 메타포로는 `hooks.json` = **소스**(source), CC가 실제 로드하는 것은 `.claude/settings.json`(+`.local.json`, +`templates/` 미러)의 공식 `hooks` 블록 = **빌드 산출물**이며, 변환은 `src/core/hooks-settings.ts`가 담당한다(matcher 조건 DSL 12건은 스크립트 자체 가드 3건 + stdin-가드 래퍼 9건으로 이관). `omcustom init` 사용자는 `installHooksSettings()`가 최종 settings.local.json에 병합하는 경로를 거친다. **`hooks.json`만 고치고 settings 재생성(빌드)을 누락하면 변경이 발화에 반영되지 않는다** — R022 Wiki Sync의 "페이지 갱신 + 매니페스트 재시딩" 이원 요구와 동형이다. 라이브 실증(2026-08-29 새 세션 프로브): SessionStart hook_success 9건(플러그인 2 + 프로젝트 7), `[claude-md-reinject]` 마커 5히트, UserPromptSubmit·Stop 발화 확인 — **세션 단위 발화의 최초 실측**은 이 릴리즈다.
|
|
25
|
+
|
|
26
|
+
| Anti-pattern | Required |
|
|
27
|
+
|--------------|----------|
|
|
28
|
+
| `hooks.json`만 편집하고 커밋 → settings.json `hooks` 블록 미재생성 | 편집 후 `hooks-settings.ts` 변환기(빌드) 실행 → settings.json(+local+templates) 재생성 확인 후 커밋 |
|
|
29
|
+
|
|
30
|
+
> **Advisory (proactive/retroactive) 발화 결함과 해소 (실측)**: `hookSpecificOutput.additionalContext` **전달 경로 자체는 #1547(v1.1.40)에서 구현**됐으나, 그 앞단 **파서 셀렉터 결함**으로 advisory가 **v1.1.42까지 한 번도 발화하지 못했다** — `jq -r '.role'`로 읽었으나 트랜스크립트 최상위에 `role` 키가 없어(실제는 `.message.role`) `last_assistant`가 항상 비고 즉시 `exit 0`으로 종료됐다. 당시 실측: 트랜스크립트 771개 전수에서 `"additionalContext":` 출현 0건, 라이브 프로브 stdout/stderr 각 0바이트. **proactive(`r007-r008-drift-advisor.sh`)와 retroactive(`session-reflection.sh`, 동일 결함) 두 계층 모두 미발화**였다. **v1.1.43에서 양 계층 파서 복구 + `PostToolUse` 배선을 완료했고, 라이브 프로브로 최초 발화를 확인했다(#1553).** **재검토(#1623)**: 이 서술은 세션 단위 CC 훅 서브시스템 경유 증거가 없다 — 당시에도 CC가 로드하는 `settings.json`에 `hooks` 블록이 부재했으므로, 리서치(git 이력 전수) 판단으로는 스크립트에 synthetic stdin을 직접 주입한 검증이었을 가능성이 가장 유력하다(단정 금지 — "직접 증거 없음"으로 기록). 세션 단위 발화의 최초 실측은 위 「훅 배선 경로 (v1.1.53, #1623)」의 v1.1.53이다. 후속으로 v1.1.44에서 R008 판정을 블록 인접 비교 → 턴 단위 개수 비교로 전환(#1563), v1.1.45에서 Skill 도구 면제를 추가했다(#1569). **v1.1.49에서 역방향 신호**(announce > tool_use — 도구 호출을 예고해 놓고 tool_use 블록 없이 턴을 종료한 방향)**를 추가했다(#1595 #6)** — 기존 판정식 `max(0, tool_use − announce)`는 이 방향을 **구조적으로 0으로 처리**해 원리적으로 탐지 불가였다. 역방향은 **전용 앵커 정규식**(`$an_anchored`, 줄 시작 앵커 있음 — forward의 `$an_tool`에는 적용하지 않는다. forward는 announce를 덜 세면 위반이 **늘어나기** 때문)을 쓰고, **Skill 포함 전체 tool_use가 0건**일 때만 계상한다(Skill 제외 카운트를 쓰면 Skill만 호출한 준수 턴에서 오발화). 기본 off 옵트인(`OMCUSTOM_R008_REVERSE=on`으로 활성)이다 — 482턴 실측에서 순진한 `announce − ntools > 0` 구현은 36턴에 발화해 advisory 총량을 2배로 만들었고(16건은 Skill 제외 아티팩트, 15건은 앵커 없는 정규식의 산문 매칭), 협소화 후 3/3 진양성·오탐 0(앵커 비용은 실제 announce 969줄 중 1줄, 0.1%)이 되었으나 표본이 3건이라 기본 활성은 보류했다. **배선 구조상 예방 효과가 없다는 점도 보류 근거다** — 결함 턴은 tool_use가 0이라 `PostToolUse`·`SubagentStop`이 발화하지 않고, `UserPromptSubmit`은 사용자가 이미 개입한 뒤 발화한다. 정시에 발화하는 유일한 이벤트는 `Stop`이며 거기 걸린 훅은 `session-reflection.sh`다. 역방향은 그래서 **의도적으로 advisor 전용**이며 `session-reflection.sh`에는 복제하지 않았다(같은 결함을 두 번 보고하면서 교정 기회는 여전히 0이 되고, 되돌릴 지점만 두 곳이 된다).
|
|
25
31
|
>
|
|
26
|
-
> 교훈: **배선 확인 ≠ 전달 확인 ≠ 발화 확인** — R020 "actual outcome ≠ attempt"의 훅 도메인 재현 사례.
|
|
32
|
+
> 교훈: **배선 확인 ≠ 전달 확인 ≠ 발화 확인 ≠ 로드 확인** — R020 "actual outcome ≠ attempt"의 훅 도메인 재현 사례. v1.1.53(#1623)이 드러낸 것은 이 셋보다 앞선 **제4층**이다 — 배선 파일(`hooks.json`) 자체가 CC에 **로드되지 않았다면** 배선·전달·발화 확인은 전부 무의미한 층 위에서 이루어진 것이다.
|
|
27
33
|
|
|
28
34
|
<!--
|
|
29
35
|
> **v2.1.163+**: Stop and SubagentStop hooks can return `hookSpecificOutput.additionalContext` (JSON) to feed structured feedback back into Claude's context without triggering a hook error label. This enables advisory-style enforcement via Stop/SubagentStop hooks (e.g., `session-reflection.sh`, omcustom-loop SubagentStop) to pass richer context — replacing plain stderr text — without disrupting the turn continuation behavior that advisory-first enforcement relies on.
|