██╗ ██╗███████╗██████╗ ████████╗ ██║ ██║██╔════╝██╔══██╗╚══██╔══╝ ██║ █╗ ██║█████╗ ██████╔╝ ██║ ██║███╗██║██╔══╝ ██╔══██╗ ██║ ╚███╔███╔╝███████╗██║ ██║ ██║ ╚══╝╚══╝ ╚══════╝╚═╝ ╚═╝ ╚═╝

A terminal-native team collaboration tool for your local network. Tasks, chat, GitHub activity — all in one TUI.

What is wert?

wert is a CLI tool that lets a small team share a live workspace over a LAN. One person runs the server (admin), everyone else joins as a member. You get a full-screen terminal UI with real-time task tracking, team chat, and optional GitHub integration — no cloud required.

Everything happens over WebSocket on your local network. No accounts, no SaaS, no internet required (except for optional GitHub integration).

Architecture

┌─────────────────────────────────────────┐ │ wert serve │ ← admin machine │ ┌──────────┐ ┌───────────────────────┐│ │ │ HTTP/WS │ │ Store (JSON on disk) ││ │ │ :8080 │ │ tasks + messages ││ │ └──────────┘ └───────────────────────┘│ └──────────────────┬──────────────────────┘ │ WebSocket ┌───────────┼───────────┐ ▼ ▼ ▼ wert join wert join wert mcp (member) (member) (Claude)
img not found

Key concepts

Download

Prebuilt binaries for macOS, Linux, and Windows. No dependencies, no runtime.

Statically compiled — just download, make executable, and move to your PATH. Or use wert update once installed to always stay current.

macOS

macOS
Apple Silicon — M1 / M2 / M3
Download
macOS
Intel — x86_64
Download
# after downloading, make it executable and move to PATH
chmod +x wert-darwin-arm64
mv wert-darwin-arm64 /usr/local/bin/wert

Linux

Linux
amd64 — most servers & desktops
Download
Linux
arm64 — Raspberry Pi, Graviton
Download
chmod +x wert-linux-amd64
sudo mv wert-linux-amd64 /usr/local/bin/wert

Windows

Windows
amd64 — most PCs
Download
Windows
arm64 — Surface Pro X, Snapdragon
Download

Rename the downloaded file to wert.exe and move it to a folder in your PATH, e.g. C:\Windows\System32\ or any custom folder you have in %PATH%.

Verify checksum

checksums.txt
SHA-256 for all platform binaries
Download
sha256sum -c checksums.txt --ignore-missing

All releases

Browse all versions and changelogs on GitHub Releases →

Self-update

Once installed, wert can update itself in one command:

wert update

Or type :update in the cmdline inside the TUI. It fetches the latest binary, replaces the current one, and relaunches automatically.

Build from source

Requires Go 1.22+.

Build

git clone https://github.com/mugiwaraluffy56/wert
cd wert
go build -o wert .

# install to PATH
cp wert /usr/local/bin/wert

Cross-compile all platforms

make cross-build   # linux + darwin + windows × amd64/arm64 → dist/

Flags overview

commandflagdefaultdescription
wert serve--namerequiredyour display name
--port8080server listen port
--tokennonejoin password — anyone who knows it can connect as member
--datawert-data.jsonpersistence file path
wert join--hostrequiredserver address, e.g. :8080
--namerequiredyour display name
--tokennonejoin password (required if server was started with --token)
wert mcp--serverhttp://localhost:8080REST API base URL
--agent-namenonename to register in the agent registry

wert serve

Start the server and open the admin TUI. Run this on the host machine.

Basic usage

wert serve --name alice

With admin token

If you set a token, only people who pass it with --token get the admin role. Without a token, the server trusts the first WebSocket connection as admin.

wert serve --name alice --token secret123 --port 9000

Data persistence

The server writes tasks and chat history to a JSON file after every mutation. Members and the server address are in-memory only.

wert serve --name alice --data /var/wert/data.json
The server prints its local IP addresses on startup so team members know what address to use with wert join --host.

