Session management and GitHub issue dispatch for multi-agent coding workflows.
# Deploy to /usr/local/bin (bin + lib — the Python modules in lib/tms/
# must be installed alongside the scripts)
sudo cp -r bin lib /usr/local/
# Browse sessions
tms
# Browse open issues across repos
tms issues
# Dispatch an agent on an issue
tmq distillery 245The review poller classifies PR diffs for specialist reviewer signals that determine which specialist personas join the review panel.
| Signal | Detection | Specialist persona |
|---|---|---|
security |
Diff contains auth/crypto/session/token/password/permission/policy keywords in added lines, or file paths containing auth, secret, crypto, permission, policy anywhere in the path (e.g. authenticator.py, crypto_helper.py, my-secrets.yaml) |
reviewer-security |
schema |
Diff touches migration/SQL/schema/alembic files, or contains DDL keywords (CREATE TABLE, ALTER TABLE, DROP TABLE, ADD COLUMN, CREATE INDEX) in added lines |
reviewer-schema |
duplication |
>40% of changed lines are deletions, or duplicated added-line blocks | (future — persona TBD) |
editorial |
ALL changed files are documentation (*.md, *.rst, *.txt, docs/, README*, CHANGELOG*, LICENSE*) |
(fast-path — generalist still runs) |
Specialists ADD to the default panel, never replace it. The generalist
reviewer + reviewer-m3 pass always runs. Specialist signals add additional
reviewer personas to the panel.
When the review poller dispatches tmq review, it sets the TMQ_SPECIALIST
environment variable to a comma-separated list of signal names (e.g.
security,schema). The review session reads this variable to include the
corresponding specialist reviewer personas. No specialists = empty/unset
variable = generalist-only panel.
- Add the signal name to
SPECIALIST_SIGNALSinlib/tms/diff_shape.py - Implement detection logic in
classify_diff() - Add unit tests in
tests/test_diff_shape.py - Create the specialist persona skill in
pi-dotfiles/skills/reviewer-<signal>/SKILL.md - Wire it in the review session prompt (reads
TMQ_SPECIALIST) - Update the routing table above
tms fzf picker: browse/attach/kill sessions
tms ls list all sessions (ISSUE + AGENT + ST columns)
tms stale find abandoned sessions (>7d idle)
tms clean kill stale + compact scratch sessions
tms issues [repo] browse open GitHub issues + dispatch agents
tms new create a GitHub issue (gh issue create wrapper)
tms import wrap a legacy c/o/p scratch session as aoe
tms events fleet dispatch metrics (issue #53)
tms events transitions poll aoe + tmux panes, emit state-transition events
tms events stats compute and print aggregate metrics report
tms events stats --json machine-readable JSON output
tms events stats --since YYYY-MM-DD filter from date
tms events stats --by-class per-class (repo:dispatch_type) with rounds & cost (#112)
tms events stats --by-class --json machine-readable per-class JSON (#112)
tms events scan-reviews list open PRs lacking reviewer verdicts (#57)
tms events scan-reviews --dispatch spawn tmq review for never-reviewed PRs
Every tmq dispatch appends an event record to tms_review.events
(postgres). tms events transitions
polls running sessions and emits transition events when <<AGENT-STATE>>
markers change. tms events stats reads the event log and computes:
- Issue→merge latency (p50/p90)
- Review rounds per PR
- BLOCKED frequency vs clean MERGE-READY
- Plan-gate fast-path rate
- Per-model outcome rates
- Per-class breakdown (
--by-class): dispatches, pass-rate, median rounds, blocked-class distribution, median cost per merged PR (tms#112)
See docs/events-format.md for the event log schema and consumer guide.
tmq <repo> <num> dispatch pi on issue
tmq <repo> <num> --agent cc use Claude Code
tmq <repo> <num> --type review code review
tmq list known repos
tms (browse + manage)
├── session view — attach, kill, rename, clean stale
│ └── column ST: aoe Running/Waiting/Idle for aoe sessions
│ (or derived from pane cmd for raw tmux sessions)
├── issues view — browse GitHub issues, pipeline status, dispatch
├── new — create GitHub issues (wraps gh issue create)
└── import — wrap a legacy c/o/p scratch session as an aoe session
tmq (issue → agent spawn)
├── gh issue view → build prompt
├── git worktree add → isolate working tree
└── aoe add → spawn agent (appears in aoe dashboard)
aoe (Agent of Empires — monitoring)
├── session dashboard + web + mobile
└── status: Running / Waiting / Idle / Error / Stopped
Three tools, three layers. aoe manages sessions; tmq dispatches work; tms connects them.
The session→issue mapping in tms reads the worktree branch directly (via
git -C <path> branch --show-current) and falls back to parsing the aoe
title only when the session is on a main checkout.
Fleet-wide dispatch metrics are stored in postgres (migrated from JSONL, tms#65):
tms_review.events dispatch/transition/failed events (postgres)
/tmp/tmq-last-status-cache.json transition detector state (ephemeral)
~/.local/state/tmq/events.jsonl.bak.YYYY-MM-DD legacy JSONL (migrated 2026-07-12)
Event types: dispatch (tmq spawn), dispatch_failed (spawn failure),
transition (AGENT-STATE marker change). Full schema and consumer guide
in docs/events-format.md.
tmux— session substratefzf— interactive pickergh— GitHub CLI (issue/PR queries + creation)python3— JSON processingpytest(dev) —pip install pytestaoe(optional) — session registration + status in Agent of Empires
# Run the test suite (fast — 137 tests, ~0.5s)
pytest tests/ -v
# The pre-push hook runs the same tests before allowing a push.
# To install (one-time):
git config core.hooksPath .githooks
# Bypass with `git push --no-verify` if needed.
# Test layout
# lib/tms/ Python modules extracted from bin/tms
# tests/ pytest test suite
# .githooks/pre-push version-controlled pre-push hookThe test suite guards the five P0/P1 regressions from PR #7's
multi-model review (cache-write races, bare except, os.path.isdir
vs worktrees, re.match vs re.search, branch-first false-positives).
See tests/test_session_matcher.py and tests/test_cache_atomic_writes.py
for the named regression tests.