Task → Slack → Claude Dispatcher
Automatically dispatches your highest-priority tasks from Linear or GitHub Issues to a Slack channel, mentioning @claude to trigger a Claude Code web session.
How It Works
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Linear Tasks │ │ │ │ │
│ (by priority) │────▶│ This Script │────▶│ Slack Channel │
├─────────────────┤ │ (scheduled) │ │ @claude │
│ GitHub Issues │────▶│ │ │ │
│ (by labels) │ └─────────────────┘ └─────────────────┘
└─────────────────┘ │
▼
┌─────────────────┐
│ │
│ Claude Code │
│ (activates) │
│ │
└─────────────────┘
- Scheduler runs (every 3 hours or custom interval)
- Fetches from Linear or GitHub the highest-priority task
- Posts to Slack with
@claude In project X, work on: <task> - Claude Code activates automatically via your existing Slack integration
Setup
1. Choose Your Task Source
Set TASK_SOURCE in your environment:
linear(default) - Use Linear for task managementgithub- Use GitHub Issues
2. Create Slack App
Important: You need a User Token (
xoxp-...), not a Bot Token. Claude's Slack integration ignores bot messages to prevent loops, so messages must appear from a human user.
- Go to api.slack.com/apps → Create New App
- Choose "From scratch"
- Under OAuth & Permissions, scroll to User Token Scopes (not Bot Token Scopes):
chat:write(post messages as yourself)
- Install to your workspace
- Copy the User OAuth Token (
xoxp-...)
3. Get Slack IDs
Channel ID:
- Right-click the channel → View channel details
- Scroll to the bottom to find the Channel ID (
C0XXXXXXXXX)
Claude User ID:
- In Slack, type
@claudeand send a message - Use Slack's API or inspect the network request to find the user ID (
U0XXXXXXXXX) - Alternatively: Slack App → Your App → Features → App Home → shows the bot user ID
4. Configure Task Source
Option A: Linear Setup
- Go to Linear → Settings → API → Personal API keys
- Create a new key with read access to issues
- Copy the
lin_api_...token
TASK_SOURCE=linear
LINEAR_API_KEY=lin_api_xxxxxxxxxxxx
# Optional: Filter to specific projects/teams
LINEAR_PROJECT_IDS=proj_abc123,proj_def456
LINEAR_TEAM_KEYS=ENG,PRODUCTOption B: GitHub Issues Setup
- Go to github.com/settings/tokens
- Create a Personal Access Token with
reposcope - Copy the
ghp_...token
TASK_SOURCE=github
GH_TOKEN=ghp_xxxxxxxxxxxx
GH_OWNER=your-username
GH_REPO=your-repo
# Optional: Custom label for ready issues (default: claude-ready)
GH_READY_LABEL=claude-readyGitHub Priority Labels:
The dispatcher sorts GitHub issues by priority labels:
priority:urgent- Highest priority (1)priority:high- High priority (2)priority:mediumorpriority:normal- Medium priority (3)priority:low- Low priority (4)
Issues without priority labels default to medium priority.
GitHub Workflow:
- Add the
claude-readylabel to issues you want dispatched - Optionally add priority labels to control order
- When dispatched, the
claude-readylabel is removed andin-progressis added
5. Configure Environment
Copy .env.example to .env and fill in your values:
Running
Local (one-time)
npm install npm run dispatch
Scheduled (GitHub Actions)
-
Push this repo to GitHub
-
Add secrets in Settings → Secrets and variables → Actions:
Required (always):
SLACK_USER_TOKENSLACK_CHANNEL_IDCLAUDE_USER_ID
For Linear:
LINEAR_API_KEY
For GitHub Issues:
GH_DISPATCH_TOKEN(secret) - classic PAT withreposcope
-
Add variables:
TASK_SOURCE(set tolinearorgithub)LINEAR_PROJECT_IDS(optional)LINEAR_TEAM_KEYS(optional)GH_OWNER- repo ownerGH_REPO- repo nameGH_READY_LABEL(optional)GH_DISPATCH_ENABLED- set totrueto enable scheduled runs
The workflow runs every 3 hours. Edit .github/workflows/dispatch.yml to change the schedule:
schedule: - cron: '0 */3 * * *' # Every 3 hours - cron: '0 9 * * *' # Daily at 9 AM UTC - cron: '0 9,14 * * 1-5' # 9 AM and 2 PM, weekdays only
Alternative: Cron Job
# Add to crontab (every 3 hours) 0 */3 * * * cd /path/to/linear-slack-dispatcher && npm run dispatch >> /var/log/dispatcher.log 2>&1
Customization
Priority Selection (Linear)
By default, selects the highest-priority unassigned task. Modify getHighestPriorityLinearTask() in src/index.ts:
const filter = { state: { type: { in: ["backlog", "unstarted", "started"] } }, // Remove this line to include assigned tasks: assignee: { null: true }, // Only tasks with a priority set: priority: { neq: 0 }, // Add label filter: labels: { name: { in: ["claude-ready"] } }, };
Priority Selection (GitHub)
Modify getHighestPriorityGitHubTask() in src/index.ts to change filtering:
const { data: issues } = await client.rest.issues.listForRepo({ owner: CONFIG.GITHUB_OWNER, repo: CONFIG.GITHUB_REPO, state: "open", labels: CONFIG.GITHUB_READY_LABEL, assignee: "none", // Only unassigned issues sort: "created", direction: "asc", per_page: 50, });
Message Format
Customize formatSlackMessage() to change how tasks are presented to Claude.
Note: Claude cannot access external links, so the task title and description should contain all necessary context.
function formatSlackMessage(task: Task): string { return `<@${CONFIG.CLAUDE_USER_ID}> Please work on this task: Task: ${task.title} Description: ${task.description} Additional context: Focus on test coverage and documentation.`; }
Troubleshooting
"No tasks to dispatch"
- Linear: Check LINEAR_PROJECT_IDS and LINEAR_TEAM_KEYS filters; verify tasks are in "backlog"/"unstarted" state; check if tasks are assigned; ensure tasks have a priority set
- GitHub: Ensure issues have the
claude-readylabel; check GITHUB_OWNER and GITHUB_REPO are correct
Slack API errors
- Check user token has
chat:writescope - Verify SLACK_CHANNEL_ID is correct
Claude not responding
- Ensure you're using a User Token (
xoxp-...), not a Bot Token — Claude ignores bot messages - Ensure your Claude Code Slack integration is active
- Verify CLAUDE_USER_ID is the correct user ID for @claude
- Check the channel is configured for Claude Code triggers
GitHub label errors
- Ensure the
claude-readylabel exists in your repository - Optionally create an
in-progresslabel for dispatched issues
License
MIT