Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

171 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TLR - Tech Lead Reporter

TLR, pronounced "Teller"

Linear tracks the current state of every issue. It does not tell you how the plan moved since last week, whether a milestone will land on its target date, who is overloaded next cycle once on-call and time off are counted, or which ticket descriptions read as AI slop. tlr answers those from the same data. It keeps a local snapshot history so it can diff the plan over time, models per-person capacity against the dependency graph, and gives you a reviewed, deterministic path for batch edits so AI-made changes and sloppy text do not reach a wider audience unchecked.

One Deno/TypeScript core owns every read, the snapshot store, and the change model. A vanilla web app and a CLI sit on top, so neither talks to Linear on its own terms. See ARCHITECTURE.md for the shape and how each part works, ROADMAP.md for what is next, and AGENTS.md for where to start.

What it does beyond Linear

  • Plan-level diff over time, the one thing Linear structurally cannot show, because its history is per-issue and never milestone-scope-over-time. tlr snapshots project state locally and diffs two captures
  • Capacity forecast per person per cycle, deflated for on-call weeks (Incident.io) and days out of office (Google Calendar), with per-person velocity from past-cycle throughput
  • Dependency waves and chain risk from the blocking graph: a chain runs one ticket at a time, so its points are charged to the people who own it and compared against the time left before the milestone it is aimed at
  • Milestone slip forecast, a realistic landing date against the target from remaining scope and team throughput, always labeled a forecast and never a real date
  • Balance proposal, a deterministic assignee-and-cycle plan for unscheduled work under a per-person point ceiling (deflated for on-call and OOO), keeping a dependency chain with one owner and reporting which milestones the estimates say will land late. Reviewable and applicable from the Balance page
  • Weekly-update narrative (shipped, moved, at risk) generated from a plan-level diff
  • Slop scan of ticket text for AI tells (dashes, stock phrases, checklists, length), with a review queue for recent edits and a way to mark each one reviewed
  • Review-and-fix loop for bulk AI changes: the Review page groups every change to a ticket, lets you clear each as reviewed, and edits a ticket in place (title, description, estimate, priority, milestone, status, cycle, assignee). It previews the change first (a dry run), then writes it to Linear on confirm. That is the only path tlr has to Linear, and it only runs from the UI, because bulk edits already go through the Linear MCP in Claude Code and tlr's job is to catch and fix what they got wrong

The board

Capacity heat per person against cycles and milestones, with slop, chain-risk, and missing-data flags. Milestone headers carry a slip marker and move the detail to the hover.

The planning board

Changes steps through snapshot history by capture or by day and writes the weekly update.

The changes page

Review runs from your last review to the newest capture, groups every change to a ticket into one unit, and lets you mark it reviewed. Editing a ticket opens a modal (title, description, estimate, priority, milestone, status, cycle, assignee) whose right column shows what the edit costs: the owner's load in the target cycle before and after, any milestone whose forecast landing moves, blockers and blocked work with the chain they sit in, and a slop scan of the rewritten description. Preview is a dry run, so nothing reaches Linear until you confirm.

The review page

Roadmap puts every ticket on one pannable plane: time across, dependency depth down, with edges between blockers. Below the filters, Dependency chains lists each connected group worst first, with the points on its critical path and whether its owners can finish before the milestone it is aimed at.

The roadmap page

Balance proposes an owner and a cycle for every unscheduled ticket, under each person's own capacity after on-call and time off. Untick anything you disagree with, preview it as a dry run, then apply.

The balance page

Writes run in one of two modes, chosen at launch. Live mode (the default) uses your real workspace key. Demo mode (TLR_DEMO=1) points every write at a free/test workspace and shows a banner, so you can try edits without touching real tickets. See SETUP.md for storing each key in the keychain.

Settings holds appearance, capacity, roster, integrations, and credentials on its own page.

The settings page

Screenshots come from the end-to-end suite against seed data. They refresh only on demand, so they do not churn on every run. Regenerate them after a UI change with deno task screenshots.

Setup

mise install
deno install
hk install

