DEV Community

Cover image for Kendi Claude Kodunu Nasıl Oluşturursun?
Tobias Hoffmann
Tobias Hoffmann

Posted on • Originally published at apidog.com

Kendi Claude Kodunu Nasıl Oluşturursun?

Özet

Claude Code kaynak kodu sızıntısı, 31 Mart 2026 tarihinde 512.000 satırlık bir TypeScript kod tabanını ortaya çıkardı. Mimari, Claude API'sini çağıran, araç çağrılarını gönderen ve sonuçları geri besleyen bir while döngüsüne indirgeniyor. Kendi sürümünüzü Python, Anthropic SDK ve temel döngü için yaklaşık 200 satır kod kullanarak oluşturabilirsiniz. Bu kılavuz, her bir bileşeni ayrıntılarıyla anlatıyor ve bunları nasıl yeniden oluşturacağınızı gösteriyor.

Apidog'u hemen deneyin

Giriş

31 Mart 2026'da Anthropic, @anthropic-ai/claude-code npm paketinin 2.1.88 sürümünün içine 59.8 MB'lık bir kaynak haritası dosyası gönderdi. Kaynak haritaları, küçültülmüş JavaScript'i orijinal kaynağına döndüren hata ayıklama yapıtlarıdır. Anthropic'in derleme aracı (Bun'ın paketleyicisi) bunları varsayılan olarak ürettiği için, TypeScript kod tabanının tamamı kurtarılabilirdi.

Saatler içinde geliştiriciler, kodu onlarca GitHub deposuna yansıttı. Topluluk, ana aracı döngüsünden "gizli mod" ve sahte araç enjeksiyonu gibi gizli özelliklere kadar her modülü hızla inceledi.

Tepkiler ikiye ayrıldı. Bazıları Anthropic'in güvenlik uygulamalarını eleştirdi. Diğerleri ise mimariye hayran kaldı. Ancak en verimli yanıt, "Bunu kendim yapabilir miyim?" diye soran geliştiricilerden geldi.

Cevap evet. Temel desenler basittir. Bu rehber, her bir mimari katmanı hızlıca uygulamanız için örnek kod ve pratik açıklamalar sunar. Ayrıca, çok turlu API konuşmalarında hata ayıklamayı ham curl komutlarından çok daha kolay hale getiren Apidog ile özel aracınızın API etkileşimlerini nasıl test edeceğinizi de göstereceğiz.

Sızıntı, Claude Code'un mimarisi hakkında neyi ortaya çıkardı?

Kod tabanına bir bakış

Dahili olarak "Tengu" kod adlı Claude Code, yaklaşık 1.900 dosyadan oluşuyor. Modül organizasyonu şöyle ayrılıyor:

cli/          - Terminal UI (React + Ink)
tools/        - 40+ tool implementations
core/         - System prompts, permissions, constants
assistant/    - Agent orchestration
services/     - API calls, compaction, OAuth, telemetry
Enter fullscreen mode Exit fullscreen mode

CLI'nin kendisi React + Ink ile yazılmış bir terminal uygulaması. Ancak, çalışan bir kodlama aracısı için React tabanlı bir terminal UI'ye ihtiyacınız yok. Basit bir REPL döngüsü yeterli.

Ana aracı döngüsü

Tüm karmaşıklığı çıkarınca, Claude Code'un çekirdeği bir while döngüsüne indirgeniyor. Temel akış:

  1. Claude API'ye mesaj gönder (sistem istemi + araç tanımları)
  2. Metin ve/veya tool_use blokları içeren bir yanıt al
  3. Her istenen aracı, ad-işleyici dağıtımı ile çalıştır
  4. Araç sonuçlarını mesaj listesine geri ekle
  5. Yanıt daha fazla araç çağrısı içeriyorsa, 1. adıma dön
  6. Yanıt araç çağrısı içermiyorsa, kullanıcıya döndür

Python’da minimal bir uygulama:

import anthropic

client = anthropic.Anthropic()
MODEL = "claude-sonnet-4-6"

def agent_loop(system_prompt: str, tools: list, messages: list) -> str:
    """The core agent loop - keep calling until no more tool use."""
    while True:
        response = client.messages.create(
            model=MODEL,
            max_tokens=16384,
            system=system_prompt,
            tools=tools,
            messages=messages,
        )

        messages.append({"role": "assistant", "content": response.content})

        if response.stop_reason != "tool_use":
            return "".join(
                block.text for block in response.content
                if hasattr(block, "text")
            )

        tool_results = []
        for block in response.content:
            if block.type == "tool_use":
                result = execute_tool(block.name, block.input)
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": result,
                })

        messages.append({"role": "user", "content": tool_results})
