Code Cells
Executable units within a Spryfile
Code cells are the executable units within a Spryfile.
What is a Code Cell?
A code cell is a fenced code block that Spry can execute or materialize. The fence info string contains metadata that tells Spry how to handle the code.
Syntax
```language identity --flags { json5-attributes }
code content
```Components
Language - The interpreter/handler (e.g., bash, sql, python)
Identity - Unique name for this cell (e.g., setup-env)
Flags - Command-line style options (e.g., --dep other-task)
Attributes - JSON5 object with additional config (e.g., { timeout: 30 })
Parsing Order
The info string is parsed in this order:
- Language - First token (required)
- Identity - Second token if not a flag (optional)
- Flags - POSIX-style options
- Attributes - JSON5 object at the end
Examples
Basic Shell Task
```bash greet
echo "Hello, World!"
```With Description
```bash greet --descr "Display a greeting message"
echo "Hello, World!"
```With Dependencies
```bash process-data --dep fetch-data --descr "Process the fetched data"
python process.py data.json
```With Multiple Flags
```bash build --dep test --descr "Build the project" --capture ./build.log
npm run build
```With JSON5 Attributes
```bash long-task --descr "Task with timeout" { timeout: 300, retry: 3 }
./long-running-script.sh
```Cell Types
Spry classifies cells into two natures based on their language:
Executable Cells
Languages that run as processes via the unified spawn framework:
| Language | Aliases | Engine | Description |
|---|---|---|---|
shell | bash, sh, zsh, fish | OS Shell | Shell scripts |
pwsh | powershell | OS Shell | PowerShell scripts |
cmd | OS Shell | Windows command scripts | |
sql | SQL Shell | SQL via psql, sqlite3, or duckdb | |
python | py | Shebang | Python scripts |
typescript | ts | Deno | TypeScript via Deno |
javascript | js | Deno | JavaScript via Deno |
deno-task | Deno | Deno task runner |
Executable cells:
- Run via
lib/spawnexecution framework - Capture stdout/stderr
- Return exit codes
- Can be awaited by dependent tasks
- Interpolation is OFF by default
Execution Engines
Spry uses a unified execution framework with distinct engine types:
| Engine Type | Examples | Description |
|---|---|---|
| OS Shell | bash, sh, pwsh, cmd, fish | Operating system shells |
| SQL Shell | psql, sqlite3, duckdb | Database CLI tools |
| Function | env, envrc | In-process execution (no subprocess) |
| Using | External references | Reference to catalog-defined resources |
The engine resolves the runtime based on the language and available tools.
Materializable Cells
Languages that produce content for external systems:
| Language | Description |
|---|---|
sql | SQL statements (for SQLPage, databases) |
html | HTML markup |
json | JSON data |
yaml | YAML configuration |
env, envrc | Environment variable files |
utf8 | Binary/blob content |
Materializable cells:
- Content is stored or emitted (filesystem, database, etc.)
- Interpolation is ON by default
- Can be injected into other cells via PARTIAL
- Used with SQLPage and other playbooks
Function Engine Languages
Some languages use in-process function engines rather than spawning external processes:
| Language | Purpose |
|---|---|
env | Environment variable files |
envrc | direnv configuration files |
These are processed by FunctionEngine implementations in lib/spawn/function-shell.ts. They:
- Execute in-process (no subprocess spawned)
- Echo input directly to output or perform TypeScript-based processing
- Are treated as materializable with
memoizeOnlybehavior - Behave identically to other engines from the caller's perspective
Capture-Only Languages
These languages are never executed, only captured:
| Language | Purpose |
|---|---|
env | Environment variable files |
envrc | direnv configuration files |
Directive Cells
Special cells that configure behavior:
| Directive | Purpose |
|---|---|
PARTIAL | Template fragment for injection |
DEFAULTS | Set default flags for cells |
contribute expand | Expand compact specs into multiple code blocks |
Contribute Expand
The contribute expand directive allows you to declare multiple code blocks in a compact format:
```contribute expand
sql core-ddl -X prime --include ../../src/core-ddl.sqlite.sql
sql info-schema -X prime --include ../../src/info-schema.sqlite.sql
```This expands into equivalent individual code blocks:
```sql core-ddl -X prime --include ../../src/core-ddl.sqlite.sql
```
```sql info-schema -X prime --include ../../src/info-schema.sqlite.sql
```You can also set a default body with the REMARKS directive:
```contribute expand
REMARKS select 1 as placeholder;
sql a --include ./a.sql
sql b --include ./b.sql
```Lines starting with #, //, or -- are treated as comments.
Cell Identity
Every executable cell needs a unique identity:
```bash my-unique-name
echo "I can be referenced as 'my-unique-name'"
```Identity Rules
- Must be unique within the document
- Should be descriptive (
build-frontendnotstep1) - Use kebab-case by convention
- Used in
--depreferences
Common Flags
--descr
Add a description to the task:
```bash setup --descr "Initialize the development environment"
npm install
```Type: String
Default: None
--dep, -D
Declare dependencies on other tasks (can be repeated):
```bash deploy --dep build --dep test
kubectl apply -f deployment.yaml
``````bash deploy -D build -D test
kubectl apply -f deployment.yaml
```Type: String (task identity), repeatable
Default: None
--capture, -C
Capture task output to a file or memory:
```bash get-version --capture ./version.txt
git describe --tags
``````bash get-value -C
echo "computed result"
``````bash get-data --capture mykey
curl https://api.example.com/data
```Type: Path (starting with ./) or memory key
Default: None
When the path starts with ./, it's treated as a relative filesystem path. Otherwise, it's treated as an in-memory capture key.
--gitignore
Add captured file to .gitignore:
```bash generate-secrets --capture ./secrets.env --gitignore
openssl rand -hex 32
```Type: Boolean flag or string
Default: false
Requires: --capture with a file path
--interpolate, -I
Enable template interpolation (off by default for executables):
```bash greet -I
echo "Hello, ${NAME}!"
```Type: Boolean flag
Default: false for executables, true for materializables
--injectable, -J
Mark as available for PARTIAL injection:
```sql create-table --injectable
CREATE TABLE users (id INT PRIMARY KEY);
```Type: Boolean flag
Default: false
--executable, -X
Mark a materializable cell as also executable:
```sql setup-db -X
CREATE TABLE IF NOT EXISTS users (id INT);
```Type: Boolean flag
Default: false
--silent
Suppress output during execution:
```bash background-task --silent
./noisy-script.sh 2>/dev/null
```Type: Boolean flag
Default: false
--graph, -G
Assign to a named graph for selective execution:
```bash deploy-step -G deploy
kubectl apply -f manifests/
```Type: String, repeatable
Default: None
Tasks with a graph can be run selectively:
spry rb run --graph deploy--include
Include external file content into the cell. Multiple --include flags can be specified to concatenate multiple files:
```sql setup --include ./schema.sql --include ./seed-data.sql
-- Additional SQL after includes
```Type: String (file path), repeatable
Default: None
Supported path formats:
- Relative paths:
--include ./path/to/file.sql - Absolute paths:
--include /absolute/path/file.sql - Quoted paths with spaces:
--include "./path with spaces/file.sql"
The included files are loaded and concatenated in order.
--conf
Specify a configuration file for the task:
```bash complex-task --conf ./config.json
./run-with-config.sh
```Type: String (file path)
Default: None
JSON5 Attributes
Extended configuration after flags:
```bash task --descr "Description" { timeout: 300, retry: 3 }
./script.sh
```Common Attributes
| Attribute | Type | Description |
|---|---|---|
route | object | SQLPage route configuration |
timeout | number | Timeout in seconds |
retry | number | Number of retry attempts |
retryDelay | number | Seconds between retries |
env | object | Additional environment variables |
workdir | string | Working directory |
SQLPage Route Attributes
For SQLPage materializables:
```sql index.sql { route: { caption: "Home", icon: "home" } }
SELECT 'card' as component;
```Attribute Examples
```sql dashboard.sql { route: { caption: "Dashboard", icon: "chart-bar", order: 1 } }
SELECT 'shell' as component, 'Dashboard' as title;
``````bash with-env { env: { DEBUG: "true", LOG_LEVEL: "verbose" } }
./script.sh
``````bash in-subdir { workdir: "./subproject" }
npm run build
```Interpolation
Cells can include interpolated values:
```bash deploy -I
echo "Deploying version ${VERSION} to ${ENVIRONMENT}"
kubectl set image deployment/app app=${IMAGE}:${VERSION}
```Interpolation sources:
- Environment variables:
${env.VAR_NAME} - Frontmatter values:
${config.key} - Captured outputs:
${TASK_NAME} - Memory stores:
${memory.key}
Interpolation Defaults
- Executable cells have interpolation OFF by default (use
-Ito enable)
Anonymous Cells
Cells without identity are not directly executable but may still be processed:
```bash
# This cell has no identity
# It won't appear in task lists
echo "Anonymous cell"
```Anonymous cells are useful for:
- Display-only code examples
- Non-runnable documentation
- Code that shouldn't be in the workflow
Flag Shortcuts
Quick reference for commonly used flag shortcuts:
| Shortcut | Full Flag |
|---|---|
-C | --capture |
-D | --dep |
-G | --graph |
-B | --branch |
-H | --hook |
-I | --interpolate |
-J | --injectable |
-X | --executable |
Complete Examples
Minimal Cell
```bash hello
echo "Hello"
```Fully Specified Cell
```bash deploy-production --descr "Deploy to production environment" --dep build --dep test --capture ./logs/deploy.log --gitignore -G deploy { route: { caption: "Deploy" } }
kubectl apply -f k8s/production/
kubectl rollout status deployment/myapp --timeout=300s
```SQL with Interpolation
```sql report --descr "Generate daily report" -I
SELECT * FROM events
WHERE date >= '${START_DATE}'
AND date <= '${END_DATE}'
ORDER BY date DESC;
```PARTIAL with Injection
```sql PARTIAL common-header --inject **/*.sql
SELECT 'shell' as component,
'${APP_NAME}' as title,
'database' as icon;
```Chain of Dependencies
```bash step-1 --descr "First step"
echo "Step 1"
```
```bash step-2 --dep step-1 --descr "Second step"
echo "Step 2"
```
```bash step-3 --dep step-2 --descr "Third step" --capture ./result.txt
echo "Final result"
```Environment File
```envrc prepare-env -C ./.envrc --gitignore --descr "Generate .envrc file"
export SPRY_DB="sqlite://app.db?mode=rwc"
export PORT=9227
```Multiple File Includes
```sql init-database --include ./schema/tables.sql --include ./schema/indexes.sql --include ./data/seed.sql
-- Additional setup after includes
SELECT 'Database initialized' as status;
```Recursive Task
```bash converge-config --recurse --descr "Converge configuration until stable"
# This task re-runs until output stabilizes
process-config.sh
```Contribute Expand Block
```contribute expand
sql schema-core -X --include ./src/core.sql
sql schema-ext -X --include ./src/extensions.sql
sql seed-data -X --include ./data/seed.sql
```Validation
Spry validates cell arguments for:
- Valid flag names
- Required values for flags that need them
- Dependency references to existing tasks
- No circular dependencies
- Unique identities within a document
spry axiom inspect myfile.mdspry rb issues myfile.mdBest Practices
Always name executable cells
Use descriptive identities for all executable cells
Add descriptions
Explain what each task does with --descr
Explicit dependencies
Don't rely on file order, use --dep flags
Be careful with interpolation
Only enable when needed for executables
Use appropriate languages
Match the task to the right language handler
Use graphs for organization
Group related tasks with --graph
How is this guide?
Last updated on