Files
hanarang-rails/hooks/post-tool.sh
이랑이 ae86d95155 feat(sprint-002): Skill 강제 진입 + Bypass 감지
Sprint 002 전체 구현 — F1 (자매 skill bypass) 해결:

Enforcement core:
- src/enforcement/skill-context.ts — 스킬 컨텍스트 생성/읽기/삭제/만료 체크
- src/enforcement/skill-trace.ts — 도구 사용 추적 (JSONL append)
- src/enforcement/guard.ts — pre-tool 가드 (context 유무 + 만료 + escape hatch)

Hooks (실제 로직):
- hooks/pre-tool.sh — Write/Edit/Bash 게이트 (context 없으면 exit 2)
- hooks/post-tool.sh — 도구 사용 trace 자동 기록

CLI:
- rails skill-context {create|show|clear}
- rails skill-trace {show|blocked}

Tests (13 신규, 22 total pass):
- skill-context: CRUD + 만료 감지
- skill-trace: append + read + blocked count
- guard: no-context 차단, valid 허용, expired 차단, RAILS_ENFORCE=off escape hatch

검증: tsc --noEmit ✓ | vitest 22/22 ✓ | build ✓ | rails --help ✓

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 13:56:58 +09:00

41 lines
1.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# hanarang-rails post-tool hook
# Appends tool usage to skill trace for audit.
# Input: stdin JSON event from Claude Code
# Exit: always 0 (post-hook should not block)
set -euo pipefail
EVENT=$(cat)
CWD="${CLAUDE_PROJECT_DIR:-$(pwd)}"
TRACE_FILE="$CWD/.rails/skill-trace.jsonl"
# Ensure directory
mkdir -p "$(dirname "$TRACE_FILE")"
# Extract fields
TOOL=$(echo "$EVENT" | jq -r '.tool_name // "unknown"' 2>/dev/null || echo "unknown")
SESSION_ID="${CLAUDE_SESSION_ID:-}"
# Read pipeline ID from context if available
PIPELINE_ID=""
CONTEXT_FILE="$CWD/.rails/skill-context.json"
if [[ -f "$CONTEXT_FILE" ]]; then
PIPELINE_ID=$(jq -r '.pipelineId // ""' "$CONTEXT_FILE" 2>/dev/null || true)
fi
# Append trace entry
ENTRY=$(jq -n \
--argjson ts "$(date +%s)000" \
--arg tool "$TOOL" \
--arg cwd "$CWD" \
--arg sessionId "$SESSION_ID" \
--arg pipelineId "$PIPELINE_ID" \
'{ts: $ts, tool: $tool, cwd: $cwd, sessionId: $sessionId, pipelineId: $pipelineId, blocked: false, reason: "post-trace"}' \
2>/dev/null || true)
if [[ -n "$ENTRY" ]]; then
echo "$ENTRY" >> "$TRACE_FILE"
fi
exit 0