Enter fullscreen mode Exit fullscreen mode

Bu yaklaşık 30 satır. Geri kalan karmaşıklık araç tanımları, izinler ve bağlamdan geliyor.

Araç sistemini oluşturma

Neden özel araçlar tek bir bash komutundan daha iyidir?

Claude Code, dosya işlemleri için özel araçlar kullanıyor. Örneğin Read, Edit, Grep, Glob gibi araçlar bash yerine kendi tanımlı işlevlerle çalışıyor. Bunun üç sebebi:

  • Yapılandırılmış çıktı: Araç yanıtları tutarlı ve ayrıştırılabilir.
  • Güvenlik: Ters tırnak ve $() gibi riskli shell özellikleri engelleniyor.
  • Token verimliliği: Araç çıktıları gereksiz yere büyük olmaz, bağlamı israf etmez.

Temel araç seti

Basit bir aracı için şu araçlar yeterli:

TOOLS = [
    {
        "name": "read_file",
        "description": "Read a file from the filesystem. Returns contents with line numbers.",
        "input_schema": {
            "type": "object",
            "properties": {
                "file_path": {
                    "type": "string",
                    "description": "Absolute path to the file"
                },
                "offset": {
                    "type": "integer",
                    "description": "Line number to start reading from (0-indexed)"
                },
                "limit": {
                    "type": "integer",
                    "description": "Max lines to read. Defaults to 2000."
                }
            },
            "required": ["file_path"]
        }
    },
    {
        "name": "write_file",
        "description": "Write content to a file. Creates the file if it doesn't exist.",
        "input_schema": {
            "type": "object",
            "properties": {
                "file_path": {"type": "string", "description": "Absolute path"},
                "content": {"type": "string", "description": "File content to write"}
            },
            "required": ["file_path", "content"]
        }
    },
    {
        "name": "edit_file",
        "description": "Replace a specific string in a file. The old_string must be unique.",
        "input_schema": {
            "type": "object",
            "properties": {
                "file_path": {"type": "string", "description": "Absolute path"},
                "old_string": {"type": "string", "description": "Text to find"},
                "new_string": {"type": "string", "description": "Replacement text"}
            },
            "required": ["file_path", "old_string", "new_string"]
        }
    },
    {
        "name": "run_command",
        "description": "Execute a shell command and return stdout/stderr.",
        "input_schema": {
            "type": "object",
            "properties": {
                "command": {"type": "string", "description": "Shell command to run"},
                "timeout": {"type": "integer", "description": "Timeout in seconds. Default 120."}
            },
            "required": ["command"]
        }
    },
    {
        "name": "search_code",
        "description": "Search for a regex pattern across files in a directory.",
        "input_schema": {
            "type": "object",
            "properties": {
                "pattern": {"type": "string", "description": "Regex pattern"},
                "path": {"type": "string", "description": "Directory to search"},
                "file_glob": {"type": "string", "description": "File pattern filter, e.g. '*.py'"}
            },
            "required": ["pattern"]
        }
    }
]
Enter fullscreen mode Exit fullscreen mode

Araç işleyici dağıtımı

Araç fonksiyonlarını eşleştiren örnek bir dispatcher:

import subprocess
import os
import re

def execute_tool(name: str, params: dict) -> str:
    """Dispatch tool calls to their handlers."""
    handlers = {
        "read_file": handle_read_file,
        "write_file": handle_write_file,
        "edit_file": handle_edit_file,
        "run_command": handle_run_command,
        "search_code": handle_search_code,
    }

    handler = handlers.get(name)
    if not handler:
        return f"Error: Unknown tool '{name}'"

    try:
        return handler(params)
    except Exception as e:
        return f"Error: {str(e)}"


def handle_read_file(params: dict) -> str:
    path = params["file_path"]
    offset = params.get("offset", 0)
    limit = params.get("limit", 2000)

    with open(path, "r") as f:
        lines = f.readlines()

    selected = lines[offset:offset + limit]
    numbered = [f"{i + offset + 1}\t{line}" for i, line in enumerate(selected)]
    return "".join(numbered)


def handle_write_file(params: dict) -> str:
    path = params["file_path"]
    os.makedirs(os.path.dirname(path), exist_ok=True)
    with open(path, "w") as f:
        f.write(params["content"])
    return f"Successfully wrote to {path}"


def handle_edit_file(params: dict) -> str:
    path = params["file_path"]
    with open(path, "r") as f:
        content = f.read()

    old = params["old_string"]
    if content.count(old) == 0:
        return f"Error: '{old[:50]}...' not found in {path}"
    if content.count(old) > 1:
        return f"Error: '{old[:50]}...' matches {content.count(old)} locations. Be more specific."

    new_content = content.replace(old, params["new_string"], 1)
    with open(path, "w") as f:
        f.write(new_content)
    return f"Successfully edited {path}"


def handle_run_command(params: dict) -> str:
    cmd = params["command"]
    timeout = params.get("timeout", 120)

    blocked = ["rm -rf /", "mkfs", "> /dev/"]
    for pattern in blocked:
        if pattern in cmd:
            return f"Error: Blocked dangerous command pattern: {pattern}"

    result = subprocess.run(
        cmd, shell=True, capture_output=True, text=True,
        timeout=timeout, cwd=os.getcwd()
    )

    output = ""
    if result.stdout:
        output += result.stdout
    if result.stderr:
        output += f"\nSTDERR:\n{result.stderr}"
    if not output.strip():
        output = f"Command completed with exit code {result.returncode}"

    if len(output) > 30000:
        output = output[:15000] + "\n\n... [truncated] ...\n\n" + output[-15000:]

    return output


def handle_search_code(params: dict) -> str:
    pattern = params["pattern"]
    path = params.get("path", os.getcwd())
    file_glob = params.get("file_glob", "")

    cmd = ["grep", "-rn", "--include", file_glob, pattern, path] if file_glob else \
          ["grep", "-rn", pattern, path]

    result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)

    if not result.stdout.strip():
        return f"No matches found for pattern: {pattern}"

    lines = result.stdout.strip().split("\n")
    if len(lines) > 50:
        return "\n".join(lines[:50]) + f"\n\n... ({len(lines) - 50} more matches)"
    return result.stdout
Enter fullscreen mode Exit fullscreen mode

Bağlam yönetimi: zor sorun

Neden bağlam, istem mühendisliğinden daha önemlidir?

Claude Code, sistem isteminden çok bağlam yönetimine mühendislik harcıyor. Otomatik sıkıştırma ve yönerge yeniden enjeksiyonu ile uzun konuşmalarda bağlam kaybı önleniyor.

  • Otomatik sıkıştırma: Bağlam penceresi sınırına yaklaşınca tetiklenir, konuşmayı özetler.
  • CLAUDE.md yeniden enjeksiyonu: Her turda proje yönergelerini tekrar sistem istemine ekler.

Basit bir sıkıştırıcı oluşturma

def maybe_compact(messages: list, system_prompt: str, max_tokens: int = 180000) -> list:
    """Compact conversation when it gets too long."""
    total_chars = sum(
        len(str(m.get("content", ""))) for m in messages
    )
    estimated_tokens = total_chars // 4

    if estimated_tokens < max_tokens * 0.85:
        return messages

    summary_response = client.messages.create(
        model=MODEL,
        max_tokens=4096,
        system="Summarize this conversation. Keep all file paths, decisions made, errors encountered, and current task state. Be specific about what was changed and why.",
        messages=messages,
    )

    summary_text = summary_response.content[0].text

    compacted = [
        {"role": "user", "content": f"[Conversation summary]\n{summary_text}"},
        {"role": "assistant", "content": "I have the context from our previous conversation. What should I work on next?"},
    ]

    compacted.extend(messages[-4:])
    return compacted
Enter fullscreen mode Exit fullscreen mode

