| name | steno |
| description | Execute stenographic shorthand commands for efficient coding. Triggers on verb-colon-target patterns (dx, mk, ch, viz, stat, fork, switch, compare, merge, abandon) and steno-colon commands for session management. Branching commands (fork, switch, compare, merge, abandon) operate on .steno/graph.json, NOT git. |
Steno: Stenographic Command Interface
You understand and execute stenographic shorthand for efficient coding and data analysis.
Recognition
Execute steno commands when you see this pattern:
[mode][verb]:[target] [@refs] [+additions] [-exclusions] [.flags] [precision]
Trigger patterns:
- verb:target - like dx:@file.csv or mk:api
- verb:type @file - like viz:pca @data.csv
- ?mode topic - like ?plan architecture
- Commands with precision markers: ~deep ~ ! ?
- steno:command - session management
- fork:name - create branch
- switch:name - switch branch
- compare:branch-a branch-b - compare branches
- merge:branch - adopt branch into current
- abandon:branch - discard branch
Core Verbs
| Verb | Meaning | Action |
|---|---|---|
dx |
Diagnose/explore | Read, analyze, summarize |
mk |
Make/create | Create new file, component, feature |
ch |
Change/modify | Edit existing code |
rm |
Remove/delete | Delete code, files, features |
fnd |
Find/search | Search codebase |
viz |
Visualize | Generate visualization or analysis |
stat |
Statistics | Run statistical test or analysis |
ts |
Test | Run or create tests |
doc |
Document | Generate documentation |
Branching Verbs
| Verb | Meaning | Action |
|---|---|---|
fork |
Create branch | fork:name - create new branch from current node |
switch |
Switch branch | switch:name - switch to existing branch |
compare |
Compare branches | compare:a b - show differences between branches |
merge |
Merge branch | merge:name - adopt branch work into current branch |
abandon |
Abandon branch | abandon:name - discard branch without merging |
These are NOT git commands. They operate on the steno graph in .steno/graph.json.
Modifiers
| Syntax | Meaning | Example |
|---|---|---|
@file.ext |
File reference | dx:@data.csv |
@name |
Named reference or bookmark | ch:@auth-module |
@branch:ref |
Cross-branch reference | viz:diff @deseq2:^ @ancombc:^ |
+feature |
Add/include | mk:api +auth +cache |
-thing |
Exclude/without | ch:@config -logging |
.flag |
Apply flag | .ts (TypeScript), .dry (dry run) |
.flag:value |
Flag with qualifier | .plot:png or .ts:edge |
Precision Markers
| Marker | Meaning | Your behavior |
|---|---|---|
| ~ | Flexible | Use your judgment |
| ! | Exact/literal | Do exactly what's specified |
| ? | Clarify | Ask before acting |
| ~deep | Extended thinking | Think deeply, consider alternatives |
Modes (prefix with ?)
| Mode | Meaning | Your behavior |
|---|---|---|
| ?plan | Planning | Outline approach before executing |
| ?sketch | Sketch | Create rough implementation for review |
| ?challenge | Challenge | Critique, find issues, push back |
| ?explore | Explore | Investigate options, think aloud |
Session Tracking
Steno tracks commands in .steno/ for cross-session memory and stale detection.
On Session Start
When you first see a steno command in a session:
- Check if
.steno/graph.jsonexists - If yes, briefly note the last session:
> Previous session (Dec 26, 3 commands) > Last: viz:pca @normalized.csv - Check for stale outputs (files that changed since commands ran)
After Each Command
After executing a steno command, log it to .steno/current-session.json:
- Read the file (create if missing, with empty
nodesarray) - Determine the next node ID from the file or start at
n_001 - Append a node:
{ "id": "n_001", "timestamp": "2024-12-27T14:30:22Z", "raw": "dx:@samples.csv", "status": "complete", "inputs": ["samples.csv"], "outputs": [], "summary": "18 samples, 5 genes, 2 conditions" } - Write back to the file
Node Structure
{
"id": "n_001",
"timestamp": "ISO timestamp",
"raw": "the original command",
"status": "complete|failed",
"inputs": ["files read"],
"outputs": ["files created or modified"],
"summary": "brief description of result"
}
Tracking Inputs/Outputs
- Inputs: Files explicitly referenced (
@file) plus any files you read - Outputs: Files you create or modify
- Summary: One-line description of the result
Reference Resolution
The ^ Reference (Previous Output)
When you see ^ in a command:
- Read
.steno/current-session.json - Find the last node
- Use its outputs as input for this command
Example:
dx:@samples.csv # outputs: analyzed samples.csv
ch:^ +normalize # ^ = samples.csv from previous
viz:pca ^ # ^ = normalized output from previous
If the previous node has no outputs, use its inputs.
The @name Reference (Bookmark)
When you see @name that isn't a file path:
- Check if it's a file that exists - if so, use the file
- Otherwise, check
.steno/graph.jsonbookmarks - If found, resolve to that node's outputs
Example:
stat:ttest @baseline-pca @new-samples
# @baseline-pca resolves to outputs of bookmarked node
# @new-samples is a file reference
Cross-Branch References (@branch:ref)
Reference outputs from other branches without switching:
| Syntax | Meaning |
|---|---|
@branch:^ |
Last output from branch |
@branch:n_001 |
Specific node from branch |
@branch:-1 |
Last node (same as ^) |
@branch:-2 |
Second-to-last node |
Examples:
# Compare results from two method branches
stat:compare @deseq2:^ @ancombc:^
# Use output from experiment branch on main
switch:main
viz:pca @experiment:n_004
# Reference specific node from another branch
ch:@caching:n_003 +redis
Resolution process:
- Parse
@branch:refformat (colon separates branch from reference) - Look up branch in
.steno/graph.json - Resolve the reference within that branch:
^or-1: Last node on that branch-N: Nth from last on that branchn_XXX: Specific node ID
- Return that node's outputs (or inputs if no outputs)
Distinguishing from files:
The parser checks in order:
- Does
@pathexist as a file? → Use file - Does
@namecontain:with valid branch? → Cross-branch ref - Is
@namea bookmark? → Bookmark ref - Error: reference not found
Example disambiguation:
@data:clean.csv # File "data:clean.csv" if exists, else branch "data" node "clean.csv"
@experiment:^ # Branch "experiment", last node
@deseq2:n_018 # Branch "deseq2", node n_018
Session Commands
steno:help
Show quick reference for steno commands.
> steno:help
VERBS: dx mk ch rm fnd viz stat ts doc
REFS: @file.csv ^ @bookmark @branch:^
MODS: +add -exclude .flag .flag:value
PREC: ~flexible !exact ?clarify ~deep
SESSION:
steno:history - command history
steno:stale - check for stale outputs
steno:refresh - re-run stale commands
steno:bookmark - save reference
steno:graph - show workflow tree
steno:undo - undo last command (! for hard)
steno:redo - restore undone command
steno:export - export workflow (.md .json .sh)
steno:import - import workflow (! to confirm)
steno:reset - clear all state (requires !)
POWER:
steno:alias - create/list/remove aliases (supports {params})
steno:search - search command history
steno:replay - re-run command sequences
steno:template - reusable command workflows
steno:diff - compare nodes/branches/bookmarks
steno:transcript - generate/link conversation transcripts
steno:history
Show command history across sessions.
> steno:history
Session Dec 27 (current, 2 commands):
n_004: viz:pca @normalized.csv -> pca_plot.png
n_003: ch:@samples.csv +normalize -> normalized.csv
Session Dec 26 (3 commands):
n_002: dx:@samples.csv
n_001: mk:analysis-script
Read from .steno/graph.json and .steno/current-session.json.
steno:stale
Check for outputs that may be stale (input files changed).
> steno:stale
Checking file modifications...
Stale:
n_002: ch:@samples.csv +normalize
input samples.csv modified since execution
Affected downstream:
n_003: viz:pca ^ (depends on n_002)
Run steno:refresh to re-run stale commands.
Compare file modification times to node timestamps.
steno:refresh
Re-run stale commands in dependency order.
> steno:refresh
Re-running n_002: ch:@samples.csv +normalize
Updated normalized.csv
Re-running n_003: viz:pca ^
Updated pca_plot.png
2 nodes refreshed.
steno:bookmark
Save the last command as a named reference.
> viz:pca @samples.csv
[creates pca_plot.png]
> steno:bookmark baseline-pca
Bookmarked n_003 as "baseline-pca"
Reference with @baseline-pca
Add to .steno/graph.json bookmarks:
{
"bookmarks": {
"baseline-pca": "n_003"
}
}
steno:clear
Clear current session (start fresh).
> steno:clear
Current session cleared. Previous sessions preserved in graph.json.
steno:undo
Undo the last command or a specific node.
Soft undo (default) — removes from graph, keeps files:
> steno:undo
Undoing n_005: ch:@data.csv +normalize
Removed from session graph.
Files kept: normalized.csv
Dependent nodes (will be orphaned):
n_006: viz:pca ^
Use steno:redo to restore.
Hard undo — also reverts file changes:
> steno:undo!
Undoing n_005: ch:@data.csv +normalize
Removed from session graph.
Deleted: normalized.csv
Reverted: data.csv (restored from before edit)
Dependent nodes removed: n_006
Undo specific node:
> steno:undo n_003
Undoing n_003: viz:heatmap @samples.csv
Removed from session graph.
Files kept: heatmap.png
Behavior:
- Read
.steno/current-session.json - Find target node (last if none specified)
- Check for dependent nodes (nodes that reference this one via
^) - Soft undo:
- Remove node from session
- Move to
undonearray (for redo) - List created files (user can delete manually)
- Warn about orphaned dependents
- Hard undo (add ! flag):
- Remove node and all dependents
- Delete created files
- Revert modified files (if backup exists)
File handling:
For hard undo, steno needs file backups. When a command modifies a file:
- Before: Save
.steno/backups/{node_id}/{filename} - After: Record in node's
backupsfield
{
"id": "n_005",
"raw": "ch:@data.csv +normalize",
"outputs": ["normalized.csv"],
"modified": ["data.csv"],
"backups": {
"data.csv": ".steno/backups/n_005/data.csv"
}
}
Hard undo restores from backup and deletes outputs.
steno:redo
Restore the last undone node.
> steno:redo
Restoring n_005: ch:@data.csv +normalize
Added back to session graph.
Note: Files were not deleted, so outputs still exist.
Behavior:
- Check
undonearray in current session - Pop the last undone node
- Add it back to the session
- Note: Does not re-execute the command, just restores tracking
If files were deleted (hard undo), redo just restores tracking:
> steno:redo
Restoring n_005: ch:@data.csv +normalize
Added back to session graph.
⚠ Files were deleted. Re-run command to recreate:
ch:@data.csv +normalize
steno:export
Export workflow for sharing or documentation.
Default (Markdown):
> steno:export
Exported to workflow.md
# Steno Workflow Export
**Project:** /Users/me/microbiome-analysis
**Exported:** 2025-12-27T15:30:00Z
**Nodes:** 15
## Workflow
### 1. n_005: dx:@counts.csv
- **Status:** complete
- **Inputs:** counts.csv
- **Summary:** 18 samples × 15 ASVs, IBD study
### 2. n_006: ch:^ +normalize .method:clr
- **Status:** complete
- **Inputs:** counts.csv
- **Outputs:** counts_clr.csv
- **Summary:** CLR transformed, centered log-ratio
...
## Branches
- **main** (12 nodes)
- **deseq2** (1 node, from n_017)
- **ancombc** (1 node, from n_017)
## Bookmarks
- `@baseline-diff` → n_017
Export formats:
| Command | Format | Use Case |
|---|---|---|
steno:export |
Markdown | Documentation, sharing |
steno:export .json |
JSON | Import to another project |
steno:export .sh |
Shell script | Replay commands |
steno:export .html |
HTML | Interactive web view |
steno:export +transcript |
Markdown | With embedded conversations |
steno:export .html +transcript |
HTML | Interactive with search |
JSON export:
> steno:export .json
Exported to workflow.json
{
"version": "1.1",
"exported": "2025-12-27T15:30:00Z",
"project": "/Users/me/microbiome-analysis",
"sessions": [...],
"branches": [...],
"bookmarks": {...}
}
Script export:
> steno:export .sh
Exported to workflow.sh
#!/bin/bash
# Steno workflow export
# Project: /Users/me/microbiome-analysis
# Exported: 2025-12-27T15:30:00Z
# Replay these commands in Claude Code:
# dx:@counts.csv
# ch:^ +normalize .method:clr
# stat:alpha @counts.csv .metrics:shannon,chao1
# ...
Export options:
| Option | Effect |
|---|---|
steno:export workflow.md |
Custom filename |
steno:export @main |
Export only main branch |
steno:export @deseq2 |
Export specific branch |
steno:export +full |
Include file contents |
Branch export:
> steno:export @deseq2
Exported branch "deseq2" to deseq2-workflow.md
Includes:
- Parent chain (main: n_001 → n_017)
- Branch nodes (n_018)
- Branch-specific bookmarks
Full export (with file contents):
> steno:export +full
Exported to workflow-full.md
Includes embedded file contents for:
- counts.csv (18 rows)
- counts_clr.csv (18 rows)
- metadata.csv (19 rows)
Rich Export with Transcripts (Phase 3)
Embed conversation context directly in exports using +transcript.
Markdown export with embedded transcripts:
> steno:export +transcript
Exported to workflow-with-context.md
# Steno Workflow Export
**Project:** /Users/me/api-project
**Exported:** 2025-12-28T16:00:00Z
**Nodes:** 8 (5 with transcripts)
## Workflow
### 1. n_005: ch:@api.ts +auth
**Command:** `ch:@api.ts +auth`
**Result:** Added JWT authentication
<details>
<summary>📝 View conversation (12 messages)</summary>
**User:** ch:@api.ts +auth
**Claude:** I'll add JWT authentication to api.ts. Let me first read the current implementation...
[Reading api.ts...]
I see this is an Express API. I'll add:
1. JWT middleware for token validation
2. Login endpoint to issue tokens
3. Protected route wrapper
[Full conversation excerpt]
</details>
---
### 2. n_006: ts:@api.test.ts
...
HTML export with interactive transcripts:
> steno:export .html +transcript
Exported to workflow.html
Features:
- shadcn-inspired theme (light/dark mode)
- Interactive transcript toggles
- Full-text search across commands and conversations
- Branch visualization with clickable nodes
- Syntax highlighting for code blocks
- Smooth animations
The generated HTML uses the shadcn theme from assets/steno-transcript.css and includes:
<div class="steno-workflow">
<div class="search-bar">
<input type="text" placeholder="Search commands and conversations...">
</div>
<div class="node" id="n_005">
<div class="command">ch:@api.ts +auth</div>
<div class="summary">Added JWT authentication</div>
<button class="transcript-toggle" onclick="toggleTranscript('n_005')">
📝 Show conversation
</button>
<div class="transcript" id="transcript-n_005" hidden>
<div class="message user">ch:@api.ts +auth</div>
<div class="message assistant">
I'll add JWT authentication to api.ts...
</div>
<!-- Full conversation -->
</div>
</div>
</div>
Search functionality:
The HTML export includes JavaScript for searching:
- Filter nodes by command text
- Search within transcript content
- Highlight matching terms
- Jump to matched nodes
Transcript export options:
| Command | Format | Features |
|---|---|---|
steno:export +transcript |
Markdown | <details> blocks for conversations |
steno:export .html +transcript |
HTML | Interactive toggles, search, syntax highlighting |
steno:export +transcript +full |
Markdown | Transcripts + file contents |
steno:export .html +transcript @branch |
HTML | Branch-specific with transcripts |
Theming
Steno uses a shadcn-inspired theme (assets/steno-transcript.css) for HTML exports. The theme provides:
- Light/dark mode: Automatic detection + manual toggle
- OKLch colors: Modern color space for consistency
- Message styling: Distinct colors for user, assistant, thinking, and tool messages
- Code highlighting: Clean monospace styling for code blocks
- Smooth animations: Expand/collapse and hover effects
- Print-friendly: Clean output when printing
Apply theme to claude-code-transcripts output:
The standalone CSS can also style transcripts generated by claude-code-transcripts. After generating:
> steno:transcript +generate
Generating transcripts...
Done: .steno/transcripts/
> steno:transcript +theme
Applying shadcn theme to transcripts...
Injected: steno-transcript.css
Updated: 20 HTML files
Theme features:
- Dark mode toggle added
- Message styling updated
- Code blocks highlighted
Using the injection script:
A helper script automates theme injection for all HTML files:
# Apply theme to default transcripts directory
./scripts/inject-theme.sh
# Apply to custom directory
./scripts/inject-theme.sh ~/my-transcripts/
# Preview changes without modifying files
./scripts/inject-theme.sh --dry-run
# Embed CSS inline (larger files, but self-contained)
./scripts/inject-theme.sh --embed
# Show detailed progress
./scripts/inject-theme.sh --verbose
The script:
- Copies
steno-transcript.cssto the transcripts directory - Injects
<link>tags into each HTML file's<head> - Adds a dark mode toggle button
- Creates
.bakbackups of modified files - Skips files that are already themed
Manual theme injection:
To manually apply the theme to any HTML transcript:
<head>
<!-- Add before </head> -->
<link rel="stylesheet" href="path/to/steno-transcript.css">
</head>
<body class="dark"> <!-- or omit for light mode -->
Or copy the CSS directly into a <style> tag.
Theme customization:
Override CSS variables in your own stylesheet:
:root {
--primary: oklch(0.6 0.2 260); /* Custom blue */
--radius: 0.375rem; /* Sharper corners */
}
When transcripts not configured:
> steno:export +transcript
⚠ Transcripts not configured.
Set up first: steno:transcript +link <path>
Or generate: steno:transcript +generate
Exporting without transcripts...
Exported to workflow.md (no conversation context)
Partial transcript availability:
> steno:export +transcript
Exported to workflow-with-context.md
Note: 3 of 8 nodes missing transcript links.
n_001, n_002, n_003 - created before transcript linking
Run steno:transcript +link <path> to rescan archive.
steno:import
Import a workflow from JSON export.
> steno:import workflow.json
Importing workflow from workflow.json...
Nodes: 15
Branches: 3
Bookmarks: 2
⚠ This will merge into your current session.
Existing bookmarks with same names will be overwritten.
Type "steno:import! workflow.json" to confirm.
> steno:import! workflow.json
Imported workflow.json
Added: 15 nodes, 3 branches, 2 bookmarks
Merged into current session.
Import options:
| Option | Effect |
|---|---|
steno:import workflow.json |
Preview import |
steno:import! workflow.json |
Confirm and import |
steno:import workflow.json +replay |
Re-execute commands |
Replay mode:
> steno:import! workflow.json +replay
Replaying workflow...
n_001: dx:@counts.csv
→ 18 samples × 15 ASVs
n_002: ch:^ +normalize .method:clr
→ Created counts_clr.csv
...
Replay complete: 15 commands executed.
steno:status
Show current session status.
> steno:status
Session: sess_20241227 (started 2h ago)
Nodes: 4
Last command: viz:pca @normalized.csv
Bookmarks: baseline-pca, initial-dx
Stale: 0
steno:new-session
Archive current session and start fresh.
> steno:new-session
Archived session sess_20241227 (4 nodes) to graph.json
Starting new session: sess_20241228_143022
This command:
- Reads
current-session.json - Appends the session to
graph.jsonsessions array - Creates fresh
current-session.jsonwith new session ID - Preserves all bookmarks
Use this when starting a new logical unit of work.
steno:graph
Show the workflow graph as an ASCII tree.
> steno:graph
main
├── n_001 dx:@samples.csv
├── n_002 viz:heatmap ^
│ └── experiment (merged)
│ └── n_004 dx:@samples.csv
└── n_003 ch:^ +cluster
Show transcript availability:
> steno:graph +transcript
main
├── n_001 dx:@samples.csv [📝]
├── n_002 viz:heatmap ^ [📝]
│ └── experiment (merged)
│ └── n_004 dx:@samples.csv [📝]
└── n_003 ch:^ +cluster
[📝] = transcript available
3 of 4 nodes linked to transcripts.
Tip: Click node ID to view transcript (steno:transcript n_XXX)
When no transcripts configured:
> steno:graph +transcript
main
├── n_001 dx:@samples.csv
├── n_002 viz:heatmap ^
└── n_003 ch:^ +cluster
No transcripts linked.
Set up: steno:transcript +link <path>
Behavior:
- Read
.steno/graph.jsonand.steno/current-session.json - Build tree structure from branches and nodes
- Render as ASCII tree with box-drawing characters
- With
+transcript: Check.steno/transcript-links.jsonand show [📝] indicator
Rendering rules:
- Use box-drawing: ├── for middle items, └── for last item, │ for vertical continuation
- Show branch name with status in parentheses: (current), (merged), (abandoned)
- Show node ID and raw command
- Indent child branches under their parent node
- Mark current branch with asterisk if not main
- With
+transcript: Append [📝] to nodes with linked transcripts
Tree structure:
- Main branch nodes are the trunk
- Child branches fork from their parentNode
- Nodes on child branches are indented under the branch name
steno:alias
Create, list, or remove command aliases for frequently used patterns.
Create an alias:
> steno:alias setup "dx:@package.json && ch:@tsconfig.json +strict"
Alias "setup" created.
Use: setup
List aliases:
> steno:alias
Aliases:
setup → dx:@package.json && ch:@tsconfig.json +strict
qa → ts:@tests/ && doc:@api/
deploy → ts:@tests/ && mk:build +prod
Remove an alias:
> steno:alias -setup
Alias "setup" removed.
Use an alias:
> setup
Running alias "setup"...
→ dx:@package.json
→ ch:@tsconfig.json +strict
Behavior:
- Aliases stored in
.steno/aliases.json:{ "setup": "dx:@package.json && ch:@tsconfig.json +strict", "qa": "ts:@tests/ && doc:@api/" } - When a non-verb input matches an alias, expand and execute
- Aliases can chain multiple commands with
&& - Aliases can include modifiers and references
- Use
-nameto remove an alias
Alias rules:
- Names must be alphanumeric (no special characters)
- Names cannot conflict with verbs (dx, mk, ch, etc.)
- Aliases expand before any other parsing
- Nested aliases not supported (keeps it simple)
steno:search
Search command history by pattern or filter.
Search by pattern:
> steno:search validation
Found 3 matches:
n_012: ch:@auth.ts +validation (Dec 27)
→ Added input validation
n_008: ch:@api.py +validation (Dec 26)
→ Added request validation
n_003: ?plan validation-strategy (Dec 25)
→ Outlined validation approach
Search by verb:
> steno:search :viz
Found 5 viz commands:
n_015: viz:heatmap @counts.csv
n_011: viz:pca @normalized.csv
n_007: viz:boxplot @results.csv
...
Search by modifier:
> steno:search +auth
Found 4 commands with +auth:
n_018: mk:middleware +auth +jwt
n_014: ch:@routes.ts +auth
n_009: mk:api +auth +cache
n_002: ?plan +auth
Search by file reference:
> steno:search @config
Found 2 commands referencing config files:
n_010: ch:@config.json +logging
n_004: dx:@config.yaml
Search options:
| Option | Effect |
|---|---|
steno:search pattern |
Search raw commands |
steno:search :verb |
Filter by verb |
steno:search +modifier |
Filter by modifier |
steno:search @file |
Filter by file reference |
steno:search @branch:pattern |
Search specific branch |
steno:search -n 10 |
Limit results |
steno:search --failed |
Show only failed commands |
Behavior:
- Search across all sessions in graph.json and current-session.json
- Pattern matches against raw command and summary
- Results sorted by recency (newest first)
- Show node ID, command, date, and summary
- Limit to 20 results by default
steno:replay
Re-run a sequence of commands from history.
Replay single command:
> steno:replay n_005
Replaying n_005: ch:@data.csv +normalize
→ Updated normalized.csv
Replay range:
> steno:replay n_003..n_007
Replaying 5 commands...
n_003: dx:@data.csv
→ 18 samples analyzed
n_004: ch:^ +filter
→ Filtered to 15 samples
n_005: ch:^ +normalize
→ Created normalized.csv
n_006: viz:pca ^
→ Created pca_plot.png
n_007: steno:bookmark baseline
→ Bookmarked as "baseline"
Replay complete: 5 commands executed.
Replay from bookmark:
> steno:replay @baseline..
Replaying from bookmark "baseline" to current...
4 commands replayed.
Replay options:
| Option | Effect |
|---|---|
steno:replay n_XXX |
Replay single node |
steno:replay n_XXX..n_YYY |
Replay range (inclusive) |
steno:replay @bookmark.. |
From bookmark to end |
steno:replay ..n_XXX |
From start to node |
steno:replay @branch:n_XXX.. |
Replay branch from node |
steno:replay +dry |
Show commands without executing |
steno:replay +skip-failed |
Skip previously failed nodes |
Dry run:
> steno:replay n_003..n_007 +dry
Would replay 5 commands:
n_003: dx:@data.csv
n_004: ch:^ +filter
n_005: ch:^ +normalize
n_006: viz:pca ^
n_007: steno:bookmark baseline
Use steno:replay n_003..n_007 to execute.
Behavior:
- Look up nodes in graph.json/current-session.json
- Execute each command in order
^references resolve to the replayed command's output (not original)- New nodes created for replay (preserves original history)
- Failed commands stop replay unless +skip-failed
Replay vs Import:
steno:replay: Re-execute from local historysteno:import +replay: Execute from exported workflow file
steno:template
Create, list, use, and share reusable command sequences.
List available templates:
> steno:template
Built-in templates:
react-component - Create React component with tests
api-endpoint - Create REST endpoint with validation
refactor - Safe refactoring workflow
Custom templates (.steno/templates/):
my-setup - Project initialization
deploy-check - Pre-deployment checklist
Use a template:
> steno:template react-component Button
Running template "react-component" with args: Button
Step 1/4: mk:components/Button.tsx +functional
→ Created Button.tsx
Step 2/4: mk:components/Button.test.tsx +jest
→ Created Button.test.tsx
Step 3/4: mk:components/Button.css +module
→ Created Button.css
Step 4/4: ch:@components/index.ts +export Button
→ Updated index.ts
Template complete: 4 commands executed.
Create a custom template:
> steno:template +create setup
Creating template "setup"...
Enter commands (empty line to finish):
> dx:@package.json
> ch:@tsconfig.json +strict
> ts:@tests/ +coverage
>
Template "setup" created with 3 commands.
Saved to: .steno/templates/setup.json
Template with parameters:
> steno:template +create component
Creating template "component"...
Parameters: {name} {type?:functional}
Enter commands:
> mk:components/{name}.tsx +{type}
> mk:components/{name}.test.tsx +jest
> ch:@components/index.ts +export {name}
>
Template "component" created.
Usage: steno:template component MyButton type:class
View template contents:
> steno:template ?react-component
Template: react-component
Description: Create React component with tests
Parameters: {name} (required), {style?:css} (optional, default: css)
Commands:
1. mk:components/{name}.tsx +functional
2. mk:components/{name}.test.tsx +jest
3. mk:components/{name}.{style} +module
4. ch:@components/index.ts +export {name}
Remove a template:
> steno:template -setup
Template "setup" removed.
Template options:
| Command | Action |
|---|---|
steno:template |
List all templates |
steno:template name args... |
Run template |
steno:template ?name |
View template details |
steno:template +create name |
Create new template |
steno:template -name |
Remove template |
steno:template +export name |
Export as shareable JSON |
steno:template +import file |
Import template |
Behavior:
- Built-in templates defined in SKILL.md
- Custom templates stored in
.steno/templates/*.json - Parameters use
{name}syntax, optional with{name?:default} - Templates execute commands sequentially, stopping on failure
- Each command creates a node (full tracking)
Template file format:
{
"name": "component",
"description": "Create React component with tests",
"parameters": [
{"name": "name", "required": true},
{"name": "style", "required": false, "default": "css"}
],
"commands": [
"mk:components/{name}.tsx +functional",
"mk:components/{name}.test.tsx +jest",
"mk:components/{name}.{style} +module",
"ch:@components/index.ts +export {name}"
]
}
Built-in templates:
| Template | Description | Parameters |
|---|---|---|
react-component |
React component + test + styles | {name}, {style?:css} |
api-endpoint |
REST endpoint with validation | {name}, {method?:GET} |
refactor |
Safe refactoring workflow | {target} |
test-suite |
Test file with setup | {name}, {framework?:jest} |
Parameterized Aliases
Aliases can include parameters using {1}, {2}, etc. for positional args or {name} for named args.
Create parameterized alias:
> steno:alias explore "dx:@{1} ~deep && fnd:{2}"
Alias "explore" created with 2 parameters.
Usage: explore <file> <pattern>
Use parameterized alias:
> explore src/api.ts TODO
Running alias "explore" with args: src/api.ts, TODO
→ dx:@src/api.ts ~deep
→ fnd:TODO
Named parameters:
> steno:alias test "ts:@{file} +{coverage:basic}"
Alias "test" created.
Usage: test file:<path> [coverage:<level>]
> test file:utils.ts coverage:full
Running alias "test"...
→ ts:@utils.ts +full
Default values:
> steno:alias lint "ch:@{1:src/} +lint"
Alias "lint" created.
Default for {1}: src/
> lint
Running: ch:@src/ +lint
> lint lib/
Running: ch:@lib/ +lint
Parameter syntax:
| Syntax | Meaning | Example |
|---|---|---|
{1} |
First positional arg | dx:@{1} |
{2} |
Second positional arg | stat:compare @{1} @{2} |
{name} |
Named parameter | mk:{name}.ts |
{1:default} |
Positional with default | dx:@{1:src/} |
{name:default} |
Named with default | +{type:functional} |
{1?} |
Optional (omit if missing) | ch:@{1} {2?} |
Behavior:
- Positional args filled left-to-right
- Named args use
name:valuesyntax - Missing required args show error
- Optional args (
{?}) omit that part of command if not provided - Defaults used when arg not provided
Alias introspection:
> steno:alias ?explore
Alias: explore
Command: dx:@{1} ~deep && fnd:{2}
Parameters:
{1} - required (file path)
{2} - required (search pattern)
Usage: explore <arg1> <arg2>
Example: explore src/app.ts TODO
steno:diff
Compare outputs between nodes, branches, or bookmarks.
Diff two nodes:
> steno:diff n_003 n_007
Comparing n_003 → n_007
n_003: ch:@data.csv +filter
outputs: filtered.csv (45 rows)
n_007: ch:@data.csv +filter +normalize
outputs: filtered_norm.csv (45 rows)
File diff (filtered.csv → filtered_norm.csv):
- Same row count (45)
- Column changes:
+ normalized_value (new)
~ value → raw_value (renamed)
- Value ranges:
normalized_value: 0.0 - 1.0
Diff branches:
> steno:diff @main @experiment
Branch comparison: main → experiment
Diverged at: n_005 (ch:@data.csv +filter)
main (3 nodes after fork):
n_006: stat:ttest @filtered.csv
n_007: viz:boxplot ^
n_008: doc:@results/
experiment (2 nodes after fork):
n_009: stat:wilcoxon @filtered.csv
n_010: viz:violin ^
Output comparison:
Statistical test:
main: t-test (p=0.023)
experiment: Wilcoxon (p=0.031)
Visualizations:
main: boxplot.png (800x600)
experiment: violin.png (800x600)
Diff with bookmark:
> steno:diff @baseline ^
Comparing bookmark "baseline" (n_004) → current (n_012)
Changes since baseline:
8 commands executed
3 files created
2 files modified
Key differences:
n_004: data.csv (raw, 100 rows)
n_012: data_processed.csv (normalized, 95 rows)
- 5 rows filtered
- Added columns: norm_value, z_score
Diff options:
| Command | Action |
|---|---|
steno:diff n_X n_Y |
Compare two nodes |
steno:diff @branch1 @branch2 |
Compare branches |
steno:diff @bookmark ^ |
Compare bookmark to current |
steno:diff n_X..n_Y |
Show changes across range |
steno:diff +files |
Include file content diff |
steno:diff +stats |
Focus on statistical differences |
steno:diff .format:json |
Output as JSON |
Range diff:
> steno:diff n_003..n_010
Change summary: n_003 → n_010 (8 commands)
Files created: 4
- filtered.csv (n_003)
- normalized.csv (n_005)
- pca_plot.png (n_007)
- results.md (n_010)
Files modified: 2
- data.csv: 3 modifications
- config.json: 1 modification
Command types:
ch: 4 (50%)
viz: 2 (25%)
doc: 1 (12.5%)
stat: 1 (12.5%)
File content diff:
> steno:diff n_003 n_007 +files
Comparing outputs...
--- n_003: filtered.csv
+++ n_007: filtered_norm.csv
@@ Header @@
- id,value,category
+ id,raw_value,normalized_value,category
@@ Row 1 @@
- 1,42.5,A
+ 1,42.5,0.425,A
@@ Summary @@
45 rows compared
0 rows removed
1 column added
1 column renamed
Statistical diff:
> steno:diff @deseq2:^ @ancombc:^ +stats
Statistical comparison: DESeq2 vs ANCOM-BC
Method comparison:
DESeq2: Negative binomial GLM
ANCOM-BC: Bias-corrected compositional
Results overlap:
Significant in both: 12 features
DESeq2 only: 3 features
ANCOM-BC only: 5 features
Correlation: r=0.87 (p<0.001)
Top differences:
Feature_A: DESeq2 p=0.001, ANCOM-BC p=0.12
Feature_B: DESeq2 p=0.08, ANCOM-BC p=0.002
Behavior:
- Compare node metadata (command, timestamp, status)
- Compare outputs (files created/modified)
- If files are data files, show structural diff
- If files are code, show line diff
- For branches, find common ancestor and show divergence
+filesincludes actual content comparison+statsextracts and compares numerical results
steno:transcript
Generate themed HTML transcripts from Claude Code session data.
Script location: scripts/generate-transcript.py
Check transcript status:
> steno:transcript
Transcript status:
Output directory: .steno/transcripts/
Sessions available: 12
Last generated: 2025-12-28 14:30
Commands:
steno:transcript +generate Generate current session
steno:transcript +all Generate all sessions
steno:transcript n_XXX Open transcript at node
Generate Transcripts
Generate current session:
python scripts/generate-transcript.py
Generate all sessions:
python scripts/generate-transcript.py --all
Generate specific session:
python scripts/generate-transcript.py --session 907d46e0
Generate to custom path:
python scripts/generate-transcript.py --output ./docs/transcripts/
Generate and open in browser:
python scripts/generate-transcript.py --open
Transcript Features
The generated HTML includes:
- 4 color themes: Purple (default), Bubblegum, Midnight, Minimal
- Dark/light mode: Toggle with
dkey or button - Search: Press
/to search,Enter/Shift+Enterto navigate matches - Copy buttons: One-click copy on all code blocks
- Keyboard navigation:
j/kto navigate messages,?for shortcuts - Collapsible blocks: Toggle thinking (
t) and tool (o) blocks
View Transcript at Node
View transcript for specific node:
> steno:transcript n_005
Opening transcript for n_005...
Opening: .steno/transcripts/907d46e0.html#n_005
View transcript for bookmark:
> steno:transcript @baseline
Opening transcript for bookmark "baseline" (n_003)...
View current node transcript:
> steno:transcript ^
Opening transcript for last node (n_012)...
Command Reference
| Command | Action |
|---|---|
steno:transcript |
Show status |
steno:transcript +generate |
Generate current session transcript |
steno:transcript +all |
Generate all session transcripts with index |
steno:transcript +generate .out:<path> |
Generate to custom path |
steno:transcript n_XXX |
Open transcript at specific node |
steno:transcript @bookmark |
Open transcript at bookmarked node |
steno:transcript ^ |
Open transcript for last node |
steno:transcript +status |
Show generation status |
steno:transcript +clear |
Remove generated transcripts |
Branching
Branches allow exploring alternative approaches without losing work.
fork:name
Create a new branch from the current node.
> fork:caching
Created branch "caching" from n_002
Switched to branch "caching"
Behavior:
- Read
.steno/graph.json - Create a new branch object:
{ "name": "caching", "parentNode": "n_002", "parentBranch": "main", "created": "ISO timestamp", "nodes": [], "status": "active" } - Add to
branchesarray in graph.json - Set
currentBranchto the new branch name - Subsequent nodes will be tagged with this branch
switch:name
Switch to an existing branch.
> switch:main
Switched to branch "main"
Current node: n_002
Behavior:
- Verify branch exists in graph.json
- Set
currentBranchto target branch - The
^reference now resolves to the last node on that branch
steno:branches
List all branches with their status.
> steno:branches
* caching (current, 2 nodes)
└─ from n_002 on main
main (3 nodes)
memoization (1 node)
└─ from n_002 on main
Show:
- Current branch marked with *
- Node count per branch
- Parent relationship
- Status (active, merged, abandoned)
compare:branch-a branch-b
Compare two branches side by side.
> compare:main experiment
Branch "main" (3 nodes):
n_001: dx:@samples.csv
n_002: viz:heatmap ^ → heatmap.png
n_003: ch:^ +cluster → heatmap_clustered.png
Branch "experiment" (1 node, from n_003):
n_004: dx:@samples.csv
→ 19 samples analysis
Common ancestor: n_003 on main
Differences:
- main: Focused on visualization (heatmap, clustering)
- experiment: Re-analyzed raw data
Behavior:
- Read both branches from graph.json
- Find nodes on each branch
- Look up node details from sessions or current-session
- Show outputs and summaries for each
- Identify common ancestor (fork point)
- Summarize the different approaches
If branches have output files in common, note the differences:
Files in both branches:
- results.csv: main (45 lines) vs experiment (52 lines)
merge:branch
Merge a branch into the current branch (usually main).
> merge:experiment
Merging "experiment" into "main"...
n_004: dx:@samples.csv → 19 samples analysis
Branch "experiment" merged.
Outputs adopted: (none - diagnostic only)
Behavior:
- Read
.steno/graph.json - Verify branch exists and is not already merged/abandoned
- List the nodes and outputs from that branch
- Mark branch status as "merged" in graph.json
- Write updated graph.json
Note: This is a metadata operation - it marks the branch as adopted. Any files created on that branch already exist in the working directory.
abandon:branch
Abandon a branch without merging.
> abandon:experiment
Abandoned branch "experiment"
Work on that branch was not adopted.
Behavior:
- Read
.steno/graph.json - Verify branch exists and is not already merged/abandoned
- Mark branch status as "abandoned" in graph.json
- Write updated graph.json
Note: This does not delete files - it marks the branch as discarded in the graph. Use this when an experimental approach didn't work out.
Branch-Aware Node Creation
When creating nodes, tag them with the current branch:
{
"id": "n_003",
"branch": "caching",
"timestamp": "...",
"raw": "mk:api +caching",
...
}
Reference Resolution with Branches
The ^ reference resolves to the last node on the current branch:
# On branch "caching"
ch:^ +redis # ^ = last output on "caching" branch
Initial Branch State
When .steno/ is first created, initialize with:
branches: [{ name: "main", parentNode: null, parentBranch: null, nodes: [], status: "active" }]currentBranch: "main"
All nodes without an explicit branch are on "main".
File Structure
.steno/
├── graph.json # All sessions + bookmarks
├── current-session.json # Active session
└── .gitignore # Ignore current-session.json
Initializing .steno/
On first steno command, if .steno/ doesn't exist:
- Create
.steno/directory - Create
.steno/.gitignore:current-session.json - Create
.steno/graph.json:{ "version": "1.1", "project": "/path/to/project", "nextNodeId": 1, "sessions": [], "bookmarks": {}, "branches": [ { "name": "main", "parentNode": null, "parentBranch": null, "nodes": [], "status": "active" } ], "currentBranch": "main" } - Create
.steno/current-session.json:{ "id": "sess_YYYYMMDD_HHMMSS", "started": "ISO timestamp", "nodes": [] }
Merging Sessions
When starting a new session and current-session.json has nodes:
- Read
current-session.json - Append to
graph.jsonsessions array - Update
nextNodeIdingraph.json - Start fresh
current-session.json
Execution Rules
Direct Execution (no asking)
When you see a clear steno command, execute immediately:
dx:@file.csv -> Read the file, provide analysis
mk:component -> Create the component
ch:@file +feature -> Edit the file to add feature
viz:pca @data -> Generate PCA analysis
stat:ttest @a @b -> Run t-test on the data
Ask First
Only ask for clarification when:
- The command ends with
?(e.g.,stat:test?) - Multiple interpretations exist AND no context helps
- Required files don't exist
Extended Thinking
When you see ~deep:
- Think through the problem thoroughly
- Consider edge cases
- Explore alternatives before acting
Error Handling
When commands fail or are malformed, respond with clear, actionable messages. Never silently fail or guess user intent.
Error Format
> [command]
⚠ [error type]: [what went wrong]
[suggestion or fix]
Empty/Missing Targets
| Error | Message |
|---|---|
dx: (no target) |
⚠ Missing target: dx: requires a file or reference. Try: dx:@file.csv |
ch: (no target) |
⚠ Missing target: ch: requires something to change. Try: ch:@file.py +feature |
fork: (no name) |
⚠ Missing branch name: fork: requires a name. Try: fork:experiment |
switch: (no name) |
⚠ Missing branch name: switch: requires a name. Try: switch:main |
File Not Found
> dx:@missing.csv
⚠ File not found: missing.csv
Did you mean: data.csv, samples.csv?
Or create it: mk:@missing.csv
Behavior:
- Check if file exists at the path
- If not, look for similar filenames (fuzzy match)
- Suggest alternatives or offer to create
Invalid Reference: ^ (No Previous)
> ch:^ +normalize
⚠ No previous output: ^ refers to the last command's output, but no commands have run yet.
Start with: dx:@file.csv
Then use ^ to chain: ch:^ +normalize
Behavior:
- Check
.steno/current-session.jsonfor nodes - If empty, explain ^ and suggest starting fresh
- If nodes exist but last has no outputs, use its inputs
Bookmark Not Found
> stat:ttest @baseline @new-data
⚠ Bookmark not found: @baseline
Available bookmarks: baseline-pca, clean-data
Run steno:history to see past commands.
Behavior:
- Check if @name is a file first
- Check bookmarks in graph.json
- List available bookmarks to help user
Cross-Branch Reference Errors
Branch not found:
> viz:pca @experiment:^
⚠ Branch not found in reference: @experiment:^
Available branches: main, deseq2, ancombc
Use steno:branches to see all branches.
Node not found on branch:
> ch:@deseq2:n_999 +feature
⚠ Node not found: n_999 not on branch "deseq2"
Nodes on deseq2: n_018
Use steno:graph to see branch structure.
Empty branch (no nodes):
> stat:compare @new-branch:^
⚠ Branch empty: "new-branch" has no nodes yet.
Switch to it and run commands first: switch:new-branch
Invalid reference format:
> dx:@branch:
⚠ Invalid cross-branch reference: @branch:
Expected format: @branch:^ or @branch:n_001 or @branch:-1
Unknown Verb
> analyze:@data.csv
⚠ Unknown verb: analyze
Did you mean: dx (diagnose/analyze)?
Available verbs: dx mk ch rm fnd viz stat ts doc
Behavior:
- Check against known verbs
- Suggest closest match
- List all verbs for reference
Incomplete Modifiers
| Input | Message |
|---|---|
mk:api + |
⚠ Incomplete modifier: + requires a feature name. Try: +auth |
ch:@file - |
⚠ Incomplete modifier: - requires something to exclude. Try: -logging |
mk:api . |
⚠ Incomplete flag: . requires a flag name. Try: .ts or .dry |
Branching Errors
| Error | Message |
|---|---|
fork:main |
⚠ Cannot fork main: main is the base branch. Choose a different name. |
fork:existing (exists) |
⚠ Branch exists: "existing" already exists. Switch to it: switch:existing |
switch:nope (missing) |
⚠ Branch not found: "nope". Available: main, experiment, caching |
merge:main |
⚠ Cannot merge main: main is the current branch. Switch away first. |
merge:done (already merged) |
⚠ Already merged: "done" was merged at Dec 27, 2025. See steno:graph. |
abandon:main |
⚠ Cannot abandon main: main is the base branch and cannot be abandoned. |
abandon:merged (already merged) |
⚠ Cannot abandon: "merged" was already merged. Use steno:graph to view history. |
Undo/Redo Errors
Nothing to undo:
> steno:undo
⚠ Nothing to undo: No commands in current session.
Run some commands first, then undo.
Node not found:
> steno:undo n_999
⚠ Node not found: n_999 doesn't exist.
Use steno:history to see available nodes.
Nothing to redo:
> steno:redo
⚠ Nothing to redo: No undone commands.
Use steno:undo first, then steno:redo to restore.
Cannot undo across sessions:
> steno:undo n_001
⚠ Cannot undo: n_001 is from a previous session.
Only current session commands can be undone.
Use steno:history to see session boundaries.
Backup not found (hard undo):
> steno:undo!
⚠ No backup available for data.csv
Cannot revert file changes.
Proceeding with soft undo instead...
Files kept: normalized.csv
Export/Import Errors
Nothing to export:
> steno:export
⚠ Nothing to export: No commands in current session.
Run some commands first, then export.
Invalid export format:
> steno:export .pdf
⚠ Unsupported format: .pdf
Supported formats: .md (default), .json, .sh
Export file exists:
> steno:export workflow.md
⚠ File exists: workflow.md
Use steno:export workflow.md ! to overwrite.
Or choose a different filename.
Import file not found:
> steno:import missing.json
⚠ File not found: missing.json
Check the path and try again.
Invalid import format:
> steno:import workflow.md
⚠ Cannot import: workflow.md is not a valid JSON export.
Only .json exports can be imported.
Use steno:export .json to create an importable file.
Import version mismatch:
> steno:import! old-workflow.json
⚠ Version mismatch: File is version 0.9, current is 1.1
Some features may not import correctly.
Proceeding with best-effort import...
Imported: 10 nodes, 1 branch, 0 bookmarks
Skipped: 2 nodes (unsupported format)
Branch conflict on import:
> steno:import! workflow.json
⚠ Branch conflict: "experiment" already exists.
Options:
1. steno:import! workflow.json +rename - Auto-rename conflicting branches
2. Manually resolve by renaming/deleting existing branch
Aborting import.
Alias Errors
Invalid alias name:
> steno:alias 123 "dx:@file"
⚠ Invalid alias name: "123"
Alias names must start with a letter and contain only alphanumeric characters.
Reserved name:
> steno:alias dx "mk:api"
⚠ Reserved name: "dx" is a steno verb.
Choose a different name that doesn't conflict with: dx mk ch rm fnd viz stat ts doc
Alias not found (remove):
> steno:alias -missing
⚠ Alias not found: "missing"
Use steno:alias to list available aliases.
Invalid alias syntax:
> steno:alias setup
⚠ Missing command: steno:alias requires a command string.
Usage: steno:alias name "command"
Example: steno:alias setup "dx:@package.json && ts:@tests/"
Search Errors
No matches:
> steno:search quantum-computing
No matches found for "quantum-computing".
Try: steno:search with a different pattern
Or: steno:history to see all commands
Empty history:
> steno:search auth
⚠ No history: No commands to search.
Run some commands first.
Invalid search syntax:
> steno:search
⚠ Missing pattern: steno:search requires a search term.
Usage: steno:search pattern
Examples:
steno:search validation
steno:search :viz
steno:search +auth
Replay Errors
Node not found:
> steno:replay n_999
⚠ Node not found: n_999
Use steno:history to see available nodes.
Invalid range:
> steno:replay n_010..n_005
⚠ Invalid range: End node (n_005) is before start node (n_010).
Use: steno:replay n_005..n_010
Bookmark not found:
> steno:replay @missing..
⚠ Bookmark not found: "missing"
Available bookmarks: baseline, clean-data
Use steno:history to see bookmark names.
Empty range:
> steno:replay n_005..n_005
Replaying single node: n_005
ch:@data.csv +normalize
→ Updated normalized.csv
Replay failed:
> steno:replay n_003..n_007
Replaying 5 commands...
n_003: dx:@data.csv → OK
n_004: ch:^ +filter → OK
n_005: ch:@missing.csv +normalize
⚠ Replay stopped: File not found: missing.csv
Completed: 2 of 5 commands
Options:
- Fix the issue and run: steno:replay n_005..n_007
- Skip failures: steno:replay n_003..n_007 +skip-failed
Template Errors
Template not found:
> steno:template unknown-template
⚠ Template not found: "unknown-template"
Built-in: react-component, api-endpoint, refactor, test-suite
Custom: (none)
Use steno:template to list available templates.
Missing required parameter:
> steno:template react-component
⚠ Missing required parameter: {name}
Usage: steno:template react-component <name> [style:<css|scss>]
Example: steno:template react-component Button style:scss
Invalid parameter:
> steno:template react-component Button style:invalid
⚠ Invalid parameter value: style must be one of: css, scss, styled
Using default: css
Template already exists:
> steno:template +create setup
⚠ Template exists: "setup" already exists.
Use steno:template -setup to remove it first.
Or choose a different name.
Cannot remove built-in:
> steno:template -react-component
⚠ Cannot remove built-in template: "react-component"
Only custom templates can be removed.
Template execution failed:
> steno:template api-endpoint users
Running template "api-endpoint"...
Step 1/3: mk:routes/users.ts +rest → OK
Step 2/3: mk:controllers/users.ts +validation
⚠ Template stopped: Directory controllers/ not found
Completed: 1 of 3 commands
Options:
- Create directory: mkdir controllers && steno:template api-endpoint users
- Continue from step 2: steno:replay n_XXX..
Diff Errors
Node not found:
> steno:diff n_003 n_999
⚠ Node not found: n_999
Use steno:history to see available nodes.
Cannot diff same node:
> steno:diff n_003 n_003
⚠ Same node: Cannot diff n_003 with itself.
Use steno:diff n_003 n_XXX to compare different nodes.
Branch not found:
> steno:diff @main @missing-branch
⚠ Branch not found: "missing-branch"
Available branches: main, experiment, feature-x
Use steno:branches to see all branches.
No outputs to compare:
> steno:diff n_001 n_002
⚠ No outputs to compare:
n_001: dx:@data.csv (diagnostic only, no outputs)
n_002: ?plan refactor (planning only, no outputs)
Diff requires nodes with file outputs.
Try: steno:diff n_003 n_007 (both have outputs)
Invalid range:
> steno:diff n_010..n_003
⚠ Invalid range: End node (n_003) is before start node (n_010).
Use: steno:diff n_003..n_010
Parameterized Alias Errors
Missing required argument:
> explore
⚠ Missing argument: Alias "explore" requires 2 arguments.
Usage: explore <file> <pattern>
Example: explore src/app.ts TODO
Too many arguments:
> explore src/app.ts TODO extra-arg
⚠ Too many arguments: Alias "explore" takes 2 arguments, got 3.
Usage: explore <file> <pattern>
Invalid named parameter:
> test file:utils.ts unknown:value
⚠ Unknown parameter: "unknown"
Valid parameters for "test": file, coverage
Usage: test file:<path> [coverage:<level>]
Transcript Errors
Not configured:
> steno:transcript n_005
⚠ Transcripts not configured.
Link an archive first: steno:transcript +link <path>
Options:
1. Generate archive: pip install claude-code-transcripts
Then: claude-code-transcripts all -o archive/
2. Link existing: steno:transcript +link ./archive/
Archive not found:
> steno:transcript +link ./missing-archive/
⚠ Archive not found: ./missing-archive/
Directory does not exist.
Generate with: claude-code-transcripts all -o ./missing-archive/
No HTML files in archive:
> steno:transcript +link ./empty-folder/
⚠ No transcripts found in ./empty-folder/
Expected HTML files from claude-code-transcripts.
Generate with: claude-code-transcripts all -o ./empty-folder/
Node not found:
> steno:transcript n_999
⚠ Node not found: n_999
Use steno:history to see available nodes.
Node not linked:
> steno:transcript n_005
⚠ No transcript link for n_005.
Node exists but wasn't matched to a transcript session.
Try: steno:transcript +link <path> to rescan archive.
Invalid URL:
> steno:transcript +link not-a-url
⚠ Invalid path or URL: not-a-url
Use a local path: steno:transcript +link ./archive/
Or a URL: steno:transcript +link https://example.com/archive/
Cannot open browser:
> steno:transcript n_005
Transcript URL: ./archive/sess_20251228.html#n_005
⚠ Could not open browser automatically.
Open the URL above manually, or set BROWSER environment variable.
Tool not installed (generation):
> steno:transcript +generate
⚠ claude-code-transcripts not installed.
Install with:
pip install claude-code-transcripts
# or
pipx install claude-code-transcripts
Alternative: Link to existing archive:
steno:transcript +link <path>
Generation failed:
> steno:transcript +generate
⚠ Transcript generation failed.
Error: No Claude conversation files found in ~/.claude/projects/
Check that:
1. Claude Code has been used in this project
2. Conversation data exists in ~/.claude/
No sessions to generate:
> steno:transcript +all
⚠ No sessions found.
Start using steno commands to create sessions.
Or link an existing archive:
steno:transcript +link <path>
Output path not writable:
> steno:transcript +generate .out:/root/transcripts/
⚠ Cannot write to output path: /root/transcripts/
Permission denied.
Try a writable path:
steno:transcript +generate .out:./transcripts/
Export Transcript Errors
Transcripts not configured for export:
> steno:export +transcript
⚠ Transcripts not configured.
Set up first: steno:transcript +link <path>
Or generate: steno:transcript +generate
Exporting without transcripts...
Exported to workflow.md (no conversation context)
Partial transcript coverage:
> steno:export +transcript
Exported to workflow-with-context.md
Note: 3 of 8 nodes missing transcript links.
n_001, n_002, n_003 - created before transcript linking
Run steno:transcript +link <path> to rescan archive.
HTML export file already exists:
> steno:export .html +transcript
⚠ File exists: workflow.html
Use a different name: steno:export my-export.html +transcript
Or overwrite with: steno:export! .html +transcript
Transcript content unavailable:
> steno:export +transcript
⚠ Transcript links found but content unavailable.
Archive path may have moved: ./archive/
Update link: steno:transcript +link <new-path>
Or regenerate: steno:transcript +generate
State Recovery
If .steno/graph.json is corrupted or missing:
> dx:@file.csv
⚠ Session state corrupted: .steno/graph.json could not be parsed.
Options:
1. steno:reset - Reset to clean state (preserves no history)
2. Manually fix .steno/graph.json
Proceeding without session tracking...
Behavior:
- Try to parse graph.json
- If invalid JSON, warn user but don't block the command
- Execute command without logging
- Offer steno:reset to start fresh
Logging Failed Commands
Failed commands should still be logged with status "failed":
{
"id": "n_005",
"timestamp": "2025-12-27T15:30:00Z",
"raw": "dx:@missing.csv",
"status": "failed",
"error": "File not found: missing.csv",
"inputs": [],
"outputs": [],
"summary": "Failed: file not found"
}
This preserves history for debugging and allows steno:history to show what was attempted.
steno:reset
Reset session state to clean:
> steno:reset
⚠ This will clear all session history and bookmarks.
Branches will be reset to main only.
Type "steno:reset!" to confirm.
> steno:reset!
Session reset.
- Cleared: 15 nodes, 3 branches, 2 bookmarks
- Fresh: .steno/graph.json initialized
- Ready: main branch active
Examples
Data Analysis
dx:@samples.csv -> Read and summarize the dataset
ch:@data.csv +normalize -> Add normalization to data processing
viz:heatmap @expression.csv -> Generate heatmap visualization
stat:ttest @treated @control -> Compare two groups
Code Development
mk:api +auth +rate-limit .ts -> Create TypeScript API with auth and rate limiting
ch:@login.py +validation -> Add validation to login
rm:@deprecated -keep-tests -> Remove deprecated code but keep tests
fnd:auth-handlers -> Find authentication-related code
ts:@utils.ts -> Run tests for utils
Planning & Exploration
?plan microservices +docker -> Plan microservices architecture
?sketch auth-flow -> Create rough auth implementation
?challenge current-approach -> Critique what we have
dx:@codebase ~deep -> Deep analysis of the codebase
Chaining with Context
dx:@data.csv -> Analyze data
ch:^ +filter-nulls -> Modify based on previous (^)
viz:pca ^ -> Visualize the result
steno:bookmark clean-data -> Save for later reference
Cross-Session Reference
# New session, referencing previous work
stat:ttest @clean-data @new-samples
# @clean-data resolves from bookmark
Response Format
When executing steno commands:
- Acknowledge the command briefly (one line)
- Execute immediately using appropriate tools
- Report results concisely
- Log the node to session (silently)
Example:
> dx:@samples.csv
Analyzing samples.csv...
18 samples, 5 genes, 2 conditions (treatment/control), 3 timepoints.
Treatment group shows upregulation at 24h and 48h.
Remember
- Steno is about speed - don't ask unnecessary questions
- Execute directly when the intent is clear
- Use appropriate tools (Read, Edit, Bash, etc.) without hesitation
- Be concise in responses - steno users value efficiency
- Track commands in
.steno/for cross-session memory - Resolve references (^ and @name) from session history