Add Slack task watcher - #19
Merged
Merged
Conversation
IsProcessRunning matched the session ID against process argv via pgrep -f. That only works for --resume invocations, which carry the ID explicitly — a freshly launched session (claude/codex started with just a prompt, no --resume) never has its own session ID in argv, since the agent generates that ID internally after starting. Every fresh session therefore read as "stopped" from the very first scan, even while actively running. Match on the process's cwd against the worktree path instead (via lsof), falling back to the old argv match for resumed sessions. Confirmed live: before this fix, a freshly launched worktree's session read "stopped" in zen agent status despite the process being alive (verified via lsof); after the fix it correctly reads "running" then "waiting" once idle.
Poll for a self-added emoji reaction (default :claudecode:) and turn each new one into a feature worktree with a live Claude Code/Codex session, seeded with the Slack thread as the initial prompt. Unlike zen review/zen work new's PR-review flow, which only prepares worktrees silently for later manual resume, this launches the agent immediately: a Slack reaction is a deliberate, comparatively rare signal, not the high-volume PR-review firehose, so there's no need to hold back. On pickup: ack the message in-thread (default 👀), fetch the thread, create the worktree from origin/main, launch the agent, and record the Slack origin (channel/ts/permalink) alongside the worktree. When that worktree's session later goes idle, the existing session-scan loop also sends a Slack DM back — with a resume command and a link to the original thread — instead of just the desktop notification. To stop the DMs once a task is actually done, each origin is checked (on the existing cleanup ticker, and once more right before any notify) for either a merged PR from its branch or a configured done-emoji (default :done_check:) on the original message; either one clears the origin so no further transition re-fires it. Uses its own Slack Web API client (internal/slack, stdlib net/http only, no new dependency) authenticated by a dedicated personal token via ZEN_SLACK_TOKEN — never through the claude.ai account connector, which isn't available to headless/daemon processes, and never stored in config.yaml. Extracted the worktree-from-origin/main git sequence (internal/worktree.CreateFromMain) out of zen work new so the new reconciler doesn't duplicate it a third time. Disabled by default (slack.enabled: false); degrades to a logged warning and continues running the rest of the daemon if enabled without a valid token, rather than failing to start. Verified end-to-end against real Slack threads: reaction detected, worktree created, agent launched, and the completion DM delivered to a real Slack DM (confirmed via conversations.history), across several real poll cycles.
…cher * origin/main: Feat/macos terminal (#16) # Conflicts: # docs/architecture.md # docs/configuration.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A new opt-in feature for
zen watch daemon: poll for a self-added emoji reaction (default:claudecode:), and turn each new one into a feature worktree with a live agent session, seeded with the Slack thread as the initial prompt. When that session later goes idle, send a Slack DM back — not just the existing desktop notification — with a resume command and a link to the thread. Stop sending those DMs once the task is actually done: either a merged PR from the worktree's branch, or a configured done-emoji (default:done_check:) reacted onto the original message.Also includes a standalone fix (separate commit):
IsProcessRunningmatched the session ID against process argv, which only works for--resumeinvocations — a freshly launched session never carries its own ID in argv, so it read "stopped" from the very first scan. Fixed to match on the process's cwd instead (falling back to the old argv match), which also fixes the existing desktop "session waiting" notification for any freshly-launchedzen work new/zen reviewsession, not just this feature.Why
The existing daemon watches GitHub for PR review requests; this gives it a second, symmetric watch loop for a different kind of "an agent should look at this" signal — reacting on something in Slack, which happens far less often than PR review requests, so unlike the PR-review flow (which only preps worktrees silently), this launches the agent immediately.
Kept the Slack I/O to a dedicated client (
internal/slack, stdlibnet/httponly) authenticated by its own token viaZEN_SLACK_TOKEN, rather than trying to piggyback on the claude.ai account connector — confirmed empirically that connector isn't available to headless/daemon Claude Code invocations at all (auth-method-gated), so a background daemon needs its own credentials regardless.Notes
slack.enabled: false); requiresslack.default_repoand a token withreactions:read,channels:history,groups:history,im:history,mpim:history,chat:writescopes (seedocs/configuration.md#slack-task-watcher). Degrades to a logged warning (not a crash) if enabled without a valid token.origin/maingit sequence intointernal/worktree.CreateFromMain, shared betweenzen work newand the new reconciler, instead of a third copy of the same "Could not write new index file" two-step dance.conversations.history).reactions:write/reactions:readrespectively on whatever Slack app provides the token — not exercised by CI, only by hand against a real workspace.