Struckdown CLI Usage Guide
Complete reference for the sd command-line interface.
Table of Contents
Installation
# Using uv (recommended)
uv tool install git+https://github.com/benwhalley/struckdown/
# Or install in current environment
uv pip install git+https://github.com/benwhalley/struckdown/
Environment Setup:
export LLM_API_KEY="your-api-key" # e.g., from openai.com
export LLM_API_BASE="https://api.openai.com/v1"
export DEFAULT_LLM="litellm/gpt-4.1-mini"
Commands
sd chat
Run a single prompt interactively. Best for testing prompts and quick experiments.
Syntax:
sd chat [OPTIONS] PROMPT...
Options:
--model-name TEXT- Override default LLM model--show-context- Print the resolved prompt context after execution--verbose- Enable debug logging
Examples:
# Simple completion
sd chat "Tell me a joke: [[joke]]"
# Multiple slots
sd chat "Name a color: [[pick:color|red,blue,green]] Describe it: [[description]]"
# With context display
sd chat "Pick a number: [[int:number]]" --show-context
sd batch
Process multiple inputs in batch mode. Supports files, globs, stdin, and chaining.
Syntax:
sd batch [OPTIONS] [INPUTS...] [PROMPT]
Arguments:
INPUTS- Input files or glob patterns (e.g.,*.txt,data/*.json)PROMPT- Inline prompt with extraction slots
Input Sources:
- Files:
sd batch file1.txt file2.txt "[[summary]]" - Globs:
sd batch inputs/*.txt "[[summary]]" - Stdin:
cat data.txt | sd batch "[[summary]]" - JSON stdin:
echo '{"name":"Alice"}' | sd batch "Hello [[greeting]]"
Output Formats:
- JSON (
.json) - Default, structured data - CSV (
.csv) - Flattened tabular format - Excel (
.xlsx) - Spreadsheet format - Markdown (
.md,.txt) - Markdown tables - stdout - JSON to stdout if no
-ospecified
Global Options
--help
Show help for any command.
sd --help
sd chat --help
sd batch --help
Output: stdout Exit code: 0
Batch Processing Options
-o / --output PATH
Output file path. Format auto-detected from extension.
sd batch *.txt "[[name]]" -o results.json # JSON output
sd batch *.txt "[[name]]" -o results.csv # CSV output
sd batch *.txt "[[name]]" -o results.xlsx # Excel output
sd batch *.txt "[[name]]" -o results.md # Markdown table
Default: Outputs JSON to stdout if omitted.
-p / --prompt PATH
Load prompt from file instead of inline.
# prompt.sd contains: "Extract name: [[name]]"
sd batch *.txt -p prompt.sd -o results.json
Cannot be combined with inline prompt.
-k / --keep-inputs
Include input fields in output (filename, content, basename).
sd batch *.txt "[[summary]]" -k -o results.json
Output includes:
{
"filename": "input.txt",
"input": "original text...",
"content": "original text...",
"basename": "input",
"summary": "extracted summary"
}
Default: Only extracted fields + filename for traceability.
-q / --quiet
Suppress progress output to stderr.
sd batch *.txt "[[name]]" -o results.json --quiet
Behavior:
- ✗ No progress bar
- ✗ No verbose logs (unless
--verbosealso specified) - ✓ Errors still shown on stderr
Use case: Scripting, cron jobs, CI/CD pipelines.
--verbose
Enable debug logging to stderr.
sd batch *.txt "[[name]]" --verbose 2> debug.log
Output includes:
- Prompts sent to LLM
- LLM responses
- Cache hits/misses
- Per-item processing status
- Stack traces on errors
Destination: stderr
--model-name TEXT
Override default LLM model for this run.
sd batch *.txt "[[summary]]" --model-name "litellm/gpt-4"
Default: $DEFAULT_LLM environment variable.
Progress Bars
sd batch shows a progress bar by default during processing:
Processing ⠋ ━━━━━━━━━━━━━━━━━━━━━━ 47/100 47% 0:00:23
Display includes:
- Spinner animation
- Progress bar
- Items completed / total
- Percentage
- Estimated time remaining (ETA)
Automatic Behavior
Shown when:
- stderr is a TTY (interactive terminal)
- NOT in
--quietmode
Hidden when:
- stderr is redirected:
sd batch ... 2> errors.log - stdout is piped:
sd batch ... | jq . --quietflag is used
Manual Control
# Force show (default in terminal)
sd batch *.txt "[[name]]" -o out.json
# Force hide
sd batch *.txt "[[name]]" -o out.json --quiet
# Redirect to ignore progress
sd batch *.txt "[[name]]" -o out.json 2>/dev/null
Output Streams
Struckdown follows CLI best practices for output streams:
stdout (File Descriptor 1)
Primary output - machine-readable results, intended for piping/capture.
Contains:
- Extracted results (JSON by default)
--versionoutput--helpoutput- Success messages from output formatters (via logger.info, but these go to stderr)
Example:
sd batch *.txt "[[name]]" > results.json # Only results captured
stderr (File Descriptor 2)
Diagnostics - human-readable status, errors, warnings.
Contains:
- Progress bars
- Error messages
- Warnings
- Verbose debug logs
- Logger output (info, warning, error levels)
Example:
sd batch *.txt "[[name]]" 2> errors.log # Only diagnostics captured
Exit Codes
| Code | Meaning | Examples |
|---|---|---|
| 0 | Success | Command completed without errors |
| 1 | Error | LLM failure, file not found, processing error |
| 2 | Usage error | Invalid arguments, missing required options |
Example:
sd batch --invalid-flag
# Exit code: 2 (usage error)
sd batch missing-file.txt "[[x]]"
# Exit code: 1 (file not found error)
sd batch *.txt "[[x]]" -o out.json
# Exit code: 0 (success)
Examples
Basic Batch Processing
Extract names from text files:
sd batch documents/*.txt "Extract person's name: [[name]]" -o names.json
Output:
[
{"filename": "doc1.txt", "name": "Alice"},
{"filename": "doc2.txt", "name": "Bob"}
]
Piping and Chaining
Chain multiple extraction steps:
sd batch *.txt "Purpose: [[purpose]] Name: [[name]]" | \
sd batch ": . Amazon equivalent? [[product]]" -k
Process and filter with jq:
sd batch *.txt "Price: [[number:price]]" | jq '.[] | select(.price > 100)'
Quiet Mode for Scripts
Silent execution, log errors:
#!/bin/bash
sd batch data/*.txt "[[summary]]" -o results.json --quiet 2> errors.log
if [ $? -eq 0 ]; then
echo "Processing complete: results.json"
else
echo "Errors occurred, check errors.log"
exit 1
fi
Verbose Debugging
Capture full debug output:
sd batch *.txt "[[name]]" --verbose 2> debug.log
Debug log contains:
- Prompts sent to LLM
- Raw LLM responses
- Cache operations
- Template rendering details
- Full stack traces on errors
Reading from Stdin
Plain text:
echo "Hello world" | sd batch "Translate to Spanish: [[translation]]"
JSON input:
echo '[{"name":"Alice"},{"name":"Bob"}]' | \
sd batch "Hello ! [[greeting]]"
From file:
cat data.txt | sd batch "Summarize: [[summary]]" -o summary.json
Multiple Output Formats
CSV for spreadsheet import:
sd batch *.txt "Name: [[name]] Age: [[int:age]]" -o people.csv
Excel for reports:
sd batch *.txt "Product: [[product]] Price: [[number:price]]" -o report.xlsx
Markdown for documentation:
sd batch *.txt "Feature: [[feature]] Status: [[pick:status|done,wip,todo]]" -o status.md
Error Handling
Separate successful results and errors:
sd batch *.txt "[[summary]]" -o results.json 2> errors.log
# Check exit code
if [ $? -ne 0 ]; then
echo "Some items failed, check errors.log"
fi
Verbose mode for debugging failures:
sd batch failing-input.txt "[[x]]" --verbose
Tips and Best Practices
Performance
- Use caching: Repeated prompts use cached results (see
$STRUCKDOWN_CACHE) - Batch processing: Process multiple files in one command, not a loop
- Progress monitoring: Leave progress bars on in long-running jobs
Composability
- Pipe results:
sd batch ... | sd batch ...for multi-stage extraction - Use jq: Post-process JSON output with
jqfor filtering/transformation - Redirect smartly: Capture results and errors separately
Debugging
- Start with chat: Test prompts with
sd chatbefore batch processing - Use –verbose: Enable verbose mode to see exactly what’s happening
- Check exit codes: Use
$?in scripts to detect failures
Scripting
- Use –quiet: Suppress progress in cron jobs and CI/CD
- Check exit codes: Always check
$?aftersd batch - Log errors: Redirect stderr to a log file for auditing
Related Documentation
- Getting Started - Quick start guide
- Template Syntax - Struckdown template syntax
- Model Overrides - Per-slot LLM configuration
- Number Extraction - Numeric validation
Migration from chatter
The legacy chatter CLI has been removed in v0.1.6.
Old command:
chatter run "Tell me a joke: [[joke]]"
New command:
sd chat "Tell me a joke: [[joke]]"
Functionality is identical. All flags and options work the same way.