Complete reference for Stim syntax, functions, and features.
- Declarations
- Agent Metadata
- Prose
- Variables
- Control Flow
- Built-in Functions
- Tasks and Parallelism
- Operators
- Comments
- Targets
- Compilation Output
Every Stim file contains exactly one top-level declaration. There are two kinds: command and agent.
Interactive workflow invoked by the user.
command name {
// Command body
}
Rules:
- Names must be valid identifiers (letters, numbers, underscore)
- Names cannot start with numbers
- Compiles to
/name(Claude Code slash command) by default
command hello { } // Valid: /hello
command deploy_app { } // Valid: /deploy_app
command 123invalid { } // Invalid: starts with number
Static persona with metadata, invoked by the AI tool itself.
agent name {
description "..."
tools [A, B, C]
model "..."
"Prose line one."
"Prose line two."
}
Rules:
- Same naming rules as commands
- Compiles to
@name(Claude Code agent) by default - Metadata fields (
description,tools,model) must come before other statements
See Agents for the full deep-dive.
The body of either declaration contains zero or more statements:
command example {
statement1()
statement2()
}
agent example {
description "..."
"Prose statement."
if (condition) {
"Conditional prose."
}
}
Only valid inside agent declarations. All fields are optional; each may appear at most once; all must come before non-metadata statements.
| Field | Type | Purpose |
|---|---|---|
description |
string | One-line summary of the agent's role |
tools |
array of identifiers | Tools the agent is allowed to call (Claude-specific) |
model |
string | Preferred model (opus, sonnet, haiku) — Claude-specific |
agent reviewer {
description "Reviews PRs for security issues"
tools [Read, Grep, Bash]
model "sonnet"
"You are a security engineer."
}
Fields a target doesn't understand are warn-and-dropped. See Targets.
Bare string literals on a line become prose statements — they compile to plain text in the output.
agent x {
description "..."
"First paragraph."
"Second paragraph."
}
Prose can appear inside control flow:
agent adaptive {
description "..."
if (expert_mode) {
"Use precise technical language."
} else {
"Define terms the first time you use them."
}
}
Variables are declared using assignment syntax:
variable_name = value
name = "John Doe"
message = 'Hello world'
empty_string = ""
Numbers are currently treated as strings:
port = "3000"
timeout = "30"
is_active = true
is_complete = false
items = ["item1", "item2", "item3"]
numbers = ["1", "2", "3"]
mixed = ["string", "123", "true"]
Variables can be used in:
- Function arguments:
ask(variable_name) - String concatenation:
"Hello " + name - Conditions:
if (is_active) - Array access:
for item in items
Variables are scoped to the command and persist throughout execution:
command scope_example {
name = "initial"
if (true) {
name = "modified" // Modifies the existing variable
local_var = "temp" // Creates new variable
}
ask(name) // "modified"
ask(local_var) // "temp" - available here too
}
if (condition) {
// Statements executed if condition is true
}
if (condition) {
// Statements for true condition
} else {
// Statements for false condition
}
Iterate over array elements:
for variable_name in array_name {
// Statements executed for each element
// variable_name contains the current element
}
Example:
languages = ["JavaScript", "Python", "Rust"]
for lang in languages {
ask("Do you use " + lang + "?")
}
Execute while condition is true:
while (condition) {
// Statements executed while condition is true
}
Example:
count = 0
while (count < 3) {
ask("Iteration " + count)
count = count + 1
}
Exit the current loop:
for item in items {
if (item == "stop") {
break
}
ask(item)
}
Ask the user a question and display it in Claude Code.
Parameters:
question(string | variable): The question to ask
Examples:
ask("What is your name?")
ask(stored_question)
ask("Hello " + user_name + ", how are you?")
Compiled Output:
- String literal:
Ask the user: "What is your name?" - Variable:
Ask the user the question from variable: stored_question
Ask for yes/no confirmation from the user.
Parameters:
message(string | variable): The confirmation message
Examples:
if (confirm("Are you ready to proceed?")) {
ask("Great! Let's continue.")
}
Compiled Output:
Ask for confirmation: "Are you ready to proceed?"
Explicitly wait for user response before continuing.
Parameters: None
Example:
ask("Please describe your requirements")
wait_for_response()
ask("Thank you for the details!")
Compiled Output:
Wait for user response before continuing.
Create a file with specified content.
Parameters:
filename(string): The name of the file to createcontent(string | variable): The content to write to the file
Examples:
create_file("README.md", "project_readme")
create_file("config.json", config_template)
create_file("output.txt", "Hello, world!")
Compiled Output:
Create file "README.md" with content: project_readme
These are placeholders for functions that would be implemented by the runtime:
git_init()
git_commit("commit message")
git_push()
git_status()
github_create_repo()
github_create_pr()
github_create_issue("title", "body")
read_file("path/to/file")
append_file("path/to/file", "content")
delete_file("path/to/file")
Tasks spawn Claude Code subagents to handle subtasks autonomously. This is one of Stim's most powerful features, enabling complex multi-agent workflows.
| Agent | Description |
|---|---|
general |
General-purpose agent (default). Maps to general-purpose in Claude Code. |
explore |
Fast codebase exploration agent. Use for searching files, reading code, answering questions about the codebase. |
bash |
Command execution specialist. Use for git operations, builds, terminal tasks. |
plan |
Software architect agent. Use for designing implementation plans. |
Spawn a subagent with an inline body:
task "description" {
// statements the agent will execute
}
With an explicit agent type:
task explore "find auth patterns" {
ask("What authentication patterns exist in the codebase?")
wait_for_response()
}
Compiled Output:
Spawn a Explore subagent task: "find auth patterns"
Use the Task tool with:
- subagent_type: Explore
- description: find auth patterns
- prompt:
- Ask the user the question from variable: What authentication patterns exist in the codebase?
- Wait for user response before continuing.Reference another .stim file. The file is read and parsed at compile time, and its body is inlined:
task("helpers/research.stim")
task("helpers/research.stim", explore)
Parameters:
- First argument (string): path to
.stimfile, relative to the current file - Second argument (optional): agent type
The referenced file must contain a valid command declaration. The command name becomes the task description if none is provided.
Run multiple tasks concurrently:
parallel {
task "analyze frontend" {
ask("What frontend patterns exist?")
}
task explore "analyze backend" {
ask("What backend patterns exist?")
}
}
Compiled Output:
Spawn 2 subagent tasks in parallel:
### Task 1
Spawn a general-purpose subagent task: "analyze frontend"
Use the Task tool with:
- subagent_type: general-purpose
- description: analyze frontend
- prompt:
- Ask the user the question from variable: What frontend patterns exist?
### Task 2
Spawn a Explore subagent task: "analyze backend"
Use the Task tool with:
- subagent_type: Explore
- description: analyze backend
- prompt:
- Ask the user the question from variable: What backend patterns exist?Rules:
- A
parallelblock may only containtaskstatements - Each task runs as an independent subagent
- Tasks within a parallel block execute concurrently
When using file reference tasks, Stim detects circular references at compile time:
// a.stim
command a {
task("b.stim") // b.stim references a.stim -> Error!
}
This produces: Error: Circular task file reference detected: b.stim
Used for string concatenation:
result = "Hello " + "World" // "Hello World"
message = "Count: " + count // "Count: 5"
if (status == "complete") {
ask("Task is done!")
}
if (status != "pending") {
ask("Status has changed")
}
if (!is_complete) {
ask("Still working...")
}
if (is_ready && has_permission) {
ask("Starting process...")
}
if (is_admin || is_owner) {
ask("Access granted")
}
Convert array to string with separator:
items = ["a", "b", "c"]
result = items.join(", ") // "a, b, c"
// This is a comment
ask("Hello") // Comment at end of line
Not currently supported. Use multiple single-line comments:
// This is a multi-line comment
// that spans several lines
// to document complex logic
Every .stim file can be compiled for one of multiple AI tools. The --target flag selects which.
stim compile reviewer.stim --target cursor
stim install reviewer.stim --target chatgpt --local| Target | Default? | Output extension | Supports tools/model |
Install location |
|---|---|---|---|---|
claude |
✓ | .md |
✓ | ~/.claude/{commands,agents}/ |
cursor |
.mdc |
drops with warning | ./.cursor/rules/ |
|
chatgpt |
.md |
drops with warning | ./dist/chatgpt/ or ./prompts/ |
stim compile always writes to ./dist/<target>/<name>.<ext> so that multi-target builds never collide.
See Targets for deeper adapter behavior and how to add a new target.
Understanding how Stim compiles to target markdown helps debug issues.
name = "John"
Compiles to:
Set name = John
if (condition) {
ask("Hello")
}
Compiles to:
If condition:
- Ask the user: "Hello"
for item in items {
ask(item)
}
Compiles to:
For each item in items:
- Ask the user the question from variable: item
create_file("test.txt", "content")
Compiles to:
Create file "test.txt" with content: content
task explore "find bugs" {
ask("What bugs exist?")
}
Compiles to:
Spawn a Explore subagent task: "find bugs"
Use the Task tool with:
- subagent_type: Explore
- description: find bugs
- prompt:
- Ask the user the question from variable: What bugs exist?
parallel {
task "task A" {
ask("Do A")
}
task "task B" {
ask("Do B")
}
}
Compiles to:
Spawn 2 subagent tasks in parallel:
### Task 1
Spawn a general-purpose subagent task: "task A"
...
### Task 2
Spawn a general-purpose subagent task: "task B"
...
agent reviewer {
description "Reviews PRs"
tools [Read, Grep]
model "sonnet"
"You are a code reviewer."
}
Compiles to (claude target):
---
name: reviewer
description: Reviews PRs
tools: [Read, Grep]
model: sonnet
---
You are a code reviewer.Compiles to (cursor target):
---
description: Reviews PRs
globs:
alwaysApply: false
---
You are a code reviewer.Compiles to (chatgpt target):
# reviewer
> Reviews PRs
You are a code reviewer.Error: Expected command declaration: command <name> {
Error: Invalid ask statement: ask(unclosed string"
Error: Invalid assignment: name =
Error: File not found: /path/to/file.stim
Error: No input file specified
Error: Input file must have .stim extension
- Check syntax: Ensure all strings are quoted and braces are balanced
- Verify file paths: Use absolute paths for input files
- Test compilation: Use
bun run dev compile --dry-runto check syntax - Read output: Check the generated
.mdfile for expected behavior
- Use descriptive names:
deployment_environmentnotenv - Use consistent casing: stick to
snake_caseorcamelCase - Avoid reserved words: don't use
if,for,whileas variable names
- Always quote string literals:
ask("Hello")notask(Hello) - Use variables for reusable content: store repeated strings in variables
- Handle edge cases: check conditions before loops and file operations
- Group related variables at the top
- Use comments to explain complex logic
- Keep functions focused on single responsibilities
This API reference is for Stim v1.0. Future versions may include:
- v1.1: String interpolation, standard library
- v2.0: Multi-file projects, package management
- Breaking changes will be announced in advance
- Migration guides will be provided for major version updates
- Backward compatibility maintained within major versions
For more help: