תיעוד 75
יצירת מוד
בקש מקלוד לכתוב מוד של Claude Code מתוך תיאור, או כתוב בעצמך מוד שסופר קריאות לכלים ומוסיף פקודה. למד את לולאת הטעינה מחדש והאימות.
מוד הוא תוסף של Claude Code עם קובץ כניסה, שנקרא מודול ה-hooks: קובץ JavaScript או TypeScript ש-Claude Code קורא לפונקציות שלו כשאירועים קורים. כדי ליצור מוד:
- בקש מקלוד לכתוב אותו: תאר מה אתה רוצה בסשן של Claude Code
- כתוב אותו בעצמך: עקוב אחרי המדריך כדי ללמוד איך הקוד של מוד עובד. אין צורך ב-Node.js, ב-bundler או בשלב בנייה, כי Claude Code טוען קבצי
.jsו-.tsישירות.
אם עוד לא החלטת אם מוד הוא הכלי המתאים, קרא קודם את ההשוואה בסקירה.
[!NOTE]
השתמש ב-Claude Code בגרסה v2.1.287 או חדשה יותר. במעטפת, הרץ
claude --versionכדי לבדוק. כדי לראות אם מודים יכולים להיטען אצלך, ראה בדיקה אם מודים יכולים להיטען.
#בקש מקלוד מוד
תאר את המוד שאתה רוצה בסשן אינטראקטיבי של Claude Code, וקלוד כותב אותו. קלוד עובד מתוך skill מובנה בשם plugin-authoring, שאומר לו איפה לכתוב את המוד, אילו אירועים ומתודות יש בגרסה שלך, ואיך המוד נטען. קלוד יכול לטעון את ה-skill כשאתה מבקש מוד, או שאתה יכול לטעון אותו בעצמך על ידי הרצת /plugin-authoring בשורת Claude Code.
המוד רץ אחרי שאתה מאשר אותו, חוץ מבסשנים שבהם מוד שקלוד כותב לא יכול להיטען.
1. תאר את המוד
בקש את המוד במילים שלך, לדוגמה make a mod that shows the current git branch above the prompt. קלוד כותב את המוד בתיקייה משלו בתיקיית המודים של הסשן, שהיא ~/.claude/dev-mods/ ואחריה מזהה הסשן. הנתיב המלא של מוד נראה כך: ~/.claude/dev-mods/3f2a9c1e-5b7d-4e8a-9c21-6d0f4b8a7e13/git-branch/.
[!NOTE]
במצבי ההרשאה
defaultו-acceptEdits, Claude Code מבקש ממך לאשר כל אחד מקבצי המוד לפני שקלוד יוצר אותו, כי תיקיית המודים היא נתיב מוגן. אשר כל קובץ כשהוא מופיע.
2. אשר את המוד
כשקלוד שומר את הקובץ הראשון, Claude Code שואל אם להפעיל טעינה מחדש חמה לסשן. טעינה מחדש חמה מריצה את המודים שקלוד כותב בסשן הזה וקולטת כל שינוי מאוחר יותר.
בחר אחת מהתשובות האלה:
- הפעלה לסשן הזה: המודים בתיקיית המודים של הסשן נטענים בסיום הסבב, ונטענים מחדש בסוף כל סבב שמשנה אותם. התשובה שלך תקפה לסשן, כולל אחרי שתמשיך אותו.
- לא עכשיו: שום דבר לא נטען לעת עתה. הקבצים נשארים איפה שקלוד כתב אותם, והמודים נטענים בפעם הבאה שהסשן הזה מתחיל. כדי שמוד לא ייטען אף פעם, מחק את התיקייה שלו.
3. בדוק שהמוד נטען
הרץ /plugin בשורת Claude Code ולחץ Tab עד שהלשונית Installed נבחרת. היא מפרטת את המוד, ואפשר לכבות אותו שם.
4. נסה את המוד
השתמש במה שביקשת. עבור הנחיית הדוגמה, שם הענף הנוכחי מופיע מעל תיבת ההנחיה. אם המוד לא עושה מה שרצית, אמור לקלוד מה לשנות. המוד נטען מחדש בסוף כל סבב שמשנה את הקבצים שלו, כך שאפשר לנסות את השינוי מיד כשקלוד מסיים.
אם שורה בתמליל שמתחילה בשם המוד אומרת שהשינוי לא נטען, הדבק את השורה בהודעה הבאה כשאתה מבקש מקלוד תיקון. קלוד לא רואה את השורה אלא אם תדביק אותה.
#שימוש במוד בסשנים אחרים
מוד שקלוד כתב נטען רק בסשן שיצר אותו, ו-Claude Code מוחק את תיקיית המודים של הסשן הזה אחרי שהיא ישנה מ-cleanupPeriodDays. כדי לשמור את המוד, העתק את התיקייה שלו אל מחוץ לתיקיית המודים למקום שלך, למשל ~/mods/git-branch. אחר כך בחר איך לטעון אותו:
- בסשן שאתה מתחיל: במעטפת, הרץ
claude --plugin-dir ~/mods/git-branch - לאנשים אחרים: הוסף אותו למרקטפלייס כדי שהם יוכלו להתקין אותו
#סשנים שבהם מוד שקלוד כותב לא יכול להיטען
מוד שקלוד כותב נטען רק אחרי שאתה מאשר אותו, בסביבת עבודה מהימנה שבה מותר למודים לרוץ. בסשנים האלה הוא לא נטען:
- אין מי שיאשר: הסשן לא יכול להציג לך בקשה, כמו בהרצה של
claude -pאו במצבdontAsk - סביבת העבודה לא מהימנה: לא קיבלת את בקשת האמון לתיקייה
- מודים מושבתים: התחלת עם
--safe-modeאו--bare, הגדרתdisableAllHooks, או שההגדרות המנוהלות של הארגון חוסמות את זה
#כתיבת מוד בעצמך
במדריך הזה אתה בונה מוד בשם first-mod שסופר את קריאות הכלים שקלוד מבצע, מציג את המונה ליד הספינר בזמן שקלוד עובד, ומוסיף פקודת /tally שמדפיסה אותו. אחר כך אתה קורא את הצהרות הטיפוסים ש-Claude Code כותב ליד המוד ומריץ claude plugin validate. יחד הם מראים לך את האירועים והמתודות שהגרסה שלך מציעה ומה Claude Code קורא מהקוד שלך.
ההקלטה הזו מציגה את המוד המוגמר. הספינר סופר קריאות כלים, /tally מדפיס את המונה, ועריכה של הקוד נכנסת לתוקף בזמן שהסשן רץ:
אתה כותב שלושה קבצים:
first-mod/
├── .claude-plugin/
│ └── plugin.json
└── hooks/
├── hooks.json
└── register.jsplugin.json: המניפסט של התוסףhooks.json: מצביע על קובץ הקוד שלךregister.js: הקוד שלך, שנקרא מודול ה-hooks
1. צור את תיקיית התוסף
צור את שתי התיקיות שמכילות את הקבצים:
Bash או Zsh
mkdir -p first-mod/.claude-plugin first-mod/hooksPowerShell
New-Item -ItemType Directory -Force first-mod\.claude-plugin, first-mod\hooks2. כתוב את המניפסט
מוד הוא תוסף, ומוד צריך מניפסט. למניפסט של המוד הזה אין שדות מיוחדים. שמור את זה כ-first-mod/.claude-plugin/plugin.json:
{
"name": "first-mod",
"version": "0.1.0",
"description": "Counts Claude's tool calls, shows the count beside the spinner, and adds a /tally command",
"author": { "name": "Your Name" }
}3. אמור ל-Claude Code איפה הקוד שלך
כש-Claude Code טוען תוסף, הוא קורא את hooks/hooks.json של התוסף. המפתח modules בקובץ הזה נותן את הנתיב לקוד שלך, והקיום שלו הוא מה שהופך את התוסף למוד. רשום נתיב אחד, יחסי ל-hooks.json. כאן הוא מצביע על register.js, שתכתוב בשלב הבא.
שמור את זה כ-first-mod/hooks/hooks.json:
{
"description": "The first-mod hooks module",
"modules": ["./register.js"]
}4. כתוב את הקוד
הקובץ הזה הוא הקוד של המוד, שנקרא מודול ה-hooks. כשהמוד נטען, Claude Code קורא לפונקציה register שהקובץ מייצא ומעביר לה פונקציה בשם on. כל קריאה ל-on רושמת מטפל באירוע, שנקרא hook, לאירוע שהוא מציין.
שמור את זה כ-first-mod/hooks/register.js:
// The count, shared by the hooks below
let calls = 0
// Claude Code calls this once when the mod loads
export function register(on) {
// Runs when the session starts, before your first prompt
on('session.start', async ($, e, next) => {
// Add the /tally command
await $.command.register({
name: 'tally',
description: 'Show how many tool calls Claude has made',
})
// Let the session start as usual
return next(e)
})
// Runs each time Claude is about to use a tool
on('tool.call', async ($, e, next) => {
calls += 1
// Ask Claude Code to draw the interface again, so the new count shows
$.ui.invalidate('ui.render')
// Let the tool run as usual
return next(e)
})
// Runs when you type /tally, and only then, because of the matcher
on('command.run', { command: 'tally' }, async () => {
// The text to print in the transcript
return { text: 'Claude has made ' + calls + ' tool calls since this mod loaded' }
})
// Runs each time Claude Code draws the spinner
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
// Keep Claude Code's spinner, with the count added after its word
return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
})
}הקובץ שומר מונה ב-calls ורושם ארבעה hooks:
session.startרץ כשהסשן מתחיל, לפני ההנחיה הראשונה שלך, ושוב בכל פעם שהמוד נטען מחדש. הוא מוסיף את הפקודה/tallyל-Claude Code.tool.callרץ בכל פעם שקלוד עומד להשתמש בכלי. הוא מוסיף אחד ל-callsומבקש מ-Claude Code לצייר את הממשק מחדש.command.runרץ כשאתה מקליד/tally. הוא מחזיר את הטקסט להדפסה.ui.renderרץ בכל פעם ש-Claude Code מצייר את הספינר. הוא מוסיף את המונה אחרי המילה של הספינר.
איך מוד הדוגמה עובד מסביר את שלושת הארגומנטים שכל hook מקבל ומה כל אחד מחזיר.
5. טען את המוד
התחל את Claude Code עם הדגל --plugin-dir, שטוען תיקיית תוסף לסשן אחד בלי להתקין אותה:
claude --plugin-dir ./first-mod6. נסה את המוד
בקש מקלוד לעשות משהו שדורש כמה קריאות כלים, למשל list the files here and read the README. בזמן שקלוד עובד, אחרי המילה של הספינר מופיע מונה שעולה, כמו ב-Thinking · tool calls: 2…. כשקלוד מסיים, הקלד /tally ולחץ Enter. התמליל מציג first-mod: Claude has made 2 tool calls since this mod loaded, עם המונה שלך. Claude Code שם את שם התוסף לפני הטקסט של הפקודה.
כדי לבדוק את הפקודה בלי סשן אינטראקטיבי, הרץ אותה במצב לא אינטראקטיבי:
claude -p "/tally" --plugin-dir ./first-modfirst-mod: Claude has made 0 tool calls since this mod loadedאם /tally לא ברשימת הפקודות, המודול לא נטען. ראה גלה למה מוד לא עושה כלום.
7. שנה את הקוד בזמן שהסשן רץ
השאר את הסשן פתוח. ב-register.js, שנה את ' · tool calls: ' ל-' · tools used: ' ב-hook של ui.render ושמור. השורה המודגשת היא זו שמשתנה:
// Runs each time Claude Code draws the spinner
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
// Keep Claude Code's spinner, with the count added after its word
return next({ ...e, props: { ...e.props, suffix: ' · tools used: ' + calls + '…' } })
})שורה בתמליל אומרת ש-first-mod נטען מחדש ומפרטת את ה-hooks שלו, והספינר הבא משתמש בטקסט החדש, כמו ב-Thinking · tools used: 1….
#איך מוד הדוגמה עובד
כל פונקציה שאתה מעביר ל-on היא hook, שהוא מטפל באירוע. Claude Code מעביר לכל hook את אותם שלושה ארגומנטים:
- ה-API של מודים, בשם
$: כל מתודה שמוד יכול לקרוא לה כדי להגיע אל מחוץ לעצמו, במרחבי שמות כמו$.uiו-$.command - האירוע, בשם
e: הקלט של האירוע כנתונים פשוטים, כמו השם והארגומנטים של קריאת כלי - המטפל הבא, בשם
next: פונקציה שמעבירה את האירוע הלאה למודים האחרים ואחר כך להתנהגות של Claude Code עצמו, ומחזירה את התוצאה
ה-hooks ב-first-mod מטפלים באירועים שלהם בדרכים האלה:
- צפייה: ה-hook של
session.startרושם את הפקודה, וה-hook שלtool.callסופר את הקריאה ומבקש ציור מחדש. שניהם מחזיריםnext(e), כך שהסשן מתחיל והכלי רץ כרגיל. - מענה: ה-hook של
command.runמחזיר תוצאה משלו ולא קורא ל-next. הארגומנט השני ל-on,{ command: 'tally' }, הוא מסנן, שנקרא matcher, כך שה-hook רץ רק עבור/tally. - שכתוב: ה-hook של
ui.renderקורא ל-nextעם עותק שלeשה-suffixשלו מכיל את המונה, כך ש-Claude Code מצייר את הספינר הרגיל שלו עם הטקסט שלך אחרי המילה
Claude Code צופה בתיקייה שנטענה עם --plugin-dir וטוען מחדש את מודול ה-hooks כשקובץ בתוכה משתנה. כל טעינה מחדש מריצה את register שוב, כך ש-calls מתאפס ל-0 ו-/tally מתחיל לספור מחדש. כדי לשמור ערך לאורך טעינות מחדש, ראה שמירת מצב.
#המשך לעבוד על מוד
אחרי שמוד נטען, אפשר לבקש מקלוד לשנות אותו, לבדוק את הקוד מול הגדרות הטיפוסים של הגרסה שלך, לרשום את האירועים והקריאות שקלוד קוד מוצא בו, ולבדוק אותו.
#שינוי מוד עם קלוד
כדי לשנות מוד שכבר יש לך, התחל את הסשן עם --plugin-dir שמצביע על ספריית המוד, כך שמה שקלוד כותב נטען באותו סשן:
claude --plugin-dir ./first-modאחר כך בקש את השינוי, למשל add a /tally-reset command to this mod that sets the tally back to zero. קלוד עורך את מודול ה-hooks, מריץ claude plugin validate, ומתקן את מה שהוא מדווח. ספרייה שטוענים עם --plugin-dir היא נתיב מוגן, כך שבמצבים default ו-acceptEdits מתבקשים לאשר כל עריכה של קלוד במוד. טבלת הנתיבים המוגנים נותנת את התוצאה עבור מצבי ההרשאה האחרים.
קבצים שקלוד שומר במהלך התור שלו נטענים מחדש כשהתור נגמר, כך שאפשר לנסות את /tally-reset ברגע שקלוד מסיים.
#קבלת הגדרות טיפוסים לגרסה שלך
כשקלוד קוד טוען מוד מ---plugin-dir בסשן אינטראקטיבי, או מוד שקלוד כתב עבורך, הוא כותב קובצי הצהרות TypeScript לתוך הספרייה .claude-plugin/types/ של המוד. הם מתארים את האירועים המדויקים, את המתודות של ה-mods API, ואת האלמנטים בגרסת קלוד קוד שרצה אצלך, כך שהעורך יכול להשלים אוטומטית ולבדוק טיפוסים של ה-hooks. הספרייה מכילה את הקבצים האלה:
| נתיב | מה הוא מצהיר |
|---|---|
claude-code/index.d.ts | כל אירוע והקלט והתוצאה שלו, כל מרחב שמות ומתודה של ה-mods API, והאלמנטים שכל משטח יכול לצייר |
claude-code-tools/index.d.ts | הקלטים והתוצאות של הכלים המובנים, כך שבדיקה של e.tool === 'Bash' מצמצמת את e |
claude-code-mcp/index.d.ts | הקלטים של כלי ה-MCP שהיו מחוברים בפעם האחרונה ששמרת קובץ במוד |
index.d.ts בספרייה שנקראת על שם פלאגין | מה שהפלאגין הזה מוסיף ל-mods API. יש ספרייה אחת לכל פלאגין ש-plugin.json שלך מציין תחת dependencies. |
tsconfig.json | אפשרויות קומפיילר שמתאימות למודול hooks |
אם למוד אין tsconfig.json משלו, קלוד קוד מוסיף אחד בשורש המוד שמרחיב את זה שנוצר, כך שהעורך ו-tsc -p ./first-mod בודקים טיפוסים של המוד בלי הגדרה נוספת.
האירועים והמתודות יכולים להשתנות בין גרסאות, אז סמוך על הקבצים האלה יותר מאשר על כל דף, כולל הדף הזה, כשהם סותרים.
claude-code/index.d.ts הוא המקור המלא ביותר לבילד שלך, עם הערה ודוגמה לכל מתודה של ה-mods API. כדי לחפש משהו, חפש בקובץ את השם שלו, למשל 'tool.call'.
#בדיקה של מה שקלוד קוד קורא מהמוד שלך
כדי לראות את המוד כמו שקלוד קוד רואה אותו, בלי להריץ את הקוד ובלי להתחיל סשן, השתמש ב-claude plugin validate. הוא בודק את המניפסט ומריץ על מקור מודול ה-hooks את אותו ניתוח סטטי שקלוד קוד מריץ כשהוא טוען מוד. במעטפת, הרץ אותו על ספריית המוד:
claude plugin validate ./first-modעבור first-mod, הפלט כולל את השורות האלה.
❯ ./register.js hooks: session.start, tool.call, command.run{command=tally}, ui.render{component=Spinner}
❯ ./register.js calls: $.command.register, $.ui.invalidate
✔ Validation passedבדוק בשורת hooks: את האירועים שהמודול תופס, כל אחד עם המסנן שלו בסוגריים מסולסלים, וב-calls: כל מתודה של ה-mods API שהוא קורא לה. אם המודול קורא או מגדיר משתני סביבה, חפש גם שורות env reads: ו-env writes:, וגם state reads: ו-state writes: אם הוא משתמש ב-$.state. רואים גם שורה אחת לכל hook שיכול לסרב לפעולה, למשל gating hook without .catch: tool.call, שאומרת אם ל-hook הזה יש מטפל .catch.
אם אירוע שהתכוונת לטפל בו חסר מהשורה הראשונה, גם קלוד קוד לא יקרא ל-hook הזה. הסיבה הרגילה היא שם אירוע עם שגיאת כתיב, שהפקודה מדווחת כשגיאה כמו "tool.calls" is not an event.
פעל לפי הכללים האלה כדי שהניתוח הסטטי יוכל למצוא כל hook וכל קריאה:
- כתוב כל קריאה ל-mods API במלואה:
$, מרחב השמות, ואז המתודה, כמו ב-$.store.get('notes'). אפשר להעביר את$לפונקציה שמוצהרת ברמה העליונה של אותו קובץ, ולפונקציה שלך בשםloadNotesשורתcalls:נקראת אז$.store.get (via loadNotes). העברת$למתודה, לפונקציה שמוגדרת בתוך ה-hook, או לפונקציה שמייבאים מקובץ אחר שלך נכשלת באימות. הפונקציותreadו-updateש-$.stateמשתמש בהן הן הייבואים שיכולים לקבל אותו. אל תשייך את$או אחד ממרחבי השמות שלו למשתנה, אל תפרק אותו, ואל תפנה אליו בשם מחושב.const ui = $.uiנכשל עם$.ui is used as a value. - כתוב את שם האירוע בכל קריאה ל-
onכליטרל מחרוזת, כמו'tool.call'. משתנה, או לולאה על רשימת שמות, נכשלים עםthe event name passed to on() is not a string literal. - בתוך
register, אל תצהיר משתנה או פרמטר שני בשםon. האימות נכשל עם"on" is declared again (shadowed). - ייבא רק מקבצים בתוך ספריית הפלאגין, בנתיב יחסי. הייבוא החשוף היחיד שמותר הוא
claude-code, לטיפוסים ולכמה פונקציות עזר. - השתמש בהצהרות
importבראש הקובץ, כמו ב-import { name } from './file.js'.import()דינמי נכשל עםa dynamic import(); a hooks module imports its own files with an import declaration. - כתוב כל קובץ כמודול ES, עם
importולאrequire. ה-מקור מפרט את סיומות הקבצים שקלוד קוד טוען.
#בדיקת המוד
אפשר לכתוב בדיקות אוטומטיות למוד ולהריץ אותן מהמעטפת עם claude plugin test, בלי סשן, בלי התחברות ובלי רשת. בדיקה מפעילה את האירועים שה-hooks מטפלים בהם ובודקת מה ה-hooks עשו.
הבדיקה הזו מפעילה שתי קריאות לכלי, מריצה את /tally, ובודקת שהתשובה סופרת את שתיהן. שמור אותה כ-first-mod/tests/first-mod.test.ts:
import { expect, test } from 'claude-code/testing'
test('/tally reports the tool calls the mod has seen', async ($, on) => {
// Answer each tool call in Claude Code's place, so no tool runs
on('tool.call', () => ({ result: 'ok' }))
// Fire two tool calls, which the mod's tool.call hook counts
await $.tool.call({ tool: 'Bash', command: 'ls' })
await $.tool.call({ tool: 'Read', file_path: 'README.md' })
// Run /tally and check the text its hook returns
const answer = await $.command.run({ command: 'tally', args: '' })
expect(answer.text).toBe('Claude has made 2 tool calls since this mod loaded')
})במעטפת, הרץ את הבדיקות מתוך הספרייה first-mod:
claude plugin testהפלט מציין כל בדיקה ואם היא עברה, עם זמנים שמשתנים מהרצה להרצה:
tests/first-mod.test.ts:
(pass) /tally reports the tool calls the mod has seen [22.87ms]
1 pass
0 fail
Ran 1 test across 1 file. [0.19s]בדיקת מוד מכסה stub לקריאת מודל או ל-store, ובדיקה של טיימרים וציורים.
#שיתוף המוד
מוד הוא פלאגין, אז מנהלים לו גרסה במניפסט ואנשים מתקינים ומעדכנים אותו עם פקודות /plugin. אופן השיתוף תלוי עבור מי הוא:
- כמה אנשים: שלח להם את ספריית הפלאגין או קובץ
.zipשלה. ראה שיתוף פלאגין בלי מרקטפלייס - הצוות שלך: רשום אותו ב-מרקטפלייס משלך, למשל מאגר פרטי עם ספרייה אחת לכל פלאגין. כדי להוסיף את המרקטפלייס הזה לכל מי שעובד במאגר, רשום אותו בהגדרות המאגר
- כל הארגון שלך: מנהל יכול להתקין את המודים של הארגון דרך הגדרות מנוהלות
- כל אחד: הפוך את מאגר המרקטפלייס לציבורי, או שלח את הפלאגין לספרייה של Anthropic
לפני כן, בדוק את name של הפלאגין: claude plugin validate נכשל על שם שנראה כמו אחד של Anthropic עצמה, למשל שם שמתחיל ב-claude-. האירועים והמתודות יכולים להשתנות בין גרסאות, אז ה-README הוא המקום לציין עם איזו גרסת קלוד קוד בדקת.
המשך לפתח מול הספרייה עם --plugin-dir, ולא מול עותק מותקן. קלוד קוד שומר במטמון פלאגין מותקן לפי גרסה, אז העריכות לא מגיעות לעותק המותקן עד שמעלים את הגרסה ומתקינים שוב.
#הצעדים הבאים
- ציור בממשק: פתיחת חלונית, ציור מעל הפרומפט, והוספת כפתורים ושדות טקסט
- תגובה לאירועים: חיבור hooks לקריאות כלים, לפרומפטים ולתורות
- שימוש ב-mods API: הוספת פקודות וכלים, קריאה למודל, והרצת עבודה על טיימר
- בדיקת מוד: stub למה שקלוד קוד היה עונה, ובדיקה של טיימרים וציורים
- פתרון תקלות במוד: הסיבות לכך שמוד לא עושה כלום, ויומן הדיבאג
- קריאת המקור של מודים מובנים: פלאגינים שלמים, כל אחד עם מודול ה-hooks והבדיקות שלו