Free

Claude Code session recording'i ikinci bir projeye taşımak

how2claude kayıt sistemi başka bir Rails projesine: 4 dosya, 5 adım, hook katmanlaması.


how2claude'da Claude Code session'larını otomatik kaydeden bir hooks seti var — iş başladığında kayda alır, her commit'te checkpoint ekler ve session bittiğinde prompt / bash / edit listesini docs/notes/<feature>/raw.md'ye çıkarır. let-claude-record-itself yazısı nasıl kurulduğunu anlatmıştı.

Problem: diğer projemde (smarts, akıllı sözleşme docs sitesi) bunların hiçbiri yoktu. Sonradan yazı yazmak istediğimde git log'u ve hafızamı karıştırıyor, iyi kısımları kaçırıyordum hep. Bu yazı kayıt sistemini oraya taşımayı anlatıyor — toplam 4 dosya, 5 dakika — ama yolda hook katmanlaması hakkında gerçek bir iç görü ortaya çıktı: Claude Code hooks'ları ile git hooks'ları tamamen farklı katmanlarda çalışıyor ve araçları karıştırdığınızda (örneğin Amp + Claude Code) neyin yakalandığı, hangi katmana kurduğunuza bağlı.


4 dosya, tek bir resim

how2claude'da kayıtla ilgili her şey:

Dosya Katman Rol
bin/recording-state script Python helper, .state.json yaşam döngüsünü yönetir
bin/extract-session-notes script Python helper, Claude Code transcript'ini okur → raw.md'ye yazar
.claude/settings.local.json Claude Code hook PostToolUse / Stop yukarıdaki script'leri tetikler
.git/hooks/post-commit git hook her commit recording-state commit çağırıp checkpoint basar
.gitignore gürültü kontrolü docs/notes/'u repo dışında tutar (notlar özel/geçici)

4 parça, 4 farklı katman. Bu ayrım aşağıda tekrar tekrar karşımıza çıkacak.

Kilit iç görü: hook katmanlaması neyin yakalandığını belirler

İki hook sistemi aynı anda çalışıyor ama kapsamları çok farklı:

Claude Code hooks (.claude/settings.local.json'da tanımlı):
- Kapsam: yalnızca Claude Code aracı içinde tetiklenir
- Tetikleyiciler: PostToolUse / Stop / PreToolUse — Claude Code yaşam döngüsü olayları
- Erişilen bilgi: tool adı, argümanlar, transcript_path (tam session jsonl) — yalnızca Claude Code'un bildiği şeyler

Git hooks (.git/hooks/ altındaki shell script'leri):
- Kapsam: git aracının oluşturduğu tüm olaylarda, git'i kim tetiklerse tetiklesin
- Tetikleyiciler: post-commit / pre-push vb.
- Erişilen bilgi: git'in kendi bildiği şeyler (sha, yazar, branch, diff)

Gerçek sonuç: Claude Code içinde kod yazıp + commit yaparsanız iki katman da ateşlenir — session bilgisi ve commit bilgisi raw.md'ye iner. Amp'a (veya Cursor'a ya da elle yazmaya) geçip yazıp + commit yaparsanız, yalnızca git hook'u ateşlenir — raw.md'ye commit iskeleti düşer ama session'ın prompt / bash / edit detayı düşmez.

Bu bir bug değil — her aracın tasarım kısıtı. Her araç altında session düzeyinde detay istemek, her araca kendi hook katmanınızı kurmak demek. Git yedeği size "ne yapıldı"yı verir; "ne düşünüldü, neresi kırıldı"yı vermez.

Seçim kılavuzu:
- Araç bağımsız iskelet (commit bilgisi, kod değişiklikleri) → git hook'una koy
- Claude Code'a özgü et (tam prompt'lar, muhakeme) → Claude Code hook'una koy
- İkisi de → iki katmana da kur

5 hamlede taşıma

Aynı kayıt kurulumunu smarts'a (/home/bob/Work/smarts, bir Rails projesi) kurmak.

1. İki Python script'i kopyala

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'lerde değişiklik yok — nereye yazacaklarına $CLAUDE_PROJECT_DIR ortam değişkeni ile karar veriyorlar:

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"

Kilit soyutlama bu: Claude Code hook ateşlerken CLAUDE_PROJECT_DIR'i otomatik set ediyor; biz git post-commit içinde manuel set ediyoruz. İki taraf da aynı env var'a saygı duyuyor, script "hangi projedeyim" diye düşünmek zorunda değil.

2. .claude/settings.local.json oluştur

Burada bir karar: sadece hook'ları taşı, permissions listesini taşıma.

how2claude'un settings.local.json'ında 100'den fazla permissions.allow kaydı var — hepsi how2claude'a özel (curl localhost:3000, bin/rails runner, kamal app exec). Bunları smarts'a taşımanın hiçbir anlamı yok. smarts kullanıldıkça kendi permissions'ını organik olarak biriktirecek.

Hook'lar desen — projeler arası aynı. Permissions proje durumu — projeler arası farklı.

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

Üç tetikleme noktası:
- Dosya düzenlendi → kaydı başlatmayı dene (maybe-start kendi kontrolü: zaten kayıtta ise atla, tree clean ise yine atla)
- Bash komutu çalıştı → durdurmayı dene (maybe-stop sıkı: yalnızca "otomatik başlatıldı VE master'a döndü VE tree clean" üçü birden sağlanınca durdurur)
- Session sona erdi → transcript'i raw.md'ye çıkar

3. .git/hooks/post-commit oluştur

3 satır:

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

Manuel CLAUDE_PROJECT_DIR=$ROOT export'u, git dünyası ile Claude Code dünyasının aynı env var ile köprülendiği tam nokta. || true hook'un commit'i asla engellememesini garanti eder.

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

4. .gitignore'a docs/notes/ ekle

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

Notlar geçici + özel — raw.md'nin PR'lara commitlenmesini istemiyorsunuz; .state.json'un git status'u kirletmesini istemiyorsunuz. how2claude'un gitignore'u da aynı şekilde davranıyor.

5. Duman testi (küçük bir sürprizle)

Kurulumdan hemen sonra maybe-start'ı manuel tetikle:

$ 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'ın o an feat/contract-to-docs branch'inde ve tree'nin dirty olduğunu doğru yakaladı — otomatik kayıt başlattı, branch feat/contract-to-docs'tan contract-to-docs feature adını çıkardı. Script'teki o mantık:

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, çıplak fix-yfix-y, master/main → None (zamanlı session-YYYYMMDD-HHMM ismine düşer).

Heuristik kasten aptalca basit — branch adı işin konusudur, size tekrar isim verdirmeye gerek yok.

Amp ile karışımda görünürlük sınırı

Kurulumdan sonra kendime sordum: arada Amp'ta çalışıyorum — orası yakalanıyor mu? Cevap tam hook katmanlaması hikâyesinin öngördüğü gibi:

Senaryo Claude Code hook git hook Notlara ne düşer
Claude Code'da çalışma + commit ✅ ateşlenir ✅ ateşlenir session detayı + commit iskeleti
Amp'ta çalışma + commit ❌ no-op ✅ ateşlenir yalnızca commit iskeleti
Elle yazma + commit ❌ no-op ✅ ateşlenir yalnızca commit iskeleti
Claude Code'da çalışma, henüz commit yok ✅ kayıt başlatır session detayı (commit girişi bir sonraki commit'i bekler)

Çıkarım: Claude Code birincil sürücüyse yeter. Commit iskeleti her durumda iner; session detayı yalnızca Claude Code yolunda iner. "Ne yapıldı" tipi yazılar için çoğunlukla commit gövdesine dayanırsınız — session prompt / bash izleri bonus, olursa iyi ama olmasa da olur.

Amp'i yoğun kullanıyorsanız, Amp'in kendi hook mekanizması var (detaylarına girmedim); recording-state maybe-start/maybe-stop'u ateşleyen küçük bir yönlendirme script'i aynı şekilde çalışır.

Kontrol listesi

Claude Code session recording'i başka bir projeye taşımak — 5 hamle:

  1. İki Python script'ini cp ile hedef projenin bin/ dizinine. Değişiklik yok — script'ler $CLAUDE_PROJECT_DIR'e saygı duyar, tasarım gereği projeler arası taşınabilir.
  2. .claude/settings.local.json oluştur, yalnızca hooks. Permissions listesini taşıma — permissions proje durumu (proje başına farklı); hook'lar desen (projeler arası aynı).
  3. .git/hooks/post-commit oluştur (3 satır), manuel export CLAUDE_PROJECT_DIR=$ROOT ve recording-state commit'i çağır. Git dünyası ile Claude Code dünyasının aynı env var ile köprülendiği tek nokta bu.
  4. .gitignore'a docs/notes/ ekle. Notlar geçici + özel, repo'nun parçası değil.
  5. Manuel duman testi: CLAUDE_PROJECT_DIR=$(pwd) ./bin/recording-state maybe-start, branch→feature çıkarımının doğru olduğunu doğrula. Tree clean ise script tasarım gereği atlar — bug değil.

Asıl tasarım kararı "nasıl taşırım" değil — taşıma neredeyse cp. Asıl karar kayıt mantığını 4 ayrı katmana bölmek, her katmanın net bir iş yapması:

  • Python script'ler: durumsuz helper, CLAUDE_PROJECT_DIR'e saygı duyar
  • Claude Code hook'u: araç içi olaylar (session etki)
  • git hook'u: araç bağımsız olaylar (commit iskeleti)
  • gitignore: gürültü kontrolü

Bu 4 katmanın her biri bağımsız olarak taşınabilir, değiştirilebilir veya atlanabilir (Amp desteği mi istiyorsunuz? bir Amp hook katmanı ekle. Not formatını mı değiştireceksiniz? Python'u düzenle. Git'in notları izlemesini mi istemiyorsunuz? post-commit'i sil). Claude kodu doğru yazabilir — ama "bu işlev hangi katmana ait" kararını sizin için veremez. Bu, sizin araç sınırlarınıza ilişkin bir karar ve o karar sizindir.