tlc

module
v0.0.0-...-5541f74 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 25, 2026 License: MIT

README ΒΆ

Spec Kit Logo

β˜‘οΈŽ TLC

Task

[!WARNING] 🚧 Do Not Use β€” History Will Be Rewritten 🚧

This repo is undergoing major restructuring as we selectively open-source internal tools built at Idea Crafters LLC. Git history will be force-pushed and rewritten multiple times. Do not fork, clone, or depend on this repo in any capacity until we tag a stable release.

An open source toolkit that allows you to focus on product scenarios and predictable outcomes instead of vibe coding every piece from scratch.

Release GoDoc Build Status 12-factor AI-CLI conformance GitHub stars License

β˜‘οΈŽ TLC

TLC is a high-performance, multi-agent task orchestration tool designed for developers and AI agents. It uses a human-friendly "Task Line Syntax" (TLS) to manage tasks in a flat text file while maintaining a synchronized SQLite database for advanced querying and state management.

πŸš€ Key Features

  • Hybrid Storage Model: Edit tasks directly in a human-friendly todo.txt (Task Line Syntax) or use the synchronized SQLite database for high-performance querying.
  • Recipes: Versioned YAML templates that materialize into a track and its tasks the same way every time β€” ordered steps with dependencies, a kind per step (agent, exec, human), conditions, retries, gates and due dates, plus the variables a run binds.
  • Recipe Execution: One executor runs the tasks in dependency batches, skipping on when, retrying to a bound, holding a step at an eva gate and waiting on human approval β€” with every materialization recorded in a run ledger.
  • Multi-Agent Collaboration: Safe coordination between humans and AI agents using task claiming, responsibility transfer, delegation protocols, and time-bounded ownership leases (TTL).
  • Deterministic Task Execution: Robust execution contract with stdout/stderr capture, error normalization, and configurable timeouts.
  • Audit-Ready Logging: A canonical, reverse-chronological CHANGELOG capturing every state transition, collaboration action, and execution attempt.
  • Natural Language Interface: Type tlc task "complete the auth task" instead of memorizing exact syntax. Local classifier handles common patterns instantly; LLM router (via hop.top/kit/llm) resolves complex prompts with confidence-gated execution and clarification.
  • Agent Context Dump: tlc task prompt <id> renders enriched task context (deps, track info, audit log) in markdown or JSON optimized for LLM consumption.
  • Advanced Query Engine: Power-user filtering with intuitive shorthands (@me, #tag), logical operators (AND/OR/NOT), and metadata-aware searching.
  • Extension System: Pluggable sync providers (GitHub Issues, Jira, Linear) built on kit/ext. Bidirectional integration while maintaining a strict boundary for internal agent tasks.
  • Event Bus: Decoupled state-change propagation via kit/bus. Drives audit logging, stale-detection hooks, and track auto-transitions without tight coupling between subsystems.
  • Auto-Archiving: Automatically clean up your workspace by archiving completed tasks after a configurable duration (default: 7 days), keeping your active list focused and high-performance.
  • Configurable Workflows: Define custom task statuses, state machine transitions, and per-tag workflow overrides via config. Semantic roles drive claim/unclaim/complete behavior.
  • Workspace Model: Organize multiple projects into workspaces and spaces. Query tasks across projects with --workspace and --space flags, or get a summary view with --summary.
  • Track Registry: First-class work streams that group related tasks with lifecycle management, computed health state, and phase progress. Auto-transition from pending to active on first task claim. Project health pulse with overcommit warnings.
  • Modern TUI & CLI: A keyboard-driven Terminal User Interface built with Bubble Tea, featuring a Kanban board, dashboard, and a Runs view over the recipe run ledger.
  • XDG Specification Compliance: Zero-config persistence following standard OS paths for data, logs, and configuration.

Architecture

TLC is built on hop.top/kit, a shared foundation providing:

  • kit/domain β€” Domain modeling primitives (entities, value objects, repositories) used throughout internal/core.
  • kit/bus β€” Event bus for decoupled state-change propagation. Powers audit logging, stale detection hooks, and track auto-transitions.
  • kit/ext β€” Extension system replacing the legacy plugin architecture. Sync providers (GitHub, Jira, Linear) register as extensions.
  • kit/llm β€” LLM provider abstraction for the natural language prompt router and config interactive mode.
  • kit/tui β€” Shared TUI components (themes, layouts) used by the Bubble Tea interface.

πŸ›  Installation

Quick Install (Build from source)

curl -fsSL https://raw.githubusercontent.com/hop-top/tlc/main/install.sh | bash

Build manually

# Clone the repository
git clone https://github.com/hop-top/tlc.git
cd tlc

# Build the binary
mkdir -p bin
go build -o bin/tlc cmd/tlc/main.go

Docker

Run TLC in a containerized environment with persistent storage:

# Build the image
docker build -t tlc-cli .

# Run interactively with TUI
docker run -it --rm \
  -v tlc-data:/home/tlc/.local/share/tlc \
  -v tlc-config:/home/tlc/.config/tlc \
  tlc-cli tui

# Run one-off commands
docker run --rm \
  -v tlc-data:/home/tlc/.local/share/tlc \
  tlc-cli task list

# Or use docker-compose
docker-compose up -d
docker-compose exec tlc tlc tui

For more Docker usage examples, see docs/docker.md.

πŸ§ͺ Testing

Run all tests

go test ./...

Generate Coverage Report

./scripts/coverage.sh

The HTML report will be available at coverage/all.html.

πŸ‘¨β€πŸ’» Development

Quick Start

maketools    # Install dev tools (golangci-lint, air)
makedev      # Run all checks (format, lint, test)
makewatch    # Auto-rebuild on changes

See docs/development-setup.md for comprehensive development workflow, watch modes, and editor integration.

Available Targets

Command Description
makebuild Build binary to bin/tlc
maketest Run tests with race detector
makelint Run golangci-lint
makefmt Format code with gofmt + goimports
makewatch Watch and rebuild on changes
makewatch-lint Watch and lint on changes
makedev Run format + lint + test
maketools Install development tools
makecheck Run lint + test (CI-like)
makevalidate-docs Validate doc command examples

Watch Mode (Auto-detection)

Get instant feedback while coding:

makewatch-lint  # Lint on file save
makewatch       # Build on file save
makewatch-test  # Test on file save

πŸ“– Usage

Quick Start

Initialize a new project:

./bin/tlc init

AI Agent Integration

Configure AI agents to use TLC for task management:

For Claude Code users:

Add this to your global Claude configuration (~/.claude/CLAUDE.md):

## Task Management with TLC

Use TLC (Task Line CLI) for all task tracking instead of TodoWrite.

**Setup (run once per project):**
- Check if initialized: `ls .tlc/` or run `tlc init` if not found
- Optional GitHub sync: Run `tlc sync pull github` or `tlc sync push github` to auto-configure (syncs with GitHub Issues)

**Common commands:**
- Create: `tlc task create "Task title" --assigned-to @me --tag feature`
- Create blocked task: `tlc task create "Task title" --blocked-by T-0041 --blocked-by other-project/T-0007`
- List: `tlc task list --mine` (shows IN_PROGRESS first, then TODO by default)
- List stale: `tlc task list --stale` (only tasks past their stale threshold)
- Stale subcommand: `tlc task stale` (dedicated stale view; fires hooks with `--run-hooks`)
- List blocked: `tlc task list --blocked` (only tasks with a blocked reason)
- List by priority: `tlc task list --priority P0` or `--priority P0,P1`
- Group a listing: `tlc task list --group-by track` (one table per track, assignee, tag, status, priority, or project)
- Cap rows per group: `tlc task list --group-by track --group-limit 5`
- List blocked by: `tlc task list --blocked-by T-0041` (tasks that depend on T-0041)
- Update: `tlc task update T-0042 --status IN_PROGRESS`
- Update blockers: `tlc task update T-0042 --add-blocked-by T-0009 --remove-blocked-by T-0003`
- Mark blocked: `tlc task update T-0042 --blocked "waiting on T-0009"`
- Clear blocked: `tlc task update T-0042 --unblock`
- Set stale timeout: `tlc task update T-0042 --timeout 2h`
- Create with timeout: `tlc task create "Task title" --timeout 4h`
- Create with due date: `tlc task create "Task title" --due tomorrow`
- Create with reminder: `tlc task create "Ship v2" --due "in 3d" --remind-every 1h`
- Suppress auto-remind: `tlc task create "Review" --due friday --no-auto-remind`
- Update due date: `tlc task update T-0042 --due "2025-05-01"`
- Clear due date: `tlc task update T-0042 --due -`
- View reminders: `tlc task remind` (upcoming + overdue)
- Check overdue: `tlc task remind --check` (exits 1 if overdue)
- Complete: `tlc task complete T-0042`
- Assign: `tlc task assign T-0042 codex`
- Claim: `tlc task claim T-0042`
- View logs: `tlc log T-0042`
- Track create: `tlc track create "Title" --type feature`
- Track list: `tlc track list --status active`
- Track show: `tlc track show <id>`
- Track summary: `tlc track summary`
- Link task to track: `tlc task create "Title" --track <track-id>`

**Tool definition:** Run `tlc schema --format mcp` for MCP tool schema.

For other AI platforms:

Add to your project's AGENTS.md file:

## Task Management

This project uses TLC for task tracking.

Setup: `tlc init`
Tool definition: `tlc schema` (or `--format openai` for OpenAI platforms)

Natural language prompts:

Use freeform text instead of exact CLI syntax:

# Classifier handles common patterns instantly (confidence 1.0)
tlc task "complete T-42"
tlc task "list my tasks"
tlc task "show T-42"

# Complex prompts route to LLM (requires LLM provider config)
tlc task "create 3 tasks for auth: login, logout, refresh"

# Flags
tlc task "mark the auth task done" --dry-run   # preview only
tlc task "delete T-42" --execute               # skip confirmation
tlc task "list blocked tasks" --json           # JSON output

Configure the LLM provider:

export TLC_PROMPT_LLM="ollama://llama3.2"  # or anthropic://, openai://

Cross-domain resolution (no LLM): Common track, recipe, and project queries resolve deterministically without LLM. Examples: "list active tracks", "list recipes", "count active tracks" β†’ track list --status active. Typo-tolerant: "list trakcs" resolves to track list (fuzzy match, confidence 0.8).

Agent context dump:

Get enriched task context for LLM consumption:

tlc task prompt T-0042           # markdown (default)
tlc task prompt T-0042 --json    # structured JSON

Output includes: task fields, blocked-by/blocking summaries, track details with phase, and recent audit log entries.

Managing Tasks

List active tasks (IN_PROGRESS first, then TODO):

./bin/tlc task list

List archived tasks:

./bin/tlc task list --archived

Claim a task for work:

./bin/tlc task claim T-0042

Release a claimed task:

./bin/tlc task unclaim T-0042

Complete a task:

./bin/tlc task complete T-0042

Assign or unassign a task:

./bin/tlc task assign T-0042 codex
./bin/tlc task unassign T-0042 --note "reason"

Create a new task:

./bin/tlc task create "Design API schema" --assigned-to engineer-1 --tag infra

Update a task status:

./bin/tlc task update T-0042 --status DONE

Stale Task Detection

Tasks are stale when not updated within their timeout. Use tlc task stale to list them:

# List stale tasks (uses per-task timeout; falls back to task.stale.default_timeout config)
tlc task stale

# List stale tasks and fire configured hooks (sets StaleFiredAt; re-fires even if set)
tlc task stale --run-hooks

Configure project-wide defaults and hooks in .tlc.yaml:

task:
  stale:
    default_timeout: 6h           # applied to tasks with no per-task timeout
    hooks:
      - command: 'echo "stale: {{.ID}} ({{.Title}})" >> /tmp/stale.log'

Per-task timeout via CLI:

tlc task update T-0042 --timeout 2h
tlc task create "Design API schema" --timeout 4h

Due Dates & Reminders

Set deadlines and reminders on tasks:

# Due date (flexible parsing: "tomorrow", "in 3d", "+2w", "friday", ISO 8601)
tlc task create "Ship v2" --due "in 3 days"
tlc task update T-0042 --due friday
tlc task update T-0042 --due -                    # clear due date

# One-shot reminder
tlc task create "Review PR" --remind-at "2025-05-01T14:00:00Z"

# Recurring reminder
tlc task create "Check CI" --remind-every 1h

# Auto-remind: 12h before due (on by default)
tlc task create "Release" --due "2025-05-01" --no-auto-remind  # suppress

# View upcoming + overdue
tlc task remind

# Scriptable overdue check (exit 1 if any overdue)
tlc task remind --check

Due dates appear in task list (Due column) and task show (overdue indicator). Overdue tasks are prefixed with ! in list output.

Auto-Scheduling by Priority

Configure automatic due dates and reminders per priority level in .tlc/config.yaml:

task:
  scheduling:
    by_priority:
      P0:
        due: 24h
        remind_every: 2h
      P1:
        due: 72h
        remind_every: 12h
      P2:
        due: 168h
        remind_every: 24h
    age_nudges:
      - status: IN_PROGRESS
        threshold: 48h
        action: remind
      - status: TODO
        threshold: 168h
        action: remind

Priority defaults apply at task creation β€” if --due is not explicitly set and the task has a priority, the configured duration is added to created_at. Explicit --due always takes precedence.

Age nudges trigger in tlc task remind for tasks without explicit due dates. Surfaced as "AGE NUDGES" section.

Workspaces

Organize projects into workspaces for cross-project task management:

# List workspaces and their projects
tlc workspace list

# Query tasks across a workspace
tlc task list --workspace myws

# Filter to a specific space
tlc task list --workspace myws --space labs

# Summary view (grouped counts by status)
tlc task list --summary

Grouping listings

Split a listing into one titled table per group with --group-by:

tlc task list --group-by track
Adoption rollout
ID      Title                      Status  Assigned  Due  Stale  Blocked
T-0004  Ship rollout toggle        To Do   -         -    -      -
T-0001  Wire adoption metrics      To Do   ann       -    -      -
T-0002  Draft rollout plan         To Do   bob       -    -      -
T-0003  Measure activation funnel  To Do   ann       -    -      -

Billing migration
ID      Title                Status  Assigned  Due  Stale  Blocked
T-0005  Port billing schema  To Do   bob       -    -      -

(none)
ID      Title            Status  Assigned  Due  Stale  Blocked
T-0006  Untracked chore  To Do   -         -    -      -

Group by track, assignee, tag, status, priority, or project.

Headings show names, not internal IDs: a track renders its title, an assignee its resolved aps profile. A track that has since been deleted falls back to its raw ID rather than an unnamed section.

Tasks with no value for the dimension collect into a (none) section, always last β€” no track, no assignee, no tags, no priority. Named groups lead; the leftovers trail.

Ordering is by group name ascending, except status and priority, which follow your configured lifecycle order (so TODO precedes IN_PROGRESS rather than sorting alphabetically). The order is stable across runs.

Tags duplicate rows

Tag membership is many-to-many, so a task tagged [api, urgent] appears under both groups. When that makes the rendered row count exceed the number of distinct tasks, a footer says so:

tlc task list --group-by tag
api
ID      Title                  Status  Assigned  Due  Stale  Blocked
T-0005  Port billing schema    To Do   bob       -    -      -
T-0001  Wire adoption metrics  To Do   ann       -    -      -
T-0002  Draft rollout plan     To Do   bob       -    -      -

docs
ID      Title                      Status  Assigned  Due  Stale  Blocked
T-0003  Measure activation funnel  To Do   ann       -    -      -

urgent
ID      Title               Status  Assigned  Due  Stale  Blocked
T-0002  Draft rollout plan  To Do   bob       -    -      -

(none)
ID      Title                Status  Assigned  Due  Stale  Blocked
T-0004  Ship rollout toggle  To Do   -         -    -      -
T-0006  Untracked chore      To Do   -         -    -      -

7 rows, 6 distinct tasks

The footer is omitted when the two counts agree.

Capping rows per group

--limit caps the match set fetched before grouping, so one large group can crowd out the rest. --group-limit caps the rows shown within each group, keeping every group represented:

tlc task list --group-by track --group-limit 2
Adoption rollout (2 of 4)
ID      Title                  Status  Assigned  Due  Stale  Blocked
T-0004  Ship rollout toggle    To Do   -         -    -      -
T-0001  Wire adoption metrics  To Do   ann       -    -      -

Billing migration
ID      Title                Status  Assigned  Due  Stale  Blocked
T-0005  Port billing schema  To Do   bob       -    -      -

(none)
ID      Title            Status  Assigned  Due  Stale  Blocked
T-0006  Untracked chore  To Do   -         -    -      -

A truncated group announces it as (shown of total). A group that fits under the cap is not annotated. --group-limit needs --group-by; on its own it is rejected rather than silently ignored.

JSON output nests

With --group-by, -f json (and -f yaml) wrap the results in groups:

tlc task list --group-by track -f json
{
  "groups": [
    {
      "name": "Adoption rollout",
      "tasks": [
        {
          "id": "task_01m29y23gcfkvvwvn1fypmqwvy",
          "seq": 4,
          "title": "Ship rollout toggle",
          "status": "TODO",
          "assigned_to": null,
          "track_id": "track_01m29y22cvecktj19v6edcfpdb"
        }
      ]
    }
  ]
}

(Task objects are shown abbreviated; each carries the same fields as an ungrouped listing. A group with no tasks serializes "tasks": [], never null.)

Group names match the table headings, and group order matches the rendered order. Without --group-by the payload stays the flat array existing consumers iterate, so adding the flag is opt-in.

--group-limit does not apply to structured output β€” it caps what a screen shows, and a truncated list inside a payload carries no marker of its truncation. Passing both prints a note on stderr and emits every task.

Not combinable with aggregates

--group-by lists rows; --summary and -f counters collapse the match set into counts. Combining them is rejected:

$ tlc task list --group-by track --summary
--group-by cannot be combined with --summary: --group-by lists rows in one table per track, --summary counts the whole match set; pick one

Tracks

Organize related tasks into tracks β€” cohesive work streams with their own lifecycle, progress, and health state:

# Create a feature track
tlc track create "Browser rendering" --type feature

# Link tasks to the track
tlc task create "Parse HTML" --track browser-rendering --tag phase:1
tlc task create "Render DOM" --track browser-rendering --tag phase:1
tlc task create "CSS engine" --track browser-rendering --tag phase:2

# View track progress with phase breakdown
tlc track show browser-rendering
# Output:
#   Track: browser-rendering
#   Progress: 1/3 tasks (33%)
#   Phase 1 β€” 1/2
#   Phase 2 β€” 0/1

# List tracks with state and progress
tlc track list --status active
# Output: ID, Title, Type, Status, State, Progress, Assignee

# Project health pulse
tlc track summary
# Shows: status counts, overcommit warning, active track table

# Link a plan with automatic task extraction
tlc track update browser-rendering \
  --add-plan docs/plans/rendering.md

# A plan's frontmatter may express blocked-by as:
#   - 0           # intra-track 0-based index into this plan
#   - "T-0001"    # an existing concrete task ID
#   - "other-track#2"  # cross-track ref: task #2 in "other-track"'s plan
# Cross-track refs require the target track to already be ingested;
# resolved refs are rewritten back into plan.md on disk.

# Lifecycle commands
tlc track update browser-rendering --status completed
tlc track archive browser-rendering

Track statuses: pending -> active -> completed/abandoned -> archived. A pending track auto-transitions to active when a linked task is claimed.

Configure health thresholds in .tlc/config.yaml:

tracks:
  stale_threshold: 48h
  health:
    max_active: 3
    min_progress_to_start: 50

Recipes

Recipes are versioned YAML templates that create tracks and tasks the same way every time: an ordered list of steps with dependencies, kinds (agent, exec, human), retries, gates and due dates, plus the variables a run binds.

recipe: code-review
version: 1.2.0
description: Review a pull request
vars:
  pr: {description: PR number, required: true}
  depth: {default: standard}
steps:
  - id: lint
    kind: exec
    exec: {argv: [golangci-lint, run], timeout: 5m}
  - id: review
    title: "Review {{pr}} ({{depth}})"
    description: "Lint said: {{results.lint.stdout}}"
    depends_on: [lint]
    when: "results.lint.exit_code == 0"
    retry: {max_attempts: 3, backoff: 30s}
  - id: sign-off
    kind: human
    human: {assignee: "@lead", timeout: 48h, on_timeout: reject}
    depends_on: [review]
  • Templates: {{var}}, {{subject.title}}, {{run.iteration}} and {{steps.<id>.<field>}} are rendered when tasks are created; {{results.<id>.<field>}} and when: are evaluated at dispatch from upstream task results. Due dates compose: due: "{{steps.planning.due}} + 5d" or due: {after: planning, offset: 5d}.
  • Composition: include: security-scan@1.0 with with: {target: "{{pr}}"} splices another recipe in as <id>/<step>; repeat: 3 with until: "results.review.verdict == 'approved'" unrolls a block into bounded iterations.
  • Search path: recipe.dir from .tlc/config.yaml, then .tlc/recipes/, then ~/.config/tlc/recipes/. Reference a recipe by name, name@version or file path.
tlc recipe list                       # every recipe in the search path
tlc recipe show code-review           # header, vars and expanded steps
tlc recipe show code-review -f json   # the expanded document
tlc recipe validate ./my-recipe.yaml  # exit 1 with the first problem

Testing a recipe β€” tlc recipe test <recipe> [run] materializes the recipe into a throwaway store and executes it inside a sandbox: a clone of the working tree, shim binaries ahead of the real ones on PATH, and a home of its own. Replay serves every tool call from a recorded cassette, so a run is deterministic and offline; --record reaches the real binaries and writes the cassettes. Runs live in fixtures/<recipe>/<run>/ beside the recipe, with record/ cassettes per step id, optional contracts/ for eva assertions, and a test.yaml declaring expected_exit and vars. A missing cassette exits 2.

Creating from a recipe β€” four verbs share one pipeline: locate, expand, bind vars, select steps, render, materialize. Each pass is recorded as a run (which recipe, version, vars, subject, selection) so a later pass can tell what already exists.

# A new track: title from the argument or the recipe's track.title,
# type from --type or track.type, plan.md from track.plan.
tlc track create --recipe release --var version=1.4.0
tlc track create "Review 42" --recipe code-review --var pr=42,depth=deep

# Tasks only: into a track, for a subject, or trackless.
tlc task create --recipe code-review --var pr=42 --track review-42
tlc task create --recipe fix-bug --for T-0042      # into T-0042's track
tlc task create --recipe fix-bug --for auth        # into the auth track
tlc task create --recipe code-review --var pr=42    # trackless

# Create for a task, then run the created tasks.
tlc task execute T-0042 --recipe fix-bug --var branch=main

# Bring a track up to date with its recipe, then run it.
tlc track execute review-42 --recipe code-review
tlc track execute review-42 --recipe code-review --recreate
  • Vars: --var key=value[,key=value] (repeatable). A required var without a value is prompted for on a terminal; otherwise the command fails naming every missing var and the --var fix. --infer is reserved and errors as not implemented yet.
  • Selection: --task 1-5,7, --task lint (repeatable) keeps a subset of the expanded steps; ordinals are the ones recipe show prints. A dependency on an unselected step is dropped and warned about, or pulled in with its closure by --with-deps. The selection and the dropped edges are recorded on the run.
  • Subjects: --for <task-or-track> binds {{subject.id}}, {{subject.title}}, {{subject.description}}, {{subject.track}}, {{subject.tags}} and {{subject.project}}. requires: {subject: task} makes a task subject mandatory, track a track one. A task subject is blocked on the run's leaves (steps nothing else in the run depends on), and track execute / task execute complete it once they are done.
  • Assignment: --assign runs the assignment engine over recipe.assignees_dir for every step that names no assignee.
  • Reconcile: track execute <track> --recipe <r> reads the track's run ledger for that recipe, creates only the steps not materialized yet, and warns when the recipe's version or content changed since the latest run. A step whose task was deleted is reported and left alone unless --recreate. Vars are the latest run's, overridden by --var.
  • Dry run: --dry-run on any of the four prints what would be created (and, on track execute, the batch plan) without writing.
  • Output: --format json renders the run id, created and skipped steps with their task ids, warnings, and the track.
Capturing and inspecting recipes

A track that already works can be captured as a recipe: one step per task in dependency order, ids from recipe provenance or the task titles, depends_on from the blocked-by edges inside the track. --var name=value lifts every whole-word occurrence of value back into {{name}} and declares the var; the vars of the track's latest recipe run are lifted the same way. A markdown procedure reachable by URL is converted by the model behind LLM_API_KEY and kept verbatim as track.plan.

tlc recipe import release-flow                    # writes ./release-flow.yaml
tlc recipe import release-flow --var pr=1234      # "Review PR 1234" β†’ "Review PR {{pr}}"
tlc recipe import release-flow --install          # into .tlc/recipes/release-flow.yaml
tlc recipe import release-flow -o - --name release --version 1.0.0
tlc recipe import https://github.com/org/repo/blob/main/docs/release.md

Every materialization is recorded in a run ledger. recipe runs lists it; recipe diff walks a recipe's steps against the runs on a track and reports each step as materialized, missing (never created), deleted (task since removed) or extra (created by the run but no longer in the recipe), noting version and content drift between the file and the run.

tlc recipe runs                                   # newest first, with task counts
tlc recipe runs code-review --track release-flow  # one recipe, one track
tlc recipe runs --all-projects
tlc recipe diff release-flow                      # against the latest run's recipe
tlc recipe diff release-flow --recipe ./code-review.yaml

Assignees

Assignees are specialized executor profiles that the assignment engine matches against a task's requirements:

List available assignees:

./bin/tlc assignee list

View assignee details:

./bin/tlc assignee show assignee:code-analyst:1.0

See docs/recipes.md for how --assign picks one.

Agent Execution

TLC supports delegating tasks and tracks to registered AI agents:

Run agents directly:

tlc agent run --agent code-analyst --task T-0042
tlc agent run --agent code-analyst --track browser-rendering

Execute from task/track context:

tlc task execute T-0042 --agent code-analyst
tlc track execute browser-rendering --agent code-analyst

Agent protocol: every run receives its context file path in TLC_CONTEXT_PATH and must write its results file to TLC_RESULTS_PATH (container: /workspace/.tlc/context.json and results.json; local: a per-run .tlc/runs/<run-id>/ directory under the repo root), then print a JSON status line on stdout. tlc track execute --concurrency N runs container agents in parallel, one pod each; local agents share the working tree and run one at a time.

Monitor and manage agent runs:

tlc agent status <job-id>     # check async job status
tlc agent cancel <job-id>     # cancel queued job
tlc agent list                # list agent run audit records
tlc agent watch               # live tail of agent output

Agent registry (.tlc/agents.yaml):

agents:
  code-analyst:
    image: ghcr.io/org/code-analyst:latest
    binary: ./bin/analyst
    env:
      TIMEOUT: "300"
    default_timeout: 300
  engineer:
    image: ghcr.io/org/engineer:latest
    binary: ./bin/engineer
    env:
      TIMEOUT: "600"
    default_timeout: 600

Modes:

  • Container mode is the default (uses image from agents.yaml)
  • --local β€” opt-in: run agent in-process using binary path

Exec-kind tasks under tlc track execute:

  • Host by default: the argv runs in the working directory with exec.cwd, env, timeout and stdout_max all enforced.
  • --with-pod passed explicitly (--with-pod or --with-pod=<image>) runs each exec task in a fresh pod from that image, else the --agent's image: the working tree is copied to /workspace, exec.env is set at pod creation and exec.cwd resolves under /workspace.
  • What the pod path cannot cap: exec.timeout ends the wait and kills the local pod client, but the protocol cannot signal the remote command (it dies with the pod, destroyed right after); exec.stdout_max is applied after the transfer, so the whole output is still buffered on the host before it is cut; an empty env value cannot unset a variable the image provides; files the command writes stay in the pod.

Interactive TUI

Launch the interactive terminal interface:

./bin/tlc tui

Keybindings:

  • j/k: Navigate tasks
  • enter: View task details (including markdown description)
  • n: Create new task (interactive form)
  • c/u: Claim / Unclaim task
  • s: Cycle task status
  • /: Search/Filter tasks
  • t: Open theme picker
  • v: Cycle views (Dashboard -> Kanban -> Runs)
  • r: Refresh data
  • q: Quit

🎨 Theme Customization

TLC features a powerful theme switcher that allows you to customize your terminal experience.

  • Built-in Themes: Start with the high-contrast default theme.
  • 250+ Community Themes: Pull themes directly from the popular mbadolato/iTerm2-Color-Schemes repository.
  • Lazy Loading: The picker fetches theme names instantly and downloads color data only when you highlight a theme, ensuring a smooth experience even with hundreds of options.

Inside the Theme Picker:

  • j/k: Scroll through themes
  • R (Shift+R): Fetch/Refresh the full list of 250+ remote themes
  • enter: Apply and save the selected theme
  • esc: Cancel and return to dashboard

Local Configuration

You can use a local configuration file to override global settings:

./bin/tlc --config tlc-local.yaml task list

Multi-Clone Workflows

TLC supports working with the same repository in multiple clones (different machines, worktrees, or branches). It automatically detects your project from git remote and provides flexible options for managing tasks across clones.

Automatic Project Detection:

When you run any TLC command in a clone without explicit configuration:

cd ~/repo-clone
tlc task list
# TLC detects project from git remote and tasks work correctly

Choosing Between Shared or Isolated Tasks:

When running tlc init in a clone, you can choose how tasks are managed:

# Share tasks across all clones (default)
tlc init --duplicate-id-strategy share

# Isolate tasks for this clone
tlc init --duplicate-id-strategy unique

# Ask interactively
tlc init --duplicate-id-strategy prompt

Common Multi-Clone Scenarios:

  • Feature Branch Worktrees: Isolated tasks per branch

    cd ~/main-repo
    tlc init --duplicate-id-strategy unique
    git worktree add ../feature-X feature-X
    cd ../feature-X
    tlc init --duplicate-id-strategy unique  # Isolated tasks
    
  • Multiple Machines: Shared tasks across devices

    # Machine A (work laptop)
    tlc init --duplicate-id-strategy share
    
    # Machine B (home computer)
    tlc init --duplicate-id-strategy share  # Same tasks
    
  • CI/CD Environments: Auto-detect without config files

    export TLC_PROJECT_FALLBACK_MODE=detected
    tlc task list  # Works in CI without .tlc directory
    

For detailed multi-clone setup guides, see:

Configuration Reference:

Add to your .tlc/config.yaml:

project:
  id: "user/my-repo"              # Auto-detected from git remote
  fallback_mode: "auto"            # auto, detected, or prompt
  duplicate_id_strategy: "share"     # share, unique, or prompt

πŸ“ Task Line Syntax (TLS)

Tasks are stored in a canonical single-line format: [status] <ID> <Title> @assignee #tag prio:<P> domain:<D> ref:<R>

Example: [~] T-0002 Fix token refresh race @engineer-3 #auth #bug ref:docs/rfc/012.md

πŸ”– Identifiers

Tasks and tracks each carry a durable internal ID and a short display alias. You type the alias; TLC stores the durable ID.

Entity Type to refer to it Also accepted Durable identity
Task T-0042 42, task_01h455vb4pex5vsknk084sn02q task_<typeid>
Track L-0002 slug, track_01h455vbqkfsn02nk084ksn02q track_<typeid>

Aliases are numbered per project and stay fixed once assigned β€” L-0002 keeps pointing at the same track for the life of that track. They are unique inside a project, so the same alias may exist in another project. Alias input is case-insensitive: l-0002 resolves like L-0002.

The track_<typeid> form is the durable identity. It never changes and is unique across every project, so use it when a reference must survive being copied between projects.

Track slugs

A track also has a slug β€” the readable name you see in prose and on disk. The slug is the directory name under .tlc/tracks/:

.tlc/tracks/browser-rendering/
    metadata.json
    plan.md

That is why tracks keep a slug alongside L-NNNN: tracks/L-0002/ would tell you nothing in a file listing or a diff.

Both forms address the same track, so use whichever reads better:

tlc track show L-0002               # by alias
tlc track show browser-rendering    # by slug

tlc track list shows both, so you can copy either one:

tlc track list
# ID      Slug               Title                  Type     Status   State     Progress  Assignee
# L-0002  render-v2          Browser rendering two  feature  pending  unlinked  0/0       -
# L-0001  browser-rendering  Browser rendering      feature  active   unlinked  0/0       -

On a narrow terminal the Slug column is the first to be dropped, and a long slug is clipped with an ellipsis to keep the other columns on screen. That is display only β€” tlc track show and -f json always carry the full slug, so a script never reads a clipped value.

tlc track create derives the slug from the title, or takes one via --id:

tlc track create "Browser rendering" --type feature
# slug: browser-rendering

tlc track create "Browser rendering" --type feature --id render-v2
# slug: render-v2

Slugs are lowercase alphanumerics and hyphens, starting and ending with an alphanumeric.

Slug length

New slugs are capped at 24 characters by default. A derived slug is cut at a word boundary, so it stays readable:

tlc track create "Config driven label templates and workflow wiring" --type feature
# slug: config-driven-label

Change the cap with tracks.slug_max_len:

# .tlc/config.yaml
tracks:
  slug_max_len: 32           # default: 24

The cap applies only to slugs being created. Tracks created before you set it β€” or under a longer cap β€” keep their names and keep resolving; TLC never renames a track directory. Only a new slug over the cap is rejected:

tlc track create "Legacy" --type feature --id config-driven-label-templates-and-workflow-wiring
# error: track slug "config-driven-label-templates-and-workflow-wiring" too long (max 24 chars)

βš™οΈ Configuration

TLC follows a hierarchical configuration:

  1. Environment variables (TLC_*)
  2. Local config (.tlc/config.yaml or custom --config)
  3. User config (<os user config dir>/tlc/config.yaml)
  4. System config (/etc/tlc/config.yaml)
  5. Built-in defaults

Write behavior:

  • tlc config set writes to the active local or explicit --config file.
  • If only system config is loaded, or no config file exists yet, TLC writes the new value to the OS user config path instead of /etc/tlc/config.yaml.

Defaults:

  • Todo file: $XDG_DATA_HOME/tlc/todo.txt
  • Database: $XDG_DATA_HOME/tlc/db.sqlite
  • Logs: $XDG_DATA_HOME/tlc/tlc.log
  • Archive threshold: 168h (7 days)

Custom Paths

All directory paths are configurable. When omitted, defaults match the original hardcoded values so existing projects work unchanged.

# .tlc/config.yaml
tracks:
  dir: docs/tracks             # default: "tracks" (relative to .tlc/)
  slug_max_len: 24             # default: 24 (new slugs only)

recipe:
  dir: recipes                 # default: none; then .tlc/recipes, ~/.config/tlc/recipes
  assignees_dir: team           # default: "examples/assignees"

task:
  todo_file: TODO.md            # default: "todo.txt"
  projection_dir: work-items    # default: "tasks"

storage:
  db_path: data/store.db        # default: "db.sqlite"
  inbox:
    dir: incoming               # default: "inbox"

Paths must be relative (resolved from project root). This is useful for mono-repo layouts where .tlc/ sits at the repo root but artifacts live in a shared directory:

# mono-repo example: .tlc/config.yaml at repo root
tracks:
  dir: packages/project-a/tracks
recipe:
  dir: packages/project-a/recipes

πŸ“š Documentation

New to TLC? Start with Getting Started Guide for a progressive learning path from basic tasks to advanced workflows.

Detailed specifications can be found in the docs/ directory:

Features & Guides:

Plans:

Specifications:

Contributing

Contributions are welcome! Check the open issues for things to work on, or open a pull request with your improvements. All PRs are appreciated -- whether it is a bug fix, new feature, or documentation update.

Feedback

Found a bug or have a feature request? Open an issue on the GitHub Issues page. For questions and general discussion, use GitHub Discussions.

License

MIT

Directories ΒΆ

Path Synopsis
cmd
genfixtures-vtodo command
Command genfixtures-vtodo regenerates the .ics test fixtures under tests/fixtures/vtodo.
Command genfixtures-vtodo regenerates the .ics test fixtures under tests/fixtures/vtodo.
tlc command
Package main provides the TLC CLI entry point.
Package main provides the TLC CLI entry point.
internal
cli
Package cli: `tlc agent register|show|registered` subcommands.
Package cli: `tlc agent register|show|registered` subcommands.
displaytime
Package displaytime is the single tlc path for rendering absolute time values to user-facing output.
Package displaytime is the single tlc path for rendering absolute time values to user-facing output.
events
Package events defines topic constants and payload types for the tlc event bus.
Package events defines topic constants and payload types for the tlc event bus.
extensions
Package extensions provides the tlc-specific ext.Manager bootstrap layer.
Package extensions provides the tlc-specific ext.Manager bootstrap layer.
flowtest
Package flowtest provides deterministic e2e testing for tlc recipes: a hermetic sandbox, cassette-backed tool shims, and the named runs that drive `tlc recipe test`.
Package flowtest provides deterministic e2e testing for tlc recipes: a hermetic sandbox, cassette-backed tool shims, and the named runs that drive `tlc recipe test`.
flowtest/shims/shimlib
Package shimlib provides the shared logic for LLM shim binaries used by the flowtest sandbox.
Package shimlib provides the shared logic for LLM shim binaries used by the flowtest sandbox.
flowtest/shims/shims
Package shimlib provides the shared logic for LLM shim binaries used by the flowtest sandbox.
Package shimlib provides the shared logic for LLM shim binaries used by the flowtest sandbox.
rpc
Package rpc provides a lightweight JSON-RPC 2.0 client for communicating with external binary plugins over stdio.
Package rpc provides a lightweight JSON-RPC 2.0 client for communicating with external binary plugins over stdio.
tui
uri
uriutil
Package uriutil provides shared URI helpers that both internal/core and internal/uri need.
Package uriutil provides shared URI helpers that both internal/core and internal/uri need.
vtodo
Package vtodo encodes and decodes tlc Task/Track/LogEntry models to and from RFC 5545 (iCalendar) + RFC 9253 (RELATED-TO RELTYPE) wire format.
Package vtodo encodes and decodes tlc Task/Track/LogEntry models to and from RFC 5545 (iCalendar) + RFC 9253 (RELATED-TO RELTYPE) wire format.
plugins
github-sync command
jira-sync command
linear-sync command
vtodo-sync command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL