Fast, minimal task tracking with plain markdown files — no database required
Minimal task tracker for AI agents with built-in Claude Code hooks.
| beads (SQLite) | dots (markdown) | |
|---|---|---|
| Binary | 25 MB | 233 KB (107x smaller) |
| Lines of code | 115,000 | 2,800 (41x less) |
| Dependencies | Go, SQLite/Wasm | None |
| Portability | Rebuild per platform | Copy .dots/ anywhere |
A CLI task tracker with zero dependencies — tasks are plain markdown files with YAML frontmatter in .dots/. No database, no server, no configuration. Copy the folder between machines, commit to git, edit with any tool. Parent-child relationships map to folders. Each task has an ID, title, status, priority, and optional dependencies.
brew install joelreymont/tap/dotsgit clone https://github.com/joelreymont/dots.git
cd dots
zig build -Doptimize=ReleaseSmall
cp zig-out/bin/dot ~/.local/bin/dot --version
# Output: dots 0.5.4# Initialize in current directory
dot init
# Creates: .dots/ directory (added to git if in repo)
# Add a task
dot add "Fix the login bug"
# Output: dots-a1b2c3d4e5f6a7b8
# List tasks
dot ls
# Output: [a1b2c3d] o Fix the login bug
# Start working
dot on a1b2c3d
# Output: (none, task marked active)
# Complete task
dot off a1b2c3d -r "Fixed in commit abc123"
# Output: (none, task marked done and archived)dot initCreates .dots/ directory. Runs git add .dots if in a git repository. Safe to run if already exists.
dot add "title" [-p PRIORITY] [-d "description"] [-P PARENT_ID] [-a AFTER_ID] [--json]
dot "title" # shorthand for: dot add "title"Options:
-p N: Priority 0-4 (0 = highest, default 2)-d "text": Long description (markdown body of the file)-P ID: Parent task ID (creates folder hierarchy)-a ID: Blocked by task ID (dependency)--json: Output created task as JSON
Examples:
dot add "Design API" -p 1
# Output: dots-1a2b3c4d5e6f7890
dot add "Implement API" -a dots-1a2b3c4d -d "REST endpoints for user management"
# Output: dots-3c4d5e6f7a8b9012
dot add "Write tests" --json
# Output: {"id":"dots-5e6f7a8b9012cdef","title":"Write tests","status":"open","priority":2,...}dot ls [--status STATUS] [--json]Options:
--status: Filter byopen,active, ordone(default: shows open + active)--json: Output as JSON array
Output format (text):
[1a2b3c4] o Design API # o = open
[3c4d5e6] > Implement API # > = active
[5e6f7a8] x Write tests # x = done
dot on <id> [id2 ...]Marks task(s) as active. Use when you begin working on tasks. Supports short ID prefixes.
dot off <id> [id2 ...] [-r "reason"]Marks task(s) as done and archives them. Optional reason applies to all. Root tasks are moved to .dots/archive/. Child tasks wait for parent to close before moving.
dot show <id>Output:
ID: dots-1a2b3c4d5e6f7890
Title: Design API
Status: open
Priority: 1
Desc: REST endpoints for user management
Created: 2024-12-24T10:30:00Z
dot rm <id> [id2 ...]Permanently deletes task file(s). If removing a parent, children are also deleted.
dot ready [--json]Lists tasks that are open and have no blocking dependencies (or blocker is done).
dot treeOutput:
[1a2b3c4] o Build auth system
+- [2b3c4d5] o Design schema
+- [3c4d5e6] o Implement endpoints (blocked)
+- [4d5e6f7] o Write tests (blocked)
dot find "query"Case-insensitive search in title and description.
dot purgePermanently deletes all archived (completed) tasks from .dots/archive/.
Tasks are stored as markdown files with YAML frontmatter in .dots/:
.dots/
a1b2c3d4e5f6a7b8.md # Root dot (no children)
f9e8d7c6b5a49382/ # Parent with children
f9e8d7c6b5a49382.md # Parent dot file
1a2b3c4d5e6f7890.md # Child dot
archive/ # Closed dots
oldtask12345678.md # Archived root dot
oldparent1234567/ # Archived tree
oldparent1234567.md
oldchild23456789.md
config # ID prefix setting
---
title: Fix the bug
status: open
priority: 2
issue-type: task
assignee: joel
created-at: 2024-12-24T10:30:00Z
blocks:
- a3f2b1c8d9e04a7b
---
Description as markdown body here.IDs have the format {prefix}-{slug}-{hex} where:
prefix: Project prefix from.dots/config(default:dots)slug: URL-safe abbreviation of the title (max 32 chars)hex: 8-character random hex suffix
Example: dots-fix-user-auth-a3f2b1c8
The slug uses common abbreviations (authentication→auth, configuration→config, etc.) and truncates at word boundaries. Run dot slugify to rename existing IDs to include slugs.
Commands accept short prefixes:
dot on a3f2b1 # Matches dots-fix-user-auth-a3f2b1c8
dot show a3f # Error if ambiguous (multiple matches)dot slugifyRenames all issue IDs (including archived) to include slugs based on their titles. Preserves the hex suffix and updates all dependency references.
open -> active -> done (archived)
open: Task created, not startedactive: Currently being worked ondone: Completed, moved to archive
0: Critical1: High2: Normal (default)3: Low4: Backlog
parent (-P): Creates folder hierarchy. Parent folder contains child files.blocks (-a): Stored in frontmatter. Task blocked until all blockers aredone.
When a task is marked done:
- Root tasks: Immediately moved to
.dots/archive/ - Child tasks: Stay in parent folder until parent is closed
- Parent tasks: Only archive when ALL children are closed (moves entire folder)
dots has built-in hook support—no Python scripts needed.
dot hook session # Show active/ready tasks at session start
dot hook sync # Sync TodoWrite JSON from stdin to dotsAdd to ~/.claude/settings.json:
{
"hooks": {
"SessionStart": [
{
"hooks": [{"type": "command", "command": "dot hook session"}]
}
],
"PostToolUse": [
{
"matcher": "TodoWrite",
"hooks": [{"type": "command", "command": "dot hook sync"}]
}
]
}
}The sync hook automatically:
- Creates
.dots/directory if needed - Maps TodoWrite content to dot IDs (stored in
.dots/todo-mapping.json) - Creates new dots for new todos
- Marks dots as done when todos are completed
If you have existing tasks in .beads/beads.db, use the migration script:
./migrate-dots.shThis exports your tasks from SQLite and imports them as markdown files. The script verifies the migration was successful before prompting you to delete the old .beads/ directory.
Requirements: sqlite3 and jq must be installed.
| Feature | Description |
|---|---|
| Markdown files | Human-readable, git-friendly storage |
| YAML frontmatter | Structured metadata with flexible body |
| Folder hierarchy | Parent-child relationships as directories |
| Short IDs | Type a3f instead of dots-a3f2b1c8d9e04a7b |
| Archive | Completed tasks out of sight, available if needed |
| Zero dependencies | Single binary, no runtime requirements |
MIT
