oh-my-customcode 1.1.71 → 1.1.73
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 +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/.claude/hooks/scripts/r007-r008-drift-advisor.sh +43 -1
- package/templates/.claude/hooks/scripts/user-prompt-preprocessor.sh +12 -2
- package/templates/.claude/rules/MUST-continuous-improvement.md +2 -0
- package/templates/.claude/rules/MUST-enforcement-policy.md +1 -1
- package/templates/.claude/rules/MUST-orchestrator-coordination.md +10 -0
- package/templates/.claude/skills/pipeline/workflows/auto-dev.yaml +3 -0
- package/templates/manifest.json +1 -1
- package/templates/workflows/auto-dev.yaml +3 -0
package/dist/cli/index.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -125,6 +125,9 @@
|
|
|
125
125
|
# OMCUSTOM_TRANSCRIPT_BASE — transcript 디렉토리 경로 override (설정 시 최우선)
|
|
126
126
|
# OMCUSTOM_R007_MARKER_DIR — dedup 마커 디렉토리 override (기본 ${TMPDIR:-/tmp})
|
|
127
127
|
# OMCUSTOM_R008_REVERSE=on — 역방향 신호(예고했으나 도구 미호출) 활성화. 기본 off.
|
|
128
|
+
# OMCUSTOM_R008_NARRATION=on — narration(thinking 직렬화) 채널도 announce 정규식으로 스캔해
|
|
129
|
+
# "그중 narration 채널에만 존재 M건"을 문구에 덧붙임. 기본 off —
|
|
130
|
+
# 꺼진 상태에서는 판정/문구가 기존과 바이트 동일(#1701 하네스 제안 2).
|
|
128
131
|
|
|
129
132
|
set -euo pipefail
|
|
130
133
|
|
|
@@ -326,7 +329,32 @@ split("\n")
|
|
|
326
329
|
| ([ $lines[] | select(test("^\\[[^\\]]+\\]\\[[^\\]]+\\] ?(→|->|—>) ?Tool:")) ] | length) as $an_anchored
|
|
327
330
|
# $nall_tools는 위 forward r008 가드에서 이미 바인딩됨 — 재바인딩하지 않고 재사용한다.
|
|
328
331
|
| (if $nall_tools == 0 and $an_anchored > 0 then $an_anchored else 0 end) as $r008rev
|
|
329
|
-
|
|
332
|
+
# ── OPT-IN narration split (#1701 하네스 제안 2) ────────────────────────────────
|
|
333
|
+
# 기본 판정($r008)은 `$blocks`(thinking 제외)만 본다 — 이 절은 그 판정을 바꾸지 않고,
|
|
334
|
+
# 같은 announce 정규식을 thinking(narration) 블록의 `.thinking` 텍스트에도 적용해
|
|
335
|
+
# "누락으로 계상된 건 중 narration 채널에만 존재하는 건수"를 부가 필드로만 산출한다.
|
|
336
|
+
# #1654가 확정한 대로 narration ≠ text이므로 기본 판정은 그대로 위반으로 남는다 —
|
|
337
|
+
# 이 필드는 진단 정보(어느 채널에 있었는지)만 덧붙인다. OMCUSTOM_R008_NARRATION=on일
|
|
338
|
+
# 때만 bash 쪽에서 문구에 반영하고, 기본(off)에서는 아래 tsv 6번째 필드가 계산은 되지만
|
|
339
|
+
# bash가 0으로 강제해 문구·판정 바이트가 기존과 동일하게 유지된다.
|
|
340
|
+
| ([ $turn[] | .message.content[]? | select(.type? == "thinking") | (.thinking? // "") ]
|
|
341
|
+
| join("\n") | split("\n")) as $nlines
|
|
342
|
+
| ([ $nlines[] | select(test("\\[.+\\]\\[.+\\] ?(→|->|—>) ?Tool:")) ] | length) as $an_tool_narr
|
|
343
|
+
| ([ $nlines[] | select(test("\\[.+\\]\\[.+\\] ?(→|->|—>) ?Tool: ?Agent")) ] | length) as $an_tool_agent_narr
|
|
344
|
+
| ([ $nlines[] | select(test("^[[:space:]]*\\[[0-9]+\\][[:space:]].*(→|->|—>)")) ] | length) as $an_spawn_item_narr
|
|
345
|
+
| ([ $nlines[] | select(test("^[[:space:]]*[a-z][a-z0-9-]*:(haiku|sonnet|opus|fable|inherit)[[:space:]]*(→|->|—>)")) ]
|
|
346
|
+
| length) as $an_spawn_single_narr
|
|
347
|
+
| ([ $nlines[] | select(test("\\[.+\\]\\[.+\\] ?(→|->|—>) ?Spawning:")) ] | length) as $an_spawn_hdr_narr
|
|
348
|
+
| (if $an_spawn_item_narr > 0 then $an_spawn_item_narr
|
|
349
|
+
elif $an_spawn_single_narr > 0 then $an_spawn_single_narr
|
|
350
|
+
else $an_spawn_hdr_narr end) as $an_spawn_narr
|
|
351
|
+
| ($an_tool_narr + (if $an_spawn_narr > $an_tool_agent_narr then $an_spawn_narr - $an_tool_agent_narr else 0 end))
|
|
352
|
+
as $announce_narr
|
|
353
|
+
# 부가 필드는 $r008(기존 판정)을 상한으로 bound한다 — narration에 announce가 더 많아도
|
|
354
|
+
# "누락으로 계상된 건수"를 초과해 보고하지 않는다(과대 서술 방지).
|
|
355
|
+
| (if $r008 > 0 then (if $announce_narr < $r008 then $announce_narr else $r008 end) else 0 end) as $r008narr
|
|
356
|
+
| [$tuuid, ($r007 | tostring), ($r008 | tostring), ($r008rev | tostring), ($tool_names_list | join(",")),
|
|
357
|
+
($r008narr | tostring)] | @tsv
|
|
330
358
|
)
|
|
331
359
|
end
|
|
332
360
|
end
|
|
@@ -352,10 +380,18 @@ r008_violations=$(printf '%s' "$result" | cut -f3)
|
|
|
352
380
|
r008_reverse=$(printf '%s' "$result" | cut -f4)
|
|
353
381
|
# 미접두 도구 이름 목록 (#1687) — $ntools와 동일 필터의 순서 보존 목록, "," join.
|
|
354
382
|
tool_names=$(printf '%s' "$result" | cut -f5)
|
|
383
|
+
# narration 채널 분리 카운트 (#1701 하네스 제안 2) — $r008(기존 판정)의 부분집합, 상한 bound.
|
|
384
|
+
r008_narration=$(printf '%s' "$result" | cut -f6)
|
|
355
385
|
|
|
356
386
|
: "${r007_violations:=0}"
|
|
357
387
|
: "${r008_violations:=0}"
|
|
358
388
|
: "${r008_reverse:=0}"
|
|
389
|
+
: "${r008_narration:=0}"
|
|
390
|
+
|
|
391
|
+
# narration 분리 신호는 OPT-IN, 기본 off — 켜지지 않으면 문구/판정은 기존과 바이트 동일.
|
|
392
|
+
if [ "${OMCUSTOM_R008_NARRATION:-off}" != "on" ]; then
|
|
393
|
+
r008_narration=0
|
|
394
|
+
fi
|
|
359
395
|
|
|
360
396
|
# 역방향 신호는 OPT-IN, 기본 off.
|
|
361
397
|
# 측정 정밀도는 3/3이지만 표본이 3건뿐이고, 배선 구조상 "예방"이 불가능하다:
|
|
@@ -403,6 +439,12 @@ if [ "$r008_violations" -gt 0 ]; then
|
|
|
403
439
|
violation_desc="R008 도구 식별 접두사 누락 ${r008_violations}건"
|
|
404
440
|
instruction="모든 도구 호출에 [agent][model] → Tool: 접두사를 포함하십시오."
|
|
405
441
|
fi
|
|
442
|
+
# narration 채널 분리 (#1701 하네스 제안 2, OPT-IN): 판정 자체는 그대로 위반으로 남기고,
|
|
443
|
+
# 그중 몇 건이 narration(thinking 직렬화)에만 존재했는지를 문구에 덧붙인다. 기본 off에서는
|
|
444
|
+
# r008_narration이 위에서 0으로 강제되어 이 블록이 문구를 바꾸지 않는다.
|
|
445
|
+
if [ "$r008_narration" -gt 0 ]; then
|
|
446
|
+
violation_desc="${violation_desc} (그중 narration 채널에만 존재 ${r008_narration}건)"
|
|
447
|
+
fi
|
|
406
448
|
# 미접두 도구 이름 귀속 (#1687): announce는 턴 단위로 매칭되므로, $tool_names(순서 보존,
|
|
407
449
|
# non-Skill tool_use)의 마지막 r008_violations개를 부족분으로 귀속한다. 카운트 문구
|
|
408
450
|
# "R008 도구 식별 접두사 누락 N건"은 위에서 이미 확정되어 바이트 동일 유지 — 이 접미사는
|
|
@@ -58,11 +58,21 @@ if [ -z "$user_input" ]; then
|
|
|
58
58
|
exit 0
|
|
59
59
|
fi
|
|
60
60
|
|
|
61
|
+
# ── 출처 필터 (#1692) ──
|
|
62
|
+
# session-end 트리거 단어("done"/"완료"/"마무리" 등)는 서브에이전트 hand-back 메시지나
|
|
63
|
+
# task-notification/system-reminder 프레임 안에도 나타날 수 있다. 그런 입력은 사용자 발화가
|
|
64
|
+
# 아니므로, 아래 프레임 마커가 프롬프트 어디에든 있으면 session-end 감지만 건너뛴다
|
|
65
|
+
# (slash-command 감지 등 다른 전처리는 영향받지 않는다).
|
|
66
|
+
is_non_user_frame=0
|
|
67
|
+
if printf '%s' "$user_input" | grep -qiE '(<agent-message|\[Subagent hand-back\]|<task-notification>|<system-reminder>|\[SYSTEM NOTIFICATION|⏺ Agent "|This session is being continued from a previous conversation|Base directory for this skill:)'; then
|
|
68
|
+
is_non_user_frame=1
|
|
69
|
+
fi
|
|
70
|
+
|
|
61
71
|
# ── 패턴 탐지 ──
|
|
62
72
|
hints=""
|
|
63
73
|
|
|
64
|
-
# Korean session-end signals
|
|
65
|
-
if printf '%s' "$user_input" | grep -qiE '(끝|종료|마무리|done|wrap up|end session)'; then
|
|
74
|
+
# Korean session-end signals (genuine user utterances only — see 출처 필터 above)
|
|
75
|
+
if [ "$is_non_user_frame" -eq 0 ] && printf '%s' "$user_input" | grep -qiE '(끝|종료|마무리|done|wrap up|end session)'; then
|
|
66
76
|
hints="${hints}[Hook] Session-end signal detected — R011 memory saves will be triggered"$'\n'
|
|
67
77
|
fi
|
|
68
78
|
|
|
@@ -42,6 +42,8 @@ Origin: #1533 (v1.1.35에서 R017 (b) 조항 추가했으나 auto-dev.yaml versi
|
|
|
42
42
|
|
|
43
43
|
Cross-reference: R021(Enforcement Policy — advisory 규칙의 발동 지점), R017(구조 검증).
|
|
44
44
|
|
|
45
|
+
**적용 범위 확장 (Origin: #1698 #1 — v1.1.72)**: 이 self-check는 서브에이전트에 **요구하는** 규율뿐 아니라 오케스트레이터가 **스스로 작성하는 산문(룰 문안·요약·수치)**에도 대칭 적용됩니다. 상세·인용은 R010 「출처 인용과 인접 문구 점검도 같은 규율」 보강 2항목 참조.
|
|
46
|
+
|
|
45
47
|
## Integration
|
|
46
48
|
|
|
47
49
|
| Situation | Action |
|
|
@@ -16,7 +16,7 @@ oh-my-customcode uses an **advisory-first enforcement model**. Most rules are en
|
|
|
16
16
|
| Soft Block | Stop hook prompt | R011 session-end saves | Auto-performs then approves |
|
|
17
17
|
| Conversation Block | PostToolUse hook + `continueOnBlock` (CC v2.1.139+), exit 2 | stuck-detector, context-budget-advisor, cost-cap-advisor | Feeds rejection reason into conversation; Claude continues with awareness |
|
|
18
18
|
| Advisory | PostToolUse hooks | R007, R008, R009, R010, R018 | Warns via stderr, never blocks |
|
|
19
|
-
| Advisory (proactive) | UserPromptSubmit + SubagentStop + PostToolUse hooks | R007, R008 (`r007-r008-drift-advisor.sh` — #1229 UserPromptSubmit, #1545 SubagentStop, #1553 PostToolUse) | Reads last assistant turn; emits advisory if header/prefix absent. SubagentStop wiring (#1545) closes the no-user-input autonomous-loop gap (`/fsd`); PostToolUse (#1553) covers the orchestrator-only stretch before the first subagent spawn. Complements retroactive Stop-hook (`session-reflection.sh`, #1190). **v1.1.43부터 실제 발화 — 아래 각주 참조.** v1.1.49부터 역방향(announce > tool_use) 신호 포함, 기본 off 옵트인 (#1595 #6). **v1.1.62(#1650 D)부터 서브에이전트 세션에서는 침묵**: hook stdin의 `agent_id`(CC 스키마상 서브에이전트 내부 발화에만 존재)가 있으면 exit 0 — 단 `SubagentStart`/`SubagentStop`은 `agent_id`가 대상 식별자라 예외(판정 수행). 중첩 서브에이전트 안의 SubagentStop은 억제 못 함(R010 정책상 도달 불가 경로). 완료 보고가 advisory 응답으로 대체되던 훅 피드백 잠식(R020 8항, #1652 #3)의 advisory 계열 차단. |
|
|
19
|
+
| Advisory (proactive) | UserPromptSubmit + SubagentStop + PostToolUse hooks | R007, R008 (`r007-r008-drift-advisor.sh` — #1229 UserPromptSubmit, #1545 SubagentStop, #1553 PostToolUse) | Reads last assistant turn; emits advisory if header/prefix absent. SubagentStop wiring (#1545) closes the no-user-input autonomous-loop gap (`/fsd`); PostToolUse (#1553) covers the orchestrator-only stretch before the first subagent spawn. Complements retroactive Stop-hook (`session-reflection.sh`, #1190). **v1.1.43부터 실제 발화 — 아래 각주 참조.** v1.1.49부터 역방향(announce > tool_use) 신호 포함, 기본 off 옵트인 (#1595 #6). v1.1.73부터 narration 채널 분리 신호(`OMCUSTOM_R008_NARRATION=on`, 기본 off 옵트인) 포함 — 접두사가 thinking/narration 블록에만 있는 누락을 별도 계수합니다 (#1701 #2). **v1.1.62(#1650 D)부터 서브에이전트 세션에서는 침묵**: hook stdin의 `agent_id`(CC 스키마상 서브에이전트 내부 발화에만 존재)가 있으면 exit 0 — 단 `SubagentStart`/`SubagentStop`은 `agent_id`가 대상 식별자라 예외(판정 수행). 중첩 서브에이전트 안의 SubagentStop은 억제 못 함(R010 정책상 도달 불가 경로). 완료 보고가 advisory 응답으로 대체되던 훅 피드백 잠식(R020 8항, #1652 #3)의 advisory 계열 차단. |
|
|
20
20
|
| Advisory (telemetry) | PostToolUseFailure hook | — (계측 전용, 규칙 강제 없음) | `failure-ledger.sh` (#1561, v1.1.44) — 도구 실패를 JSONL 원장에 append. stdout/stderr 무출력이라 모델에 도달하지 않으며 절대 차단하지 않음 |
|
|
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 |
|
|
@@ -375,6 +375,16 @@ Origin: #1646 (v1.1.58 세션 — 커밋 위임서 `12e74a76` vs 실제 `e101a0b
|
|
|
375
375
|
| 인용 대상 코드를 보지 않고 제안 문안을 룰로 승격 | 인용문이 가리키는 코드·파일과 대조 후 기재 |
|
|
376
376
|
| 오케스트레이터의 함의를 확정형으로 위임서에 기재 | `[가설 — 실측이 반대면 반대로 기재]` 표기 + 판정 기준 동봉 |
|
|
377
377
|
|
|
378
|
+
**보강 2항목 (Origin: #1698 #1·#2 — #3은 #1로 흡수 — v1.1.72)**: [#1698 #1] 오케스트레이터는 룰·문서에 커밋될 **최종 문안**을 완성문으로 위임서에 붙여 넣지 않습니다 — 서브에이전트가 이슈 본문에서 사실·수치를 직접 뽑아 작성하고, 오케스트레이터는 제안 포인터·삽입 위치·제약을 지정합니다. 단 인용 원문, 실측 사실(HEAD/브랜치/카운트), 승인·금지 작업 목록, 표준 금지 문구, `[가설]` 표기 함의, 검증 재실행 금지 목록은 계속 오케스트레이터가 기재합니다 — 이들은 "최종 문안"이 아니라 위임 전제입니다(배선: `auto-dev.yaml` implement 스텝 description, 4사본). 원인은 "오케스트레이터가 위임서에 룰 문안 전체를 직접 작성하면서 수치를 기억으로 옮김 — 서브에이전트에는 `gh issue view` 인용을 요구했으나 오케스트레이터 자신의 산문은 같은 규율을 받지 않음"이며, "인용 정확성이 주제인 반복에서 같은 계열 3회째(v1.1.69 세션번호 → v1.1.70 "and marketplace" → v1.1.71 "High 2건")"가 재발 근거입니다. "리뷰에서 오류가 난 문장은 전부 오케스트레이터 작성분이었음"이며, "인용 대조 14건 중 오케스트레이터 작성 수치 1건 외 전부 원문 일치"가 실증됩니다. [#1698 #2] 인접 점검은 "같은 주제 키워드 grep"에만 의존하지 않고, 삽입 지점 앞뒤 섹션(±1 heading)의 Anti-pattern 표를 전수 대조하는 이원화를 적용합니다 — "인접성 점검을 키워드 grep에만 의존"했다가 12줄 아래 조항과의 모순을 놓친 사례가 원인입니다. [#1701 #1] 위임서에 적는 오케스트레이터 자신의 **요구사항 요약**(예: "…만 지정"과 같은 범위 문구)도 최종 문안과 동일하게 취급합니다 — 단 위 예외 목록(승인·금지 작업 목록·표준 금지 문구·실측 사실 등)은 이 취급에서 제외합니다. 원인은 "위임서가 이슈 원문 대신 오케스트레이터가 요약한 요구 문구("…만")를 전달 — 룰 문안은 서브에이전트가 썼지만 범위 규정은 오케스트레이터 산문이었음"이며, 이슈 제안 문장을 `gh issue view` 출력에서 복사해 전달하고 오케스트레이터의 범위 요약에는 `[요약 — 원문 우선]`을 표기합니다(배선: auto-dev.yaml implement 스텝 bullet 확장, 4사본 — R016 Rule Wiring Check).
|
|
379
|
+
|
|
380
|
+
> **#1700 판정 (v1.1.72)**: 이 인용의 "v1.1.69 세션번호"는 발생 시점이 아니라 조항 작성 시점으로 보입니다 — 오류 발생·적발은 Iteration 4/v1.1.67(#1688 찐빠 #1), 조항 초안은 커밋 448143c7(PR #1690 = release/v1.1.69) 실측입니다.
|
|
381
|
+
|
|
382
|
+
| Anti-pattern | Required |
|
|
383
|
+
|--------------|----------|
|
|
384
|
+
| 룰 문안 **최종 문안**을 오케스트레이터가 직접 작성해 위임서에 기재 | 서브에이전트가 이슈 본문에서 사실·수치를 직접 뽑아 작성; 오케스트레이터는 포인터·삽입 위치·제약만 지정 — 인용 원문·실측 사실·승인/금지 목록·표준 금지 문구·`[가설]` 표기·재실행 금지 목록은 오케스트레이터가 계속 기재 |
|
|
385
|
+
| 위임서의 "요구사항 요약"을 오케스트레이터가 산문으로 재구성해 확정형으로 기재 (예: "…만 지정") | 이슈 제안 문장을 `gh issue view` 출력에서 복사해 전달; 오케스트레이터의 범위 요약에는 `[요약 — 원문 우선]` 표기 |
|
|
386
|
+
| 같은 주제 키워드 grep만으로 인접성 점검 완료 판정 | 키워드 grep + 삽입 지점 ±1 heading의 Anti-pattern 표 전수 대조를 병행 |
|
|
387
|
+
|
|
378
388
|
#### 참인 전제 ≠ 참인 함의 — 브랜치 전환 위임 (Origin: #1595 #2)
|
|
379
389
|
|
|
380
390
|
uncommitted 변경이 있는 상태의 브랜치 전환을 위임할 때, 위임서에 **전환의 결과를 확정형으로 예측해 적지 않는다**. "대상 브랜치에 그 파일이 없다"는 **사실**에서 "전환해도 안전하다"는 **함의**는 도출되지 않는다 — 파일이 **없기 때문에** checkout이 그 파일을 삭제해야 하고, modified 상태면 거부된다.
|
|
@@ -384,6 +384,9 @@ steps:
|
|
|
384
384
|
- No direct orchestrator writes (R010)
|
|
385
385
|
- All Agent tool calls MUST pass mode: "bypassPermissions" to prevent permission prompts during unattended execution
|
|
386
386
|
- Mid-run scope additions (a new issue added via user interrupt AFTER implement began) MUST re-enter the verify-build gate before release — do NOT let a late-added scope item ride to release on a prior verify-build pass. Critical for code changes; benign for docs-only (#1301).
|
|
387
|
+
- When an implement-stage delegation edits rule/skill/guide TEXT, the orchestrator MUST NOT author the final committed wording — the subagent writes it directly from the issue body (`gh issue view <N> --json body`); the delegation prompt supplies pointers, insertion anchors, constraints, verbatim tool-output quotations, and measured facts only
|
|
388
|
+
(R010 「출처 인용과 인접 문구 점검도 같은 규율」 보강 2항목, #1698 #1). The orchestrator's own requirement/scope summary counts as final wording too — copy the issue's proposal sentence from `gh issue view` output verbatim, and tag any orchestrator paraphrase "[요약 — 원문 우선]"
|
|
389
|
+
(#1701 #1).
|
|
387
390
|
|
|
388
391
|
|
|
389
392
|
## Sensitive Path Handling (CC v2.1.121+)
|
package/templates/manifest.json
CHANGED
|
@@ -384,6 +384,9 @@ steps:
|
|
|
384
384
|
- No direct orchestrator writes (R010)
|
|
385
385
|
- All Agent tool calls MUST pass mode: "bypassPermissions" to prevent permission prompts during unattended execution
|
|
386
386
|
- Mid-run scope additions (a new issue added via user interrupt AFTER implement began) MUST re-enter the verify-build gate before release — do NOT let a late-added scope item ride to release on a prior verify-build pass. Critical for code changes; benign for docs-only (#1301).
|
|
387
|
+
- When an implement-stage delegation edits rule/skill/guide TEXT, the orchestrator MUST NOT author the final committed wording — the subagent writes it directly from the issue body (`gh issue view <N> --json body`); the delegation prompt supplies pointers, insertion anchors, constraints, verbatim tool-output quotations, and measured facts only
|
|
388
|
+
(R010 「출처 인용과 인접 문구 점검도 같은 규율」 보강 2항목, #1698 #1). The orchestrator's own requirement/scope summary counts as final wording too — copy the issue's proposal sentence from `gh issue view` output verbatim, and tag any orchestrator paraphrase "[요약 — 원문 우선]"
|
|
389
|
+
(#1701 #1).
|
|
387
390
|
|
|
388
391
|
|
|
389
392
|
## Sensitive Path Handling (CC v2.1.121+)
|