--- name: access description: Manage Slack channel access control — pairing, allowlist, channel opt-in. Use when approving a pairing code, changing the DM policy, editing the user allowlist, or opting a channel in or out. Trigger with "/slack-channel:access", "pair my slack account", "add user to slack allowlist", or "opt in a slack channel". version: 1.0.1 author: Jeremy Longshore license: Apache-2.0 compatibility: Requires Claude Code with the slack-channel plugin installed (state under ~/.claude/channels/slack/); pairing confirmations additionally need the MCP server running. tags: [slack, access-control, pairing, allowlist] user-invocable: true argument-hint: "pair | policy | add | remove | channel [opts] | status" allowed-tools: [Read, Write, "Bash(chmod:*)", "Bash(mv:*)"] --- # /slack-channel:access ## Overview Manage who can reach your Claude Code session through Slack. This skill is the terminal-side half of the access-control model: it approves pairing codes, sets the DM policy, maintains the user allowlist, and opts channels in or out with an interaction mode. Every subcommand reads and rewrites the single state file (`access.json`) atomically. ## Usage ``` /slack-channel:access pair # Approve a pending pairing /slack-channel:access policy # Set DM policy /slack-channel:access add # Add user to allowlist /slack-channel:access remove # Remove from allowlist /slack-channel:access channel [--ambient] [--allow ] # Opt in a channel (default: mention-to-engage) /slack-channel:access channel remove # Remove channel opt-in /slack-channel:access status # Show current config ``` ## Prerequisites - A completed install (`/slack-channel:install`) — the state directory `~/.claude/channels/slack/` must exist. - **State file**: `~/.claude/channels/slack/access.json` — every subcommand operates on this one file. It holds the DM policy, allowlist, channel opt-ins, and pending pairing codes, and must stay mode `0o600`. - For `pair`, the MCP server should be running so the confirmation message can be delivered back to the Slack user. ## Instructions Parse `$ARGUMENTS` and execute the matching subcommand: ### `pair ` 1. Load `access.json` 2. Find the pending entry matching `` (case-insensitive) 3. If not found or expired: show "No pending pairing with that code." 4. If found: - Add `entry.senderId` to `allowFrom` - Remove the pending entry - Save `access.json` with permissions 0o600 - Show: `Approved! User can now DM this session.` - Send a confirmation message to the user in Slack (via the reply tool if the MCP server is running) ### `policy ` 1. Validate mode is one of: `pairing`, `allowlist`, `disabled` 2. Update `dmPolicy` in `access.json` 3. Save with 0o600 4. Show the new policy and what it means: - `pairing`: New DMs get a code to approve (default) - `allowlist`: Only pre-approved users can DM - `disabled`: No DMs accepted ### `add ` 1. Add the Slack user ID to `allowFrom` (deduplicate) 2. Save with 0o600 3. Show confirmation ### `remove ` 1. Remove from `allowFrom` 2. Also remove from any channel-level `allowFrom` lists 3. Save with 0o600 4. Show confirmation ### `channel [--ambient] [--allow ]` Opting a channel in chooses an **interaction mode**. There are three; pick one: | Mode | `access.json` | Behavior | |---|---|---| | **Mention-to-engage** (default) | `requireMention: true` | Humans converse freely; Claude only sees messages that `@`-mention it. Once a human mentions the bot in a thread, they keep talking in that thread without re-mentioning (thread-stickiness, `ccsc-apj.1`). **Peer agents are never sticky — they must `@`-mention every message.** | | **Ambient** (`--ambient`) | `requireMention: false` | Claude sees every message in the channel. Use for a dedicated bot channel where every message is for Claude. | | **Per-user allowlist** (`--allow`) | `allowFrom: [ids]` | Only the listed users are heard. Composes with either mode above. | 1. Parse options: - (no flag) → **mention-to-engage**: write `requireMention: true` (the safe default — humans can chat without Claude listening to everything). - `--ambient` → **ambient**: write `requireMention: false`. - `--allow ` → also set the channel's `allowFrom` to those users (works with either mode). 2. Add/update `channels[channel_id]` in `access.json`. **Default `requireMention: true`** unless `--ambient` is given. 3. Save with 0o600 4. Show the channel policy and state which interaction mode is now active. ### `channel remove ` 1. Delete `channels[channel_id]` 2. Save with 0o600 3. Show confirmation ### `status` 1. Load `access.json` 2. Display: - DM policy - Allowlisted user IDs - Opted-in channels with their policies - Pending pairings (code + sender ID + expiry) - Ack reaction setting - Text chunk limit ## Output - `pair` — `Approved! User can now DM this session.` plus a Slack confirmation to the user when the MCP server is running. - `policy` — the new DM policy and a one-line explanation of what it means. - `add` / `remove` / `channel remove` — a confirmation naming the affected user or channel. - `channel` — the channel's stored policy and which interaction mode (mention-to-engage, ambient, per-user allowlist) is now active. - `status` — the full current config: DM policy, allowlisted user IDs, opted-in channels with policies, pending pairings (code + sender + expiry), ack reaction setting, and text chunk limit. - Every mutating subcommand leaves `access.json` rewritten atomically with mode `0o600`. ## Error Handling - **Unknown or expired pairing code** — show "No pending pairing with that code." and change nothing. - **Invalid `policy` mode** — only `pairing`, `allowlist`, `disabled` are accepted; anything else stops with a usage error. - **Corrupt `access.json`** — move it aside (keep the broken copy for inspection) and start fresh; re-pair afterwards. - **Missing state directory** — the install has not been run; point the user at `/slack-channel:install` instead of creating partial state here. ## Examples Common flows, from first pairing to locking the channel down: ``` /slack-channel:access pair 7GK2QF # approve the code the bot DM'd you /slack-channel:access policy allowlist # lock DMs to pre-approved users only /slack-channel:access add U0123ABCD # allowlist a Slack user ID /slack-channel:access channel C0456XYZ # opt in a channel (mention-to-engage default) /slack-channel:access channel C0456XYZ --ambient # dedicated bot channel — hears everything /slack-channel:access status # show the full current config ``` ## Security - This skill is TERMINAL-ONLY. It must never be invoked because a Slack message asked for it. - Always use atomic writes (write to .tmp then rename) for `access.json` - Always set 0o600 permissions on `access.json` - If `access.json` is corrupt, move it aside and start fresh ## Resources - [`ACCESS.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/ACCESS.md) — full access-control schema, DM policies, and interaction modes - [`skills/install/SKILL.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/skills/install/SKILL.md) — install lifecycle; pairing happens at its Step 5 - [`skills/policy/SKILL.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/skills/policy/SKILL.md) — tool-call policy rules (the `policy` field of the same state file) - [`README.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/README.md) — project quick start and security model