تعديلات Claude Code: ١٠ حالات استخدام مُجربة (٢٠٢٦)
٩ أكتوبر ٢٠٢٦

تعد إضافات Claude Code (المعروفة بـ mods) عبارة عن ملحقات تعمل وظائف الـ JavaScript أو TypeScript الخاصة بها داخل Claude Code عند وقوع حدث معين: استدعاء أداة، أو مطالبة (prompt)، أو دورة (turn)، أو إعادة رسم. أدناه يوجد 10 إضافات تعمل لمهام شائعة، بدءاً من حماية الأوامر الخطيرة وصولاً إلى رسم الأجزاء (panes)، وكل واحدة منها تجتاز claude plugin test.
ملخص
أضافت Anthropic الإضافات (mods) إلى Claude Code في 1 أكتوبر 2026، في الإصدار 2.1.287.12 تقوم الإضافة بربط حدث معين ويمكنها مراقبته، أو إعادة كتابته، أو الرد عليه بدلاً من Claude Code، كما يمكنها رسم أجزاء (panes)، وأشرطة (bands)، وخطوط حالة خاصة بها.3
لقد كتبنا 10 إضافات للمهام اليومية وتحققنا منها على Claude Code 2.1.295. جميع الإضافات العشر تجتاز claude plugin validate، وجميع اختبارات الوحدة الـ 25 تجتاز بنجاح، كما أن ستة منها عملت كما هو متوقع في عمليات تشغيل claude -p حية. الكود أدناه منسوخ من الملفات التي تم اختبارها.
تعمل الإضافات بصلاحياتك وليست معزولة (sandboxed)، لذا اقرأ الإضافة قبل تثبيتها.3
ما ستتعلمه
- ما هي إضافة Claude Code، ومتى تختارها بدلاً من خطافات الإعدادات (settings hooks)، أو المهارات (skills)، أو خادم MCP
- الملفات الثلاثة التي تحتاجها كل إضافة، وكيفية تحميلها والتحقق منها واختبارها
- 10 حالات استخدام مختبرة مع الكود الكامل: حواجز الأمان، سياق المطالبة، الأوامر، الأجزاء، أداة مخصصة ومراقب خلفي
- ما الذي تغير في الأسبوع الأول بعد الإطلاق، والقيود التي قد تسبب ارتباكاً للمستخدمين
- كيف تتحكم الفرق والمؤسسات في الإضافات التي يتم تشغيلها
ما هي إضافة Claude Code؟
إضافة Claude Code هي ملحق يقوم الكود الخاص به بتسجيل معالجات للأحداث، تسمى خطافات (hooks).
توضح وثائق Anthropic الأمر كالتالي: "الإضافة هي ملحق يغير مظهر وسلوك Claude Code. وهي تتكون من معالجات أحداث JavaScript أو TypeScript."3 ويصفها الإعلان بأنها "وظائف TypeScript صغيرة" يمكنها إعادة كتابة مطالبة، أو إضافة واجهة مستخدم جديدة، أو استبدال ميزة مدمجة، أو إضافة وظائف جديدة.1
يحصل كل خطاف على نفس الوسائط الثلاثة:4
$، وهو API الخاص بالإضافات: كل طريقة يمكن للإضافة استدعاؤها، مجمعة في مساحات أسماء مثل$.uiو$.fsو$.processe، وهو الحدث: بيانات بسيطة ومجمدة مثل اسم استدعاء الأداة ووسائطهاnext، وهو المعالج التالي: يقوم بتمرير الحدث إلى الإضافات الأخرى ثم إلى سلوك Claude Code نفسه
ما يفعله الخطاف بـ next هو ما يحدد نوع الخطاف. إذا قمت بإرجاع next(e) فهو يقوم فقط بـ المراقبة. وإذا مررت لـ next نسخة معدلة فهو يقوم بـ إعادة كتابة الحدث. أما إذا أرجعت نتيجة دون استدعاء next فهو يقوم بـ الرد على الحدث بنفسه، وبالتالي لا يتم تشغيل سلوك Claude Code الأصلي أبداً.5
الإضافات مقابل خطافات الإعدادات، المهارات وخوادم MCP
كان Claude Code يحتوي بالفعل على خطافات إعدادات، ومهارات، وخوادم MCP، وتقارن الوثائق بين الأربعة. باختصار: الإضافة هي الوحيدة التي تعمل داخل عملية (process) Claude Code والوحيدة التي يمكنها الرسم في واجهة المستخدم.3
| Mod | Settings hook | Skill | MCP server | |
|---|---|---|---|---|
| ما هو | وظائف في إضافة (plugin) يستدعيها Claude Code في عمليته الخاصة | أمر shell، أو طلب HTTP، أو prompt يقوم Claude Code بتشغيله عند حدث في دورة الحياة (lifecycle event) | ملف تعليمات SKILL.md يقرأه Claude | عملية أو خدمة خارجية تمنح Claude أدوات |
| ما الذي يمكنه تغييره | استدعاءات الأدوات، الـ prompts، الأوامر، الأدوار (turns)، وما ترسمه الواجهة | ما إذا كان استدعاء الأداة أو الـ prompt سيستمر، وسائط ونتيجة استدعاء الأداة، والسياق المضاف لـ Claude | ما يعرفه Claude وما يفعله | الأدوات التي يمتلكها Claude |
| هل يمكنه الرسم في الواجهة | نعم | لا | لا | لا |
| ما الذي تكتبه | JavaScript أو TypeScript | سكربت بالإضافة إلى إدخال في settings.json | Markdown | سيرفر بأي لغة برمجة |
| اختره عندما | تريد لوحة (pane)، أو شريطاً فوق الـ prompt، أو أمراً مخصصاً، أو إعادة كتابة حدث ما | تريد حظر أو السماح أو تسجيل حدث باستخدام سكربت لديك بالفعل | تستمر في لصق نفس التعليمات في الدردشة | يحتاج Claude للوصول إلى نظام خارجي |
مقتبس من وثائق Claude Code، "Mods overview".3 يمكن للإضافة أن تحتوي على العناصر الأربعة معاً، لذا يمكن شحن mod بجانب skill و MCP server.
كيف تقوم ببناء واختبار Claude Code mod؟
تحتاج إلى Claude Code 2.1.287 أو إصدار أحدث في الـ terminal. علامة تبويب Code في تطبيق Desktop تشغل الـ mods من إصدار 2.1.286.3 لا تحتاج إلى Node.js أو خطوة بناء (build step)، لأن Claude Code يحمل ملفات .js و .ts مباشرة.4
يتكون الـ mod من ثلاثة ملفات:46
my-mod/
├── .claude-plugin/
│ └── plugin.json # the plugin manifest: name, version, description
└── hooks/
├── hooks.json # {"modules": ["./register.js"]} points at your code
└── register.js # your hooks module, which exports register(on)
كل mod في هذا المنشور يستخدم نفس ملفي JSON، مع تغيير الاسم والوصف فقط. إليك الملفات الخاصة بالأول:
{
"name": "confirm-risky",
"version": "0.1.0",
"description": "Asks before Claude runs a destructive shell command",
"author": { "name": "NerdLevelTech" }
}
{
"modules": ["./register.js"]
}
ثلاثة أوامر تغطي الدورة الكاملة:476
# Load the mod for one session; Claude Code hot-reloads it when you save
claude --plugin-dir ./confirm-risky
# See what Claude Code reads from the mod: the events it hooks and the API calls it makes
claude plugin validate ./confirm-risky
# Run the mod's *.test.ts files with no session, sign-in or network
cd confirm-risky && claude plugin test
يمكنك أيضاً وصف الـ mod الذي تريده في جلسة Claude Code وتترك Claude يكتبه. يستخدم Claude مهارة (skill) مدمجة تسمى plugin-authoring لهذا الغرض، ولا يعمل الـ mod إلا بعد موافقتك عليه.4
كيف اختبرنا هذه الـ 10 mods
كتبنا كل mod يدوياً من الوثائق الرسمية وتصريحات الأنواع (type declarations) التي يشحنها Claude Code لإصداره، ثم أجرينا ثلاثة فحوصات في 9 أكتوبر 2026 باستخدام Claude Code 2.1.295:
claude plugin validateعلى كل mod. اجتازت جميع الـ 10 الفحص.claude plugin testمع 2 إلى 4 اختبارات لكل mod، بإجمالي 25 اختباراً. اجتازت جميع الـ 25 الفحص.- تشغيل مباشر لـ
claude -pباستخدام نموذج Haiku في مستودع git مؤقت، لسبعة mods. ستة منها عملت كما هو متوقع. السابع، وهو turn receipt، اجتاز اختباراته، ولكنclaude -pيطبع الإجابة فقط، لذا لا يظهر السطر الخاص به هناك وقد تحققنا منه عبر الاختبارات فقط.
تظهر الـ Mods التي تقوم بالرسم فقط في الطرفية (terminal) التفاعلية أو تطبيق سطح المكتب.3 لقد قمنا برسم الشريط واللوحة باستخدام $.ui.mount الخاصة بمجموعة أدوات الاختبار، والتي تقوم بتصيير موقع بدون جلسة، وتحققنا من سطر الحالة عن طريق عمل stub لـ $.ui.status.7
الاختبار هو ملف TypeScript يقوم بإثارة الأحداث وعمل stub لأي شيء قد يجيب عليه Claude Code. هذا هو الاختبار الخاص بـ push guard في حالة الاستخدام 2. يقوم بالرد على $.process.run باسم فرع وهمي، لذا لا يتم تشغيل أي git:7
import { expect, test } from 'claude-code/testing'
// Stubs what the permission rules decided and what git says the current branch is,
// and counts how often the mod ran git
function stubs(on, branch: string, rules = 'allow') {
const runs = { git: 0 }
on('tool.check', () => ({ decision: rules }))
on('process.run', () => {
runs.git += 1
return { value: { exitCode: 0, stdout: branch + '\n', stderr: '' } }
})
return runs
}
test('git push while main is checked out is denied', async ($, on) => {
stubs(on, 'main')
const out = await $.tool.check({ tool: 'Bash', input: { command: 'git push' } })
expect(out.decision).toBe('deny')
})
test('a push that targets main from another branch is denied', async ($, on) => {
stubs(on, 'feature/login')
for (const command of ['git push origin main', 'git push origin HEAD:main', 'git push origin +main', 'git push origin feature/login:refs/heads/main', 'git push --force origin main;', 'git push origin "main"']) {
const out = await $.tool.check({ tool: 'Bash', input: { command } })
expect(out.decision).toBe('deny')
}
})
test('a feature branch push keeps the rules decision', async ($, on) => {
stubs(on, 'feature/login')
const out = await $.tool.check({ tool: 'Bash', input: { command: 'git push origin feature/login' } })
expect(out.decision).toBe('allow')
})
test('other commands never run git', async ($, on) => {
const runs = stubs(on, 'main', 'ask')
const out = await $.tool.check({ tool: 'Bash', input: { command: 'npm test' } })
expect(out.decision).toBe('ask')
expect(runs.git).toBe(0)
})


1. السؤال قبل أن يقوم Claude بتشغيل أمر shell تدميري
استخدم هذا إذا كنت تسمح لـ Claude بتشغيل أوامر shell ولكنك تريد مراجعة بشرية للأوامر التي لا يمكن التراجع عنها.
يمكن لـ hook من نوع tool.call إيقاف استدعاء الأداة بينما يطرح عليك سؤالاً. يقوم $.ui.ask بإظهار السؤال في الحوار الذي يستخدمه Claude للأسئلة وينتهي بالوصول إلى التسمية (label) التي تختارها. يتم رفض الطلب عندما تغلقه، وفي تشغيل claude -p حيث لا يمكن لأحد الإجابة.5 لذا يبدأ الـ hook من "الرفض" (Refuse) ولا يشغل الأمر إلا عند اختيار "تشغيله" (Run it) بشكل صريح.
// Commands worth a second look: recursive deletes (rm -r, -R, -rf, -f -r, rm dir -rf, --recursive),
// hard resets, and force pushes (--force, -f, or a +branch refspec)
const RISKY = [
/\brm\b.*\s-[a-zA-Z]*[rR][a-zA-Z]*(?=\s|$)/,
/\brm\b.*\s--recursive\b/,
/\bgit\s+reset\s+--hard\b/,
/\bgit\s+push\b.*(?:\s--force\b|\s-f\b|\s\+\S)/,
]
export function register(on) {
on('tool.call', { tool: 'Bash' }, async ($, e, next) => {
// Everything else runs as usual
if (!RISKY.some((pattern) => pattern.test(e.command))) return next(e)
// Start from the safe answer, so a question nobody answers refuses the command
let answer = 'Refuse'
try {
// The tool call waits here until you pick an option; Refuse is listed first
answer = await $.ui.ask('Run this command? ' + e.command, ['Refuse', 'Run it'])
} catch {
// Dismissed, or a claude -p run with nobody to ask
}
if (answer !== 'Run it') {
return { deny: 'The user declined this command. Ask before trying another approach.' }
}
return next(e)
}).catch(async ($, e, next) => {
// If the guard itself fails before the command ran, refuse instead of failing open
return next.called ? next(e) : { deny: 'The confirm-risky guard failed, so this command was not run.' }
})
}
تعتبر .catch مهمة هنا. أي hook يتسبب في خطأ (throw) أو ينتهي وقته قبل استدعاء next يتم تخطيه، وسيعمل الأمر. يقوم معالج .catch بالرد بدلاً منه، لذا فإن هذا الحارس يفشل في حالة الإغلاق (fails closed).5
تغطي ثلاثة اختبارات ستة أوامر آمنة يجب ألا تثير سؤالاً (من بينها rm notes.txt و docker rm -f web و npm run build -- --force-color)، وثمانية أوامر خطيرة يجب رفضها (rm -rf و rm -Rf و rm -R و rm -f -r و rm build -rf و rm --recursive و git reset --hard و force push لـ +main)، وأمر git push -f معتمد.
في تجربة حية، طلبنا من Claude تشغيل rm build -rf مع تحميل الـ mod. رد Claude قائلاً "تم رفض الأمر، لذا لم يتم تشغيل أي شيء و build لم يمس". وكان مجلد build لا يزال موجوداً.
تنبيه واحد: الأنماط تعتمد على مطابقة النصوص. فهي تغفل الأوامر التدميرية التي لا تشبه هذه الأنماط، مثل find . -delete أو سكربت يقوم بحذف الملفات، لذا تعامل مع الـ mod كمطب للسرعة وليس كبيئة معزولة (sandbox). يمكنه أيضاً السؤال عن أوامر غير ضارة، مثل تشغيل grep -r بعد git rm. تم وضع "الرفض" (Refuse) أولاً عن قصد، حتى لا يؤدي الضغط غير المقصود على Enter إلى تشغيل الأمر.
2. منع الـ pushes إلى main، بغض النظر عما تقوله القواعد
هذا مخصص للفرق التي تريد من Claude أن يقوم بعمل push لفروع الميزات (feature branches) ولكن ليس لـ main أبداً.
يتم إطلاق tool.check بعد أن تقرر قواعد الأذونات و hooks الإعدادات ما إذا كان الاستدعاء يمكن أن يعمل، وينتهي next(e) بهذا القرار: allow (سماح)، أو ask (سؤال)، أو deny (رفض). يمكن للـ hook أن يعيد قراراً مختلفاً.5
وهذا يجعلها الحدث المناسب لفحص يعتمد على ما هو صحيح في الوقت الحالي، مثل الفرع الحالي. يرفض هذا التعديل (mod) أي عملية push تحدد main كهدف لها (origin main، أو HEAD:main، أو +main، أو refs/heads/main أو "main" بين علامتي تنصيص)، وأي عملية push بينما يكون main هو الفرع النشط (checked out).
export function register(on) {
// tool.check fires after your permission rules and settings hooks have decided
on('tool.check', { tool: 'Bash' }, async ($, e, next) => {
// What the rules decided: allow, ask, or deny
const decided = await next(e)
const command = e.input.command
if (!/\bgit\s+push\b/.test(command)) return decided
// A push that names main as its target: "origin main", "HEAD:main", "+main", "refs/heads/main", "'main'"
const namesMain = /\bgit\s+push\b.*(?:[\s:+'"]|refs\/heads\/)main(?=$|[\s;&|'"])/.test(command)
// Or any push while main is checked out
const branch = await $.process.run(['git', 'branch', '--show-current'])
const onMain = branch.exitCode === 0 && branch.stdout.trim() === 'main'
if (!namesMain && !onMain) return decided
return { decision: 'deny', reason: 'Pushing to main is blocked here. Push a feature branch and open a pull request instead.' }
}).catch(async ($, e, next) => {
// If the check fails, for example git can't run, refuse the push rather than let it through
return { decision: 'deny', reason: 'push-guard could not check this push (' + next.error.kind + '), so it was refused.' }
})
}
هناك أربعة اختبارات تحاكي اسم الفرع وقرار القواعد، واختبار واحد يحسب عدد مرات تشغيل git لإثبات أن الأوامر الأخرى لا تقوم بتشغيله أبداً.
في التجربة الحية، ومع السماح بـ Bash(git push:*)، أدى تنفيذ git push origin main على فرع main إلى ظهور رسالة "Permission to use Bash denied by plugin push-guard: Pushing to main is blocked here. Push a feature branch and open a pull request instead."
ومن فرع يسمى feature/demo، حصلت أوامر git push origin HEAD:main و git push origin +main على نفس الرفض، ولم يحتوي المستودع البعيد (remote) إلا على فرع feature/demo.
أما عملية push لفرع feature/demo نفسه فقد كانت مسموحة وأبلغت بأن "Everything up-to-date".
ترفض .catch عملية الـ push إذا فشل الفحص نفسه، على سبيل المثال عندما لا يمكن تشغيل git. ومع ذلك، تقول الوثائق بوضوح أن الـ hook الذي يطابق نص الأمر هو مجرد تذكير لـ Claude. فأي صيغة لا يتوقعها النمط، مثل git -C . push، ستمر. كما يمكن رفض أمر غير ضار أيضاً، مثل git push origin feature/login && git log main. لمنع عمليات الـ push إلى main للجميع، قم بحماية الفرع على مستضيف Git الخاص بك.5
3. إخفاء الأسرار في مخرجات الأوامر قبل أن يقرأها Claude
استخدم هذا الحل عندما يمكن أن تحتوي مخرجات الـ shell على بيانات اعتماد، مثل ملفات تكوين السحابة أو تفريغ لبيانات البيئة (environment dumps).
يسمح هذا الـ hook بتشغيل الأمر، ثم يعيد كتابة ما نتج عنه. تذكر الإعلانات أن تنقيح الأسرار من مخرجات الأدوات هو أحد الأشياء التي يمكن للتعديل (mod) القيام بها باستخدام دالة واحدة.1 إن إرجاع كائن result جديد، بدلاً من الكائن الذي قدمه لك next، هو ما يجعل Claude يقرأ نسختك أنت.
الأمر الفاشل يحتاج إلى مسار معالجة خاص به. تعود مخرجاته مع تعيين isError والنص الذي يقرأه Claude يكون في text، وليس في stdout.8 يقوم التعديل بإخفاء هذا النص وإرجاعه كـ deny، بحيث لا يقرأ Claude إلا النص المخفي.
// Common credentials: AWS key ids (AKIA, ASIA), AWS secret keys and session tokens,
// GitHub tokens, Slack tokens, and private key blocks
const SECRETS = [
[/\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g, '[REDACTED]'],
[/\b(aws_secret_access_key|aws_session_token)(\s*[=:]\s*)\S+/gi, '$1$2[REDACTED]'],
[/\bgh[pousr]_[A-Za-z0-9]{36,}\b/g, '[REDACTED]'],
[/\bgithub_pat_[A-Za-z0-9_]{22,}\b/g, '[REDACTED]'],
[/\bxox[abprs]-[A-Za-z0-9-]{10,}\b/g, '[REDACTED]'],
[/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g, '[REDACTED]'],
]
function redact(text) {
return SECRETS.reduce((out, [pattern, replacement]) => out.replace(pattern, replacement), text)
}
export function register(on) {
on('tool.call', { tool: 'Bash' }, async ($, e, next) => {
// Let the command run, then look at what it printed
const ran = await next(e)
// A refused call has nothing to clean
if (ran.deny !== undefined) return ran
if (ran.isError) {
// A failed command's output reaches Claude as error text
const text = typeof ran.text === 'string' ? ran.text : typeof ran.result === 'string' ? ran.result : ''
const masked = redact(text)
if (masked === text) return ran
$.ui.log('masked a credential in the error output of: ' + e.command.slice(0, 60))
// An error result can't be rewritten in place, so withhold it and pass on the masked text
return { deny: masked }
}
// Another mod may have answered with a plain string; leave that alone
if (typeof ran.result !== 'object' || ran.result === null) return ran
const stdout = redact(ran.result.stdout ?? '')
const stderr = redact(ran.result.stderr ?? '')
if (stdout === ran.result.stdout && stderr === ran.result.stderr) return ran
$.ui.log('masked a credential in the output of: ' + e.command.slice(0, 60))
// Return a new result, so Claude reads the masked text and not the original
return { result: { ...ran.result, stdout, stderr } }
}).catch(async () => {
// If masking itself fails, withhold the output rather than pass it on unmasked
return { deny: 'mask-secrets could not check this output, so it was withheld.' }
})
}
تتحقق أربعة اختبارات من إخفاء معرف مفتاح AWS والمفتاح السري، ومن مرور المخرجات النظيفة والأخطاء النظيفة دون تغيير، ومن إخفاء المفتاح في الأمر الفاشل، ومن قيام .catch بحجب المخرجات إذا فشلت عملية الإخفاء.
في التجربة الحية، وضعنا ملف بيانات اعتماد AWS وهمي في creds.txt وطلبنا من Claude اقتباس cat creds.txt__PRESERSEVERE__ حرفياً. مع وجود التعديل، اقتبس Claude aws_access_key_id = [REDACTED] و aws_secret_access_key = [REDACTED]. وبدونه، اقتبست محاولة سابقة لنفس الأمر معرف المفتاح الوهمي كاملاً. كما أن الأمر الفاشل، cat creds.txt; exit 3، عاد مخفياً أيضاً.
هذا يغطي مخرجات Bash والأنماط المدرجة فقط. لا يزال بإمكان Claude فتح نفس الملف باستخدام أداة Read، لذا قم بدمج هذا الحل مع حماية لعمليات القراءة. يوضح منشورنا السابق أحد هذه الحلول: تعديل لـ Claude Code يمنع قراءة ملفات .env.
4. إضافة فرع git الحالي إلى المطالبات المتعلقة بطلبات السحب (pull requests)
هذا يوفر عليك كتابة حقائق المستودع في كل مطالبة.
ترى هوك prompt.submit كل أمر (prompt) قبل بدء الدور. تمرير حدث يحتوي على إدخال context إضافي إلى next يضيف نصاً يقرأه Claude بعد الأمر الخاص بك، بينما يظل السجل (transcript) يعرض ما كتبته فقط.5
export function register(on) {
on('prompt.submit', async ($, e, next) => {
// Leave prompts that don't mention a pull request alone
if (!/\bPR\b|pull request/i.test(e.text)) return next(e)
let git
try {
git = await $.process.run(['git', 'branch', '--show-current'])
} catch {
// git isn't installed, so send the prompt as it is
return next(e)
}
// Outside a git repository there is no branch to add
if (git.exitCode !== 0) return next(e)
// Claude reads this line after your prompt; the transcript still shows what you typed
return next({ ...e, context: [...(e.context ?? []), 'Current branch: ' + git.stdout.trim()] })
})
}
يتحقق اختباران من أن الأمر الذي يشير إلى PR يحصل على Current branch: feature/auth، ويقومان بعد مرات تشغيل git لإثبات أن الأوامر الأخرى لا تشغله أبداً. في الواقع، ومع تعطيل Bash حتى لا يتمكن Claude من البحث عنها، حصل أمر يشير إلى PR على الإجابة feature/demo. أما السؤال الذي لا يشير إلى PR فقد حصل على "Unknown"، لأنه لم يتم إضافة أي فرع (branch).
يقرأ Claude السطر المضاف مع كل أمر مطابق، لذا اجعله قصيراً وأضفه فقط عندما يكون مفيداً، كما يفعل هذا التعديل (mod).
5. إضافة أمر "slash" فوري يتجاوز النموذج
بعض عمليات البحث لا تحتاج إلى تفكير Claude على الإطلاق.
الأمر المسجل بواسطة mod يقوم بتشغيل الدالة الخاصة بك على الفور، دون أي دور لـ Claude.3 قم بتسجيله في session.start، وأجب عليه في command.run، وأضف immediate: true ليعمل أيضاً بينما يكون Claude مشغولاً.9
export function register(on) {
on('session.start', async ($, e, next) => {
// immediate: true lets /todos run even while Claude is working
await $.command.register({ name: 'todos', description: 'List TODO and FIXME lines in tracked files', immediate: true })
return next(e)
})
on('command.run', { command: 'todos' }, async ($) => {
let out
try {
// git grep searches tracked files only and needs no shell
out = await $.process.run(['git', 'grep', '-n', '-I', '-E', 'TODO|FIXME'])
} catch {
return { text: 'Could not run git grep.' }
}
// git grep exits 1 when nothing matches
if (out.exitCode === 1) return { text: 'No TODO or FIXME lines found.' }
if (out.exitCode !== 0) return { text: 'git grep failed: ' + out.stderr.trim() }
const lines = out.stdout.trim().split('\n')
const shown = lines.slice(0, 20).join('\n')
const more = lines.length > 20 ? '\n... and ' + (lines.length - 20) + ' more' : ''
return { text: lines.length + ' TODO/FIXME lines:\n' + shown + more }
})
}
يغطي اختباران حالة المطابقة وعدم المطابقة. في الواقع، في مستودع (repository) يحتوي على سطرين محددين، قام الأمر claude -p "/todos" --plugin-dir ./todo-scan بطباعة:
todo-scan: 2 TODO/FIXME lines:
app.js:2:// TODO handle empty input
app.js:3:// FIXME flaky retry
لم تكن هناك حاجة لاستدعاء النموذج. يضع Claude Code اسم الإضافة (plugin) أمام نص الأمر.4
انتبه للاسم الذي تختاره: تطلق $.command.register خطأً إذا كان هناك أمر مدمج يستخدم الاسم بالفعل، وأي هوك يطلق خطأً يتم تخطيه، لذا قم بتسجيل الأوامر في نهاية session.start أو غلفها بـ try.9
6. إظهار "إيصال" تحت كل إجابة: الوقت المستغرق والتوكنز المستخدمة
هذا مفيد إذا كنت تراقب المدة التي تستغرقها الأدوار أو تكلفة التوكنز.
يتم إطلاق turn.complete عند انتهاء الدور. يحمل الحدث durationMs، وusage للتوكنز في الدور، وisAborted، ويمكن للهوك إرجاع حقل text يظهر كسطر تحت الإجابة.5
export function register(on) {
on('turn.complete', async ($, e, next) => {
const result = await next(e)
// Subagent turns and interrupted turns get no receipt
if (e.agentId || e.isAborted) return result
const seconds = (e.durationMs / 1000).toFixed(1)
const tokens = e.usage ? ' · ' + e.usage.output_tokens + ' output tokens' : ''
// A text other than the answer itself is shown under the answer
return { ...result, text: 'Done in ' + seconds + 's' + tokens }
})
}
يتحقق اختباران من السطر لدور استغرق 12,345 مللي ثانية مع 420 توكن مخرجات (Done in 12.3s · 420 output tokens) ومن أن الدور المقطوع لا يحصل على شيء. في تشغيل claude -p يتم طباعة الإجابة فقط، لذا يظهر هذا السطر في الجلسات التفاعلية.
يتخطى الهوك أدوار الوكلاء الفرعيين (subagent turns)، والتي تطلق أيضاً turn.complete مع تعيين agentId.5
7. رسم مقياس للسياق فوق الأمر
هذا مفيد عندما تريد رؤية نافذة السياق وهي تمتلئ قبل أن تكتمل.
الشريط الموجود فوق الأمر هو موقع رندر (render site) يتشاركه كل mod. يقوم هوك ui.render المفلتر إلى { component: 'AbovePrompt' } بإرجاع ما يجب رسمه هناك، أو next(e) لعدم رسم شيء.10 ترجع $.session.usage() توكنز نافذة السياق tokens، وwindow وpercent، من بين أرقام أخرى.6
// A 20-cell bar, filled in proportion to the percent used
function bar(percent) {
const filled = Math.round(percent / 5)
return '█'.repeat(filled) + '░'.repeat(20 - filled)
}
export function register(on) {
// After each turn the context has grown, so ask for a redraw of the band
on('turn.complete', async ($, e, next) => {
const result = await next(e)
$.ui.invalidate('ui.render')
return result
})
on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
// Give way to a survey that wants the band
if (e.props.hasSurvey) return next(e)
const { context } = await $.session.usage()
// No reading yet, before the first response
if (context.percent === undefined) return next(e)
const { Text } = $.ui.resolve(e)
const color = context.percent >= 80 ? 'red' : context.percent >= 60 ? 'yellow' : 'green'
const used = Math.round((context.tokens ?? 0) / 1000) + 'k of ' + Math.round(context.window / 1000) + 'k'
return Text({ color, children: ['context ' + bar(context.percent) + ' ' + context.percent + '% (' + used + ')'] })
})
}
الرسم يتغير فقط عندما يتم تشغيل الـ hook الخاص به مرة أخرى، لذا يقوم الـ mod باستدعاء $.ui.invalidate('ui.render') بعد كل دور.10
يقوم الاختبار الأول برسم الشريط باستخدام $.ui.mount على كل من أسطح الـ terminal والـ desktop. مع استخدام 84,000 من أصل 200,000 توكن، يقوم برسم context ████████░░░░░░░░░░░░ 42% (84k of 200k). أما الاختبار الثاني فيتحقق من عدم رسم أي شيء قبل القراءة الأولى.
يقوم Claude Code بتقليل سرعة إعادة الرسم إلى 10 مرات في الثانية، أو 30 في الـ terminal لبعض المواقع، وهو أكثر بكثير مما يحتاجه هذا المقياس.6
8. احتفظ بملاحظاتك المسودة في لوحة تصمد أمام إعادة التشغيل
إذا كنت تحتفظ بقائمة جارية من الأشياء التي يجب التحقق منها أثناء عمل Claude، فضعها في لوحة (pane).
يفتح /notes لوحة تحتوي على حقل نصي. تحصل كل ملاحظة على زر x لحذفها، ويتم حفظ القائمة في $.store، وهو مخزن مفتاح-قيمة (key-value store) يستمر بين الجلسات.10 يتبع هذا مثال الملاحظات الموجود في الوثائق، مع إضافة خاصية التحميل.
const PANE = 'scratch-notes'
// The notes the pane draws, loaded from the store when the session starts
let notes = []
export function register(on) {
on('session.start', async ($, e, next) => {
const saved = await $.store.get('notes')
if (Array.isArray(saved)) notes = saved
await $.command.register({ name: 'notes', description: 'Open your scratch notes pane' })
return next(e)
})
on('command.run', { command: 'notes' }, async ($) => {
await $.ui.open({ id: PANE, title: 'Notes', focus: true, closeOnEscape: true })
// Print nothing in the transcript
return {}
})
on('ui.render', { component: 'Pane' }, async ($, e, next) => {
// Leave other mods' panes alone
if (e.requestId !== PANE) return next(e)
const { Box, Text, Button, Input } = $.ui.resolve(e)
const save = async () => {
$.ui.invalidate('ui.render')
await $.store.set('notes', notes)
}
return Box({
flexDirection: 'column',
children: [
Input({
key: 'new-note',
label: 'Note',
placeholder: 'Type a note and press Enter',
value: '',
submitLabel: 'add',
autoFocus: true,
onSubmit: async (value) => {
if (!value.trim()) return
notes = [...notes, value.trim()]
await save()
},
}),
...notes.map((note, i) =>
Box({
flexDirection: 'row',
columnGap: 1,
children: [
Button({
key: 'delete-' + i,
label: 'x',
plain: true,
onPress: async () => {
notes = notes.filter((_, j) => j !== i)
await save()
},
}),
Text({ children: [note] }),
],
}),
),
],
})
})
}
يقوم الاختبار الأول بكتابة ملاحظة، والتحقق من رسمها، ثم حذفها، والتحقق من القائمة المحفوظة على كل من أسطح الـ terminal والـ desktop. ويتحقق اختبار ثانٍ من أن الملاحظات المحفوظة مسبقاً تعود بعد session.start.
ضع في اعتبارك أن $.store مشترك بين كل جلسة على جهازك، وعملية get متبوعة بـ set ليست ذرية (atomic)، لذا يمكن لجلساتين أن تمسح تغييرات بعضهما البعض.10 يتسع المخزن لما يصل إلى 4 ميجابايت من JSON.6
9. امنح Claude أداة مخصصة دون تشغيل خادم MCP
غالباً ما ترغب الفرق في أن يقوم Claude بالبحث عن شيء ما في أحد أنظمتهم الخاصة.
يضيف $.tool.register أداة يمكن لـ Claude استدعاؤها. يراها Claude تحت اسم أطول، mcp__<plugin>__<tool>، ويقوم الـ tool.call hook الخاص بك لهذا الاسم الكامل بالرد عليه.9
هذه الأداة تجلب مشكلة (issue) من GitHub. تقول وثائق REST الخاصة بـ GitHub إن الطلبات التي لا تحتوي على ترويسة User-Agent يتم رفضها، ولهذا السبب يرسل الـ mod واحدة.11 هناك متغير بيئة اختياري TICKET_API_BASE، يتم قراءته بواسطة $.env.get، يوجه الأداة نحو GitHub Enterprise أو خادم اختبار.9
export function register(on) {
on('session.start', async ($, e, next) => {
await $.tool.register({
name: 'github_issue',
// Claude decides when to call the tool from this description
description: 'Look up a GitHub issue or pull request and return its title, state, labels and first 1,500 characters',
inputSchema: {
type: 'object',
properties: {
repo: { type: 'string', description: 'owner/name, such as anthropics/claude-code' },
number: { type: 'integer' },
},
required: ['repo', 'number'],
},
})
return next(e)
})
// The full name is mcp__, the plugin's name, two underscores, and the tool's name
on('tool.call', { tool: 'mcp__ticket-tool__github_issue' }, async ($, e) => {
if (!/^[\w.-]+\/[\w.-]+$/.test(e.repo)) return { result: 'repo must look like owner/name' }
// GitHub by default; set TICKET_API_BASE to point at GitHub Enterprise or a test server
const base = (await $.env.get('TICKET_API_BASE')) ?? 'https://api.github.com'
const url = base + '/repos/' + e.repo + '/issues/' + Number(e.number)
const response = await $.http.fetch(url, { headers: { accept: 'application/vnd.github+json', 'user-agent': 'ticket-tool-mod' } })
if (!response.ok) return { result: 'Lookup failed with status ' + response.status }
let issue
try {
issue = JSON.parse(response.text)
} catch {
return { result: 'The lookup returned something that is not JSON' }
}
const labels = (issue.labels ?? []).map((l) => l.name).join(', ') || 'none'
return {
result: '#' + issue.number + ' ' + issue.title + '\nstate: ' + issue.state + '\nlabels: ' + labels + '\n\n' + (issue.body ?? '').slice(0, 1500),
}
}).catch(async ($, e, next) => {
// A network error or a timeout becomes a result Claude can read
return { result: 'The issue lookup failed: ' + next.error.kind }
})
}
يقوم اختباران بعمل stub لـ $.http.fetch: أحدهما يتحقق من رابط GitHub والملخص لمشكلة تم العثور عليها، والآخر يتحقق من أن TICKET_API_BASE يغير الرابط وأنه يتم الإبلاغ عن خطأ 404.
أول تشغيل حي لنا ضد api.github.com أعطى HTTP 403، لأن بروكسي الشبكة الخاص بالـ sandbox يسمح فقط باستدعاءات GitHub API للمستودعات المرتبطة بالجلسة.
لذا قمنا بتشغيله حياً ضد خادم محلي صغير يستجيب مثل نقطة نهاية المشكلات (issues endpoint) في GitHub. استدعى Claude الأداة ورد بالعنوان والحالة والتصنيفات التي أرسلها الخادم (Export button does nothing, open, bug, p1)، وأظهر سجل الخادم الطلب مع User-Agent الخاص بالـ mod.
يسمح GitHub بمزيد من الطلبات في الساعة عند المصادقة.11 بالنسبة للمستودعات الخاصة، اقرأ رمز الوصول (token) باستخدام $.env.get وأرسله كـ header.9
10. مراقبة CI وعرض النتيجة أسفل الأمر (prompt)
تساعد هذه الإضافة أثناء انتظارك لعمليات التحقق من الـ pull request مع الاستمرار في العمل.
العمل الذي يستمر لأكثر من حدث واحد يعمل على مؤقت يبدأ في session.start. ويحل $.clock.every محل setInterval، بينما يقوم $.ui.status بتعيين سطر واحد أسفل الأمر يظل موجوداً حتى تقوم بتغييره.9
تطلب هذه الإضافة من GitHub CLI التحقق من حالة الـ pull request الحالية مرة كل دقيقة. يتضمن gh pr checks --json حقلاً باسم bucket يصنف كل عملية تحقق إلى pass، أو fail، أو pending، أو skipping أو cancel.12
// Turn gh's JSON into a short line such as "3 pass · 1 fail · 2 pending"
function summarize(json) {
const counts = {}
for (const check of JSON.parse(json)) counts[check.bucket] = (counts[check.bucket] ?? 0) + 1
const parts = ['pass', 'fail', 'pending'].filter((b) => counts[b]).map((b) => counts[b] + ' ' + b)
return parts.length > 0 ? parts.join(' · ') : 'no checks'
}
async function refresh($) {
let out
try {
out = await $.process.run(['gh', 'pr', 'checks', '--json', 'bucket,name'])
} catch {
$.ui.status(undefined)
return
}
// gh exits 8 while checks are pending and 1 when the command fails,
// so read the JSON instead of the exit code
let line
try {
line = 'CI: ' + summarize(out.stdout)
} catch {
line = undefined
}
// undefined clears the line
$.ui.status(line)
}
export function register(on) {
on('session.start', async ($, e, next) => {
// Check once a minute; the timer stops when the mod reloads
$.clock.every(60_000, () => refresh($))
return next(e)
})
}
يستخدم اختباران mock.clock الخاص بالمجموعة، لذا تمر الدقيقة فوراً. بعد 59 ثانية لا يظهر شيء. وعند 60 ثانية يقرأ السطر CI: 2 pass · 1 pending. وعندما لا يكون هناك pull request، يتم مسح السطر.
تتوقف المؤقتات عند إعادة تحميل الإضافة، ويبدأ مثيل (instance) جديد مؤقته الخاص.910 يبدأ سطر الحالة بعلامة تحذير واسم الإضافة، لذا اجعل النص قصيراً.9
ما الذي تغير في الأسبوع الأول
وصلت الإضافات (Mods) في الإصدار 2.1.287 في 1 أكتوبر 2026، وتبع ذلك ثمانية إصدارات أخرى بحلول 8 أكتوبر.2 هناك بعض التغييرات التي تهم مؤلفي الإضافات:
| الإصدار | التاريخ | التغيير |
|---|---|---|
| 2.1.287 | 1 أكتوبر 2026 | "إضافة Claude Mods: يمكن للإضافات الآن تعديل سلوكيات أعمق" |
| 2.1.288 | 2 أكتوبر 2026 | إضافة $.ui.selection()، والتي تعيد النص الذي حددته مؤخراً في وضع ملء الشاشة |
| 2.1.290 | 5 أكتوبر 2026 | يقوم claude plugin validate بسرد كل hook مسجل في موقع gating، وما إذا كان يحتوي على .catch |
| 2.1.292 | 6 أكتوبر 2026 | تخزين مؤقت للأوامر (Prompt caching) لـ $.model.complete، وحدث prompt.autocomplete جديد |
| 2.1.293 | 7 أكتوبر 2026 | إضافة isDeferred في $.tool.register، لسرد مخطط الأداة (tool's schema) في الأمر منذ البداية |
| 2.1.295 | 8 أكتوبر 2026 | إضافة $.ui.notify؛ الرفض بعد تشغيل الأداة يقول الآن "تم تشغيل الأداة وقامت إضافة بحجب نتيجتها" |
المصدر: سجل تغييرات Claude Code.2 تغيير الإصدار 2.1.290 هو السبب في أن مخرجات validate لدينا تتضمن أسطراً مثل gating hook with .catch: tool.call{tool=Bash}، وهو ما لا تظهره مخرجات الأمثلة في التوثيق بعد. يحذر التوثيق من أن الأحداث والطرق قد تتغير بين الإصدارات، وأن تعريفات الأنواع التي يكتبها Claude Code لإصدارك هي المرجع الأدق في حال وجود تعارض مع أي صفحة توثيق.4
الأخطاء الشائعة والقيود
claude plugin validate يقرأ الكود المصدري الخاص بك بنفس الطريقة التي يقوم بها Claude Code، ومعظم الأخطاء الأولى تأتي من قواعده:4
- اكتب كل استدعاء API بالكامل، كما في
$.store.get('notes'). استدعاءconst ui = $.uiسيفشل مع خطأ$.ui is used as a value. - استخدم نصاً صريحاً (string literal) لاسم الحدث. استخدام متغير أو حلقة تكرارية (loop) على الأسماء سيؤدي لفشل التحقق.
- سجل كل حدث مرة واحدة لكل matcher. وجود اثنين من خطافات (hooks)
session.startبدون matcher يمنع تحميل الموديول.5 - استخدم ES modules. لا تستخدم
require، ولا استدعاءاتimport().4
القيود الأكثر شيوعاً:6
| القيد | القيمة |
|---|---|
| وقت تشغيل الخطاف (hook) لحدث واحد | 10 ثوانٍ |
معالج .catch | ثانية واحدة |
مهلة $.process.run | 30 ثانية افتراضياً، 10 دقائق كحد أقصى |
maxTokens في $.model.complete | 1,024 افتراضياً |
$.fs.read و $.fs.write | 4 ميجابايت لكل ملف |
$.store | 4 ميجابايت من JSON إجمالاً |
اختبار claude plugin test واحد | 5 ثوانٍ ما لم يتم تعيين timeoutMs |
الوقت الذي يتم قضاؤه في الانتظار داخل next أو استدعاء API للمودز لا يُحتسب ضمن الـ 10 ثوانٍ، باستثناء $.clock.sleep.6 وهذا هو السبب في أن حارس confirm-risky يمكنه الانتظار على $.ui.ask دون تجاوز الحد.5
هل مودز Claude Code آمنة؟
المودز تكون آمنة بقدر أمان مؤلفها. تقول الوثائق أن المود "يعمل بصلاحياتك": يمكنه قراءة وكتابة ملفاتك، وبدء البرامج، وإجراء طلبات الشبكة، وقراءة متغيرات البيئة الخاصة بك، ورؤية كل أمر (prompt) واستدعاء أداة، والموافقة على استدعاء أداة قبل أن يُطلب منك ذلك، واستدعاء نموذج (model) ضمن خطتك. "المودز ليست معزولة (sandboxed)". وحتى مع تفعيل العزل، فإن أي عملية يبدأها المود تعمل خارج نطاق العزل.3
هناك شيئان لا يمكن للمود القيام بهما: تغيير ما يظهر في مطالبة الصلاحيات، أو تجاوز حظر من خطاف PreToolUse في إعدادات الإدارة الخاصة بمؤسستك، والتي تعمل قبل أي مود.35
قبل تثبيت مود خاص بشخص آخر، قم بتشغيل claude plugin validate على المجلد الخاص به. ستعرض أسطر hooks: و calls: الأحداث التي يتعامل معها وكل استدعاء API يقوم به، دون تشغيله.3 يغطي تحليلنا في Plugin4Shell سبب أهمية الثقة في الإضافات بالنسبة لوكلاء البرمجة.
لإيقاف تشغيل الـ mods: قم بتعطيل أحدها في /plugin، أو ابدأ جلسة باستخدام --safe-mode، أو قم بتعيين "disableAllHooks": true في ~/.claude/settings.json.3
عناصر التحكم للفرق والمؤسسات
في خطط Team و Enterprise، وعلى الأجهزة ذات الإعدادات المدارة، يتم تحميل حارس مدمج يسمى sec-default قبل الـ mods التي يثبتها المستخدم، لمنعهم من القيام بأشياء خطيرة مثل تجاوز قواعد رفض الأذونات.16 يمكن للمسؤولين أيضاً:6
- تشغيل الـ mods الخاصة بهم أولاً أو أخيراً باستخدام
prependPluginsوappendPlugins - السماح فقط بـ mods المؤسسة باستخدام
allowManagedModsOnly - رفض
--plugin-dirو--plugin-urlعند بدء التشغيل باستخدامdisableSideloadFlags
تتضمن أمثلة فريق الإعلان نفسها حالة CI/CD في لوحة جانبية، وتأكيداً قبل أن تؤثر الأوامر على إعدادات الإنتاج (production config)، وسجلاً تدقيقياً لكل استدعاءات الـ mod.1
أين تعمل الـ mods؟
ترتبط الـ mods بالأحداث في كل مكان يتم فيه تحميل الإضافة، لكنها ترسم فقط في بعض الأماكن.3
| مكان تشغيل Claude Code | تشغيل الـ Hooks | ظهور الرسومات |
|---|---|---|
claude في Terminal | نعم | نعم |
| تبويب Code في تطبيق Desktop (ليس جلسات WSL) | نعم | نعم، باستثناء عناصر الـ terminal فقط |
| لوحة الدردشة في إضافة VS Code | نعم | لا |
claude -p و Agent SDK | نعم | لا |
| جلسة سحابية (cloud session) | نعم، إذا وصلت الإضافة إليها | لا |
لهذا السبب أمكن اختبار ستة من الـ mods الخاصة بنا مباشرة في claude -p بينما لم يكن ذلك ممكناً للـ mods التي تعتمد على الرسم.
أماكن إضافية للبدء
- الـ mods المدمجة. بعض ميزات Claude Code نفسها هي عبارة عن mods، مثل
/diff، ويمكنك رؤيتها تحت قسم Built-in في/plugin. يوجد مصدر العديد منها في مجلدmodsالخاص بمستودع Claude Code.3 - نماذج Anthropic. يحتوي مستودع
claude-code-playgroundعلىtoken-weather(توقعات للسياق فوق المطالبة)، وblast-radius(يحتجز الأمر الخطير ويظهر ما سيقوم بتغييره)، وreplay-theater(أمر/replayلتعديلات الدورة الأخيرة). تشارك Anthropic هذه النماذج كما هي، بدون دعم.3 - اسأل Claude. صف الـ mod الذي تريده، وسيقوم Claude بكتابته باستخدام مهارة
plugin-authoring. سيعمل بمجرد موافقتك عليه.4
الخلاصة
يمكنك الآن تغيير طريقة سلوك Claude Code ببضع عشرات من أسطر JavaScript. تغطي الأمثلة العشرة هنا المهام الشائعة: حماية أمر ما، تغيير ما يقرأه Claude، إضافة أمر أو أداة، ورسم حالة خاصة بك. كل مثال منها قصير، ويسمح لك claude plugin test بإثبات عمله قبل أن يلمس جلسة حقيقية. اقرأ أي إضافة لم تكتبها بنفسك قبل تحميلها، لأنها تعمل بصلاحياتك.
قراءات ذات صلة
- تعديل Claude Code لمنع قراءة ملفات .env: تم الاختبار (2026) يقوم ببناء حماية للملفات السرية وقياس أماكن تسريب مطابقة النصوص.
- Plugin4Shell: ترقيع وتحصين وكيل البرمجة الخاص بك (2026) يشرح مخاطر تثبيت الإضافات في وكيل البرمجة.
- إضافات الوكلاء 1.0: إضافة واحدة، تسعة عملاء في 2026 يغطي كيفية تعبئة المهارات وخوادم MCP كإضافات.
- خطافات استنتاج Claude: بوابة DLP لعام 2026 وثغراتها ينظر في طبقة أخرى لمنع وصول البيانات إلى المطالبات (prompts).
المراجع
Footnotes
-
Customize Claude Code with mods, Anthropic, October 1, 2026. Source for "small TypeScript functions", what a mod can do with one function including redacting secrets from tool output,
/diffshipping as a mod,sec-defaulton Team and Enterprise plans, the team examples, and the warning that mods aren't sandboxed. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 -
Claude Code changelog, Anthropic, versions 2.1.287 (October 1, 2026) to 2.1.295 (October 8, 2026), read October 9, 2026. ↩ ↩2 ↩3 ↩4
-
Mods overview, Claude Code docs, read October 9, 2026. Source for the definition, version requirements, the comparison table, what a mod can reach, the permission prompt, where mods run, turning mods off, built-in mods and the sample mods. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 ↩15 ↩16 ↩17 ↩18 ↩19
-
Create a mod, Claude Code docs, read October 9, 2026. Source for the three files, no build step,
--plugin-dir, theplugin-authoringskill,claude plugin validateand its rules, and the advice to trust the type declarations for your version. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 -
React to events with a mod, Claude Code docs, read October 9, 2026. Source for observe, rewrite and answer,
tool.call,$.ui.ask,tool.check,prompt.submitcontext,turn.complete, managedPreToolUsehooks running first, failing hooks and.catch. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 -
Mods reference, Claude Code docs, read October 9, 2026. Source for files,
$.session.usage(), limits, render sites and organization settings. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 -
Test a mod, Claude Code docs, read October 9, 2026. Source for
claude plugin test, stubs,mock.clock,$.ui.mountand the exit status. ↩ ↩2 ↩3 ↩4 -
Claude Code 2.1.295 type declarations for mods,
ToolCallResult, the file Claude Code writes beside a mod as.claude-plugin/types/claude-code/index.d.ts. A published copy lives at mods/types/claude-code.d.ts in the Claude Code repository and can be older than your version. ↩ -
Use the mods API, Claude Code docs, read October 9, 2026. Source for commands,
immediate, tools and theirmcp__names,$.clock,$.ui.status,$.ui.logand$.env. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 -
Draw in the interface with a mod, Claude Code docs, read October 9, 2026. Source for render sites, the band above the prompt, panes, the notes example, redraws and
$.store. ↩ ↩2 ↩3 ↩4 ↩5 -
Getting started with the REST API, GitHub Docs, read October 9, 2026. Source for the required
User-Agentheader and higher hourly limits for authenticated requests. ↩ ↩2 -
gh pr checks, GitHub CLI manual, read October 9, 2026. Source for the
--jsonfields and thebucketvalues. ↩
