#!/bin/bash # SessionStart hook — 세션 시작 교훈 환기 체계 6계층 (BT4, 2026-04-23 PD 승인) # 구 버전(7일 고정 10건 주입)에서 확장 — PD 지시 "1일 우선 + 필요 시 확장" # 관련 규칙: C31-G 자기검증 · C33 조직 공유·기록 체계 · 헌법 원칙 ⑤ # # 6계층 구조 # 계층 0: 고정 주입 (공백 무관) — 헌법급 feedback 9종(tier: constitutional 자동 선별) · 활성 PD 지시 로그 · 완료 아카이브 최근 5건 · 최근 7일 기각안 · project_context_조직운영 20개 # 계층 1: 공백 ≤1일 → 1일 윈도우 (feedback·대화로그) # 계층 2: 공백 2~7일 → 공백일수 자동 확장 # 계층 3: 공백 >7일 → 전수 탐색 모드 (개수 기반 feedback 10건·대화로그 프로젝트당 3건) # 계층 4: --extend=N 수동 확장 (환경변수 SESSION_RESTORE_EXTEND) # 계층 5: 내용축 트리거 (인프라 commit·외부 레포·PD 키워드·기획 경로·순수 코드 연쇄) → 7일 max # # 모니터링: feedback_session_restore_monitoring.md (pm-auditor 운영 데이터 축적 SOT) REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) [ -z "$REPO_ROOT" ] && exit 0 MEMORY_DIR="$REPO_ROOT/memory/org" DIALOG_DIR="$REPO_ROOT/공유/대화로그" PD_LOG_DIR="$REPO_ROOT/공유/PD_지시_트래킹" [ ! -d "$MEMORY_DIR" ] && exit 0 NOW=$(date +%s) # ───────────────────────────────────────────────────────────────────── # 계층 0: 고정 주입 (공백 무관) # ───────────────────────────────────────────────────────────────────── echo "🏛️ [계층 0 · 고정 주입] 헌법급 feedback · 활성 PD 지시 · 기각안 · project_context" echo "" # 0-A. 헌법급 feedback (frontmatter tier: constitutional 자동 선별) CONST_FILES=$(grep -l "^tier: constitutional" "$MEMORY_DIR"/feedback_*.md 2>/dev/null | sort) if [ -n "$CONST_FILES" ]; then CONST_COUNT=$(echo "$CONST_FILES" | wc -l) echo " 📌 헌법급 feedback $CONST_COUNT 건:" while IFS= read -r file; do [ -f "$file" ] || continue name=$(basename "$file" .md) desc=$(awk '/^description:/ {sub(/^description: /, ""); print; exit}' "$file" 2>/dev/null | cut -c 1-100) echo " • $name" [ -n "$desc" ] && echo " → $desc" done <<< "$CONST_FILES" fi # 0-B. 활성 PD 지시 (개발팀·기획팀 활성 테이블) echo "" echo " 📋 활성 PD 지시:" ACTIVE_LINES=$(grep -h "^| [A-Z0-9]" "$PD_LOG_DIR"/개발팀_PD_지시_로그.md "$PD_LOG_DIR"/기획팀_PD_지시_로그.md 2>/dev/null | grep -E "진행중|대기|보류" | head -10) if [ -n "$ACTIVE_LINES" ]; then echo "$ACTIVE_LINES" | while IFS= read -r line; do id=$(echo "$line" | awk -F'|' '{print $2}' | xargs) brief=$(echo "$line" | awk -F'|' '{print $4}' | cut -c 1-90) status=$(echo "$line" | awk -F'|' '{print $5}' | xargs) echo " • [$id · $status] $brief" done else echo " (활성 지시 없음)" fi # 0-C. 완료 아카이브 최근 5건 echo "" echo " ✅ PD 지시 완료 아카이브 최근 5건:" COMPLETED=$(grep -h "^| [A-Z0-9].*완료" "$PD_LOG_DIR"/개발팀_PD_지시_로그.md "$PD_LOG_DIR"/기획팀_PD_지시_로그.md 2>/dev/null | tail -5) if [ -n "$COMPLETED" ]; then echo "$COMPLETED" | while IFS= read -r line; do id=$(echo "$line" | awk -F'|' '{print $2}' | xargs) brief=$(echo "$line" | awk -F'|' '{print $4}' | cut -c 1-90) echo " • [$id] $brief" done else echo " (완료 아카이브 없음)" fi # 0-D. 최근 7일 기각안 엔트리 (대화로그 "기각안" grep) echo "" echo " 🚫 최근 7일 기각안 요지:" REJECTED=$(find "$DIALOG_DIR" -name "*.md" -mtime -7 -exec grep -l "기각안" {} \; 2>/dev/null | head -5) if [ -n "$REJECTED" ]; then echo "$REJECTED" | while IFS= read -r file; do rel=${file#$REPO_ROOT/} count=$(grep -c "^### 기각" "$file" 2>/dev/null || echo 0) [ "$count" -gt 0 ] && echo " • $rel — $count 건" done else echo " (최근 7일 기각안 엔트리 없음)" fi # 0-E. project_context 활성 엔트리 요지 (최신 5개만 미리보기) echo "" echo " 📚 project_context_조직운영 (최신 5개):" CTX_FILE="$MEMORY_DIR/project_context_조직운영.md" if [ -f "$CTX_FILE" ]; then grep "^- \[" "$CTX_FILE" | head -5 | while IFS= read -r line; do echo " $line" done else echo " (파일 없음)" fi echo "" # ───────────────────────────────────────────────────────────────────── # 활동 시각 실측 + 공백일수 산정 # ───────────────────────────────────────────────────────────────────── # 마지막 commit 시각 LAST_COMMIT=$(git log -1 --format=%ct 2>/dev/null || echo 0) # 최신 feedback mtime LAST_FB=0 if [ -d "$MEMORY_DIR" ]; then LAST_FB=$(find "$MEMORY_DIR" -name "feedback_*.md" -printf '%T@\n' 2>/dev/null | sort -n | tail -1 | cut -d. -f1) [ -z "$LAST_FB" ] && LAST_FB=0 fi # 최신 대화로그 mtime LAST_DL=0 if [ -d "$DIALOG_DIR" ]; then LAST_DL=$(find "$DIALOG_DIR" -name "*.md" -printf '%T@\n' 2>/dev/null | sort -n | tail -1 | cut -d. -f1) [ -z "$LAST_DL" ] && LAST_DL=0 fi # 최대값 LAST_ACTIVITY=$LAST_COMMIT [ "$LAST_FB" -gt "$LAST_ACTIVITY" ] && LAST_ACTIVITY=$LAST_FB [ "$LAST_DL" -gt "$LAST_ACTIVITY" ] && LAST_ACTIVITY=$LAST_DL GAP_SEC=$((NOW - LAST_ACTIVITY)) GAP_DAYS=$((GAP_SEC / 86400)) # ───────────────────────────────────────────────────────────────────── # 계층 1~4: 공백일수 기반 동적 윈도우 # ───────────────────────────────────────────────────────────────────── # 계층 5 내용축 트리거 감지 (공백 기반 윈도우와 max 채택) TRIGGER_WINDOW=0 TRIGGER_REASONS="" # 인프라 commit 감지 (당일 HEAD 커밋 기준) INFRA_CHANGED=$(git log -1 --name-only --format='' 2>/dev/null | grep -E '(\.claude/skills/BurningTimes-코어룰/SKILL\.md|memory/org/feedback_|scripts/.*(hook|ensure|inject|gate|register|archive|sync)|setup/setup_|\.claude/settings|CLAUDE\.md|paths\.local\.json)' | head -1) if [ -n "$INFRA_CHANGED" ]; then TRIGGER_WINDOW=7 TRIGGER_REASONS="$TRIGGER_REASONS · 인프라 commit 감지" fi # 활성 PD 지시 로그 파일 변경 감지 (최근 1시간) PD_LOG_RECENT=$(find "$PD_LOG_DIR" -name "*_로그.md" -mmin -60 2>/dev/null | head -1) if [ -n "$PD_LOG_RECENT" ]; then [ "$TRIGGER_WINDOW" -lt 7 ] && TRIGGER_WINDOW=7 TRIGGER_REASONS="$TRIGGER_REASONS · PD 지시 상태 변경" fi # 기획 경로 수정 감지 (당일 commit 기준) PLAN_CHANGED=$(git log -1 --name-only --format='' 2>/dev/null | grep -E '프로젝트/.*/기획/' | head -1) if [ -n "$PLAN_CHANGED" ]; then [ "$TRIGGER_WINDOW" -lt 7 ] && TRIGGER_WINDOW=7 TRIGGER_REASONS="$TRIGGER_REASONS · 기획 경로 수정" fi # 시간축 윈도우 결정 if [ -n "$SESSION_RESTORE_EXTEND" ] && [ "$SESSION_RESTORE_EXTEND" -gt 0 ] 2>/dev/null; then WINDOW=$SESSION_RESTORE_EXTEND LAYER_TAG="계층 4 · 수동 --extend=$SESSION_RESTORE_EXTEND" elif [ "$GAP_DAYS" -le 1 ]; then WINDOW=1 LAYER_TAG="계층 1 · 세션 연속 (공백 ≤1일)" elif [ "$GAP_DAYS" -le 7 ]; then WINDOW=$GAP_DAYS LAYER_TAG="계층 2 · 단기 공백 ($GAP_DAYS일 자동 확장)" else WINDOW=0 LAYER_TAG="계층 3 · 장기 공백 (${GAP_DAYS}일) → 전수 탐색 모드" fi # 계층 5 max 채택 if [ "$TRIGGER_WINDOW" -gt "$WINDOW" ]; then WINDOW=$TRIGGER_WINDOW LAYER_TAG="$LAYER_TAG + 계층 5 ($TRIGGER_WINDOW일$TRIGGER_REASONS)" fi echo "⏱️ [활동 시각] 마지막 활동 ${GAP_DAYS}일 전 · $LAYER_TAG" echo "" # ───────────────────────────────────────────────────────────────────── # feedback·대화로그 주입 # ───────────────────────────────────────────────────────────────────── if [ "$WINDOW" -eq 0 ]; then # 계층 3 전수 탐색 모드 — 개수 기반 echo "🔭 [계층 3 · 전수 탐색] 최근 활동 지점부터 역순 복원:" echo "" echo " 📘 feedback 최신 10건:" find "$MEMORY_DIR" -name "feedback_*.md" -type f -printf '%T@ %p\n' 2>/dev/null | sort -rn | head -10 | awk '{print $2}' | while IFS= read -r file; do name=$(basename "$file" .md) desc=$(awk '/^description:/ {sub(/^description: /, ""); print; exit}' "$file" 2>/dev/null | cut -c 1-100) echo " • $name" [ -n "$desc" ] && echo " → $desc" done echo "" echo " 📝 대화로그 프로젝트당 최신 3건:" for proj_dir in "$DIALOG_DIR"/*/; do [ -d "$proj_dir" ] || continue proj=$(basename "$proj_dir") find "$proj_dir" -name "*.md" -type f -printf '%T@ %p\n' 2>/dev/null | sort -rn | head -3 | awk '{print $2}' | while IFS= read -r file; do rel=${file#$REPO_ROOT/} echo " • $rel" done done else # 계층 1·2·4·5 — 기간 기반 echo "📘 [feedback $WINDOW일 윈도우]:" RECENT_FB=$(find "$MEMORY_DIR" -name "feedback_*.md" -mtime -${WINDOW} -type f 2>/dev/null | head -15) if [ -n "$RECENT_FB" ]; then FB_COUNT=$(echo "$RECENT_FB" | wc -l) echo " ($FB_COUNT 건)" echo "$RECENT_FB" | while IFS= read -r file; do name=$(basename "$file" .md) desc=$(awk '/^description:/ {sub(/^description: /, ""); print; exit}' "$file" 2>/dev/null | cut -c 1-100) echo " • $name" [ -n "$desc" ] && echo " → $desc" done else echo " (해당 윈도우 내 feedback 없음)" fi fi echo "" echo "📖 [본문 Read 의무 — C31-G] 의사결정 전 관련 메모리 본문 선행 Read 필수" echo " PD님 지시·지적 키워드 매칭 메모리·헌법급 feedback은 계층 0에서 이미 환기됨" echo " 상세: memory/org/feedback_session_restore_monitoring.md (6계층 운영 모니터링 SOT)" exit 0