tkx
tkx
The git-backed issue tracker for AI agents, in Crystal. tkx is a from-scratch Crystal port of wedow/ticket — same file format, same core commands, compiled into a single native binary instead of shelling out to bash/awk. Unlike upstream, there's no plugin system: every command (including ls, query, edit, and tui) is a built-in of the one binary.
Tickets are markdown files with YAML frontmatter in .tickets/. This allows AI agents to easily search them for relevant content without dumping ten thousand character JSONL lines into their context window.
Using ticket IDs as file names also allows IDEs to quickly navigate to the ticket for you. For example, you might run git log in your terminal and see something like:
nw-5c46: add SSE connection management
VS Code allows you to Ctrl+Click or Cmd+Click the ID and jump directly to the file to read the details.
Install
From source:
git clone <this-repo> tkx
cd tkx
shards build
ln -s "$PWD/bin/tkx" ~/.local/bin/tk
shards build compiles a single binary into bin/tkx. Put it on your PATH (as shown above, or just add bin/ itself to PATH).
Requirements
tkx itself is a dependency-free Crystal binary (no bash, awk, or coreutils required at runtime). The query command's optional jq-filter argument still shells out to jq if you use it — everything else, including plain query output, needs nothing beyond the compiled binary. The tui command is the one exception: it's built on tui.cr (fetched by shards install) and needs a real terminal to run in.
Agent Setup
Add this line to your CLAUDE.md or AGENTS.md:
This project uses a CLI ticket system for task management. Run `tk help` when you need to use it.
Usage
tkx - minimal ticket system with dependency tracking
Usage: tkx <command> [args]
Commands:
create [title] [options] Create ticket, prints ID
-d, --description Description text
--design Design notes
--acceptance Acceptance criteria
-t, --type Type (bug|feature|task|epic|chore) [default: task]
-p, --priority Priority 0-4, 0=highest [default: 2]
-a, --assignee Assignee
--external-ref External reference (e.g., gh-123, JIRA-456)
--parent Parent ticket ID
--tags Comma-separated tags (e.g., --tags ui,backend,urgent)
start <id> Set status to in_progress
close <id> Set status to closed
reopen <id> Set status to open
status <id> <status> Update status (open|in_progress|closed)
dep <id> <dep-id> Add dependency (id depends on dep-id)
dep tree [--full] <id> Show dependency tree (--full disables dedup)
dep cycle Find dependency cycles in open tickets
undep <id> <dep-id> Remove dependency
link <id> <id> [id...] Link tickets together (symmetric)
unlink <id> <target-id> Remove link between tickets
ready [-a X] [-T X] List open/in-progress tickets with deps resolved
blocked [-a X] [-T X] List open/in-progress tickets with unresolved deps
closed [--limit=N] [-a X] [-T X] List recently closed tickets (default 20, by mtime)
show <id> Display ticket
add-note <id> [text] Append timestamped note (or pipe via stdin)
ls, list List tickets with optional filters
query [jq-filter] Output tickets as JSON, optionally filtered with jq
edit <id> Open ticket in $EDITOR
tui Interactive terminal UI for browsing, filtering, and editing tickets
Tickets stored as markdown files in .tickets/
Supports partial ID matching (e.g., 'tkx show 5c4' matches 'nw-5c46')
Every command above is a true built-in of the single tkx binary. tkx (like upstream) also searches parent directories for .tickets/, overridable with the TICKETS_DIR env var.
See man/tkx.1 for the full command reference (man ./man/tkx.1, or man tkx if you've installed it into your MANPATH).
TUI
tk tui
tk tui is a full-screen terminal UI for browsing and editing tickets, built on tui.cr. It's a built-in command like every other, not a separate binary.
The main screen is a sortable, filterable table of every ticket:
↑↓navigate,Enteropens a ticket,/filters by substring (id, title, assignee, tags),scycles the sort column,fcycles the status filter (All → Open → In Progress → Closed)ncreates a new ticket,ddeletes the selected ticket (with an are-you-sure prompt),?shows a help popup,qquits
Enter on a ticket opens a split-screen detail view: an editable form on the left, that ticket's notes on the right. Tab switches which pane has focus. Every field commit (Enter to edit, then Esc/Enter to commit) writes straight to the ticket file immediately — there's no separate save step. a opens a small form to append a new timestamped note; d deletes the ticket currently open. Esc backs out one screen at a time.
New-ticket and add-note are the two screens that don't auto-persist as you type (there's nothing on disk yet to write to): fill in the new-ticket form and press s to save and create it, or Esc to discard the draft; for a note, Esc commits the note text and appends it, and a second Esc returns to the ticket.
Development
shards build # compile the single tkx binary into bin/
crystal spec # run the test suite (auto-builds first)
crystal tool format # format source
The test suite is plain Crystal spec — no Python/behave dependency. It's split into unit specs for the library internals (spec/frontmatter_spec.cr, spec/store_spec.cr) and integration specs that spawn the compiled binary against throwaway .tickets directories, one file per area (creation, status, dependencies, links, listing, show, notes, id resolution, directory resolution, query, edit).
Differences from upstream
This is a faithful port, not a bug-for-bug clone — the on-disk ticket format and core commands/flags match upstream, but a few rough edges were fixed rather than reproduced, and the plugin system was dropped entirely in favor of a single self-contained binary:
- No plugin system:
ls,list,query,edit, andtuiare true built-ins rather than separatetk-<cmd>executables discovered onPATH, and there's no way to add third-party plugins.migrate-beads(a one-time beads-import helper in earlier versions) was dropped outright rather than kept as a built-in. - Dependency/link add, remove, and lookup use exact ID matching instead of raw substring matching, so ids like
task-1andtask-10can't collide. queryneeds nojqat all unless you pass it a filter expression (plain output is generated with native JSON, not shelled out).- Error messages for a bad/ambiguous ID are consistent across every command (upstream's
dep tree/dep cycleused a differently-worded, unquoted variant), and usage messages always reflect however you actually invoked the binary rather than a mix of that and a hardcodedticket. - Broken pipes exit quietly (e.g.
tkx ls | head) instead of crashing with a stack trace. dep tree's box-drawing continuation lines render correctly for every branch shape. (Upstream relies on macOS's defaultawk, which mis-compares some multi-byte tree-drawing characters and occasionally drops a continuation line; Crystal's tree renderer doesn't share that bug.)
Everything else — ID generation, partial ID matching, dependency tree sorting, cycle detection, output formats — is deliberately identical, and was checked by running upstream's own BDD test suite against this implementation before the plugin system was removed.
License
MIT
tkx
- 0
- 0
- 0
- 0
- 2
- about 5 hours ago
- July 9, 2026
MIT License
Thu, 09 Jul 2026 18:57:38 GMT