Mellow Peek for VS Code
Viewer for the Mellow Peek feed, plus human presence. Runs in VS Code and Cursor.
The extension is a client of the local mellow-peek daemon. It shows what your teammates and their
coding agents are doing, marks files another session has claimed, and reports this editor
window as a vscode-human session if you let it. Install the mellow-peek binary first; the extension
does not ship it.
Install
Install mellow.mellow-peek from the VS Code Marketplace or Open VSX (pre-release for now:
"Switch to Pre-Release Version"). It needs the mellow-peek daemon. When none is running, the
extension offers to install it: it downloads the newest mellow-peek of its channel (insiders for
a pre-release build) from releases.mellow.build, verifies it against the TUF root shipped in the
extension (resources/tuf/root.json), puts it in ~/.local/bin (%LOCALAPPDATA%\Programs\mellow-peek
on Windows) with the harness plugins in the config directory, starts the daemon as a user
service, and offers mellow-peek init. It never installs over a mellow-peek that is already
there (on PATH, in ~/.local/bin, or from Homebrew). Turn the offer off with
mellowPeek.daemon.offerInstall; "Mellow Peek: Install and Start Daemon" runs it any time. The
install script in the repository README does the same from a terminal. Publication steps are in
docs/RELEASING.md.
What you get
| Surface |
What it shows |
| Feed view |
Topics, newest first. Each topic is two lines: the title, then who, how long ago, the repository (a remote as owner/name, a remote-less checkout by the folder name its sender gave; a parent topic shows its sub-topics' repositories), who did the work (agent, 2 agents + editor, from the sessions' harnesses; a *-human harness is the person's editor), and the team. Click a topic to read its summary sentence, with sub-topics nested under it and earlier or merged versions and the facts and agent replies it covers folded under it; a fact belongs to the topic that names its session, so nothing about work sits at the top level. Besides topics the top level holds team joins and leaves and notes addressed to you, each on one line. A brand-new session shows up when its first topic lands (seconds after it goes quiet); the Sessions view has it at once. Each summary reads as written ("lukasfri is wiring the network node into the daemon in peek…") and the facts it covers fold under it; session facts read as "ada started a claude-code session in …", "ada is now working on ", "ada finished (exit)", plus agent messages, directives and team changes. Running/idle flips are hidden unless mellowPeek.feed.showStatusChanges. Filter by team, repository, or person from the view title; defaults to the repository of the first workspace folder. Updates live. The trash icon clears the feed on the daemon (feed.clear); live sessions stay. On top, one card per team whose team digest you turned on: what your teammates are doing, with a line per teammate when opened (digest.get; hidden under a person or repository filter). |
| Sessions view |
+ creates a team for the open repository (a peer-to-peer team about it, linked for you and published as .mellow/peek/team.toml for teammates to discover); each team lists its Repositories next to Members (whether you share each one; admins add repositories from a multi-select of the workspace's folders or any folder, remove them, and make the team strict or open), and its My links setting says which of your checkouts it hears about; the link icon on a team you are an admin of copies an invite. Each team's description says when you are its owner or an admin, and its tooltip names the owner and admins. Right-click a team, or hover it for the Leave and Delete buttons, to copy its id, to Publish Team File…, to Leave Team… (not offered to the owner), or, as its owner, to Delete Team…: a relay team is deleted on its relay for everyone; a peer-to-peer team's key is destroyed, so nobody can be made admin again (admins can still admit and remove people). Both confirm first, and Delete asks you to type the team name. Under each team, Settings shows what you share with it, each row with its current value: your Scope (personal teams: all repositories or an allowlist picked from your open folders and live sessions' repositories; a repository team is fixed to its repository), your Sharing (a preset, Full, Detailed or Minimal, or Custom; changed through the presets or Advanced…, fact by fact, offering only what the team allows, and noting when the team ceiling lowers it; the tooltip lists every fact), and the Team digest; the team's policy (Team ceiling, Summary cadence, Retention, Ask members for the digest, and on a relay team its Access), which its admins change (peer-to-peer: signed into the team's roster log through TeamService.SetPolicy; relay: owners and admins, through the relay); and for a repository team the Team file, written with TeamService.PublishTeam. Click a row to change it; rows you may not change show why. Members lists everyone (★ owner, shield admin, invitations not yet taken up marked invited); owners and admins can remove a member they outrank (and on a relay team revoke an invitation), and the owner (on a peer-to-peer team, the device holding the team key) can make a member admin or an admin member; admins of a peer-to-peer team invite people too, so it admits newcomers while its creator is offline. People with sessions show their role too. Teams → people → their sessions. A session sits under every team it was sent on (the daemon's binding: a repository team admits its repository, a personal team your scope for it), and under Unshared when no team admits it, so it never left this machine (mellowPeek.sessions.showUnshared). Each session is labelled by its title (or "Untitled session") with status and age; a running agent spins. A person's own editor window (a *-human harness) is marked as such: a window icon, editor instead of a status, and, until the daemon narrates it from that person's own edits, the label <App> editor (Cursor editor); expanded, the session's description, then a file tree of what it claimed or touched. Files that exist in the workspace open on click. Tooltip has the description, harness, model, repository, commits, unpushed count, teams. Right-click a person or a live session to Leave a Note… for them. The gear in the view title opens Settings…. Right-click a person to mute them, or a session to mute that device: the daemon deletes what they sent and drops the rest until Mellow Peek: Manage Mutes… unmutes. |
| Directives view |
Pending first. Acknowledge one inline or all from the title bar; acknowledgements go to the daemon. |
| File decorations |
C on a file another session has claimed, T on one it touched, in the explorer and tabs, with who and which session in the tooltip. |
| Status bar |
Connected or not, paused flag, team count, pending directive count; with presence on, the title the daemon narrated for this window's own session. Click it to refresh, or to open setup when the daemon is not reachable. |
| Human presence |
Off by default. Turned on (Mellow Peek: Toggle Human Presence), your window is a vscode-human session: the workspace, the file you have open (touched), a file you start editing (claimed). Never contents, and no title: the daemon titles the session from your own edits. The daemon applies your excludes, scope, and each team's policy. A reload ends the previous window's session; a window that closed without a chance to say so is ended by the next window that activates (heartbeats in globalState tell live windows from gone ones). |
Setup
Mellow Peek: Set Up… (also the status bar item while disconnected) offers each step; they run the
mellow-peek binary or call the daemon:
| Command |
Runs |
Mellow Peek: Install and Start Daemon |
mellow-peek daemon install, then mellow-peek daemon start |
Mellow Peek: Start Daemon |
mellow-peek daemon start |
Mellow Peek: Install Harness Plugin (mellow-peek init) |
mellow-peek init |
Mellow Peek: Join Team with Invite |
opens the join form for a pasted token: the team (name, kind, home, who invited you, its ceiling, whether it asks for the digest), then what you share, which repositories, your summaries and the digest, privacy-first by default. Nothing joins before you press Join; the answers apply before the team goes live (TeamService.PreviewTeam, then JoinTeam with choices). Show Invitations, teams a relay offers you, a repository's announced team and invite links open the same form |
Mellow Peek: Create Team for This Repository… |
creates a team about this repository on this device (TeamService.CreateTeam) or on the configured relay (CreateRelayTeam), publishes .mellow/peek/team.toml, then offers an invite |
Mellow Peek: Add Repositories to Team… |
a team you may change the policy of: a multi-select of the workspace's checkouts (checked when the team is already about them; unchecking one removes it) and Choose a folder… for any other checkout; then offers to share your checkouts of the ones added. The prompt for a repository no team is about offers it too (Add to team…) |
| Opening a checkout of a team's repository |
when the folder is a checkout of one of a joined team's repositories that you have not linked (ResolveWorkspace says pending), asks once: Share links the repository, Not here excludes it for that team and stops the question |
Mellow Peek: Invite to Team… |
asks the daemon for a one-time invite (InviteToTeam), then copies an invite link (vscode://mellow.mellow-peek/join?token=…, in Cursor its own scheme) or the raw token. Opening the link opens the join form; it works once, within 7 days, while your daemon is up. On a relay team, after inviting handles, it offers a link that names the relay and the team |
Mellow Peek: Leave Team… |
after a confirmation, LeaveTeam (a relay team on its relay first); not offered for a team you own |
Mellow Peek: Delete Team… |
owners only, after a confirmation and typing the team name: deleted on its relay (ManageRelayTeam) for a relay team; for a peer-to-peer one, leaving destroys the signing key (LeaveTeam) |
Mellow Peek: Log In to Relay |
Signs in to a relay with no terminal: through the browser (the daemon waits for it; cancel from the notification) or with an operator token. Then it lists the teams you can activate on this device; each one you pick opens the join form, and the rest wait under Available teams |
Mellow Peek: Be Discoverable Nearby (10 min) / Stop Being Discoverable Nearby |
Invite Nearby (NearbyService.SetDiscoverable): people on this network see your handle and name for ten minutes and can offer you a team. A status bar countdown shows the window (click to stop); each offer shows a notification (Review… opens the join form, Decline) and waits under Available teams |
Mellow Peek: Invite Nearby… |
from a peer-to-peer team you administer, or Invite to Team…: a live list of people discoverable nearby, with fingerprints to compare and members marked; picking one offers them the team (OfferTeam) with an invite that works on their device only |
Mellow Peek: Pause Sharing / Resume Sharing |
the daemon's pause / resume |
Mellow Peek: Team Digest… |
from a team's context menu or the palette: turn the team digest on or off for you, or follow the team (SetDigest). When a team asks members to turn it on and you have not answered, a notification asks once per window: "Turn on for me" or "Not for me" |
Mellow Peek: Clear Feed |
the daemon's feed.clear ("Clear items") or feed.reset ("Reset everything": also forgets remembered sessions and summary topics; live sessions return), after a confirmation |
Mellow Peek: Mute Person / Mute This Device / Manage Mutes… |
the daemon's mute.add / mute.remove; your own handle and device are refused |
Mellow Peek: Toggle Human Presence |
flips mellowPeek.presence |
Mellow Peek: Settings… |
the gear on the Sessions view: one menu over every global control (sharing, excludes, mutes, presence, the summarizer, relays, daemon, plugins, feed) |
Mellow Peek: Choose Summarizer… / Set Summarizer Model… / Your Feed Written From (agent threads, facts, off)… / Choose Narrators… |
the daemon's summarizer.set: written to daemon.toml (comments kept) and in force at once. The feed source is this device's own feed only (local_source); what each team gets is its Sharing |
Mellow Peek: Edit daemon.toml… |
opens the daemon's daemon.toml for everything else (cadences, the network); those apply on a daemon restart |
Mellow Peek: Stop or Resume Sharing This Repository… |
the daemon's exclude.add for this checkout or the repository wherever it is checked out; when it is already excluded, exclude.remove after a confirmation |
Mellow Peek: Manage Excludes… |
lists exclude.list; picking one shares it again (exclude.remove) |
Mellow Peek: Leave a Note… |
from a person or a live session in the Sessions view, or the palette: the daemon's directive.post to that session or every session of the person, with a severity (info, warn hands it to their agent as context, interrupt also asks their harness to stop); kept for an hour |
Mellow Peek: Log Out of Relay… |
the daemon's TeamService.LogoutRelay for one of your relay teams' relays or mellowPeek.teams.relay |
Mellow Peek: Show Status |
the daemon's status and mellow-peek doctor, in the output channel |
Mellow Peek: Stop Daemon / Restart Daemon |
mellow-peek daemon stop (then daemon start) |
Mellow Peek: Uninstall Harness Plugin… |
after a confirmation, mellow-peek init --uninstall |
Mellow Peek: Publish Team File… |
teams about a repository, from the team's context menu or the palette: PublishTeam writes .mellow/peek/team.toml for contributors to discover |
Presence is opt-in; nothing about you is reported until mellowPeek.presence is on.
Each window also checks its workspace folders: for a folder whose repository no joined team is
about, it offers to join the team the repository announces in .mellow/peek/team.toml; every
other such folder goes into one question, to add them all to one of your teams (a multi-select
with them checked) or to create one (mellowPeek.teams.suggest). A question answered after
another one settled it does nothing. "Never for these repositories" is remembered.
Teams on a relay (docs/RELAY.md) show a person icon for Show Team
Members and an Invite action that takes handles. A repository whose .mellow/peek/team.toml
names a relay offers Join; if you are not logged in to that relay yet, the extension offers to
log in first, then opens the form again.
Available teams, a section of the Sessions view, holds what you could take part in on this
device but have not activated (TeamService.ListAvailableTeams):
- each relay you are logged in to: its name,
as <handle>, or log in again / unreachable.
Under it, the teams it admits you to and the invite-only teams that invited you (invited),
or "Nothing to activate". Its menu has Log In Again, Refresh and Log Out;
- a team an open folder announces in
.mellow/peek/team.toml that you have not joined;
- a team someone nearby offered you ("offered by nearby"): Review… or Decline;
- Log in to a relay….
Activate… on a team opens the join form; nothing is shared with a team until you join it.
Mellow Peek: Show Invitations reveals the section. It refreshes after logins, joins and folder
changes, and every mellowPeek.availability.refreshSeconds.
When the relay advertises managed teams, Create Team also offers Managed on : facts go
through the relay, which stores them (and can read them) so members need never be online
together. A managed team's node reads "managed by · hub connected" or "hub offline".
Settings
| Setting |
Default |
Meaning |
mellowPeek.client |
daemon |
daemon talks to the local daemon; mock replays the mellow-peek-protocol fixtures with no daemon. Reload after changing. |
mellowPeek.daemon.configDir |
"" |
The daemon's config directory. Empty means MELLOW_PEEK_CONFIG_DIR or the platform default (~/.config/mellow/peek, ~/Library/Application Support/mellow/peek, %APPDATA%\mellow\peek). |
mellowPeek.daemon.socket |
"" |
Override the socket path or named pipe. Empty means <config dir>/daemon.sock, or \\.\pipe\mellow-peek-<user> on Windows. |
mellowPeek.binaryPath |
mellow-peek |
The binary the setup commands run. |
mellowPeek.presence |
off |
on reports this window as a session; off never does. |
mellowPeek.feed.defaultToCurrentRepository |
true |
Start the feed filtered to the first workspace folder's repository, read from .git/config. |
mellowPeek.feed.showStatusChanges |
false |
Also show running/idle flips and file-only updates in the feed. |
mellowPeek.availability.refreshSeconds |
300 |
How often the Available teams section asks each relay you are logged in to what you could activate; 0 refreshes only on demand, after logins and joins, and when folders change. |
mellowPeek.teams.relay |
empty |
A relay's base URL. Create Team then offers an invite-only, open or (when the relay offers it) managed team on it (the relay admits people while you are offline), Invite on such a team asks for handles, and Log In and Show Invitations default to it. |
mellowPeek.teams.suggest |
ask |
ask offers, once per window, to join, add to or create a team for the folders whose repositories no joined team is about; off never asks. |
mellowPeek.sessions.showUnshared |
true |
Show the Unshared group: sessions no team binding admits. Off, only sessions bound to a team are listed. |
mellowPeek.mock.fixturesDir |
"" |
Where the mock reads fixtures. Empty resolves crates/mellow-peek-protocol/tests/fixtures from the extension's checkout or the workspace. |
How it talks to the daemon
Per crates/mellow-peek/SPEC.md § Local API: the daemon's Connect services
(api/proto/mellow/peek/local/v1/) over HTTP/1.1 on the Unix socket or Windows named pipe, with
the token file's contents as Authorization: Bearer on every call. The feed uses one long-lived
TailFeed stream in follow mode that reconnects with exponential backoff (1 s to 30 s) and resumes
after the last feed item, so a daemon restart loses nothing. The Mellow Peek output channel logs connection changes and delivery failures.
Development
pnpm install # from the repository root
pnpm --filter mellow-peek run generate # api/protocol.schema.json -> src/generated/protocol.ts (or `task generate`)
pnpm --filter mellow-peek run build # tsc --noEmit, vitest, esbuild -> dist/extension.js
pnpm --filter mellow-peek run package # dist/mellow-peek-<version>.vsix
pnpm --filter mellow-peek run e2e # extension-host suite in a downloaded VS Code (needs a display)
pnpm --filter mellow-peek run e2e -- --editor /usr/share/cursor/cursor # the same suite inside Cursor
pnpm --filter mellow-peek run e2e -- --daemon ../../target/debug/mellow-peek # against a real daemon
With --daemon, the launcher starts mellow-peek daemon run on a scratch config dir (networking off,
template summarizer with one-second narration and summary windows) and the suite reports a
session through the daemon's own socket. This is the phase 7 done-when (the dev host connects
to the daemon, the feed updates live, the claimed file shows the C decoration) and the phase 9
one (three touches give the session a description under its harness title, an untitled session
gains a generated title, the sessions view shows the description node and the file tree, and
the feed shows a summary sentence). It passes in VS Code and Cursor.
The e2e suite (src/e2e/) launches the editor with the extension from source, a scratch
profile, and a scratch workspace whose settings select the mock backend, then checks
activation, commands, view data, the claim index, and pause/resume. It is not part of
pnpm run build because it needs a display.
Open extensions/vscode in VS Code or Cursor and press F5 to launch the extension development
host. With a running daemon the views fill from it; without one, set mellowPeek.client to mock and
open this repository as the workspace so the mock finds the fixtures.
To try the packaged extension: code --install-extension dist/mellow-peek-*.vsix or
cursor --install-extension dist/mellow-peek-*.vsix. Publishing to the VS Code Marketplace and
Open VSX is phase 8: pnpm run publish:marketplace, pnpm run publish:openvsx.
Layout:
| Path |
Role |
src/generated/protocol.ts |
Generated from api/protocol.schema.json; never edit. A test fails if it drifts. |
src/generated/proto/ |
Generated from api/proto/mellow/peek/local/v1/ by protoc-gen-es: the local API's messages and Connect services; never edit. |
src/client/rpc.ts |
The generated local API in one import, and the decoding of the protocol types it carries as JSON text (teamOf, envelopeOf, sessionOf). |
src/client/daemon.ts |
The live client: the Connect services over the daemon's socket or pipe, the follow subscription, reconnect. Tested against src/client/testing/fakeDaemon.ts, a Connect server on a socket. |
src/client/paths.ts |
Config dir, socket, and token resolution per platform. |
src/client/mock.ts, fixtures.ts |
The fixture-replaying backend. |
src/model/state.ts |
Pure view-model: sessions deduped on (person, device, session_id), feed items classified as what changed, directives, claims index. Unit-tested. |
src/model/compose.ts, src/model/tree.ts |
The consumer side of summaries (facts fold under the summary that covers them, ported from mellow-peek-core) and the file tree a session's paths become. |
src/presenceEvents.ts, src/presence.ts |
Human presence: pure event builders, and the editor wiring with debounce. |
src/cli.ts |
Runs the mellow-peek binary for the setup commands. |
src/e2e/, scripts/e2e.mjs |
Extension-host suite and its launcher. |
src/views/, src/decorations.ts, src/statusBar.ts |
The vscode-facing layer. |
scripts/protocol-generator.mjs |
Merges the per-root schema files and runs json-schema-to-typescript. |
The generated file is excluded from Biome; everything else must pass pnpm exec biome check ..
| |