A command-line tool for managing agents, projects, workers, PRs, messaging, and voice — the single interface for agent operations in tmux sessions.
- Project Management: Add, list, and archive projects
- Agent Management: Configure agents with role, voice, and status tracking
- Worker Management: Spawn and close Claude Code workers in isolated sessions
- PR Management: Create, modify, merge (squash), and comment on Forgejo PRs — context auto-resolved from worker session
- Messaging: Bidirectional agent ↔ human (Telegram) and agent ↔ agent (tmux) communication via
ttal send - Today Focus: Manage daily task focus list via taskwarrior's
scheduleddate - Task Routing: Route tasks to agents by name or role with
ttal task route --to <agent>, or spawn workers withttal task execute - Task Utilities: Search tasks and export rich prompts with inlined markdown context
- Voice: Text-to-speech using per-agent Kokoro voices on Apple Silicon
- Daemon: Communication hub — Telegram polling, message delivery, worker cleanup (launchd)
brew tap tta-lab/ttal
brew install ttalgo install github.com/tta-lab/ttal-cli@latestgit clone https://github.com/tta-lab/ttal-cli.git
cd ttal-cli
make install-dictateThe easiest way to get started — clone this repo, open in Claude Code, and run /setup:
git clone https://github.com/tta-lab/ttal-cli.git && cd ttal-cli
# Open in Claude Code, then: /setupThe /setup skill installs ttal, runs ttal onboard, and handles Telegram config.
Templates are in templates/ — no network clone needed.
# Set up taskwarrior hook (routes task events to agents)
ttal worker install
# Set up daemon (Telegram integration + worker cleanup, macOS)
ttal daemon installTo remove:
ttal worker uninstall
ttal daemon uninstall# Format code
make fmt
# Generate JSON Schema from config structs
make schema
# Run tests
make test
# Build binary
make build
# Run all checks (CI equivalent)
make ciInstall lefthook to automatically run checks before each commit:
lefthook installThe pre-commit hook runs fmt, vet, and lint in parallel. Tests are CI-only.
This project uses:
- gofmt - Code formatting
- golangci-lint - Comprehensive linting
- go vet - Static analysis
Run linting:
make lintPR Workflow (.github/workflows/pr.yaml)
- Runs on all pull requests
- Checks formatting, vet, linting
- Verifies generated JSON Schema is up-to-date
- Runs tests and builds binary
CI Workflow (.github/workflows/ci.yaml)
- Runs on push to main
- Full build and lint checks
- Ensures main branch stays healthy
Release Workflow (.github/workflows/release.yaml)
- Triggers on version tags (e.g.,
v1.0.0) - Uses GoReleaser to build binaries for Linux/macOS (amd64, arm64)
- Creates GitHub release with archives and checksums
- Auto-pushes Homebrew formula to
tta-lab/homebrew-ttaltap
# Tag a new version
git tag v1.0.0
git push origin v1.0.0
# GoReleaser builds binaries, creates release, and updates Homebrew formula
# Users upgrade via: brew upgrade ttalProject aliases support hierarchical matching for taskwarrior integration. When a task has project:ttal.daemon.watcher, ttal tries matching in order: ttal.daemon.watcher → ttal.daemon → ttal. This lets ttal act as a catch-all for all sub-projects.
# Add a project
ttal project add --alias=clawd --name='TTAL Core' --path=/Users/neil/clawd
# List all projects
ttal project list
# Modify project fields
ttal project modify clawd name:'New Project Name'
ttal project modify clawd path:/new/path
# Modify multiple fields at once
ttal project modify clawd name:'New Name' path:/new/path
# Archive a project
ttal project archive old-project
# Unarchive a project
ttal project unarchive old-project# Add an agent
ttal agent add yuki --voice af_heart --emoji 🦅 --role designer
# List all agents
ttal agent list
# Get agent info
ttal agent info yuki
# Modify agent metadata
ttal agent modify yuki voice:af_heart
ttal agent modify yuki emoji:🐱 description:'Task orchestration'
ttal agent modify yuki role:researcherWorker commands manage Claude Code instances running in isolated tmux sessions, tracked via taskwarrior. These commands do not require the ttal database.
# List active workers
ttal worker list
# Spawn a worker for a task
ttal task execute <uuid>
# Close a worker (smart mode - auto-cleanup if PR merged + clean worktree)
ttal worker close <session-name>
# Force close (dump state and cleanup regardless of PR status)
ttal worker close <session-name> --force| Code | Meaning |
|---|---|
| 0 | Cleaned up successfully |
| 1 | Needs manual decision (PR not merged, dirty worktree) |
| 2 | Error (worker not found, script failure) |
ttal worker install installs two taskwarrior hooks (on-add-ttal and on-modify-ttal).
Worker cleanup after PR merge is handled by the daemon (see Daemon Setup below).
# Install taskwarrior hooks
ttal worker install
# Remove taskwarrior hooks
ttal worker uninstall
# View hook logs
tail -f ~/.task/hooks.logTasks go through hook-driven stages:
1. on-add: Auto-enrichment — When a task is created, the on-add hook resolves project_path and generates a branch name inline using project.ResolveProjectPath() and enrichment.GenerateBranch(). No subprocess is forked.
2. on-modify (complete): Auto-cleanup — When a task is completed, the hook validates the completion (PR merge check). Workers are spawned explicitly via ttal task execute, not by task start.
3. Daemon cleanup — After a PR is merged, ttal pr merge drops a cleanup request file. The daemon picks it up and handles: close tmux session → remove worktree → mark task done. If the PR is not merged or the worktree is dirty, ErrNeedsDecision is returned, causing the CLI to exit with code 1 for manual handling.
# Example flow:
task add "Fix auth timeout in login API" # on-add: resolves project_path/branch inline
ttal task execute <uuid> # spawns worker/fix-auth-timeout
ttal pr merge # from worker: merges PR + triggers daemon cleanupManage your daily focus list. Tasks are filtered by taskwarrior's scheduled date. These commands do not require the ttal database.
# List today's focus tasks (scheduled on or before today, sorted by urgency)
ttal today list
# Show tasks completed today
ttal today completed
# Add tasks to today's focus (accepts 8-char UUID prefix or full UUID)
ttal today add <uuid> [uuid...]
# Remove tasks from today's focus
ttal today remove <uuid> [uuid...]Taskwarrior query utilities for searching tasks and exporting rich prompts. These commands do not require the ttal database.
# Export a task as a rich prompt (inlines referenced markdown files from annotations)
ttal task get <uuid>
# Search tasks by keyword (OR logic, case-insensitive)
ttal task find <keyword> [keyword...]
# Search completed tasks
ttal task find <keyword> --completed
# Route task to a specific agent by name
ttal task route <uuid> --to <agent-name>
# Spawn a worker to execute a task (replaces hook-based spawning)
ttal task execute <uuid>ttal task get is designed for piping to agents — it formats the task with description, annotations, and inlined content from annotations matching Plan:, Design:, Doc:, Reference:, or File: patterns.
ttal task route --to <agent> resolves agents by their role field in CLAUDE.md frontmatter:
# In an agent's CLAUDE.md frontmatter:
---
role: designer
---The agent's role determines which prompt key is used (from ~/.config/ttal/roles.toml). The command sends a role-tagged message with the UUID, description, and completion instructions. If no agent has the required role, the error suggests checking ttal agent list.
The daemon is a long-running process (managed by launchd on macOS) that acts as a communication hub for agents.
- Telegram → Agent: Polls each agent's Telegram bot for inbound messages, delivers them to the agent's tmux session via
send-keys - CC → Telegram: JSONL watcher tails active CC session files (via fsnotify) and sends assistant text blocks to Telegram automatically — no agent action needed
- Agent → Agent: Routes
ttal send --to bbetween agents via tmux with attribution (agent identity fromTTAL_AGENT_NAMEenv) - Worker cleanup: Processes post-merge cleanup requests (close session, remove worktree, mark task done)
Config lives in ~/.config/ttal/config.toml (created by ttal daemon install):
[teams.default]
chat_id = "845849177"
team_path = "/path/to/agents"
[teams.default.agents.kestrel]
# Bot token resolved from .env: KESTREL_BOT_TOKENchat_id— Telegram chat ID for the teamagents— per-agent config (bot tokens stored in~/.config/ttal/.env)- Notification bot token:
{TEAM}_NOTIFICATION_BOT_TOKENin.env
# Install launchd plist + create config template
ttal daemon install
# Remove launchd plist, socket, and pid file
ttal daemon uninstall
# Check if daemon is running
ttal daemon status
# Run daemon in foreground (for debugging)
ttal daemontail -f ~/.ttal/daemon.logSend messages between agents and humans with explicit direction:
# System/hook delivers to agent via tmux
ttal send --to kestrel "Task started: implement auth"
# Agent-to-agent via tmux (recipient sees [agent from:yuki] attribution)
# Agent identity comes from TTAL_AGENT_NAME env var
ttal send --to kestrel "Can you review my auth module?"
# Read message from stdin
echo "done" | ttal send --to kestrel --stdinNote: Agent → Telegram is handled automatically by the daemon's JSONL watcher — agents don't need to call
ttal sendto reach humans.
Message formats delivered to CC terminal:
[telegram from:neil]
Can you check the deployment?
[agent from:yuki]
Can you review my auth module?
Manage Forgejo pull requests directly from worker sessions. Context is auto-resolved from TTAL_JOB_ID (task UUID prefix) → project_path → git remote get-url origin.
# Create PR (stores pr_id in task UDA automatically)
ttal pr create "feat: add user authentication"
ttal pr create "fix: timeout bug" --body "Fixes #42"
# Modify PR title or body
ttal pr modify --title "updated title"
ttal pr modify --body "updated description"
# Squash-merge the PR (deletes branch by default)
ttal pr merge
ttal pr merge --keep-branch
# Add a comment to the PR
ttal pr comment create "LGTM — no critical issues"
# List comments on the PR
ttal pr comment listThe reviewer is advisory only — they post a verdict but never merge:
- Reviewer examines the PR and posts a comment ending with
VERDICT: LGTMorVERDICT: NEEDS_WORK - Coder triages the review. Even with LGTM, the reviewer may note non-blocking issues that should be addressed first
- Coder fixes remaining issues, posts a triage update via
ttal pr comment create - Coder merges with
ttal pr mergeonce all issues are addressed — the PR is already approved
Requires FORGEJO_URL and FORGEJO_TOKEN environment variables.
| Variable | Required | Description |
|---|---|---|
FORGEJO_URL |
Yes | Forgejo instance URL (e.g., https://git.guion.io) |
FORGEJO_TOKEN or FORGEJO_ACCESS_TOKEN |
Yes | Forgejo API token |
Required by: ttal pr *, ttal worker close (smart mode).
| Variable | Required | Description |
|---|---|---|
TTAL_JOB_ID |
No | Task UUID prefix (set automatically in worker sessions) |
Worker commands require these User Defined Attributes in ~/.taskrc:
uda.branch.type=string
uda.branch.label=Branch
uda.project_path.type=string
uda.project_path.label=Project Path
uda.pr_id.type=string
uda.pr_id.label=PR ID
Worker commands require these tools in $PATH:
task- Taskwarrior (task tracking)tmux- Terminal multiplexer (worker and agent sessions)git- Version control (worktrees, branch management)fish- Fish shell (used in tmux sessions)
The modify command uses field:value syntax for updates:
field:value- Update a field value- Use quotes for values with spaces:
name:'My Project Name' - Combine multiple updates:
name:'New Name' path:/new/path
Agent fields:
voice- Kokoro TTS voice IDemoji- Display emojidescription- Short role summaryrole- Agent role (maps to prompt key in prompts.toml or roles.toml)
Project fields:
alias- Project alias (rename)name- Project namepath- Filesystem path
ttal project modify clawd name:'New Name' path:/new/path
ttal agent modify yuki voice:af_heart description:'Task orchestration'When building ttal-cli, use the following commit format:
ttal: [category] description
Example: ttal: impl - add worker execute
ttal: refactor - optimize tag queries
Agents can have one of three status values:
idle- Available for task assignmentbusy- Currently workingpaused- Temporarily disabled
MIT