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>
This commit is contained in:
55
.claude/hooks/session_context.py
Executable file
55
.claude/hooks/session_context.py
Executable file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python3
|
||||
"""SessionStart — 브랜치·미커밋·origin/main 대비·KST 장 상태·alembic head·3줄 요약 주입."""
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from _lib import git, kst_now, market_state, project_dir, read_stdin_json # noqa: E402
|
||||
|
||||
|
||||
def alembic_head(root: str) -> str:
|
||||
revs: dict[str, str | None] = {}
|
||||
for f in glob.glob(os.path.join(root, "backend/alembic/versions/*.py")):
|
||||
try:
|
||||
t = open(f, encoding="utf-8").read(4000)
|
||||
except OSError:
|
||||
continue
|
||||
r = re.search(r"^revision\s*(?::[^=]+)?=\s*['\"]([^'\"]+)['\"]", t, re.M)
|
||||
d = re.search(r"^down_revision\s*(?::[^=]+)?=\s*(.+)$", t, re.M)
|
||||
if r:
|
||||
revs[r.group(1)] = d.group(1) if d else None
|
||||
downs = set()
|
||||
for d in revs.values():
|
||||
if d:
|
||||
downs.update(re.findall(r"['\"]([^'\"]+)['\"]", d))
|
||||
heads = [r for r in revs if r not in downs]
|
||||
return ", ".join(sorted(heads)) or "?"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
data = read_stdin_json()
|
||||
root = project_dir(data)
|
||||
_, br = git(["rev-parse", "--abbrev-ref", "HEAD"], root)
|
||||
_, st = git(["status", "--porcelain"], root)
|
||||
dirty = len([l for l in st.splitlines() if l.strip()])
|
||||
rc, ahead = git(["rev-list", "--count", "origin/main..HEAD"], root)
|
||||
rc2, behind = git(["rev-list", "--count", "HEAD..origin/main"], root)
|
||||
now = kst_now()
|
||||
ctx = (
|
||||
f"[auto-stock 세션 상태] 브랜치 {br.strip() or '?'} / 미커밋 {dirty}개 / "
|
||||
f"origin/main 대비 앞섬 {ahead.strip() if rc == 0 else '?'}·뒤처짐 {behind.strip() if rc2 == 0 else '?'}(로컬 ref 기준) / "
|
||||
f"KST {now:%Y-%m-%d %H:%M} {'월화수목금토일'[now.weekday()]}요일 {market_state(now)} / alembic head {alembic_head(root)}\n"
|
||||
"1) 로컬 백엔드·docker compose up 기동 금지(NAS 와 KIS 앱키 공유 → 토큰 무효화). 검증은 격리 pytest 만.\n"
|
||||
"2) 배포는 /deploy 스킬(.claude/scripts/deploy.sh) — 장중(평일 08:30~15:40)은 재기동이 매매에 영향.\n"
|
||||
'3) 테스트: cd backend && DATABASE_URL="postgresql+asyncpg://x:x@localhost/autostock_test" '
|
||||
"../.venv/bin/python -m pytest tests/ --ignore=tests/integration -q (DB 이름 *_test 필수)"
|
||||
)
|
||||
json.dump({"hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": ctx}}, sys.stdout, ensure_ascii=False)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user