Add history tracking hooks and project structure

- Add docs/history/ directory for work history files
- Add PROGRESS.md for project status tracking
- Add .claude/settings.json with Gitea MCP permissions
- Gitea issue #1: 히스토리 및 AI 사용량 추적 시스템 설정

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
minsung
2026-04-07 11:47:14 +09:00
parent c66750790f
commit 5ac612f373
11 changed files with 317 additions and 0 deletions

12
.claude/settings.json Normal file
View File

@@ -0,0 +1,12 @@
{
"permissions": {
"allow": [
"mcp__gitea__get_me",
"mcp__gitea__create_repo",
"mcp__gitea__issue_write"
],
"additionalDirectories": [
"C:\\Users\\nbright\\.claude"
]
}
}

17
PROGRESS.md Normal file
View File

@@ -0,0 +1,17 @@
# PROGRESS.md — recordingtest
## 현재 상태 요약
- 프로젝트: WPF Application User Input Regression Test
- 저장소: https://gitea.hmac.kr/kimminsung/recordingtest
- 히스토리 경로: docs/history
## 작업 이력
| 날짜 | 작업 | 이슈 | 파일 |
|------|------|------|------|
## 참고
- 히스토리 파일은 `docs/history/YYYY-MM-DD_{작업명}.md` 형식으로 저장
- 필수 항목: **소요 시간**, **Context 사용량**, **이슈** (#N)

36
historyhooks/auto-lint.sh Normal file
View File

@@ -0,0 +1,36 @@
#!/bin/bash
# auto-lint.sh
# PostToolUse (Edit|Write) 훅: TypeScript 파일 수정 후 컴파일 체크
#
# 수정된 파일이 .ts/.tsx인 경우 tsc --noEmit으로 타입 체크
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# 파일 경로가 없으면 통과
if [ -z "$FILE_PATH" ]; then
exit 0
fi
# TypeScript 파일이 아니면 통과
if ! echo "$FILE_PATH" | grep -qE '\.(ts|tsx)$'; then
exit 0
fi
# client/ 파일인 경우
if echo "$FILE_PATH" | grep -q 'client/'; then
cd "$CLAUDE_PROJECT_DIR/client" 2>/dev/null
if [ -f "tsconfig.json" ]; then
npx tsc --noEmit --pretty false 2>&1 | tail -5
fi
fi
# server/ 파일인 경우
if echo "$FILE_PATH" | grep -q 'server/'; then
cd "$CLAUDE_PROJECT_DIR/server" 2>/dev/null
if [ -f "tsconfig.json" ]; then
npx tsc --noEmit --pretty false 2>&1 | tail -5
fi
fi
exit 0

View File

@@ -0,0 +1,80 @@
# -*- coding: utf-8 -*-
"""
PostToolUse(Write) 훅: 히스토리 파일 필수 필드 검증
path.json 의 history_path 하위 *.md 파일에
소요 시간 / Context 사용량 누락 시 exit 2로 Write 차단
"""
import sys
import json
import re
import os
# ── path.json 로드 ────────────────────────────────────────────────────────────
HOOKS_DIR = os.path.dirname(os.path.abspath(__file__))
PATH_JSON = os.path.join(HOOKS_DIR, "path.json")
try:
with open(PATH_JSON, encoding="utf-8") as f:
paths = json.load(f)
except Exception:
paths = {}
history_path = paths.get("history_path", "docs/history").replace("\\", "/").rstrip("/")
# history_path 하위 어느 깊이든 *.md 이면 검사
history_pattern = re.escape(history_path) + r"/.+\.md$"
# ── 훅 입력 파싱 ──────────────────────────────────────────────────────────────
try:
data = json.load(sys.stdin)
except Exception:
sys.exit(0)
tool_input = data.get("tool_input", {})
file_path = tool_input.get("file_path", "")
content = tool_input.get("content", "")
normalized = file_path.replace("\\", "/")
if not re.search(history_pattern, normalized):
sys.exit(0)
missing = []
if not re.search(
r"\*\*소요\s*시간\*\*|^##\s*소요\s*시간|소요\s*시간\s*:",
content,
re.MULTILINE,
):
missing.append("소요 시간")
if not re.search(
r"\*\*Context\s*사용량\*\*|^##\s*Context\s*사용량|Context\s*사용량\s*:",
content,
re.MULTILINE | re.IGNORECASE,
):
missing.append("Context 사용량")
if missing:
sys.stderr.write("[BLOCKED] 히스토리 파일 필수 항목 누락: " + ", ".join(missing) + "\n")
sys.stderr.write("\n")
sys.stderr.write("파일 상단에 다음 항목을 반드시 포함하세요:\n")
sys.stderr.write("\n")
sys.stderr.write(" **소요 시간**: X분\n")
sys.stderr.write(" **Context 사용량**: input Xk / output Xk tokens\n")
sys.stderr.write("\n")
sys.stderr.write("파일: " + file_path + "\n")
sys.exit(2)
# ── 이슈 번호 검사 (없으면 사용자에게 질문 요청) ──────────────────────────────
if not re.search(r"\*\*이슈\*\*\s*[:\s]+#\d+", content, re.MULTILINE):
sys.stderr.write("[이슈 번호 필요] 히스토리 파일에 이슈 번호가 없습니다.\n")
sys.stderr.write("\n")
sys.stderr.write("사용자에게 다음을 질문하세요:\n")
sys.stderr.write(" '이 작업과 관련된 Gitea 이슈 번호가 있나요? (예: #1 / 없으면 #0)'\n")
sys.stderr.write("\n")
sys.stderr.write("답변 후 파일 상단에 추가하고 다시 저장하세요:\n")
sys.stderr.write(" **이슈**: #N\n")
sys.stderr.write("\n")
sys.stderr.write("파일: " + file_path + "\n")
sys.exit(2)
sys.exit(0)

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
# PostToolUse(Write) 훅: 히스토리 파일 필수 필드 검증
# 대상: docs/*/history/*.md
# 필수 항목(소요 시간, Context 사용량) 누락 시 exit 2로 Write 차단
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cat | python3 "$SCRIPT_DIR/guard-history-fields.py"

View File

@@ -0,0 +1,38 @@
#!/bin/bash
# Stop 훅: 작업 완료 후 히스토리 기록 리마인더
# path.json 의 history_path 에서 저장 경로를 읽어 출력
set -euo pipefail
HOOKS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PATH_JSON="$HOOKS_DIR/path.json"
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(cd "$HOOKS_DIR/../.." && pwd)}"
if command -v jq &>/dev/null && [[ -f "$PATH_JSON" ]]; then
history_path=$(jq -r '.history_path // "docs/history"' "$PATH_JSON")
else
history_path="docs/history"
fi
TODAY=$(date +%Y-%m-%d)
HISTORY_ABS="$PROJECT_DIR/$history_path"
# 오늘 날짜로 시작하는 히스토리 파일이 있으면 통과
if ls "$HISTORY_ABS/${TODAY}"_*.md 2>/dev/null | grep -q .; then
exit 0
fi
cat >&2 <<EOF
[HISTORY REQUIRED] 오늘(${TODAY}) 작업 히스토리가 없습니다.
아래 형식으로 파일을 작성하고 저장하세요:
${history_path}/${TODAY}_{작업명}.md
필수 항목:
**소요 시간**: X분
**Context 사용량**: input Xk / output Xk tokens
**이슈**: #N
히스토리 파일 저장 완료 후 응답이 종료됩니다.
EOF
exit 2