REST API

The server exposes a REST API at the same port, used by the MCP server and agents:

methodpathdescription
GET/api/memberslist connected members
GET/POST/api/taskslist or create tasks
PUT/DELETE/api/tasks/:idupdate status or delete task
POST/api/tasks/:id/claimclaim task {"agent":"name"}
POST/api/tasks/:id/unclaimrelease task claim
POST/api/messagessend a chat message
GET/api/watch?filter=typesSSE stream of all broadcast events
GET/POST/DELETE/api/agentsagent capability registry
POST/api/directdeliver private DM to one recipient
POST/api/resultspost structured agent result to team chat
GET/healthhealth check

wert join

Connect to an existing server as a team member.

Basic usage

wert join --host :8080 --name bob

If the server was started with --token, pass the same token:

wert join --host :8080 --name bob --token secret123

Join approval

First-time members wait for the admin to approve them:

Admins can also approve or reject from the input box: /accept <username> or /reject <username>.

Reconnection

If the connection drops, wert automatically retries up to 15 times (every 3 seconds). The status bar shows progress. TUI state is preserved during reconnection.

Roles

rolewhat you can do
admincreate tasks, assign to others, delete tasks, approve/reject joins, all member actions
memberupdate status on your own assigned tasks, send chat messages

MCP server

Let Claude manage your team's tasks, send messages, and coordinate with other agents.

Start the MCP server

wert mcp --server http://:8080 --agent-name claude

--agent-name registers this instance in the agent capability registry and stamps the agent's name on task updates. Omit it for anonymous access.

Claude Code config

Add wert to ~/.claude/settings.json:

{
  "mcpServers": {
    "wert": {
      "command": "wert",
      "args": ["mcp", "--server", "http://:8080", "--agent-name", "claude"]
    }
  }
}

Ask Claude to run team_context first — it returns all members and tasks in one call.

Available tools

tooldescription
team_contextstart here — full markdown: members, task counts, per-person breakdown with IDs and claimed status
get_tasksingle task by ID prefix — all fields including claimed_by
search_taskscase-insensitive keyword search across titles and descriptions
list_tasksall tasks, optionally filtered by assignee or status
list_membersconnected members and their online status
create_taskcreate and assign a task with title, description, assignee, priority, due date
update_taskchange status (todo / in_progress / done / blocked); stamps agent name as updated_by
delete_taskpermanently delete a task — prefer update_task to done instead
send_messagebroadcast a chat message to the whole team
get_dashboardmarkdown table: members, tasks per person, claimed status
claim_tasklock a task so other agents know you're working on it
unclaim_taskrelease a task claim when done or handing off
wait_for_changeblock until a team event fires (SSE push); optional type filter and timeout
send_direct_messageprivate message delivered only to a specific agent or member
post_resultpublish a structured result block (analysis, summaries, reviews) to team chat
register_capabilitiesannounce what this agent can do (visible in Members screen)
list_agentsdiscover registered agents and their capabilities
The MCP server uses the REST API only — no WebSocket. Use wait_for_change for push-based event delivery instead of polling.

Agent communication

Two AI agents can coordinate through wert: claim tasks, exchange private messages, publish results, and react to events in real time.

How it works

claude-1 ──claim_task──► wert server ◄──list_agents── claude-2 ──post_result──► ◄──wait_for_change── ──send_direct──► (only claude-2 receives it)

Typical agent workflow

  1. 1
    Discover other agents. Call list_agents to see who else is connected and what they can do.
  2. 2
    Claim a task. Call claim_task before starting work. Returns a conflict error if another agent already claimed it — prevents duplicate work.
  3. 3
    Do the work. Update the task to in_progress with update_task.
  4. 4
    Publish the result. Call post_result with a title and markdown content. It appears as a bordered result block in the team chat — distinct from regular messages.
  5. 5
    Hand off privately. Use send_direct_message to send instructions only to a specific agent or human. The rest of the team doesn't see it.
  6. 6
    Mark done and release. Call update_task to done, then unclaim_task.

