Skip to main content

Claude Code: The Perfect Development Loop

ยท 4 min read
Craig P. Motlin
Software Engineer

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.

You can find more examples and command definitions in the claude-code-prompts repository.

The Development Loop

When building larger features, I work through a task list in .llm/todo.md:

  1. Run /todo to implement the next task
  2. Test and debug manually
  3. Run /commit to commit the changes
  4. Run /compact to clean up the conversation history
  5. 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.

.claude-prompts/commands/todo.md
---
name: markdown-tasks
description: Work with markdown-based task lists in .llm/todo.md files. Use when managing tasks, working with todo lists, extracting incomplete tasks, marking tasks complete, or implementing tasks from a task list.
---

# Markdown Task Management

This skill enables working with the markdown task list stored in `.llm/todo.md`.

## Important: Do Not Explore Plugin Directory

The scripts referenced below are part of this plugin and are pre-verified to work correctly. Do NOT:

- Search for or read the `.py` script files
- Explore the `${CLAUDE_PLUGIN_ROOT}` directory
- Try to understand the script implementation

Simply **run the bash commands exactly as shown**. The scripts handle all the complexity internally.

## Scripts

These scripts require Python 3 with standard library only (no external packages needed).

### task_get.py - Extract Next Task

Extract the first incomplete task with its context by running this command (do NOT read the script file):

```bash
python3 ${CLAUDE_PLUGIN_ROOT}/skills/tasks/scripts/task_get.py .llm/todo.md
```

Returns the first `[ ]` checkbox line with all indented context lines below it.

**Exit codes**: 0 (success), 1 (file not found or error)

### task_add.py - Add New Task

Add a new task by running this command (do NOT read the script file):

```bash
python3 ${CLAUDE_PLUGIN_ROOT}/skills/tasks/scripts/task_add.py .llm/todo.md "Task description
Context line 1
Context line 2"
```

Creates the `.llm/` directory and `todo.md` file if they do not exist, and appends the new task with a `[ ]` checkbox. The script preserves all indentation in multi-line strings.

**Exit codes**: 0 (success), 1 (error)

### task_complete.py - Mark Task Done

Mark the first incomplete task as done by running this command (do NOT read the script file):

```bash
python3 ${CLAUDE_PLUGIN_ROOT}/skills/tasks/scripts/task_complete.py .llm/todo.md
```

Changes the first `[ ]` to `[x]`.

**Exit codes**: 0 (success), 1 (no incomplete tasks or error)

### task_archive.py - Archive Task List

Archive a completed task list by running this command (do NOT read the script file):

```bash
python3 ${CLAUDE_PLUGIN_ROOT}/skills/tasks/scripts/task_archive.py .llm/todo.md
```

Moves the file to `.llm/YYYY-MM-DD-todo.md` where YYYY-MM-DD is today's date.

**Exit codes**: 0 (success), 1 (file not found or error)

## Task Format

The task list is in `.llm/todo.md`.

NEVER use the `Read` tool on `.llm/todo.md`. Always interact with the task list exclusively through the Python scripts.

### Task States

- `[ ]` - Not started (ready to work on)
- `[x]` - Completed
- `[!]` - Blocked after failed attempt

### Task Structure

Each task includes indented context lines with full implementation details:

- Absolute file paths
- Exact function/class names
- Code analogies to existing patterns
- Dependencies and prerequisites
- Expected outcomes

### Standalone Context

Each task is extracted and executed in isolation. Every task must contain ALL context needed to implement it. Repeat shared context in every related task. Never reference other tasks.

View on GitHub

"Think hard" is a magic word that triggers extended reasoning.

/commit

After implementing and testing a task, I use /commit to create a commit:

.claude-prompts/commands/commit.md
---
description: Commit local changes to git
---

ALWAYS use the `code:cli` skill.

## Context

- Current git status: !`git status`
- Current git diff (staged and unstaged changes): !`git diff HEAD`
- Current branch: !`git branch --show-current`
- Recent commits: !`git log --oneline -10`

## Task

1. **File Staging**

- ๐Ÿ“ฆ Stage files individually using `git add <file1> <file2> ...`
- NEVER use commands like `git add .`, `git add -A`, or `git commit -am` which stage all changes
- Only stage files that were explicitly modified for the current task

2. **Commit Message Creation**

- ๐Ÿ› If the user pasted a compiler or linter error, create a `fixup` commit using `git commit --fixup <sha>`
- Otherwise commit messages should:
- Start with a present-tense verb (Fix, Add, Implement, etc.)
- Be concise (60-120 characters)
- Be a single line
- End with a period.
- Borrow language from the original prompt
- Avoid praise adjectives (comprehensive, robust, essential, best practices)
- Echo exactly this: Running: `git commit --message "<message>"`
- ๐Ÿš€ Run git commit without confirming again with the user.

3. **Pre-commit hooks**

When pre-commit hooks fail:

- Stage the files modified by the hooks individually
- Retry the commit
- Never use `git commit --no-verify`

View on GitHub

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.