Git-native issue tracker for AI agent workflows.
Replaces beads in the overstory/mulch ecosystem. No Dolt, no daemon, no binary DB files. The JSONL file IS the database.
bun install -g @os-eco/seeds-cliOr try without installing:
npx @os-eco/seeds-cli --helpgit clone https://github.com/jayminwest/seeds
cd seeds
bun install
bun link # Makes 'sd' available globally
bun test # Run all tests
bun run lint # Biome check
bun run typecheck # tsc --noEmit# Initialize in your project
sd init
# Create an issue
sd create --title "Add retry logic to mail client" --type task --priority 1
# List open issues
sd list
# Find work (open, unblocked)
sd ready
# Claim and complete
sd update seeds-a1b2 --status in_progress
sd close seeds-a1b2 --reason "Implemented with exponential backoff"
# Commit .seeds/ changes to git
sd syncEvery command supports --json for structured output. sd list, sd ready, sd search, sd show, sd blocked, and sd stats also accept --format <markdown|compact|plain|ids|json>; --json is an alias for --format json. The ids mode prints issue IDs one per line for shell pipelines, e.g. sd list --label bug --format ids | xargs sd close. Global flags: -v/--version, -q/--quiet, --verbose, --timing. ANSI colors respect NO_COLOR.
| Command | Description |
|---|---|
sd init |
Initialize .seeds/ in current directory |
sd create --title <text> |
Create a new issue (--type, --priority, --description, --assignee) |
sd show <id> |
Show issue details |
sd list |
List issues with filters (--status, --type, --assignee, --label, --priority, --priority-max, --limit, --all, --sort, --format) |
sd ready |
Open issues with no unresolved blockers (--type, --assignee, --label, --label-any, --unlabeled, --priority, --priority-max, --limit, --sort, --format) |
sd search <query> |
Case-insensitive substring search on title + description (--status, --type, --assignee, --label, --label-any, --unlabeled, --priority, --priority-max, --limit, --sort, --format) |
sd update <id> |
Update issue fields (--status, --title, --priority, --assignee, --description) |
sd close <id> [<id2> ...] |
Close one or more issues (--reason) |
sd dep add <issue> <depends-on> |
Add dependency |
sd dep remove <issue> <depends-on> |
Remove dependency |
sd dep list <issue> |
Show deps for an issue |
sd block <id> --by <blocker-id> |
Mark issue as blocked by another |
sd unblock <id> --from <blocker-id> |
Remove a blocker (--all to clear all) |
sd blocked |
Show all blocked issues |
sd label add <id> <label> |
Add a label to an issue |
sd label remove <id> <label> |
Remove a label from an issue |
sd label list <id> |
List labels on an issue |
sd label list-all |
List all labels across issues |
sd stats |
Project statistics |
sd sync |
Stage and commit .seeds/ changes (--status, --dry-run) |
| Command | Description |
|---|---|
sd plan templates |
List available plan templates |
sd plan prompt <seed-id> |
Emit structured planning prompt JSON for a seed (--template, --domain) |
sd plan submit <seed-id> --plan <file> |
Validate a plan, spawn child seeds, write the plan row (--overwrite, --record-decision, --domain) |
sd plan show <pl-id> |
Show a plan with sections, children, and status (recurses through nested sub-plans up to max_plan_depth) |
sd plan validate <pl-id> |
Re-run validation against the current template definition |
sd plan list |
List plans (--seed, --status, --outcome, --template) |
sd plan outcome <pl-id> --result <success|partial|failure> |
Record a plan outcome (--note) |
sd plan review <pl-id> --by <name> |
Record a reviewer (informational; not a state transition) |
See Planning below for the end-to-end workflow.
| Command | Description |
|---|---|
sd tpl create --name <text> |
Create a template |
sd tpl step add <id> --title <text> |
Add step (supports {prefix} interpolation) |
sd tpl list |
List all templates |
sd tpl show <id> |
Show template with steps |
sd tpl pour <id> --prefix <text> |
Instantiate template into issues |
sd tpl status <id> |
Show convoy completion status |
| Command | Description |
|---|---|
sd doctor |
Check project health and data integrity (--fix) |
| Command | Description |
|---|---|
sd prime |
Output AI agent context (--compact) |
sd onboard |
Add seeds section to CLAUDE.md / AGENTS.md |
| Command | Description |
|---|---|
sd upgrade |
Upgrade seeds to latest version from npm (--check) |
sd completions <shell> |
Output shell completion script (bash, zsh, fish) |
sd migrate-from-beads |
Import .beads/issues.jsonl into .seeds/ |
sd plan adds structured planning that spawns child seeds. Use it when work is large or ambiguous enough that an LLM benefits from decomposing it before implementing — for small, well-scoped tasks just sd create directly.
The walkthrough is a three-step loop: prompt → fill → submit.
sd plan prompt seeds-9c4d --jsonReturns a structured prompt request the LLM can fill in:
{
"plan_request": {
"seed": "seeds-9c4d",
"template": "feature",
"instructions": "Fill every section. Required fields are marked.",
"sections": [
{ "name": "context", "required": true, "kind": "text", "min_length": 50, "prompt": "Why does this work need to happen?", "prior_art": [] },
{ "name": "approach", "required": true, "kind": "text", "prompt": "What's the chosen approach, and why this over alternatives?", "prior_art": [] },
{ "name": "steps", "required": true, "kind": "steps", "min": 2, "prompt": "Decompose into ordered, independent implementation steps." },
{ "name": "acceptance", "required": true, "kind": "list", "min": 1, "prompt": "Concrete, verifiable conditions for plan completion." }
],
"validation": { "all_required_present": true, "min_steps": 2, "min_acceptance": 1 }
}
}The LLM produces a submission JSON in the same shape, with concrete content. Each steps[] entry becomes a child seed; blocks: [step_index] translates into seed-level blockedBy dependencies.
{
"template": "feature",
"sections": {
"context": "...",
"approach": "Use AJV to validate template-driven plans, mirroring mulch's custom_types pipeline.",
"steps": [
{ "title": "Schema generator", "type": "task", "priority": 1, "blocks": [] },
{ "title": "Submit command", "type": "task", "priority": 1, "blocks": [0] },
{ "title": "Show command", "type": "task", "priority": 2, "blocks": [0] }
],
"acceptance": ["End-to-end submit + show works"]
}
}sd plan submit seeds-9c4d --plan plan.jsonValidates against the template, spawns one child seed per step, wires blockedBy from step.blocks, and writes a plans.jsonl row with status approved.
sd plan show pl-a1b2 # sections, children, recursive sub-plans
sd plan outcome pl-a1b2 --result success
sd plan review pl-a1b2 --by alice # optional, informationalOutcomes (success | partial | failure) are storage-only — aggregation and retros are out of scope and left to teams. Review is suggested but never gating: sd plan show prints a "review suggested" hint when the plan is approved/active and no reviewer is recorded.
| Template | Default for | Adds |
|---|---|---|
feature |
task, feature, epic |
context, approach, alternatives, steps, risks, acceptance |
bug |
bug |
reproduction, root_cause |
refactor |
opt-in only | behavior_invariant (must stay equal) |
refactor is opt-in via --template refactor — it has no matching seed type so seeds does not auto-route to it. Custom templates declared under plan_templates: in .seeds/config.yaml override the built-ins.
A step can declare plan_template: <name> to spawn a child seed that requires its own sub-plan. The child is created with requires_plan: true and is hidden from sd ready until its plan is submitted. sd plan show recursively renders nested plans up to max_plan_depth (default 3).
Full spec: see PLAN_SPEC.md.
Seeds stores all data in JSONL files inside a .seeds/ directory — one JSON object per line, fully diffable and mergeable via git. Advisory file locks (O_CREAT | O_EXCL) and atomic writes (temp file + rename) ensure safe concurrent access from multiple agents. The merge=union gitattribute handles parallel branch merges; dedup-on-read (last occurrence wins) resolves any duplicates. See CLAUDE.md for full technical details.
Beads works but carries baggage overstory doesn't need:
| Problem | Beads | Seeds |
|---|---|---|
| Storage | 2.8MB binary beads.db (can't diff/merge) |
JSONL (diffable, mergeable) |
| Sync | 286 export-state tracking files | No sync — file IS the DB |
| Concurrency | beads.db lock contention |
Advisory locks + atomic writes |
| Dependencies | Dolt embedded | chalk + commander |
| Value | Label | Use |
|---|---|---|
| 0 | Critical | System-breaking, drop everything |
| 1 | High | Core functionality |
| 2 | Medium | Default — important but not urgent |
| 3 | Low | Nice-to-have |
| 4 | Backlog | Future consideration |
.seeds/
config.yaml # Project config: project name, version
issues.jsonl # All issues, one JSON object per line
templates.jsonl # Template definitions
.gitignore # Ignores *.lock files
Add to your .gitattributes (done automatically by sd init):
.seeds/issues.jsonl merge=union
.seeds/templates.jsonl merge=union
The merge=union strategy handles parallel agent branch merges. Seeds deduplicates by ID on read (last occurrence wins), so conflicts resolve automatically.
Success:
{ "success": true, "command": "create", "id": "myproject-a1b2" }Error:
{ "success": false, "command": "create", "error": "Title is required" }Seeds is safe for concurrent multi-agent use:
- Advisory file locks —
O_CREAT | O_EXCL, 30s stale threshold, 100ms retry with jitter, 30s timeout - Atomic writes — temp file + rename under lock
- Dedup on read — last occurrence wins after
merge=uniongit merges
Overstory wraps sd via Bun.spawn(["sd", ...]) with --json parsing, identical to how it wraps bd:
| BeadsClient method | sd command |
|---|---|
ready() |
sd ready --json |
show(id) |
sd show <id> --json |
create(title, opts) |
sd create --title "..." --json |
claim(id) |
sd update <id> --status=in_progress --json |
close(id, reason) |
sd close <id> --reason "..." --json |
Seeds is part of the os-eco AI agent tooling ecosystem.
Contributions are welcome! See CONTRIBUTING.md for guidelines.
MIT