Reacting to events with wait_for_change

Instead of polling, agents can block until something happens:

# wait for any event (30s timeout by default)
wait_for_change

# wait only for task updates or agent results
wait_for_change  filter="task_update,agent_result"  timeout=60

Event types: task_create, task_update, task_delete, task_claim, task_unclaim, agent_result, direct_message, chat, member_join, member_leave, agent_online

SSE endpoint (direct HTTP)

Non-MCP clients can subscribe to the event stream directly:

GET /api/watch
GET /api/watch?filter=task_update,agent_result

The server sends text/event-stream. Each event is data: {"type":"...","payload":{...}}. A keepalive comment is sent every 20 seconds.

Capability registry

Agents announce what they can do on startup. The registry is visible in the Members screen under "AI Agents".

# register via MCP tool
register_capabilities  capabilities="code_review,testing,analysis"

# or directly via REST
POST /api/agents  {"name": "claude", "capabilities": ["code_review", "analysis"]}

Registrations are in-memory only — agents re-register on each startup. The --agent-name flag on wert mcp handles this automatically.

Multi-agent config example

Two Claude instances with different roles in ~/.claude/settings.json:

{
  "mcpServers": {
    "wert-reviewer": {
      "command": "wert",
      "args": ["mcp", "--server", "http://localhost:8080", "--agent-name", "reviewer"]
    },
    "wert-deployer": {
      "command": "wert",
      "args": ["mcp", "--server", "http://localhost:8080", "--agent-name", "deployer"]
    }
  }
}

Home screen

Your landing screen with stats and quick navigation menu.

The Home screen shows the wert logo, a live summary (open tasks, done tasks, online members), and a navigation menu. It's purely informational — no input box.

Getting around from Home

Press 26 to jump directly to a screen, or open the cmdline with Shift+; and type a screen number.

The stats on Home update live as tasks are completed and members connect/disconnect.

Chat

Real-time team chat with @mentions and message history.

Sending messages

Type in the input box at the bottom and press Enter. Messages are broadcast to everyone connected. The last 100 messages are persisted on the server.

@mentions

Include @username in a message to mention someone. They'll see a notification badge in the status bar even if they're on a different screen.

Unread badge

When you're on any screen other than Chat and a new message arrives, an unread count badge appears in the header next to "2:Chat". It clears when you visit Chat.

Sending from Workstation

The Workstation screen has its own message input at the bottom. Messages sent there go to the same shared chat channel.

Tasks

Create, assign, track, and update tasks across the team.

Task statuses

statuscommandcolour
todo/todo <id>red
in_progress/wip <id>green
done/done <id>dim green
blocked/blocked <id>dim red

Sub-tabs and filters

Press number keys 15 while on the Tasks screen (when the input box is empty) to jump directly to a filter tab:

keyfilter
1All tasks
2Todo
3In Progress
4Done
5Blocked

You can also use [ and ] to cycle through filters left and right.

Creating tasks (admin)

# basic
/assign @bob "Fix login bug"

# with description and priority
/assign @alice "Deploy to staging" "Run deploy.sh on prod box" high

# with due date
/assign @bob "Write release notes" "" medium due:2025-01-31

Updating tasks

Use the short 8-character ID prefix shown in the task list:

/wip a1b2c3d4
/done a1b2c3d4
/blocked a1b2c3d4

Deleting tasks (admin)

/delete a1b2c3d4

Notification

When a new task is assigned to you, a notification appears in the status bar regardless of which screen you're on.

GitHub

View your org's repos, open PRs, and open issues — live from the GitHub API.

Quick setup

You need a Classic PAT with two scopes. See the GitHub setup guide → for a full step-by-step walkthrough.

/github setup --token ghp_xxxxxxxxxxxx --org your-org-or-username

Refresh

/github refresh

Sub-tabs

Press number keys 14 while on the GitHub screen (when the input box is empty) to jump to a tab:

