Skip to content

tarikh/cos-slack-worker

Repository files navigation

cos-slack-worker

An always-on Slack worker that turns a private channel into a conversation with your AI chief of staff — running on your own box, against your own markdown brain, at $0 marginal cost on a Claude subscription.

Text it from your phone like you'd text a person. It reads your brain (a private git repo of markdown — see founder-cos for the pattern), answers in-thread, and commits decisions and status changes back to the brain as git commits.

you (phone) ──→ #your-channel ──→ Bolt (Socket Mode, outbound websocket only)
                                    │
                              filter · dedup · turn queue
                                    │
                          Claude Agent SDK (cwd = brain clone)
                                    │
                     serialized git writer ──→ github.com/you/your-brain

What you get

  • No mention ritual. It answers every message you send in the channel, top-level or threaded. A leading . mutes a message.
  • Visible liveness. 👀 within ~2s, swapped for ✅/⚠ when the turn lands. If a turn runs past 4s, a live progress line rolls ("📖 reading todos.md…", "💾 committing to the brain…") and then morphs into the final answer — one clean bot message per turn.
  • Thread memory. Each Slack thread resumes its own agent session (24h window, survives restarts). Long-term memory is the brain repo itself.
  • Captures as commits. With writes enabled, "contract signed, park the dentist thing" becomes edits to your brain files plus a git commit — audit-trailed, reversible, synced everywhere the brain is.
  • Terse-ASK replies. When a scheduled brief in the channel ends with numbered asks, you can answer "1 signed · 2 sent · 3 skip" and each answer is parsed, verified against the brief, captured, and routed to the cheap model.
  • Never silent. Errors post ⚠ in-thread; systemd restarts crashes; if the restart-storm brake trips, an OnFailure unit posts "worker is DOWN" to the channel — the one failure systemd can't otherwise report.
  • Spend guardrails. Daily turn cap always; $-cap hard stop in API-key mode. "cost today?" in the channel answers from the ledger with zero model calls.

Security model

The model never gets Bash and never touches git. Tool access is triple-gated (tools base set + allowedTools + disallowedTools), a PreToolUse hook denies any file write outside the brain clone, and all git operations go through one serialized writer in worker code (stale-lock self-heal, pull-rebase, push with retry). Socket Mode means no inbound port on your box.

Requirements

  • A brain repo: a private GitHub repo of markdown built per founder-cos (SOUL.md, todos.md, MEMORY.md, domain files). The worker is a surface for that brain, not a standalone bot.
  • A box: any always-on Linux host with systemd. 1GB RAM is plenty (~50MB idle, ~250MB peak per turn).
  • Slack: free plan works — Socket Mode needs no public URL.
  • Claude auth, one of:
    • Subscription OAuth token (claude setup-token, Pro/Max plan): $0 marginal cost. Note this token is documented by Anthropic for CI/scripts; whether a personal always-on worker fits your plan's terms is between you and your plan — read them.
    • API key: pay per token; the DAILY_USD_CAP hard stop arms automatically.

Setup

Follow deploy/setup.md — Slack app from slack-manifest.yml, tokens, clone, systemd. Budget about 30 minutes, most of it in Slack's admin UI.

Rollout is staged by env: WORKER_MODE=echo (plumbing test, no model) → WORKER_MODE=agent with AGENT_WRITES=0 (read-only) → AGENT_WRITES=1 (captures commit to the brain).

Customizing the Slack persona

The worker appends surface rules (read budget, Slack formatting, capture protocol) to the agent's system prompt. If your brain repo has a SLACK.md at its root, that file replaces the built-in rules — edit it in the brain, takes effect next turn, no redeploy. Identity itself (who your CoS is, its voice) lives in the brain's own CLAUDE.md/SOUL.md, which the Agent SDK loads because the turn runs with cwd set to the brain clone.

Model routing: capture turns → MODEL_CHEAP; say "think hard" or "use opus" → MODEL_BIG; everything else → MODEL_DEFAULT.

Layout

File Job
src/index.ts Bolt app, event filter + dedup, 👀→✅/⚠ acks, turn queue, live progress line
src/agent.ts Agent SDK sessions (resume per thread), model routing, tool gates, commit sweep
src/writer.ts THE serialized brain writer: stale-lock guard, pull-rebase-commit-push + retry
src/ask-parser.ts pure parser: numbered brief ASKs × terse reply ("1 signed · 2 sent")
src/threads.ts thread_ts → session id map (resume window, persisted)
src/budget.ts daily ledger, turn cap, $ cap (API mode), "cost today?"
src/slack-fmt.ts markdown → Slack mrkdwn + progress lines
test/ npm test — node:test suites; writer tests run against a throwaway repo + file:// remote
slack-manifest.yml paste-in Slack app manifest (includes the private-channel scopes)
deploy/ systemd units (incl. the down-notifier), box bootstrap, deploy.sh

Scope, honestly

Built for one operator, one channel, one brain — the turn queue is global, the auth is one Slack user ID. That's a feature (it's a chief of staff, not a team bot), but know what you're cloning. It also assumes the founder-cos brain conventions (todos.md as hot view, direct-to-main commits). MIT licensed; fork away.

Part of the founder-cos arc — this repo is the v3 substrate: your runtime, your code.

About

Always-on Slack worker for your chief-of-staff brain: Bolt Socket Mode + Claude Agent SDK on your own box

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors