Version: 1.0 Target Audience: AI Agents (Claude Code, ChatGPT, and similar LLM-based coding assistants) Last Updated: 2025-01-15
Lash is a minimalist, Markdown-native task tracker designed for both humans and AI agents. It treats Markdown files as the single source of truth, with SQLite providing an acceleration layer for fast search and queries.
Key characteristics:
- Markdown-first: All task data lives in
.mdfiles with a strict, predictable format - Linter-enforced: Format is validated and enforced for consistency
- Dependency-aware: Tasks can depend on other tasks within or across files
- Token-efficient: Designed to minimize token usage in agent contexts
- Fast: Optimized for quick parsing, indexing, and querying
- Predictable format: Strict linting ensures consistent structure you can rely on
- Safe modifications: Clear rules about what can and cannot be changed
- Validation feedback: Immediate feedback via
lash lintprevents errors - Token optimization: Built-in tools for generating minimal context (
lash agent-prompt) - Structured errors: Machine-readable error codes with clear explanations
- Formal schema: Complete specification of allowed syntax and semantics
- Contextual notes: Plain bullets for requirements without completion tracking
- Documentation references:
@docannotations link to relevant documentation - Sparse context generation: Tools to extract only relevant information
- Workflow commands: Purpose-built CLI for agent operations
To understand the Lash file format, run:
lash agent-prompt --format plainThis generates:
- Complete file format specification
- Allowed operations
- Safety guidelines
- Current project task summaries (if database exists)
Output formats:
--format plain: Human-readable Markdown (default)--format json: Structured JSON with schema and tasks--format agents-md: Ready-to-paste fragment for AGENTS.md
To install a static Lash skill into a coding-agent's skills directory (Claude Code's
.claude/skills/, Cursor's.cursor/rules/, or anAGENTS.lash.mdsibling for Codex / generic AGENTS.md hosts), uselash skill install --target <claude|codex|cursor|agents-md>instead β see the user guide for details.
Lash task files have four main sections:
- Header: Title (H1) and metadata annotations
- Description: Optional
## Descriptionsection with context - Tasks:
## Taskssection with hierarchical checkboxes - References: Optional notes or documentation links
File structure:
# Topic Title
@id: unique-identifier
@labels: tag1, tag2
@owner: assignee-name
@created: YYYY-MM-DD
## Description
Free-form Markdown text explaining scope, constraints, and context.
Can include inline @agent-note: hints for agents.
## Tasks
- [ ] Top-level task
- Implementation note without checkbox (contextual note)
- [ ] Child task (indented with 2 spaces)
- [ ] Grandchild task
- [x] Completed task
- [-] Waived task (not applicable)Safe operations (always permitted):
- Add new tasks using checkbox syntax:
- [ ] Task description - Update task status:
[ ]β[x](done),[-](waived),[!](blocked) - Add contextual notes: plain bullets under tasks (no checkbox)
- Add annotations:
@labels,@owner,@estimate,@agent-note - Add dependencies:
@depends-on: path/to/file.md#task:id - Add documentation references:
@doc: path/to/doc.md#section
Restricted operations (use with caution):
- Removing tasks (check for reverse dependencies first)
- Renaming task IDs (breaks references)
- Restructuring hierarchy (may affect dependencies)
Forbidden operations:
- Creating tasks beyond maximum depth (3-4 levels)
- Using invalid status symbols (only
[ ],[x],[-],[!]) - Creating duplicate IDs within a file
- Breaking existing dependency references
- Modifying files outside the project root
TASK_FILE := HEADER DESCRIPTION? TASKS REFERENCES?
HEADER := H1_TITLE NL ANNOTATIONS*
DESCRIPTION := "## Description" NL FREE_TEXT NL
TASKS := "## Tasks" NL TASK_TREE
TASK_TREE := TASK_ITEM+
TASK_ITEM := CHECKBOX_ITEM | NOTE_ITEM
Task item (checkbox):
CHECKBOX_ITEM := INDENT* "- [STATUS] " TITLE (INLINE_LABELS?) NL
(NOTE_ITEM | CHILD_TASK)*
STATUS := " " | "x" | "-" | "!"
INDENT := " " (2 spaces per level, max 3-4 levels)
Note item (plain bullet):
NOTE_ITEM := INDENT "- " TEXT NL
where INDENT is exactly 2 spaces deeper than parent task
Key distinction:
- [ ]or- [x]or- [-]or- [!]= Task (checkbox, tracked for completion)-(plain bullet) = Note (contextual information, not tracked)
All annotations are optional unless marked required.
| Annotation | Type | Description | Example |
|---|---|---|---|
@id |
string | Unique identifier within file | @id: feature-auth |
@labels |
comma-separated | Tags for filtering | @labels: backend, api |
@owner |
string | Person/agent responsible | @owner: alice |
@created |
date | Creation date (YYYY-MM-DD) | @created: 2025-01-15 |
@estimate |
duration | Time estimate | @estimate: 2d |
@depends-on |
reference | Cross-file dependency | @depends-on: core/auth.md#task:login |
@agent-note |
text | Hints for AI agents | @agent-note: Use pattern X |
@doc |
path | Documentation reference | @doc: ../docs/design.md#section-7 |
Annotation placement:
- File-level: After H1 title, before
## Description - Task-level: On line following task, indented to match task
Within-file dependencies: Automatic based on hierarchy. Parent tasks depend on all children.
Cross-file dependencies:
@depends-on: path/to/file.md#task:task-id
@depends-on: ../sibling/file.md#task:other-idPaths are relative to project root or relative file paths.
Documentation references (non-blocking):
@doc: ../docs/design-doc.md#section-name
@doc: ../../README.md| Symbol | Name | Meaning | Usage |
|---|---|---|---|
[ ] |
open | Not started or in progress | Default for new tasks |
[x] |
done | Completed successfully | Mark when work is finished |
[-] |
waived | Not applicable or cancelled | Use when task no longer needed |
[!] |
blocked | Blocked by dependencies | System may auto-set based on deps |
- Unique IDs:
@idmust be unique within each file (globally unique =file-path#task:id) - Max depth: Task hierarchies limited to 3-4 levels of nesting
- Status consistency: Parent tasks can only be
[x]when all children are[x]or[-] - Valid dependencies:
@depends-ontargets must exist and be resolvable - Contextual notes:
- Must be indented exactly 2 spaces deeper than parent task
- Cannot have children (no nesting under notes)
- Should appear before child tasks (convention, soft warning)
- Are indexed and searchable but not tracked for completion
- Description length: Recommended 500-1000 chars, warning at 1000, error at 2000
To add a top-level task:
## Tasks
- [ ] New task descriptionTo add a subtask:
- [ ] Parent task
- [ ] New child task (indent with 2 spaces)To add a task with metadata:
- [ ] Implement authentication
@id: auth-impl
@labels: backend, security
@estimate: 3d
@agent-note: Use bcrypt for password hashingValidation:
lash lint path/to/file.mdContextual notes provide requirements, constraints, or implementation hints without being tracked as tasks.
When to use notes vs. child tasks:
Use notes for:
- Requirements: "Must support multi-tenancy"
- Constraints: "Response time < 100ms for 95th percentile"
- Implementation hints: "Use Redis for session storage"
- API specifics: "Stripe API v3, not v2"
Use child tasks for:
- Multi-step processes needing completion tracking
- Independently trackable work items
- Sub-tasks that may have their own sub-tasks
Example:
- [ ] Implement payment processing
- Use Stripe API v3 for all transactions
- Must handle webhooks for async payment confirmation
- Support credit card, ACH, and digital wallets
- [ ] Set up Stripe webhook handlers
- [ ] Implement payment intent creation
- [ ] Add refund supportMark task as done:
- [x] Completed taskMark task as waived:
- [-] Task no longer neededMark task as blocked:
- [!] Task blocked by external factorImportant: Parent tasks automatically become [!] blocked if any child is [ ] open. Don't manually mark parents as [x] done when children are incomplete.
Add cross-file dependency:
@depends-on: features/auth.md#task:login-endpointAdd documentation reference:
@doc: ../docs/api-spec.md#authenticationValidation:
lash check-links # Verify all references are validFile-level labels:
@labels: backend, api, securityTask-level labels (inline):
- [ ] Implement OAuth flow #security #authLabels enable cross-cutting queries:
lash list --label securityDon't:
- Create tasks deeper than 3-4 levels
- Use arbitrary status symbols (only
[ ],[x],[-],[!]) - Duplicate IDs within a file
- Break dependency references by deleting target tasks
- Mark parent tasks as done when children are incomplete
- Create circular dependencies (A depends on B, B depends on A)
- Add checkboxes to contextual notes (notes are plain bullets only)
- Nest notes under other notes (notes cannot have children)
- Modify files without running
lash lintafterward
Goal: Understand the project structure and current task state.
# Generate agent-friendly prompt with format spec
lash agent-prompt --format plain > lash-context.txt
# List all task files
lash list --tree
# Search for relevant tasks
lash search "authentication"
# View specific task with dependencies
lash show features/auth.md#task:login --depsToken optimization:
# Filter by labels to reduce context
lash agent-prompt --labels backend,security
# Set token budget (approximate)
lash agent-prompt --max-tokens 2000
# Get only schema without examples
lash agent-prompt --format jsonGoal: Read and understand task files before making changes.
# Read file with standard tools
cat features/auth.md
# Or use show command for formatted view
lash show features/auth.mdWhat to check:
- Current
@idand@labels - Task hierarchy and depth
- Existing dependencies (
@depends-on) - Contextual notes for requirements
- Documentation references (
@doc)
Goal: Make changes to task files safely.
Steps:
- Read the file first
- Make changes following format rules
- Validate with
lash lint - Update index if needed
Example:
# Edit file (use your preferred method)
# Add new task:
# - [ ] Implement password reset
# Validate immediately
lash lint features/auth.md
# If lint passes, update index
lash indexUsing programmatic creation:
# Use lash add for safer task creation
lash add "Implement user registration" \
--file features/auth.md \
--label backend \
--label security \
--format jsonGoal: Ensure changes are valid before committing.
# Lint specific file
lash lint features/auth.md
# Lint all files
lash lint
# Check for broken links
lash check-links
# Verify index consistency
lash check-indexHandle errors:
# Get detailed explanation of error code
lash explain E001
# List all error codes
lash explain --listGoal: Rebuild the SQLite index after making changes.
# Update index (incremental)
lash index
# Force full rebuild
lash index --force
# Verify consistency
lash check-indexWhen to run:
- After adding new tasks
- After modifying task status
- After changing dependencies
- After bulk edits
Not needed for:
- Reading tasks
- Generating prompts (uses stale data if index is out of date)
Cause: Two tasks in the same file have the same @id.
Solution:
# Find the duplicate
lash lint features/auth.md
# Change one of the IDs to be unique
@id: login-endpoint-v2Cause: @depends-on points to a non-existent task.
Solution:
# Check what exists
lash list
# Update reference to correct path/ID
@depends-on: core/auth.md#task:correct-id
# Or remove invalid dependencyCause: Task hierarchy too deep (>3-4 levels).
Solution: Flatten the hierarchy or split into multiple files.
Cause: Used a checkbox symbol other than [ ], [x], [-], [!].
Solution:
# Change to valid symbol
- [ ] Task (not - [o] or - [v])Cause: Parent task marked [x] but children are [ ] or [!].
Solution: Either complete all children or waive them:
- [ ] Parent (change from [x] to [ ])
- [x] Child 1
- [-] Child 2 (waive if not needed)General recovery process:
- Run
lash lintto identify errors - Read error message carefully (includes file, line, and cause)
- Fix the specific issue mentioned
- Re-run
lash lintto verify - Repeat until clean
Example error output:
features/auth.md:42: E002: Invalid dependency reference
@depends-on: core/nonexistent.md#task:foo
Target file or task not found.
Suggestion: Verify the path and task ID exist.
If a dependency target was deleted:
# Option 1: Remove the dependency
# (edit file and delete @depends-on line)
# Option 2: Update to new target
@depends-on: new/path.md#task:new-idIf a dependency target was moved:
# Update path to new location
@depends-on: features/auth/login.md#task:endpointAutomated checking:
# Find all broken links
lash check-links
# Attempt automatic fix (interactive)
lash check-links --fixSymptoms:
- Search returns incorrect results
lash check-indexfails- Database errors
Solution:
# Delete index and rebuild
rm -rf .lash/lash.db
lash indexThe index is fully reconstructible from Markdown files.
Instead of loading entire task files, use sparse context generation:
# Get context for specific task with dependencies
lash show features/auth.md#task:login --deps
# Output shows:
# - The specific task
# - Its immediate dependencies (status only)
# - Summary instead of full contentBenefits:
- Reduced token usage
- Focused context
- Faster processing
Instead of copying full task descriptions, refer by ID:
Verbose (more tokens):
Work on the task "Implement user authentication with OAuth 2.0
and JWT tokens" in features/auth.md
Concise (fewer tokens):
Work on features/auth.md#task:auth-impl
Use lash show features/auth.md#task:auth-impl to get details only when needed.
Use task counts instead of full lists:
lash list --format json | jq '.summary'
# Output:
# {
# "total": 50,
# "completed": 30,
# "open": 15,
# "blocked": 5
# }Filter to relevant subset:
# Only backend tasks
lash agent-prompt --labels backend
# Only specific directory
lash agent-prompt --path features/auth/
# Combination
lash agent-prompt --labels security --path features/Start with minimal context, expand as needed:
-
First: Get overview
lash list --tree
-
Then: Get specific file
lash show features/auth.md
-
Finally: Get full context if needed
lash agent-prompt --path features/auth/
Command:
lash agent-prompt --format plainOutput (truncated):
# Lash Agent Usage Guide
## Overview
Lash is a minimalist, Markdown-native task tracker where:
- Markdown files are the single source of truth
- Tasks are hierarchical checkbox lists with annotations
...
## File Format
# Lash Task File Format
Hierarchical Markdown checkboxes with annotations
**Version:** 1.0
## Annotations
- `@id`: Unique identifier within file
- Example: `@id: feature-auth`
...Step 1: Read the file
cat features/auth.mdContent:
# Feature: Authentication
@id: feature-auth
@labels: backend, security
## Description
User authentication system with OAuth 2.0 and JWT tokens.
## Tasks
- [ ] Implement login endpoint
- [ ] Add password validation
- [ ] Generate JWT tokens
- [ ] Implement registrationStep 2: Make modifications
# Feature: Authentication
@id: feature-auth
@labels: backend, security
## Description
User authentication system with OAuth 2.0 and JWT tokens.
## Tasks
- [ ] Implement login endpoint
- Use bcrypt for password hashing
- JWT tokens expire after 24 hours
- [x] Add password validation
- [ ] Generate JWT tokens
- [ ] Implement registration
- [ ] Validate email format
- [ ] Send confirmation emailStep 3: Validate
lash lint features/auth.md
# Output: β features/auth.md is validStep 4: Update index
lash index
# Output: Indexed 1 file, 5 tasksFind tasks by label:
lash list --label backend --status open
# Output:
# - features/auth.md#task:login-endpoint: Implement login endpoint (open)
# - features/auth.md#task:registration: Implement registration (open)Search for specific term:
lash search "JWT"
# Output:
# features/auth.md:12: Generate JWT tokens
# features/auth.md:15: JWT tokens expire after 24 hoursView task with dependencies:
lash show features/profile.md#task:profile-page --deps
# Output:
# Task: Profile page implementation
# Status: blocked
# Dependencies:
# - features/auth.md#task:login-endpoint (open) [BLOCKING]Using lash add command:
lash add "Implement password reset" \
--file features/auth.md \
--label backend \
--label security \
--estimate 4h \
--agent-note "Use email-based reset tokens with 1-hour expiry" \
--format jsonJSON response:
{
"success": true,
"task_id": "implement-password-reset",
"file_path": "/project/features/auth.md",
"line_number": 23,
"is_new_file": false
}Using lash complete command:
# Complete a single task
lash complete features.auth#implement-login --json
# Complete multiple tasks at once
lash complete features.auth#task-1 features.auth#task-2 --jsonJSON response (success):
{
"success": true,
"completed": [
{
"task_id": "features.auth#implement-login",
"file_path": "features/auth.md",
"previous_status": "open"
}
],
"errors": []
}JSON response (task not found with suggestions):
{
"success": false,
"completed": [],
"errors": [
{
"task_id": "features.auth#implment-login",
"code": "E_NOT_FOUND",
"message": "Task not found: features.auth#implment-login",
"suggestions": ["features.auth#implement-login"]
}
]
}Dry run (preview without changes):
lash complete --dry-run features.auth#implement-login
# Output:
# Would complete:
# [x] features.auth#implement-login (features/auth.md)Exit codes:
0- All tasks completed successfully1- Validation error (task already complete, waived, etc.)5- Task not found
Scenario: Accidentally create duplicate ID
Edit:
- [ ] Task one
@id: duplicate-id
- [ ] Task two
@id: duplicate-idValidate:
lash lint features/auth.md
# Output:
# features/auth.md:15: E001: Duplicate task ID 'duplicate-id'
# First occurrence: line 12
# Duplicate found: line 15
#
# Fix: Ensure each @id is unique within the file.Fix:
- [ ] Task one
@id: duplicate-id
- [ ] Task two
@id: unique-id-2Re-validate:
lash lint features/auth.md
# Output: β features/auth.md is validAt the start of each agent session:
- Get fresh instructions:
lash agent-prompt --format plain - Understand current state:
lash list --tree - Filter to relevant area:
lash search <relevant-term> - Work on tasks, validating after each change
- Update index before session ends
When working on a specific task:
- Find the task:
lash search <description>orlash list --label <label> - Get task details:
lash show <task-id> --deps - Read the task file
- Make changes
- Validate:
lash lint <file> - Update index:
lash index - Mark task as done when complete
Use @doc references to find relevant documentation:
- Read task file, note
@docannotations - Read referenced documentation
- Apply guidance from docs to implementation
- Update task with progress
Example:
- [ ] Implement caching layer
@doc: ../docs/architecture.md#caching-strategy
- Use Redis for distributed cache
- TTL of 1 hour for user sessionsBefore starting work, check dependency chain:
- Show task with dependencies:
lash show <id> --deps - Verify no blockers:
lash list --blocked - If blocked, work on dependencies first
- Mark dependencies as done
- Proceed with original task
- Always validate: Run
lash lintafter every file modification - Check dependencies first: Use
lash show --depsbefore starting work - Use contextual notes: Add plain bullets for requirements and constraints
- Keep descriptions concise: Target 500-1000 characters
- Respect depth limits: Maximum 3-4 levels of nesting
- Update index after batch changes: Run
lash indexwhen done - Reference documentation: Use
@docto link relevant resources - Use progressive disclosure: Start with minimal context, expand as needed
- Prefer programmatic creation: Use
lash addfor safer task creation - Handle errors gracefully: Use
lash explain <code>for error details
- Forgetting to lint: Always validate before considering changes complete
- Ignoring depth limits: Don't create deeply nested hierarchies
- Breaking dependencies: Check reverse dependencies before deleting tasks
- Inconsistent status: Don't mark parents done when children are incomplete
- Skipping index update: Database queries use stale data until index is updated
- Confusing notes and tasks: Remember plain bullets are notes, checkboxes are tasks
- Use filters:
--labeland--pathreduce context size - Set token budgets:
--max-tokensprevents context overflow - Use JSON format: Structured output is easier to parse programmatically
- Batch operations: Make multiple edits, then lint once
- Incremental indexing:
lash indexis incremental by default
# Get agent instructions
lash agent-prompt --format plain
# Validate files
lash lint [file]
# List tasks
lash list [--label <label>] [--status <status>]
# Search tasks
lash search <query>
# Show task details
lash show <task-id> [--deps]
# Update index
lash index
# Check for broken links
lash check-links
# Explain error
lash explain <error-code># Topic Title
@id: unique-id
@labels: tag1, tag2
@depends-on: other/file.md#task:other-id
@doc: ../docs/reference.md#section
## Description
Context and requirements (500-1000 chars recommended).
## Tasks
- [ ] Task (checkbox = tracked)
- Plain bullet = note (not tracked)
- Requirement or constraint here
- [ ] Child task
- [ ] Grandchild task
- [x] Completed task
- [-] Waived task
- [!] Blocked task[ ]= open (not started or in progress)[x]= done (completed)[-]= waived (not applicable)[!]= blocked (dependencies incomplete)
@id= unique identifier@labels= comma-separated tags@owner= assignee@created= creation date (YYYY-MM-DD)@estimate= time estimate@depends-on= cross-file dependency@agent-note= hints for AI agents@doc= documentation reference
Cause: Not in a Lash project directory.
Solution:
# Create an index file at project root
echo "# Project Tasks" > lash.index.md
lash indexCause: Index not yet created.
Solution:
lash indexCause: Invalid task files.
Solution:
# Fix all lint errors first
lash lint
# Then rebuild index
lash indexCause: Index out of date.
Solution:
lash index # Update indexCause: Dependency may be resolved but index not updated.
Solution:
lash index # Update index
lash show <id> # Verify current status- User Guide:
docs/user-guide.md- Comprehensive user documentation - Design Document:
docs/design-doc.md- Technical specification - Error Codes:
docs/error-codes.md- Complete error reference - Examples:
examples/- Sample projects and tutorials
This guide provides a complete reference for AI agents to use Lash safely and effectively. Key takeaways:
- Format is strict: Follow the schema exactly
- Validation is essential: Always run
lash lintafter changes - Context is tunable: Use filters and token budgets to minimize overhead
- Errors are structured: Machine-readable codes with clear explanations
- Dependencies matter: Check before deleting or restructuring
For the most up-to-date information, run:
lash agent-prompt --format plainThis command generates fresh instructions based on the current project state.
Document Version: 1.0 Last Updated: 2025-01-15 Feedback: File issues at project repository