Paul O'Reilly 85dfa2ef2e Fix reflect skill: remove $() substitution that fails permission checks
The !`command` blocks in SKILL.md go through Claude Code's Bash permission
checker, which rejects $() command substitution. Simplified the git log
command, replaced dynamic verify script path with a Read tool instruction,
and narrowed allowed-tools to specific command patterns. Also documented
the constraints in CLAUDE.md for future skill development.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 20:06:52 +13:00

3.0 KiB

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-name>/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/<name> → this repo's skills/<name>/
  • 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 binaryBash(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 onesBash(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.