Research notes — compiled from the official Claude Code docs and the Agent Skills open standard
scripts/ Are Used by Claude Code! CommandsA skill is a folder containing a SKILL.md file (with YAML frontmatter + Markdown instructions) plus optional supporting files. Claude Code discovers skills in:
| Location | Path | Scope |
|---|---|---|
| Personal | ~/.claude/skills/<name>/SKILL.md | All your projects |
| Project | .claude/skills/<name>/SKILL.md | This project only |
| Plugin | <plugin>/skills/<name>/SKILL.md | Where plugin enabled |
| Enterprise | Managed settings | Entire org |
Skills follow the Agent Skills open standard (adopted by Claude Code, Cursor, VS Code, Gemini CLI, Roo Code, and many others). Claude Code extends the standard with invocation control, subagent execution, and dynamic context injection.
my-skill/
├── SKILL.md # Required — metadata + instructions (the entrypoint)
├── scripts/ # Optional — executable code Claude can run
│ ├── validate.sh
│ └── process.py
├── references/ # Optional — extra docs loaded on demand
│ └── REFERENCE.md
├── assets/ # Optional — templates, schemas, data files
│ └── template.json
└── examples/ # Optional — example outputs
└── sample.md
Key points from the spec:
SKILL.md is the only required file. Everything else is optional.scripts/, references/, assets/) are loaded on demand, not automatically injected into context.SKILL.md should be kept under 500 lines. Move detailed material to separate files.scripts/ Are Used by Claude CodeScripts are not automatically executed when a skill loads. They are files that Claude is instructed to run via the Bash tool, according to the instructions in SKILL.md. The scripts/ directory is simply a conventional place to put executable code that the skill's instructions tell Claude to run.
Here's the full lifecycle:
name and description into context so Claude knows what's available (~100 tokens per skill)./skill-name, or by Claude when it decides the skill is relevant), the full SKILL.md body is loaded into Claude's context.Bash tool to execute them.Scripts are not magic. They are regular files (Python, Bash, JS, etc.) that Claude runs via its Bash tool. The scripts/ directory is purely a convention — you reference scripts from SKILL.md using relative paths, and Claude executes them as shell commands.
There are two approaches:
You reference scripts relative to the skill directory root. Claude resolves these automatically:
# In SKILL.md:
Run the validation script:
```bash
bash scripts/validate.sh "$INPUT_FILE"
```
${CLAUDE_SKILL_DIR}For scripts that need absolute paths (e.g., when the working directory might be different), use the ${CLAUDE_SKILL_DIR} substitution variable, which resolves to the directory containing the skill's SKILL.md:
# In SKILL.md:
```bash
python ${CLAUDE_SKILL_DIR}/scripts/visualize.py .
```
Or with a hardcoded personal skill path:
python ~/.claude/skills/codebase-visualizer/scripts/visualize.py .
allowed-tools connectionIf your script needs Claude to run Bash commands, you can pre-approve them in frontmatter so Claude doesn't ask for permission each time:
---
name: my-skill
allowed-tools: Bash(python *), Bash(bash *)
---
These conventions come from the Agent Skills "Using Scripts" guide and are critical for making scripts work well with Claude (or any AI agent):
This is a hard requirement. Agents run scripts in non-interactive shells — they cannot respond to TTY prompts, password dialogs, or confirmation menus. A script that blocks on input will hang indefinitely.
# ❌ Bad: hangs waiting for input
$ python scripts/deploy.py
Target environment: _
# ✅ Good: accepts input via flags
$ python scripts/deploy.py --env staging --tag v1.2.3
# ✅ Good: clear error when required args missing
$ python scripts/deploy.py
Error: --env is required. Options: development, staging, production.
Usage: python scripts/deploy.py --env staging --tag v1.2.3
--help--help output is the primary way an agent learns your script's interface. Include a brief description, available flags, and usage examples:
Usage: scripts/process.py [OPTIONS] INPUT_FILE
Process input data and produce a summary report.
Options:
--format FORMAT Output format: json, csv, table (default: json)
--output FILE Write output to FILE instead of stdout
--verbose Print progress to stderr
Examples:
scripts/process.py data.csv
scripts/process.py --format csv --output report.csv data.csv
When the agent gets an error, the message directly shapes its next attempt. Say what went wrong, what was expected, and what to try:
# ❌ Opaque
Error: invalid input
# ✅ Actionable
Error: --format must be one of: json, csv, table. Received: "xml"
Prefer JSON, CSV, or TSV over free-form text. Send structured data to stdout and diagnostics to stderr:
# ❌ Hard to parse
NAME STATUS CREATED
my-service running 2025-01-15
# ✅ Unambiguous
{"name": "my-service", "status": "running", "created": "2025-01-15"}
| Principle | Why |
|---|---|
| Idempotency | Agents may retry commands. "Create if not exists" is safer than "create and fail on duplicate." |
| Input constraints | Reject ambiguous input with a clear error. Use enums and closed sets where possible. |
| Dry-run support | For destructive operations, a --dry-run flag lets the agent preview what will happen. |
| Meaningful exit codes | Use distinct exit codes for different failure types and document them in --help. |
| Safe defaults | Destructive operations should require explicit --confirm or --force flags. |
| Predictable output size | Agent harnesses truncate output beyond ~10-30K chars. Default to summaries; support --offset or --output FILE for large data. |
List available scripts and tell the agent how to run them:
## Available scripts
- **`scripts/validate.sh`** — Validates configuration files
- **`scripts/process.py`** — Processes input data
## Workflow
1. Run the validation script:
```bash
bash scripts/validate.sh "$INPUT_FILE"
```
2. Process the results:
```bash
python3 scripts/process.py --input results.json
```
Script execution paths (in code blocks) are relative to the skill directory root, because the agent runs commands from there. The same relative-path convention works in support files like references/*.md.
When you need third-party packages, use inline dependency declarations so the script is self-contained — no separate requirements.txt or install step needed:
#!/usr/bin/env python3
# /// script
# dependencies = [
# "beautifulsoup4>=4.12,<5",
# "requests>=2.31",
# ]
# requires-python = ">=3.10"
# ///
from bs4 import BeautifulSoup
import requests
# ... your code ...
Run with: uv run scripts/extract.py — uv creates an isolated environment, installs deps, and runs it.
import { parse } from "jsr:@std/csv@1.0.4";
// ... Deno auto-resolves jsr: and npm: imports
Run with: deno run scripts/process.ts
import { $ } from "bun";
// bun auto-installs missing packages
Tip: Pin versions in your inline dependencies (e.g. "beautifulsoup4>=4.12,<5") so the script behaves the same over time.
! CommandsClaude Code extends the Agent Skills standard with a preprocessing feature: the !`command` syntax runs shell commands before the skill content is sent to Claude. Output replaces the placeholder:
---
name: pr-summary
description: Summarize changes in a pull request
context: fork
agent: Explore
allowed-tools: Bash(gh *)
---
## Pull request context
- PR diff: !`gh pr diff`
- PR comments: !`gh pr view --comments`
- Changed files: !`gh pr diff --name-only`
## Your task
Summarize this pull request...
This is preprocessing, not something Claude executes. Claude only sees the final rendered output. This is different from scripts in scripts/ which Claude actively runs during its task.
This example from the official docs shows the complete pattern — a skill that bundles a Python script to generate an interactive HTML visualization:
~/.claude/skills/codebase-visualizer/
├── SKILL.md
└── scripts/
└── visualize.py
---
name: codebase-visualizer
description: Generate an interactive collapsible tree visualization of your
codebase. Use when exploring a new repo, understanding project structure,
or identifying large files.
allowed-tools: Bash(python *)
---
# Codebase Visualizer
Generate an interactive HTML tree view of your project's file structure.
## Usage
Run the visualization script from your project root:
```bash
python ~/.claude/skills/codebase-visualizer/scripts/visualize.py .
```
This creates `codebase-map.html` in the current directory and opens it
in your default browser.
## What the visualization shows
- **Collapsible directories**: Click folders to expand/collapse
- **File sizes**: Displayed next to each file
- **Colors**: Different colors for different file types
- **Directory totals**: Shows aggregate size of each folder
A regular Python script using only stdlib (json, sys, webbrowser, pathlib, collections). It scans a directory tree and generates a self-contained HTML file. Key properties:
"Generated /path/to/codebase-map.html")| # | Convention | Details |
|---|---|---|
| 1 | Place scripts in scripts/ | Standard directory name per the Agent Skills spec |
| 2 | Document scripts in SKILL.md | List each script with a description and exact run command |
| 3 | No interactive prompts | Accept all input via CLI flags, env vars, or stdin |
| 4 | Implement --help | Agent's primary way to learn the interface |
| 5 | Helpful error messages | Say what went wrong, what was expected, and what to try |
| 6 | Structured output (JSON/CSV) | stdout for data, stderr for diagnostics |
| 7 | Be idempotent | Agents may retry; "create if not exists" is safer |
| 8 | Pin dependency versions | Use PEP 723 inline deps with version ranges |
| 9 | Self-contained if possible | Use uv run/deno run/npx for auto-dependency resolution |
| 10 | Meaningful exit codes | Different codes for different failure types |
| 11 | Safe defaults | Destructive ops need --confirm or --force |
| 12 | Predictable output size | Default to summaries; support --offset for pagination |
| 13 | Set allowed-tools | Pre-approve Bash(python *) etc. so Claude doesn't ask each time |
| 14 | Use ${CLAUDE_SKILL_DIR} | For absolute paths that work regardless of cwd |