# Custom Claude Skills Reusable Claude Code skills shared across all projects via symlinks into `~/.claude/skills/`. ## Repository Structure ``` custom-claude-skills/ ├── CLAUDE.md # This file ├── MEMORY.md # Learnings about skill development ├── FUTURE.md # Future skill ideas ├── README.md # Setup instructions and skill catalogue ├── scripts/ │ └── install.sh # Symlinks all skills into ~/.claude/skills/ └── skills/ └── reflect/ └── SKILL.md # Milestone reflection skill ``` ## Conventions - Each skill lives in `skills//SKILL.md` - Skills should be project-agnostic where possible — use dynamic context injection (`!`command``) to adapt to the current project - The `scripts/install.sh` script creates symlinks from `~/.claude/skills/` → this repo's `skills//` - After adding a new skill, run `./scripts/install.sh` to register it - Skills that are only useful for one project should live in that project's `.claude/skills/` instead ## Skill Development Guidelines - **Inline by default** — only use `context: fork` if the skill genuinely doesn't need conversation history - **Pre-fetch context** with `!`command`` injection to reduce tool calls during execution - **Restrict tools** with `allowed-tools` to the minimum needed — this reduces permission prompts - **Use $ARGUMENTS** for user input, `$0`, `$1` etc. for positional args - Dynamic commands in `!`command`` run at skill load time, not during Claude's execution ## `!`command`` Gotchas The `!`command`` syntax in SKILL.md runs shell commands at skill load time to pre-gather context. These commands go through Claude Code's Bash permission checker against the `allowed-tools` patterns. Key constraints: - **No `$()` command substitution** — the permission checker rejects commands containing `$()` even if the outer command matches an allowed pattern. This means you cannot nest subshells (e.g., `git log --since="$(git log ...)"` will fail). - **No complex shell pipelines relying on subshells** — keep `!`command`` commands simple and self-contained. If you need complex logic, have the skill instructions tell Claude to run it via tool calls instead. - **`allowed-tools` patterns must match the command binary** — `Bash(git *)` allows any `git` subcommand, but `Bash(cat *)`, `Bash(sed *)`, `Bash(ls *)` must be listed separately if those commands appear in `!`command`` blocks. - **Prefer specific tool patterns over broad ones** — `Bash(git log *)`, `Bash(git diff *)` is safer than `Bash(git *)` to avoid accidentally allowing destructive git commands (e.g., `git reset`, `git push --force`). - **Fallback to tool instructions for dynamic paths** — if a command needs `$ARGUMENTS` to compute a file path (e.g., `cat scripts/verify-m${N}.sh`), replace the `!`command`` with a plain-text instruction telling Claude to use the Read tool. This avoids `$()` substitution and is more reliable.