Intisari
Kebocoran kode sumber Claude Kode mengekspos basis kode TypeScript 512.000 baris pada 31 Maret 2026. Arsitekturnya berbasis pada perulangan while yang memanggil API Claude, menjalankan alat, dan mengumpan balik hasilnya. Anda bisa membangun versi sendiri dengan Python, Anthropic SDK, dan kurang dari 200 baris kode untuk loop inti. Artikel ini memecah setiap komponen dan memperlihatkan cara implementasinya.
Pendahuluan
Pada 31 Maret 2026, Anthropic mengirimkan file source map 59,8 MB dalam paket npm @anthropic-ai/claude-code versi 2.1.88. Source map adalah artefak debugging yang merekonstruksi JavaScript yang diminify ke kode asli. Tool build Anthropic (Bun's bundler) menghasilkan ini secara default, sehingga seluruh basis kode TypeScript bisa dipulihkan.
Dalam hitungan jam, pengembang sudah me-mirror kode tersebut ke puluhan repo GitHub. Komunitas menganalisis setiap modul, dari agent loop hingga fitur tersembunyi seperti “mode penyamaran” dan injeksi alat palsu.
Reaksi terpecah; ada yang mengkritik keamanan Anthropic, ada yang kagum pada arsitekturnya. Tapi respons paling berguna datang dari developer yang bertanya: “Bisakah saya bikin ini sendiri?”
Jawabannya: ya. Pola intinya sederhana. Artikel ini membedah tiap lapisan arsitektur, menjelaskan alasan desain, serta memberi contoh kode siap pakai. Anda juga akan belajar cara menguji interaksi API agen Anda dengan Apidog, sehingga debugging percakapan API multi-giliran jauh lebih mudah dibandingkan pakai curl.
Apa yang Diungkapkan Kebocoran tentang Arsitektur Claude Code
Struktur Basis Kode
Claude Code (internal codename: Tengu) terdiri dari ~1.900 file, terorganisir sebagai berikut:
cli/ - UI Terminal (React + Ink)
tools/ - 40+ implementasi alat
core/ - Prompt sistem, izin, konstanta
assistant/ - Orkestrasi agen
services/ - Panggilan API, kompresi, OAuth, telemetri
CLI-nya adalah aplikasi React yang dirender dengan Ink (React untuk terminal). Menggunakan Yoga (engine flexbox CSS) dan ANSI escape code untuk styling. Setiap tampilan adalah komponen React.
Untuk proyek DIY, Anda tidak perlu UI berbasis React. REPL sederhana sudah cukup.
Loop Agen Utama
Inti Claude Code adalah perulangan while. Anthropic menyebutnya “nO”. Prosesnya:
- Kirim pesan ke API Claude (prompt sistem + definisi alat)
- Terima respons teks/blok
tool_use - Jalankan alat yang diminta (berdasarkan nama)
- Tambah hasil alat ke daftar pesan
- Jika ada panggilan alat lain, ulang ke langkah 1
- Jika hanya teks biasa, kembalikan ke user
Satu “giliran” = satu round trip. Berikut versi Python minimalnya:
import anthropic
client = anthropic.Anthropic()
MODEL = "claude-sonnet-4-6"
def agent_loop(system_prompt: str, tools: list, messages: list) -> str:
"""Loop inti agen - ulangi sampai tidak ada 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})
Sisa kompleksitas berasal dari alat, sistem izin, manajemen konteks, dan memori.
Membangun Sistem Alat
Kenapa Alat Khusus Lebih Baik dari Bash
Claude Code memakai alat khusus untuk operasi file, bukan sekadar menjalankan perintah bash. Contohnya:
-
Read(bukancat) -
Edit(bukansed) -
Grep(bukangrep) -
Glob(bukanfind)
Keunggulan alat khusus:
- Output terstruktur: Lebih mudah di-parse model; output bash tidak terprediksi.
-
Keamanan: BashTool menolak backtick/subshell (
$()) untuk cegah injeksi. - Efisiensi token: Output alat bisa dipotong/disampling; output bash mudah menghabiskan token.
Daftar Alat Minimal
Untuk agen DIY, cukup 5 alat berikut:
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"]
}
}
]
Eksekusi dan Pengiriman Alat
Implementasi dispatch alat:
import subprocess
import os
import re
def execute_tool(name: str, params: dict) -> str:
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)
# Block dangerous patterns
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
Manajemen Konteks: Tantangan Utama
Kenapa Manajemen Konteks Lebih Kritis dari Prompt Engineering
Kode Claude memprioritaskan manajemen konteks dibanding prompt. Kompresor konteks (“wU2”) punya lima strategi.
Untuk DIY, cukup dua:
- Kompresi otomatis: Ketika percakapan mendekati limit, Claude Code memicu ringkasan (buffer 13.000 token).
- Injeksi ulang CLAUDE.md: Guidelines proyek diinjeksikan ulang di setiap giliran untuk menjaga konsistensi.
Kompresor Sederhana
def maybe_compact(messages: list, system_prompt: str, max_tokens: int = 180000) -> list:
"""Compact conversation ketika terlalu panjang."""
# Estimasi: 4 karakter per token
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
# Ringkas percakapan dengan model
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
Injeksi Ulang Konteks Proyek
def build_system_prompt(project_dir: str) -> str:
"""Bangun system prompt dengan re-injeksi konteks proyek."""
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
Sistem Memori Tiga Lapis
Lapisan 1: MEMORY.md (selalu dimuat)
Indeks ringan, satu baris per entri (<150 karakter), max 200 baris/25KB. Contoh:
- [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
Lapisan 2: File Topik (on-demand)
File detail yang dimuat jika relevan.
Lapisan 3: Transkrip Sesi (dicari, tidak dibaca penuh)
Log sesi lengkap, hanya dicari untuk ID tertentu, tidak pernah dimuat penuh.
Sistem Memori Minimal
import json
MEMORY_DIR = ".agent/memory"
def load_memory_index() -> str:
"""Load memory index untuk system prompt."""
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):
"""Simpan entri memori dan update 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)
Tambahkan alat save_memory ke daftar tools agar agen bisa menyimpan pengetahuan antar sesi.
Sistem Izin Sederhana
Claude Code membedakan lima mode izin: default, auto, bypass, yolo, deny. Setiap alat diklasifikasi RENDAH, SEDANG, TINGGI.
Untuk DIY, cukup tiga level:
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:
"""Cek izin user untuk 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"
Menguji Panggilan API Agen dengan Apidog
Membangun agen pengode berarti ratusan panggilan API ke Claude. Debugging multi-turn dengan log mentah sangat merepotkan.
Apidog membantu memeriksa dan menguji request API agen Anda. Cara penggunaannya:
Menangkap & Memutar Ulang Request API
- Buka Apidog, buat project baru untuk agen Anda
- Impor endpoint API Anthropic:
POST https://api.anthropic.com/v1/messages - Siapkan body request dengan sistem prompt, array tools, dan pesan Anda
- Uji giliran individual dengan replay permintaan dan edit parameter
Ini memungkinkan Anda mengisolasi alat tertentu tanpa menjalankan seluruh loop. Jika model mengembalikan panggilan alat tak terduga atau parameter aneh, modifikasi request di Apidog dan replay.
Debugging Percakapan Multi-Giliran
Variabel lingkungan Apidog bisa menyimpan snapshot percakapan:
- Simpan array
messagessetelah tiap giliran - Replay dari titik mana pun
- Bandingkan hasil alat antar eksekusi
Validasi Skema Alat
Skema JSON alat Anda menentukan perilaku model. Skema salah bentuk = alat gagal tanpa pesan. Import skema ke Apidog dan gunakan validator Skema JSON-nya untuk menangkap error sebelum request dikirim.
Menyatukan Segalanya: Contoh REPL Lengkap
Berikut REPL coding agent sederhana:
#!/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})
# Compact jika perlu
messages = maybe_compact(messages, system_prompt)
# Re-inject context setiap giliran
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()
Dengan kurang dari 300 baris Python, Anda sudah punya agen pengodean: baca/edit file, jalankan perintah, cari kode, kelola konteks, dan simpan memori.
Apa yang Bisa Ditambahkan Selanjutnya
-
Sub-agen untuk pekerjaan paralel: Munculkan
agent_loop()baru untuk task independen, lalu gabungkan hasilnya. - Deduplikasi pembacaan file: Track file yang sudah dibaca, skip jika tidak berubah.
- Pemotongan output alat: Untuk output besar, potong dan beri tahu model jumlah baris yang di-truncate.
- Kompresi otomatis dengan injeksi ulang file: Setelah kompresi, injeksi ulang isi file yang diakses (maks 5.000 token/file).
Pelajaran dari Kebocoran
- Loop inti agen sangat sederhana (30 baris)
- Alat khusus jauh lebih efektif dari bash
- Memori perlu sistem berlapis
- Manajemen konteks adalah kunci
- Produk utamanya adalah harness, bukan model
Jika ingin menguji dan debug interaksi API multi-giliran, skema kompleks, dan validasi respons, coba Apidog secara gratis. Debugging API jadi mudah, Anda bisa fokus pada logika agen.
FAQ
Bisakah saya secara legal menggunakan pola dari kebocoran Kode Claude?
Kebocoran ini mengungkap pola arsitektur, bukan algoritma proprietary. Loop while dengan dispatch alat adalah pola standar (lihat dokumentasi API Anthropic). Jangan salin kode verbatim, tapi reimplementasi arsitektur sendiri adalah praktik wajar.
Model apa yang cocok untuk agen pengode DIY?
Claude Sonnet 4.6 seimbang (kecepatan & kemampuan). Untuk tugas kompleks, Opus 4.6 lebih baik (tapi mahal/lambat). Untuk editing/cari file sederhana, Haiku 4.5 cukup dan biaya murah.
Berapa biaya menjalankan agen pengode sendiri?
Sesi 30-50 giliran (Claude Sonnet 4.6) sekitar $1-5. Biaya utama dari ukuran konteks; kompresi agresif menjaga biaya rendah. Claude Code memicu kompresi di 92% penggunaan konteks.
Mengapa Claude Code memakai React untuk aplikasi terminal?
Ink (React untuk terminal) memungkinkan reuse komponen dan state management React. Untuk DIY, REPL sederhana (input() / print()) sudah cukup.
Fitur apa yang paling penting ditambahkan setelah loop inti?
Sistem izin. Tanpa izin, model bisa overwrite file/jalankan perintah arbitrer. Konfirmasi sederhana sebelum tulis/eksekusi mencegah kerusakan.
Bagaimana Claude Code menangani error dari tool call?
Error alat dikembalikan sebagai konten teks di pesan tool_result. Model memutuskan retry/cara lain/tanya user. Tidak ada penanganan error khusus; model yang memutuskan.
Bisakah pola ini dipakai di model lain selain Claude?
Bisa. Pattern function calling didukung GPT-4, Gemini, Llama, dll. Format API perlu adaptasi, tapi arsitektur loop/alat/memori tetap.
Bagaimana cara mencegah agen menjalankan perintah berbahaya?
Gunakan daftar blokir (rm -rf /, mkfs, dll) dan konfirmasi eksplisit pada semua run_command. Klasifikasikan risiko (RENDAH, SEDANG, TINGGI), lalu blokir/tanya user sesuai level. Terapkan pola yang sama untuk alat Anda.

Top comments (0)