Files
auto-stock/.claude/hooks/auto-orchestrate.py
shuim ea644a37f3 chore(harness): Claude Code 하네스 구성 — 안전 가드·규칙 검사·배포 스킬
- .claude/settings.json (공유) 신설, hooks 를 settings.local.json 에서 이동
- bash_guard (PreToolUse): 로컬 스택 기동·운영 DB alembic·_test 아닌 pytest·main 강제 push·위험 rm 차단,
  main push/deploy.sh 는 장중 경고와 함께 확인 요청, backend 커밋 전 유닛 스위트 게이트
- rules_check (PostToolUse): py_compile + CLAUDE.md 규칙(json.dumps, except 후 logger.debug,
  except-pass, 실시간 계층 DB I/O) — diff 추가 라인만
- session_context (SessionStart): 브랜치·미커밋·장 상태·alembic head 주입
- stop_check (Stop): frontend 변경 시 tsc
- auto-orchestrate: 서브에이전트 보고·시스템 알림·질문형 프롬프트 오발동 제거
- /deploy 스킬 + deploy.sh / prod_smoke.sh: 테스트→dev push→main 병합→재기동 감시→공개 API 스모크
- 팀 에이전트·orchestrate 스킬 추적 시작, settings.local.json 추적 해제(.gitignore)
- CLAUDE.md §9 하네스
- 훅 테스트 69 passed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 09:05:35 +09:00

83 lines
3.9 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""UserPromptSubmit hook: 작업성 요청이면 오케스트레이션 모드 지침을 컨텍스트로 주입한다.
- 화이트리스트(작업 동사) 방식: 확실한 작업 요청에만 반응해 오탐을 줄인다.
- 이스케이프: 프롬프트에 #직접 또는 #no-orch 가 있으면 주입하지 않는다.
"""
import json
import re
import sys
ACTION = re.compile(
r"(추가|수정|구현|개발|만들|생성|작성|고쳐|고치|리팩|변경|바꿔|바꾸|적용|삭제|제거|"
r"최적화|개선|버그|디버그|테스트|백테스트|검증|배포|마이그|리뷰|점검|분석|튜닝|"
r"보완|정리|통합|연동|설계|스캔|리팩터링|해결|처리해|작업|고도화|"
r"\badd\b|\bimplement\b|\bfix\b|\brefactor\b|\bcreate\b|\bbuild\b|\bupdate\b|"
r"\bremove\b|\bdelete\b|\boptimi[sz]e\b|\bmigrat|\bdebug\b|\btest\b|\bdeploy\b|"
r"\breview\b|\banaly[sz]e\b)",
re.IGNORECASE,
)
SKIP = re.compile(r"(#직접|#no-orch)", re.IGNORECASE)
# 서브에이전트 보고·시스템 알림에는 주입하지 않는다.
NOTIFY_PREFIX = "Another Claude session sent a message"
NOTIFY_MARKERS = ("<agent-message", "[Subagent hand-back]", "<task-notification>", "[SYSTEM NOTIFICATION")
# 강한 작업 동사(분석·점검 같은 약한 명사는 제외)
STRONG = re.compile(
r"(추가|수정|구현|개발|만들|생성|작성|고쳐|고치|리팩|변경|바꿔|바꾸|적용|삭제|제거|최적화|개선|"
r"마이그|튜닝|보완|통합|연동|설계|해결|고도화|배포|"
r"\badd\b|\bimplement\b|\bfix\b|\brefactor\b|\bcreate\b|\bbuild\b|\bupdate\b|\bremove\b|"
r"\bdelete\b|\boptimi[sz]e\b|\bmigrat|\bdeploy\b)",
re.IGNORECASE,
)
# 질문형: 물음표 종결 / 의문 어미 / 정보 요청
QUESTION = re.compile(
r"(\?\s*$|(했어|됐어|됬어|했나|했니|했죠|했지|되었어|돼\s*있어|있어|없어|인가|일까|인지|나요|어요|까요|죠|니)\??\s*$|"
r"(알려줘|알려주세요|알려줄래|보여줘|보여주세요|설명해줘?|설명해주세요|궁금\S*)\s*[.!]?\s*$)",
)
# 명시적 작업 요청 어미
REQUEST = re.compile(r"(해줘|해\s*주세요|해주라|하세요|해라|합시다|부탁|진행해|시작해)")
CONTEXT = """[자동 오케스트레이션 모드]
이 프롬프트는 작업성 요청으로 감지되었습니다. 답변/작업을 시작하기 전에 Skill 툴로 `orchestrate` 스킬을 호출하고, 그 지침에 따라 처리하십시오 (작업 분해 → 팀 에이전트 병렬/순차 분배 → code-verifier 검증 게이트 → 최종 보고). args 에는 사용자 요청 원문을 그대로 전달합니다.
단, 스킬 지침 4번대로 하위 작업이 1개뿐이고 단순하면 오케스트레이션 오버헤드 없이 담당 에이전트 하나만 호출하거나 직접 처리해도 됩니다.
사용자가 프롬프트에 #직접 을 넣으면 이 주입은 발생하지 않습니다."""
def main() -> int:
try:
data = json.load(sys.stdin)
except Exception:
return 0
prompt = (data.get("prompt") or data.get("user_prompt") or "").strip()
if not prompt or prompt.startswith("/") or len(prompt) < 6:
return 0
if prompt.startswith(NOTIFY_PREFIX) or any(m in prompt for m in NOTIFY_MARKERS):
return 0
if SKIP.search(prompt) or not ACTION.search(prompt):
return 0
if QUESTION.search(prompt) and not REQUEST.search(prompt.rstrip("?? ")) :
# 질문형: 강한 작업 동사가 없거나 짧은 상태 확인("수정했어?")이면 주입하지 않는다.
if not STRONG.search(prompt) or len(prompt) <= 40:
return 0
json.dump(
{
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": CONTEXT,
},
"suppressOutput": True,
},
sys.stdout,
ensure_ascii=False,
)
return 0
if __name__ == "__main__":
sys.exit(main())