Changelog
[Unreleased]
[0.9.1] - 2026-06-06
Added
- Live working/idle status in the agent roster (
teamctl ui). A running agent now shows a bright●when it’s actively working versus a dim○when idle — driven by an activity heartbeat refreshed as the agent works — and the old✉unread-mail glyph is dropped so the status column reads at a glance. In monochrome terminals these degrade to*/o. (#432, #433, #434)
Changed
whatsnewand the post-updatesummary now render the release-body markdown with terminal styling instead of printing the raw markup. (#426)
Fixed
- Headless agents no longer freeze on Claude’s “New MCP server found in this
project” prompt. The rendered settings now pre-trust every project-scoped
MCP server (
enableAllProjectMcpServers), so discovering a.mcp.jsonserver on a fresh session, or afterupdateintroduces a new one, no longer strands an unattended pane waiting on a confirmation no one can give. Attended sessions skip the rendered settings, so a human at the terminal still sees and answers the prompt. Existing frozen agents need areload(ordown/up) to pick up the setting, sinceupdateswaps the binary without restarting live panes. (#421)
[0.9.0] - 2026-06-03
Added
- Per-agent capability stacks. Each agent can now declare its own
sub-agents, skills, hooks, and MCP servers directly in compose —
subagents:(rendered into Claude Code’s--agents),skills:(mounted via--add-dir),hooks:(merged into the agent’ssettings.json), andmcps:(merged alongside the built-inteammailbox server). All additive, on top of project- and user-level config; claude-only for hooks/sub-agents/skills, runtime-agnostic for MCP. (#387, #388, #389, #391) - The guided plugin reshaped around goals and capabilities.
/teamctl:initnow opens on your goal with pickable starters, presents the team as a capability setup (each session’s model, sub-agents, skills, hooks, and whether it runs a/loop) rather than an org chart, iterates until you approve, and emits the full per-agent capability stack on the fly./teamctl:adjustgained a verb for evolving that stack, so the mindset survives the first adjustment. The plugin ships a capability catalog of dogfood-proven archetypes. (#418, #419) - Ideate & Build template for
teamctl init— a compass + executor + engineers shape for the idea→ship workflow, sub-agent-heavy. (#382) teamctl initpreviews the team shape and file tree before scaffolding, with refreshed next-steps. (#405)- Richer
teamctl ui— a live detail pane and stream-keys agent switching. (#407)
Changed
- Minimum supported Rust version raised from 1.78 to 1.86. The locked
dependency graph drifted past 1.78:
clap_lex1.1.0 needs Rust 1.85 (edition = "2024"), and theicu_*2.2.0 crates (pulled transitively viaurl→idna) need Rust 1.86 — so 1.86 is the true floor. The CI MSRV job is also fixed to actually run on the matrix toolchain: the repo’srust-toolchain.toml(channelstable) had been silently shadowing it back tostable, so the old1.78leg never tested the MSRV at all. The opt-inteamctl-uiTUI crate (installed separately viacargo install teamctl-ui) declares its own higher MSRV of 1.88 — its ratatui 0.29 dep tree pullsinstability/darling, which need 1.88 — independent of the CLI’s 1.86 floor. (#397) teamctl attachdefaults to read-write and drops the confirm gate — attach lands you straight in. (#408)teamctl upnow prints a short notice when it pre-accepts Claude Code’s workspace trust for an agent’s folder: it names the folders, the config file (~/.claude.json), and how to undo.teamctlno longer edits your Claude config silently.- Headless
claude-codeagents auto-accept Claude’s “new MCP server(s) found in this project” prompt (Enter enables the discovered servers), so a project.mcp.jsonno longer strands an unattended pane. A general fix for the whole startup-prompt class is tracked in #421. teamctl bot setupnow leads with manual token as option 1 (the default on a bare Enter) and managed bots as option 2 — the managed Telegram-side bot creation is rougher, so new users aren’t led down it first. (#366)- Templates are embedded via
include_dirinstead of a hand-listedinclude_str!set, so new template files can’t be silently dropped from the binary. (#396) - Telegram first-connect message simplified to a one-line greeting. (#381)
- Examples showcase curated and revamped — trimmed to 7 examples, each rebuilt around per-agent capability stacks, plus a cross-model product-team example and an autonomous-prototyper showcase. (#406, #410, #412, #414, #415, #416, #417)
- README and docs site revamped around reproducibility and the hero path. (#390, #392, #393, #401, #403, #404)
Fixed
- Headless
claude-codeagents no longer stall at startup on Claude’s “Quick safety check” / trust-folder dialog. The agent wrapper’s auto-confirm watcher now dismisses it (accepting first-run trust for the agent’s own working directory) so an unattended pane keeps running instead of waiting for a keystroke no one is there to press. The match is gated on a two-string co-occurrence so an agent that merely prints the phrase can’t trigger a stray Enter. (#369 follow-up) - Mailbox: privileged
kind='system'is guarded on UPDATE paths, so a message can’t be silently elevated to system after insert. (#409) teamctl uistream-keys forwards Esc to the pane; Ctrl+E exits. (#378)- The splash-screen snapshot redacts the version token, so version bumps no longer break CI. (#379)
- Examples no longer instruct a manual
team-botlaunch that duplicatesteamctl up(a second poller caused a Telegram 409). (#413)
[0.8.7] - 2026-06-01
Added
- The
teamctl uimailbox is now interactive: scroll the pane (a per-tab row cursor), filter (f) and search (/) messages by sender or text, and open any message in a detail view (Enter) showing its full body and a relative timestamp. Help, onboarding, and statusline copy updated to match. (#331, #335, #336, #340) - macOS:
teamctl upkeeps the host awake (caffeinate -i -s) while agents are running, so long-running tasks survive display/idle sleep; the keep-awake is released automatically when the last team on the host goesdown. Host-level and refcounted — one assertion covers every running team. Lid-open display sleep only; lid-closed/clamshell is hardware-gated and unaffected, and the display is never forced on. No-op on non-macOS hosts. (#370) teamctl --versionnow self-identifies dev builds: a local build off a git checkout reports thegit describeform (0.8.6-55-ge4adf50, with-dirtywhen the tree has uncommitted changes), so it’s distinguishable from a clean tagged release. Tagged releases still report the plain version (0.8.7), and builds without git (extracted tarball, crates.io) fall back to the Cargo version — the build never fails when git is absent. (#359)teamctl bot setupgains a managed-bots path: set up one manager bot (Telegram Managed Bots, Bot API 9.6) and it spawns a child bot per manager for you — confirm onet.melink each instead of a separate BotFather trip. The wizard mints each child’s token, runs the same/startchat authorization, and writes the per-managerinterfaces.telegramblock plus the project-levelinterfaces.telegram.manager_botconfig. The original manual-token flow is preserved verbatim as the other fork at the top of the wizard. (#344, #132)teamctl reload --freshandteamctl up --freshrestart agents into a brand-new Claude conversation (re-running the bootstrap prompt) instead of resuming the prior session, while keeping durable on-disk files (task.md, memory, ways-of-working). The escape hatch from always-on session resume — for a wedged context, a bad self-compact, or token bloat.--freshonly affects agents the command actually (re)starts, and composes with the scoped force-restart. Claude runtime only;codex/geminiagents are skipped with a warning (parity gap). (#352)
Changed
- Headless
claude-codeagents now launch with--permission-mode autoinstead of--dangerously-skip-permissions. Auto mode runs a safety classifier before each tool call: routine work (file edits, builds, pushing to the working branch, read-only HTTP) runs with no prompts, while genuinely risky actions (force-push,rm -rfof pre-existing files, secret exfiltration, production deploys) are blocked outright — no permission dialog to strand an unattended pane. This swaps the bypass-everything flag for a real safety gate.permission_mode: attended(human at the keyboard) is unchanged;permission_mode: bypassPermissionsrestores the old bypass-everything behavior for disposable sandboxes. Two edges to know: (1) if the classifier blocks 3 actions in a row or 20 total in a session, auto falls back to prompting — which an unattended pane cannot answer; narrow (only under sustained repeated risky attempts), and the existing deny hook still covers plan-mode /AskUserQuestionprompts. (2) Auto requires Claude Opus 4.6+ / Sonnet 4.6 on the Anthropic API (not Bedrock/Vertex); on other models/providers it is unavailable. Claude runtime only —codex/geminiare unaffected. (#361) teamctl init(Guided) andteamctl adjustnow default their “open Claude Code… Continue?” confirm to Yes ([Y/n]): once you’ve chosen the LLM-led path, a bare Enter proceeds instead of cancelling. (#356)
Fixed
- Telegram attachment downloads are now capped against the bot’s reported
file.sizebefore writing to disk, closing a disk-fill vector (the download_to disk-write counterpart to the existing RAM-OOM defense). (#332) tools/install.shonly offers the Claude Code plugin install when Claude Code is ≥ 2.1.141 — older versions have--session-idresume bugs that turn the firstteamctl upinto a confusing failure. (#262)
[0.8.6] — 2026-05-17
Changed
- teamctl’s Linux release binaries are now fully static (musl): they carry zero glibc dependency and install and run on any Linux — including old, minimal, or glibc-absent systems such as Debian, Alpine, Proxmox, and embedded boxes. This permanently eliminates the
GLIBC_x.xx not foundinstall failures that the previousubuntu-22.04runner pin only deferred. (#309)
Fixed
- New and Essentials teams now receive their Telegram messages:
bot setupno longer corrupts thetelegramblock when writing config — a YAML-edit bug mis-nested a replaced leaf’s values, breaking delivery for freshly-created teams. (#318) teamctl bot setupno longer echoes the Telegram bot token as it is typed or pasted. The prompt now reads it with terminal echo disabled, keeping the credential out of the screen, scrollback, screen-shares, and recordings. (#315)- Agent env-file loading is hardened: env values are no longer passed through the shell, so a value containing spaces or glob characters (
*,?) can no longer mangle the agent’s environment or pull unintended files into it. (#307) - The
teamctl uiDetail pane now fits the agent’s terminal output correctly — the inner tmux session is sized withresize-window, so captured content no longer overflows or clips inside a smaller pane. (#317) - Submitting in the
teamctl uicompose editor now works on all terminals: plain Enter in Normal mode submits, fixing send on default-mode terminals (xterm, Terminal.app, GNOME Terminal) where the previous Alt/Ctrl+Enter chord never fired. (#316)
[0.8.5] — 2026-05-16
Added
request_approvalgains multi-option interactive decisions: a newoptionsparameter and a newdecidedstatus. A manager can offer the user a choice list (not just approve / deny) and read back which option was picked. Touches theteam-mcp/team-bot/team-coreapproval schema. (#301)teamctl adjust— a CLI shim that execsclaude /teamctl:adjust, mirroring howteamctl initenters its interactive skill. (#248)- Interactive substrate shared by the
/teamctl:initand/teamctl:adjustskills — a conversational layer the skills drive. (#247) teamctl initpre-flight: a.team/guard so an existing team isn’t clobbered, a dependency pre-flight check, and a Guided / Essentials / Blank mode picker. (#297)- Headless
claudeagents block interactive tools (AskUserQuestion,EnterPlanMode,ExitPlanMode) by default, so a headless agent can’t stall on a prompt it can’t receive. Thanks to outside contributor Hamed Fathi. (#246)
Changed
teamctl uimailbox channel tab now prefixes each row with the channel name and sender, so cross-channel traffic is legible at a glance. (#251)- README “Start a team” now leads with
teamctl initinstead of theclaudeslash-command, and lists the Guided / Essentials / Blank picker options. (#239, #250) /teamctl:releaseskill tightened to a shorter-is-better shape with major / minor / patch templates and a verify-handle thanks rule, per owner directive. (#234)
Fixed
- cargo-dist linux release binaries no longer require
GLIBC_2.39. The linux build runners are pinned toubuntu-22.04(glibc 2.35), restoring the binary install path on Debian 12, Ubuntu 22.04, Proxmox, and most stable LTS Linux. (#296) - The installer and docs now reference the correct Claude Code plugin id
teamctl@teamctlforclaude plugin update, so the plugin auto-update path works instead of failing on an unrecognized plugin name. (#298) reply_to_userrejects oversized text / caption at the MCP boundary instead of failing deeper in the bridge. (#293)team-botsetup no longer fails on a just-edited compose file when run a second time — it reuses the parsed compose instead of re-reading from disk. (#245)team-botreplies with a configuration hint when a voice note arrives but speech-to-text is unconfigured, instead of silently dropping it. (#237)- macOS CI purges pre-seeded rust stub shims before the toolchain install and pins the runner to
macos-14, fixing a redmainpipeline. (#285)
[0.8.4] — 2026-05-12
Removed
/teamctl:releaseskill is no longer shipped via the Claude Code plugin. It was an internal release-authoring tool, not a user-facing feature; previously bundled inplugins/claude-code/skills/release/by mistake. Moved to.claude/skills/release/SKILL.md(project-local, not part of the plugin install). Users of theteamctlplugin won’t see it after their nextteamctl update. (#228 follow-up)
[0.8.3] — 2026-05-12
Added
examples/gastown-in-teamctl/— a positioning artifact expressing Gas Town’s seven-role formation (mayor / crew / refinery / witness / polecats / deacon / dog) as a teamctl team. README walks the mapping from Gas Town’s opinionated frame to teamctl’s unopinionated declarative layer, including the reinterpretations (polecats as a fixed pool, beads-vs-mailbox as a deliberate architectural trade-off, formulas as a vision-track gap). Credits Steve Yegge’s original Medium piece. (#229, #230)teamctl uiAgents pane now rendersreports_torelationships as a nested tree with Unicode glyphs (and ASCII fallback underNO_COLOR/ monochrome terminals). Selection stays sticky-on-id; flat teams render byte-identically. (#211, #225)
Changed
teamctl uimailbox Sent tab now shows[→recipient]instead of the redundant[sender](always self for the focused agent). Recipient resolves to display_name for agents,#namefor channels, and the verbatim id for user surfaces likeuser:telegram. Other tabs unchanged. (#231, #232)/teamctl:releaseplugin skill grew durable shape variants (major / minor / patch templates) and a verify-handle thanks rule, so future releases pull a tighter shape and never ship a guessed contributor handle. (#228)
[0.8.2] — 2026-05-12
Added
- Bottom status bar in
teamctl ui— left side shows the team-root path, right side shows live CPU% + RAM%, with a center slot reserved for per-agent surfaces. Uses thesysinfocrate trimmed to a system-only feature set. (#209, #217) - Per-agent claude rate-limit indicator in the bottom-bar center slot — shows
limit Xh Ym/limit Xm Ysfor the focused agent when claude has signalled a rate-limit window. Preview-gated behindTEAMCTL_UI_RATE_LIMIT_INDICATOR=1(any non-empty value opts in; default OFF; read once at TUI start). (#212, #218) - CI now runs a dedicated
macos-latestcell so darwin-flavor regressions get caught at PR time, plus abash --posix -O compat32 -nparse-check on the existing Linux runner that surfaces the bash-3.2 class of wrapper-script bugs without waiting on a macOS minute. Both motivated by the T-190 regression that bit 0.8.0 on macOS. (#192, #193, #205)
Changed
teamctl initretires thesolotemplate. New three-option picker: Guided (interactive conversation, default for interactive runs), Essentials (two-project layout — blankmain.yamlplus anopsproject with abuilderagent that has scoped authority overmain; default for--yes), and Blank (kept as the minimal scaffold).--template guided --yesrejects cleanly. ADR-0004 carries a supersede note. New cookbook walkthrough onteamctl.run. (#206, #216)teamctl updatenow reinstallsteamctl-uialongsideteamctl,team-mcp, andteam-boton the cargo-install path. The four crate names are centralised as a single constant so the bug class can’t recur silently. Stale 3-crate command references in docs and the Claude Code init template were swept in the same wave. (#188, #204, #207)teamctl uisplash screen now has a single blank line between the ASCII logo and the version/team-status line so the layout is less cramped. (#208, #214)teamctl uireflows the focused agent’s tmux pane size to match theDetailrect on every frame (cache-gated so steady-state frames don’t fork tmux). The claude TUI now visibly tracks teamctl-ui resizes. Scoped to the Triptych layout; Wall + MailboxFirst sweeps will land separately. (#199, #210)
Fixed
teamctl whatsnewno longer dumps the cargo-dist per-crate install tables verbatim after the curated release prose; the renderer now detects the# <crate> <semver>heading injected by cargo-dist and truncates there. (#197, #200)teamctl whatsnew --since <ver>no longer emits a redundantv<from> → v<to>range frame plus per-version subheader when the resolved range contains a single version. (#198, #200)teamctl whatsnewno longer renders a double blank line under the frame when the body is empty or got truncated to empty by the cargo-dist heading detection. (#201, #203)team-mcpchannel-notify watcher no longer hits a lost-wake race against macOS scheduling.tokio::sync::Notify::notify_waiterswas swapped fornotify_oneso thenotifications/initializedsignal buffers a permit if the watcher hasn’t parked yet. Added a Linux-deterministic regression pin via a test-only env var. (#215)
[0.8.1] — 2026-05-11
Fixed
agent-wrapper.shnow starts cleanly on macOS (bash 3.2). The previous${BOOTSTRAP_PROMPT:=...}parameter-expansion form tripped a parser bug in bash 3.2 (macOS/bin/sh) when the default contained escaped backticks, causing agents to abort immediately afterteamctl up. Rewritten as a plainif [ -z ... ]conditional that parses identically on bash 3.2, bash 4+, and dash. Latent since T-104. (#190, #191)agent-wrapper.shnow guards$CLAUDE_SESSION_IDand$CLAUDE_SESSION_NAMEunderset -u. Previously, if either env var failed to render into the agent env file, the wrapper would abort silently. (#190, #191)
[0.8.0] — 2026-05-11
Added
- Voice notes on Telegram are transcribed to text via Groq STT and forwarded to the manager (#105).
- File attachments in compose: path-input overlay in the TUI plus agent reads via the new
read_attachmentMCP tool (#139, #147). - Stream-keys (
Ctrl+E) in the mailbox pane forwards subsequent keystrokes straight to the focused agent’s tmux pane (#114). Senttab in the mailbox pane shows the focused agent’s outbox alongsideInbox,Channel, andWire(#127).- Mouse-wheel scrolling in
teamctl uiroutes through the focused pane — copy-mode history onDetail, agent step onRoster(#163). teamctl sessionslists every tmux session across projects in one view (#112).teamctl updaterefreshes the Claude Code plugin alongside the binaries (#148).role_promptaccepts a list of markdown paths, concatenated in declared order at agent boot (#142).- Per-role
ways-of-working.mdconvention — gitignored, lazy-created, read at the start of every tick (#136, #143). compact_selfMCP tool — agents tidy their own context without an external nudge (#128).show_typingMCP tool — managers send a “typing…” indicator to Telegram (#123).- Lazy inbox delivery: channel notifications arrive as short stubs;
/readnowprefix bypasses for per-message full-body delivery (#113). - Always-on session resume via deterministic per-agent UUID (#151).
- Daily update-availability nudge on
teamctl statusandteamctl upwhen a newer release is on GitHub (#150). - Mailbox tab navigation via arrow keys (#125).
- Per-project scope for
teamctl up/down/reload(#135). teamctl initreplaces the template picker with a domain-discovery conversation (#157).- Telegram bot renders a markdown subset to Telegram HTML parse mode (#137).
- HITL approver name is derived from the Telegram callback sender (#120).
Fixed
- HITL approval outcome no longer hardcodes the approver name (#120).
- Telegram bot html-escapes agent identifiers in HITL cards and attribution lines (#145).
agent-wrapper.shauto-confirms the bypass-permissions and usage-limit dialogs so first launches and rate-limit windows don’t strand agents (#121). Thanks to Hamed Fathi for surfacing the bypass-permissions deadlock.claudeTUI renders at the full pane size instead of80×24(#99).teamctl uiRoster column renamed to Agents; Triptych Detail/Mailbox split tuned to 60/40 (#95, #96).- Telegram bot restricts
fence_markerlanguage tags to[A-Za-z0-9_-](attribute-injection hardening) (#154). - Deflake
channels_notifyunder loaded CI runners (#119, #144). - Deflake
real_scanner_for_spec_timeout_returns_rejected(#152).
Changed
tools/install.shis now served as a static asset onteamctl.run/installand regenerated on every docs build (#92, #93).tools/install.shbundlesteamctl-uiand offers the Claude Code plugin install/update on interactive runs (#97).- README rewritten with an examples-first flow (#161).
- Docs site polish sweep: legacy landing retired, modernized guides, ways-of-working page (#162).
- Six relatable example teams replace the SaaS-only example (#165).
- New “How to think about agent teams” concept page (#153).
- Channels-with-ACLs explainer added to the concepts section (#166).
[0.7.3] — 2026-05-08
Added
react_to_userMCP tool (T-086-E). Manager agents can now react to operator Telegram messages with an emoji from a curated allowlist (👍 👎 ❤️ 🎉 👀 🤝 👨💻). Reactions ride thekind+structured_payloaddiscriminator added in 0.7.2 — the bot dispatcher readskind = "reaction"and routes to Telegram’ssetMessageReactionAPI instead ofsendMessage. Off-allowlist emoji surface a clean MCP error rather than reaching Telegram and getting silently rejected. Manager-gating preserved — worker agents cannot react.reply_to_userreply-threading viareply_to_message_id(T-086-B). Agents can now thread replies under the operator’s prior message in Telegram by passing the inbound message’stelegram_msg_idasreply_to_message_id. Outbound rows carry the field forward;team-botattachesreply_parametersonsendMessage/sendPhoto/sendDocumentso the reply visually nests under the parent in the chat client. Multi-content calls (text + image in one tool invocation) share one threading target so the operator sees both attachments under the same parent, not split across threads. Back-compat preserved: callers omitting the field land messages as fresh posts as before.- Inbound media handling (T-086-C).
team-botnow detects inbound photos and documents from the operator, downloads them to a per-project disk cache (<media_root>/<project>/<row_id>.<ext>), and writes a structured mailbox row (kind = "image"/"file",structured_payload = {path, mime, size_bytes, caption?}). Agents read file bytes from the disk path via their runtime’s vision plumbing. Two-phase SQL pattern: insertmedia_pendingplaceholder → download → UPDATE to final kind. Network/disk-full failures surface asmedia_errorrows with the verbatim cause (R12) so the operator’s reply prompt has a real diagnostic instead of silent drop. Documents whose mime starts withimage/are classified askind = "image"so vision plumbing picks them up — operators often upload PNG/GIF as document to avoid Telegram’s JPEG recompression onphoto. - Version line in
teamctl --help(T-091). The help page’s first line now showsteamctl <version>so operators can disambiguate “which version am I running” without a separate--versionround-trip. Pulls fromCARGO_PKG_VERSIONso it auto-tracks the workspace version at release time.
Fixed
teamctl-uiwas never published to crates.io (T-095). Theteamctl uicommand resolves tocargo install teamctl-uito install the TUI on demand (intentionally opt-in to avoid pulling the ratatui+crossterm dep tree into every install), but the publish-crates workflow shipped only the four core crates and skippedteamctl-ui— soteamctl uifailed withcould not find teamctl-ui in registry crates-io. The workflow now publishesteamctl-uialongside the others; v0.7.2 was manually backfilled to crates.io to unblock the operator surface, and v0.7.3 onward ships through the workflow.tools/install.shtag-name parser broken on single-line GitHub API responses (T-085). The previous parser used a greedysed -E 's/.*"([^"]+)".*/\1/'aftergrep '"tag_name":', which works on pretty-printed JSON but captures the last quoted string on a line — buried deep in the releasebodyfield’s CHANGELOG markdown when the API returns compact (single-line) JSON. The installer then resolved$VERSIONto garbage and failed at the tarball fetch. Parser now anchors on thetag_name:field name withjqwhen available and a tightersed -nE 's/.*"tag_name": *"([^"]+)".*/\1/p'fallback otherwise. Same behaviour on pretty-printed input; correctly handles compact JSON.- macOS-14 installer-smoke check rate-limited on shared runner
IPs (T-088, folded into T-085). The smoke workflow ran
install.shwhosecurl https://api.github.com/repos/.../releases/latesthit the 60-req/hour unauthenticated-IP rate limit on shared CI runner pools. Workflow now pre-resolvesTEAMCTL_VERSIONvia authenticatedgh release view(5,000 req/hour for authenticated requests) and exports it as an env var so install.sh skips its own latest-resolver. Linux runners weren’t affected in practice, but the auth fix covers them too.
Changed
install.shserved as a static asset on teamctl.run/install (PR #86, post-0.7.2 hot-fix). The previous/installendpoint did a redirect to the raw GitHub URL, which Cloudflare Workers occasionally responded to with cached redirect chains that confusedcurl -fsSL. The Astro docs site now servestools/install.shdirectly as/install— no redirect, no caching surprises.tools/install.shis now the source of truth for the install path.publish-crates.ymlworkflow gainsworkflow_dispatchtrigger. The release-tag-push trigger remains; the manual trigger is for ad-hoc backfill scenarios (e.g. publishing a newly-added crate against the current main branch without cutting a fresh release). Version-match guard now scoped topushevents only.
[0.7.2] — 2026-05-04
Added
- Outbound media via
reply_to_user(T-086-A). Manager agents can now send images and files to operators through the Telegram bot, with optional captions. Thereply_to_userMCP tool gains optionalimage: {source: "path"|"url", value, caption?}andfile: {...}fields. Text-only callers continue working unchanged. Multi-content per call yields separate Telegram messages in order; the response carries both legacyidand a newidsarray. Path-source files validated for existence + ≤50MB Telegram bot limit + image extension allowlist (jpg/jpeg/png/webp/gif). Manager-gating preserved — worker agents cannot send media. - Slash-passthrough to tmux session (T-086-G), Claude-Code-only.
Telegram messages starting with
/(e.g.,/clear,/compact,/cost) bypass mailbox routing and get typed directly into the manager’s tmux session viatmux send-keys. Feature-gated onruntime: claude-code; non-CC managers respond with a clear reject message naming the actual runtime. Trust posture: operator owns the bot (single-operator deployment shape); arbitrary text typed via slash-passthrough runs at agent privilege — same trust boundary the operator already extends to the agent’s tmux via direct ssh / tmux attach. - Telegram bot autocomplete via
setMyCommands(T-086-H). Each manager-scoped CC bot registers a curated set of 12 Claude Code slash commands (/clear,/compact,/cost,/help,/init,/mcp,/model,/permissions,/resume,/review,/status,/vim) on startup so the operator gets an autocomplete menu when typing/. Pairs with the slash-passthrough above for clean operator UX. Hyphenated CC commands and login flows excluded (Telegram’s bot-API restricts command names to[a-z0-9_]; login flows are awkward over chat). Best-effort registration: a Telegram API failure logs a warning and bot startup continues; slash-passthrough still works manually. Non-CC managers register no commands (clean degrade).
Fixed
- TUI chord-arm casing-fold across remaining Ctrl+letter chords
(T-082). Following 0.7.1’s Ctrl+W/M case-fold fix, sweeps the same
bug class for
Ctrl+H/Ctrl+J/Ctrl+K/Ctrl+L(split cycling) andCtrl+Q(close-focused-split). All five chord arms now accept both lowercase and uppercase Char so they survive CapsLock + Shift+Ctrl variants. Plain-q quit-confirm guarded withis_empty()modifier check so plain-q doesn’t shadowCtrl+qclose- focused-split.
[0.7.1] — 2026-05-04
Changed
- Plugin slash commands renamed:
/teamctl:teamctl-init→/teamctl:init,/teamctl:teamctl→/teamctl:adjust. Operators upgrading must use the new invocation forms. /teamctl:initStage 6 now defers toteamctl bot setuprather than wrapping the BotFather/token/chat-id wizard inline. Onboarding now points the user at the CLI wizard for Telegram setup instead of running it through the model.- README rewritten to a tighter two-section onboarding shape (Interactive Setup with the Claude Code plugin + Manual setup), -53 lines net. Comparison content moved to https://teamctl.run/compare/. Hero motto updated to “Run real AI agent teams from one YAML. Each agent is a long-lived CLI process.” with a body soft-analogy for readers who know docker-compose.
Fixed
- TUI layout-switch chord
Ctrl+W/Ctrl+Mnow triggers correctly when CapsLock is engaged or Shift is held alongside Ctrl. Prior arms only matched lowercaseChar('w')/Char('m'), so the chord died silently on uppercase variants. - TUI DM compose modal accepts
Alt+Enteras a universal send chord on standard terminals (xterm, Terminal.app, tmux). PriorCtrl+Enterwas the only wired chord, but standard terminals strip the Control modifier from Enter so the chord was unreachable except on kitty-keyboard-protocol terminals.Ctrl+Enterstill works on those terminals.
[0.7.0] — 2026-05-03
0.7.0 ships the Claude Code plugin. Install once (claude plugin marketplace add https://github.com/Alireza29675/teamctl && claude plugin install teamctl@teamctl), invoke /teamctl-init, and you’re walked from no-teamctl-installed through a running supervised team in tmux in a few minutes. The plugin is teamctl’s onboarding from inside Claude Code; the .team/ directory it produces is the same hand-authorable YAML you’ve always had, byte-for-byte indistinguishable from one you’d type yourself. Parallel plugins for OpenCode, Codex CLI, and Gemini CLI are tracked at #59, #60, #61.
Added
- Claude Code plugin at
plugins/claude-code/(T-077). Two slash-invokable commands ship:/teamctl-initwalks an operator from no-teamctl-installed to a running supervised team in tmux through a 7-stage flow (detect+install → pick a team shape from four named defaults → propose a named ASCII org tree → scaffold.team/to matchexamples/<chosen>/.team/byte-for-byte with role prompts generated against an 8-section spine → reveal beat →teamctl up→ defer toteamctl bot setupfor Telegram + voice-customize per manager → hand the keys back with the three lifecycle commands)./teamctlis the open-ended ongoing skill the operator keeps invoking afterwards: five v1 verbs (add manager, add worker, scope channel, wire telegram, retire agent) each running a Read → Propose → Confirm → Apply → Validate → Offer-reload loop with unified-diff receipts and substrate constraint #4 enforced (every action reproducible byvim .team/team-compose.yaml). Repo-root.claude-plugin/marketplace.jsonregisters teamctl as a single-plugin marketplace; install viaclaude plugin marketplace add https://github.com/Alireza29675/teamctl && claude plugin install teamctl@teamctl. - Comment-preserving YAML edit substrate at
team-core::yaml_edit(T-077-E-prereq). Wrapsyaml-editwith a bounded line-anchored helper for nested-block insertion.teamctl bot setup’sinterfaces.telegramupsert path now routes through the substrate, preserving comments, blank-line clusters, and key ordering across edits — closing the recurring.team/projects/<id>.yamlround-trip regression class observed across 0.5.x and 0.6.x cascades. examples/solo-triage/as the fourth named-default team folder (T-077-B-prereq). Manager + research worker + inbox/journal worker; HITL onpublishandexternal_email. Mirrorsoss-maintainer/’s shape; serves as the byte-for-byte diff target for the plugin’s scaffolding when the operator picks “Solo triage.”- Repo-root
CLAUDE.md(T-077-F) carrying the cross-cutting rule that every release or substantive change to teamctl must consider impact on the plugin, the TUI, the docs, and the tests. Plus the 4-bullet behavioural-guidelines spine (think before coding, simplicity first, surgical changes, goal-driven execution). - Three sister-plugin GitHub issues for OpenCode CLI (#59), Codex CLI (#60), and Gemini CLI (#61). Each carries the spine sentence, the four substrate constraints, and links to the marketing positioning thread. External contributors can pick them up against the canonical Claude Code plugin reference.
Changed
- Examples env-var naming aligned to the canonical
TEAMCTL_TG_<NAME>_TOKEN/CHATSpattern (T-077-C-prereq). All five example folders’.env.exampleandREADME.mdfiles now match the YAML-side env-var references — closing a drift class where copying.env.exampleliterally would have set env vars the YAML didn’t read.startup-teamandmarket-analystsalso gained yaml-canonical alignments (PRODUCT_BOT_*→TEAMCTL_TG_PRODUCT_MANAGER_*;MARKETS_*→TEAMCTL_TG_CHIEF_*).
Notes
- Tagged history note: 0.5.2, 0.6.2, 0.6.3, and 0.6.4 were released
on
mainas version-bumpedCargo.toml+ CHANGELOG entries but were not tagged on origin (cargo-dist publish was not triggered for those bumps). 0.7.0 is the next tagged release afterv0.6.1, superseding the 0.6.x untagged series.
[0.6.4] — 2026-05-03
Fixed
reply_to_userfanned out to every Telegram bot in the project.team-bot’s outbound loop only filtered reply rows byproject_id, so when a project ran one bot per manager (e.g.pm,eng_lead,marketingall insooleh), every bot forwarded every reply and the operator received the same message three times under three bot avatars. The forward loop now appliesshould_routeper row — mirroring the approvals path — so only the manager-scoped bot whose chainmanager_of(sender)matches actually surfaces the reply. Unscoped bots keep the back-compat fallback (forward everything).
Changed
- Reply attribution moved to the end of the message. Forwarded
replies used to lead with
[sender] body, which buried the actual content behind a tag the reader already knew (the bot avatar identifies the manager). Now the body comes first and the sender is appended as\n\n— replied by <sender>, so the message reads naturally and the attribution is a footer. - Expanded
reply_to_userMCP tool description. The tool now spells out for the model that it is the only channel back to the human (stdout never reaches the operator), that proactive replies are welcome, and that long-running work should ack first then reply on completion. Thetextfield documents that delivery is plain text — no markdown, no headings, no code fences — and recommends sparing emoji use for scanability.thread_idnow has a description (group the reply with the inbound channel meta’sthread_id; omit for a fresh thread).
[0.6.3] — 2026-05-03
Fixed
- Claude Code Channels never fired in-session.
team-mcp’sinitializeresponse advertised only thetoolscapability, so Claude Code did not register anotifications/claude/channellistener and silently dropped every event the notifier emitted — mailbox rows accumulated without surfacing as<channel source="team">events. Initialize now declaresexperimental.claude/channel: {}(the documented capability that registers the listener), ships a recommendedinstructionsstring, and renamesserverInfo.namefromteam-mcptoteamso the rendered tag matches the.mcp.jsonkey and the bootstrap prompt. - Channel notifications were dropped as wire-format violations.
params.metaisRecord<string, string>per the Channels reference, but the notifier emittedid/sent_atas numbers andthread_idasnullwhen unset. Claude Code dropped the malformed events silently, so even with the listener registered the agent never saw a<channel>tag — it was reaching the message only through the oldinbox_watchlong-poll. All meta values are now strings, andthread_idis omitted when not set. - Agent wrapper used
--channelsfor an off-allowlist server. Custom channels are silently dropped by--channelsduring the research preview. Wrapper now uses--dangerously-load-development-channels server:team --(with the--separator so the variadic flag does not swallow the bootstrap prompt). - Dev-channels confirmation dialog stranded agents on every restart. Claude Code prompts “I am using this for local development” each time it boots with a non-allowlisted dev channel, with no persistent acceptance. Wrapper now side-spawns a watcher that polls its own tmux pane for the dialog header and presses Enter once, then exits (60 s deadline; no-op once team-mcp is allowlisted or when running outside tmux).
[0.6.2] — 2026-05-02
Fixed
teamctl upfailed whenproject.cwdwas a relative path. The rendered per-agent env file omittedTEAMCTL_ROOT, so the wrapper fell back toCLAUDE_PROJECT_DIR(often a literal..). After the wrapper’scd "$CLAUDE_PROJECT_DIR", the subsequentteamctl --root ".." rl-watch …resolved one directory above the intended.team/, and the runtime crash-looped withread …/team-compose.yaml: No such file or directory. Renderer now emits an absoluteTEAMCTL_ROOT=<compose.root>so--rootis pinned regardless of post-cdcwd.- Agent-wrapper crashed under
set -ufor agents without aneffort:field. The renderer only emitsEFFORT=for agents that set it, but the wrapper unconditionally referenced$EFFORTvia[ -n "$EFFORT" ]. Withset -uactive, that aborted the wrapper before exec — visible only after theTEAMCTL_ROOTfix let the wrapper progress past compose loading. Wrapper now defaultsEFFORTto empty alongside the other optional vars.
[0.6.1] — 2026-05-02
Added
teamctl update— self-update command. Detects the install method fromcurrent_exe()’s path (Cellar/teamctl → Homebrew,~/.cargo/bin/→ cargo, otherwise the shell installer) and re-runs the matching update flow. Checks GitHub Releases for the latest version first; no-ops when already current. Flags:--check(just print the version comparison),--yes(skip confirmation),--method <shell|brew|cargo>(override autodetect). New guide at/guides/updating/. Closes the gap that caused v0.5.2 and v0.6.0 to ship late — once update is in the wild, operators can pull each release without remembering the curl-pipe by hand.
[0.6.0] — 2026-05-02
Added
teamctl bot setup— interactive 1:1 Telegram bot wizard. Walks BotFather → token →/start→ chat id for every manager, prompts for env-var names with sensible defaults, writes.team/.env(idempotent upsert; existing vars preserved), and adds aninterfaces.telegramblock to that manager inprojects/<id>.yaml. Resumable: fully-configured managers skip silently, partials only re-ask for the missing piece (token or chat id), and YAML-fixed env-var names are reused without re-prompting. Positional[manager]arg scopes the wizard (teamctl bot setup news:head_editor);--forcere-asks for everything. Siblingbot listshows env-var status;bot statusshows running tmux sessions. ADR 0005.- Per-manager Telegram bots auto-spawn under
teamctl up. Oneteam-bottmux session per manager-with-interfaces.telegram, named<prefix>bot-<project>-<role>, scoped via--managerso each bot only sees its manager’s traffic.teamctl downstops them alongside agents. Skips with a warning when the token env var is unset (no hard fail — agents still come up). - DM-the-bot routing in
team-bot. Plain text on a manager-scoped bot is now treated as a message to that manager; no/dm role textceremony required. The/startand/helpreplies on a scoped bot tell the operator which manager they’re talking to./dm,/pending, and inline approval buttons remain as escape hatches.
Changed
- Telegram config moved from top-level
interfaces:to per-managerinterfaces.telegram. The new shape lives directly on the manager definition inprojects/<id>.yaml, keeping related fields together and removing a YAML cross-reference. The top-levelinterfaces:array is reserved for non-Telegram adapters (Discord, iMessage, CLI, webhook) — those still fit the array-of-named-channels shape better. telegram_inbox: trueis removed. Presence ofinterfaces.telegramon a manager is the new “this manager receives Telegram forwards” signal. Validation now flags aninterfaces.telegramblock on a worker the same way the oldtelegram_inbox: trueflag did.reports_to_user: trueis removed. The flag was already functionally inert —reply_to_usergates onis_manager, not this — and overlapped semantically withinterfaces.telegram. Dropping it is a strict simplification: one fewer field in the schema, the docs, the templates, and every example. Old YAMLs carrying the line still parse (the field is silently ignored, no hard break).- Examples (
startup-team,oss-maintainer,indie-game-studio,market-analysts,hello-team) and the dogfood.team/migrated to the new shape; their.env.exampleentries align with theTEAMCTL_TG_<MANAGER>_TOKEN/_CHATSdefaults the wizard picks.
Migration
- If you wired Telegram by hand via the old top-level `interfaces:
- type: telegram
block,team-botkeeps running against whatever you start manually. To switch to auto-spawn, runteamctl bot setup(it will skip managers whose env vars are already populated unless you pass—force`) and remove the legacy top-level entry.
- type: telegram
- If you had
telegram_inbox: trueorreports_to_user: trueon any agent, drop the lines — neither is in the new schema. They’re silently ignored on existing YAML, but cleaning them up is the intended end state. The validator will tell you if any worker accidentally inherits aninterfaces.telegramblock.
[0.5.2] — 2026-05-02
Added
team-mcppushes new mail as Claude Code Channels notifications. When the connected client is Claude Code v2.1.80+ launched with--channels server:team,team-mcpemitsnotifications/claude/channelfor every new inbox row addressed to the agent. The runtime injects each event as a<channel source="team">tag, so agents react on arrival without polling and idle silently between events. The wrapper sets the flag automatically for the claude-code runtime; bootstrap prompt rewritten to expect channel events and useinbox_peekfor restart catch-up only. Codex/Gemini paths unchanged. README and ROADMAP have promised this since v0.2.9 — first release that actually ships it.
[0.5.1] — 2026-05-02
Fixed
teamctl uiapprove modal accepts lowercasey. Previous uppercase-only matcher meantydid nothing — operators concluded the modal was broken. Asymmetric chord shape now:yorYapprove (loose, common path);Nonly deny (strict, preserves destructive-deny Shift-gate). Modal label and help overlay both reflect the new shape.- Tutorial body wraps to modal width. Long step descriptions
no longer extend past the modal —
Wrap { trim: true }on the Paragraph render. - Tab cycles pane focus uniformly. Previously Tab cycled INTO
mailbox tabs (Inbox → Channel → Wire) instead of moving to the
next pane — operators got stuck. Tab now consistently cycles
Roster → Detail → Mailbox → Roster across all panes. New
[and]chords walk mailbox tabs when Mailbox is focused (vim[t/]tmental model). - Statusline pins Tab pane-cycle hint always-visible. First segment of every statusline now reads “Tab cycle panes” so the chord is discoverable from the very first launch. Mailbox-focused contextual hint updated to ”[ / ] tabs.”
- Tmux ANSI colors render in detail pane. Captured agent output
now passes through
tmux capture-pane -eand parses throughansi-to-tui(MIT, MSRV 1.78). Falls back to raw text on parse error so malformed escapes don’t crash the render.
Notes
- Release-pipeline gap caught alongside this patch — cargo-dist
smoke-test case-statement order matters when binary-name prefixes
overlap (
teamctl-uivsteamctl). Always put longest-prefix branches first. Same bug shape as the splash isometric4 figlet glyph collision. - TUI bug cluster (1) detail-pane height + (2) mailbox-bottom-half layout (operator preference) tracked as T-074 PR #2; ships as 0.5.2.
[0.5.0] — 2026-05-02
Added
teamctl-ui— terminal control room for autonomous agent teams. Ships as a sibling crate (cargo install teamctl-ui) that the mainteamctlbinary can launch via the newteamctl uisubcommand wrapper. Triptych layout (Roster / Detail / Mailbox) with state-glyph priority indicators on every agent; live tmux pane streaming for the focused agent; mailbox tabs (Inbox / Channel / Wire) with notify-based file-watch for real-time updates; approvals stripe + modal that route writes through the existingteamctl approve|denyCLI to preservedelivered_atcontracts; vim-keyed compose modal (@DM /!broadcast with per-channel picker) sending viateamctl send|broadcast; Wall and MailboxFirst alternate layouts (Ctrl+W/Ctrl+M); split- screen with vertical/horizontal orientation per cell (Ctrl+|/Ctrl+-) andCtrl+W q/ochord-prefix navigation;?help overlay reading from the same keymap registry the event loop uses; first-launch onboarding tutorial (tto reopen). 110 tests; capability-aware theming degrades cleanly to monochrome.teamctl uisubcommand in the main binary. Detectsteamctl-uion PATH and execs it with clean process handoff (Unix) or spawn-and-propagate-exit-code (Windows); friendly install hint with explicit[y/N]prompt when missing.--no-promptflag for non-interactive shells / CI.- Per-agent
effort:field on the team-compose schema. Acceptslow | medium | high | xhigh | maxand flows through toclaude --effortat spawn time. Strict-enum validation rejects typos with a clear error citing the offending agent. - Project-as-code dogfood — teamctl ships a
.team/directory inside its own repo demonstrating the.team/walk-up convention end-to-end on the project that maintains itself.
Changed
- Approval routing invariant tightened across all decide call sites
(CLI + Telegram callback). Status pin now precedes the
delivered_atflip, with the flip gated on a successful pin — preserving theundeliverable ↔ delivered_at IS NULLinvariant against late stale taps. - CLI approval decisions now use a single fractional-seconds
now()call threaded through bothdelivered_atanddecided_atwrites, matching the broker’sstore::now()precision and column affinity. Supervisor::drainextracted intoorchestrate_drainwith a testable trait-method poll interval (default 250ms). Drain contract end-to-end pinned by mock-host tests including the timeout=0 fast-path.- README links retargeted at the live docs site
(
https://teamctl.run/...) instead of repo-relative paths that 404 on GitHub renders.
Fixed
- Installer prints actionable shell-tailored PATH hint when the
install dir isn’t on
$PATH(zsh / bash / fish / fallback profile). Friendly without auto-mutating — copy-paste one-liner, never edits operator rc files.
Notes
- TUI bug cluster from operator first-trial (T-074) — modal keymap discoverability, tmux color pass-through, focus-cycle semantics, layout-height polish — landing as 0.5.1 follow-up. This release ships the cascade substance; the polish iteration follows immediately.
[0.4.0] — 2026-04-30
Added
teamctl initsubcommand. Drops a.team/skeleton into the current directory (or any path passed as a positional). Two templates today —solo(single agent, single channel — the default and the right starting point for “drop teamctl into this project”) andblank(empty.team/ready to fill in). Refuses to overwrite an existing non-empty.team/without--force. Generated files include short prose comments explaining what to edit next.- Snapshot v2 + first-class
ReloadPlan.teamctl reload --dry-runnow prints the plan that would execute — adds, removes, restarts, and skips — without touching anything. Snapshot hashing is deterministic across runs (blake3 over normalised inputs), so “did this agent’s config change?” stops flapping on Rust’s per-processDefaultHashersalt. - Reload drain. When an agent gets restarted by reload, its
in-flight work is given a chance to finish first. Configurable
via
drain_timeout_secsinteam-compose.yaml(default: 10 seconds; cap 600).0short-circuits to instant restart for the cases where you really mean it. - First-class
effortfield on the per-agent schema inteam-compose.yaml. Acceptslow | medium | high | xhigh | max; renders toEFFORT=<level>in the generated agent env and flows through toclaude --effort <level>. Precedence: per-agent YAML > workspace.env> wrapper default. Strict enum — typos likehgihfail compose validation loudly with the offending agent named. - Reload now persists each agent’s tmux session name in the
snapshot, so removing or restarting an agent always targets
the right session — even if
supervisor.tmux_prefixwas changed between reloads.
Changed
.team/is now the canonical project root. Discovery walks up from cwd to the first.team/it finds and runs that team — npm/yarn shape, no auto-register-context magic. Operatorscdinto the project they’re working on (or pass-C <path>) andteamctl up/reload/psresolve naturally.- Worktree-friendly runtime state. Each
.team/state/is now intended to be gitignored; per-worktree runtime state lives inside the worktree’s own.team/, while the.team/source layout (compose, roles, projects) is shared via git. Two worktrees of the same repo can run two independent agent teams side by side. examples/*restructured to the.team/convention. Every example now runs withcd examples/<name> && teamctl up— no-Cflags. Theoss-maintainerexample demonstrates a non-defaulteffort:field; new cookbook entry at/cookbook/effort/documents the field, the five accepted values, and the precedence rule.- README rewritten with a project-voice “Getting started” arc
showing the canonical flow:
cd /path/to/your/project,teamctl init,teamctl up,teamctl reload. Frames teamctl as the team-of-agents that fits into your existing project, not a project scaffolder. The Mermaid diagram is gone.
Deprecated
teamctl context. The.team/walk-up replaces every shape the registered-context model used to handle. The command still works in 0.4.0 with a stderr deprecation note; scheduled for removal in 0.5.0. Migrate bycd-ing into the project root (or using-C <path>) before running teamctl commands; if you usedteamctl context use <path>to pin a default, the new shape is to put a.team/in that path.
[0.3.0] — 2026-04-30
Added
- Per-manager bot scoping for Telegram approval routing. Approval
cards now reach exactly one chat — the bot scoped to the manager
that the requesting agent reports to — instead of fanning out to
every connected bot. Routing follows the worker’s direct
reports_toonly; deeper manager hierarchies (worker → team-lead → manager) are tracked as a follow-up. - Approval delivery state on the broker. The
approvalstable grows a nullabledelivered_at REALcolumn and a new terminal statusundeliverable. Whenexpires_atelapses, rows withdelivered_at IS NULLend asundeliverable; rows that were surfaced to a human end asexpired(existing behaviour). Callers can now distinguish “the human never saw the prompt” from “the human declined to respond.” wait: boolargument on therequest_approvalMCP tool (defaulttrue).wait: falsereturns the freshly inserted row’s status immediately, skipping the long-poll — useful for fire-and-forget callers and diagnostic tooling.- Telegram approval cards now resolve in place. Tapping Approve
or Reject edits the message to show the outcome and removes the
buttons. Stale taps on a duplicate copy answer with
#<id> already resolvedand leave the row untouched. - Plain-text rendering for outbound Telegram messages. Markdown
syntax (
**bold**,_italic_,- bullets) is stripped before send so chat surfaces don’t render literal punctuation. Buttons (approval cards) are unaffected. - Context-override warning on read-side commands.
teamctl ps,mail, andinspectnow print a stderr note when active context orTEAMCTL_ROOToverrides walk-up resolution, with the source of the override called out (CLI flag vs environment). oss-maintainerexample. Pipeline workflow + cross-channel ACLs- plan-mode HITL on release-critical actions. Demonstrates a triage / bug-fix / docs / release-manager team for an open-source maintainer.
indie-game-studioexample. Plan-mode dissenter on a creative team + private critique channel. Demonstrates a director / designer / writer / playtest-critic team where the critic vetoes privately rather than publicly.- Cookbook section under
docs/cookbook/. Captures patterns from examples that are too narrow to ship as their own example folder (multi-agent ACL composition, multi-runtime cohabitation, cross-project bridges). - Lychee link-checker on the docs CI. Internal link breakage fails
PRs that touch
docs/; external links warn-only to keep the check stable against third-party HTTP flakiness.
Changed
- Author voice across source code, doc-comments, operator-references,
example fixtures, and landing copy is now project-voice — the
project speaks as itself rather than through a personal first-person
maker. Author attribution metadata (LICENSE copyright, Cargo
authors, ADR
Author:lines) is preserved as factual. - Cookbook prose for the
oss-maintainerexample softened to match what the example actually demonstrates (single-project) rather than the cross-project framing that lived in earlier drafts. - Docs deploy workflow’s deploy step now runs on both
pushtomainandworkflow_dispatch, so manual redeploys viagh workflow run docs.ymlactually deploy.
Removed
- Deprecated example folders:
multi-agent,multi-runtime,two-projects. The patterns they demonstrated (channels + ACL composition, multi-runtime cohabitation, project bridges) survive instartup-team,newsletter-office,oss-maintainer,indie-game-studio, and the new cookbook recipes. WhyIBuiltThis.astrolanding-page section. Was a placeholder waiting on a personal-voice interview that the project-voice shift retired.
[0.2.9] — 2026-04-26
Added
reply_to_userMCP tool. Managers (is_manager: true) can now talk back to the human operator who DMed them; the configured interface adapter (Telegram, Discord, …) forwards the reply. Inserts a message row withrecipient = "user:telegram". Workers calling it get an explicit error — inter-agent traffic stays ondm. Companion:Store::is_manager(agent_id)lookup against theagentstable.- Telegram bot bootstrap UX. A
/startfrom a chat that isn’t on the allow list now replies with the chat’s numeric id and a copy-paste hint for.env, removing the @userinfobot detour during first-run setup.TEAMCTL_TELEGRAM_CHATSaccepts an empty value to make bootstrap reachable.
Changed
- Telegram bot’s outbound stream now forwards messages whose
recipient = 'user:telegram'(thereply_to_useroutput) and ack’s them viaacked_at. Previously it forwarded messages going into managers, which surfaced inbound traffic instead of outbound replies. .gitignore: added.envand**/.envso Telegram tokens and per-team secrets don’t get committed.
[0.2.8] — 2026-04-26
Fixed
- aarch64-unknown-linux-gnu Release builds, take 4. With the cross-gcc
installed (v0.2.7), the C parts compiled but the Rust linker still
defaulted to the host’s x86_64
rust-lld, producing “is incompatible with elf64-x86-64” on every aarch64 object. Added.cargo/config.tomlwithtarget.aarch64-unknown-linux-gnu.linker = "aarch64-linux-gnu-gcc"so cargo invokes the cross linker for that target.
[0.2.7] — 2026-04-26
Fixed
- aarch64-unknown-linux-gnu Release builds (final). Even with rustls
in v0.2.6,
ring(rustls’s crypto provider) needs to compile its ARM assembly usingaarch64-linux-gnu-gcc, which the GitHub Actions ubuntu-24.04 runner doesn’t ship by default. Configured cargo-dist’s[workspace.metadata.dist.dependencies.apt]to installgcc-aarch64-linux-gnuonly on the aarch64-linux build matrix entry, so cc-rs auto-resolves the cross compiler.
[0.2.6] — 2026-04-26
Changed
team-botnow uses rustls instead of native-tls. Vendoring OpenSSL in v0.2.5 wasn’t enough — building openssl-src from source also needsaarch64-linux-gnu-gcc, which isn’t on the GitHub Actions cross-build runner. rustls is pure Rust with zero C dependencies, so it cross-compiles cleanly to every dist target. Switched teloxide’s features todefault-features = false+["macros", "ctrlc_handler", "rustls"].
[0.2.5] — 2026-04-26
Fixed
- (intended) aarch64-unknown-linux-gnu Release builds via vendored
OpenSSL. Released to crates.io but the build still failed because
the openssl-src vendored build still requires
aarch64-linux-gnu-gccwhich isn’t installed on the runner. Superseded by 0.2.6’s switch to rustls.
[0.2.4] — 2026-04-26
Fixed
- Release builds for every platform. cargo-dist 0.25.1’s default runner
labels (
ubuntu-20.04,macos-13) were both retired by GitHub Actions in 2025-2026 — jobs targeting them sit queued forever. Override every target via inlinegithub-custom-runners = { x86_64-unknown-linux-gnu = "ubuntu-24.04", aarch64-unknown-linux-gnu = "ubuntu-24.04", x86_64-apple-darwin = "macos-14", aarch64-apple-darwin = "macos-14" }. v0.2.3 attempted this with the[workspace.metadata.dist.github-custom-runners]table syntax; cargo-dist 0.25.1’s deserializer rejects that with “invalid type: sequence, expected a string” — the inline-table form is what the v0 schema actually accepts.
[0.2.3] — 2026-04-26
Fixed
- (intended) macOS Release builds via
github-custom-runnerstable. Released to crates.io but the Release workflow rejected the table syntax. Superseded by 0.2.4’s inline form.
[0.2.2] — 2026-04-26
Fixed
- Release pipeline. v0.2.0 and v0.2.1 published to crates.io but
produced no GitHub Release artifacts (no platform tarballs, no
Homebrew formula bump) because
dist hostexited 255 on a freshness check: the hand-editedruns-on: ubuntu-24.04inrelease.ymldiverges from whatcargo-dist 0.25.1would generate (ubuntu-20.04, retired by GitHub Actions in April 2026). Addingallow-dirty = ["ci"]to the dist metadata tells dist to skip the workflow-freshness diff so releases unblock. - Docs build (Astro Starlight). The Astro 4.16 / Starlight 0.29 pin
pulled in newer transitive
zodversions whose internal v4 API layout brokezod-to-json-schema. Bumped to Astro 5 + Starlight 0.30, both of which handle modern zod cleanly.
[0.2.1] — 2026-04-26
Changed
teamctl rl-watchnow spawns the runtime under a real pseudo-terminal (viaportable-pty) and forwards stdin from the wrapper’s controlling TTY. Without this, runtimes detected non-TTY stdio and silently dropped into one-shot/print mode — sotmux attach -t a-<agent>showed a five-second restart loop instead of an interactive Claude Code REPL. Rate-limit pattern scanning is preserved by tee-ing the pty’s output through an ANSI-stripping line scanner before re-emitting it.agent-wrapper.shnow passes runtime arguments as properargvtoteamctl rl-watch -- "$BIN" "$@"instead of round-tripping them through a single$BIN_ARGSstring. The old shape silently word-split multi-word values like--append-system-prompt "$(cat role.md)", feeding the runtime garbage. The wrapper also appends a configurableBOOTSTRAP_PROMPT(defaults to “Begin your shift as. Open inbox_watch via team MCP. Stay running.”) so agents enter their work loop on launch instead of sitting at an empty prompt. teamctl uprewritesbin/agent-wrapper.shwhenever the on-disk copy differs from the binary’s bundled template. Previously the wrapper was written only on first launch, so upgrading teamctl never delivered wrapper fixes to existing workspaces.teamctl upauto-accepts Claude Code’s per-workspace trust dialog for every cwd that will host aclaude-codeagent (writeshasTrustDialogAccepted: trueinto~/.claude.json). Runningteamctl upis itself an explicit “I trust this directory” signal — without this, the runtime blocks on a trust prompt the moment it boots and defeats the “agents start working when teamctl up runs” model.claude-codeagents now launch with--dangerously-skip-permissionsin addition to whateverpermission_mode:the agent sets. Auto mode in Claude Code still prompts for tool calls its risk classifier deems sensitive (anything matchingclaude mcp *,git push, …). With no human at the keyboard those prompts deadlock the pane, so the classifier becomes advisory and the prompt is suppressed. The proper human-in-loop ring for teamctl is the team-mcprequest_approvaltool gated by the agent’sautonomy:field — not the per-tool-call prompt buried inside the runtime.
Fixed
- Runtime adapter descriptors for the three shipped runtimes (Claude Code,
Codex, Gemini) are now embedded in the
team-corebinary instead of being read from aruntimes/directory at the compose root. Without this, every fresh install (teamctl init+teamctl up, or anycargo install/ Homebrew /install.shflow) tight-looped withruntime 'claude-code' for agent 'X' has no descriptor in runtimes/because the YAMLs only existed inside the source tree and were never packaged.<root>/runtimes/<id>.yamlcontinues to work as an override, matching the design intent in ADR 0004 (“optional overrides for shipped runtimes”). Validator andrl-watcherror messages now reflect that the missing-runtime case means no built-in and no override.
[0.1.2] — 2026-04-25
Fixed
- Release pipeline now produces GitHub Release artifacts. v0.1.1 published
to crates.io but the hand-written cargo-dist workflow ran cross-compile
on a single Ubuntu runner, so no platform tarballs were ever uploaded.
Regenerated
release.ymlfromdist generate(proper job matrix) and split crates.io publishing into a siblingpublish-crates.yml.
[0.1.1] — 2026-04-25
Added
- Rate-limit handling. Every runtime invocation flows through
teamctl rl-watch, which detects rate-limit signatures from the runtime’srate_limit_patterns, records them in a newrate_limitstable, runs a configurable hook chain (wait/send/webhook/run), and waits until the limit clears before letting the wrapper respawn — replacing the previous 5-second tight retry. - Per-agent
on_rate_limit:override and a globalrate_limits.hooks:block withdefault_on_hitchain. - Runtime descriptor field:
rate_limit_patternswith optionalresets_at_capture/resets_in_captureregexes. - Docs:
docs/concepts/rate-limits.md.
[0.1.0] — 2026-04-25
Added
team-core— YAML schema, validator, renderer,Supervisortrait with portableTmuxSupervisor.team-mcp— stdio JSON-RPC MCP server withwhoami,dm,broadcast,inbox_peek/ack/watch,list_team,org_chart,request_approval.teamctlCLI —validate,up,down,reload,status,logs,send,bridge open/close/list/log,pending,approve,deny,budget,gc.team-bot— Telegram interface adapter with inline approval UI and--managerscoping.- Runtime adapters for Claude Code, Codex CLI, Gemini CLI.
- Project isolation; time-boxed inter-project manager bridges; HITL permission fabric with default sensitive-action list.
- Interfaces abstraction (Telegram, Discord, iMessage, CLI, webhook — Telegram adapter shipped; others documented).
- Astro Starlight docs site scaffold + Cloudflare Pages deploy workflow.
cargo-distrelease pipeline,install.sh, Homebrew tap config, crates.io publish.- Examples:
hello-team,multi-agent,multi-runtime,two-projects,newsletter-office,startup-team,market-analysts. - 28 unit + integration tests.