Free

ย้าย session recording ของ Claude Code ไปยังโปรเจกต์ที่สอง

ระบบบันทึกของ how2claude ไปโปรเจกต์ Rails อื่น: 4 ไฟล์ 5 ขั้น การแบ่งชั้น hook


how2claude มีชุด hooks ที่บันทึก session ของ Claude Code อัตโนมัติ — เริ่มบันทึกเมื่องานเริ่ม เพิ่ม checkpoint ทุกครั้งที่ commit และเมื่อ session จบจะดึง prompts / bash / รายการ edit ไปที่ docs/notes/<feature>/raw.md บทความ let-claude-record-itself เล่าว่าสร้างมันขึ้นมายังไง

ปัญหา: อีกโปรเจกต์ของผม (smarts เว็บเอกสารสำหรับ smart contract) ไม่มีของพวกนี้เลย ทุกครั้งที่อยากเขียนบทความย้อนหลัง ก็ต้องขุด git log บวกความจำและรู้สึกว่าพลาดของดี บทความนี้เล่าเรื่องการย้ายระบบบันทึกไปที่นั่น — รวม 4 ไฟล์ 5 นาที — แต่ระหว่างทาง โผล่ insight จริงเรื่องการแบ่งชั้น hook: hooks ของ Claude Code กับ hooks ของ git ทำงานกันคนละชั้นโดยสิ้นเชิง และอะไรจะถูกจับเมื่อคุณผสมเครื่องมือ (เช่น Amp + Claude Code) ขึ้นอยู่กับว่าคุณติดตั้งที่ชั้นไหน


4 ไฟล์ 1 ภาพ

ทุกอย่างใน how2claude ที่เกี่ยวกับการบันทึก:

ไฟล์ ชั้น บทบาท
bin/recording-state script Python helper จัดการวงจรชีวิตของ .state.json
bin/extract-session-notes script Python helper อ่าน transcript ของ Claude Code → เขียนลง raw.md
.claude/settings.local.json hook Claude Code PostToolUse / Stop ทริกเกอร์ script ด้านบน
.git/hooks/post-commit hook git ทุก commit เรียก recording-state commit เพื่อ checkpoint
.gitignore ควบคุมเสียงรบกวน กัน docs/notes/ ออกจาก repo (โน้ตเป็นส่วนตัว/ชั่วคราว)

4 ชิ้น 4 ชั้นต่างกัน ความแตกต่างนี้จะกลับมาด้านล่าง

insight หลัก: การแบ่งชั้น hook ตัดสินว่าอะไรถูกจับ

สองระบบ hook วิ่งพร้อมกันแต่ scope ต่างกันมาก:

Hooks ของ Claude Code (นิยามใน .claude/settings.local.json):
- Scope: ยิงในเครื่องมือ Claude Code เท่านั้น
- ทริกเกอร์: PostToolUse / Stop / PreToolUse — event วงจรชีวิต Claude Code
- ข้อมูลที่ได้: ชื่อ tool, args, transcript_path (jsonl เต็มของ session) — สิ่งที่มีแต่ Claude Code รู้

Hooks ของ git (shell script ใต้ .git/hooks/):
- Scope: ยิงทุก event ของ git ไม่สำคัญใครเป็นคนกระตุ้น git
- ทริกเกอร์: post-commit / pre-push / ฯลฯ
- ข้อมูลที่ได้: สิ่งที่ git เองรู้ (sha, author, branch, diff)

ผลลัพธ์จริง: เขียนโค้ด + commit ใน Claude Code ทั้งสองชั้นยิง — ข้อมูล session และข้อมูล commit ลงใน raw.md ทั้งคู่ สลับไป Amp (หรือ Cursor หรือพิมพ์มือ) เพื่อเขียน + commit ยิงแค่ hook ของ git — raw.md ได้โครง commit แต่ไม่มี prompt / bash / รายละเอียด edit ของ session

ไม่ใช่บั๊ก — เป็นข้อจำกัดการออกแบบของแต่ละเครื่องมือ อยากได้รายละเอียดระดับ session ใต้ทุกเครื่องมือ = ต้องติดตั้งชั้น hook ของตัวเองในแต่ละเครื่องมือ ตาข่ายสำรองของ git ให้ "ทำอะไรไป" แต่ไม่ให้ "คิดอะไร พังตรงไหน"

คู่มือการเลือก:
- โครง ไม่ขึ้นกับเครื่องมือ (ข้อมูล commit, การเปลี่ยนโค้ด) → ใส่ใน git hook
- เนื้อ เฉพาะ Claude Code (prompt เต็ม, กระบวนการคิด) → ใส่ใน Claude Code hook
- ทั้งสองอย่าง → ติดตั้งทั้งสองชั้น

ย้ายใน 5 ก้าว

