| Crates.io | spool-cli |
| lib.rs | spool-cli |
| version | 1.0.0 |
| created_at | 2026-01-25 02:19:46.902177+00 |
| updated_at | 2026-01-25 02:19:46.902177+00 |
| description | CLI for spool - git-native task management |
| homepage | |
| repository | https://github.com/iamnbutler/spool |
| max_upload_size | |
| id | 2067954 |
| size | 51,337 |
Git-native, event-sourced task management.
cargo install spool
Tasks are stored as append-only event logs in .spool/events/. Every change is tracked, branches work naturally, and merge conflicts resolve automatically.
cd your-project
spool init
Creates .spool/ with events/ and archive/ directories. Commit this to git.
spool add "Implement authentication"
spool add "Fix login bug" -p p0 -t bug -d "Users getting logged out unexpectedly"
spool add "Backend API" -p p1 -t feature -a @alice --stream <stream-id>
Options: -p priority (p0-p3), -t tag (repeatable), -d description, -a assignee, --stream stream ID.
spool list # Open tasks (default)
spool list -s all # All tasks
spool list -s complete # Completed only
spool list -a @alice # By assignee
spool list -p p0 -t bug # By priority and tag
spool list --stream <id> # By stream
spool list --no-stream # Tasks without a stream
spool list -f json # JSON output
spool list -f ids # IDs only (for scripting)
spool show <task-id>
spool show <task-id> --events # Include event history
spool update <id> -t "New title"
spool update <id> -d "New description"
spool update <id> -p p1
spool update <id> --stream <stream-id>
spool assign <id> @alice # Assign to user
spool claim <id> # Assign to yourself
spool free <id> # Unassign
spool complete <id> # Default resolution: done
spool complete <id> -r wontfix # Other: duplicate, obsolete
spool reopen <id> # Reopen completed task
Streams group tasks into collections (features, sprints, areas).
spool stream add "api" -d "Backend API work"
spool stream list
spool stream show <id>
spool stream show --name "api"
spool stream update <id> -n "new-name" -d "New description"
spool stream delete <id> # Must have no tasks
spool rebuild # Regenerate caches from events
spool archive --days 30 # Archive old completed tasks
spool archive --dry-run # Preview what would be archived
spool validate # Check event file integrity
spool validate --strict # Fail on warnings too
Instead of mutable records, every change is an immutable event:
{"v":1,"op":"create","id":"k8b2x-a1c3","ts":"2026-01-13T12:00:00Z","by":"@alice","branch":"main","d":{"title":"Fix bug"}}
{"v":1,"op":"assign","id":"k8b2x-a1c3","ts":"2026-01-13T12:01:00Z","by":"@bob","branch":"main","d":{"to":"@bob"}}
{"v":1,"op":"complete","id":"k8b2x-a1c3","ts":"2026-01-13T14:00:00Z","by":"@bob","branch":"main","d":{"resolution":"done"}}
Events are stored in daily JSONL files: .spool/events/2026-01-13.jsonl
State is materialized by replaying events. Caches (.index.json, .state.json) are gitignored and rebuilt on demand with spool rebuild.
.spool/
├── events/ # Daily event logs (committed)
│ └── 2026-01-13.jsonl
├── archive/ # Monthly archives (committed)
│ └── 2026-01.jsonl
├── .index.json # Cache (gitignored)
├── .state.json # Cache (gitignored)
└── .gitignore
| Operation | Description |
|---|---|
create |
Create task with title, description, priority, assignee, tags |
update |
Update task fields |
assign |
Change assignee (null to unassign) |
complete |
Mark complete with resolution |
reopen |
Reopen completed task |
comment |
Add comment |
link / unlink |
Manage relationships (blocks, blocked_by, parent) |
set_stream |
Set or remove task's stream |
create_stream |
Create stream |
update_stream |
Update stream metadata |
delete_stream |
Delete stream |
archive |
Archive completed task |
Format: {timestamp}-{random} where timestamp is Unix ms in base36 and random is 4 alphanumeric chars.
Example: k8b2x-a1c3
git add src/feature.rs .spool/events/
git commit -m "Implement feature and update task"
#!/bin/sh
# .git/hooks/post-merge
spool rebuild
Event files are append-only. On conflict, keep both sets of events:
# Keep all events from both versions, then:
spool validate
spool rebuild
spool validate --strict || exit 1
# Get task IDs
TASKS=$(spool list -f ids)
# Process JSON
spool list -f json | jq '.[] | select(.priority == "p0")'
# Batch operations
for id in $(spool list -f ids -t bug); do
spool update $id -p p1
done
MIT