keytabshows
1Overviewstat boxes, recently pushed repos, recent PRs, org members
2Reposfull repo table — name, description, stars, open issues, last push
3Pull Requestsopen PRs grouped by repo, with author and age
4Issuesopen issues grouped by repo, with labels, author, age

You can also use [ / ] to cycle through tabs.

Personal accounts

wert automatically falls back from the org: search qualifier to user:, and then to a per-repo search if needed. Personal accounts and orgs both work.

GitHub data is fetched on demand and cached in-memory. It's not persisted between restarts.

Workstation

A dedicated split-pane workspace with a message input. Screen 5.

Default layout

By default, Workstation shows Tasks on the left and Chat on the right, with a message input at the bottom. Navigate here with 5 or :5.

┌──────────────────┬──────────────────┐ │ Tasks │ Chat │ │ ─────────────── │ ─────────────── │ │ │ │ │ [TODO] Fix bug │ alice: shipped │ │ [WIP] Deploy │ bob: nice │ │ │ │ └──────────────────┴──────────────────┘ ┌─────────────────────────────────────┐ │ ▌ │ ← message input └─────────────────────────────────────┘

Split pane commands

Open the cmdline (Shift+;) and use sp to set a custom layout:

commandresult
sp h3full pane showing Tasks
sp v2current screen top, Chat bottom
sp h3 h2Tasks left, Chat right
sp v3 h2Tasks top, then Chat left + GitHub right in bottom half
clreset to default Tasks / Chat layout

Screen numbers for panes

numbercontent
1Home
2Chat
3Tasks
4GitHub
6Members

Message input

The input box at the bottom of Workstation sends messages to the shared chat channel — same as the Chat screen's input.

Members

See who's on the team, their role, task counts, and registered AI agents. Screen 6.

Navigate here with 6 or :6. Each row shows:

AI agents section

Below the human members, a separator shows registered AI agents with their capabilities. Agents appear when they connect via wert mcp --agent-name and disappear when they disconnect.

Members appear here once they've connected at least once. Offline members stay visible with their last-seen task stats. Agents are in-memory only — they re-register on each startup.

In Workstation panes

You can show the Members view as a pane inside Workstation using screen number 6:

:sp h6    # members pane, full width
:sp h3 h6 # tasks left, members right

Cmdline

A Vim-style floating command bar accessible from every screen.

Activating

Press Shift+; (i.e. :) from any screen. The cmdline appears as a centered floating box overlaid on the current content. Press Shift+; again or Esc to dismiss it.

╭──────────────── Cmdline ────────────────╮ │ ❯ 3▌ │ ╰─────────────────────────────────────────╯

Navigation commands

commandaction
16switch to that screen
q / q! / quitquit wert
docsopen the wert documentation in your browser

Workstation commands

commandaction
sp <dir><n> [<dir><n>]open Workstation with a split layout
cl / closereset Workstation to default layout

Split command syntax

v = vertical split (top / bottom), h = horizontal split (left / right), number = screen.

sp v3 h2   # tasks top, chat left + github right (bottom)
sp h3      # tasks only, full pane
sp h6 h3   # members left, tasks right

Split panes

Arrange multiple screen views side-by-side inside the Workstation.

How it works

The sp command builds a binary pane tree. Each argument adds a split:

Examples

sp h3 — Tasks, full width

┌───────────────────────────────────────┐ │ Tasks │ │ │ └───────────────────────────────────────┘

sp v2 — current top, Chat bottom

┌───────────────────────────────────────┐ │ (current screen) │ ├───────────────────────────────────────┤ │ Chat │ └───────────────────────────────────────┘

sp h3 h2 — Tasks left, Chat right

┌──────────────────┬────────────────────┐ │ Tasks │ Chat │ │ │ │ └──────────────────┴────────────────────┘

sp v3 h2 — Tasks top, Chat/GitHub bottom

┌───────────────────────────────────────┐ │ Tasks │ ├──────────────────┬────────────────────┤ │ Chat │ GitHub │ └──────────────────┴────────────────────┘

Reset

Type :cl in the cmdline to go back to the default Tasks + Chat layout.

