tkx

ticket system for the terminal

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, Enter opens a ticket, / filters by substring (id, title, assignee, tags), s cycles the sort column, f cycles the status filter (All → Open → In Progress → Closed)
  • n creates a new ticket, d deletes the selected ticket (with an are-you-sure prompt), ? shows a help popup, q quits

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, and tui are true built-ins rather than separate tk-<cmd> executables discovered on PATH, 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-1 and task-10 can't collide.
  • query needs no jq at 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 cycle used a differently-worded, unquoted variant), and usage messages always reflect however you actually invoked the binary rather than a mix of that and a hardcoded ticket.
  • 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 default awk, 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

Repository

tkx

Owner
Statistic
  • 0
  • 0
  • 0
  • 0
  • 2
  • about 5 hours ago
  • July 9, 2026
License

MIT License

Links
Synced at

Thu, 09 Jul 2026 18:57:38 GMT

Languages