4
historyhooks/path.json Normal file
View File

@@ -0,0 +1,4 @@
{
"_comment": "각 프로젝트에서 이 파일을 복사해 history_path 만 재정의하세요.",
"history_path": "docs/history"
}

View File

@@ -0,0 +1,23 @@
#!/bin/bash
# progress-reminder.sh
# PostToolUse (Bash) 훅: 빌드/테스트 명령 실행 후 PROGRESS.md 업데이트 리마인더
#
# npm test, npm run build 등 마일스톤 명령 감지 시 알림
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
# 마일스톤 명령 감지
if echo "$COMMAND" | grep -qE '(npm (run )?(build|test|dev)|npx tsc)'; then
# additionalContext로 리마인더 전달
cat <<'EOF'
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "[리마인더] 빌드/테스트 명령이 실행되었습니다. PROGRESS.md 업데이트가 필요한지 확인하세요."
}
}
EOF
fi
exit 0

View File

@@ -0,0 +1,45 @@
#!/bin/bash
# protect-files.sh
# PreToolUse (Edit|Write) 훅: 보호 파일 수정 차단
#
# 차단 대상:
# - CLAUDE.md (명시적 요청 없이 수정 금지)
# - .env 파일 (보안)
# - storage/ 외부에 영상/바이너리 파일 쓰기
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# 파일 경로가 없으면 통과
if [ -z "$FILE_PATH" ]; then
exit 0
fi
# .env 파일 직접 수정 차단
if echo "$FILE_PATH" | grep -qE '\.env$'; then
echo "Blocked: .env 파일 직접 수정은 보안상 차단됩니다. .env.example을 수정하세요." >&2
exit 2
fi
# storage/ 외부에 영상 파일 쓰기 차단
# 주의: .ts는 TypeScript와 HLS 세그먼트 모두에 사용됨
# src/ 하위의 .ts 파일은 TypeScript이므로 허용
if echo "$FILE_PATH" | grep -qiE '\.(mp4|mkv|webm|mov|avi|m3u8)$'; then
if ! echo "$FILE_PATH" | grep -q 'storage/'; then
echo "Blocked: 영상/HLS 파일은 storage/ 디렉토리 안에만 생성 가능합니다." >&2
exit 2
fi
fi
# HLS .ts 세그먼트 파일만 차단 (src/ 하위 TypeScript 제외)
if echo "$FILE_PATH" | grep -qE '\.ts$'; then
if ! echo "$FILE_PATH" | grep -qE '(src/|\.config\.ts|tsconfig)'; then
if echo "$FILE_PATH" | grep -q 'storage/'; then
: # storage 내 .ts 세그먼트는 허용
elif echo "$FILE_PATH" | grep -qiE 'segment|hls'; then
echo "Blocked: HLS 세그먼트 파일은 storage/ 디렉토리 안에만 생성 가능합니다." >&2
exit 2
fi
fi
fi
exit 0

