Free

Memindahkan Session Recording Claude Code ke Proyek Kedua

Sistem rekam how2claude ke proyek Rails lain: 4 file, 5 langkah, hook layering.


how2claude punya satu set hooks yang otomatis ngerekam session Claude Code — mulai rekam pas kerjaan dimulai, nambahin checkpoint di tiap commit, dan pas session selesai, nge-ekstrak prompt / bash / daftar edit ke docs/notes/<feature>/raw.md. Artikel let-claude-record-itself udah bahas gimana dibangunnya.

Masalahnya: proyek gue satunya (smarts, situs docs untuk smart contract) gak punya apa-apa dari ini. Tiap mau nulis artikel belakangan, gue bongkar git log plus ingatan dan ngerasa ada yang kelewat. Artikel ini cerita gimana mindahin sistem rekam ke sana — 4 file, 5 menit total — tapi di jalan muncul satu insight real soal hook layering: hooks Claude Code dan hooks git beroperasi di layer yang sama sekali beda, dan apa yang ketangkep pas lo campur tool (Amp + Claude Code, misalnya) tergantung layer mana lo pasangnya.


4 file, satu gambar

Semua di how2claude yang nyangkut rekaman:

File Layer Peran
bin/recording-state script Python helper, ngelola lifecycle .state.json
bin/extract-session-notes script Python helper, baca transcript Claude Code → nulis raw.md
.claude/settings.local.json hook Claude Code PostToolUse / Stop ngetrigger script di atas
.git/hooks/post-commit hook git tiap commit manggil recording-state commit buat checkpoint
.gitignore kontrol noise jaga docs/notes/ gak masuk repo (catatan itu privat/transient)

4 potongan, 4 layer beda. Pembedaan ini muncul balik terus di bawah.

Insight kunci: hook layering nentuin apa yang ketangkep

Dua sistem hook jalan bareng, tapi scope-nya jauh beda:

Hooks Claude Code (didefinisiin di .claude/settings.local.json):
- Scope: cuma nyala di dalam tool Claude Code
- Trigger: PostToolUse / Stop / PreToolUse — event lifecycle Claude Code
- Info tersedia: nama tool, args, transcript_path (jsonl session lengkap) — barang yang cuma Claude Code tau

Hooks git (script shell di bawah .git/hooks/):
- Scope: nyala di tiap event git, gak peduli siapa yang ngetrigger git-nya
- Trigger: post-commit / pre-push / dll.
- Info tersedia: apa yang git sendiri tau (sha, author, branch, diff)

Konsekuensi nyata: nulis kode + commit di dalam Claude Code ngetrigger dua layer sekaligus — info session dan info commit dua-duanya mendarat di raw.md. Ganti ke Amp (atau Cursor, atau ngetik tangan) buat nulis + commit dan cuma hook git yang nyala — raw.md dapet kerangka commit tapi gak ada detail prompt / bash / edit dari session.

Ini bukan bug — ini constraint desain masing-masing tool. Mau detail level session di bawah semua tool berarti pasang hook layer lo sendiri per tool. Fallback git kasih lo "apa yang dikerjain"; gak kasih lo "apa yang dipikirin, apa yang rusak".

Panduan pemilihan:
- Kerangka tool-agnostic (info commit, perubahan kode) → taruh di hook git
- Daging khas Claude Code (prompt lengkap, proses pikir) → taruh di hook Claude Code
- Dua-duanya → pasang di dua layer

Migrasi dalam 5 langkah

Pasang setup rekam yang sama di smarts (/home/bob/Work/smarts, proyek Rails).

1. Copy dua 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 tanpa modifikasi — pake env var $CLAUDE_PROJECT_DIR buat nentuin nulis ke mana:

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"

Ini abstraksi kunci: Claude Code nge-set CLAUDE_PROJECT_DIR otomatis pas ngetrigger hook; kita nge-set manual di dalam post-commit git. Dua sisi ngormatin env var yang sama, dan script gak perlu ngerti "gue lagi di proyek mana".

2. Bikin .claude/settings.local.json

Satu keputusan di sini: cuma mindahin hooks, bukan daftar permissions.

settings.local.json how2claude punya 100+ entri permissions.allow — semuanya spesifik how2claude (curl localhost:3000, bin/rails runner, kamal app exec). Gak ada artinya bawa ke smarts. smarts bakal nimbun permissions sendiri organik selama dipake.

Hooks itu pola — identik antar proyek. Permissions itu state proyek — beda antar proyek.

{
  "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" }
        ]
      }
    ]
  }
}

