Create custom skills changedskills/how-to
Nearest release: v2.1.284, published 16 hours after upstream edited the page. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.
Upstream edited this page at 28 Sep 2026 00:41 UTC, give or take a minute or two: the time comes from Anthropic’s own sitemap rather than from a commit. This site recorded the change at 20 Oct 2026 18:07 UTC.
Upstream edited
Recorded here
Lines+119added
Lines−488removed
From line
2
where the diff opens
First seen
14 Aug 2026
this site's first read of the page
Recorded edits7to this page, all time
The whole hunk
from line 2, old and new numbered
/
The two sides of this change are more than 400 edits apart, too far apart to line up, so this is the differ's own diff of it and the words inside a line are not marked.
from line 2
22
33> Create a custom skill for Claude: write the SKILL.md file, add resources and scripts, package the skill, and test it.
44
5export const Piece = ({id, children}) => <div className="pe-piece" data-piece={id}>{children}</div>;
6
7export const PluginExplorer = ({children, variant}) => {
8 const SKILL_PIECES = [{
9 id: 'skillmd',
10 required: 'Required',
11 name: 'SKILL.md',
12 path: 'brand-guidelines/SKILL.md',
13 lines: [{
14 depth: 0,
15 kind: 'file',
16 text: 'SKILL.md'
17 }],
18 href: 'https://agentskills.io/specification',
19 linkText: 'Go to the Agent Skills specification'
20 }, {
21 id: 'references',
22 name: 'Reference file',
23 path: 'brand-guidelines/references/voice-and-tone.md',
24 lines: [{
25 depth: 0,
26 kind: 'folder',
27 text: 'references/'
28 }, {
29 depth: 1,
30 kind: 'file',
31 text: 'voice-and-tone.md'
32 }],
33 href: 'https://agentskills.io/specification',
34 linkText: 'Go to the Agent Skills specification'
35 }, {
36 id: 'assets',
37 name: 'Asset',
38 path: 'brand-guidelines/assets/slide-template.md',
39 lines: [{
40 depth: 0,
41 kind: 'folder',
42 text: 'assets/'
43 }, {
44 depth: 1,
45 kind: 'file',
46 text: 'slide-template.md'
47 }],
48 href: 'https://agentskills.io/specification',
49 linkText: 'Go to the Agent Skills specification'
50 }, {
51 id: 'scripts',
52 name: 'Script',
53 path: 'brand-guidelines/scripts/check_contrast.py',
54 lines: [{
55 depth: 0,
56 kind: 'folder',
57 text: 'scripts/'
58 }, {
59 depth: 1,
60 kind: 'file',
61 text: 'check_contrast.py'
62 }],
63 href: 'https://code.claude.com/docs/en/skills',
64 linkText: 'Go to Skills in Claude Code'
65 }];
66 const PLUGIN_PIECES = [{
67 id: 'manifest',
68 required: 'Required',
69 name: 'Manifest',
70 path: '.claude-plugin/plugin.json',
71 lines: [{
72 depth: 0,
73 kind: 'folder',
74 text: '.claude-plugin/'
75 }, {
76 depth: 1,
77 kind: 'file',
78 text: 'plugin.json'
79 }],
80 href: '/plugins/build#write-the-manifest',
81 linkText: 'Go to Write the manifest'
82 }, {
83 id: 'skills',
84 name: 'Skill',
85 path: 'skills/file-expense/SKILL.md',
86 lines: [{
87 depth: 0,
88 kind: 'folder',
89 text: 'skills/'
90 }, {
91 depth: 1,
92 kind: 'folder',
93 text: 'file-expense/'
94 }, {
95 depth: 2,
96 kind: 'file',
97 text: 'SKILL.md'
98 }],
99 href: '/skills/how-to',
100 linkText: 'Go to Create custom skills'
101 }, {
102 id: 'references',
103 name: 'Skill reference file',
104 path: 'skills/file-expense/references/categories.md',
105 lines: [{
106 depth: 2,
107 kind: 'folder',
108 text: 'references/'
109 }, {
110 depth: 3,
111 kind: 'file',
112 text: 'categories.md'
113 }],
114 href: '/skills/how-to#add-resources',
115 linkText: 'Go to Add resources'
116 }, {
117 id: 'scripts',
118 name: 'Skill script',
119 path: 'skills/file-expense/scripts/total.py',
120 lines: [{
121 depth: 2,
122 kind: 'folder',
123 text: 'scripts/'
124 }, {
125 depth: 3,
126 kind: 'file',
127 text: 'total.py'
128 }],
129 href: '/skills/how-to#add-scripts',
130 linkText: 'Go to Add scripts'
131 }, {
132 id: 'commands',
133 name: 'Command',
134 path: 'commands/summarize.md',
135 lines: [{
136 depth: 0,
137 kind: 'folder',
138 text: 'commands/'
139 }, {
140 depth: 1,
141 kind: 'file',
142 text: 'summarize.md'
143 }],
144 href: '/plugins/platform-support#compare-component-support-by-app',
145 linkText: 'Go to component support by app'
146 }, {
147 id: 'mcp',
148 name: 'MCP connector',
149 path: '.mcp.json',
150 lines: [{
151 depth: 0,
152 kind: 'file',
153 text: '.mcp.json'
154 }],
155 href: '/plugins/build#bundle-an-mcp-connector-with-its-skill',
156 linkText: 'Go to Bundle an MCP connector with its skill'
157 }, {
158 id: 'readme',
159 required: 'Required to publish',
160 name: 'README',
161 path: 'README.md',
162 lines: [{
163 depth: 0,
164 kind: 'file',
165 text: 'README.md'
166 }],
167 href: '/plugins/pre-submission-checklist#readme-and-license',
168 linkText: 'Go to README and license checks'
169 }, {
170 id: 'license',
171 required: 'Required to publish',
172 name: 'License',
173 path: 'LICENSE',
174 lines: [{
175 depth: 0,
176 kind: 'file',
177 text: 'LICENSE'
178 }],
179 href: '/plugins/pre-submission-checklist#readme-and-license',
180 linkText: 'Go to README and license checks'
181 }];
182 const isSkill = variant === 'skill';
183 const PIECES = isSkill ? SKILL_PIECES : PLUGIN_PIECES;
184 const rootLabel = isSkill ? 'brand-guidelines/' : 'expense-reports/';
185 const title = isSkill ? 'What goes in the skill folder' : 'What goes in the plugin folder';
186 const treeCaption = isSkill ? 'Skill folder' : 'Plugin folder';
187 const [selectedId, setSelectedId] = useState(isSkill ? 'skillmd' : 'manifest');
188 const [isFullscreen, setIsFullscreen] = useState(false);
189 const rootRef = useRef(null);
190 useEffect(() => {
191 const onFsChange = () => setIsFullscreen(!!document.fullscreenElement);
192 document.addEventListener('fullscreenchange', onFsChange);
193 return () => document.removeEventListener('fullscreenchange', onFsChange);
194 }, []);
195 const toggleFullscreen = () => {
196 if (!rootRef.current) return;
197 if (document.fullscreenElement) document.exitFullscreen(); else rootRef.current.requestFullscreen().catch(() => {});
198 };
199 const selected = PIECES.find(p => p.id === selectedId) || PIECES[0];
200 const onTreeKeyDown = e => {
201 const keys = ['ArrowDown', 'ArrowUp', 'Home', 'End'];
202 if (keys.indexOf(e.key) === -1) return;
203 const i = PIECES.findIndex(p => p.id === selectedId);
204 let next = i;
205 if (e.key === 'ArrowDown') next = Math.min(PIECES.length - 1, i + 1);
206 if (e.key === 'ArrowUp') next = Math.max(0, i - 1);
207 if (e.key === 'Home') next = 0;
208 if (e.key === 'End') next = PIECES.length - 1;
209 e.preventDefault();
210 if (next === i) return;
211 const id = PIECES[next].id;
212 setSelectedId(id);
213 const el = document.getElementById('pe-node-' + id);
214 if (el) el.focus();
215 };
216 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">
217 <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" />
218 </svg>;
219 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">
220 <path d="M4 1.5h5.5L13 5v9.5H4z" />
221 <path d="M9.5 1.5V5H13" />
222 </svg>;
223 return <div ref={rootRef} className={isFullscreen ? 'pe-root pe-fullscreen not-prose' : 'pe-root not-prose'} data-selected={selected.id}>
224 <style>{`
225 .pe-root {
226 --pe-mono: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
227 --pe-accent: #D97757;
228 --pe-accent-text: #A8502F;
229 --pe-accent-bg: rgba(217,119,87,0.10);
230 --pe-bg: #FFFFFF;
231 --pe-surface: #FAFAF7;
232 --pe-hover: #F0EEE6;
233 --pe-border: #E8E6DC;
234 --pe-text: #141413;
235 --pe-text-2: #3D3D3A;
236 --pe-text-3: #5E5D59;
237 font-family: inherit;
238 background: var(--pe-bg);
239 color: var(--pe-text);
240 border: 1px solid var(--pe-border);
241 border-radius: 12px;
242 margin: 1.5rem 0;
243 overflow: hidden;
244 box-sizing: border-box;
245 }
246 .dark .pe-root {
247 --pe-accent-text: #EBA98F;
248 --pe-accent-bg: rgba(217,119,87,0.18);
249 --pe-bg: #1A1918;
250 --pe-surface: #232221;
251 --pe-hover: #2E2D2B;
252 --pe-border: #3A3936;
253 --pe-text: #F1EFE9;
254 --pe-text-2: #D6D4CA;
255 --pe-text-3: #B8B5AD;
256 }
257 .pe-root *, .pe-root *::before, .pe-root *::after { box-sizing: border-box; }
258 .pe-head { display: flex; align-items: flex-start; gap: 12px; padding: 18px 24px 16px; border-bottom: 1px solid var(--pe-border); }
259 .pe-head-text { flex: 1; min-width: 0; }
260 .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; }
261 .pe-fs-btn:hover { background: var(--pe-hover); }
262 .pe-fs-btn:focus-visible { outline: 2px solid var(--pe-accent); outline-offset: 2px; }
263 .pe-fullscreen { border-radius: 0; height: 100vh; display: flex; flex-direction: column; overflow: auto; }
264 .pe-fullscreen .pe-body { flex: 1; }
265 .pe-title { font-size: 19px; font-weight: 600; line-height: 1.3; color: var(--pe-text); margin: 0; }
266 .pe-sub { font-size: 15px; line-height: 1.5; color: var(--pe-text-3); margin: 4px 0 0; }
267 .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); }
268 .pe-body { display: flex; align-items: stretch; }
269 .pe-tree-pane { width: 270px; flex-shrink: 0; background: var(--pe-surface); border-right: 1px solid var(--pe-border); padding: 16px 0 12px; }
270 .pe-panel { flex: 1; min-width: 0; padding: 16px 24px 24px; }
271 .pe-caption { font-size: 13px; font-weight: 600; color: var(--pe-text-3); margin: 0 0 10px; }
272 .pe-tree-pane .pe-caption { padding: 0 16px; }
273 .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); }
274 .pe-node {
275 display: block; width: 100%; margin: 0; padding: 3px 16px 3px 30px; text-align: left; cursor: pointer;
276 background: transparent; color: var(--pe-text-2);
277 border: none; border-left: 3px solid transparent;
278 font-family: var(--pe-mono); font-size: 13.5px; line-height: 1.4;
279 }
280 .pe-node:hover { background: var(--pe-hover); }
281 .pe-node:focus-visible { outline: 2px solid var(--pe-accent); outline-offset: -2px; }
282 .pe-node[aria-pressed="true"] { background: var(--pe-accent-bg); border-left-color: var(--pe-accent); color: var(--pe-accent-text); font-weight: 600; }
283 .pe-line { display: flex; align-items: center; gap: 7px; padding: 2px 0; }
284 .pe-line span { overflow-wrap: anywhere; }
285 .pe-line > span:not(.pe-req) { white-space: nowrap; flex-shrink: 0; }
286 .pe-req { flex-shrink: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; margin-left: 8px; padding: 0 6px; border-radius: 999px; font-size: 11px; line-height: 18px; font-family: var(--pe-sans, inherit); letter-spacing: .02em; color: var(--pe-accent-text); border: 1px solid var(--pe-border); background: var(--pe-surface); white-space: nowrap; }
287 .pe-piece { display: none; font-size: 16px; line-height: 1.6; color: var(--pe-text-2); }
288 ${PIECES.map(p => '.pe-root[data-selected="' + p.id + '"] .pe-piece[data-piece="' + p.id + '"]').join(',\n ')} { display: block; }
289 .pe-piece p { margin: 0 0 10px; }
290 .pe-piece p:last-child { margin-bottom: 0; }
291 .pe-piece ul { list-style: disc; padding-left: 1.25em; margin: 0 0 10px; }
292 .pe-piece li { margin: 2px 0; }
293 .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); }
294 .pe-piece .code-block { margin: 12px 0 0; }
295 .pe-piece pre code { padding: 0; border: none; background: none; }
296 .pe-piece a { color: var(--pe-accent-text); }
297 .pe-line-compact { display: none; }
298 .pe-icon { flex-shrink: 0; }
299 .pe-name { font-size: 22px; font-weight: 600; line-height: 1.25; letter-spacing: -0.2px; color: var(--pe-text); margin: 0; }
300 .pe-path { font-family: var(--pe-mono); font-size: 13.5px; color: var(--pe-accent-text); margin: 4px 0 0; overflow-wrap: anywhere; }
301 .pe-block { margin: 20px 0 0; }
302 .pe-link {
303 display: inline-block; margin: 24px 0 0; padding: 8px 14px; border-radius: 8px;
304 font-size: 14.5px; font-weight: 600; text-decoration: none;
305 color: var(--pe-accent-text); background: var(--pe-accent-bg); border: 1px solid var(--pe-accent);
306 }
307 .pe-link:hover { filter: brightness(0.97); }
308 .pe-link:focus-visible { outline: 2px solid var(--pe-accent); outline-offset: 2px; }
309 @media (max-width: 700px) {
310 .pe-head { padding: 16px 16px 14px; }
311 .pe-body { flex-direction: column; }
312 .pe-tree-pane { width: 100%; border-right: none; border-bottom: 1px solid var(--pe-border); }
313 .pe-line-tree { display: none; }
314 .pe-line-compact { display: flex; }
315 .pe-panel { padding: 16px 16px 20px; }
316 }
317 `}</style>
318
319 <div className="pe-head">
320 <div className="pe-head-text">
321 <div className="pe-title">{title}</div>
322 {isSkill ? <div className="pe-sub">This example skill, <code>brand-guidelines</code>, has one of each kind of file a skill can carry. Select a file to read what it's for and see a minimal example.</div> : <div className="pe-sub">This example plugin, <code>expense-reports</code>, has one of each file that chat, Cowork, and Claude Code all load, plus the README and license the directory requires. Select a file to read what it’s for and see a minimal example.</div>}
323 </div>
324 <button type="button" className="pe-fs-btn" onClick={toggleFullscreen} aria-label={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'} title={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}>
325 {isFullscreen ? '⤡' : '⛶'}
326 </button>
327 </div>
328
329 <div className="pe-body">
330 <div className="pe-tree-pane">
331 <div className="pe-caption" id="pe-tree-caption">{treeCaption}</div>
332 <div role="group" aria-labelledby="pe-tree-caption" onKeyDown={onTreeKeyDown}>
333 <div className="pe-rootline"><FolderIcon /><span>{rootLabel}</span></div>
334 {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)}>
335 {p.lines.map((line, i) => <span key={i} className="pe-line pe-line-tree" style={{
336 paddingLeft: line.depth * 18 + 'px'
337 }}>
338 {line.kind === 'folder' ? <FolderIcon /> : <FileIcon />}
339 <span>{line.text}</span>
340 {p.required && i === p.lines.length - 1 ? <span className="pe-req" title={p.required}>Required</span> : null}
341 </span>)}
342 <span className="pe-line pe-line-compact">
343 <FileIcon />
344 <span>{p.path}</span>
345 {p.required ? <span className="pe-req" title={p.required}>Required</span> : null}
346 </span>
347 </button>)}
348 </div>
349 </div>
350
351 <div className="pe-panel" role="region" aria-labelledby="pe-panel-caption" aria-live="polite" aria-atomic="true">
352 <div className="pe-caption" id="pe-panel-caption">Selected file</div>
353 <div className="pe-name">{selected.name}{selected.required ? <span className="pe-req">{selected.required}</span> : null}</div>
354 <div className="pe-path">{selected.path}</div>
355
356 <div className="pe-block">{children}</div>
357
358 <a className="pe-link" href={selected.href}>{selected.linkText}</a>
359 </div>
360 </div>
361 </div>;
362};
363
3645A custom skill is a folder with a `SKILL.md` file of instructions, and optionally scripts and reference files, that Claude loads when a task matches the skill's description. This guide is for anyone writing a skill of their own. It explains how to create, structure, and test one.
3656
3667If you already know what the skill should do, start with the [directory structure](#directory-structure). If you're not sure a skill is the right tool, read [Decide what skill to create](#decide-what-skill-to-create) first.
3678
368<Note>
369 Skills follow the [Agent Skills specification](https://agentskills.io/specification). See the specification for more in-depth information.
370</Note>
9> [!NOTE]
10>
11> Skills follow the [Agent Skills specification](https://agentskills.io/specification). See the specification for more in-depth information.
37112
37213## Decide what skill to create
37314
from line 26
38526
38627A skill is a folder named after the skill. The only required file is `SKILL.md`; the other folders are optional and hold material that `SKILL.md` points Claude to. Select a file in the explorer to see what goes in it and what Claude does with it.
38728
388<PluginExplorer variant="skill">
389 <Piece id="skillmd">
390 <p>The one required file. Its frontmatter names and describes the skill, and Claude reads the <code>description</code> to decide when the skill applies. The body holds the instructions Claude follows once it does.</p>
391 <p>In this example, the skill applies Acme's brand guidelines and tells Claude where the supporting files are:</p>
392
393 ```markdown SKILL.md theme={null}
394 ---
395 name: brand-guidelines
396 description: Apply Acme Corp brand guidelines to presentations and documents, including official colors, fonts, and logo usage.
397 ---
398 Use these guidelines whenever you produce a document or deck for Acme.
399
400 1. Use the colors and fonts in the sections below.
401 2. For wording, follow `references/voice-and-tone.md`.
402 3. For slides, start from `assets/slide-template.md`.
403 4. Before you finish, run `python3 ${CLAUDE_SKILL_DIR}/scripts/check_contrast.py` on any color pairs you chose.
404 ```
405 </Piece>
406
407 <Piece id="references">
408 <p>Longer background that Claude reads only when a step calls for it, so it doesn't crowd <code>SKILL.md</code>. Mention the file by path at the step where Claude needs it.</p>
409 <p>In this example, the file holds the writing rules the instructions point to in step 2:</p>
410
411 ```markdown references/voice-and-tone.md theme={null}
412 # Voice and tone
413 - Write in the second person and the present tense.
414 - Prefer short sentences. Avoid exclamation marks.
415 - Product names are always capitalized: Acme Cloud, Acme Sync.
416 ```
417 </Piece>
418
419 <Piece id="assets">
420 <p>Templates, images, and data files that Claude copies or fills in rather than reads for guidance.</p>
421 <p>In this example, the asset is the slide outline that step 3 tells Claude to start from:</p>
422
423 ```markdown assets/slide-template.md theme={null}
424 # [Deck title]
425 ## Agenda
426 ## [Section 1]
427 ## [Section 2]
428 ## Next steps
429 ```
430 </Piece>
431
432 <Piece id="scripts">
433 <p>Code that Claude runs while following the skill, for work that is more reliable as a program than as instructions. Reference it from `SKILL.md` with `${CLAUDE_SKILL_DIR}` so the path resolves wherever the skill is installed.</p>
434 <p>In this example, the script checks that a text and background color pair has enough contrast:</p>
435
436 ```python scripts/check_contrast.py theme={null}
437 import sys
438
439 def luminance(hex_color):
440 r, g, b = (int(hex_color[i:i+2], 16) / 255 for i in (1, 3, 5))
441 f = lambda c: c / 12.92 if c <= 0.03928 else ((c + 0.055) / 1.055) ** 2.4
442 return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b)
443
444 fg, bg = sys.argv[1], sys.argv[2]
445 l1, l2 = sorted((luminance(fg), luminance(bg)), reverse=True)
446 ratio = (l1 + 0.05) / (l2 + 0.05)
447 print(f"{ratio:.2f}", "OK" if ratio >= 4.5 else "LOW")
448 ```
449 </Piece>
450</PluginExplorer>
29What goes in the skill folder
30
31This example skill, `brand-guidelines`, has one of each kind of file a skill can carry. Select a file to read what it's for and see a minimal example.
32
33Skill folder
34
35brand-guidelines/
36
37Selected file
38
39The one required file. Its frontmatter names and describes the skill, and Claude reads the `description` to decide when the skill applies. The body holds the instructions Claude follows once it does.In this example, the skill applies Acme's brand guidelines and tells Claude where the supporting files are:
40
41```markdown SKILL.md theme={null}
42---
43name: brand-guidelines
44description: Apply Acme Corp brand guidelines to presentations and documents, including official colors, fonts, and logo usage.
45---
46Use these guidelines whenever you produce a document or deck for Acme.
47
481. Use the colors and fonts in the sections below.
492. For wording, follow `references/voice-and-tone.md`.
503. For slides, start from `assets/slide-template.md`.
514. Before you finish, run `python3 ${CLAUDE_SKILL_DIR}/scripts/check_contrast.py` on any color pairs you chose.
52```
53
54Longer background that Claude reads only when a step calls for it, so it doesn't crowd `SKILL.md`. Mention the file by path at the step where Claude needs it.In this example, the file holds the writing rules the instructions point to in step 2:
55
56```markdown references/voice-and-tone.md theme={null}
57# Voice and tone
58- Write in the second person and the present tense.
59- Prefer short sentences. Avoid exclamation marks.
60- Product names are always capitalized: Acme Cloud, Acme Sync.
61```
62
63Templates, images, and data files that Claude copies or fills in rather than reads for guidance.In this example, the asset is the slide outline that step 3 tells Claude to start from:
64
65```markdown assets/slide-template.md theme={null}
66# [Deck title]
67## Agenda
68## [Section 1]
69## [Section 2]
70## Next steps
71```
72
73Code that Claude runs while following the skill, for work that is more reliable as a program than as instructions. Reference it from `SKILL.md` with `${CLAUDE_SKILL_DIR}` so the path resolves wherever the skill is installed.In this example, the script checks that a text and background color pair has enough contrast:
74
75```python scripts/check_contrast.py theme={null}
76import sys
77
78def luminance(hex_color):
79 r, g, b = (int(hex_color[i:i+2], 16) / 255 for i in (1, 3, 5))
80 f = lambda c: c / 12.92 if c <= 0.03928 else ((c + 0.055) / 1.055) ** 2.4
81 return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b)
82
83fg, bg = sys.argv[1], sys.argv[2]
84l1, l2 = sorted((luminance(fg), luminance(bg)), reverse=True)
85ratio = (l1 + 0.05) / (l2 + 0.05)
86print(f"{ratio:.2f}", "OK" if ratio >= 4.5 else "LOW")
87```
45188
45289As a plain tree, the same skill looks like this. The directory name must match the `name` field in your `SKILL.md`, and everything except `SKILL.md` is optional:
45390
from line 212
575212 └── scripts/
576213```
577214
578<Steps>
579 <Step title="Check the directory name">
580 Make sure the directory name matches the `name` field in `SKILL.md`.
581 </Step>
582
583 <Step title="Zip the directory from its parent folder">
584 Where the `zip` command is available, such as on macOS and Linux, run it from the folder that contains the skill directory, so the directory becomes the top level of the archive:
585
586 ```bash theme={null}
587 zip -r my-skill.zip my-skill/
588 ```
589
590 If you use another tool to create the ZIP, compress the skill folder itself rather than the files inside it.
591 </Step>
592
593 <Step title="Confirm the structure">
594 List the archive and check that every entry starts with `my-skill/`:
595
596 ```bash theme={null}
597 unzip -l my-skill.zip
598 ```
599
600 If `SKILL.md` appears without the `my-skill/` prefix, you zipped the contents instead of the folder; zip again from the parent folder.
601 </Step>
602</Steps>
215**1. Check the directory name**
216
217Make sure the directory name matches the `name` field in `SKILL.md`.
218
219**2. Zip the directory from its parent folder**
220
221Where the `zip` command is available, such as on macOS and Linux, run it from the folder that contains the skill directory, so the directory becomes the top level of the archive:
222
223```bash theme={null}
224zip -r my-skill.zip my-skill/
225```
226
227If you use another tool to create the ZIP, compress the skill folder itself rather than the files inside it.
228
229**3. Confirm the structure**
230
231List the archive and check that every entry starts with `my-skill/`:
232
233```bash theme={null}
234unzip -l my-skill.zip
235```
236
237If `SKILL.md` appears without the `my-skill/` prefix, you zipped the contents instead of the folder; zip again from the parent folder.
603238
604239To check the skill's contents rather than the archive shape, [validate it before uploading](#before-uploading) with `skills-ref validate` or `claude plugin validate`, or ask Claude to review the folder against the [Agent Skills specification](https://agentskills.io/specification).
605240
from line 246
611246
612247Before you upload the ZIP, check the skill's files:
613248
614<Steps>
615 <Step title="Review SKILL.md">
616 Review `SKILL.md` for clarity.
617 </Step>
618
619 <Step title="Check the description">
620 Verify the description accurately reflects when Claude should use the skill.
621 </Step>
622
623 <Step title="Check referenced files">
624 Check that all referenced files exist.
625 </Step>
626
627 <Step title="Validate the skill">
628 Check the frontmatter against the Agent Skills specification with the [`skills-ref` reference tool](https://github.com/agentskills/agentskills/tree/main/skills-ref). It isn't preinstalled: clone that repository and install it into a Python virtual environment as its README describes, which puts `skills-ref` on your `PATH` while the environment is active. Then, from the folder that contains the skill directory, run:
629
630 ```bash theme={null}
631 skills-ref validate ./my-skill
632 ```
633
634 A skill that passes prints `Valid skill: ./my-skill`. Otherwise the command lists each problem, such as a `name` that doesn't match the directory or a frontmatter field the specification doesn't define.
635
636 If the skill is inside a plugin folder, running `claude plugin validate ./my-plugin` in your terminal also parses each skill's frontmatter: it reports a `SKILL.md` whose frontmatter doesn't parse, and prints `✔ Validation passed` when the plugin passes.
637 </Step>
638</Steps>
249**1. Review SKILL.md**
250
251Review `SKILL.md` for clarity.
252
253**2. Check the description**
254
255Verify the description accurately reflects when Claude should use the skill.
256
257**3. Check referenced files**
258
259Check that all referenced files exist.
260
261**4. Validate the skill**
262
263Check the frontmatter against the Agent Skills specification with the [`skills-ref` reference tool](https://github.com/agentskills/agentskills/tree/main/skills-ref). It isn't preinstalled: clone that repository and install it into a Python virtual environment as its README describes, which puts `skills-ref` on your `PATH` while the environment is active. Then, from the folder that contains the skill directory, run:
264
265```bash theme={null}
266skills-ref validate ./my-skill
267```
268
269A skill that passes prints `Valid skill: ./my-skill`. Otherwise the command lists each problem, such as a `name` that doesn't match the directory or a frontmatter field the specification doesn't define.
270
271If the skill is inside a plugin folder, running `claude plugin validate ./my-plugin` in your terminal also parses each skill's frontmatter: it reports a `SKILL.md` whose frontmatter doesn't parse, and prints `✔ Validation passed` when the plugin passes.
639272
640273### In Claude Code
641274
from line 278
645278
646279After you upload, confirm that Claude loads the skill when it should:
647280
648<Steps>
649 <Step title="Turn the skill on">
650 Go to [**Customize > Skills**](https://claude.ai/customize/skills) in claude.ai or the desktop app and turn the skill on.
651 </Step>
652
653 <Step title="Try prompts that should trigger it">
654 Send prompts that should trigger the skill, and review Claude's thinking to confirm it's loading the skill.
655 </Step>
656
657 <Step title="Iterate on the description">
658 Iterate on the description if Claude isn't using it when expected.
659 </Step>
660</Steps>
281**1. Turn the skill on**
282
283Go to [**Customize > Skills**](https://claude.ai/customize/skills) in claude.ai or the desktop app and turn the skill on.
284
285**2. Try prompts that should trigger it**
286
287Send prompts that should trigger the skill, and review Claude's thinking to confirm it's loading the skill.
288
289**3. Iterate on the description**
290
291Iterate on the description if Claude isn't using it when expected.
661292
662293### Measure whether the skill improves the output
663294
664295
No line in this hunk matches that.