ติดตั้งการตั้งค่าบันทึกแบบเดียวกันใน smarts (/home/bob/Work/smarts, โปรเจกต์ Rails)

1. คัดลอก script Python สองตัว

mkdir -p /home/bob/Work/smarts/bin
cp /home/bob/Work/how2claude/bin/recording-state \
   /home/bob/Work/smarts/bin/recording-state
cp /home/bob/Work/how2claude/bin/extract-session-notes \
   /home/bob/Work/smarts/bin/extract-session-notes
chmod +x /home/bob/Work/smarts/bin/{recording-state,extract-session-notes}

Script ไม่ต้องแก้ — ใช้ตัวแปรสภาพแวดล้อม $CLAUDE_PROJECT_DIR ตัดสินว่าจะเขียนที่ไหน:

def project_dir():
    return os.environ.get("CLAUDE_PROJECT_DIR") or os.getcwd()

def state_path():
    return pathlib.Path(project_dir()) / "docs/notes/.state.json"

นี่คือ นามธรรมหลัก: Claude Code ตั้ง CLAUDE_PROJECT_DIR อัตโนมัติเมื่อยิง hook เราตั้งมันด้วยมือใน git post-commit ทั้งสองฝั่งเคารพตัวแปรสภาพแวดล้อมเดียวกัน และ script ไม่ต้องหาว่า "ฉันอยู่โปรเจกต์ไหน"

2. สร้าง .claude/settings.local.json

การตัดสินใจหนึ่ง: ย้ายแค่ hooks ไม่ย้ายรายการ permissions

settings.local.json ของ how2claude มี permissions.allow กว่า 100 รายการ — ทั้งหมดเฉพาะ how2claude (curl localhost:3000, bin/rails runner, kamal app exec) ไร้ความหมายถ้าลากไป smarts smarts จะสะสม permissions ของตัวเองแบบธรรมชาติขณะใช้

Hooks คือ แพทเทิร์น — เหมือนกันข้ามโปรเจกต์ Permissions คือ สถานะโปรเจกต์ — ต่างกันข้ามโปรเจกต์

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/bin/recording-state maybe-start"
          }
        ]
      },
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/bin/recording-state maybe-stop"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "$CLAUDE_PROJECT_DIR/bin/extract-session-notes" }
        ]
      }
    ]
  }
}

สามจุดทริกเกอร์:
- ไฟล์ถูกแก้ → ลองเริ่มบันทึก (maybe-start เช็คตัวเอง: ถ้าบันทึกอยู่แล้วข้าม, ถ้า tree clean ก็ข้าม)
- คำสั่ง bash รัน → ลองหยุด (maybe-stop เข้มงวด: หยุดเมื่อ "auto-start และกลับมา master และ tree clean" ครบสามข้อพร้อมกัน)
- Session จบ → ดึง transcript ลง raw.md

3. สร้าง .git/hooks/post-commit

3 บรรทัด:

#!/bin/bash
ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0
CLAUDE_PROJECT_DIR="$ROOT" "$ROOT/bin/recording-state" commit || true

การ export CLAUDE_PROJECT_DIR=$ROOT ด้วยมือคือจุดเป๊ะที่โลกของ git กับโลกของ Claude Code ถูกเชื่อมด้วยตัวแปรสภาพแวดล้อมเดียวกัน || true รับประกันว่า hook จะไม่มีวันบล็อก commit

chmod +x /home/bob/Work/smarts/.git/hooks/post-commit

4. เพิ่ม docs/notes/ ใน .gitignore

# Session recording notes (transient, for article material)
docs/notes/

โน้ต ชั่วคราว + ส่วนตัว — ไม่อยากให้ raw.md ถูก commit เข้า PR ไม่อยากให้ .state.json เลอะ git status gitignore ของ how2claude จัดการแบบเดียวกัน

5. Smoke test (พร้อมเซอร์ไพรส์เล็ก ๆ)

ทริกเกอร์ maybe-start ด้วยมือทันทีหลังติดตั้ง:

$ cd /home/bob/Work/smarts && CLAUDE_PROJECT_DIR=$(pwd) ./bin/recording-state maybe-start
[recording] auto-started: contract-to-docs (branch: feat/contract-to-docs)

$ cat docs/notes/.state.json
{
  "feature": "contract-to-docs",
  "started_at": "2026-04-20T17:47:18-04:00",
  "branch": "feat/contract-to-docs",
  "auto_started": true
}

Script จับได้ถูกว่า smarts บังเอิญอยู่ที่ branch feat/contract-to-docs กับ tree dirty — เริ่มบันทึกอัตโนมัติ อนุมานชื่อ feature contract-to-docs จาก branch feat/contract-to-docs ตรรกะใน script:

def branch_to_feature(branch):
    if not branch or branch in ("master", "main"):
        return None
    if "/" in branch:
        return branch.split("/", 1)[1]
    return branch

feat/XX, feature/XX, fix-y โล้น → fix-y, master/main → None (ตกกลับไปชื่อ timestamp session-YYYYMMDD-HHMM)

heuristic โง่เป๊ะในระดับที่พอดี — ชื่อ branch คือ หัวข้องาน ไม่ต้องบังคับคุณตั้งชื่อซ้ำ

เขตแดนการมองเห็นเมื่อผสมกับ Amp

หลังติดตั้งถามตัวเอง: บางครั้งทำงานใน Amp — ส่วนนั้นถูกจับไหม? คำตอบตรงกับเรื่องการแบ่งชั้น hook ที่พยากรณ์ไว้:

สถานการณ์ Hook Claude Code Hook git อะไรลงในโน้ต
ทำงาน Claude Code + commit ✅ ยิง ✅ ยิง รายละเอียด session + โครง commit
ทำงาน Amp + commit ❌ no-op ✅ ยิง โครง commit เท่านั้น
พิมพ์มือ + commit ❌ no-op ✅ ยิง โครง commit เท่านั้น
ทำงาน Claude Code ยังไม่ commit ✅ เริ่มบันทึก รายละเอียด session (รายการ commit รอ commit ถัดไป)

สรุป: พอใช้ถ้า Claude Code เป็นผู้ขับหลัก โครง commit ลงเสมอ รายละเอียด session ลงเฉพาะเส้นทาง Claude Code สำหรับบทความประเภท "ทำอะไรไป" คุณพึ่ง body commit เป็นหลัก prompt / ร่องรอย bash ของ session เป็นโบนัส — มีก็ดีแต่ไม่ต้องมี

ถ้าใช้ Amp หนัก Amp มีกลไก hook เอง (ไม่ได้เจาะรายละเอียด) script ส่งต่อเล็กๆ ที่ทริกเกอร์ recording-state maybe-start/maybe-stop ใช้หลักการเดียวกัน

รายการตรวจ

ย้าย session recording ของ Claude Code ไปโปรเจกต์อื่น — 5 ก้าว:

  1. cp script Python สองตัว ไป bin/ ของโปรเจกต์เป้าหมาย ไม่ต้องแก้ — script เคารพ $CLAUDE_PROJECT_DIR พกพาข้ามโปรเจกต์ได้ตามดีไซน์
  2. สร้าง .claude/settings.local.json hooks เท่านั้น อย่าย้ายรายการ permissions — permissions คือสถานะโปรเจกต์ (ต่างกันต่อโปรเจกต์) hooks คือแพทเทิร์น (เหมือนกันข้ามโปรเจกต์)
  3. สร้าง .git/hooks/post-commit (3 บรรทัด) export CLAUDE_PROJECT_DIR=$ROOT ด้วยมือและเรียก recording-state commit นี่คือจุดเดียวที่โลก git และโลก Claude Code ถูกเชื่อมด้วยตัวแปรสภาพแวดล้อมเดียวกัน
  4. เพิ่ม docs/notes/ ใน .gitignore โน้ตชั่วคราว + ส่วนตัว ไม่ใช่ส่วนของ repo
  5. Smoke test ด้วยมือ: CLAUDE_PROJECT_DIR=$(pwd) ./bin/recording-state maybe-start ตรวจว่าการอนุมาน branch→feature ถูกต้อง ถ้า tree clean script ถูก ออกแบบ ให้ข้าม — ไม่ใช่บั๊ก

การตัดสินใจด้านดีไซน์จริงๆ ไม่ใช่ "ย้ายยังไง" — ย้ายเกือบจะเป็น cp มันคือการ แบ่งตรรกะการบันทึกเป็น 4 ชั้นต่างกัน แต่ละชั้นทำหน้าที่หนึ่งชัดเจน:

  • Script Python: helper ไม่มีสถานะ เคารพ CLAUDE_PROJECT_DIR
  • Hook Claude Code: event ในเครื่องมือ (เนื้อ session)
  • Hook git: event ไม่ขึ้นเครื่องมือ (โครง commit)
  • gitignore: ควบคุมเสียงรบกวน

ทั้ง 4 ชั้นย้าย เปลี่ยน หรือข้ามได้อย่างอิสระ (อยากได้ support Amp? เพิ่มชั้น hook ของ Amp เปลี่ยนรูปแบบโน้ต? แก้ Python ไม่อยากให้ git ติดตามโน้ต? ลบ post-commit) Claude เขียนโค้ดถูกต้องได้ — แต่การตัดสิน "ฟังก์ชันนี้อยู่ชั้นไหน" เขาทำแทนคุณไม่ได้ นั่นเป็นการตัดสินเรื่องขอบเขตเครื่องมือของคุณ และเป็นของคุณ