mise install pins Deno, hk (git hooks), and dprint (JSON/Markdown/TOML formatting) to this repo's versions. hk install wires hk.pkl's pre-commit hook into git so fmt/lint/test run automatically.

See SETUP.md for the credentials (Linear, Incident.io, Google Calendar) the data-refresh scripts need. To run everything offline without a Linear key, deno task seed writes two dated synthetic snapshots and registers a demo project, which is also what the tests use.

Usage

The web app, at localhost:8000, has six pages behind a shared nav: Board (capacity and dependencies), Changes (the weekly update), Review (recent edits), Roadmap (the dependency plane), Balance (proposed owners and cycles), and Settings.

deno task dev              # serve the web app at localhost:8000
deno task seed             # write synthetic snapshots + a demo project into web/data (no Linear key)
deno task issues "Name"    # refresh project / cycles / milestones / issues from Linear
deno task capacity         # refresh on-call / out-days / velocity into web/data/cpu.json
deno task roster           # resolve assignee names to emails from Linear
deno task gcal:freebusy    # spike: read teammates' free/busy from Google Calendar

CLI

deno task cli is the read-and-preview surface for Claude Code (or a person) to pull facts Linear does not aggregate, before making a batch edit. Every command prints JSON (SVG for export), so it pipes cleanly.

deno task cli scan     --project seed-b.json          # slop score per issue, or --text "<t>"
deno task cli capacity --project seed-b.json          # load vs capacity per person per cycle
deno task cli balance  --project seed-b.json --weekly 14 --start 49 --end 54 --lead 8  # propose assignee+cycle, with milestone deadline risk
deno task cli timeline --project seed-b.json          # dependency waves and chain risks
deno task cli diff     --a seed-a.json --b seed-b.json # plan-level change between two snapshots
deno task cli report   --a seed-a.json --b seed-b.json # weekly-update narrative from a diff
deno task cli forecast --project seed-b.json [--weekly 25]  # realistic landing date per milestone (--weekly overrides throughput)
deno task cli review   --a seed-a.json --b seed-b.json # what changed worth a look since last review
deno task cli plan     --project seed-b.json --text "move SEED-105 to M2"  # guidance -> ops, preview diff
deno task cli snapshot --project seed-b.json          # capture a snapshot into the local store
deno task cli export   --project seed-b.json          # SVG of the board (or --timeline)

The CLI reads and previews only; it never writes to Linear (plan shows the ops and the resulting diff but applies nothing). Writes run from the Review page. There is no MCP server by design, and none is planned: bulk edits already go through the Linear MCP in Claude Code, so a tlr write command would only duplicate it. If tlr is ever hosted, the CLI can gain a mode that calls the hosted API instead of reading local files, reusing the same handlers.

Development

deno task test            # run unit tests
deno task test:e2e        # run the end-to-end suite (seeds data, no Linear key)
deno task screenshots     # regenerate the README screenshots on demand
deno task fmt             # format *.ts/*.js
deno task lint            # lint *.ts/*.js
deno task check           # type-check
hk run pre-commit --all   # everything the pre-commit hook runs, on the whole repo

Unit and VCR tests cover the ingest path, so real Linear data lands in the right shape. The end-to-end suite runs against seed data with no live connection, so it checks that the interactions work (a refresh would fire, an edit would call the API) rather than re-testing the data format.

Architecture

src/              the core: seed (data contract), snapshot store, diff, review, ops, plan,
                  linear_write (the one write adapter), report, forecast, export, and
                  commands/ (scan, capacity, balance, timeline)
scripts/          data-refresh and dev-server scripts (issues, capacity, roster, serve, seed, cli)
web/              the app: app.js (board), changes.js, review.js, style.css
web/lib/          pure logic (planning.js, capacity.js), imported by both the browser and Deno tests
web/templates/    Vento page and layout templates rendered by the server
tests/            Deno unit tests plus tests/e2e Playwright smoke tests
presentations/    the Slidev theme and deck template for internal decision talks
adr/              decisions and why

About

tech lead reporter ("teller")

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages