Dynamic Context
String substitutions let you inject user arguments and built-in variables into skill content. But what if you need information that only exists at runtime â the current git branch, the contents of a config file, the output of an API call? The dynamic context system solves this with the !`command` syntax, which runs a shell command and injects its output directly into the skill content before the agent sees it.
The !`command` Syntax
Any text wrapped in !`...` inside a skill is treated as a shell command. The agent executes the command and replaces the entire !`...` expression with the command's stdout. This happens at load time, before the agent processes the skill content.
---
description: Review the current git changes
---
Here is the current git status:
!`git status --short`
And here are the staged changes:
!`git diff --cached`
Review these changes and suggest improvements.When this skill is invoked, the agent does not see the !`git status` expression. It sees the actual output of the command, something like:
Common Use Cases
Injecting Git Context
Git information is one of the most common dynamic context sources. You can inject branch names, recent commits, diffs, and more.
---
description: Generate release notes from recent commits
---
Current branch: !`git branch --show-current`
Commits since last tag:
!`git log $(git describe --tags --abbrev=0)..HEAD --oneline`
Generate release notes from these commits. Group them by type
(features, fixes, chores) using conventional commit prefixes.Reading Configuration Files
Skills can adapt their behavior based on project configuration by injecting file contents.
---
description: Create a new component following project conventions
---
The project uses the following TypeScript configuration:
!`cat tsconfig.json`
The ESLint rules are:
!`cat .eslintrc.json 2>/dev/null || echo 'No ESLint config found'`
Create a new component named $1 that follows these project conventions.Use shell fallbacks like 2>/dev/null || echo 'fallback' to prevent errors when a file might not exist. The agent will inject whatever the command outputs, including error messages, so defensive shell scripting matters here.
Environment Information
You can inject environment variables, system information, or the output of any CLI tool.
---
description: Debug environment configuration issues
---
## Current Environment
Node version: !`node --version`
npm version: !`npm --version`
OS: !`uname -s`
Working directory: !`pwd`
## Package Info
!`cat package.json | jq '{name, version, engines}'`
## Environment Variables (non-sensitive)
!`env | grep -E '^(NODE_ENV|PORT|DATABASE_URL|API_BASE)=' | sort`
Analyze this environment and identify any configuration issues.Dynamic Context in Frontmatter
The !`command` syntax also works inside frontmatter values. This opens up powerful patterns like dynamically scoping tool permissions or generating descriptions.
---
description: Work with the current feature branch
allowed-tools:
- Bash(git push origin !`git branch --show-current`)
- Bash(git pull origin !`git branch --show-current`)
- Bash(git log)
---
You are working on the !`git branch --show-current` branch.
The allowed-tools are scoped to only push/pull this specific branch.Commands in !`...` run with the same permissions as your shell. Be careful with skills that are shared across teams or sourced from plugins. A malicious skill could use !`...` to execute arbitrary commands. Only use skills from sources you trust, and review any third-party skill that uses dynamic context before adding it to your project.
Execution Timing
Understanding when !`command` expressions execute is important:
- At skill load time (Tier 2) â Commands execute the moment the agent loads the skill into context, before it starts processing. This means the output reflects the state of the system at invocation time, not at some later point during execution.
- Once per invocation â Each
!`...`expression runs exactly once. If you invoke the same skill twice, the commands run again and may produce different output. - Synchronously and sequentially â Commands run in order from top to bottom. A slow command will delay skill loading. Keep commands fast (under 1-2 seconds) for the best experience.
The !`command` syntax is fundamentally different from the agent using the Bash tool during execution. Dynamic context runs before the agent sees the skill. The Bash tool runs during task execution based on the agent's decisions. Think of !`...` as a preprocessor, not a tool invocation.
Combining with String Substitutions
Dynamic context and string substitutions can be combined. Substitutions are applied first, then dynamic context commands are executed. This means you can use $ARGUMENTS or positional variables inside your !`...` commands.
---
description: Analyze the history of a specific file
---
Showing git history for: $1
Recent changes:
!`git log --oneline -10 -- $1`
Last author to modify:
!`git log -1 --format='%an (%ar)' -- $1`
Number of commits touching this file:
!`git rev-list --count HEAD -- $1`
Analyze the change history of this file and identify patterns.Invoking /file-history src/auth.ts first substitutes $1 with src/auth.ts, then runs each git command with that file path.
Performance Tips
- Keep commands fast. Each
!`...`adds latency to skill loading. Avoid commands that take more than a second or two. - Limit output size. Use
head,tail, orjqto trim large outputs. A 100,000-line log dump will consume context window tokens. - Use fallbacks. Always handle the case where a command might fail or produce no output.
- Avoid network calls when possible. Curl or API calls add unpredictable latency and can fail silently.
What's Next
Dynamic context gives skills access to runtime information. The next lesson covers allowed tools â how to control which tools a skill can use for safety and focus.