Skip to content

Repository files navigation

Tick

Task management for agentic engineering

A Go CLI that gives AI agents deterministic, token-efficient task tracking,
without the complexity of full project management tools.

License: MITGo

Install · Quick Start · Commands · Output Formats · Why Tick?


Tick is a lightweight CLI for tracking tasks, dependencies, and status transitions inside your project. It stores everything in a plain JSONL file (human-readable, git-friendly) with a SQLite cache for fast queries. Run tick init, and you're set.

It's built to be used by AI agents as much as by humans. Output auto-switches between a token-efficient format for agents and clean tables for terminals, so the same commands work in both contexts.

Why Tick?

Claude, Cursor, and other AI coding agents need a way to track tasks across sessions. The built-in approaches have problems:

  • TodoWrite / in-context lists — lost between sessions, no persistence, no dependency tracking
  • Markdown files — no structure, agents parse them inconsistently, output is verbose
  • Beads / full PM tools — too much complexity for a coding session, heavy overhead

Tick sits in between: structured enough for agents to reason about reliably, simple enough that it doesn't get in the way.

Key differences

TickTodoWriteMarkdownBeads
Persists across sessionsYesNoYesYes
Dependencies & blockersYesNoNoYes
Token-efficient outputTOON (30-60% savings)N/ANoNo
Deterministic formatYesVariesNoYes
ComplexityLowMinimalMinimalHigh
Setuptick initNoneNoneConfig

Install

macOS

brew install leeovery/tools/tick

Linux

curl -fsSL https://raw.githubusercontent.com/leeovery/tick/main/scripts/install.sh | bash

Go

go install github.com/leeovery/tick/cmd/tick@latest

Quick Start

tick init # create .tick/ in your project
tick create "Build auth module"# create a task
tick create "Write tests" --priority 1 --blocked-by tick-a1b2
tick list # see all tasks
tick ready # tasks with no blockers
tick start tick-a1b2 # open → in_progress
tick done tick-a1b2 # in_progress → done

Commands

init

Initialize a new tick project in the current directory. Creates a .tick/ directory with an empty tasks.jsonl file.

tick init

create

Create a new task. Returns the full task detail on success.

tick create <title> [flags]
FlagTypeDefaultDescription
--priority0-420 critical, 1 high, 2 medium, 3 low, 4 backlog
--descriptionstringTask description (supports multi-line)
--typestringTask type: bug, feature, task, chore
--tagsstringsComma-separated tags (kebab-case, max 10)
--refsstringsComma-separated external references (URLs, issue keys)
--parentIDMake this a subtask of another task
--blocked-byIDsComma-separated list of tasks this depends on
--blocksIDsComma-separated list of tasks this blocks
tick create "Build auth module"
tick create "Critical fix" --priority 0 --type bug
tick create "Write tests" --blocked-by tick-a1b2,tick-c3d4 --tags backend,testing
tick create "Login endpoint" --parent tick-a1b2 --refs https://github.com/org/repo/issues/42

list

List tasks with optional filters. Results are sorted by priority (ascending), then creation date.

tick list [flags]
FlagTypeDefaultDescription
--statusstringFilter by status: open, in_progress, done, cancelled
--priority0-4Filter by priority level
--typestringFilter by type: bug, feature, task, chore
--tagstringFilter by tag (repeatable, see below)
--parentIDShow descendants of a task
--readyboolfalseShow only ready tasks (open, no unresolved blockers, no open children, no dependency-blocked ancestor)
--blockedboolfalseShow only blocked tasks (open with unresolved blockers, open children, or dependency-blocked ancestor)
--countintLimit results to N tasks

--ready and --blocked are mutually exclusive.

Tag filtering supports AND/OR composition:

  • --tag ui,backend — AND: tasks must have both tags
  • --tag ui --tag api — OR: tasks with either tag
  • --tag ui,backend --tag api — mixed: (ui AND backend) OR api
tick list # all tasks
tick list --status open # filter by status
tick list --priority 0 # only critical tasks
tick list --type bug # only bugs
tick list --tag backend # tasks tagged "backend"
tick list --parent tick-a1b2 # descendants of a task
tick list --count 5 # first 5 results

ready

Alias for tick list --ready. Shows tasks that are open, have no unresolved blockers, no open children, and no dependency-blocked ancestor. Accepts the same filter flags as list (--status, --priority, --type, --tag, --parent, --count).

tick ready
tick ready --count 1 # next task to work on
tick ready --type bug --count 3

blocked

Alias for tick list --blocked. Shows tasks that are open but waiting on dependencies, have open children, or have an ancestor with unresolved blockers. Accepts the same filter flags as list.

