موجز سريع
كشف تسريب شيفرة مصدر Claude Code أظهر قاعدة شيفرة TypeScript بحجم 512,000 سطر في 31 مارس 2026. البنية قائمة على حلقة تكرارية (while-loop) تستدعي واجهة Claude API، ترسل استدعاءات الأدوات، وتعيد النتائج. يمكنك بناء نسخة خاصة بك باستخدام بايثون، وSDK الخاص بـ Anthropic، وحوالي 200 سطر فقط للحلقة الأساسية. هذا الدليل يشرح كل مكون ويعطيك خطوات عملية لإعادة البناء.
مقدمة
في 31 مارس 2026، أصدرت Anthropic ملف خريطة مصدر بحجم 59.8 ميجابايت ضمن الإصدار 2.1.88 من حزمة npm الخاصة بها @anthropic-ai/claude-code. خرائط المصدر هذه سمحت باسترداد قاعدة شيفرة TypeScript بالكامل بسهولة بسبب إعدادات مجمع Bun الافتراضية.
خلال ساعات، انتشرت الشيفرة في العديد من مستودعات GitHub. تم تحليل كل وحدة بسرعة: من حلقة الوكيل الأساسية إلى ميزات مثل "وضع التخفي" وحقن الأدوات المزيفة.
رد الفعل انقسم بين انتقاد الممارسات الأمنية والانبهار بالهندسة. النتيجة العملية: "هل يمكنني بناء هذا بنفسي؟" الجواب: نعم، بنمط بسيط وعملي. هذا الدليل يشرح كيف تبني وكيل برمجة عامل خطوة بخطوة، ويعرض شيفرة حقيقية كنقطة بداية، ويوضح كيف تختبر وكلائك باستخدام Apidog لتسهيل تصحيح أخطاء محادثات API متعددة الأدوار.
ما كشفه التسريب عن بنية Claude Code
نظرة سريعة على قاعدة الشيفرة
Claude Code (الاسم الرمزي "Tengu") يمتد عبر 1900 ملف بتنظيم وحدات واضح:
cli/ - واجهة المستخدم الطرفية (React + Ink)
tools/ - أكثر من 40 أداة
core/ - موجهات النظام، الأذونات، الثوابت
assistant/ - تنسيق الوكيل
services/ - استدعاءات API، الضغط، OAuth، القياس عن بعد
واجهة CLI هي تطبيق React عبر Ink، تستخدم Yoga للتخطيط وANSI للتصميم. كل عنصر هو مكون React.
للتطبيقات العملية: لا تحتاج CLI مبنية على React. يكفي REPL بسيط بـ input() و print().
حلقة الوكيل الرئيسية
جوهر Claude Code هو حلقة تكرارية (while-loop) بسيطة:
- إرسال الرسائل إلى Claude API (موجه النظام + تعريفات الأدوات)
- استقبال استجابة (نص و/أو كتل
tool_use) - تنفيذ كل أداة مطلوبة عبر توزيع الاسم إلى المعالج
- إضافة نتائج الأداة إلى الرسائل
- إذا هناك استدعاءات أدوات أخرى: كرر من الخطوة 1
- إذا فقط نص: أعده للمستخدم
مثال عملي بالبايثون:
import anthropic
client = anthropic.Anthropic()
MODEL = "claude-sonnet-4-6"
def agent_loop(system_prompt: str, tools: list, messages: list) -> str:
"""الحلقة الأساسية للوكيل - تستمر حتى لا يوجد طلب أداة."""
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})
بقية التعقيد في الأدوات، الأذونات، والسياق.
بناء نظام الأدوات
لماذا الأدوات المخصصة أفضل من Bash
Claude Code يستخدم أدوات مخصصة مثل Read, Edit, Grep, Glob بدل أوامر Bash مباشرة. الأسباب:
- مخرجات منظمة: الأدوات تعطي نواتج ثابتة يسهل تحليلها، على عكس نواتج Bash المتقلبة.
- الأمان: BashTool تمنع أنماطًا خطيرة كالاقتباس العكسي. الأدوات المخصصة أكثر أمانًا.
- كفاءة الرموز: يمكن تقطيع نتائج الأدوات أو أخذ عينات منها بسهولة.
مجموعة الأدوات الأساسية
للوكيل البرمجي الأساسي، تحتاج فقط هذه الأدوات الخمسة:
TOOLS = [
{
"name": "read_file",
"description": "قراءة ملف من النظام مع ترقيم الأسطر.",
"input_schema": {
"type": "object",
"properties": {
"file_path": {"type": "string", "description": "المسار المطلق"},
"offset": {"type": "integer", "description": "من أين تبدأ القراءة"},
"limit": {"type": "integer", "description": "أقصى عدد أسطر"}
},
"required": ["file_path"]
}
},
{
"name": "write_file",
"description": "كتابة محتوى لملف (ينشئ الملف إذا لم يوجد).",
"input_schema": {
"type": "object",
"properties": {
"file_path": {"type": "string", "description": "المسار المطلق"},
"content": {"type": "string", "description": "المحتوى"}
},
"required": ["file_path", "content"]
}
},
{
"name": "edit_file",
"description": "استبدال سلسلة نصية محددة في ملف.",
"input_schema": {
"type": "object",
"properties": {
"file_path": {"type": "string", "description": "المسار المطلق"},
"old_string": {"type": "string", "description": "النص المراد تغييره"},
"new_string": {"type": "string", "description": "النص البديل"}
},
"required": ["file_path", "old_string", "new_string"]
}
},
{
"name": "run_command",
"description": "تنفيذ أمر shell مع إرجاع stdout/stderr.",
"input_schema": {
"type": "object",
"properties": {
"command": {"type": "string", "description": "الأمر"},
"timeout": {"type": "integer", "description": "مهلة التنفيذ"}
},
"required": ["command"]
}
},
{
"name": "search_code",
"description": "البحث بنمط regex عبر ملفات في مجلد.",
"input_schema": {
"type": "object",
"properties": {
"pattern": {"type": "string", "description": "نمط regex"},
"path": {"type": "string", "description": "المجلد"},
"file_glob": {"type": "string", "description": "تصفية الملفات مثل *.py"}
},
"required": ["pattern"]
}
}
]
توزيع معالج الأدوات
اربط أسماء الأدوات بالدوال المقابلة:
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)
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
إدارة السياق: المشكلة الصعبة
لماذا إدارة السياق أهم من هندسة الموجهات
Claude Code يركز كثيرًا على إدارة السياق، خاصة عند الاقتراب من حد نافذة الرموز. الاستراتيجيتان الأساسيتان:
- الضغط التلقائي عند 92% من السعة.
- إعادة حقن CLAUDE.md في كل دورة للحفاظ على ثبات إرشادات المشروع.
بناء ضاغط سياق بسيط
def maybe_compact(messages: list, system_prompt: str, max_tokens: int = 180000) -> list:
"""ضغط المحادثة عند الاقتراب من الحد."""
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
إعادة حقن سياق المشروع
اقرأ ملف .claude/CLAUDE.md وأعد حقنه مع كل دورة:
def build_system_prompt(project_dir: str) -> str:
"""بناء موجه النظام مع إعادة حقن سياق المشروع."""
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
أضف أداة save_memory لقائمة أدواتك من أجل الاحتفاظ بالمعرفة بين الجلسات.
نظام الذاكرة ثلاثي الطبقات
الطبقة 1: MEMORY.md (محمل دائمًا)
فهرس بسيط في موجه النظام، كل إدخال أقل من 150 حرفًا. مثال:
- [تفضيلات المستخدم](memory/user-prefs.md) - يفضل TypeScript، ويستخدم ربط مفاتيح Vim
- [اتفاقيات API](memory/api-conventions.md) - REST مع مواصفات JSON:API، snake_case
- [عملية النشر](memory/deploy.md) - تستخدم GitHub Actions، وتنشر على AWS EKS
الطبقة 2: ملفات المواضيع (عند الطلب)
ملفات تحتوي تفاصيل مثل اتفاقيات المشروع وقرارات التصميم، تُحمّل فقط عند الحاجة.
الطبقة 3: سجلات الجلسات (للبحث فقط)
سجلات كاملة للجلسات لا تُقرأ كاملة أبدًا، بل يُبحث فيها عن معرّفات محددة فقط.
بناء نظام ذاكرة بسيط
import json
MEMORY_DIR = ".agent/memory"
def load_memory_index() -> str:
"""تحميل فهرس الذاكرة لحقنه في موجه النظام."""
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):
"""حفظ إدخال ذاكرة وتحديث الفهرس."""
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)
إضافة نظام أذونات
استخدم نظام تصنيف بسيط (منخفض/متوسط/مرتفع):
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:
"""تحقق من موافقة المستخدم على استدعاء الأداة."""
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"
اختبار استدعاءات API لوكيلك باستخدام Apidog
بناء وكيل برمجة يتطلب مئات استدعاءات API. تصحيح أخطاء هذه التفاعلات يصبح أكثر سهولة مع أدوات مثل Apidog.
يساعدك Apidog في فحص واختبار طلبات API بدقة. خطوات عملية:
التقاط وإعادة تشغيل طلبات API
- افتح Apidog وأنشئ مشروعًا جديدًا.
- استورد نقطة نهاية Anthropic Messages API:
POST https://api.anthropic.com/v1/messages - أضف نص الطلب مع موجهك، الأدوات، والرسائل.
- اختبر دورات فردية بإعادة تشغيل الطلبات مع تعديلات.
يمكنك عزل واختبار استدعاءات أداة معينة، وتعديل المدخلات مباشرة من واجهة Apidog.
تصحيح أخطاء المحادثات متعددة الأدوار
- احفظ مصفوفة
messagesكمتغير بيئة بعد كل دورة. - أعد التشغيل من أي نقطة في المحادثة.
- قارن نتائج الأدوات بين عمليات التشغيل.
التحقق من صحة مخططات الأدوات
تعريفات الأدوات (JSON Schemas) يجب أن تكون دقيقة. استوردها إلى Apidog واستخدم مدقق JSON Schema لاكتشاف الأخطاء مبكرًا.
تجميع الكل: REPL الكامل
وكيل برمجة كامل (REPL) بأقل من 300 سطر بايثون:
#!/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()
الوكيل يقرأ ويعدل الملفات، ينفذ الأوامر، يبحث في الشيفرة، يدير السياق، ويحتفظ بالذاكرة.
ماذا تضيف لاحقًا
ميزات متقدمة لبنائها بعد الحلقة الأساسية:
الوكلاء الفرعيون للعمل المتوازي
استدع وكلاء فرعيين بمهام مستقلة عبر استدعاء agent_loop() جديد بسياق فرعي.
إلغاء تكرار قراءة الملفات
تتبع الملفات وأوقاتها لتوفير الرموز عند إعادة القراءة.
اقتطاع وأخذ عينات من المخرجات
اقتطع نتائج الأدوات الضخمة، وأخبر النموذج بعدد النتائج المحذوفة.
الضغط التلقائي مع إعادة حقن الملفات
بعد التلخيص، أعد حقن محتويات الملفات التي تم الوصول إليها مؤخرًا.
ما تعلمناه من التسريب
- الحلقة الأساسية بسيطة: نمط الوكيل في 30 سطر فقط.
- الأدوات المخصصة أفضل من Bash: مخرجات منظمة وأكثر أمانًا.
- الذاكرة تحتاج إلى طبقات: فهرس دائم، وملفات عند الطلب، وسجلات للبحث فقط.
- إدارة السياق هي المنتج الحقيقي: الضغط، إعادة الحقن، واقتطاع المخرجات.
- السرج (Harness) هو المنتج: النموذج ذكي، لكن السرج يضيف الإدراك والعمل والذاكرة.
لتصحيح تفاعلات API لوكيلك، خاصة متعددة الأدوار والأدوات، واختبار مخططات الطلبات والاستجابات، جرّب Apidog مجانًا. ركز على منطق الوكيل ودع Apidog يهتم بتصحيح الواجهات.
الأسئلة الشائعة
هل يمكنني استخدام أنماط من تسريب شيفرة Claude Code بشكل قانوني؟
الأنماط المعمارية عامة وموثقة في وثائق Anthropic. لا تقم بنسخ الشيفرة حرفيًا، لكن يمكنك البناء بنفس الأسلوب.
ما النموذج الذي يجب أن أستخدمه لوكيل برمجة DIY؟
Claude Sonnet 4.6 متوازن بين السرعة والقدرة. Opus 4.6 أدق وأغلى، Haiku 4.5 أرخص وأسرع لتعديلات الملفات والبحث.
كم تكلفة تشغيل وكيل البرمجة الخاص بك؟
جلسة 30-50 دورة مع Sonnet 4.6 تكلف 1-5 دولار. الضغط القوي يخفض التكاليف.
لماذا يستخدم Claude Code React لتطبيق طرفي؟
Ink (React للطرفيات) يسمح بإدارة واجهات معقدة. لمشروع عملي، يكفي REPL بسيط بـ input() وprint().
ما أهم ميزة يجب بناؤها بعد الحلقة الأساسية؟
نظام الأذونات. بدونه قد يعدل النموذج ملفات أو ينفذ أوامر دون رقابة. حتى بوابة تأكيد بسيطة تمنع الأضرار.
كيف يتعامل Claude Code مع أخطاء استدعاءات الأدوات؟
الأخطاء ترجع كنص في tool_result، ويقرر النموذج كيف يتصرف (إعادة المحاولة أو طلب مدخلات).
هل يمكنني استخدام هذا مع نماذج غير Claude؟
نعم، أي نموذج يدعم استدعاء الوظائف (GPT-4، Gemini، Llama...). فقط عدل تنسيق استدعاء API.
كيف أمنع الوكيل من تشغيل أوامر خطيرة؟
استخدم قائمة حظر لأنماط خطيرة (rm -rf /، mkfs...) واطلب موافقة المستخدم لأوامر run_command. اصنف كل عملية حسب الخطر.
إذا أردت تصحيح وتطوير وكلائك البرمجية بسهولة، خاصة مع محادثات وأدوات متعددة، جرّب Apidog اليوم.

Top comments (0)