Proje bağlamını yeniden enjekte etme

def build_system_prompt(project_dir: str) -> str:
    """Build system prompt with project context re-injection."""
    base_prompt = """You are a coding assistant that helps with software engineering tasks.
You have access to tools for reading, writing, editing files, running commands, and searching code.
Always read files before modifying them. Prefer edit_file over write_file for existing files.
Keep responses concise. Focus on the code, not explanations."""

    claude_md_path = os.path.join(project_dir, ".claude", "CLAUDE.md")
    if os.path.exists(claude_md_path):
        with open(claude_md_path, "r") as f:
            project_context = f.read()
        base_prompt += f"\n\n# Project guidelines\n{project_context}"

    root_md = os.path.join(project_dir, "CLAUDE.md")
    if os.path.exists(root_md):
        with open(root_md, "r") as f:
            root_context = f.read()
        base_prompt += f"\n\n# Repository guidelines\n{root_context}"

    return base_prompt
Enter fullscreen mode Exit fullscreen mode

Üç katmanlı bellek sistemi

Sızdırılan kaynak, Claude Code'un üç katmanlı bir bellek mimarisi kullandığını ortaya çıkardı.

Katman 1: MEMORY.md (her zaman yüklü)

Kısa, dizin formatlı, her zaman sistem istemine eklenen bir dosya:

- [User preferences](memory/user-prefs.md) - prefers TypeScript, uses Vim keybindings
- [API conventions](memory/api-conventions.md) - REST with JSON:API spec, snake_case
- [Deploy process](memory/deploy.md) - uses GitHub Actions, deploys to AWS EKS
Enter fullscreen mode Exit fullscreen mode

Katman 2: Konu dosyaları (talep üzerine yüklenir)

Dizin ilgisine göre yüklenen ayrıntılı bilgi dosyaları.

Katman 3: Oturum transkriptleri (aranır, asla okunmaz)

Toptan yüklenmeyen, sadece grep ile aranan oturum günlükleri.

Minimal bir bellek sistemi oluşturma

import json

MEMORY_DIR = ".agent/memory"

def load_memory_index() -> str:
    """Load the memory index for system prompt injection."""
    index_path = os.path.join(MEMORY_DIR, "MEMORY.md")
    if os.path.exists(index_path):
        with open(index_path, "r") as f:
            return f.read()
    return ""


def save_memory(key: str, content: str, description: str):
    """Save a memory entry and update the index."""
    os.makedirs(MEMORY_DIR, exist_ok=True)

    filename = f"{key.replace(' ', '-').lower()}.md"
    filepath = os.path.join(MEMORY_DIR, filename)
    with open(filepath, "w") as f:
        f.write(f"---\nname: {key}\ndescription: {description}\n---\n\n{content}")

    index_path = os.path.join(MEMORY_DIR, "MEMORY.md")
    index_lines = []
    if os.path.exists(index_path):
        with open(index_path, "r") as f:
            index_lines = f.readlines()

    new_entry = f"- [{key}]({filename}) - {description}\n"
    updated = False
    for i, line in enumerate(index_lines):
        if filename in line:
            index_lines[i] = new_entry
            updated = True
            break
    if not updated:
        index_lines.append(new_entry)

    with open(index_path, "w") as f:
        f.writelines(index_lines)
Enter fullscreen mode Exit fullscreen mode

Araç listenize bir save_memory aracı ekleyerek oturumlar arası kalıcılık sağlayabilirsiniz.

Bir izin sistemi ekleme

Basit üç seviyeli bir izin sistemi için:

# Risk levels for operations
RISK_LEVELS = {
    "read_file": "low",
    "search_code": "low",
    "edit_file": "medium",
    "write_file": "medium",
    "run_command": "high",
}

def check_permission(tool_name: str, params: dict, auto_approve_low: bool = True) -> bool:
    """Check if the user approves this tool call."""
    risk = RISK_LEVELS.get(tool_name, "high")

    if risk == "low" and auto_approve_low:
        return True

    print(f"\n--- Permission check ({risk.upper()} risk) ---")
    print(f"Tool: {tool_name}")
    for key, value in params.items():
        display = str(value)[:200]
        print(f"  {key}: {display}")

    response = input("Allow? [y/n/always]: ").strip().lower()
    if response == "always":
        RISK_LEVELS[tool_name] = "low"
        return True
    return response == "y"
Enter fullscreen mode Exit fullscreen mode

Aracınızın API çağrılarını Apidog ile test etme

Bir kodlama aracısı geliştirirken Claude'a çok sayıda API çağrısı yapacaksınız. Özellikle araç çağrılarıyla çok turlu konuşmalarda hata ayıklamak için görsel ve tekrar oynatılabilir bir test ortamı gereklidir.

Apidog'un API istek detaylarını gösteren arayüzünün ekran görüntüsü

Apidog, aracınızın gönderdiği tam API isteklerini incelemenize ve test etmenize yardımcı olur. Şöyle kullanabilirsiniz:

API isteklerini yakalama ve yeniden oynatma

  • Apidog'u açın ve yeni bir proje oluşturun.
  • Anthropic Mesajlar API uç noktasını ekleyin: POST https://api.anthropic.com/v1/messages
  • Sistem istemi, araç dizisi ve mesajlarınızı içeren istek gövdesini oluşturun.
  • Yakalanan istekleri farklı parametrelerle yeniden oynatın.

Bu yöntemle, REPL'i tamamen çalıştırmadan tekil turları, araç çağrılarını ve hata durumlarını izole edebilirsiniz.

Çok turlu konuşmalarda hata ayıklama

  • Her turdan sonra tam messages dizisini ortam değişkeni olarak saklayın.
  • Konuşmanın herhangi bir anından istekleri tekrar oynatın.
  • Çalıştırmalar arasında araç sonuçlarını karşılaştırarak davranış değişimini analiz edin.

Araç şemalarını doğrulama

Araç tanımlarınız (API’ye ilettiğiniz JSON şemaları) modelin davranışını doğrudan etkiler. Yanlış şema modelin aracı atlamasına veya yanlış parametre göndermesine neden olur. Apidog'un JSON şema doğrulayıcısı ile, API’ye ulaşmadan önce hataları yakalayabilirsiniz.

Aracınızın API etkileşimlerinde hata ayıklamaya başlamak için Apidog'u indirin.

Hepsini bir araya getirme: eksiksiz REPL

Aşağıdaki gibi çalışan bir REPL ile tüm sistemi birleştirebilirsiniz:

#!/usr/bin/env python3
"""A minimal Claude Code-style coding agent."""

import anthropic
import os
import sys

client = anthropic.Anthropic()
MODEL = "claude-sonnet-4-6"
PROJECT_DIR = os.getcwd()


def main():
    system_prompt = build_system_prompt(PROJECT_DIR)
    memory = load_memory_index()
    if memory:
        system_prompt += f"\n\n# Memory\n{memory}"

    messages = []
    print("Coding agent ready. Type 'quit' to exit.\n")

    while True:
        user_input = input("> ").strip()
        if user_input.lower() in ("quit", "exit"):
            break
        if not user_input:
            continue

        messages.append({"role": "user", "content": user_input})

        messages = maybe_compact(messages, system_prompt)

        current_system = build_system_prompt(PROJECT_DIR)
        memory = load_memory_index()
        if memory:
            current_system += f"\n\n# Memory\n{memory}"

        result = agent_loop(current_system, TOOLS, messages)
        print(f"\n{result}\n")


if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Bu kod, 300 satırdan az Python ile dosya okuma, kod düzenleme, komut çalıştırma, arama, bağlam yönetimi ve oturumlar arası bellek sağlar.

Sıradaki eklenecekler

Claude Code sızıntısında ortaya çıkan ve kendi aracınıza ekleyebileceğiniz ek özellikler:

Paralel çalışma için alt aracılar

Bağımsız görevler için alt aracılar (forked agents) oluşturun. Yeni bir agent_loop() başlatın, sonucu dize olarak döndürün.

Dosya okuma tekilleştirme

Okunan dosyaların değişip değişmediğini takip edin. Değişmediyse yeniden okumayı atlayın, modele "dosya son okumadan bu yana değişmedi" mesajı gönderin.

Çıktı kırpma ve örnekleme

Büyük araç çıktılarında (örneğin 10.000 satır grep sonucu) çıktıyı kırpın ve modele kaç sonucun atlandığını bildirin.

Dosya yeniden enjeksiyonu ile otomatik sıkıştırma

Sıkıştırmadan sonra yakın zamanda erişilen dosyaların içeriğini (örneğin dosya başına 5.000 token) tekrar sisteme enjekte edin.

Sızıntıdan öğrendiklerimiz

  • Temel döngü basit: Aracı döngüsü ~30 satır kod. Karmaşıklık araçlarda ve bağlam yönetiminde.
  • Özel araçlar bash'ten daha iyi: Yapılandırılmış araçlar, bash komutlarından daha iyi bilgi yoğunluğu sağlar.
  • Belleğin katmanlara ihtiyacı var: Her zaman yüklü bir dizin, isteğe bağlı konu dosyaları ve sadece grep ile aranan transkriptler.
  • Bağlam yönetimi asıl ürün: Otomatik sıkıştırma, proje yönergesi yeniden enjeksiyonu ve çıktı kırpma, uzun oturumları sürdürülebilir kılar.
  • Ürün model değil, koşum takımıdır: Model zekayı, koşum takımı dosya erişimi, eylem ve belleği sağlar.

Çok turlu araç konuşmaları ve karmaşık istek/yanıt şemalarını test etmek için Apidog’u ücretsiz deneyin. API hata ayıklamasını ona bırakın, siz aracı mantığına odaklanın.

Sıkça Sorulan Sorular

Claude Code sızıntısındaki desenleri yasal olarak kullanabilir miyim?

Mimari desenler kamuya açık. Aracı döngüsü gibi desenler standart API belgelerinde de yer alıyor. Kodun tamamını kopyalamayın; mimariyi kendi kodunuzla uygulayın.

Kendin yap bir kodlama aracısı için hangi modeli kullanmalıyım?

Claude Sonnet 4.6, kodlama görevleri için iyi bir hız/yetenek dengesi sunar. Daha karmaşık işler için Claude Opus 4.6 tercih edilebilir. Basit işler için Claude Haiku 4.5 daha ucuzdur.

Kendi kodlama aracınızı çalıştırmak ne kadara mal olur?

Claude Sonnet 4.6 ile tipik bir oturum (30-50 tur) API ücretleri olarak 1-5 dolar arası tutar. Bağlam penceresi boyutu ana maliyet kalemidir. Sıkıştırma maliyetleri düşürür.

Claude Code neden terminal uygulaması için React kullanıyor?

Ink (terminaller için React), ekibin izin iletişim kutuları, akışlı çıktı ve araç çağrısı gösterimleri gibi karmaşık UI etkileşimleri için React’in bileşen modelini kullanmasını sağlar. DIY projede basit bir input() / print() REPL yeterli.

Temel döngüden sonra inşa edilecek en önemli özellik nedir?

İzin sistemi. Olmazsa model dosyaları silebilir veya rastgele komutlar çalıştırabilir. Küçük bir "önce onayla" kapısı bile kazara zararı engeller.

Claude Code, araç çağrılarından kaynaklanan hataları nasıl ele alıyor?

Araç hataları, tool_result mesajında metin olarak döndürülür. Model hatayı görür ve yeniden dener, farklı yaklaşım dener veya kullanıcıya sorar. Özel bir hata yönetimi yoktur.

Bunu Claude dışındaki modellerle kullanabilir miyim?

Evet. Fonksiyon çağrısını destekleyen GPT-4, Gemini, Llama gibi diğer modellerle de çalışır. API çağrı formatını uyarlamanız gerekir.

Aracının tehlikeli komutları çalıştırmasını nasıl engellerim?

rm -rf /, mkfs gibi tehlikeli desenleri engelleyin ve tüm run_command çağrıları için açık onay isteyin. Claude Code da her işlemi DÜŞÜK, ORTA, YÜKSEK risk olarak sınıflandırır.


Yukarıdaki adımları uygulayarak Claude Code tarzı bir aracı hızlıca inşa edebilir, Apidog ile her aşamada API etkileşimlerinizi görsel olarak test edip hata ayıklayabilirsiniz.

Top comments (0)