From 5b6cfe334260a087b8aea1231cd14f63be207c83 Mon Sep 17 00:00:00 2001 From: swrring Date: Thu, 16 Apr 2026 20:50:14 +0900 Subject: [PATCH] =?UTF-8?q?feat(live):=20P25=20Live=20=EC=A6=9D=EB=B6=84?= =?UTF-8?q?=20=EB=8F=99=EA=B8=B0=ED=99=94=20=EC=B2=B4=EA=B3=84=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(PD=EB=8B=98=20=EC=A7=81=EC=A0=91=20=EC=A7=80?= =?UTF-8?q?=EC=8B=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 세션 중 반영 불가 파일 9종의 변경을 즉시 반영하는 체계: - .claude/live/ 파일별 더미 + 증분 읽기 - live_inject.sh: UserPromptSubmit hook (추가분만 주입, 변경 없으면 토큰 0) - live_session_load.sh: SessionStart hook (전량 1회 로드) - P25 코어룰 명문화 + P21-2 세션 공유에 Live 비우기 편입 - settings.json hook 등록 Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/settings.json | 8 ++++ .claude/skills/너드나비스-코어룰/SKILL.md | 47 +++++++++++++++++-- scripts/live_inject.sh | 56 +++++++++++++++++++++++ scripts/live_session_load.sh | 47 +++++++++++++++++++ 4 files changed, 154 insertions(+), 4 deletions(-) create mode 100644 scripts/live_inject.sh create mode 100644 scripts/live_session_load.sh diff --git a/.claude/settings.json b/.claude/settings.json index e8fc4e0..82f97ce 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -57,6 +57,10 @@ { "type": "command", "command": "bash scripts/change_digest.sh 2>/dev/null || true" + }, + { + "type": "command", + "command": "bash scripts/live_session_load.sh 2>/dev/null || true" } ] } @@ -72,6 +76,10 @@ { "type": "command", "command": "bash scripts/hold_watch.sh 2>/dev/null || true" + }, + { + "type": "command", + "command": "bash scripts/live_inject.sh 2>/dev/null || true" } ] } diff --git a/.claude/skills/너드나비스-코어룰/SKILL.md b/.claude/skills/너드나비스-코어룰/SKILL.md index 66d8552..88034e6 100644 --- a/.claude/skills/너드나비스-코어룰/SKILL.md +++ b/.claude/skills/너드나비스-코어룰/SKILL.md @@ -733,10 +733,12 @@ PD님이 **"세션 갱신"**이라고 지시하면, PM 단일 세션 에이전 PD님이 **"세션 공유"**라고 지시하면, 현재 세션의 모든 변경사항을 **즉시 git commit + push**하여 다른 세션에서 접근 가능하게 만든다. PD님에게 추가 확인을 요청하지 않는다. ### 수행 절차 -1. `git add -A` -2. `git commit` (변경 내용 요약 메시지 자동 생성) -3. `git push origin main` -4. 완료 보고 (1줄) +1. `.claude/live/` 더미 파일 내용을 원본에 반영 (아직 미반영분이 있다면) +2. `.claude/live/` 더미 파일 비우기 (README.md 제외) +3. `git add -A` +4. `git commit` (변경 내용 요약 메시지 자동 생성) +5. `git push origin main` +6. 완료 보고 (1줄) ### 트리거 표현 - "세션 공유" @@ -877,6 +879,43 @@ grep -r "기각안" 공유/대화로그/ # 기각 이유 추적 | **P24 (대화로그)** | "논의 맥락·경위·기각 이유" | 상위 근거 | | ~~P20 (일일보고)~~ | 폐기 — P24로 대체 | - | +## P25. Live 증분 동기화 체계 (2026-04-16 PD님 직접 지시) + +> 세션 시작 후 변경된 설정·규칙·에이전트 정의를 **세션 갱신 없이 즉시 반영**하기 위한 임시 더미 파일 체계. 원본 수정 + 더미 기록의 이중 반영으로, 현재 세션은 hook이 즉시 주입하고 다음 세션은 원본이 이미 최신. + +### 대상 (세션 중 반영 불가 9종) +CLAUDE.md, CLAUDE.local.md, .claude/settings.json, settings.local.json, .claude/skills/*/SKILL.md, .claude/agents/*.md, .claude/rules/*.md, .claude/commands/*.md, .mcp.json + +### 더미 파일 위치 +`.claude/live/` — 원본과 동일 파일명으로 **변경분(diff)만** 기록. + +### 변경 발생 시 절차 +1. **원본 파일 즉시 수정** (다음 세션·다른 PC를 위해) +2. **`.claude/live/{파일명}`에 변경 요지 append** (현재 세션 즉시 반영용) +3. UserPromptSubmit hook(`live_inject.sh`)이 증분 감지 → 추가분만 컨텍스트 주입 + +### 증분 읽기 원리 +- hook이 파일별 "마지막 읽은 줄 번호" 기록 +- 다음 턴에 추가된 줄만 stdout 출력 → 에이전트 컨텍스트 주입 +- 변경 없으면 출력 없음 (토큰 비용 0) + +### 서브에이전트 의무 +모든 에이전트는 작업 착수 전 `.claude/live/` 디렉토리에 더미 파일이 존재하는지 확인하고, 존재하면 Read하여 변경사항을 인지한다. + +### Write 권한 +- **PM만 Write** (서브에이전트는 Read 전용) +- 각 더미 파일 최대 8,000자 + +### "세션 공유" 시 동기화 (P21-2 연계) +1. 더미 내용이 원본에 이미 반영되어 있는지 확인 +2. `.claude/live/` 더미 파일 비우기 (README.md 제외) +3. commit + push + +### C14 준수 +- 변경 없는 턴: 토큰 비용 0 (해시 비교만) +- 변경 감지 턴: 추가분만 주입 (전체 재읽기 없음) +- 세션 시작 시: SessionStart hook이 전량 1회 로드 + --- ## 교훈 및 노하우 diff --git a/scripts/live_inject.sh b/scripts/live_inject.sh new file mode 100644 index 0000000..b40a2fc --- /dev/null +++ b/scripts/live_inject.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# UserPromptSubmit hook — .claude/live/ 증분 읽기 + 컨텍스트 주입 +# 세션 중 반영 불가 파일의 변경분을 실시간 주입 +# 증분 방식: 마지막 읽은 줄 이후 추가분만 출력 + +REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) +[ -z "$REPO_ROOT" ] && exit 0 + +LIVE_DIR="$REPO_ROOT/.claude/live" +[ ! -d "$LIVE_DIR" ] && exit 0 + +THROTTLE_DIR="$HOME/.claude/.nerdnavis_throttle" +mkdir -p "$THROTTLE_DIR" 2>/dev/null + +OUTPUT="" +TOTAL_CHARS=0 +MAX_CHARS=8000 # 10,000자 한도에서 기존 hook 여유분 확보 + +for LIVE_FILE in "$LIVE_DIR"/*.md "$LIVE_DIR"/*.json; do + [ ! -f "$LIVE_FILE" ] && continue + BASENAME=$(basename "$LIVE_FILE") + [ "$BASENAME" = "README.md" ] && continue # README는 스킵 + + # 파일별 마지막 읽은 줄 번호 추적 + FILE_HASH=$(echo "$LIVE_FILE" | sha1sum 2>/dev/null | cut -d' ' -f1) + LAST_LINE_FILE="$THROTTLE_DIR/live_lastline_$FILE_HASH" + LAST_LINE=$(cat "$LAST_LINE_FILE" 2>/dev/null || echo 0) + TOTAL_LINES=$(wc -l < "$LIVE_FILE" 2>/dev/null || echo 0) + + # 새 줄이 추가된 경우에만 출력 + if [ "$TOTAL_LINES" -gt "$LAST_LINE" ]; then + NEW_CONTENT=$(tail -n +$((LAST_LINE + 1)) "$LIVE_FILE" 2>/dev/null) + NEW_CHARS=${#NEW_CONTENT} + + # 한도 체크 + if [ $((TOTAL_CHARS + NEW_CHARS)) -lt $MAX_CHARS ]; then + OUTPUT="$OUTPUT +[LIVE:$BASENAME] 변경분 (${LAST_LINE}줄 이후): +$NEW_CONTENT +" + TOTAL_CHARS=$((TOTAL_CHARS + NEW_CHARS + 50)) # 헤더 여유 + echo "$TOTAL_LINES" > "$LAST_LINE_FILE" + else + OUTPUT="$OUTPUT +[LIVE:$BASENAME] ⚠️ 증분 크기 초과 — Read 도구로 직접 확인 필요 +" + fi + fi +done + +# 출력이 있을 때만 주입 +if [ -n "$OUTPUT" ] && [ "$TOTAL_CHARS" -gt 0 ]; then + echo "📝 [Live 증분 동기화]$OUTPUT" +fi + +exit 0 diff --git a/scripts/live_session_load.sh b/scripts/live_session_load.sh new file mode 100644 index 0000000..2f6a953 --- /dev/null +++ b/scripts/live_session_load.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# SessionStart hook — .claude/live/ 전량 로드 (세션 시작 시) +# 세션 시작 시점에 모든 live 더미 파일을 읽어서 컨텍스트에 주입 +# 증분 카운터도 리셋하여 다음 턴부터 증분 추적 시작 + +REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) +[ -z "$REPO_ROOT" ] && exit 0 + +LIVE_DIR="$REPO_ROOT/.claude/live" +[ ! -d "$LIVE_DIR" ] && exit 0 + +THROTTLE_DIR="$HOME/.claude/.nerdnavis_throttle" +mkdir -p "$THROTTLE_DIR" 2>/dev/null + +OUTPUT="" +TOTAL_CHARS=0 +MAX_CHARS=8000 +FILE_COUNT=0 + +for LIVE_FILE in "$LIVE_DIR"/*.md "$LIVE_DIR"/*.json; do + [ ! -f "$LIVE_FILE" ] && continue + BASENAME=$(basename "$LIVE_FILE") + [ "$BASENAME" = "README.md" ] && continue + + CONTENT=$(head -c $MAX_CHARS "$LIVE_FILE" 2>/dev/null) + CHARS=${#CONTENT} + + if [ "$CHARS" -gt 0 ] && [ $((TOTAL_CHARS + CHARS)) -lt $MAX_CHARS ]; then + OUTPUT="$OUTPUT +[LIVE:$BASENAME] +$CONTENT +" + TOTAL_CHARS=$((TOTAL_CHARS + CHARS + 30)) + FILE_COUNT=$((FILE_COUNT + 1)) + + # 증분 카운터를 현재 줄 수로 설정 (다음 턴부터 증분 추적) + FILE_HASH=$(echo "$LIVE_FILE" | sha1sum 2>/dev/null | cut -d' ' -f1) + TOTAL_LINES=$(wc -l < "$LIVE_FILE" 2>/dev/null || echo 0) + echo "$TOTAL_LINES" > "$THROTTLE_DIR/live_lastline_$FILE_HASH" + fi +done + +if [ "$FILE_COUNT" -gt 0 ]; then + echo "📝 [Live 세션 로드] ${FILE_COUNT}개 더미 파일:$OUTPUT" +fi + +exit 0