--- title: Daemon consumers description: First consumers of the gwm daemon, namely a compact statusline for shell prompts, the raw JSON-RPC one-liner, and an editor recipe (Zed / VS Code). navigation: title: Daemon consumers --- # Daemon consumers [`gwm daemon`](/cli/reference#gwm-daemon-socket-path-poll-ms-ms-issue-38) is a long-running JSON-RPC 2.0 server over a unix domain socket (a named pipe on Windows, #439): editors, statusbars, and tooling connect once and call `list` / `doctor` / `path`, or `subscribe` for pushed `worktrees.changed` notifications, instead of spawning `gwm` per query. This page covers the **consumer** side: the bundled `gwm statusline`, the raw protocol one-liner, and an editor recipe. > The daemon is **one per repo**: it answers for the repository it was launched in. Start it from inside the worktree set you want to watch (`cd` into any worktree of the repo, then `gwm daemon`). The default socket path is shared, so to run daemons for **several repos at once** give each a distinct `--socket` (and point that repo's `gwm statusline --socket` at the same path); otherwise the second `gwm daemon` is refused by the live-socket guard. ## Start the daemon ```bash # Binds $XDG_RUNTIME_DIR/gwm.sock (→ $TMPDIR → /tmp on macOS, where # XDG_RUNTIME_DIR is unset). Override with --socket. # # Security note: when the chosen base dir is NOT owner-only (the shared # /tmp last resort), the socket is isolated in a per-user owner-only # sub-dir instead — $base/gwm-/gwm.sock — so another local user can't # connect. The common $XDG_RUNTIME_DIR / per-user $TMPDIR paths (already # private) are unchanged. The daemon prints its actual bound path to # stderr ("gwm daemon listening on "), so a raw nc/socat consumer # in that fallback case should read the path from there rather than # hard-coding $base/gwm.sock. gwm daemon # A faster subscribe cadence (default 1000 ms): gwm daemon --poll-ms 300 # Several repos at once: one socket each (the default path is shared, so a # second daemon on it is refused). Point each consumer at the matching path. gwm daemon --socket "${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/gwm-api.sock" # in repo A gwm statusline --socket "${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/gwm-api.sock" ``` The daemon prints `gwm daemon listening on ` to stderr **only once the socket is bound**, so a wrapper can treat that line as a readiness signal. ## Statusline: `gwm statusline` The first bundled consumer. It connects to the daemon, asks for the worktree set, and renders a single compact line for a tmux / starship / zsh prompt: ```text $ gwm statusline feat/#309-daemon-consumer · 3 wt · * ↑1 · #309 · PR #310 ``` The line is built **only** from the daemon's stable schema: | Token | Meaning | |:-----------------|:-----------------------------------------------------| | `feat/#309-…` | active worktree's branch (or its name when detached) | | `3 wt` | total worktree count | | `*` | the active worktree has uncommitted changes | | `↑n` / `↓n` | commits ahead of / behind the upstream | | `#309` | linked issue number | | `PR #310` | linked PR number | | `claude` | an **active** AI-agent session in the active worktree (from the experimental `agents` field, #408). Idle sessions are not advertised here; the TUI overlay (`a`) has the full list. | The **active** worktree is the one whose directory encloses the current working directory; outside any worktree the line collapses to just the count (`3 wt`). > **No CI rollup.** The `CI passing 9/9`-style half of the original sketch is intentionally omitted: a CI rollup is not part of the daemon's stable schema, and fetching it would mean a `gh` call on every prompt redraw. It is left as a follow-up rather than smuggled into the wire protocol. ### Live updates: `--watch` `--watch` subscribes to the daemon's `worktrees.changed` stream and reprints the line on every change, one line per update, which is ideal for a long-running command feeding a statusbar: ```bash gwm statusline --watch ``` ### Graceful degradation When **no daemon is reachable**, `gwm statusline` prints an empty line and exits `0`, so a prompt substitution degrades to nothing instead of showing an error. That makes it safe to drop into a prompt unconditionally. ### Prompt recipes zsh, right prompt (refreshed on each prompt; one socket round-trip to the running daemon): ```bash # ~/.zshrc function gwm_rprompt() { gwm statusline 2>/dev/null } setopt PROMPT_SUBST RPROMPT='$(gwm_rprompt)' ``` tmux `status-right`, re-queried on the status interval (for push-driven updates instead of polling, run `gwm statusline --watch` into a pane and read its tail): ```bash # ~/.tmux.conf set -g status-interval 5 set -ag status-right ' #(cd #{q:pane_current_path} && gwm statusline)' ``` starship, custom command: ```toml # ~/.config/starship.toml [custom.gwm] command = "gwm statusline" when = true shell = ["bash", "--noprofile", "--norc"] format = "[$output]($style) " ``` ## Raw protocol: the thin proof No `gwm` binary required on the consumer side: the wire format is newline-delimited JSON, one request per line, one response per line. Anything that can write a line to a unix socket is a client. ```bash # socat printf '{"jsonrpc":"2.0","method":"list","id":1}\n' \ | socat - UNIX-CONNECT:"${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/gwm.sock" # nc / netcat printf '{"jsonrpc":"2.0","method":"list","id":1}\n' \ | nc -U "${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/gwm.sock" # subscribe — holds the connection open and streams a `worktrees.changed` # notification on every change (the first line is the current snapshot). # `cat` keeps the write side open so the daemon keeps streaming; without it # `printf` exits, the socket half-closes, and only the first snapshot # arrives. Ctrl-C to stop. { printf '{"jsonrpc":"2.0","method":"subscribe","id":1}\n'; cat; } \ | socat - UNIX-CONNECT:"${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/gwm.sock" ``` Methods: `list` (worktree array), `doctor` (health report with `severity` + `exit_code`), `path` (`{"params":{"pattern":""}}` → `{ name, path, branch }`), and `subscribe` (upgrades the connection to a one-way notification stream). Agent-session changes push too: a session appearing, vanishing, flipping `active` ↔ `idle`, or writing fresh activity (`last_activity`) triggers a `worktrees.changed`. Unlike `age_seconds`, `last_activity` only moves on real agent writes, and the 30 s detection cache bounds the push frequency. The response schemas are the same stable DTOs the `--format=json` CLI flags emit. See [`docs/schema/`](https://github.com/kbrdn1/gwm-cli/tree/main/docs/schema) and the [`gwm daemon` reference](/cli/reference#gwm-daemon-socket-path-poll-ms-ms-issue-38). ## Editor recipe An editor doesn't need a plugin: a task that shells out to the daemon (or to `gwm`'s `--format=json` surface) is enough to list worktrees and jump between them. ### Zed `.zed/tasks.json`, a task that prints the live statusline and one that lists the worktrees through the socket: ```json [ { "label": "gwm: statusline", "command": "gwm statusline", "use_new_terminal": false, "reveal": "always" }, { "label": "gwm: worktrees (via daemon)", "command": "printf '{\"jsonrpc\":\"2.0\",\"method\":\"list\",\"id\":1}\\n' | socat - UNIX-CONNECT:\"${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/gwm.sock\" | jq -r '.result[] | \"\\(.name)\\t\\(.path)\"'", "reveal": "always" } ] ``` ### VS Code `.vscode/tasks.json`, the same idea, using the daemon socket to list worktrees: ```json { "version": "2.0.0", "tasks": [ { "label": "gwm: worktrees (via daemon)", "type": "shell", "command": "printf '{\"jsonrpc\":\"2.0\",\"method\":\"list\",\"id\":1}\\n' | socat - UNIX-CONNECT:\"${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/gwm.sock\" | jq -r '.result[] | \"\\(.name)\\t\\(.path)\"'", "problemMatcher": [] } ] } ``` To **jump** to a worktree by fuzzy pattern, call `path` instead of `list`: ```bash printf '{"jsonrpc":"2.0","method":"path","params":{"pattern":"309"},"id":1}\n' \ | socat - UNIX-CONNECT:"${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/gwm.sock" | jq -r '.result.path' ``` Without a running daemon, the same data is one `gwm` invocation away (`gwm list --format=json` and `gwm path --format=json` emit the identical schemas), but the daemon avoids a process spawn per query.