A comprehensive guide to building sophisticated Claude Code commands with Stim.
- Introduction
- Basic Concepts
- Your First Command
- Working with Variables
- Control Flow
- User Interaction
- File Operations
- Tasks and Subagents
- Building a Complete Command
- Advanced Techniques
- Best Practices
This tutorial will teach you how to build powerful Claude Code commands using Stim. By the end, you'll be able to create complex, interactive workflows that automate your development tasks.
We're going to build a comprehensive code review command that:
- Asks questions about the code to review
- Performs different types of analysis based on responses
- Generates structured reports
- Creates follow-up tasks
Every Stim file defines a single command:
command mycommand {
// Your logic here
}
Commands contain statements that execute in order:
command example {
ask("What's your name?") // Statement 1
wait_for_response() // Statement 2
create_file("output.txt", "Hi!") // Statement 3
}
Stim compiles your .stim file into a .md file that Claude Code can execute:
bun run build && ./dist/stim compile mycommand.stim
# Creates ~/.claude/commands/mycommand.mdLet's start with a simple greeting command:
command greet {
ask("What's your name?")
wait_for_response()
ask("Hello! What can I help you with today?")
}
Save this as greet.stim and compile:
bun run build && ./dist/stim compile greet.stimTest it in Claude Code:
/greet
Claude will ask for your name, wait for your response, then greet you.
Variables store data you can reuse throughout your command:
command variables_demo {
project_name = "My Awesome Project"
version = "1.0.0"
is_production = true
ask("Working on " + project_name + " version " + version)
}
Arrays store multiple values:
command array_demo {
languages = ["JavaScript", "TypeScript", "Python", "Rust"]
ask("Which language do you prefer? Options: " + languages.join(", "))
wait_for_response()
}
command types_demo {
// String
name = "Stim"
// Number (treated as string in current version)
port = "3000"
// Boolean
debug = true
// Array
items = ["a", "b", "c"]
}
Control flow lets you create dynamic, responsive commands.
command conditional_demo {
ask("Are you working on a new project?")
wait_for_response()
if (confirm("Is this a web application?")) {
ask("What frontend framework are you using?")
wait_for_response()
}
}
Iterate over arrays:
command for_loop_demo {
tasks = ["Setup environment", "Write code", "Test", "Deploy"]
for task in tasks {
if (confirm("Have you completed: " + task + "?")) {
ask("Great! Any notes on " + task + "?")
wait_for_response()
}
}
}
Continue until a condition is met:
command while_loop_demo {
questions_complete = false
while (!questions_complete) {
ask("What's your next question about the project?")
wait_for_response()
if (confirm("Do you have more questions?")) {
questions_complete = false
} else {
questions_complete = true
}
}
}
Stim provides several ways to interact with users:
ask("What's your preferred programming language?")
ask(variable_containing_question)
if (confirm("Do you want to proceed?")) {
ask("Great! Let's continue.")
}
ask("Please describe your requirements")
wait_for_response()
ask("Based on your input, here's what I recommend...")
Create files as part of your workflow:
command file_demo {
ask("What should I name the new file?")
wait_for_response()
create_file("README.md", "project_readme_template")
create_file("package.json", "package_template")
ask("Created your project files!")
}
Tasks let you spawn Claude Code subagents that work autonomously on subtasks. This is powerful for breaking complex workflows into independent pieces.
The simplest form spawns a general-purpose subagent:
command research {
task "explore the auth module" {
ask("What authentication patterns exist in the codebase?")
wait_for_response()
ask("Summarize the findings")
}
}
Different agent types are optimized for different work:
command analyze {
// Fast codebase exploration
task explore "find all API endpoints" {
ask("List every API endpoint in the project")
}
// Shell command execution
task bash "run the test suite" {
ask("Run all tests and report results")
}
// Architecture planning
task plan "design the caching layer" {
ask("Design a caching strategy for this application")
}
}
Use parallel to run multiple tasks concurrently:
command full_analysis {
parallel {
task explore "analyze frontend" {
ask("What frontend patterns and frameworks are used?")
}
task explore "analyze backend" {
ask("What backend patterns and APIs exist?")
}
task bash "check test coverage" {
ask("Run test coverage and report results")
}
}
ask("All analysis tasks complete. Summarize the findings.")
}
Extract common task logic into separate .stim files and reference them:
// helpers/security_check.stim
command security_check {
ask("Scan for common security vulnerabilities")
wait_for_response()
ask("Check for exposed secrets and credentials")
wait_for_response()
}
// main.stim
command review {
task("helpers/security_check.stim", explore)
ask("Security review complete!")
}
The referenced file is read and inlined at compile time, so the output is entirely self-contained.
Let's build a comprehensive code review command that showcases all Stim features:
command code_review {
// Configuration
review_types = [
"Security vulnerabilities",
"Performance issues",
"Code style and formatting",
"Architecture and design",
"Testing coverage",
"Documentation"
]
// Gather basic information
ask("What files or directories should I review?")
wait_for_response()
ask("What's the primary language/framework of this code?")
wait_for_response()
// Select review types
selected_reviews = []
for review_type in review_types {
if (confirm("Include " + review_type + " in the review?")) {
ask("Any specific focus areas for " + review_type + "?")
wait_for_response()
}
}
// Perform the review
ask("Starting comprehensive code review...")
// Security check
if (confirm("Should I check for common security issues?")) {
security_checks = [
"SQL injection vulnerabilities",
"XSS attack vectors",
"Authentication/authorization flaws",
"Sensitive data exposure"
]
for check in security_checks {
ask("Checking for: " + check)
wait_for_response()
}
}
// Performance analysis
if (confirm("Analyze performance bottlenecks?")) {
ask("Looking for inefficient algorithms, database queries, and resource usage...")
wait_for_response()
}
// Generate report
ask("Analyzing code structure and generating detailed report...")
wait_for_response()
create_file("CODE_REVIEW_REPORT.md", "code_review_template")
// Follow-up actions
if (confirm("Create follow-up tasks for issues found?")) {
ask("What's your preferred task format? (GitHub issues, Jira tickets, etc.)")
wait_for_response()
create_file("REVIEW_TASKS.md", "task_list_template")
}
// Summary
ask("Code review complete! Check CODE_REVIEW_REPORT.md for detailed findings.")
if (confirm("Would you like me to prioritize the most critical issues?")) {
ask("Here are the top 3 issues that need immediate attention:")
wait_for_response()
}
}
Compile and test:
bun run build && ./dist/stim compile code_review.stim
# Use with: /code_reviewUse variables to create dynamic questions:
command dynamic_demo {
user_type = "developer"
if (user_type == "developer") {
technical_questions = [
"What's your preferred IDE?",
"Do you use version control?",
"What testing framework do you use?"
]
}
for question in technical_questions {
ask(question)
wait_for_response()
}
}
Create complex decision trees:
command nested_demo {
if (confirm("Are you building a web app?")) {
if (confirm("Is it a single-page application?")) {
ask("React, Vue, or Angular?")
} else {
ask("Server-side rendering framework?")
}
} else {
if (confirm("Is it a mobile app?")) {
ask("Native or cross-platform?")
}
}
}
Create structured content:
command template_demo {
ask("What's the project name?")
wait_for_response()
ask("Brief description?")
wait_for_response()
// Create multiple related files
create_file("README.md", "readme_template")
create_file("CONTRIBUTING.md", "contributing_template")
create_file("package.json", "package_template")
ask("Project structure created! Ready to start coding?")
}
// Good
deployment_environments = ["staging", "production"]
user_confirmation_required = true
// Avoid
envs = ["staging", "production"]
flag = true
// Good - clear steps
command deploy {
// Step 1: Validate
if (confirm("Run tests first?")) {
run_tests()
}
// Step 2: Select environment
environments = ["staging", "production"]
for env in environments {
if (confirm("Deploy to " + env + "?")) {
deploy_to_environment(env)
}
}
// Step 3: Verify
ask("Deployment complete! Please verify the application is working.")
}
// Good - tells user what's happening
ask("Analyzing your codebase for security vulnerabilities...")
wait_for_response()
ask("Found 3 potential issues. Generating detailed report...")
wait_for_response()
// Avoid - unclear what's happening
ask("Processing...")
command robust_demo {
if (confirm("Do you have a package.json file?")) {
ask("Great! I'll analyze your dependencies.")
} else {
ask("No package.json found. Should I create one for you?")
if (confirm("Create package.json?")) {
create_file("package.json", "basic_package_template")
}
}
}
// Good - consistent naming pattern
command project_setup {
project_name = "my-project"
project_type = "web-app"
project_language = "typescript"
}
// Avoid - inconsistent naming
command project_setup {
name = "my-project"
type_of_project = "web-app"
lang = "typescript"
}
Now that you understand Stim fundamentals:
- Explore Examples: Check out the
examples/directory for real-world commands - Read the API Reference: Learn about all available functions in API.md
- Build Your Own: Start converting your existing Claude commands to Stim
- Contribute: Help improve Stim by contributing examples or features
Syntax Error: Expected }
- Check that every
{has a matching} - Verify proper nesting of control structures
Compilation Failed: Invalid assignment
- Make sure strings are quoted:
name = "value" - Arrays need square brackets:
items = ["a", "b"]
Command not found in Claude Code
- Verify the
.mdfile was created in~/.claude/commands/ - Restart Claude Code if needed
- Check that your command name matches the file name
- FAQ - Common questions and solutions
- Syntax Reference - Complete language documentation
- GitHub Issues - Report bugs or request features
🎉 You're now ready to build powerful Claude Code commands with Stim! Start by converting one of your existing commands, or create something entirely new.