Skip to main content

Claude Code: Moving from Slash Commands to Agents

· 7 min read
Craig P. Motlin
Software Engineer

When agents were released, the documentation was too vague for me to understand what they were for. One effective way to use them is as a more powerful form of slash commands.

Agents Seemed Vague​

When Claude Code released agents, the documentation described them as "specialized AI assistants that can be invoked to handle specific types of tasks." Each agent:

  • Has a specific purpose and expertise area
  • Uses its own context window separate from the main conversation
  • Can be configured with specific tools it's allowed to use
  • Includes a custom system prompt that guides its behavior

This definition was so generic I couldn't figure out what they were actually for. The examples showed personas like "code-reviewer," "debugger," and "data-scientist." I've never understood the pattern of personifying AI with "you are a senior code reviewer" type prompts. The LLM's expertise comes from its training data, not from prompts written in second person.

Porting Slash Commands to Agents​

I already had custom slash commands that worked well. So I started experimenting by converting commands into agents.

I used the /agents slash command to set up the markdown file structure, then copied my existing prompt content in.

At this point I noticed a mechanical benefit - I could tell Claude to run multiple agents in sequence. It's not possible to execute multiple slash commands in one go. A prompt like Please /commit and then /rebase doesn't work - presumably because they are a special syntax.

With slash commands, I had felt like I was babysitting the agents. At the end of each task, I ran /comments, then just precommit (pasting failures if it broke), then /commit, then git rebase (running /conflicts if it failed). With subagents, I can say "run comment-cleaner, precommit-runner, git-commit-handler, and git-rebaser" and it works great.

I kept my slash commands. I still use them occasionally, but I use the subagent versions more.

Why Agents Are Better Than Alternatives​

Agents share features with several existing tools but combine their benefits:

What Agents Have in Common With Other Tools​

  • Slash Commands: Both let you invoke complex behavior with a simple trigger, keeping detailed prompts out of context until needed
  • Task Tool: Both run in isolated context windows to avoid polluting your main conversation
  • MCP Tools: Both can run automatically when Claude recognizes they're needed, or can be invoked explicitly

How Agents Improve on Each​

  1. Context Isolation (vs Slash Commands): Slash command prompts don't load into context on startup, but they do load when you invoke them. Subagents are more like MCP tools; only their description loads into main context on startup, and the full prompt loads in the agent's isolated context. Subagents are also like the Task tool; all the output from their work stays in the agent's context as well.

  2. Model Switching (vs Slash Commands and Task Tool): With slash commands and Tasks, everything runs using the same model. With subagents, I can use a more powerful and expensive model for my main work but run agents on a cheaper model. This is not always a huge benefit - it depends on the models and pricing of the day.

  3. Detailed Instructions (vs Task Tool): Before subagents, I had tried prompts like "Run /commit using the Task tool" but this simply didn't work. Subagents fill this gap.

Practical Benefits​

After using agents for a few weeks, here's what I've found:

  • Longer Sessions: By offloading repetitive tasks to agents, my main context stays clean. I can work much longer without hitting context limits or needing to compact.

  • Better Task Decomposition: LLMs perform better with several short, focused prompts rather than a single long prompt with multiple steps. Since agents can call other agents (like git-rebaser calling git-rebase-conflict-resolver), they naturally decompose complex tasks into manageable pieces.

  • Cost Savings: On personal projects where I pay per token or have usage caps, running simpler tasks on Sonnet instead of Opus saves money and stays under limits. This is less relevant with Sonnet 4.5.

My Three Main Skills​

In my ~/.claude/CLAUDE.md, I specify three skills that should run when code changes are ready. The orchestration:finish skill runs them in order:

- Commit with the `git:commit` skill
- Run the `build:precommit` skill
- Rebase with the `git:git-rebase` skill

git:commit​

This skill stages and commits changes. It's based on my /commit command.

plugins/git/skills/commit/SKILL.md
---
name: commit
description: Commit local changes with careful staging and single-line messages. Use for all git commit operations.
---

# Commit

Use the `code:cli` and `git:git-workflow` skills.

## Inspect Context

```bash
git status
git diff --cached
git diff
git branch --show-current
git log --oneline -10
```

## Stage Files

Stage only the files modified for the current task, individually:

```bash
git add <file1> <file2>
```

Never use `git add .`, `git add -A`, `git commit -am`, or anything else that stages unrelated changes.

## Commit Message

If the user pasted a compiler or linter error, or the task is explicitly a fixup for a known commit, use `git commit --fixup <sha>`.

Otherwise write one line following the format in `git:git-workflow`. The prompt is the intent, not the message text: borrow its language, but distill a long or multi-line prompt to one line.

Echo exactly:

```text
Running: `git commit --message "<message>"`
```

Then commit without confirming again with the user:

```bash
git commit --message "<message>"
```

If pre-commit hooks modify files, stage those files individually and retry. Never use `git commit --no-verify`.

A commit that a hook stopped was never made, so fix the problem, stage the files, and run `git commit` again. Never run `git commit --amend`: after a failed commit, `--amend` rewrites the previous commit, which usually belongs to an earlier task. To change a commit that already exists, make a new commit or a `git commit --fixup <sha>` commit. A stray fixup that never folds in is easy to clean up later with `git history reword`, and a wrongly amended commit is not.

