Includes: spec-driven and test-driven development best practices, reproduce-before-fixing debugging workflow, require-plan-file hook, find-project-root script, session logs, memory files for decisions/ gotchas/process-lessons, and updates to existing best practice topics. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2.7 KiB
2.7 KiB
Claude Code Skills
Skill Structure
- Each skill lives in
skills/<skill-name>/SKILL.md - Skills should be project-agnostic where possible — use dynamic context injection to adapt
- After adding a new skill, run the install script to register it
- Skills only useful for one project should live in that project's
.claude/skills/instead
Authoring Guidelines
- Inline by default — only use
context: forkif the skill genuinely doesn't need conversation history - Pre-fetch context with
!command`` injection to reduce tool calls during execution - Restrict tools with
allowed-toolsto the minimum needed — reduces permission prompts - Use $ARGUMENTS for user input,
$0,$1etc. for positional args - Dynamic commands in
!command`` run at skill load time, not during Claude's execution
Portable Path Resolution
- Use
CLAUDE_PROJECT_ROOTenv var for cross-project path references in!command`` blocks. Hardcoded absolute paths (e.g.,~/dev/claude/...) are user-specific. Relative paths (../) break depending on CWD and can trigger sandbox violations when they resolve outside allowed directories. - Add a detection fallback. Include a "Step 0" in skill instructions that detects the project root by walking up the directory tree to find the highest
CLAUDE.mdif the env var isn't set. This makes skills work even without prior setup. - Keep config paths relative to the root. Settings files should use paths relative to
CLAUDE_PROJECT_ROOT(e.g.,projects_dir: projects) rather than absolute paths, so they're portable across machines.
Skill Discovery Timing
- Skills are discovered at session start, not dynamically. Creating or symlink a new skill mid-session requires restarting Claude Code to use it as a slash command.
- Broken symlinks cause silent failures under
set -e.readlink -fon a broken symlink returns empty string. The install script should validate symlinks and remove stale ones.
!command`` Gotchas
- No
$()command substitution — the permission checker rejects commands containing$() - No complex shell pipelines relying on subshells — keep commands simple and self-contained
allowed-toolspatterns must match the command binary — each binary used in!command`` blocks needs its own pattern- Prefer specific tool patterns over broad ones —
Bash(git log *)is safer thanBash(git *) - Fallback to tool instructions for dynamic paths — if a command needs
$ARGUMENTSto compute a path, use a plain-text instruction telling Claude to use the Read tool instead - Env var expansion works —
${CLAUDE_PROJECT_ROOT}expands in!command`` blocks because they run as shell commands. This is the recommended pattern for portable cross-project paths.