User Guide
Learn what literate commands are, when to use them, and how to create them.
What Are Literate Commands?
Literate commands are markdown files that define interactive, multi-step workflows. They're designed for complex tasks that:
- Require user input at multiple stages
- Need to branch based on conditions
- Should execute scripts with collected data
Unlike regular slash commands (like /help or /todo), literate commands guide users through a conversation rather than executing a single action.
When to Use Literate Commands
✓ Good Use Cases
- Onboarding wizards — Collect user preferences, set up accounts
- Setup workflows — Initialize projects, configure environments
- Decision trees — Ask questions, branch based on answers
- Data collection — Gather structured input before processing
- Multi-step processes — Break complex tasks into digestible steps
✗ Avoid For
- Simple one-off commands that just run a script
- Quick actions without user interaction
- Tasks that don't need state between steps
How Literate Commands Work
A literate command is a markdown file with:
- Frontmatter with
literate: true - Steps separated by
--- - Step configuration in YAML code blocks with
{config}annotation - Prompts and scripts in regular code blocks
Basic Structure
---
description: My workflow
literate: true
---
Step 1 content and prompts...
---
Step 2 content...
Creating a Literate Command
1. Create the File
Create a markdown file in .opencode/commands/:
touch .opencode/commands/my-command.md
2. Add Frontmatter
---
description: A brief description of what this command does
literate: true
---
3. Define Steps
Steps are separated by ---:
---
description: My workflow
literate: true
---
First step content...
---
Second step content...
---
Third step content...
4. Add Step Configuration
Use YAML code blocks with {config} to control step behavior:
```yaml {config}
step: step-name
## Step Configuration Reference
### Basic Options
| Option | Type | Description |
|--------|------|-------------|
| `step` | string | Unique name for routing (use `kebab-case`) |
| `stop` | boolean | End command after this step |
### Collecting Variables
Use `parse` to collect data from the user:
```yaml {config}
step: collect-name
parse:
name: string
age: number
newsletter: bool
The model will be prompted to respond with JSON containing these variables.
Type Coercion
| Type | Description | Example |
|---|---|---|
string |
Plain text | "Alice" |
number |
Numeric value | 42 |
bool |
Boolean | true / false |
Conditional Routing
Use next to control which step runs next:
step: decide
next:
"role === 'admin'": admin-panel
"role === 'user'": user-panel
_: fallback
- Conditions use JavaScript expressions
- The
_key is the fallback when no condition matches - Conditions are evaluated against collected variables
Simple Redirect
next: other-step
Variable Substitution
Use $variable to inject collected data anywhere in your prompts:
Hello **$name**! You selected $count items.
Nested Variables
Access nested properties with dot notation:
| Syntax | Example | Result |
|---|---|---|
$obj.prop |
$user.email |
alice@example.com |
$arr.0 |
$items.0 |
first item |
$$ |
$$ |
Full metadata JSON |
Script Execution
Run scripts within steps using {exec} code blocks:
Basic Execution
```bash {exec}
echo "Hello $name"
```
The script runs and its output is shown to the user.
Storing Script Output
Use mode=store to parse JSON output as variables:
```python {exec mode=store}
import json
print(json.dumps({"result": len("$name")}))
```
Exec Modes
| Mode | Description |
|---|---|
stdout |
Show output (default) |
store |
Parse JSON output as variables |
none |
Execute silently |
Custom Interpreters
Specify the interpreter explicitly:
```python {exec=uv run python -c}
print("Hello!")
```
```bash {exec=sh -c}
echo "Running in sh"
```
Complete Example
Here's a project setup wizard:
---
description: Create a new project
literate: true
---
```yaml {config}
step: project-info
parse:
name: string
type: string
step: confirm
next:
"confirmed === true": create
_: cancel
true or false.
step: create
mkdir -p "$name"
cd "$name" && git init
echo "✓ Created $name"
step: success
stop: true
step: cancel
stop: true
## Best Practices
### Keep Steps Focused
One clear purpose per step. If a step does multiple things, consider splitting it.
### Validate Early
Collect and validate data before doing expensive operations.
### Use Meaningful Step Names
Makes routing easier to debug and maintain.
### Store, Don't Echo
Use `mode=store` for script results you want to keep as variables.
### Test Your Commands
Run your command and verify each branch works:
```bash
opencode run --print-logs --log-level DEBUG --command your-command-name
Security Considerations
Review Before Running
Literate commands can execute arbitrary code. Always review command files before running them, especially those from third parties.
When sharing literate commands: - Document what scripts they execute - Make the source of scripts clear - Encourage users to review before executing
Next Steps
- Installation — Install the plugin in your project
- Architecture — Understand how the plugin works
- Create your own — Start building literate commands for your workflows