Intermediate

String Substitutions

Skills become dramatically more useful when they can accept input from the user and access runtime information. Most agent platforms provide a string substitution system that replaces special variables in your skill content with actual values at invocation time. This turns static instruction documents into dynamic, parameterized workflows.

How Substitution Works

When the agent loads a skill (Tier 2), it scans both the frontmatter and the body for substitution variables. These variables are replaced with their actual values before the agent sees the content. The agent never sees the raw $ARGUMENTS placeholder — it sees the actual text the user passed in.

Invoking a skill with arguments
> /translate-doc README.md spanish # Claude sees the skill body with substitutions applied: # $ARGUMENTS → "README.md spanish" # $1 → "README.md" # $2 → "spanish"

$ARGUMENTS — The Full Argument String

The $ARGUMENTS variable contains everything the user typed after the skill name, as a single string. This is the most common substitution variable and works well when your skill expects free-form input.

explain/SKILL.md
---
description: Explain a concept in simple terms
---

The user wants you to explain the following topic:

$ARGUMENTS

Explain it as if teaching a junior developer. Use analogies,
avoid jargon, and include a concrete code example.

When invoked as /explain dependency injection, the agent sees the body with $ARGUMENTS replaced by "dependency injection".

$1 through $9 — Positional Arguments

When your skill expects structured input with distinct parameters, use positional variables. The agent splits the argument string on whitespace and assigns each token to $1, $2, through $9.

rename-component/SKILL.md
---
description: Rename a component across the codebase
---

Rename the React component from **$1** to **$2** across the entire codebase.

## Steps

1. Find all files that import or reference `$1`
2. Update the component name in its definition file
3. Update all import statements
4. Update all JSX usage sites
5. Rename the file from `$1.tsx` to `$2.tsx`
6. Run `npm run typecheck` to verify nothing broke
Using positional arguments
> /rename-component UserCard ProfileCard # Substitutions: # $ARGUMENTS → "UserCard ProfileCard" # $1 → "UserCard" # $2 → "ProfileCard"
âš ī¸Whitespace splits arguments

Positional arguments are split on whitespace. If a user types /skill my file name.txt output, then $1 is "my", $2 is "file", and so on. There is no quoting mechanism. If your skill needs a multi-word argument, use $ARGUMENTS and parse it in the instructions, or design the skill so that each argument is a single token (like a file path without spaces).

Built-in Variables

Beyond user-provided arguments, some platforms provide built-in variables that give skills access to runtime context. In Claude Code, these use the ${VARIABLE_NAME} syntax with curly braces.

${CLAUDE_SESSION_ID}

A unique identifier for the current agent session. This is useful for skills that need to create temporary files or directories without naming collisions.

benchmark/SKILL.md
---
description: Run benchmarks and save results
allowed-tools:
  - Bash(*)
  - Write
---

Run the project benchmarks and save results to a unique file:

```
benchmark-results-${CLAUDE_SESSION_ID}.json
```

This ensures each session's results don't overwrite previous runs.

${SKILL_DIR}

The absolute filesystem path to the directory containing the current skill's SKILL.md. This is essential for referencing supporting files that live alongside the skill.

api-docs/SKILL.md
---
description: Generate API documentation from OpenAPI spec
allowed-tools:
  - Read
  - Write
---

Read the documentation template from:
${SKILL_DIR}/templates/api-doc.md.hbs

Read the styling config from:
${SKILL_DIR}/config/doc-style.json

Use these to generate documentation for the API endpoint
described in $ARGUMENTS.
â„šī¸SKILL_DIR is always absolute

The ${SKILL_DIR} variable always resolves to an absolute path, regardless of where the agent was launched from. This makes skills portable — they work the same whether invoked from the project root or a subdirectory.

Substitution in Frontmatter

Variables work in frontmatter fields too, not just the body. This is particularly powerful with allowed-tools, where you can dynamically scope tool permissions based on user input.

test-module/SKILL.md
---
description: Run tests for a specific module
allowed-tools:
  - Bash(npm test -- --filter $1)
  - Bash(npm run lint -- $1)
---

Run the test suite for the **$1** module.

1. First run the linter: `npm run lint -- $1`
2. Then run tests: `npm test -- --filter $1`
3. Report any failures with suggested fixes.

Invoking /test-module auth makes the allowed-tools expand to Bash(npm test -- --filter auth) and Bash(npm run lint -- auth). This tightly scopes the skill's permissions to only the relevant module.

Handling Empty Arguments

Users will not always provide arguments. A well-designed skill should handle this gracefully. If $ARGUMENTS is empty, the variable is replaced with an empty string. You can account for this in your instructions:

review/SKILL.md
---
description: Review code in a specific file or the whole project
---

Review the code for quality issues.

If a file path was provided, focus on that file: $ARGUMENTS

If no file was specified, scan the entire project for the most
critical issues and report the top 5.
💡Design for optional arguments

Write your skill instructions so that the agent does something reasonable when no arguments are provided. The agent is smart enough to interpret "If a file path was provided, focus on that file: " (with nothing after the colon) as meaning no file was specified.

Quick Reference

substitution-reference.md
# Substitution Variables

## User-provided
$ARGUMENTS          # Full argument string after skill name
$1, $2, ... $9      # Positional arguments (whitespace-split)

## Built-in
${CLAUDE_SESSION_ID} # Unique session identifier
${SKILL_DIR}         # Absolute path to the skill's directory

## Where they work
- ✓ Frontmatter values (description, allowed-tools, etc.)
- ✓ Skill body (Markdown instructions)
- ✗ Frontmatter keys (the field names themselves)

What's Next

String substitutions inject static values at load time. The next lesson covers dynamic context — how to use the !`command` syntax to run shell commands and inject their live output into skill content.