Claude Code: The Perfect Development Loop
I've written two slash commands for Claude Code that form the core of my development workflow: /todo
implements one task and /commit
commits it.
This post is part of my Claude Code setup series.
Understanding Slash Commands
Claude Code comes with built-in slash commands like /pr-comments
, which are essentially shortcuts for specific prompts. You can also create custom slash commands.
- Global commands: Place them in
$HOME/.claude/commands/example.md
- Project commands: Place them in
./.claude/commands/example.md
Both are invoked with /example
. If a command exists in both locations, the project-specific version takes precedence.
The Development Loop
When building larger features, I work through a task list in .llm/todo.md
:
- Run
/todo
to implement the next task - Test and debug manually
- Run
/commit
to commit the changes - Run
/compact
to clean up the conversation history - Repeat
Creating the Task List
When planning larger changes, I tend to chat with a thinking model in the browser. At the end, I ask for a task list:
Write a task list that we can work through to implement this idea, as a markdown checklist.
Each checkbox should represent a task that we can implement and commit on its own.
Arrange the tasks in the order we need to implement them, not in order of importance.
Example format:
- Add a test case for feature ABC.
- It's ok for the test to be failing at this point.
- Implement feature ABC.
- Delete DEF
- Replace usages of DEF with ABC.
I save this list to .llm/todo.md
. I've configured Claude to ignore the .llm
directory in version control.
/todo
This command tells Claude to implement one task from the list.
📋 Find and implement the next incomplete task from the project todo list.
- Spawn a sub-agent or sub-task to find the next task in the todo list.
- In the sub-agent or sub-task:
- Read the file `.llm/todo.md`
- The file will only exist in this directory or in the main repository if we're in a worktree.
- First try reading `./.llm/todo.md`
- If that doesn't exist, use `git rev-parse --git-common-dir` to find the main repository and check if `.llm/todo.md` exists there.
- Don't look in other locations. Don't look in the home directory.
- Find the first line with an incomplete task, with `- [ ] <task>` (not `[x]` or `[>]`)
- Keep in mind that the completed tasks might not be contiguous, since it's common to prepend new tasks at the top
- End the sub-agent or sub-task here and share back the entire task information verbatim.
- Show the user the task we just found. Use the format:
```markdown
The next incomplete task is:
- [ ] Replace DEF with ABC.
```
- Now implement the task
- Think hard about the plan
- Focus ONLY on implementing this specific task
- Ignore all other tasks in the `.llm/todo.md` file or TODOs in the source code
- Work through the implementation methodically and completely, addressing all aspects of the task
- Run appropriate tests and validation to ensure the implementation works
- ✅ After the implementation is complete and verified
- Spawn a sub-agent or sub-task to mark the task as complete
- Find the task we are working on in `.llm/todo.md` again
- Mark the completed task as done by changing `- [ ]` to `- [x]`
- End the sub-agent or sub-task here and confirm the task was marked complete.
- At the same time as we are marking the task complete, spawn a second sub-agent or sub-task in parallel to perform a git commit
- In the sub-agent, commit according to the instructions in ~/.claude/commands/commit.md
"Think hard" is a magic word that triggers extended reasoning.
/commit
After implementing and testing a task, I use /commit
to create a commit:
📝 Commit the local changes to git.
- 🔧 Run `just precommit` (if a `justfile` exists and contains a `precommit` recipe)
- 📦 Stage individually using `git add <file1> <file2> ...`
- Only stage changes that you remember editing yourself.
- Avoid commands like `git add .` and `git add -A` and `git commit -am` which stage all changes
- Use single quotes around file names containing `$` characters
- Example: `git add 'app/routes/_protected.foo.$bar.tsx'`
- 🐛 If the user's prompt was a compiler or linter error, create a `fixup!` commit message.
- Otherwise:
- Commit messages should:
- Start with a present-tense verb (Fix, Add, Implement, etc.)
- Not include adjectives that sound like praise (comprehensive, best practices, essential)
- Be concise (60-120 characters)
- Be a single line
- Sound like the title of the issue we resolved, and not include the implementation details we learned during implementation
- End with a period.
- Describe the intent of the original prompt
- Commit messages should not include a Claude attribution footer
- Don't write: 🤖 Generated with [Claude Code](https://claude.ai/code)
- Don't write: Co-Authored-By: Claude <noreply@anthropic.com>
🚀 Run git commit without confirming again with the user.
- If pre-commit hooks fail, then there are now local changes
- `git add` those changes and try again
- Never use `git commit --no-verify`
You don't need a custom command to commit - and Claude will offer to commit without being asked. But this gives me control over timing while leveraging Claude's ability to write good summaries.
Managing the Context Window with /compact
The /compact
command is built in but not well documented. It's built on a markdown prompt to summarize the conversation, then replaces Claude's context with that summary.
After completing a task, I need to make room in the context window for the next task. I used to run /compact
if the next task was related, and /clear
if unrelated. Now I just always run /compact
.
This is also a good time to restart with claude --continue
if it has been running too long or if there's a new version available.
Summary
This workflow breaks large features into manageable steps.
These commands work best when combined with proper Claude Code configuration.
For more custom commands, see Claude Code Utility Commands.