# Skills in Claude Code

Package the ticket-service migration procedure as a Claude Code skill in .claude/skills/add-migration/ that Claude loads on its own or you run with /add-migration.

Source: https://ai-sw-factory.mellicci.dev/fundamentals/skills/claude-code

## At a glance

| | Claude Code |
|---|---|
| Term | Skill (`SKILL.md`). Custom commands in `.claude/commands/` have been merged into skills |
| Configured in | `.claude/skills/<name>/SKILL.md` (project), `~/.claude/skills/<name>/SKILL.md` (personal) |
| Loads / runs | Name and description are listed in context; the body loads when you type `/<name>` or Claude invokes the skill |
| Scope & precedence | Enterprise, personal, project. Same name: enterprise over personal, personal over project. Plugin skills are namespaced |

## Build the scenario

| Step | What you write or run | Where |
|---|---|---|
| 1 | Create the skill folder | `.claude/skills/add-migration/` |
| 2 | Write the instructions and frontmatter | `SKILL.md` |
| 3 | Add the conventions and the check script (shown on the [example scenario](https://ai-sw-factory.mellicci.dev/fundamentals/skills/example-scenario)) | `references/drizzle-conventions.md`, `scripts/check-migration.sh` |
| 4 | Run `/add-migration add a priority column to tickets`, or ask for the change in plain words | Claude Code session |

`.claude/skills/add-migration/SKILL.md`:

```markdown
---
name: add-migration
description: "Change the ticket-service database schema safely: edit src/db/schema.ts, generate a migration with pnpm db:generate, and verify it. Use for any new table, column or index."
argument-hint: "[schema change]"
allowed-tools: Bash(ls src/db/migrations) Bash(pnpm db:generate) Bash(${CLAUDE_SKILL_DIR}/scripts/check-migration.sh)
---
Existing migrations: !`ls src/db/migrations`
Requested change: $ARGUMENTS

1. Read `${CLAUDE_SKILL_DIR}/references/drizzle-conventions.md`.
2. Edit `src/db/schema.ts` for the requested change.
3. Run `pnpm db:generate`. Never edit files in `src/db/migrations/`.
4. Run `${CLAUDE_SKILL_DIR}/scripts/check-migration.sh` and fix what it reports.
5. Summarize the new migration file and its SQL.
```

The `!` line runs `ls` before Claude sees the skill, so the list of migrations is already in the prompt. The `allowed-tools` rules let the injected command, `pnpm db:generate` and the script run without a prompt during that turn.

## Specifics

### Locations and discovery

| Location | Path | Loads in |
|---|---|---|
| Enterprise | `.claude/skills/<name>/` in the managed settings directory | All users where the organization deploys it |
| Personal | `~/.claude/skills/<name>/SKILL.md` | All your projects on this machine |
| Project | `.claude/skills/<name>/SKILL.md` | Sessions in this repository; commit it |
| Nested | `<subdir>/.claude/skills/<name>/SKILL.md` | Loads when Claude reads or edits a file below `<subdir>` |
| Plugin | `<plugin>/skills/<name>/SKILL.md` | Wherever the plugin is enabled, as `/plugin:name` |

- Claude Code scans `.claude/skills/` in the start directory and each parent up to the repository root. Edits to skill files are picked up without a restart; a new top-level skills directory needs `/reload-skills`.
- Custom commands: `.claude/commands/deploy.md` and `.claude/skills/deploy/SKILL.md` both create `/deploy`. If both exist, the skill wins.
- Plugins can ship skills; see the [plugins](https://ai-sw-factory.mellicci.dev/fundamentals/plugins) concept.
- List skills with `/skills` (filter, sort by token count, change visibility). The `What skills are available?` prompt also works as a check.

### SKILL.md fields

All fields are optional; `description` is recommended. Claude Code ignores an unrecognized field without an error.

| Field | Effect |
|---|---|
| `name` | Command name; defaults to the directory name |
| `description`, `when_to_use` | What Claude matches against; combined text is cut at 1,536 characters |
| `argument-hint`, `arguments` | Autocomplete hint; named positional arguments (`$name`) |
| `disable-model-invocation` | `true`: only you can run the skill |
| `user-invocable` | `false`: only Claude can run it; hidden from the `/` menu |
| `allowed-tools` | Tools usable without a prompt during the invoking turn |
| `context`, `agent`, `background` | `context: fork` runs the skill in a subagent of type `agent` |
| `model`, `effort`, `hooks`, `paths`, `shell` | Per-skill model, effort, hooks, file globs, shell for `!` commands |

### Invocation and arguments

| Mode | How | Notes |
|---|---|---|
| Automatic | Claude matches the task to `description` | Default. The body then stays in context |
| Explicit | `/add-migration add a priority column to tickets` | Text after the name becomes the arguments |
| You only | `disable-model-invocation: true` | For side effects such as deploys |
| Claude only | `user-invocable: false` | Background knowledge |

| Placeholder | Expands to |
|---|---|
| `$ARGUMENTS` | Everything typed after the skill name |
| `$0`, `$1` or `$ARGUMENTS[N]` | Individual arguments (0-based, shell-style quoting) |
| `$name` | A name declared in `arguments` |

If you pass arguments but the body has no placeholder, Claude Code appends `ARGUMENTS: <value>`.

### Scripts and pre-injected data

| Mechanism | Syntax | Behavior |
|---|---|---|
| Injection, inline | `` !`command` `` at line start or after whitespace | Runs before Claude sees the skill; output replaces the line |
| Injection, block | a ```` ```! ```` fenced block | Multi-line commands |
| Script Claude runs | `${CLAUDE_SKILL_DIR}/scripts/x.sh` in the instructions | Claude runs it through the Bash tool |
| Permission | `allowed-tools: Bash(<same command>)` | Match the body's command exactly and it runs without a prompt |

An injected command that fails (or that no rule allows, outside auto mode) aborts the whole invocation. `disableSkillShellExecution: true` in settings replaces every injected command with a placeholder.

### Context cost and visibility

| Part | Cost |
|---|---|
| Name and description | In context every turn, within a budget of 1% of the context window |
| Body | Enters the conversation once when invoked, then stays in context |
| After compaction | Most recent invocation of each skill re-attached, first 5,000 tokens each, 25,000 in total |
| `disable-model-invocation: true` | Not listed at all; zero cost until you run it |
| Reference files and scripts | Cost nothing until read or run |

`/context` shows the Skills row, `/doctor` estimates the listing cost, and `/skill-doctor` reports per-skill cost and use. `skillOverrides` (`on`, `name-only`, `user-invocable-only`, `off`) changes visibility without editing the file. The `Skill(name)` and `Skill(name *)` permission rules allow or deny specific skills.

## Gotchas

- Quote a `description` that contains `: `, as in this page's example; unquoted, the YAML does not parse and the skill loads with no metadata, so Claude cannot match it.
- `ls src/db/migrations` exits non-zero if the folder is missing, which aborts the skill. Append `|| true` to injected commands that may fail.
- `allowed-tools` lasts for the invoking turn only and does not block other tools. Review it in skills you clone from other repositories.
- The body is fixed after loading; changed arguments or new injected output append the full body again.
- Keep `SKILL.md` under 500 lines and put important instructions first, because compaction truncates from the end.