Tiga titik trigger:
- File diedit → coba mulai rekam (maybe-start self-check: kalo udah rekam, skip; kalo tree clean, juga skip)
- Command bash dijalanin → coba stop (maybe-stop ketat: cuma stop kalo "auto-started DAN balik ke master DAN tree clean" semua kepenuhan)
- Session selesai → ekstrak transcript ke raw.md

3. Bikin .git/hooks/post-commit

3 baris:

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

Export manual CLAUDE_PROJECT_DIR=$ROOT itu titik persis di mana dunia git dan dunia Claude Code dijembatanin pake env var yang sama. || true jamin hook gak pernah ngeblok commit.

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

4. Tambahin docs/notes/ ke .gitignore

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

Catatan itu transient + privat — lo gak pengen raw.md di-commit ke PR; lo gak pengen .state.json kotorin git status. gitignore how2claude juga begitu.

5. Smoke test (dengan kejutan kecil)

Trigger maybe-start manual langsung abis install:

$ 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 bener-bener ngenalin smarts kebetulan di branch feat/contract-to-docs dengan tree dirty — auto-mulai rekam, nyimpulin nama feature contract-to-docs dari branch feat/contract-to-docs. Logika itu di 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 polos → fix-y, master/main → None (yang fallback ke nama timestamp session-YYYYMMDD-HHMM).

Heuristik-nya sengaja dudul-dudul pas — nama branch adalah subjek kerjaan, gak perlu lo kasih nama lagi.

Batas visibilitas saat nyampur sama Amp

Abis pasang gue nanya diri: kadang-kadang gue kerja di Amp — itu ketangkep? Jawabannya persis kayak prediksi cerita hook layering:

Skenario Hook Claude Code Hook git Apa yang masuk catatan
Kerja Claude Code + commit ✅ nyala ✅ nyala detail session + kerangka commit
Kerja Amp + commit ❌ no-op ✅ nyala cuma kerangka commit
Ngetik tangan + commit ❌ no-op ✅ nyala cuma kerangka commit
Kerja Claude Code, belum commit ✅ mulai rekam detail session (entry commit nunggu commit berikutnya)

Intinya: cukup kalo Claude Code jadi driver utama. Kerangka commit selalu masuk; detail session cuma masuk di jalur Claude Code. Buat artikel "apa yang dikerjain" lo utamanya sandarin ke body commit — prompt / jejak bash session itu bonus, enak kalo ada tapi gak wajib.

Kalo lo pake Amp berat, Amp punya sistem hook sendiri (gak gue dalemin); script forwarding kecil yang ngetrigger recording-state maybe-start/maybe-stop bakal jalan sama.

Checklist

Mindahin session recording Claude Code ke proyek lain — 5 langkah:

  1. cp dua script Python ke bin/ proyek target. Tanpa modifikasi — script ngormatin $CLAUDE_PROJECT_DIR, portable antar proyek by design.
  2. Bikin .claude/settings.local.json, cuma hooks. Jangan pindahin daftar permissions — permissions itu state proyek (beda per proyek); hooks itu pola (identik antar proyek).
  3. Bikin .git/hooks/post-commit (3 baris), export CLAUDE_PROJECT_DIR=$ROOT manual dan panggil recording-state commit. Itu satu-satunya titik di mana dunia git dan dunia Claude Code dijembatanin pake env var yang sama.
  4. Tambahin docs/notes/ ke .gitignore. Catatan itu transient + privat, bukan bagian dari repo.
  5. Smoke test manual: CLAUDE_PROJECT_DIR=$(pwd) ./bin/recording-state maybe-start, cek inferensi branch→feature bener gak. Kalo tree clean, script didesain buat skip — bukan bug.

Keputusan desain beneran bukan "gimana gue mindahin" — mindahin hampir tinggal cp. Tapi bagi logika rekam ke 4 layer beda, masing-masing ngerjain satu hal yang jelas:

  • Script Python: helper stateless, ngormatin CLAUDE_PROJECT_DIR
  • Hook Claude Code: event dalam-tool (daging session)
  • Hook git: event tool-agnostic (kerangka commit)
  • gitignore: kontrol noise

Masing-masing dari 4 layer bisa dipindahin, diganti, atau dilewatin secara independen (mau dukungan Amp? tambahin layer hook Amp. Ubah format catatan? edit Python. Gak mau git ngelacak catatan? hapus post-commit). Claude bisa nulis kode bener — tapi pertimbangan "fungsi ini masuk layer mana" gak bisa dia buatin buat lo. Itu pertimbangan soal batas tooling lo, dan itu lo punya.