Skip to content

Latest commit

 

History

40 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

trk — an in-repo issue tracker

A small, agent-first task tracker that lives in your repo as an append-only log and projects to a generated TODO.md. Tasks are nodes wired by two edge kinds — a needs prerequisite (a DAG) and an in arc membership — and the headline query, next, returns the prereqs-met ready frontier. It is self-contained: one Zig host tool, no daemon, no database.

Why it exists: a flat prose backlog forgets prerequisites and goes stale. A real DAG makes "what's ready to work on now" a query instead of a memory exercise, and the whole surface is explorable from the tool itself (trk <verb> --help) — designed to be driven by agents as much as by humans.

Install

trk needs only a Zig toolchain and builds native for Linux, macOS, and Windows (trk.exe, chosen automatically from the build target).

git clone <this-repo> trk && cd trk
zig build -Doptimize=ReleaseSafe            # -> zig-out/bin/trk

Install onto your PATH with Zig's built-in --prefix (installs to <prefix>/bin):

zig build --prefix ~/.local -Doptimize=ReleaseSafe        # Linux/macOS -> ~/.local/bin/trk
zig build --prefix C:\Tools -Doptimize=ReleaseSafe        # Windows     -> C:\Tools\bin\trk.exe

If your PATH directory is flat (no trailing bin), drop the binary straight in with --prefix-exe-dir .:

zig build --prefix <dir> --prefix-exe-dir . -Doptimize=ReleaseSafe   # -> <dir>/trk

Re-run the same command to redeploy after pulling changes. Run zig build test to run the host test suite.

Quick start

cd your-project
trk init                          # scaffold .tracker/ + config.json + a starter TODO.md
trk add "Add dark mode" --tag ui  # create a task (prints its id)
trk next                          # the ready frontier (prereqs met)
trk render                        # regenerate TODO.md (destination from .tracker/config.json)

trk finds .tracker/ by walking up from the current directory (git-style), so it runs from any subdirectory of a project. The walk stops at a linked git worktree's root (its .git is a plain file, never a directory) — it never escapes a worktree into an enclosing repo's tracker, even when the worktree has no .tracker of its own yet. Set TRK_READONLY=1 in the environment to refuse every mutating verb outright (a belt-and-suspenders guard for a dispatched agent's env, independent of discovery).

Model in one screen

  • Task = a node with id (a ULID, minted once), title, body, state, priority, tags, doc-refs.
  • A needs B — a prerequisite edge, forming a DAG. Cycles are rejected. trk dep <needer> --needs <prereq>.
  • T in X — task T belongs to arc X. An arc is a task that's either declared (trk arc <id>, or trk add --arc — works even with zero members) or has ≥1 direct in member. trk in <task> <arc>. trk list --no-arc lists every task in no arc.
  • Stateopendonearchived (via trk archive, which graduates done tasks to changelog bullets and tombstones them), plus blocked (held) and dropped (won't-do).
  • next — the ready frontier: every open task whose prerequisites are all satisfied. An arc root is a container ("do the arc" = do its non-parked members): next holds it back until the members are finished, then surfaces it once as the close-out prompt — closing the root is what marks the goal complete and unblocks anything that needs the arc.
  • .tracker/ — the append-only log.jsonl (+ an optional compacted snapshot.jsonl). It union-merges on concurrent appends, so parallel workers on disjoint tasks can each close their own without conflict.
  • Short ids are frozen at mint time and never change. trk add prints the full ULID, and every human projection displays a short id — for a task minted going forward, that short is set ONCE and stays identical across every future add/archive/compact. A task that predates this (every task minted before it shipped) still gets a dynamically-computed short that can move as the live id set changes; run trk migrate-shorts once to freeze those in place too (current values only — see its --help). trk migrate-shorts --min <n> is a one-time REPAIR that also LENGTHENS an already-frozen short below n chars (e.g. bring a repo's ids up to match what's already been written down elsewhere) — run it once, deliberately, since it changes existing ids.

Commands

add · dep · undep · in · unin · arc · migrate-arcs · migrate-shorts · state · edit · show · next · list · render · tree · log · doc · compact · archive · init

Every verb self-documents: trk <verb> --help (or trk help <verb>) prints its synopsis, flags, and an example; bare trk prints the overview.

Config

.tracker/config.json (written by trk init) persists where render/archive write, so you don't pass --out every time:

{ "render": { "out": "docs/TODO.md" }, "archive": { "out": null } }

render truncates its target (a regenerated projection); archive appends under a ## YYYY-MM-DD heading (a changelog accumulates), so its target can safely be the changelog itself.

Precedence for the output path: explicit --out > config value > stdout. A repo with no config behaves exactly as if the fields were unset.

Design

The full rationale — the two-edge data model, the next query, the state lifecycle, the union-merge model for parallel writers, and the rejected alternatives — is in docs/design.md.

License

GPL-3.0-or-later — see LICENSE. Copyright (C) 2026 Scott Lowe.

About

trk — an in-repo, agent-first issue tracker: a task DAG that projects to TODO.md (Zig, GPLv3)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages