1> ## Documentation Index
2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
3> Use this file to discover all available pages before exploring further.
4
5# Tambahkan komponen ke plugin
6
7> Tambahkan skills, hooks, server MCP, dan setiap jenis komponen lainnya ke plugin Claude Code, dengan contoh yang memvalidasi untuk masing-masing.
8
9export const Piece = ({id, children}) => <div className="pe-piece" data-piece={id}>{children}</div>;
10
11export const PluginExplorer = ({children}) => {
12 const PIECES = [{
13 id: 'manifest',
14 name: 'Manifest',
15 path: '.claude-plugin/plugin.json',
16 required: "Required by Anthropic's directory",
17 lines: [{
18 depth: 0,
19 kind: 'folder',
20 text: '.claude-plugin/'
21 }, {
22 depth: 1,
23 kind: 'file',
24 text: 'plugin.json'
25 }],
26 href: '/en/plugins/manifest-reference#manifest-file',
27 linkText: 'Go to the manifest reference'
28 }, {
29 id: 'skills',
30 name: 'Skills',
31 path: 'skills/review/SKILL.md',
32 lines: [{
33 depth: 0,
34 kind: 'folder',
35 text: 'skills/'
36 }, {
37 depth: 1,
38 kind: 'folder',
39 text: 'review/'
40 }, {
41 depth: 2,
42 kind: 'file',
43 text: 'SKILL.md'
44 }],
45 href: '/en/plugins/components#skills',
46 linkText: 'Go to the Skills section'
47 }, {
48 id: 'commands',
49 name: 'Commands',
50 path: 'commands/about.md',
51 lines: [{
52 depth: 0,
53 kind: 'folder',
54 text: 'commands/'
55 }, {
56 depth: 1,
57 kind: 'file',
58 text: 'about.md'
59 }],
60 href: '/en/plugins/components#commands',
61 linkText: 'Go to the Commands section'
62 }, {
63 id: 'agents',
64 name: 'Agents',
65 path: 'agents/security-reviewer.md',
66 lines: [{
67 depth: 0,
68 kind: 'folder',
69 text: 'agents/'
70 }, {
71 depth: 1,
72 kind: 'file',
73 text: 'security-reviewer.md'
74 }],
75 href: '/en/plugins/components#agents',
76 linkText: 'Go to the Agents section'
77 }, {
78 id: 'hooks',
79 name: 'Hooks',
80 path: 'hooks/hooks.json',
81 lines: [{
82 depth: 0,
83 kind: 'folder',
84 text: 'hooks/'
85 }, {
86 depth: 1,
87 kind: 'file',
88 text: 'hooks.json'
89 }],
90 href: '/en/plugins/components#hooks',
91 linkText: 'Go to the Hooks section'
92 }, {
93 id: 'monitors',
94 name: 'Monitors',
95 path: 'monitors/monitors.json',
96 lines: [{
97 depth: 0,
98 kind: 'folder',
99 text: 'monitors/'
100 }, {
101 depth: 1,
102 kind: 'file',
103 text: 'monitors.json'
104 }],
105 href: '/en/plugins/components#monitors',
106 linkText: 'Go to the Monitors section'
107 }, {
108 id: 'output-styles',
109 name: 'Output styles',
110 path: 'output-styles/terse.md',
111 lines: [{
112 depth: 0,
113 kind: 'folder',
114 text: 'output-styles/'
115 }, {
116 depth: 1,
117 kind: 'file',
118 text: 'terse.md'
119 }],
120 href: '/en/plugins/components#themes-and-output-styles',
121 linkText: 'Go to the Themes and output styles section'
122 }, {
123 id: 'themes',
124 name: 'Themes',
125 path: 'themes/dracula.json',
126 lines: [{
127 depth: 0,
128 kind: 'folder',
129 text: 'themes/'
130 }, {
131 depth: 1,
132 kind: 'file',
133 text: 'dracula.json'
134 }],
135 href: '/en/plugins/components#themes-and-output-styles',
136 linkText: 'Go to the Themes and output styles section'
137 }, {
138 id: 'workflows',
139 name: 'Workflows',
140 path: 'workflows/audit-routes.js',
141 lines: [{
142 depth: 0,
143 kind: 'folder',
144 text: 'workflows/'
145 }, {
146 depth: 1,
147 kind: 'file',
148 text: 'audit-routes.js'
149 }],
150 href: '/en/workflows#distribute-a-workflow-in-a-plugin',
151 linkText: 'Go to Distribute a workflow in a plugin'
152 }, {
153 id: 'bin',
154 name: 'Executables',
155 path: 'bin/hello-plugin',
156 lines: [{
157 depth: 0,
158 kind: 'folder',
159 text: 'bin/'
160 }, {
161 depth: 1,
162 kind: 'file',
163 text: 'hello-plugin'
164 }],
165 href: '/en/plugins/components#executables',
166 linkText: 'Go to the Executables section'
167 }, {
168 id: 'scripts',
169 name: 'Scripts',
170 path: 'scripts/format.sh',
171 lines: [{
172 depth: 0,
173 kind: 'folder',
174 text: 'scripts/'
175 }, {
176 depth: 1,
177 kind: 'file',
178 text: 'format.sh'
179 }],
180 href: '/en/plugins/components#hooks',
181 linkText: 'Go to the Hooks section'
182 }, {
183 id: 'settings',
184 name: 'Default settings',
185 path: 'settings.json',
186 lines: [{
187 depth: 0,
188 kind: 'file',
189 text: 'settings.json'
190 }],
191 href: '/en/plugins/components#default-settings',
192 linkText: 'Go to the Default settings section'
193 }, {
194 id: 'mcp',
195 name: 'MCP servers',
196 path: '.mcp.json',
197 lines: [{
198 depth: 0,
199 kind: 'file',
200 text: '.mcp.json'
201 }],
202 href: '/en/plugins/components#mcp-servers',
203 linkText: 'Go to the MCP servers section'
204 }, {
205 id: 'lsp',
206 name: 'LSP servers',
207 path: '.lsp.json',
208 lines: [{
209 depth: 0,
210 kind: 'file',
211 text: '.lsp.json'
212 }],
213 href: '/en/plugins/components#lsp-servers',
214 linkText: 'Go to the LSP servers section'
215 }];
216 const [selectedId, setSelectedId] = useState('manifest');
217 const [isFullscreen, setIsFullscreen] = useState(false);
218 const rootRef = useRef(null);
219 useEffect(() => {
220 const onFsChange = () => setIsFullscreen(!!document.fullscreenElement);
221 document.addEventListener('fullscreenchange', onFsChange);
222 return () => document.removeEventListener('fullscreenchange', onFsChange);
223 }, []);
224 const toggleFullscreen = () => {
225 if (!rootRef.current) return;
226 if (document.fullscreenElement) document.exitFullscreen(); else rootRef.current.requestFullscreen().catch(() => {});
227 };
228 const selected = PIECES.find(p => p.id === selectedId) || PIECES[0];
229 const onTreeKeyDown = e => {
230 const keys = ['ArrowDown', 'ArrowUp', 'Home', 'End'];
231 if (keys.indexOf(e.key) === -1) return;
232 const i = PIECES.findIndex(p => p.id === selectedId);
233 let next = i;
234 if (e.key === 'ArrowDown') next = Math.min(PIECES.length - 1, i + 1);
235 if (e.key === 'ArrowUp') next = Math.max(0, i - 1);
236 if (e.key === 'Home') next = 0;
237 if (e.key === 'End') next = PIECES.length - 1;
238 e.preventDefault();
239 if (next === i) return;
240 const id = PIECES[next].id;
241 setSelectedId(id);
242 const el = document.getElementById('pe-node-' + id);
243 if (el) el.focus();
244 };
245 const FolderIcon = () => <svg className="pe-icon" width="15" height="15" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.3" strokeLinejoin="round" aria-hidden="true">
246 <path d="M1.5 4.5a1 1 0 0 1 1-1h3.2l1.3 1.5h6a1 1 0 0 1 1 1V12a1 1 0 0 1-1 1h-10.5a1 1 0 0 1-1-1z" />
247 </svg>;
248 const FileIcon = () => <svg className="pe-icon" width="15" height="15" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.3" strokeLinejoin="round" aria-hidden="true">
249 <path d="M4 1.5h5.5L13 5v9.5H4z" />
250 <path d="M9.5 1.5V5H13" />
251 </svg>;
252 return <div ref={rootRef} className={isFullscreen ? 'pe-root pe-fullscreen not-prose' : 'pe-root not-prose'} data-selected={selected.id}>
253 <style>{`
254 .pe-root {
255 --pe-mono: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
256 --pe-accent: #D97757;
257 --pe-accent-text: #A8502F;
258 --pe-accent-bg: rgba(217,119,87,0.10);
259 --pe-bg: #FFFFFF;
260 --pe-surface: #FAFAF7;
261 --pe-hover: #F0EEE6;
262 --pe-border: #E8E6DC;
263 --pe-text: #141413;
264 --pe-text-2: #3D3D3A;
265 --pe-text-3: #5E5D59;
266 font-family: inherit;
267 background: var(--pe-bg);
268 color: var(--pe-text);
269 border: 1px solid var(--pe-border);
270 border-radius: 12px;
271 margin: 1.5rem 0;
272 overflow: hidden;
273 box-sizing: border-box;
274 }
275 .dark .pe-root {
276 --pe-accent-text: #EBA98F;
277 --pe-accent-bg: rgba(217,119,87,0.18);
278 --pe-bg: #1A1918;
279 --pe-surface: #232221;
280 --pe-hover: #2E2D2B;
281 --pe-border: #3A3936;
282 --pe-text: #F1EFE9;
283 --pe-text-2: #D6D4CA;
284 --pe-text-3: #B8B5AD;
285 }
286 .pe-root *, .pe-root *::before, .pe-root *::after { box-sizing: border-box; }
287 .pe-head { display: flex; align-items: flex-start; gap: 12px; padding: 18px 24px 16px; border-bottom: 1px solid var(--pe-border); }
288 .pe-head-text { flex: 1; min-width: 0; }
289 .pe-fs-btn { flex-shrink: 0; width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--pe-border); border-radius: 6px; background: var(--pe-surface); color: var(--pe-text-2); font-size: 15px; line-height: 1; cursor: pointer; }
290 .pe-fs-btn:hover { background: var(--pe-hover); }
291 .pe-fs-btn:focus-visible { outline: 2px solid var(--pe-accent); outline-offset: 2px; }
292 .pe-fullscreen { border-radius: 0; height: 100vh; display: flex; flex-direction: column; overflow: auto; }
293 .pe-fullscreen .pe-body { flex: 1; }
294 .pe-title { font-size: 19px; font-weight: 600; line-height: 1.3; color: var(--pe-text); margin: 0; }
295 .pe-sub { font-size: 15px; line-height: 1.5; color: var(--pe-text-3); margin: 4px 0 0; }
296 .pe-sub code { font-family: var(--pe-mono); font-size: 0.88em; padding: 1px 5px; border-radius: 4px; background: var(--pe-surface); border: 1px solid var(--pe-border); }
297 .pe-body { display: flex; align-items: stretch; }
298 .pe-tree-pane { width: 270px; flex-shrink: 0; background: var(--pe-surface); border-right: 1px solid var(--pe-border); padding: 16px 0 12px; }
299 .pe-panel { flex: 1; min-width: 0; padding: 16px 24px 24px; }
300 .pe-caption { font-size: 13px; font-weight: 600; color: var(--pe-text-3); margin: 0 0 10px; }
301 .pe-tree-pane .pe-caption { padding: 0 16px; }
302 .pe-rootline { display: flex; align-items: center; gap: 7px; padding: 3px 16px; font-family: var(--pe-mono); font-size: 13.5px; color: var(--pe-text-3); }
303 .pe-node {
304 display: block; width: 100%; margin: 0; padding: 3px 16px 3px 30px; text-align: left; cursor: pointer;
305 background: transparent; color: var(--pe-text-2);
306 border: none; border-left: 3px solid transparent;
307 font-family: var(--pe-mono); font-size: 13.5px; line-height: 1.4;
308 }
309 .pe-node:hover { background: var(--pe-hover); }
310 .pe-node:focus-visible { outline: 2px solid var(--pe-accent); outline-offset: -2px; }
311 .pe-node[aria-pressed="true"] { background: var(--pe-accent-bg); border-left-color: var(--pe-accent); color: var(--pe-accent-text); font-weight: 600; }
312 .pe-line { display: flex; align-items: center; gap: 7px; padding: 2px 0; }
313 .pe-line-tree { flex-wrap: wrap; }
314 .pe-line-tree .pe-req { flex-basis: 100%; margin: 2px 0 0 22px; white-space: normal; width: fit-content; max-width: calc(100% - 22px); }
315 .pe-line span { overflow-wrap: anywhere; }
316 .pe-piece { display: none; font-size: 16px; line-height: 1.6; color: var(--pe-text-2); }
317 .pe-root[data-selected="manifest"] .pe-piece[data-piece="manifest"],
318 .pe-root[data-selected="skills"] .pe-piece[data-piece="skills"],
319 .pe-root[data-selected="commands"] .pe-piece[data-piece="commands"],
320 .pe-root[data-selected="agents"] .pe-piece[data-piece="agents"],
321 .pe-root[data-selected="hooks"] .pe-piece[data-piece="hooks"],
322 .pe-root[data-selected="monitors"] .pe-piece[data-piece="monitors"],
323 .pe-root[data-selected="output-styles"] .pe-piece[data-piece="output-styles"],
324 .pe-root[data-selected="themes"] .pe-piece[data-piece="themes"],
325 .pe-root[data-selected="workflows"] .pe-piece[data-piece="workflows"],
326 .pe-root[data-selected="bin"] .pe-piece[data-piece="bin"],
327 .pe-root[data-selected="scripts"] .pe-piece[data-piece="scripts"],
328 .pe-root[data-selected="settings"] .pe-piece[data-piece="settings"],
329 .pe-root[data-selected="mcp"] .pe-piece[data-piece="mcp"],
330 .pe-root[data-selected="lsp"] .pe-piece[data-piece="lsp"] { display: block; }
331 .pe-piece p { margin: 0 0 10px; }
332 .pe-piece p:last-child { margin-bottom: 0; }
333 .pe-piece code { font-family: var(--pe-mono); font-size: 0.88em; padding: 1px 5px; border-radius: 4px; background: var(--pe-surface); border: 1px solid var(--pe-border); }
334 .pe-piece .code-block { margin: 12px 0 0; }
335 .pe-piece pre code { padding: 0; border: none; background: none; }
336 .pe-piece a { color: var(--pe-accent-text); }
337 .pe-line-compact { display: none; }
338 .pe-icon { flex-shrink: 0; }
339 .pe-req { margin-left: 8px; padding: 0 6px; border-radius: 999px; font-size: 11px; line-height: 18px; letter-spacing: .02em; color: var(--pe-accent-text); border: 1px solid var(--pe-border); background: var(--pe-surface); white-space: nowrap; font-weight: 500; vertical-align: middle; }
340 .pe-name { font-size: 22px; font-weight: 600; line-height: 1.25; letter-spacing: -0.2px; color: var(--pe-text); margin: 0; }
341 .pe-path { font-family: var(--pe-mono); font-size: 13.5px; color: var(--pe-accent-text); margin: 4px 0 0; overflow-wrap: anywhere; }
342 .pe-block { margin: 20px 0 0; }
343 .pe-link {
344 display: inline-block; margin: 24px 0 0; padding: 8px 14px; border-radius: 8px;
345 font-size: 14.5px; font-weight: 600; text-decoration: none;
346 color: var(--pe-accent-text); background: var(--pe-accent-bg); border: 1px solid var(--pe-accent);
347 }
348 .pe-link:hover { filter: brightness(0.97); }
349 .pe-link:focus-visible { outline: 2px solid var(--pe-accent); outline-offset: 2px; }
350 @media (max-width: 700px) {
351 .pe-head { padding: 16px 16px 14px; }
352 .pe-body { flex-direction: column; }
353 .pe-tree-pane { width: 100%; border-right: none; border-bottom: 1px solid var(--pe-border); }
354 .pe-line-tree { display: none; }
355 .pe-line-compact { display: flex; }
356 .pe-panel { padding: 16px 16px 20px; }
357 }
358 `}</style>
359
360 <div className="pe-head">
361 <div className="pe-head-text">
362 <div className="pe-title">What goes in a plugin</div>
363 <div className="pe-sub">This example plugin, <code>my-plugin</code>, has one of every kind of component, each in its default location. Select a file or folder to read what it’s for and see what goes in it.</div>
364 </div>
365 <button type="button" className="pe-fs-btn" onClick={toggleFullscreen} aria-label={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'} title={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}>
366 {isFullscreen ? '⤡' : '⛶'}
367 </button>
368 </div>
369
370 <div className="pe-body">
371 <div className="pe-tree-pane">
372 <div className="pe-caption" id="pe-tree-caption">Plugin directory</div>
373 <div role="group" aria-labelledby="pe-tree-caption" onKeyDown={onTreeKeyDown}>
374 <div className="pe-rootline"><FolderIcon /><span>my-plugin/</span></div>
375 {PIECES.map(p => <button key={p.id} id={'pe-node-' + p.id} type="button" className="pe-node" aria-pressed={p.id === selected.id} aria-label={p.name + ', ' + p.path} onClick={() => setSelectedId(p.id)}>
376 {p.lines.map((line, i) => <span key={i} className="pe-line pe-line-tree" style={{
377 paddingLeft: line.depth * 18 + 'px'
378 }}>
379 {line.kind === 'folder' ? <FolderIcon /> : <FileIcon />}
380 <span>{line.text}</span>
381 {p.required && i === p.lines.length - 1 ? <span className="pe-req">{p.required}</span> : null}
382 </span>)}
383 <span className="pe-line pe-line-compact">
384 <FileIcon />
385 <span>{p.path}</span>
386 {p.required ? <span className="pe-req">{p.required}</span> : null}
387 </span>
388 </button>)}
389 </div>
390 </div>
391
392 <div className="pe-panel" role="region" aria-labelledby="pe-panel-caption" aria-live="polite" aria-atomic="true">
393 <div className="pe-caption" id="pe-panel-caption">Selected piece</div>
394 <div className="pe-name">{selected.name}{selected.required ? <span className="pe-req">{selected.required}</span> : null}</div>
395 <div className="pe-path">{selected.path}</div>
396
397 <div className="pe-block">{children}</div>
398
399 <a className="pe-link" href={selected.href}>{selected.linkText}</a>
400 </div>
401 </div>
402 </div>;
403};
404
405Plugin Claude Code dibangun dari komponen, seperti skills, agents, hooks, dan server MCP. Setiap komponen memiliki folder default di plugin, kunci manifest opsional di `.claude-plugin/plugin.json` yang menggantikan atau menambah folder tersebut, dan nama yang dilihat pengguna. Untuk tabel bidang lengkap setiap kunci, lihat [referensi manifest](/docs/id/plugins/manifest-reference#fields).
406
407Gunakan halaman ini untuk menambahkan komponen ke plugin yang sudah dimuat.
408
409Setelah Anda menambahkan komponen, jalankan `/reload-plugins` dalam sesi yang sedang berjalan atau mulai sesi baru sehingga Claude Code memuatnya. Untuk memeriksa file komponen sebelum memuatnya, jalankan [`claude plugin validate .`](/docs/id/plugins/cli-reference#plugin-validate) di shell Anda dari direktori plugin.
410
411<Note>
412 Kasus-kasus ini tercakup di halaman lain:
413
414 * **Membangun plugin pertama Anda**: mulai dengan [Buat plugin](/docs/id/plugins/create)
415 * **Memasang plugin orang lain**: lihat [Pasang plugins](/docs/id/plugins/install)
416 * **Pengguna plugin Anda berada di claude.ai atau di Cowork**: serangkaian komponen yang berbeda dimuat di sana. Lihat [Plugins di claude.ai dan di Cowork](https://claude.com/docs/plugins/overview)
417</Note>
418
419<h2 id="explore-the-plugin-directory">
420 Jelajahi direktori plugin
421</h2>
422
423Explorer menunjukkan plugin contoh, `my-plugin`, yang memiliki satu dari setiap jenis komponen di lokasi defaultnya:
424
425* Skill review dan perintah `about`
426* Subagent security-review
427* Hook yang memformat file setelah Claude mengeditnya, dan folder `scripts/` yang dipanggilnya
428* Monitor log
429* Gaya output dan tema warna
430* Workflow route-audit
431* Executable `hello-plugin`
432* Pengaturan default
433* Server MCP lokal dan language server Go
434
435Setiap file adalah contoh valid terkecil dari formatnya, ada untuk menunjukkan bentuknya daripada untuk berguna: skill atau agent nyata membawa instruksi lengkap dan sering kali file pendukung, dan hook atau monitor nyata melakukan pekerjaan nyata. Bagian setelah explorer menggunakan file yang sama sebagai contoh mereka dan menautkan ke yang lebih lengkap. Pilih file atau folder untuk membaca tujuannya, lihat apa yang ada di dalamnya, dan temukan bagian yang mencakupnya.
436
437<PluginExplorer>
438 <Piece id="manifest">
439 [Manifest](/docs/id/plugins/manifest-reference) adalah file `plugin.json` di direktori `.claude-plugin/` plugin. Ini berisi metadata plugin dan nilai `userConfig` yang diminta Claude Code kepada pengguna. Hanya `name` yang diperlukan. Di sini, `description` adalah teks yang dilihat pengguna untuk plugin di `/plugin`, dan `version` membuat pengguna tetap pada versi itu sampai Anda mengubahnya:
440
441 ```json theme={null}
442 {
443 "name": "my-plugin",
444 "version": "1.0.0",
445 "description": "Review, formatting, and database tools for this team"
446 }
447 ```
448 </Piece>
449
450 <Piece id="skills">
451 [Skill](/docs/id/skills) adalah file `SKILL.md`. Simpan setiap skill di direktorinya sendiri di bawah `skills/`. Claude membaca `description` setiap skill, dan ketika apa yang diminta pengguna cocok dengannya, seperti meminta Claude untuk meninjau pull request di sini, Claude memuat instruksi skill dan mengikutinya. Pengguna juga dapat menjalankannya secara langsung sebagai `/my-plugin:review`:
452
453 ```markdown theme={null}
454 ---
455 description: Reviews a pull request for style and test coverage. Use when asked to review code.
456 ---
457
458 Review the changed files. Report style problems first, then missing tests.
459 ```
460 </Piece>
461
462 <Piece id="commands">
463 Perintah adalah file Markdown tunggal yang dijalankan pengguna berdasarkan nama. Perintah adalah format yang lebih lama: skill berjalan berdasarkan nama dengan cara yang sama dan juga dapat membawa file pendukung di direktorinya sendiri, jadi tulis yang baru sebagai skills dan simpan `commands/` untuk file yang sudah Anda miliki. File ini menjadi `/my-plugin:about` dan mengambil frontmatter yang sama dengan skill:
464
465 ```markdown theme={null}
466 ---
467 description: Summarize the repository
468 ---
469
470 Summarize what this repository does in three sentences.
471 ```
472 </Piece>
473
474 <Piece id="agents">
475 [Subagent](/docs/id/sub-agents) adalah asisten terpisah, dengan instruksinya sendiri dan jendela konteksnya sendiri, yang dapat didelegasikan Claude untuk menyelesaikan tugas dan mendapatkan hasil kembali. Setiap file Markdown di bawah `agents/` mendefinisikan satu: frontmatter menamainya dan mengatakan kapan menggunakannya, dan body adalah system prompt-nya. Yang ini dinamai `my-plugin:security-reviewer`, dan pengguna dapat memanggilnya dengan `@agent-my-plugin:security-reviewer`:
476
477 ```markdown theme={null}
478 ---
479 name: security-reviewer
480 description: Reviews code changes for security issues. Use after edits to authentication or input handling.
481 model: sonnet
482 ---
483
484 You are a security reviewer. Read the changed files and report injection, authentication, and secrets-handling risks.
485 ```
486 </Piece>
487
488 <Piece id="hooks">
489 [Hook](/docs/id/hooks-guide) menjalankan sesuatu secara otomatis pada titik dalam siklus hidup Claude Code, seperti setelah setiap pengeditan file: perintah shell, permintaan HTTP, panggilan tool MCP, prompt ke model, atau subagent. Simpan hooks plugin di `hooks/hooks.json` di root plugin. Yang ini menjalankan `scripts/format.sh` plugin setelah Claude menulis atau mengedit file:
490
491 ```json theme={null}
492 {
493 "hooks": {
494 "PostToolUse": [
495 {
496 "matcher": "Write|Edit",
497 "hooks": [
498 {
499 "type": "command",
500 "command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/format.sh\""
501 }
502 ]
503 }
504 ]
505 }
506 }
507 ```
508 </Piece>
509
510 <Piece id="monitors">
511 Monitor adalah perintah shell yang dimulai Claude Code di latar belakang ketika sesi dimulai dan terus berjalan sampai berakhir, menggunakan [Monitor tool](/docs/id/tools-reference#monitor-tool). Apa yang dicetak mencapai Claude sebagai notifikasi. Field `when` dapat malah memulainya pertama kali skill bernama berjalan. Yang ini mengekor log kesalahan:
512
513 ```json theme={null}
514 [
515 {
516 "name": "error-log",
517 "command": "tail -F ./logs/error.log",
518 "description": "Application error log"
519 }
520 ]
521 ```
522 </Piece>
523
524 <Piece id="output-styles">
525 Plugin dapat menyertakan [output styles](/docs/id/output-styles), yang mengubah cara Claude memformat dan merumuskan balasannya. Simpan setiap output style sebagai `output-styles/<name>.md`. Yang ini muncul di `/output-style` sebagai `my-plugin:terse`:
526
527 ```markdown theme={null}
528 ---
529 name: terse
530 description: Answer in as few words as possible
531 keep-coding-instructions: true
532 ---
533
534 Keep every reply short. Skip preambles and summaries.
535 ```
536 </Piece>
537
538 <Piece id="themes">
539 Plugin dapat menyertakan [color themes](/docs/id/terminal-config#create-a-custom-theme) untuk antarmuka Claude Code. Simpan setiap tema sebagai `themes/<slug>.json`. Yang ini muncul di `/theme` sebagai `Dracula`, ditandai sebagai dari `my-plugin`:
540
541 ```json theme={null}
542 {
543 "name": "Dracula",
544 "base": "dark",
545 "overrides": {
546 "claude": "#bd93f9",
547 "error": "#ff5555"
548 }
549 }
550 ```
551 </Piece>
552
553 <Piece id="workflows">
554 Folder `workflows/` menyimpan file `.js` [workflow](/docs/id/workflows): blok `meta`, kemudian body script yang mengorkestra beberapa subagent. Yang ini berjalan sebagai `/my-plugin:audit-routes`:
555
556 ```javascript theme={null}
557 export const meta = {
558 name: 'audit-routes',
559 description: 'Audit every route handler for missing auth checks',
560 }
561
562 const found = await agent('List every .ts file under src/routes/.', {
563 schema: { type: 'object', required: ['files'], properties: { files: { type: 'array', items: { type: 'string' } } } },
564 })
565
566 const audits = await pipeline(found.files, file =>
567 agent(`Audit ${file} for missing authentication checks.`, { label: file }),
568 )
569
570 return audits.filter(Boolean)
571 ```
572 </Piece>
573
574 <Piece id="bin">
575 `bin/` adalah cara plugin mengirimkan alat command-line. Sementara plugin diaktifkan, Claude Code menempatkan folder ini di `PATH` shell tempat ia menjalankan perintah, sehingga Claude, atau instruksi skill, dapat menjalankan alat berdasarkan nama tanpa pengguna memasangnya. Dengan [executable](#executables) ini di tempat, `hello-plugin` adalah perintah yang dapat dijalankan Claude:
576
577 ```bash theme={null}
578 #!/bin/bash
579 echo "hello from my-plugin"
580 ```
581 </Piece>
582
583 <Piece id="scripts">
584 Hook di `hooks/hooks.json` menjalankan script, dan folder ini adalah tempat contoh menyimpannya. Nama `scripts/` adalah konvensi, bukan sesuatu yang dicari Claude Code: hook menunjuk ke file berdasarkan jalurnya, `${CLAUDE_PLUGIN_ROOT}/scripts/format.sh`. Script formatter mungkin terlihat seperti ini:
585
586 ```bash theme={null}
587 #!/bin/bash
588 npx prettier --write .
589 ```
590 </Piece>
591
592 <Piece id="settings">
593 `settings.json` di root plugin menyimpan [settings](/docs/id/settings-reference) yang berlaku sementara plugin diaktifkan, sehingga plugin dapat mengubah cara sesi berperilaku dan tidak hanya menambahkan komponen. Hanya dua kunci yang berlaku dari plugin, [`agent`](/docs/id/settings-reference#agent) dan [`subagentStatusLine`](/docs/id/settings-reference#subagentstatusline); setiap kunci lain dijatuhkan. Lihat [Default settings](#default-settings).
594
595 Yang ini menetapkan `agent`, yang menjalankan thread utama sesi sebagai agent `security-reviewer` plugin sendiri, sehingga system prompt, pembatasan tool, dan model agent itu berlaku untuk seluruh sesi:
596
597 ```json theme={null}
598 {
599 "agent": "security-reviewer"
600 }
601 ```
602 </Piece>
603
604 <Piece id="mcp">
605 [Server MCP](/docs/id/mcp) memberikan Claude tools dari sistem eksternal. Deklarasikan di `.mcp.json` di root plugin. Yang ini memulai server lokal dari script di dalam plugin, dan muncul di `/mcp` sebagai `plugin:my-plugin:db`:
606
607 ```json theme={null}
608 {
609 "mcpServers": {
610 "db": {
611 "command": "node",
612 "args": ["${CLAUDE_PLUGIN_ROOT}/server.js"]
613 }
614 }
615 }
616 ```
617 </Piece>
618
619 <Piece id="lsp">
620 Server LSP memberikan Claude [diagnostics dan code navigation](/docs/id/plugins/code-intelligence) untuk bahasa. Deklarasikan server di `.lsp.json` di root plugin. Yang ini menghubungkan language server Go untuk file `.go`:
621
622 ```json theme={null}
623 {
624 "gopls": {
625 "command": "gopls",
626 "args": ["serve"],
627 "extensionToLanguage": {
628 ".go": "go"
629 }
630 }
631 }
632 ```
633 </Piece>
634</PluginExplorer>
635
636<h2 id="add-each-kind-of-component">
637 Tambahkan setiap jenis komponen
638</h2>
639
640Setiap bagian di bawah mencakup satu jenis komponen: di mana file-filenya berada di plugin, contoh yang memvalidasi, apa yang dilihat pengguna setelah plugin dimuat, dan kunci manifest yang mengubah lokasi default. Tambahkan yang dibutuhkan plugin Anda; tidak ada yang diperlukan.
641
642<h3 id="skills">
643 Skills
644</h3>
645
646[Skill](/docs/id/skills) adalah file `SKILL.md` yang dapat dimuat Claude ketika deskripsinya cocok dengan tugas. Pengguna juga dapat menjalankannya sebagai perintah. Simpan setiap skill di direktorinya sendiri di bawah `skills/`:
647
648```text theme={null}
649my-plugin/
650├── .claude-plugin/
651│ └── plugin.json
652└── skills/
653 └── review/
654 └── SKILL.md
655```
656
657Berikan `SKILL.md` `description` sehingga Claude tahu kapan menggunakannya:
658
659```markdown skills/review/SKILL.md theme={null}
660---
661description: Reviews a pull request for style and test coverage. Use when asked to review code.
662---
663
664Review the changed files. Report style problems first, then missing tests.
665```
666
667Setelah Anda memuat plugin, `/my-plugin:review` menjalankan skill. Nama perintah dan siapa yang dapat memanggilnya mengikuti aturan ini:
668
669* **Nama perintah**: `/<plugin>:<directory>`, jadi `skills/review/SKILL.md` di `my-plugin` adalah `/my-plugin:review`. Jika Anda menetapkan `name` di frontmatter, itu menggantikan segmen terakhir dan awalan plugin tetap. Lihat [bagaimana skill mendapatkan nama perintahnya](/docs/id/skills#how-a-skill-gets-its-command-name)
670* **Siapa yang memanggilnya**: Claude, pengguna, atau keduanya, dikendalikan oleh frontmatter. Lihat [Kontrol siapa yang memanggilnya skill](/docs/id/skills#control-who-invokes-a-skill)
671
672Anda juga dapat menempatkan skills di luar direktori default `skills/`:
673
674* **Direktori tambahan**: daftarkan di kunci manifest `skills`. Mereka menambah pemindaian default `skills/` daripada menggantinya, tidak seperti `commands` dan `agents`
675* **Skill tunggal di root plugin**: tanpa direktori `skills/` dan tanpa kunci manifest `skills`, `SKILL.md` di root plugin dimuat sebagai satu skill. Tetapkan `name` di frontmatter-nya, karena jika tidak, instalasi marketplace menamakan skill setelah [cache directory](/docs/id/plugins/loading#find-plugins-on-disk)-nya daripada plugin Anda
676
677Untuk menyertakan instruksi dalam plugin, tulislah sebagai skill. Claude Code tidak memuat `CLAUDE.md` di root plugin, dan `claude plugin validate` memperingatkan `CLAUDE.md at the plugin root is not loaded as project context`.
678
679Untuk field frontmatter dan file pendukung, lihat [Skills](/docs/id/skills).
680
681<h3 id="commands">
682 Commands
683</h3>
684
685Perintah adalah file Markdown tunggal yang dijalankan pengguna berdasarkan nama, seperti `/my-plugin:about`.
686
687<Note>
688 Perintah adalah format yang lebih lama, dan [skills](#skills) menggantikannya untuk pekerjaan baru. Skill berjalan berdasarkan nama dengan cara yang sama, dan itu juga dapat membawa file pendukung di direktorinya. Simpan `commands/` untuk file yang Anda pindahkan dari `.claude/commands/`.
689</Note>
690
691Simpan perintah di `commands/<file>.md` dan itu menjadi `/<plugin>:<file>`. Subdirektori menambah segmen, jadi `commands/db/migrate.md` adalah `/my-plugin:db:migrate`.
692
693File perintah mengambil frontmatter yang sama dengan skills.
694
695<h4 id="define-commands-in-the-manifest">
696 Tentukan perintah di manifest
697</h4>
698
699Anda hanya membutuhkan ini jika Anda ingin menyimpan file perintah di tempat lain selain `commands/`, atau untuk mendefinisikan perintah pendek di dalam `plugin.json` tanpa file Markdown terpisah. Tetapkan kunci manifest `commands`, dan Claude Code membacanya daripada memindai `commands/`. Kunci mengambil jalur, array jalur, atau objek yang memetakan setiap nama perintah ke file `source` atau `content` inline.
700
701Manifest ini mendefinisikan `/my-plugin:about` inline, tanpa file Markdown:
702
703```json .claude-plugin/plugin.json theme={null}
704{
705 "name": "my-plugin",
706 "commands": {
707 "about": {
708 "content": "Summarize what this repository does in three sentences.",
709 "description": "Summarize the repository"
710 }
711 }
712}
713```
714
715Muat plugin dan jalankan `/my-plugin:about` dalam sesi untuk mengonfirmasi itu dimuat.
716
717Untuk sintaks kunci lengkap, lihat [`commands`](/docs/id/plugins/manifest-reference#commands).
718
719<h3 id="agents">
720 Agents
721</h3>
722
723[Subagent](/docs/id/sub-agents) adalah asisten terpisah, dengan instruksinya sendiri dan jendela konteks, yang dapat didelegasikan Claude untuk menyelesaikan tugas. Setiap file Markdown di bawah `agents/` mendefinisikan satu:
724
725```markdown agents/security-reviewer.md theme={null}
726---
727name: security-reviewer
728description: Reviews code changes for security issues. Use after edits to authentication or input handling.
729model: sonnet
730---
731
732You are a security reviewer. Read the changed files and report injection, authentication, and secrets-handling risks.
733```
734
735Agent ini dinamai `my-plugin:security-reviewer`, dan pengguna dapat [memanggilnya secara eksplisit](/docs/id/sub-agents#invoke-subagents-explicitly) dengan `@agent-my-plugin:security-reviewer`. Bentuk nama adalah `<plugin>:<name>`, di mana `<name>` berasal dari frontmatter, atau dari nama file ketika tidak ada.
736
737Kunci manifest `agents` menggantikan pemindaian `agents/`.
738
739<h4 id="organize-agents-in-subfolders">
740 Atur agents dalam subfolder
741</h4>
742
743Anda dapat menempatkan file agent plugin dalam subfolder `agents/`. Claude Code [memuatnya secara rekursif](/docs/id/sub-agents#choose-the-subagent-scope) dan menggabungkan nama plugin, setiap nama subfolder, dan nama file dengan titik dua untuk membentuk nama scoped agent. Misalnya, `agents/review/security.md` dalam plugin bernama `my-plugin` dimuat sebagai `my-plugin:review:security`. Dua pengaturan mengubah nama itu:
744
745* Frontmatter `name`: itu menggantikan hanya nama file, jadi `name: audit` di `agents/review/security.md` dimuat sebagai `my-plugin:review:audit`
746* Field manifest [`agents`](/docs/id/plugins/manifest-reference#fields): file yang Anda daftarkan di sana dimuat tanpa nama subfolder, jadi `"agents": "./custom/review/security.md"` dimuat sebagai `my-plugin:security`
747
748<h4 id="frontmatter-fields-in-plugin-agents">
749 Field frontmatter dalam agent plugin
750</h4>
751
752Frontmatter agent plugin mengikuti aturan ini:
753
754* **Field yang didukung**: `name`, `description`, `model`, `effort`, `maxTurns`, `tools`, `disallowedTools`, `skills`, `memory`, `background`, `omitClaudeMd`, `isolation`, `color`, dan kunci `cacheTtl` dari `experimental`. Satu-satunya nilai `isolation` yang valid adalah `"worktree"`. Lihat [field frontmatter yang didukung](/docs/id/sub-agents#supported-frontmatter-fields) untuk apa yang dilakukan masing-masing
755* **Field yang diabaikan**: `permissionMode`, `hooks`, `mcpServers`, dan `initialPrompt`. File agent tidak dapat menambahkan hooks atau server MCP sendiri, jadi tambahkan itu sebagai plugin [hooks](#hooks) dan [server MCP](#mcp-servers) sebagai gantinya
756* **Frontmatter yang tidak diuraikan**: agent masih dimuat dengan setiap field diabaikan. Itu dinamai setelah file, dan deskripsinya berbunyi `Agent from my-plugin plugin`. Jalankan [`claude plugin validate`](/docs/id/plugins/cli-reference#plugin-validate) di shell Anda untuk menemukan file-file ini
757
758Untuk apa yang dilakukan setiap field dan aturan prioritas, lihat [Subagents](/docs/id/sub-agents#supported-frontmatter-fields).
759
760<h3 id="hooks">
761 Hooks
762</h3>
763
764[Hook](/docs/id/hooks-guide) menjalankan sesuatu secara otomatis pada titik dalam siklus hidup Claude Code, seperti setelah setiap pengeditan file: perintah shell, permintaan HTTP, panggilan tool MCP, prompt ke model, atau subagent. Simpan hooks plugin di `hooks/hooks.json` di root plugin, di bawah kunci top-level `"hooks"`, dalam bentuk yang sama dengan objek `hooks` di `settings.json`. Itu memungkinkan Anda menyalin hook pengaturan yang ada tanpa perubahan.
765
766Hook ini menjalankan script bundled setelah setiap `Write` atau `Edit`:
767
768```json hooks/hooks.json theme={null}
769{
770 "hooks": {
771 "PostToolUse": [
772 {
773 "matcher": "Write|Edit",
774 "hooks": [
775 {
776 "type": "command",
777 "command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/format.sh\""
778 }
779 ]
780 }
781 ]
782 }
783}
784```
785
786Simpan script di `scripts/format.sh` dan buat dapat dieksekusi.
787
788Muat plugin dan minta Claude untuk mengedit file. Hook `PostToolUse` yang keluar 0 tidak menunjukkan apa pun dalam transkrip, jadi konfirmasi itu berjalan dengan [debug logging](/docs/id/hooks#debug-hooks) atau dengan apa yang diubah script itu sendiri.
789
790Hooks di `hooks/hooks.json` dan di kunci manifest `hooks` keduanya dimuat. Untuk setiap event dan payload-nya, lihat [Hook events](/docs/id/hooks#hook-events).
791
792<h4 id="when-plugin-hooks-fire">
793 Kapan hook plugin dipecat
794</h4>
795
796Hook plugin tidak menunggu salah satu skill atau perintah plugin digunakan. Claude Code mendaftarkannya ketika sesi memuat plugin, dan mereka dipecat pada event mereka sejak saat itu. Untuk membatasi kapan hook berjalan, persempit `matcher`-nya.
797
798Jika hook tidak pernah dipecat, lihat [hooks yang tidak dipecat](/docs/id/plugins/troubleshooting#failed-to-load-hooks-from-and-hooks-that-dont-fire).
799
800<h4 id="environment-quoting-and-matching-mcp-tools">
801 Lingkungan, quoting, dan pencocokan tool MCP
802</h4>
803
804Lingkungan hook, quoting `${CLAUDE_PLUGIN_ROOT}`, dan matcher untuk tool MCP plugin sendiri bekerja sebagai berikut:
805
806* **Lingkungan**: setiap proses hook menerima `CLAUDE_PLUGIN_ROOT` dan `CLAUDE_PLUGIN_DATA` di lingkungannya, ditambah `CLAUDE_PLUGIN_OPTION_<KEY>` untuk setiap nilai [konfigurasi pengguna](#user-configuration), sehingga script Anda dapat membacanya dari sana
807* **Quoting**: ketika `command` tidak memiliki `args`, itu berjalan melalui shell, jadi bungkus jalur `${CLAUDE_PLUGIN_ROOT}` dalam tanda kutip ganda, seperti contoh `hooks/hooks.json` di bawah [Hooks](#hooks), untuk menjaga jalur yang diperluas sebagai satu kata shell. Ketika Anda melewatkan `args` sebagai gantinya, setiap elemen dilewatkan sebagai satu argumen tanpa shell dan tidak memerlukan quoting. Lihat [exec form dan shell form](/docs/id/hooks#exec-form-and-shell-form)
808* **Pencocokan tool MCP plugin sendiri**: tool dari [server MCP yang dideklarasikan plugin ini](#mcp-servers) dinamai `mcp__plugin_<plugin>_<server>__<tool>`, jadi tulis nama lengkap itu di matcher. Matcher pada nama server saja tidak pernah dipecat. Lihat [Match MCP tools](/docs/id/hooks#match-mcp-tools)
809
810<h3 id="mcp-servers">
811 Server MCP
812</h3>
813
814Server MCP memberikan Claude tools dari sistem eksternal. Deklarasikan di `.mcp.json` di root plugin, dalam bentuk yang sama dengan [project `.mcp.json`](/docs/id/mcp#project-scope). `.mcp.json` ini mendeklarasikan satu server bernama `db`:
815
816```json .mcp.json theme={null}
817{
818 "mcpServers": {
819 "db": {
820 "command": "node",
821 "args": ["${CLAUDE_PLUGIN_ROOT}/server.js"]
822 }
823 }
824}
825```
826
827Anda juga dapat menghilangkan wrapper `mcpServers` dan menempatkan `db` di level atas file.
828
829Muat plugin dan jalankan `/mcp` untuk mengonfirmasi server muncul sebagai `plugin:my-plugin:db`.
830
831`claude plugin validate` memeriksa `.mcp.json` dan melaporkan entri server yang akan dijatuhkan Claude Code pada waktu muat sebagai kesalahan. Memerlukan Claude Code v2.1.281 atau lebih baru.
832
833Untuk di mana entri buruk muncul pada waktu muat, lihat [Server MCP yang tidak dimulai](/docs/id/plugins/troubleshooting#invalid-mcp-server-config-for-and-mcp-servers-that-dont-start).
834
835Kunci manifest `mcpServers` mengambil peta server inline, jalur ke file JSON, atau array dari itu. Ketika server manifest memiliki nama yang sama dengan yang di `.mcp.json`, server manifest menggantinya.
836
837<h4 id="reach-users-on-claude-ai-and-cowork">
838 Jangkau pengguna di claude.ai dan Cowork
839</h4>
840
841Server stdio lokal, seperti server `db` di bawah [Server MCP](#mcp-servers), berjalan di Claude Code dan dalam sesi Cowork yang berjalan di mesin Anda di aplikasi Claude Desktop, tetapi bukan di claude.ai. Untuk menjangkau pengguna di sana juga, referensikan server jarak jauh dengan URL `https://`-nya, yang claude.ai dan Cowork tawarkan kepada pengguna sebagai konektor.
842
843<h4 id="server-names-tool-names-and-reloads">
844 Nama server, nama tool, dan reload
845</h4>
846
847Nama server, substitusi variabel, dan perilaku reload mengikuti aturan ini:
848
849* **Nama server**: `plugin:<plugin>:<server>`, jadi server `db` di `my-plugin` adalah `plugin:my-plugin:db` di `/mcp`. Gunakan bentuk yang sama untuk menamakan server dalam hook [`mcp_tool`](/docs/id/hooks#mcp-tool-hook-fields)
850* **Nama tool**: `mcp__plugin_<plugin>_<server>__<tool>`, jadi tool `query` pada server `db` itu adalah `mcp__plugin_my-plugin_db__query`. Itu adalah nama yang digunakan dalam [aturan izin](/docs/id/permissions) dan [matcher hook](#hooks)
851* **Substitusi**: `${CLAUDE_PLUGIN_ROOT}` dan [variabel jalur](#path-variables-and-persistent-data) lainnya disubstitusikan dalam `command`, `args`, dan `env`. Tidak ada quoting yang diperlukan dalam `args`, karena setiap elemen dilewatkan sebagai satu argumen
852* **Reload**: ketika pengguna menjalankan `/reload-plugins` dan [reload berlaku](/docs/id/plugins/cli-reference#reloads-that-change-mcp-tools), server yang konfigurasinya tidak berubah menjaga koneksinya. Server yang konfigurasinya berubah terhubung kembali, dan yang Anda hapus terputus
853
854<h4 id="include-a-packaged-mcpb-server">
855 Sertakan server MCPB yang dikemas
856</h4>
857
858Kunci `mcpServers` juga menerima server yang dikemas sebagai file [MCPB](https://github.com/modelcontextprotocol/mcpb), yang ekstensinya adalah `.mcpb` atau `.dxt` yang lebih lama. Arahkan kunci ke file, sebagai jalur di dalam plugin atau URL `https://`:
859
860```json .claude-plugin/plugin.json theme={null}
861{
862 "name": "my-plugin",
863 "mcpServers": "./servers/db.mcpb"
864}
865```
866
867Server mengambil namanya dari `name` dalam manifest bundle.
868
869Untuk transport dan autentikasi, lihat [MCP](/docs/id/mcp#plugin-provided-mcp-servers).
870
871<h3 id="lsp-servers">
872 Server LSP
873</h3>
874
875Server LSP memberikan Claude diagnostics dan code navigation untuk bahasa. Jika [plugin code intelligence resmi](/docs/id/plugins/code-intelligence) sudah mencakup bahasa Anda, pasang itu daripada menulis satu. Jika tidak, deklarasikan server di `.lsp.json` di root plugin:
876
877```json .lsp.json theme={null}
878{
879 "gopls": {
880 "command": "gopls",
881 "args": ["serve"],
882 "extensionToLanguage": {
883 ".go": "go"
884 }
885 }
886}
887```
888
889File memetakan setiap nama server langsung ke konfigurasinya, tanpa objek wrapper di sekitar peta. `command` adalah nama binary, dengan argumennya di `args`. `extensionToLanguage` memerlukan setidaknya satu ekstensi, masing-masing dimulai dengan `.`.
890
891`claude plugin validate` tidak membaca file ini. Ketika entri apa pun tidak valid, seluruh file dilewati pada waktu muat dan `Invalid LSP server config for ".lsp.json"` muncul di tab **Errors** `/plugin`.
892
893Plugin Anda mengonfigurasi koneksi tetapi tidak memasang binary server, dan setiap ekstensi file mendapat satu server:
894
895* **Binary yang hilang**: Claude Code memulai `command` berdasarkan nama dari `PATH` pengguna. Ketika binary tidak ada, server gagal dimulai dan `claude --debug` mencatat `LSP server <name> failed to start`
896* **Konflik ekstensi**: ketika dua server yang diaktifkan mengklaim ekstensi yang sama, yang pertama terdaftar menangani file-file itu dan yang lain tidak digunakan untuk mereka, apakah server berasal dari satu plugin atau dua. Tab **Errors** `/plugin` menunjukkan peringatan `LSP server "<name>" is not used for <ext> files`
897
898Kunci manifest `lspServers` mengambil peta yang sama inline, jalur ke file JSON, atau array dari itu, dan server-nya menambah yang di `.lsp.json`. Ketika server manifest memiliki nama yang sama dengan yang di `.lsp.json`, server manifest menggantinya.
899
900Untuk `transport`, timeout, restart, dan field lainnya, lihat [`lspServers`](/docs/id/plugins/manifest-reference#lspservers).
901
902Kirim output log ke stderr, bukan stdout. Claude Code membaca stdout server sebagai pesan protokol saja, dan menerima header pesan hingga 64 KiB dan body pesan hingga 32 MiB.
903
904Claude Code memutuskan server yang melebihi batas apa pun atau menulis output non-protokol ke stdout, dan menghitung putus sebagai crash untuk `restartOnCrash` dan `maxRestarts`. Ketika Anda menjalankan dengan `--debug`, Claude Code menulis kesalahan yang menamai penyebabnya ke log debug.
905
906<h3 id="executables">
907 Executables
908</h3>
909
910File di `bin/` di root plugin berada di `PATH` shell tool Bash sementara plugin diaktifkan, sehingga Claude dapat menjalankannya sebagai perintah bare. Tambahkan script yang dapat dieksekusi:
911
912```bash bin/hello-plugin theme={null}
913#!/bin/bash
914echo "hello from my-plugin"
915```
916
917Buat dapat dieksekusi dengan `chmod +x bin/hello-plugin` dan muat plugin. Ketika Anda meminta Claude untuk menjalankan `hello-plugin`, hasil tool Bash menunjukkan output script.
918
919Direktori `bin/` plugin datang setelah entri `PATH` pengguna sendiri, jadi plugin tidak dapat menaungi `git`, `ls`, atau perintah sistem lainnya.
920
921claude.ai dan Cowork tidak memasang plugin yang memiliki direktori `bin/` level atas, termasuk yang Anda [distribusikan melalui pengaturan organisasi claude.ai](/docs/id/plugins/host-marketplace#distribute-through-organization-settings).
922
923<h3 id="default-settings">
924 Pengaturan default
925</h3>
926
927Untuk menetapkan default yang berlaku sementara plugin diaktifkan, tambahkan `settings.json` di root plugin, atau letakkan objek yang sama inline di kunci manifest `settings`. Dua kunci berlaku, `agent` dan `subagentStatusLine`, dan setiap kunci lain dijatuhkan.
928
929Tetapkan `agent` untuk menjalankan salah satu agent plugin sendiri sebagai thread utama:
930
931```json settings.json theme={null}
932{
933 "agent": "security-reviewer"
934}
935```
936
937Muat plugin dan mulai sesi. Claude kemudian menjawab dalam percakapan utama dengan system prompt dan model agent `security-reviewer`.
938
939Untuk semua yang dikontrol kunci, lihat pengaturan [`agent`](/docs/id/settings-reference#agent).
940
941Ketika kunci yang sama ditetapkan di lebih dari satu tempat, aturan ini memutuskan nilai mana yang berlaku:
942
943* **File atas manifest**: ketika keduanya ada dan `settings.json` menetapkan setidaknya satu kunci yang didukung, `settings.json` berlaku dan `settings` manifest diabaikan
944* **Pengaturan pengguna atas default plugin**: di seluruh sumber pengaturan, default plugin adalah layer terendah, jadi `agent` pengguna sendiri di `~/.claude/settings.json` menggantikan milik Anda
945* **Dua plugin menetapkan kunci yang sama**: nilai dari plugin yang dimuat terakhir berlaku, dan `claude --debug` mencatat `overrides setting`
946
947Untuk bentuk `subagentStatusLine`, lihat [subagent status lines](/docs/id/statusline#subagent-status-lines).
948
949<h3 id="themes-and-output-styles">
950 Tema dan output styles
951</h3>
952
953Plugin dapat menyertakan color themes dan output styles. Keduanya muncul di picker yang sama dengan pengguna sendiri. Untuk salah satu, menetapkan kunci manifest menggantikan pemindaian folder.
954
955| Komponen | Simpan sebagai | Format | Muncul di | Kunci manifest |
956| :----------- | :------------------------ | :------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------- | :-------------------- |
957| Tema | `themes/<slug>.json` | Format [file tema kustom](/docs/id/terminal-config#create-a-custom-theme) yang ditulis pengguna di `~/.claude/themes/` | `/theme`, di bawah `name` file | `experimental.themes` |
958| Output style | `output-styles/<name>.md` | Format [output style kustom](/docs/id/output-styles#create-a-custom-output-style), dengan frontmatter `name` dan `description` | `/output-style`, sebagai `<plugin>:<name>` | `outputStyles` |
959
960Tema plugin adalah read-only, jadi ketika pengguna mengedit satu di `/theme`, edit disimpan sebagai salinan di direktori tema mereka sendiri.
961
962Tema ini mengubah warna prompt accent dan error text pada preset dark:
963
964```json themes/dracula.json theme={null}
965{
966 "name": "Dracula",
967 "base": "dark",
968 "overrides": {
969 "claude": "#bd93f9",
970 "error": "#ff5555"
971 }
972}
973```
974
975<h3 id="channels">
976 Channels
977</h3>
978
979[Channel](/docs/id/channels) memungkinkan sistem luar seperti aplikasi chat mengirim pesan ke sesi. Dalam plugin, channel adalah salah satu server MCP ditambah entri `channels` yang mengikat ke itu dan dapat meminta konfigurasinya sendiri. Manifest ini mengikat channel ke server `telegram` dan meminta token bot:
980
981```json .claude-plugin/plugin.json theme={null}
982{
983 "name": "my-plugin",
984 "mcpServers": {
985 "telegram": {
986 "command": "node",
987 "args": ["${CLAUDE_PLUGIN_ROOT}/server.js"],
988 "env": { "BOT_TOKEN": "${user_config.bot_token}" }
989 }
990 },
991 "channels": [
992 {
993 "server": "telegram",
994 "userConfig": {
995 "bot_token": {
996 "type": "string",
997 "title": "Bot token",
998 "description": "Telegram bot token",
999 "sensitive": true
1000 }
1001 }
1002 }
1003 ]
1004}
1005```
1006
1007`server` harus cocok dengan kunci di `mcpServers`. Per-channel `userConfig` mengambil bentuk yang sama dengan kunci [`userConfig` level atas](#user-configuration).
1008
1009Untuk apa yang harus diimplementasikan server dan bagaimana pengguna mengaktifkan plugin channel, lihat [Package as a plugin](/docs/id/channels-reference#package-as-a-plugin) dalam referensi channels. Untuk tabel field, lihat [`channels`](/docs/id/plugins/manifest-reference#channels).
1010
1011<h3 id="monitors">
1012 Monitors
1013</h3>
1014
1015Monitor adalah perintah shell yang berjalan di latar belakang untuk seluruh sesi. Apa yang dicetak mencapai Claude sebagai notifikasi, sehingga Claude dapat bereaksi terhadap log atau perubahan status tanpa diminta untuk menontonnya. Simpan entri di `monitors/monitors.json`:
1016
1017```json monitors/monitors.json theme={null}
1018[
1019 {
1020 "name": "error-log",
1021 "command": "tail -F ./logs/error.log",
1022 "description": "Application error log"
1023 }
1024]
1025```
1026
1027Perintah berjalan dalam shell, di direktori kerja tempat sesi dimulai.
1028
1029Perintah monitor dibatasi di mana itu dimulai dan apa yang dapat direferensikan:
1030
1031* **Sesi interaktif saja**: monitor plugin dimulai dalam sesi interaktif dan tidak pernah dalam mode non-interaktif dengan flag `-p`. Mereka juga dimulai hanya di mana [Monitor tool](/docs/id/tools-reference#monitor-tool) tersedia
1032* **Tidak ada konfigurasi pengguna**: `command` mendapat [variabel jalur](#path-variables-and-persistent-data) dan `${ENV_VAR}` dari lingkungan, tetapi tidak pernah `${user_config.*}`. Monitor yang mereferensikan satu tidak dimulai, dan proses monitor tidak menerima `CLAUDE_PLUGIN_OPTION_<KEY>` juga
1033* **Menonaktifkan mid-session**: jika Anda menonaktifkan plugin mid-session, Claude Code tidak menghentikan monitor yang sudah berjalan. Mereka berhenti ketika sesi berakhir
1034
1035Kunci manifest `experimental.monitors` mengambil array yang sama inline atau jalur ke file JSON, dan dibaca daripada `monitors/monitors.json`.
1036
1037Untuk trigger `when` dan field lainnya, lihat [`monitors`](/docs/id/plugins/manifest-reference#monitors).
1038
1039<h2 id="user-configuration">
1040 Minta pengguna untuk nilai konfigurasi
1041</h2>
1042
1043Deklarasikan nilai yang dibutuhkan plugin Anda dari pengguna di kunci manifest `userConfig`, sehingga pengguna tidak mengedit `settings.json` sendiri. Setiap opsi muncul dalam dialog dengan `title`-nya sebagai label dan `description`-nya di bawahnya.
1044
1045Tetapkan `"sensitive": true` untuk token atau password. Dialog kemudian menutupi input, dan nilai disimpan dalam penyimpanan aman daripada `settings.json`.
1046
1047Manifest ini meminta endpoint dan token:
1048
1049```json .claude-plugin/plugin.json theme={null}
1050{
1051 "name": "my-plugin",
1052 "userConfig": {
1053 "api_url": {
1054 "type": "string",
1055 "title": "API URL",
1056 "description": "Base URL of your team's API"
1057 },
1058 "api_token": {
1059 "type": "string",
1060 "title": "API token",
1061 "description": "Token for your team's API",
1062 "sensitive": true
1063 }
1064 }
1065}
1066```
1067
1068<h3 id="when-the-configuration-dialog-appears">
1069 Kapan dialog konfigurasi muncul
1070</h3>
1071
1072Dialog muncul hanya dalam antarmuka `/plugin` interaktif. Itu terbuka untuk opsi apa pun yang belum ditetapkan ketika pengguna melakukan salah satu dari berikut:
1073
1074* Memasang plugin di `/plugin`
1075* Menjalankan `/plugin install <plugin>@<marketplace>` di dalam sesi
1076* Mengaktifkan plugin dari tab **Installed** di `/plugin`
1077
1078Untuk membuka dialog yang sama kapan saja, pengguna menjalankan `/plugin configure <plugin>@<marketplace>`.
1079
1080Perintah shell `claude plugin install` tidak pernah meminta nilai `userConfig`. Untuk menetapkan nilai dari shell, lewatkan masing-masing sebagai `--config KEY=VALUE`. Ketika opsi tetap tidak ditetapkan, perintah mencetak baris `userConfig options not yet set` yang menamai kedua cara untuk menetapkannya. [Dialog `userConfig` tidak pernah muncul](/docs/id/plugins/troubleshooting#the-userconfig-dialog-never-appears) mengutip baris.
1081
1082Untuk field opsi, di mana setiap nilai disimpan, bagaimana komponen mereferensikan nilai yang disimpan, dan field mana yang menolak `${user_config.*}`, lihat [User configuration](/docs/id/plugins/manifest-reference#user-configuration).
1083
1084<h2 id="path-variables-and-persistent-data">
1085 Referensikan jalur plugin dan simpan data
1086</h2>
1087
1088Anda tidak tahu di mana plugin Anda akan dipasang, jadi referensikan file dan data-nya melalui variabel ini daripada jalur tetap. Mereka disubstitusikan dalam skill, command, dan agent content, dalam hook dan monitor commands, dan dalam konfigurasi server MCP dan LSP. Mereka juga diekspor ke hook, MCP, dan proses LSP:
1089
1090* **`${CLAUDE_PLUGIN_ROOT}`**: direktori instalasi plugin. Setiap versi memiliki [cache directory](/docs/id/plugins/loading#find-plugins-on-disk)-nya sendiri, jadi jalur berubah ketika plugin diperbarui. Jangan tulis state di sana
1091* **`${CLAUDE_PLUGIN_DATA}`**: direktori yang bertahan dari update, untuk `node_modules`, virtual environments, dan caches. Itu diselesaikan ke `~/.claude/plugins/data/<id>/` dan dibuat ketika pertama kali direferensikan
1092* **`${CLAUDE_PROJECT_DIR}`**: root proyek, nilai yang sama yang diterima hooks
1093
1094Dalam jalur direktori data, `<id>` adalah identifier plugin dengan setiap karakter selain huruf, digit, `_`, dan `-` diganti dengan `-`, jadi `my-plugin@my-marketplace` menjadi `my-plugin-my-marketplace`.
1095
1096Di Windows, jalur yang disubstitusikan menggunakan forward slashes sehingga shell tidak membaca backslashes sebagai escapes.
1097
1098<h3 id="install-dependencies-into-the-data-directory">
1099 Pasang dependensi ke direktori data
1100</h3>
1101
1102Untuk plugin yang dipasang marketplace, Claude Code memasang [dependensi paket Node.js](/docs/id/plugins/loading#node-js-package-dependencies) yang memenuhi syarat secara otomatis ketika itu cache plugin, jadi Anda mungkin tidak perlu memasangnya sendiri. Ketika Anda melakukannya, hook `SessionStart` ini memasang `node_modules` ke `${CLAUDE_PLUGIN_DATA}` pada run pertama dan lagi setelah update mengubah `package.json`:
1103
1104```json hooks/hooks.json theme={null}
1105{
1106 "hooks": {
1107 "SessionStart": [
1108 {
1109 "hooks": [
1110 {
1111 "type": "command",
1112 "command": "diff -q \"${CLAUDE_PLUGIN_ROOT}/package.json\" \"${CLAUDE_PLUGIN_DATA}/package.json\" >/dev/null 2>&1 || (cd \"${CLAUDE_PLUGIN_DATA}\" && cp \"${CLAUDE_PLUGIN_ROOT}/package.json\" . && npm install) || rm -f \"${CLAUDE_PLUGIN_DATA}/package.json\""
1113 }
1114 ]
1115 }
1116 ]
1117 }
1118}
1119```
1120
1121Setelah sesi pertama, `~/.claude/plugins/data/<id>/node_modules` ada. Server MCP kemudian dapat menetapkan `NODE_PATH` ke `${CLAUDE_PLUGIN_DATA}/node_modules` di `env`-nya. Untuk field mana yang mensubstitusikan variabel mana, lihat [Environment variables](/docs/id/plugins/manifest-reference#environment-variables).
1122
1123<h2 id="next-steps">
1124 Langkah berikutnya
1125</h2>
1126
1127* [Referensi manifest plugin](/docs/id/plugins/manifest-reference): field `plugin.json`, aturan jalur, dan layout standar
1128* [Test plugins dengan evals](/docs/id/plugin-evals): periksa bahwa komponen yang Anda tambahkan mengubah perilaku Claude dengan cara yang Anda maksudkan
1129* [Publikasikan dan distribusikan plugin](/docs/id/plugins/publish): versi plugin dan letakkan di marketplace
1130* [Troubleshoot plugins](/docs/id/plugins/troubleshooting): apa yang harus dilakukan ketika komponen tidak dimuat atau hook tidak dipecat