skills/slack-cdp/SKILL.md
Control Slack via CDP or headless API tokens. Navigate channels, read/send messages, search conversations, check unreads, and manage status. Two modes: CDP (Slack desktop with --remote-debugging-port) for full UI control, or headless (xoxp/xoxb token) for data operations without Slack running. Triggers on: slack, read slack, search slack, slack unreads, send slack message, slack status, navigate slack, check slack, slack messages, go to channel, slack DM.
npx skillsauth add catalan-adobe/skills slack-cdpInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Control Slack via two modes:
SLACK_USER_TOKEN or SLACK_BOT_TOKEN env var). No Slack
desktop needed. Covers data operations: read, send, search, status.Zero dependencies beyond Node 22. CDP mode also uses cdp-connect.
Slack must be running with remote debugging enabled:
/Applications/Slack.app/Contents/MacOS/Slack --remote-debugging-port=9222
If connect fails, tell the user to quit Slack and relaunch with
that flag. Their session persists — they stay logged in.
slack-cdp.js — main script:
if [[ -n "${CLAUDE_SKILL_DIR:-}" ]]; then
SLACK_JS="${CLAUDE_SKILL_DIR}/scripts/slack-cdp.js"
else
SLACK_JS="$(command -v slack-cdp.js 2>/dev/null || \
find ~/.claude -path "*/slack-cdp/scripts/slack-cdp.js" -type f 2>/dev/null | head -1)"
fi
if [[ -z "$SLACK_JS" || ! -f "$SLACK_JS" ]]; then
echo "Error: slack-cdp.js not found." >&2
exit 1
fi
cdp.js — for UI operations (click, screenshot, ax-tree):
CDP_JS="$(command -v cdp.js 2>/dev/null || echo "$HOME/.local/bin/cdp.js")"
node "$SLACK_JS" connect # Verify CDP + auth
node "$SLACK_JS" where # Current view info
node "$SLACK_JS" navigate <query> # Cmd+K quick switcher
node "$SLACK_JS" read [--channel ID] [--limit N] # Read messages
node "$SLACK_JS" send <channel|current> <message> # Send message
node "$SLACK_JS" search <query> [--limit N] # Full-text search
node "$SLACK_JS" whoami # User and team info
node "$SLACK_JS" unread # Unread channels
node "$SLACK_JS" status [":emoji: text"] # Get or set status
All commands default to port 9222. Override with --port N.
When the user asks about Slack, pick the right tool:
Data operations (read, search, send, status, unread, whoami) → Use the API subcommands. Fast, reliable, structured output.
Navigation (go to a channel, switch DM, open an app)
→ Use navigate <query>. Works from any view.
Verify current state (what channel am I in?)
→ Use where. Reads title, channel ID, active tab from DOM.
Click a UI element (tab, button, sidebar item)
→ Delegate to cdp.js click '<selector>' with data-qa selectors.
Visual debugging (something looks wrong, need to see the screen)
→ Last resort: cdp.js screenshot /tmp/slack.png
Never screenshot to verify navigation. Use where instead.
Common multi-step operations:
Go to a channel and read messages:
node "$SLACK_JS" navigate "engineering"
node "$SLACK_JS" read --limit 20
Search and navigate to result:
node "$SLACK_JS" search "deployment issue" --limit 5
# Extract channel from results, then:
node "$SLACK_JS" navigate "channel-name"
Summarize unreads:
node "$SLACK_JS" unread
# For each unread channel:
node "$SLACK_JS" read --channel <ID> --limit 10
After navigate, always verify with where:
node "$SLACK_JS" navigate "Andrei Tuicu"
node "$SLACK_JS" where
# Confirm title matches expected destination
For tab switching or button clicks not covered by subcommands,
use cdp.js with Slack's data-qa selectors:
node "$CDP_JS" click '[data-qa="messages"][role="tab"]' --port 9222
node "$CDP_JS" click '[data-qa="message_input"]' --port 9222
Prefer data-qa attributes (Slack's own QA hooks) over CSS classes.
If a Slack OAuth token is available via environment variable, data operations can run without Slack desktop or CDP. Two token types:
| Token | Env var | Acts as | Best for |
|-------|---------|---------|----------|
| xoxp- (user) | SLACK_USER_TOKEN | The user | Full access: read, send, search, status |
| xoxb- (bot) | SLACK_BOT_TOKEN | The bot | Channels the bot is in; sends as @bot |
Prefer the user token — it covers every data command. The bot token can only read channels it's been invited to and sends as the bot identity.
# Auth test
curl -s -X POST https://slack.com/api/auth.test \
-H "Authorization: Bearer $SLACK_USER_TOKEN"
# Read channel
curl -s -X POST https://slack.com/api/conversations.history \
-H "Authorization: Bearer $SLACK_USER_TOKEN" \
-d "channel=CHANNEL_ID&limit=10"
# Search (user token only)
curl -s -X POST https://slack.com/api/search.messages \
-H "Authorization: Bearer $SLACK_USER_TOKEN" \
-d "query=your+search&count=5"
# Send message
curl -s -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_USER_TOKEN" \
-d "channel=CHANNEL_ID&text=Hello"
Use headless (token) when:
Use CDP when:
User token (xoxp-) — requires a Slack app with User Token Scopes:
channels:history, channels:read, chat:write, search:read,
users.profile:read, users.profile:writehttps://localhost as a Redirect URLhttps://slack.com/oauth/v2/authorize?client_id=CLIENT_ID&user_scope=channels:history,channels:read,chat:write,search:read,users.profile:read,users.profile:write&redirect_uri=https://localhostcode parameter from the redirect URLcurl -s -X POST https://slack.com/api/oauth.v2.access \
-d "client_id=CLIENT_ID&client_secret=CLIENT_SECRET&code=CODE&redirect_uri=https://localhost"
authed_user.access_token field is your xoxp- tokenBot token (xoxb-) — generated automatically when the app is
installed to a workspace. Found on the OAuth & Permissions page.
| Error | Fix |
|-------|-----|
| Cannot connect to CDP on port 9222 | Restart Slack with --remote-debugging-port=9222 |
| No Slack page target found | Slack is running but not fully loaded — wait and retry |
| enterprise_is_restricted | API method blocked on Enterprise Grid — use search or DOM fallback |
| Auth failed | Slack session expired — re-login in Slack, then retry |
navigate uses Cmd+K — works from any view, no need to check starting statesend current <msg> sends to whatever channel is currently opensearch returns results across all channels — good for finding channel IDsstatus with no args reads current status; with args sets itread without --channel reads from the currently visible channeltools
Reduce a webpage to a structural skeleton with semantic tokens. Two-phase pipeline: Phase 1 injects a browser script that tokenizes content ({TEXT}, {HEADING:n}, {IMAGE:WxH}, {CTA:label}, {LINK:label}, {INPUT:type}, {VIDEO}, {ICON}). Phase 2 applies LLM structural reasoning to collapse repeated patterns ({REPEAT:N}), remove decorative wrappers, strip utility classes, and produce skeleton.html + manifest.json. Use when migrating pages to EDS, analyzing page structure, extracting page blueprints, or preparing input for GenAI block generation. Triggers on: reduce page, page skeleton, page blueprint, extract structure, tokenize page, page reduction, structural skeleton, reduce URL.
tools
Capture a spatial hierarchy of rendered DOM elements from any webpage. Injects a pre-built script via playwright-cli that walks the DOM, detects layout grids, extracts backgrounds, prunes invisible nodes, promotes elements rendered outside their DOM parent (overlays, fixed navs, modals), and tags overlay nodes with occlusion metadata. Returns three outputs: LLM-friendly indented text, structured JSON tree, and a nodeMap mapping positional IDs to CSS selectors with background and overlay data. Use before page decomposition, overlay detection, brand extraction, or any workflow that needs structured page analysis. Triggers on: visual tree, capture tree, page structure, page hierarchy, DOM tree, capture visual, page analysis, extract tree.
tools
Summarize any video by analyzing both audio and visuals. Downloads via yt-dlp, extracts transcript (YouTube captions or Whisper), pulls scene-detected keyframes, and produces a multimodal summary with clickable timestamped YouTube links. Use this skill whenever the user wants to summarize a YouTube video, digest a talk or tutorial, get notes from a video, extract key points from a recording, or says things like "tl;dw", "summarize this video", "what's in this video", or pastes a YouTube URL and asks for a summary. Also triggers for non-YouTube URLs that yt-dlp supports.
development
Design and build web UIs with Adobe Spectrum 2 design system. Applies S2 layout principles, visual hierarchy, spacing, and component composition to produce accessible interfaces. Outputs vanilla CSS with Spectrum tokens (static pages) or Spectrum Web Components (interactive apps). Recommends tier based on complexity. Covers sp-theme setup, side-effect imports, overlay system, form patterns, --mod-* token customization, and 14 critical gotchas. Use for: spectrum 2 web, SWC, sp-button, sp-theme, build UI with spectrum, S2 layout, spectrum application, adobe design system, web component form, spectrum overlay.