Command reference

All slash commands and cmdline commands in one place.

Slash commands (input box)

commandroledescription
/helpallshow command reference in chat
/done <id>allmark task as done
/wip <id>allmark task as in progress
/blocked <id>allmark task as blocked
/todo <id>allreset task to todo
/membersallshow member list in chat
/assign @user "title" ["desc"] [priority] [due:YYYY-MM-DD]admincreate and assign a task
/delete <id>admindelete a task
/github setup --token <t> --org <o>allconfigure GitHub integration
/github refreshallrefresh GitHub data
/exit / /quitallquit wert

Cmdline commands (Shift+;)

commanddescription
16navigate to screen
q / q! / quitquit
updatedownload latest release and relaunch
docsopen documentation in browser
sp <v|h><n> [<v|h><n>]open Workstation with split layout
cl / closereset Workstation to default layout

Task priorities

valuedisplay
high^ high
medium (default)- med
lowv low

GitHub setup guide

Step-by-step: create a Classic PAT with the right scopes so wert can read your org's repos, PRs, and issues.

Use a Classic token — not Fine-grained. Fine-grained PATs have very limited GitHub Search API support and require org admin approval. Classic tokens work out of the box.

Step-by-step

  1. 1
    Open GitHub in your browser. Make sure you're signed in to the account that is a member of your org.
  2. 2
    Go to token settings.
    Click your profile photo (top-right) → Settings → scroll all the way to the bottom of the left sidebar → Developer settingsPersonal access tokensTokens (classic).
  3. 3
    Generate a new classic token.
    Click Generate new tokenGenerate new token (classic).
    GitHub may ask you to confirm your password.
  4. 4
    Fill in the token details.
    • Note: anything descriptive, e.g. wert-local
    • Expiration: set to 90 days or No expiration (your call)
  5. 5
    Select exactly these two scopes:
    repo REQUIRED

    Full control of private repositories. This lets wert list your repos and read PR/issue data from private repos. Check the top-level repo checkbox — it automatically selects all sub-scopes (repo:status, repo:deployment, public_repo, repo:invite, security_events).

    read:org REQUIRED

    Read org and team membership. This is what enables the GitHub Search API to accept org:yourorg as a search qualifier. Without this, you get a 422 Validation Failed error even if you own the org. It's under the admin:org section — expand it and check only read:org.

    You do not need admin:org, write:org, or any other scope. Just repo + read:org.
  6. 6
    Click "Generate token" at the bottom of the page.
    GitHub shows you the token exactly once. Copy it now — you cannot see it again.
    ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  7. 7
    If your org uses SAML SSO — authorize the token.
    Next to your newly created token in the list, click Configure SSOAuthorize next to your org name. If you skip this, GitHub will reject all API calls for that org even with the right scopes.
    If you don't see a "Configure SSO" button, your org doesn't use SAML SSO — skip this step.
  8. 8
    Connect wert to GitHub.
    In the wert TUI, go to screen 4 (GitHub) or use the input on any screen:
    /github setup --token ghp_xxxxxxxxxxxx --org your-org-name

    The --org value is the org slug (the part after github.com/), not the display name. For personal accounts, use your GitHub username.

  9. 9
    You're done. wert fetches your repos, open PRs, and open issues. Use /github refresh any time to reload the data.
    wert automatically tries three search strategies: org:user: → per-repo. So even if one approach fails for your setup, it falls through to the next.

Troubleshooting

errorcausefix
422 Validation Failed Token is missing read:org scope, or it's a Fine-grained PAT Create a new Classic token with both repo and read:org
401 Unauthorized Token is invalid or expired Generate a new token and run /github setup again
403 Forbidden SAML SSO not authorized for this token Go to token settings on GitHub → Configure SSO → Authorize your org
404 on repos but not PRs Org name is wrong, or you used a user account name for an org Double-check the slug in the GitHub URL (e.g. github.com/your-org)
No PRs or issues shown Your repos are private and the token has no repo scope Re-create the token and check the repo checkbox