Dynamic Context in Skills
Skills are static SKILL.md files — but they can pull live data at invocation time. Add a ## Context block and Claude Code evaluates shell commands and injects the results before Claude reads your instructions.
What is dynamic context?
When Claude Code runs a skill, it processes the SKILL.md file before sending anything to Claude. If you include a ## Context section with shell substitution expressions, Claude Code evaluates them and injects the real output into the prompt.
| Data | Expression | Type |
|---|---|---|
| Today's date | $CURRENT_DATE | Built-in variable |
| Git branch | $(git branch --show-current) | Shell substitution |
| Staged files | $(git diff --cached --name-only) | Shell substitution |
| Full staged diff | $(git diff --cached) | Shell substitution |
| Last commit | $(git log -1 --pretty='%h %s') | Shell substitution |
| Env variable | $(echo $NODE_ENV) | Shell substitution |
How it works
- You type a slash command (e.g.
/standup) - Claude Code reads SKILL.md
- Shell commands in ## Context run; output is injected
- Claude sees live data plus your Steps instructions
Before and after — /standup
Compare the same skill with and without dynamic context to see the difference.
Without dynamic context
--- name: standup description: Write my daily standup update. --- ## Steps 1. Ask the user what they worked on yesterday and today. 2. Format as three bullet points: Yesterday / Today / Blockers.
Claude has no idea what branch you are on or what changed. It will ask you — or guess.
With dynamic context
--- name: standup description: Write my daily standup update. --- ## Context - Today's date: $CURRENT_DATE - Current git branch: $(git branch --show-current) - Staged files: $(git diff --cached --name-only) ## Steps 1. Use the branch name to infer the feature being worked on. 2. Use the staged file list to determine what changed. 3. Format as three bullet points: Yesterday / Today / Blockers. 4. Populate "Today" automatically from the staged files.
Claude already knows today's date, the branch, and which files changed.
/standup — live output
Below is what the terminal looks like when you run /standup with dynamic context enabled.
Example — /review with staged diff
Injecting the full staged diff means Claude reviews exactly the code you are about to commit — not the whole file, not a pasted snippet.
--- name: review description: Review the code I am about to commit. --- ## Context - Current date: $CURRENT_DATE - Branch: $(git branch --show-current) - Staged diff: $(git diff --cached) ## Steps 1. Read the staged diff from context. 2. Look for logic errors, missing error handling, and security issues. 3. Output findings grouped as Critical, Warnings, and Suggestions. ## Constraints - Only review the staged changes, not the whole file. - Do not suggest stylistic rewrites unless they affect correctness.
Code Review — feature/auth-refresh
[Critical]
jwt.verify does not validate expiry. Pass ignoreExpiration: false explicitly.
[Warning]
process.env.SECRET is not type-guarded. Add a startup check.
git diff --cached -- src/auth.ts to a single file.Example — /deploy-check with multiple sources
A pre-deployment checklist skill can pull the branch, last commit, and working-tree status all in one go.
--- name: deploy-check description: Run a pre-deployment checklist before I deploy. --- ## Context - Date: $CURRENT_DATE - Branch: $(git branch --show-current) - Last commit: $(git log -1 --pretty=format:"%h %s") - Uncommitted changes: $(git status --short) ## Steps 1. Confirm the branch is not "main" or "master" unless the user says it is intentional. 2. Flag any uncommitted changes as a blocker. 3. Show the last commit hash and message. 4. Ask the user to confirm they have run tests.
Dynamic context syntax reference
| Expression | What it injects | Works in |
|---|---|---|
| $CURRENT_DATE | Today's date (YYYY-MM-DD) | Claude Code |
| $(git branch --show-current) | Active branch name | Claude Code |
| $(git diff --cached --name-only) | List of staged file paths | Claude Code |
| $(git diff --cached) | Full staged diff (use carefully) | Claude Code |
| $(git log -1 --pretty='%h %s') | Hash + message of last commit | Claude Code |
| $(git status --short) | Uncommitted changes (porcelain format) | Claude Code |
| $(echo $NODE_ENV) | Any environment variable value | Claude Code |
| $(cat package.json | head -5) | First 5 lines of any file | Claude Code |
Best practices
- Keep injected output small — prefer
--name-onlyover a full diff when you only need file names. - Never inject secrets directly — avoid
$(cat .env). Reference env values by name inside Steps instead. - Label each line clearly — e.g. Branch: $(git branch …) so Claude knows which value is which.
- Test with a dry run first — run on a throwaway branch to confirm injected values look correct.
$CURRENT_DATE and $(git branch --show-current). Add the staged diff only when the skill genuinely needs to read code.Before you continue
- Add a
## Contextblock with$CURRENT_DATEor shell substitutions for live data. - Shell substitution runs in Claude Code only — not in claude.ai chat skills.
- Label context lines clearly and keep injected output small.
- Never inject secrets via shell commands that read .env files.
- Next lesson: Skills with Subagents.
What's Next
Dynamic context injects live data at runtime. Next: skills with sub-agents for multi-step, parallel workflows.