Lash is a terminal-first task management system that uses Markdown as the single source of truth. It's designed for developers and AI agents who want:
- Markdown-native: Task files are just structured Markdown
- Fast: SQLite-backed indexing for instant queries
- Strict format: Linter-enforced structure for predictability
- Agent-friendly: Token-minimized output for LLM integration
- Dependency-aware: Cross-file task dependencies with cycle detection
All core functionality is production-ready and backed by an extensive test suite (3,000+ tests):
- Markdown Parser - Full task file parsing with contextual notes support
- Linter & Formatter - 28 validation rules with auto-formatting and interactive mode
- SQLite Indexing - Fast indexing engine exceeding performance targets by 8-12x
- Dependency Resolution - Complete graph analysis with cycle detection
- Terminal UI (TUI) - Interactive interface with 300+ Gogh color schemes and task creation
- CLI Framework - Configuration, logging, and command execution infrastructure
- Query Commands - List, search, show, and graph commands for exploring tasks
- Task Creation - CLI and TUI support for adding tasks with full annotation support
- Agent Integration - Token-minimized prompt generation for LLM workflows
lash/
βββ crates/
β βββ lash-types/ # Shared types, errors, config
β βββ lash-core/ # Markdown parsing & validation
β βββ lash-db/ # SQLite indexing & queries
β βββ lash-agent/ # Agent integration & prompt generation
β βββ lash-tui/ # Terminal UI
β βββ lash-cli/ # CLI binary
βββ docs/ # Design docs & error codes
βββ tasks/ # Development task tracking
Requirements: Rust stable toolchain (see rust-toolchain.toml)
# Build all crates
cargo build --workspace
# Run tests
cargo test --workspace
# Check formatting and lints
cargo fmt --check
cargo clippy --workspace -- -D warningsbrew install fixture-dev/tap/lashUpgrade with brew upgrade lash, remove with brew uninstall lash.
Download a prebuilt binary for Linux, macOS, or Windows from the latest release, or use the installer scripts published with each release:
# Linux / macOS
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/fixture-dev/lash/releases/latest/download/lash-installer.sh | sh# Windows
powershell -ExecutionPolicy Bypass -c "irm https://github.com/fixture-dev/lash/releases/latest/download/lash-installer.ps1 | iex"Pick one method. The installer script and
cargo installplacelashin~/.cargo/bin, while Homebrew uses its own prefix. Installing both leaves two binaries on your PATH, and whichever comes first wins β which makes version mismatches confusing. Runwhich -a lashif output looks stale.
Install Lash globally using the install script:
# Install Lash to ~/.cargo/bin/lash
./scripts/install.sh
# Force reinstall (useful for local development/testing)
./scripts/install.sh reinstall
# Check installation status
./scripts/install.sh status
# Uninstall
./scripts/install.sh uninstallThe script builds an optimized release binary and installs it to ~/.cargo/bin/lash. Ensure ~/.cargo/bin is in your PATH:
export PATH="$HOME/.cargo/bin:$PATH"Alternatively, install directly with Cargo:
cargo install --path crates/lash-cli # Install
cargo install --path crates/lash-cli --force # Reinstall
cargo uninstall lash # UninstallWant to explore Lash's features without setting up your own project? Try the playground!
lash playground init
cd playground
lash list --label gameplayThe playground creates "PixelQuest" - a realistic game development demo project with:
- 25+ task files + index across features, systems, content, and milestones
- Hundreds of tasks showing realistic project complexity
- Examples of dependencies, labels, statuses, and annotations
- A comprehensive
PLAYGROUND_GUIDE.mdwith usage examples
Perfect for:
- Learning Lash's features
- Testing new commands
- Demos and presentations
- Understanding best practices
See playground/PLAYGROUND_GUIDE.md for detailed usage instructions.
This project follows strict quality standards:
- Pre-commit hooks: Auto-enforces formatting, linting, and tests
- Zero warnings: All clippy lints must pass with
clippy::pedantic - Comprehensive tests: 3,000+ tests across all crates (>80% coverage target)
- Error taxonomy: 75+ documented error codes in
docs/error-codes.md, all queryable withlash explain - Doctests: All public APIs include executable examples
- CI/CD: Automated testing on Linux, macOS, and Windows
# Run with formatting
cargo fmt --all
# Run comprehensive checks (enforced by pre-commit hook)
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace --all-targets
cargo test --doc
# Install pre-commit hooks
./scripts/install-pre-commit-hook.sh
# Generate coverage report
cargo install cargo-llvm-cov
cargo llvm-cov --workspace --html
open target/llvm-cov/html/index.htmlSee docs/TESTING.md for detailed testing documentation.
Lash includes comprehensive benchmarks for indexing performance. Benchmarks measure performance across different project sizes and scenarios.
# Run all benchmarks
cargo bench --package lash-db --bench indexing
# Run specific benchmark
cargo bench --package lash-db --bench indexing -- full_indexing
# Quick benchmarks (faster, less accurate)
cargo bench --package lash-db --bench indexing -- --quick
# View HTML reports
open target/criterion/report/index.htmlPerformance achieved (exceeds targets by 8-12x):
- Small projects (10 files, ~50 tasks): 10.5ms (target: <50ms)
- Medium projects (100 files, ~500 tasks): 61ms (target: <500ms)
- Large projects (1000 files, ~5000 tasks): 425ms (target: <5s)
Enable profiling to measure time spent in each indexing phase:
use lash_db::{Indexer, IndexerConfig, init_database};
use lash_types::LashConfig;
let conn = init_database(&db_path)?;
let config = IndexerConfig::new(project_root)
.with_profiling(true); // Enable profiling
let parser_config = LashConfig::default();
let mut indexer = Indexer::new(&conn, config, &parser_config);
let report = indexer.index_project()?;
// Print performance summary
if let Some(profile) = report.profile {
profile.print_summary();
// Or export to JSON for analysis
println!("{}", profile.to_json_pretty());
}The profiler tracks:
- Phase times: discovery, diff, parsing, database, closure_rebuild
- Per-file parse times: Individual file parsing performance
- Database operations: Query and insert times with row counts
- Total duration: End-to-end indexing time
# Initialize a new Lash project
lash init [--path DIR]
# Initialize demo project (PixelQuest)
lash playground init# Lint task files
lash lint [PATH...] [--fix] [--interactive]
# Format task files (alias: fmt)
lash format [PATH...] [--check] [--diff]
# Explain any code the linter reports
lash explain W_INDEX_ORPHAN
lash explain --listCommands that walk the project (lint, format, index, check-index,
check-links) read every .md file under the project root, skipping anything
excluded by .gitignore or by a .lashignore at the project root.
.lashignore uses .gitignore syntax, so a directory of Markdown that is not
task files is one line:
printf 'content/\n' >> .lashignoreWithout it, each such file is reported once per run as W_INDEX_ORPHAN ("not
referenced in the root index"). Common documentation filenames and the docs/
directory are exempt already.
# Index files into database
lash index [--force] [--show-files]
# Verify database consistency
lash check-index [--diff]# List tasks (with filters)
lash list [--label backend] [--status open] [--owner name]
lash list [--tree] [--show-descriptions] [--show-notes]
# Search tasks (full-text)
lash search "authentication" [--limit 20]
# Show task details (agent note, dependency status, children; --short for terse)
lash show <task-id> [--deps] [--rdeps] [--short]# Add a new task
lash add "Task description" [--file path.md] [--parent task-id]
lash add "Task" --label backend --owner alice --estimate 2h
lash add --interactive # Interactive mode# Mark tasks as complete
lash complete <task-id> # Complete a single task
lash complete task1 task2 task3 # Complete multiple tasks
lash complete --dry-run <task-id> # Preview without changing files
lash complete --json <task-id> # Machine-readable output# Mark a task as waived (not applicable) instead of completed
lash waive <task-id>
lash waive --reason "Superseded by task-2" <task-id> # Record why
lash waive --cascade <task-id> # Also waive unchecked plain-bullet children
lash waive --dry-run <task-id> # Preview without changing filesWrites the [-] checkbox marker and re-indexes in the same run β no
separate lash index step needed. Already-waived tasks and completed
([x]) tasks are refused (hand-edit the checkbox if completed work truly
needs to be waived).
# Rewrite a task's title (pins the old title-derived @id first, so any
# @depends-on reference pointing at it keeps resolving)
lash update <task-id> --title "New title"
# Labels, owner, estimate
lash update <task-id> --add-label urgent --remove-label backend
lash update <task-id> --owner alice --estimate 2h
lash update <task-id> --owner "" # empty string clears the annotation
# Agent notes
lash update <task-id> --agent-note "Replace the whole note"
lash update <task-id> --append-agent-note "Add a continuation line"
# Dependencies (validated against the project, like `lash add --depends-on`)
lash update <task-id> --add-depends-on other-task
lash update <task-id> --remove-depends-on other-task
lash update <task-id> --add-depends-on not-yet-created --allow-forward-ref
# Preview without writing
lash update <task-id> --title "New title" --dry-runAt least one mutation flag is required. Writes and re-indexes atomically,
same as complete/waive.
# Export dependency graph
lash graph [--format ascii|dot|json|mermaid]
lash graph [--scope file.md] [--hide-completed]
# Validate cross-file links
lash check-links [--fix] [--dry-run]When a task references a documentation fragment with
@doc: path/to/file.md#fragment, Lash matches fragment against the headings
of the target document using case-insensitive, punctuation-insensitive
normalization. The fragment and each heading are both reduced to a canonical
form before comparison:
- Lowercase the text.
- Replace
-with a space. - Drop every character that is not alphanumeric or whitespace
(so
<,>,/,.,(,), backticks, and_are all stripped β no word boundary is inserted). - Collapse runs of whitespace into single spaces.
Examples:
| Heading | Matching fragment |
|---|---|
## Section One |
section-one |
## 1. Three-Runtime Separation |
1-three-runtime-separation |
## Validation rules (must pass at index time) |
validation-rules-must-pass-at-index-time |
### Pack manifest (`<pack>/SKILL.md`) |
pack-manifest-packskillmd (slashes/dots/angle brackets collapse to nothing) |
### `allowed_tools` vocabulary (launch) |
allowed_tools-vocabulary-launch (underscores match anything or nothing) |
The matcher is symmetric: any fragment that normalizes to the same canonical
form as the heading will match. lash lint raises W_SEM_DOC_FRAGMENT when no
heading in the target file matches the fragment, and the warning message lists
the headings that do exist so you can pick the right one. Run
lash explain W_SEM_DOC_FRAGMENT for the long-form explanation.
# Generate live, project-specific prompt for LLMs (dynamic context on demand)
lash agent-prompt [--format plain|json|agents-md]
lash agent-prompt [--label backend] [--max-tokens 4000]
lash agent-prompt [--include-descriptions] [--include-notes]
# Install a static Lash skill into a coding agent's conventional directory
lash skill install --target claude|codex|cursor|agents-md [--scope project|user]
lash skill list # show installed skills
lash skill update --target claude # refresh after upgrading lash
lash skill uninstall --target claude# Launch TUI
lash tui [--color-scheme "Nord"]# Manage configuration
lash config get <key>
lash config set <key> <value>
lash config list [--changed]
# Generate shell completions
lash completion bash|zsh|fish|powershell|elvish
# Explain error codes
lash explain <CODE>
lash explain --list # Show all error codesLash supports contextual notes - plain bullet points (without checkboxes) nested under tasks that provide inline context, requirements, or acceptance criteria:
- [ ] Implement payment gateway
- Use Stripe API v3 for transactions
- Support credit card and ACH payments
- Must handle refunds and partial captures
- [ ] Set up Stripe account
- [ ] Implement checkout flow
- [ ] Add webhook handlingKey points:
- Plain bullets (
- Text) provide context and are not tracked for completion - Checkbox bullets (
- [ ] Text) are actionable tasks tracked for completion - Notes should appear before child tasks (convention)
- Notes cannot have children (enforced by linter)
- Notes are searchable via
lash search
This distinction helps separate "what needs to be done" from "how to do it" or "acceptance criteria", making task files more readable and providing better context for both humans and AI agents.
See examples/contextual-notes.md for comprehensive examples.
Task files can include an optional ## Description section for providing detailed context about the file's purpose:
# Authentication System
@id: auth
@labels: backend, security
## Description
This module handles all authentication flows including login, logout,
password reset, and session management. It integrates with our OAuth
providers and implements JWT-based token authentication.
## Tasks
- [ ] Implement login endpoint
- [ ] Add password reset flowKey points:
- Description sections are full-text searchable via
lash search - Displayed in task detail views (
lash show) and TUI - Use
--show-descriptionswithlash listto include in output - Great for providing context to both humans and AI agents
Lash supports 300+ color schemes from the Gogh collection. You can:
-
Set globally via
~/.lash/config.toml:color_scheme = "Nord"
-
Override per-command with
--color-scheme:lash tui --color-scheme "Dracula" -
Change in TUI by pressing
tto open the theme selector
Popular schemes include: Nord, Dracula, Solarized Dark, Solarized Light, Monokai, Tokyo Night, Catppuccin, and Base2Tone Desert (default).
- User Guide - Complete user documentation
- Developer Guide - Architecture and contribution guide
- Agent Integration Guide - Guide for AI agents using Lash
- Design Document - Comprehensive specification
- Error Codes - Complete error catalog
- Testing Guide - Testing documentation
- Examples - Tutorials and sample projects
We welcome contributions! See CONTRIBUTING.md for guidelines.
For the current development roadmap, see tasks/tasks.md and devlog.md for recent progress.
Licensed under the Apache License, Version 2.0.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you shall be licensed as above, without any additional terms or conditions.
Bundled terminal color schemes in crates/lash-tui/data/themes.json are derived
from the Gogh collection (MIT License). See
NOTICE for details.
