From 4c5f81c87ecdfa861cfb6384d150ae9fb35b2a34 Mon Sep 17 00:00:00 2001 From: minsung Date: Thu, 9 Apr 2026 15:29:06 +0900 Subject: [PATCH] chore: remove historyhooks scaffolding, update .claude config + token-usage hook Co-Authored-By: Claude Sonnet 4.6 --- .claude/hooks/aptabase.json | 2 +- .claude/hooks/token-usage/.gitignore | 1 + .claude/settings.json | 86 ++++++++++++++++++++++++++ .gitignore | 1 + historyhooks/auto-lint.sh | 36 ----------- historyhooks/guard-history-fields.py | 80 ------------------------ historyhooks/guard-history-fields.sh | 8 --- historyhooks/guard-history-reminder.sh | 38 ------------ historyhooks/path.json | 4 -- historyhooks/progress-reminder.sh | 23 ------- historyhooks/protect-files.sh | 45 -------------- historyhooks/session-context.sh | 29 --------- historyhooks/session-start.sh | 25 -------- 13 files changed, 89 insertions(+), 289 deletions(-) create mode 100644 .claude/hooks/token-usage/.gitignore delete mode 100644 historyhooks/auto-lint.sh delete mode 100644 historyhooks/guard-history-fields.py delete mode 100644 historyhooks/guard-history-fields.sh delete mode 100644 historyhooks/guard-history-reminder.sh delete mode 100644 historyhooks/path.json delete mode 100644 historyhooks/progress-reminder.sh delete mode 100644 historyhooks/protect-files.sh delete mode 100644 historyhooks/session-context.sh delete mode 100644 historyhooks/session-start.sh diff --git a/.claude/hooks/aptabase.json b/.claude/hooks/aptabase.json index 96511d1..b3d2527 100644 --- a/.claude/hooks/aptabase.json +++ b/.claude/hooks/aptabase.json @@ -1,6 +1,6 @@ { "enabled": true, - "app_key": "A-SH-7756143445", + "app_key": "A-SH-1673443719", "aptabase_host": "https://aptabase.hmac.kr", "user_name": "김민성(b16213)", "git_repositories": [ diff --git a/.claude/hooks/token-usage/.gitignore b/.claude/hooks/token-usage/.gitignore new file mode 100644 index 0000000..bb68236 --- /dev/null +++ b/.claude/hooks/token-usage/.gitignore @@ -0,0 +1 @@ +aptabase.json diff --git a/.claude/settings.json b/.claude/settings.json index e83cb77..a4e753a 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -60,6 +60,33 @@ "command": "bash .claude/hooks/aptabase-accumulate.sh" } ] + }, + { + "hooks": [ + { + "type": "command", + "command": "t=$(mktemp);cat>\"$t\";e=./.claude/hooks/token-usage/claude-hook.exe;[ -x \"$e\" ] && \"$e\" stop-record \"$t\";rm -f \"$t\"", + "timeout": 5 + } + ] + }, + { + "hooks": [ + { + "type": "command", + "command": "t=$(mktemp);cat>\"$t\";e=./.claude/hooks/token-usage/claude-hook.exe;[ -x \"$e\" ] && \"$e\" stop-record \"$t\";rm -f \"$t\"", + "timeout": 5 + } + ] + }, + { + "hooks": [ + { + "type": "command", + "command": "t=$(mktemp);cat>\"$t\";e=./.claude/hooks/token-usage/claude-hook.exe;[ -x \"$e\" ] && \"$e\" stop-record \"$t\";rm -f \"$t\"", + "timeout": 5 + } + ] } ], "PostToolUse": [ @@ -71,6 +98,65 @@ "command": "bash .claude/hooks/aptabase-commit.sh" } ] + }, + { + "hooks": [ + { + "type": "command", + "command": "t=$(mktemp);cat>\"$t\";e=./.claude/hooks/token-usage/claude-hook.exe;[ -x \"$e\" ] && \"$e\" aptabase-commit \"$t\";rm -f \"$t\"", + "timeout": 15 + } + ], + "matcher": "Bash" + }, + { + "hooks": [ + { + "type": "command", + "command": "t=$(mktemp);cat>\"$t\";e=./.claude/hooks/token-usage/claude-hook.exe;[ -x \"$e\" ] && \"$e\" aptabase-commit \"$t\";rm -f \"$t\"", + "timeout": 15 + } + ], + "matcher": "Bash" + }, + { + "hooks": [ + { + "type": "command", + "command": "t=$(mktemp);cat>\"$t\";e=./.claude/hooks/token-usage/claude-hook.exe;[ -x \"$e\" ] && \"$e\" aptabase-commit \"$t\";rm -f \"$t\"", + "timeout": 15 + } + ], + "matcher": "Bash" + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "t=$(mktemp);cat>\"$t\";e=./.claude/hooks/token-usage/claude-hook.exe;[ -x \"$e\" ] && \"$e\" session-context \"$t\";rm -f \"$t\"", + "timeout": 5 + } + ] + }, + { + "hooks": [ + { + "type": "command", + "command": "t=$(mktemp);cat>\"$t\";e=./.claude/hooks/token-usage/claude-hook.exe;[ -x \"$e\" ] && \"$e\" session-context \"$t\";rm -f \"$t\"", + "timeout": 5 + } + ] + }, + { + "hooks": [ + { + "type": "command", + "command": "t=$(mktemp);cat>\"$t\";e=./.claude/hooks/token-usage/claude-hook.exe;[ -x \"$e\" ] && \"$e\" session-context \"$t\";rm -f \"$t\"", + "timeout": 5 + } + ] } ] } diff --git a/.gitignore b/.gitignore index 825e3f7..53b6446 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ baselines/**/*.received.* # Local smoke test output artifacts/ scenarios/ +.usage/ diff --git a/historyhooks/auto-lint.sh b/historyhooks/auto-lint.sh deleted file mode 100644 index ff98ba5..0000000 --- a/historyhooks/auto-lint.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/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 diff --git a/historyhooks/guard-history-fields.py b/historyhooks/guard-history-fields.py deleted file mode 100644 index 6ffd553..0000000 --- a/historyhooks/guard-history-fields.py +++ /dev/null @@ -1,80 +0,0 @@ -# -*- 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) diff --git a/historyhooks/guard-history-fields.sh b/historyhooks/guard-history-fields.sh deleted file mode 100644 index 756e175..0000000 --- a/historyhooks/guard-history-fields.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/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" diff --git a/historyhooks/guard-history-reminder.sh b/historyhooks/guard-history-reminder.sh deleted file mode 100644 index d0814d7..0000000 --- a/historyhooks/guard-history-reminder.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/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 <&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 diff --git a/historyhooks/session-context.sh b/historyhooks/session-context.sh deleted file mode 100644 index 3f4b1bb..0000000 --- a/historyhooks/session-context.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/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 사용량** (누락 시 저장 차단됨)" diff --git a/historyhooks/session-start.sh b/historyhooks/session-start.sh deleted file mode 100644 index 484bdbb..0000000 --- a/historyhooks/session-start.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/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 <