View on GitHub

build:precommit​

This skill runs the project's precommit checks: builds, tests, and formatters. It runs them through git test against the committed tree, so a passing result is cached against the commit. If git test isn't set up yet, I go ahead and set it up.

plugins/build/skills/precommit/SKILL.md
---
name: precommit
description: Running precommit checks and build validation. ALWAYS use after ANY code changes.
---

# Precommit and Build Validation

## Battery check

Skip the build on battery power. Resolve `<plugin-root>` first: in Claude Code use `${CLAUDE_PLUGIN_ROOT}`; in Codex use the plugin root that contains this `skills/precommit/SKILL.md` file.

```bash
<plugin-root>/scripts/check-battery || { echo "⚡ Skipping precommit on battery power"; exit 0; }
git test run HEAD --retest --verbose --verbose
```

## Run precommit

Validate with `git test run HEAD --retest --verbose --verbose`, using a timeout of at least 10 minutes. It runs the test configured via `git test add`, typically formatting, builds, tests, and other checks.

- Commit staged, unstaged, and other uncommitted changes with the `git:commit` skill before running it. Don't wait for `git test run` to refuse the dirty tree.
- Never pass `--forget-results` or run `git test forget-results`. Either one deletes every stored result in the repository, not just the named commit's. `--retest` already re-runs the named commit.
- Never substitute `just precommit` or another direct build command. Running on the committed tree caches the result against the commit. An eager validation commit is fine; the caller can reset, squash, or fix it up later.
- Try `git test run` with permitted execution before requesting escalation; it refreshes the index and writes `.git/index.lock` before the configured command starts. If it succeeds, use that result.
- If the sandbox blocks it, request escalation only when the active policy allows it. Under `approval_policy=never`, don't request escalation or bypass the policy. If no permitted execution can complete the command, report committed-tree validation as blocked with the command and permission error. Other checks don't establish that committed-tree validation passed.

If `git test` is not configured for this repository, say so and suggest the `build:test-setup` skill.

## Fix failures

Fix the failures, then commit the fixes with `git add -u && git commit --fixup=HEAD` before retrying, because `git test run HEAD` refuses a dirty tree and the fixup folds into the original commit on the next rebase. Repeat until precommit exits 0.

## Report

Start the final message with one of:

- "⚡ **Skipped precommit checks (on battery power)**"
- "✅ **Precommit checks passed**"
- "✅ **Precommit checks passed** (after fixing [brief description])"
- "⛔ **Committed-tree validation blocked**": permissions prevent `git test run HEAD` and escalation is unavailable or denied. Include the command and permission error, and report other checks separately without claiming precommit passed.

View on GitHub

git:git-rebase​

This skill rebases the current branch on top of the upstream branch to ensure changes are up to date before handing control back. If it encounters merge conflicts, it hands off to the git:conflicts skill.

plugins/git/skills/git-rebase/SKILL.md
---
name: git-rebase
description: Rebase local commits on the configured upstream using the plugin rebase script.
---

# Git Rebase

Resolve `<plugin-root>` before running plugin scripts:

- In Claude Code, use `${CLAUDE_PLUGIN_ROOT}`.
- In Codex, use the plugin root that contains this `skills/git-rebase/SKILL.md` file.

Run `git status`. If there are uncommitted changes, stop and report them.

Then run exactly this, with no added arguments or environment overrides:

```bash
<plugin-root>/scripts/rebase
```

The script reads the project's configured upstream remote and branch. Other rebase commands pick the wrong base: `git rebase` and `git replay` don't know the upstream, and `git pull --rebase` and `git rebase @{upstream}` use the branch's tracking info, such as `origin/<current-branch>`.

## Outcomes

- Success: report success.
- Merge conflicts: use the `git:conflicts` skill.
- Other errors: report the specific error and stop.

View on GitHub

Putting It All Together: The /todo Command​

As I found myself manually running the same sequence of cleanup steps after implementing each task, I created a /todo command to gather the entire workflow into a single command. I started keeping a markdown task list in .llm/todo.md. I run it to get Claude to implement one task from the list. It is now the markdown-tasks:do-one-task skill. To summarize what it does:

  1. Read the next incomplete task from .llm/todo.md
  2. Implement it (focusing ONLY on this task, ignoring other items)
  3. Run the finish workflow
  4. Mark the task complete

Step 3 is where these skills come together:

markdown-tasks:do-one-task
├── 1-2: Find and implement task
└── 3: orchestration:finish
├── git:commit
├── build:precommit
└── git:git-rebase
└── git:conflicts (if conflicts occur)

Complex workflows naturally decompose when skills can call other skills. Instead of one long prompt trying to handle all edge cases, each skill focuses on its specific concern - committing, running tests, rebasing, or resolving conflicts.

Try It Yourself​

You can copy these skills directly from my GitHub repo. Start with something simple - maybe just the commit skill - and see how it changes your workflow.

I feel like I'm just scratching the surface with agents. If you find different patterns that work well, let me know in the comments below.