Retrieves and classifies Slack workspace messages for digest generation. Activates when the user wants to scan Slack, catch up on channels, find decisions, or asks 'what happened in Slack while I was away?' Covers channel scanning, message extraction, thread context, message classification, and decision detection.
From founder-osnpx claudepluginhub thecloudtips/founder-os --plugin founder-osThis skill uses the workspace's default tool permissions.
references/decision-patterns.mdDesigns and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Retrieve raw Slack messages from specified channels and DMs, extract structured data from each message, classify messages by type, and detect decisions embedded in conversations. This skill serves as the data retrieval and classification layer that both /founder-os:slack:digest and /founder-os:slack:catch-up commands depend on. It handles all Slack MCP interaction, temporal filtering, thread resolution, and message type assignment. Downstream consumers (the message-prioritization skill) receive the classified output for scoring, noise filtering, and digest formatting.
Interact with the Slack workspace through four core MCP operations via the Slack MCP server configured in .mcp.json with the SLACK_BOT_TOKEN environment variable.
Call list_channels to discover all channels the bot can access. Each record includes id, name, is_archived, num_members, topic, purpose. Cache the channel list for the duration of the scan session to avoid redundant API calls. Filter out archived channels from the working set. When the --all flag is active, use this full filtered list as the scan target. When specific channel names are provided, use the list only for ID resolution.
Retrieve messages from a single channel within a time window. Call get_channel_history with the resolved channel ID and computed oldest Unix timestamp (epoch seconds). Set limit to 200 per request to balance completeness with API efficiency. If the response has_more flag is true, paginate using the cursor until complete or the message count exceeds 1000. Cap at 1000 messages per channel to prevent runaway scans on extremely active channels. When the cap is reached, append: "Showing most recent 1000 of [N] messages in time window."
Retrieve reply messages for a specific parent message. Call get_thread_replies with the channel ID and parent message ts (thread timestamp). The response includes the parent message plus all replies. Apply the selective fetching rules from Thread Context Resolution below to avoid fetching threads unnecessarily. Limit each thread fetch to 20 replies maximum.
When --include-dms is provided, call list_conversations with types: "im" to discover DM channels, then apply the same get_channel_history logic. Requires im:history bot scope. If unavailable, skip with warning: "DM scanning unavailable -- bot lacks im:history scope."
Translate user-friendly channel names to Slack internal IDs before any history or thread API call.
#general, general, #my-channel, my-channel.#, convert to lowercase.name field.id for all API calls.When the user provides a channel ID directly (starts with C + alphanumeric), accept it as-is.
Convert the user-provided time window into a Unix timestamp for the oldest parameter.
| Input Format | Interpretation | Example |
|---|---|---|
Nh | N hours ago | 8h = 8 hours ago |
Nd | N days ago | 3d = 72 hours ago |
Nw | N weeks ago | 1w = 7 days ago |
YYYY-MM-DD | Specific date at 00:00:00 local | 2026-03-01 |
YYYY-MM-DD HH:MM | Specific date and time | 2026-03-01 09:00 |
| (none) | Default: 24 hours ago | Omitted = 24h |
^\d+[hdw]$ for relative, ^\d{4}-\d{2}-\d{2} for absolute).oldest parameter to all channel history requests.Process channels sequentially to respect Slack API rate limits.
--all is active, sort by num_members descending.On HTTP 429: wait 10 seconds, retry once. If the retry also fails, skip the channel with a warning and continue. Include skipped channels in the final scan summary.
Extract a structured record from each raw Slack message with the following fields:
| Field | Type | Description |
|---|---|---|
id | string | client_msg_id or fall back to ts |
channel_id | string | Channel where the message was posted |
channel_name | string | Human-readable name with # prefix |
author | string | Resolved display name (cached per session) |
author_id | string | Slack user ID |
text | string | Full message text, Slack markup preserved |
timestamp | string | Slack ts (unique within channel) |
thread_ts | string | Parent thread timestamp; null if not threaded |
reply_count | integer | Thread reply count; 0 if not a parent |
reaction_count | integer | Total reactions (computed) |
reactions | list | Emoji names with counts |
is_bot | boolean | True if from a bot or integration |
subtype | string | Slack subtype or null for normal messages |
has_attachments | boolean | True if files or rich attachments present |
permalink | string | Constructed deep link URL |
mentions | list | User IDs extracted from <@U...> patterns |
edited | boolean | True if message was edited |
deleted | boolean | Always false; deleted messages are excluded |
Resolve user IDs to display names via the Slack API. Maintain a session-level user cache: on first encounter of a user ID, resolve the display name and store it. Reuse the cached name for all subsequent messages from the same author. When resolution fails (deleted user, deactivated account), use "Unknown ([user_id])".
Construct permalinks using the pattern: https://[workspace].slack.com/archives/[channel_id]/p[ts_without_dot]. Replace the dot in the ts with nothing (e.g., 1709510400.001234 becomes p1709510400001234). Omit permalink when workspace domain is unknown rather than guessing.
Fetch thread replies selectively to minimize API calls while capturing important context.
Fetch replies only when a parent message meets at least one criterion:
reply_count >= 3 -- meaningful discussion, not simple acknowledgment.${CLAUDE_PLUGIN_ROOT}/skills/slack/slack-analysis/references/decision-patterns.md.reaction_count >= 5 -- strong engagement signal.Skip all other threads. This reduces a 500-message/50-thread channel from 51 API calls to approximately 6-16.
Fetch a maximum of 20 replies per thread. For threads exceeding 20, capture the first 5 and last 15 to preserve opening context and resolution. Include the parent message. Apply the same extraction schema to each reply.
Classify every message into exactly one of 9 types using first-match-wins priority order.
| Priority | Type | Detection Signals |
|---|---|---|
| 1 | decision | Matches patterns from ${CLAUDE_PLUGIN_ROOT}/skills/slack/slack-analysis/references/decision-patterns.md + context signal validation (keyword + 2+ positive signals) |
| 2 | announcement | Posted by channel creator or workspace admin, OR contains @channel/@here/<!channel>/<!here>, OR starts with "Announcing", "Important:", "Update:", "Notice:", "Reminder:", "Please note", "Attention" |
| 3 | action_assignment | @mention (<@U...>) within 50 chars of a task verb: review, update, fix, deploy, create, test, check, send, prepare, schedule, write, approve, investigate, resolve, submit, implement |
| 4 | question | Ends with ? after trimming, OR starts with question word (who, what, when, where, why, how, can, could, should, would, is, are, does, do, will, has, have). Match whole words at message start only |
| 5 | status_update | Contains progress language: "done", "completed", "shipped", "blocked", "in progress", "WIP", "working on", "finished", "launched", "merged", "deployed", "released", "started", "on track", "behind schedule" (case-insensitive) |
| 6 | fyi | Contains "FYI", "heads up", "PSA", "for your information", "just so you know", OR short message (< 100 chars) sharing a link with brief context, OR contains "sharing this", "thought you'd want to see" |
| 7 | discussion | Thread with 3+ unique participants, OR alternating authors across 4+ consecutive thread messages |
| 8 | noise | Clear-cut noise only: channel join/leave, pure emoji, greeting-only. Full noise filtering belongs to message-prioritization skill |
| 9 | other | Default when no type matches |
is_bot: true for downstream filtering.decision.Activate only with explicit --include-dms opt-in.
im:history scope by attempting to list IM conversations. On failure, warn and skip.list_conversations with types: "im".Never scan DMs without explicit opt-in. Label all DM content clearly: "DM with [display_name]".
When --all is provided, auto-discover and scan all accessible channels.
num_members descending.Handle these 6 scenarios without halting the scan:
is_bot: true. Eligible for type classification. Message-prioritization skill handles final filtering.edited: true. Do not retrieve edit history.For complete decision detection keyword categories, context signal validation rules, confidence classification criteria, and common false positive patterns, consult:
${CLAUDE_PLUGIN_ROOT}/skills/slack/slack-analysis/references/decision-patterns.md