View File

@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# UserPromptSubmit 훅: 프로젝트 컨텍스트 주입
# path.json 의 history_path 아래 최근 작업 문서를 읽어 출력
set -euo pipefail
HOOKS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PATH_JSON="$HOOKS_DIR/path.json"
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(cd "$HOOKS_DIR/../.." && pwd)}"
if command -v jq &>/dev/null && [[ -f "$PATH_JSON" ]]; then
history_rel=$(jq -r '.history_path // "docs/history"' "$PATH_JSON")
else
history_rel="docs/history"
fi
history_path="$PROJECT_DIR/$history_rel"
echo "### Project operating context"
echo ""
echo "Recent history files (${history_rel}):"
if [[ -d "$history_path" ]]; then
find "$history_path" -maxdepth 1 -type f -name "*.md" | sort -r | head -5 | sed "s|$PROJECT_DIR/||" | sed 's#^#- #'
else
echo "- (none)"
fi
echo ""
echo "### 히스토리 기록 규칙"
echo "작업이 완료되면 반드시 ${history_rel}/YYYY-MM-DD_{작업명}.md 파일을 작성하라."
echo "필수 항목: **소요 시간**, **Context 사용량** (누락 시 저장 차단됨)"

View File

@@ -0,0 +1,25 @@
#!/bin/bash
# session-start.sh
# SessionStart 훅: 세션 시작 시 프로젝트 상태를 컨텍스트에 주입
#
# PROGRESS.md의 현재 상태 요약을 읽어서 에이전트에게 전달
PROGRESS_FILE="$CLAUDE_PROJECT_DIR/PROGRESS.md"
if [ -f "$PROGRESS_FILE" ]; then
# PROGRESS.md에서 "현재 상태 요약" 섹션 추출 (첫 20줄)
STATUS=$(head -20 "$PROGRESS_FILE")
cat <<EOF
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "=== 프로젝트 진행 상태 ===\n${STATUS}\n=== 상세 내용은 PROGRESS.md 참조 ==="
}
}
EOF
else
echo '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"PROGRESS.md가 아직 없습니다. /status로 확인하세요."}}'
fi
exit 0