{"id":49280679,"url":"https://github.com/mostlydev/talking-stick","last_synced_at":"2026-05-10T06:01:37.081Z","repository":{"id":353440179,"uuid":"1218441379","full_name":"mostlydev/talking-stick","owner":"mostlydev","description":"MCP server for multi-agent coordination in a shared workspace (WIP, design-only)","archived":false,"fork":false,"pushed_at":"2026-04-23T22:23:08.000Z","size":135,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-04-23T23:33:21.089Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mostlydev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-22T22:06:31.000Z","updated_at":"2026-04-23T22:23:12.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mostlydev/talking-stick","commit_stats":null,"previous_names":["mostlydev/talking-stick"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/mostlydev/talking-stick","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mostlydev%2Ftalking-stick","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mostlydev%2Ftalking-stick/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mostlydev%2Ftalking-stick/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mostlydev%2Ftalking-stick/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mostlydev","download_url":"https://codeload.github.com/mostlydev/talking-stick/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mostlydev%2Ftalking-stick/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32273139,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-25T18:29:39.964Z","status":"ssl_error","status_checked_at":"2026-04-25T18:29:32.149Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-25T18:30:43.914Z","updated_at":"2026-05-10T06:01:37.073Z","avatar_url":"https://github.com/mostlydev.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Talking Stick\n\nA CLI coordination tool that lets multiple AI coding agents share a single workspace without stepping on each other. One agent holds the stick at a time; handoffs carry structured context so the next agent doesn't have to re-derive it.\n\n**Version:** 0.4.1. Multi-process-safe (SQLite WAL), liveness-aware, no daemon. Supports Claude Code, Codex CLI, Gemini CLI, and OpenCode out of the box. Two agents in the same room can also chat out-of-band — without passing the stick — via `tt msg send/recv`.\n\n## Quickstart\n\nThree steps, then you're coordinating two agents in the same repo.\n\n### 1. Install the `tt` binary\n\n```bash\nnpm i -g talking-stick\n```\n\n### 2. Install the skill in every harness\n\n```bash\ntt install --all\n```\n\nRestart any harness that was already running so it loads the updated skill. The skill teaches agents to coordinate by running `tt` CLI commands from the workspace. To tune the default collaboration prompt without editing installed package files, run `tt instructions edit`.\n\n### 3. Try it: two agents, one repo\n\nOpen two terminal panes side by side — tmux split, iTerm split, two windows, whatever you like. `cd` into the same repo in each, and launch a different harness in each pane:\n\n| Pane A — Claude Code | Pane B — Codex |\n|---|---|\n| `cd ~/myrepo \u0026\u0026 claude [--dangerously-skip-permissions]` | `cd ~/myrepo \u0026\u0026 codex` |\n\nThen prompt them.\n\n**Pane A (Claude Code):**\n\n\u003e Draft a plan to add OAuth login. When it's solid, pass the stick to Codex for critique. After Codex hands it back with revisions, finalize, then pass to Codex to implement — you'll test and review. `/talking-stick`\n\n**Pane B (Codex):**\n\n\u003e Join the room and wait for the stick. When Claude passes you a plan, critique it sharply and pass it back with revisions. Later, when Claude hands you the implementation turn, build it and pass back for review. `$talking-stick`\n\nThat's the whole workflow. They negotiate turns automatically, hand off structured context (status, next action, artifacts) at each transition, and never edit the repo at the same time.\n\n### Install options\n\n| Method | Command | Notes |\n|---|---|---|\n| **From npm** | `npm i -g talking-stick` | Published as `0.4.1`. Requires Node ≥ 22. |\n| **From GitHub** | `npm i -g github:mostlydev/talking-stick` | Tracks the `master` branch; builds on install via the `prepare` hook. |\n| **From source** | `git clone … \u0026\u0026 npm install \u0026\u0026 npm link` | For contributors. |\n\nAll three produce a `tt` binary on your `PATH`. Everything else below works identically.\n\n### Verify without installing\n\nWant to see exactly what `tt install` would change before touching anything?\n\n```bash\ntt install --all --print\n```\n\n### Install into a subset\n\n```bash\ntt install claude-code codex\n```\n\nDuring normal execution, install commands skip harnesses that are not present instead of failing or creating new harness config roots.\n\n### Update\n\nUses the right npm/pnpm/yarn by default:\n\n```bash\ntt self-update\n```\n\n`tt self-update` also removes stale Talking Stick MCP registrations left by older installs. The first normal `tt` invocation after a package-version change runs the same cleanup if the package manager skipped lifecycle scripts.\n\n### Remove\n\n```bash\ntt uninstall --all\n```\n\n## What it gives your agent\n\nOnce installed, each agent harness has a skill that tells it to coordinate through the `tt` CLI:\n\n```\ntt list            — which rooms exist under a path\ntt join            — join the room for this workspace\ntt leave           — explicitly leave a room; deletes it when no active members remain\ntt wait            — block until the stick is available, with takeover signals\ntt release         — normal handoff to the next fair waiter, with structured Handoff\ntt assign          — explicit handoff to a named agent\ntt take            — deliberate claim when the prior holder is gone/stuck\ntt kick            — evict an idle member whose process is gone\ntt state           — authoritative state projection\ntt events          — audit log and long-poll stream of turn transitions/messages\ntt notes add/list  — durable async observations for the room\ntt msg send/recv   — out-of-band chat into the room event log\ntt instructions    — editable collaboration prompt loaded by the skill\n```\n\nA workspace maps to a room — usually the `git` root or nearest project marker — so two agents `cd`'d anywhere under the same repo join the same room automatically.\n\nThe global skill tells the model when to join, wait, take over, leave notes, send messages, and hand off.\n\n## Editable collaboration instructions\n\nThe bundled skill is the safety floor. It is intentionally small and package-managed. Local collaboration preferences live in editable Markdown files that `tt instructions` shows to agents after they join.\n\n```bash\ntt instructions show                     # effective prompt for the detected harness\ntt instructions show --harness codex     # view one harness's effective prompt\ntt instructions edit                     # edit user defaults\ntt instructions edit --project           # edit this repo's overrides\ntt instructions reset --project          # remove this repo's override\n```\n\nEffective instructions are layered in this order: bundled defaults, user defaults at `${TALKING_STICK_DATA_DIR}/instructions.md` (normally `~/.local/share/talking-stick/instructions.md`), then project overrides at `.talking-stick/instructions.md` in the workspace root. User and project files are created lazily on first edit, so installing `tt` does not litter repositories or harness config directories.\n\n## Non-owner notes\n\nWhile you wait your turn you may still need to flag something to the current owner: a subtle invariant, a related bug, a pointer to a doc. Non-owner notes give you a durable channel without interrupting the turn.\n\n- Any joined member (owner or not) can `add_note` with a short plain-text body (≤ 16 KB). An optional `turn_id` scopes the note to a specific turn; omitted, the note is room-scoped and survives turn transitions.\n- `list_notes` returns notes for the room; readers can paginate with `after_note_id` and opt into resolved entries with `include_resolved`.\n- Notes are for observations and pointers, not for coordinating shared edits. Shared workspace changes still require holding the stick.\n\n## Out-of-band messaging\n\nThe stick guarantees single-writer authority over shared workspace state. It is **not** a chat protocol. When two agents need to talk — design questions, \"are you about to break X?\", live coordination — use messages instead of churning the stick.\n\n```bash\ntt msg send \u003crecipient|room\u003e \"\u003cbody\u003e\" [--interrupt] [--stdin]\ntt msg recv [--wait|--follow] [--from agent] [--after N] [--target self|any|agent]\ntt events --wait|--follow [--event TYPE[,TYPE]] [--target self|any|agent]\n```\n\n- `\u003crecipient\u003e` is a full `agent_id`, an unambiguous active display name (`codex`, `claude`), or the literal `room` for broadcast.\n- `--interrupt` marks the message time-sensitive; receivers decide whether to act on it now.\n- `tt msg recv --follow` is a long-running tail (one JSON line per event) suited to harnesses that can monitor child stdout (Claude Code Monitor, terminals).\n- `tt msg recv --wait` exits on the next matching batch — ideal for harnesses that can launch a background command and notice when it completes; restart with `--after \u003clast_event_seq\u003e` to resume.\n- `tt events --wait` and `tt events --follow` default to `--target self`; pass `--target any` only for audit/debug views.\n- `wait_for_events` is observer-safe: it never mutates room state, so non-holders can use it freely without disturbing turn-fairness bookkeeping.\n- Event receive does not grant the stick. Agents must still use `tt wait` for ownership before editing shared files.\n\n**When to message vs note vs handoff.**\n\n- **Message** — conversational, ephemeral, between live processes. Six round-trips of \"what about line 84?\" cost about as much as one structured handoff and zero stick churn.\n- **Note** (`tt notes add`) — durable, resolvable artifacts. Leave a note when the next holder should consider something at handoff, or when the observation should outlive the conversation.\n- **Handoff** (`release_stick` / `pass_stick`) — transfer of work. Messages do not replace handoffs; they live alongside them.\n\n**`to_agent_id` is routing, not ACL.** Any room member can read any message via `get_room_events` or `tt events --follow --target any`. Messages are not private. They also do not grant the stick — a non-holder paging the holder gets attention, not write authority.\n\nFor harnesses that only notice completed subprocesses, run `tt events --wait --after \u003ccursor\u003e --json` as a wake process alongside the normal `tt wait --json` loop. A message, pass, release, or assignment event should make the agent read/reply/retry `tt wait`; it is not permission to mutate the workspace.\n\n## How installation works per harness\n\n`tt install` installs or refreshes the bundled `talking-stick` skill. It does not add MCP servers. During install, uninstall, package update, and first run after an installed package version changes, `tt` removes stale MCP registrations written by older Talking Stick releases.\n\n- Claude Code: copied or linked into `~/.claude/skills/talking-stick`\n- Codex: copied or linked into `~/.codex/skills/talking-stick`\n- Gemini: installed with `gemini skills install ... --scope user` or linked with `gemini skills link ... --scope user`\n- OpenCode: copied or linked into `~/.opencode/skills/talking-stick`\n\nBy default, `tt install` links the bundled skill into each harness so local updates are picked up immediately. Pass `--copy` if you want a standalone snapshot instead.\n\nStale MCP cleanup is strict for OpenCode JSON entries: it removes only the canonical `mcp.talking-stick` value with `[\"tt\", \"mcp\"]` and leaves hand-edited entries alone. Claude Code, Codex, and Gemini cleanup uses their own `mcp remove` commands when the old server name exists. Every cleanup run appends JSONL audit entries to `${TALKING_STICK_DATA_DIR}/update-migrations.log`.\n\nHuman CLI invocations also perform a silent best-effort sync for already-installed file-based skills in Claude Code, Codex, and OpenCode. If the installed skill is a copy, it is refreshed from the bundled skill; if it is a stale symlink, it is relinked. Missing harness config directories and missing skill installs are skipped. Gemini skills are managed by Gemini's own registry, so use `tt install gemini` after updating when needed.\n\n## Human CLI\n\nThe same `tt` binary also works as a human CLI, useful for watching or participating in a room from your terminal:\n\n```text\ntt whoami [--explain]                                      # show the resolved CLI identity\ntt list [path]                                            # list rooms\ntt join [path] [--force-new]                              # join the room for path\ntt leave [path]                                           # leave the room for path\ntt wait [path] [--timeout 110s]                           # block until your turn\ntt try [path]                                             # non-blocking claim attempt\ntt state [path]                                           # full room state\ntt events [path] [--after N] [--limit N] [--wait|--follow] [--event TYPE[,TYPE]] [--target self|any|agent]  # room event log; --wait/--follow long-polls\ntt msg send \u003crecipient|room\u003e \u003cbody...\u003e [--interrupt] [--stdin] [--path DIR]  # send an OOB message\ntt msg recv [--wait|--follow] [--from agent] [--after N] [--target self|any|agent] [--path DIR]  # receive OOB messages\ntt instructions show [path] [--harness claude|codex|gemini|opencode|all] [--scope effective|bundled|user|project]  # show collaboration prompt\ntt instructions edit [path] [--user|--project]             # edit user or project prompt\ntt instructions reset [path] (--user|--project)            # delete a user or project prompt\ntt release [path] --status TEXT --next-action TEXT        # normal handoff\ntt pass [path] --status TEXT --next-action TEXT           # pass/end your turn\ntt assign \u003ctarget|next\u003e [path] --status TEXT --next-action TEXT  # explicit handoff\ntt take [path] [--reason TEXT]                            # human-friendly take/override\ntt takeover [path] [--reason TEXT]                        # alias for take\ntt notes add \u003cbody\u003e [--turn N] [--path DIR] [--stdin]     # leave an async note\ntt notes list [--all] [--after ID] [--limit N] [--path DIR] # read notes\ntt install \u003charness...\u003e | --all [--print] [--copy] [--link]  # install skill and clean stale MCP entries\ntt uninstall \u003charness...\u003e | --all [--print]               # remove skill and stale MCP entries\ntt self-update [--print] [--manager npm|pnpm|yarn|bun]    # update to the latest published tt\n```\n\n`[path]` defaults to the current working directory. Omit it for normal in-repo coordination; pass it only when you intentionally want a different or nested room.\n\n`tt self-update` detects how `tt` was installed (npm / pnpm / yarn / bun, including npm-via-Homebrew/mise/asdf/nvm), runs the right global-update command, then removes stale MCP registrations from older Talking Stick installs. Pass `--print` to see the inferred command without running it; pass `--manager` to override detection. Running `tt self-update` from a development checkout (where `tt` resolves outside `node_modules/talking-stick`) refuses and tells you to `git pull \u0026\u0026 npm install \u0026\u0026 npm run build` instead.\n\nHuman CLI commands use a stable identity like `human:\u003cusername\u003e`. When `tt wait`, `tt take`, or `tt takeover` wins the turn, a small background guardian keeps the lease alive on your behalf until you release, pass, or assign it. Human CLI `take` intentionally works without a required reason so an operator can step into a stuck room quickly; harness-aware CLI takeovers still require `--reason` unless the command includes `--operator-requested`.\n\n### CLI identity\n\nBy default, `tt` behaves like a human CLI and resolves to `human:\u003cusername\u003e` only when no harness environment is detected.\n\nHarness-aware CLI identity is resolved before the human fallback:\n\n- Known harness environment markers such as `CLAUDECODE=1`, `CODEX_THREAD_ID`, `GEMINI_CLI=1`, or `OPENCODE=1` make `tt` derive a harness-style identity automatically.\n- Set `TT_HARNESS_AGENT_ID=\u003cagent-id\u003e` if the harness wants to export the exact agent id directly.\n- Set `TT_HARNESS_EXPORT=1` only when you need ancestry-based harness detection without a known harness environment marker.\n\nIf no harness signal is present, `tt` stays on the human CLI path. That keeps ordinary shell usage predictable while preventing harness-launched shells from silently joining rooms as `human:\u003cusername\u003e`.\n\nUse `tt whoami --explain` to see which identity path the CLI chose.\n\n## Design highlights\n\n- **Workspace-root room resolution.** An agent at any depth under `/repo/` joins the `/repo/` room automatically. Nested rooms require explicit `force_new`.\n- **Structured handoffs.** `release_stick` and `pass_stick` carry a typed `Handoff` with required `status` / `next_action` and optional `artifacts[]` pointing at specific files and line ranges.\n- **Fair handoff selection.** Normal release prefers a recent waiter that is new or has gone longest without holding the stick; if the best-known candidate is between wait polls, a short grace window prevents immediate recycling to a less-fair claimant.\n- **No immediate take-backs.** If release leaves a handoff idle, the prior owner waits through the short grace window before reclaiming while another member exists.\n- **Ephemeral rooms.** `leave_room`/`tt leave` removes membership, rooms with no active members are physically deleted, and long-idle rooms are purged opportunistically on later invocations.\n- **Fencing tokens.** `lease_id` + `turn_id` make stale writes impossible — an agent who lost their turn cannot commit anything under the room's name.\n- **Liveness-aware recovery.** Dead or crashed holders are detected with OS-level process checks; claim-timeout takeover skips the prior owner when another active member is waiting.\n- **Multi-process safe.** Shared SQLite with WAL mode, `BEGIN IMMEDIATE` writes, 250 ms polling for `wait_for_turn`. No daemon required.\n- **Per-call identity derivation.** Harness-launched CLI calls derive identity from harness environment or ancestry. Human CLI callers get a stable `human:\u003cusername\u003e` identity.\n\n## Storage\n\nThe coordination database lives at:\n\n- Linux/macOS: `~/.local/share/talking-stick/rooms.sqlite` (or `$XDG_DATA_HOME/talking-stick/rooms.sqlite`)\n- Windows: `%APPDATA%\\talking-stick\\rooms.sqlite`\n\nOverride with `TALKING_STICK_DATA_DIR` if you want to keep per-project state.\n\n## Development\n\n```bash\nnpm install\nnpm test\nnpm run typecheck\nnpm run build\n```\n\n## Changelog\n\nSee [`CHANGELOG.md`](CHANGELOG.md) for a per-version summary; full release notes live in [`docs/releases/`](docs/releases/).\n\n## Read next\n\n- [`docs/talking-stick-plan.md`](docs/talking-stick-plan.md) — full protocol, state transitions, persistence model, design rationale, and open questions.\n- [`docs/ambient-presence.md`](docs/ambient-presence.md) — design sketch for shell-prompt awareness, event streaming, and agent skills that make room state ambient rather than appointment-only.\n- [`docs/non-owner-notes.md`](docs/non-owner-notes.md) — design backing the v1 notes feature; documents what shipped, what was deferred (`resolve_note`, wait_for_turn unread hints), and the rationale.\n- [`skills/talking-stick/SKILL.md`](skills/talking-stick/SKILL.md) — the portable skill installed into global harness skill directories.\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmostlydev%2Ftalking-stick","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmostlydev%2Ftalking-stick","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmostlydev%2Ftalking-stick/lists"}