tick blocked
tick blocked --tag backend

show

Display full detail for a single task, including type, tags, refs, notes, blockers, children, and description.

tick show <task-id>

update

Modify one or more fields on an existing task. At least one flag is required.

tick update <task-id> [flags]
FlagTypeDescription
--titlestringSet a new title
--descriptionstringSet or replace the description
--clear-descriptionboolRemove the description (mutually exclusive with --description)
--priority0-4Change priority level
--typestringSet task type (bug, feature, task, chore)
--clear-typeboolRemove the type
--tagsstringsReplace tags (comma-separated)
--clear-tagsboolRemove all tags
--refsstringsReplace refs (comma-separated)
--clear-refsboolRemove all refs
--parentIDSet or change the parent task (pass empty string to clear)
--blocksIDsComma-separated list of tasks this blocks
tick update tick-a1b2 --title "Revised title" --priority 1
tick update tick-a1b2 --type bug --tags critical,backend
tick update tick-a1b2 --parent tick-c3d4

start / done / cancel / reopen

Transition a task between statuses.

tick start <task-id># open → in_progress
tick done<task-id># in_progress → done
tick cancel <task-id># any → cancelled
tick reopen <task-id># done/cancelled → open

done and cancel set a closed timestamp. reopen clears it.

Cascading: Status changes automatically propagate through parent/child hierarchies:

  • Start cascades up — starting a child auto-starts open ancestors
  • Done/Cancel cascades down — completing or cancelling a parent cascades to non-terminal descendants
  • Done/Cancel cascades up — when all children are terminal, the parent auto-completes (done if any child is done, cancelled if all cancelled)
  • Reopen cascades up — reopening a child reopens done ancestors
  • Adding a child to a done parent auto-reopens it; adding to a cancelled parent is blocked
  • Reparenting — moving a child away from a parent triggers completion re-evaluation: if all remaining children are terminal, the old parent auto-completes

remove

Permanently delete one or more tasks. Removing a parent cascades to all descendants. Dependency references on surviving tasks are automatically cleaned up.

tick remove <id> [<id>...] [flags]
FlagTypeDescription
--force, -fboolSkip confirmation prompt
tick remove tick-a1b2 # remove with confirmation
tick remove tick-a1b2 tick-c3d4 -f # remove multiple, skip prompt

Since tasks.jsonl is tracked in git, accidental removals can be recovered from history.

note

Add or remove timestamped notes on a task.

tick note add <task-id><text>
tick note remove <task-id><index>
tick note add tick-a1b2 "Discussed approach with team"
tick note remove tick-a1b2 1 # remove note at index 1 (1-based)

dep

Manage and visualize task dependencies. Tick validates all dependency changes and prevents cycles, self-references, children blocked by their own parent, and dependencies on cancelled tasks.

tick dep add <task-id><blocked-by-id>
tick dep remove <task-id><blocked-by-id>
tick dep tree [task-id]
tick dep add tick-a1b2 tick-c3d4 # tick-a1b2 is now blocked by tick-c3d4
tick dep remove tick-a1b2 tick-c3d4 # remove that dependency

dep tree — Visualize dependency chains. Two modes:

tick dep tree # full graph: all dependency chains
tick dep tree tick-a1b2 # focused: upstream + downstream from a task

Full graph shows root tasks (tasks that block others but aren't blocked themselves) with their downstream chains, plus a summary line. Focused view walks both directions from the target — what blocks it and what it unblocks. Diamond dependencies are duplicated at each path.

Pretty (box-drawing tree)

$ tick dep tree
tick-a1b2 Setup auth (done)
└── tick-c3d4 Login endpoint (open)
└── tick-f3e4 Write tests (open)
1 chain, longest: 2, 2 blocked

TOON (flat edge list)

$ tick dep tree
dep_tree[2]{from,to}:
tick-a1b2,tick-c3d4
tick-c3d4,tick-f3e4
summary{chains,longest,blocked}:
1,2,2

stats

Show aggregate task counts grouped by status, workflow state (ready/blocked), and priority.

tick stats

doctor

Run diagnostic checks against your task data. Read-only, never modifies data.

tick doctor

Checks for: JSONL syntax errors, invalid IDs, duplicates, orphaned references, self-referential dependencies, dependency cycles, parent/child constraint violations, and cache staleness.

rebuild

Force a full SQLite cache rebuild from the JSONL source file, bypassing the freshness check.

tick rebuild

version

Print the tick version and exit. The --version global flag is equivalent.

tick version
tick --version

help

Show usage information. With no argument, lists all commands and global flags. With a command name, shows detailed help including flags.

tick help# list all commands
tick help create # detailed help for create
tick help --all # full reference of all commands and flags
tick create --help # same as tick help create
tick -h # same as tick help

migrate

Import tasks from external tools.

tick migrate --from <provider> [flags]
FlagTypeDefaultDescription
--fromstringrequiredProvider to import from (currently: beads)
--dry-runboolfalsePreview what would be imported without persisting
--pending-onlyboolfalseOnly import tasks not yet migrated
tick migrate --from beads
tick migrate --from beads --dry-run --pending-only

Output Formats

Tick auto-detects the context and picks the right format:

ContextDefault formatOverride
Terminal (TTY)--pretty--toon, --json
Pipe / agent--toon--pretty, --json

Agent / pipe (TOON)

$ tick list
tasks[3]{id,title,status,priority,type}:
tick-a1b2,Auth middleware,in_progress,1,feature
tick-f3e4,Write tests,open,2,task
tick-d5c6,Update docs,open,3,

Terminal (Pretty)

$ tick list
ID STATUS PRI TYPE TITLE
tick-a1b2 in_progress 1 feature Auth middleware
tick-f3e4 open 2 task Write tests
tick-d5c6 open 3 - Update docs

TOON (Token-Oriented Object Notation)

Designed for AI consumption. Schema is declared once in the header; rows are compact CSV-like lines. Uses 30-60% fewer tokens than equivalent JSON.

tasks[2]{id,title,status,priority,type}:
tick-a1b2,Setup auth,done,1,feature
tick-c3d4,Login endpoint,open,1,task
task{id,title,status,priority,type,created,updated}:
tick-a1b2,Setup auth,in_progress,1,feature,"2026-01-19T10:00:00Z","2026-01-19T14:30:00Z"
tags[2]:
backend
auth
refs[1]:
https://github.com/org/repo/issues/42
blocked_by[1]{id,title,status}:
tick-c3d4,Database migrations,done
children[0]{id,title,status}:
notes[1]{text,created}:
Discussed approach with team,"2026-01-19T14:00:00Z"
description:
Full task description here.
Can be multiple lines.

Pretty

Clean aligned columns for terminals. No borders, no colors, no icons.

ID STATUS PRI TYPE TITLE
tick-a1b2 in_progress 1 feature Setup auth
tick-c3d4 open 1 task Login endpoint

Transition & Cascade Output

When you run start, done, cancel, or reopen, the output confirms the transition. If the change cascades to related tasks, those are shown too.

Simple transition (TOON / Pretty)

$ tick start tick-a1b2
tick-a1b2: open → in_progress

Simple transition (JSON)

{
"id": "tick-a1b2",
"from": "open",
"to": "in_progress"
}

Cascade — completing a parent cascades to children:

TOON (flat lines)

$ tick done tick-a1b2
tick-a1b2: in_progress → done
tick-c3d4: open → done (auto)
tick-f3e4: done (unchanged)

Pretty (tree with box-drawing)

$ tick done tick-a1b2
tick-a1b2: in_progress → done
Cascaded:
├─ tick-c3d4 "Subtask one": open → done
└─ tick-f3e4 "Subtask two": done (unchanged)

JSON

Standard 2-space indented JSON with snake_case keys.

[
{
"id": "tick-a1b2",
"title": "Setup auth",
"status": "in_progress",
"priority": 1
}
]

Partial ID Matching

Task IDs can be abbreviated to any unique prefix. If only one task matches, it resolves automatically. This works everywhere a task ID is accepted.

tick show tick-a1 # resolves to tick-a1b2c3 if unique
tick start a1b2 # tick- prefix is optional
tick dep add a1 c3 # both IDs resolved

Storage

Tick stores data in a .tick/ directory at your project root:

  • tasks.jsonl — append-only source of truth (one JSON object per line, human-editable, git-friendly)
  • cache.db — SQLite cache (auto-rebuilt when JSONL changes, do not commit)
  • lock — file lock for safe concurrent access

Add to .gitignore:

.tick/cache.db
.tick/lock

Global Flags

--help, -h Show help (tick --help or tick <command> --help)
--version Print version and exit (equivalent to `tick version`)
--quiet, -q Minimal output (IDs only where applicable)
--verbose, -v Debug logging to stderr
--toon Force TOON format
--pretty Force pretty format
--json Force JSON format

Global flags are accepted on every command. Unknown or misspelled flags are rejected with a helpful error:

$ tick list --stauts open
unknown flag "--stauts" for "list". Run 'tick help list' for usage.

License

MIT

About

Tick is a lightweight task management Go CLI designed for AI coding agents. It prioritises determinism, simplicity, and zero-friction git integration.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages