Slack Focus is a local-first macOS app that listens to Slack, scores each message for urgency, and only interrupts you when something looks important.
It is built for people who want a calmer Slack workflow without sending private messages to hosted AI services. Slack messages, labels, predictions, settings, and trained model weights stay on your Mac.
Slack has two painful modes: keep notifications on and get interrupted all day, or mute them and risk missing the message that actually matters. Slack Focus gives you a middle path.
- Watches Slack through your own authorized Slack user token.
- Scores messages from
0.0to1.0with a small local MLX model. - Shows every captured message with its score and priority level, even when it does not notify you.
- Sends macOS notifications only at or above your configured level.
- Opens Slack directly to the relevant channel when you click a notification.
- Lets you label examples as
ignore,low,high, orcritical. - Retrains locally so the model adapts to what you personally consider urgent.
- Stores all local state under your own data folder, not in the repository.
Slack Focus is intentionally boring about data:
- No Slack text is sent to OpenAI, Anthropic, or any hosted model provider.
- Tokens are written only to local
data/settings.json. - Slack messages and labels are stored only in local SQLite.
- Trained weights and vocabulary are stored only in local
models/. - Release builds do not include private data, trained weights, tokens, or Slack history.
The app uses Slack APIs, so it can only read conversations your authorized Slack user can access.
For normal use, download the latest Slack-Focus-macOS.dmg from GitHub Releases:
macOS will show "Slack Focus.app" Not Opened because releases are unsigned. That is Apple Gatekeeper saying the app was not notarized by Apple, not a Slack Focus runtime error.
For a local unsigned build, you can open it with:
xattr -dr com.apple.quarantine "/Applications/Slack Focus.app"
open "/Applications/Slack Focus.app"Requirements:
- macOS 13 or newer.
- Apple Silicon Mac recommended for MLX training.
uvinstalled and available at~/.local/bin/uv,/opt/homebrew/bin/uv,/usr/local/bin/uv, or onPATH.- A Slack app installed in your workspace with the scopes below.
- Optional:
terminal-notifierfor better click-through notifications.
Install uv:
curl -LsSf https://astral.sh/uv/install.sh | shOptional notification helper:
brew install terminal-notifierCreate a Slack app for your workspace:
- Go to https://api.slack.com/apps and create an app.
- Enable Socket Mode.
- Create an app-level token with
connections:write; it starts withxapp-. - Add these User Token OAuth scopes:
channels:readgroups:readim:readmpim:readchannels:historygroups:historyim:historympim:historyusers:read
- Subscribe to workspace events:
message.channelsmessage.groupsmessage.immessage.mpim
- Install the app to your workspace.
- Copy the app-level
xapp-token and the user OAuthxoxp-token into Slack Focus.
- Open Slack Focus.
- Paste your
xapp-andxoxp-tokens. - Click Save Settings.
- Click Backfill to import a recent local training/review set.
- Label a handful of messages.
- Click Train + Rescore.
- Click Enable Listening.
Threshold defaults:
| Level | Score |
|---|---|
| Low | 0.35 |
| Medium | 0.55 |
| High | 0.75 |
| Critical | 0.90 |
Default notification policy is high+.
The classifier is deliberately small. It is not an LLM and does not call a hosted AI API.
message text -> cleanup -> bag-of-words vocabulary -> MLX MLP -> urgency score
In plain English:
- Slack Focus cleans the message text by masking things like user IDs, channel IDs, URLs, and code blocks.
- It converts the message into bag-of-words features, which are simple word-count style inputs.
- A small multilayer perceptron, or MLP, predicts an urgency score from
0.0to1.0. - Thresholds turn that score into
ignore,low,medium,high, orcritical. - Only messages at or above your notification level trigger a macOS notification.
Training uses:
- Public support-ticket priority examples for a cold start.
- Your local Slack labels with extra weight so the model personalizes quickly.
Public bootstrap data is fetched from Prady06/customer-support-tickets on Hugging Face. That dataset is licensed CC-BY-NC-4.0, so treat bootstrap-trained weights as non-commercial unless you retrain without that data.
The tradeoff is intentional: this model is much smaller than an LLM, so it is fast and private, but it improves mostly through examples you label.
Clone the repo:
git clone git@github.com:ParkerSm1th/slack-focus.git
cd slack-focusInstall dependencies and run tests:
uv run pytest
swift buildRun the app from source:
swift run SlackAlertRun CLI commands directly:
uv run python -m slack_priority setup
uv run python -m slack_priority backfill --days 14
uv run python -m slack_priority train --epochs 5
uv run python -m slack_priority score "prod checkout is down"
uv run python -m slack_priority listenCompare eager MLX execution against mx.compile for the local classifier:
uv run python scripts/benchmark_mlx_compile.pyCheck whether the model looks overfit:
uv run python -m slack_priority overfit-checkFor the most honest personal check, use only your own labeled Slack examples:
uv run python -m slack_priority overfit-check --local-onlyThe overfit check trains temporary in-memory models only. It does not overwrite your saved model. It reports train vs validation metrics, compares against a constant-score baseline, and runs a shuffled-label sanity check. Watch for a large train/validation gap or a model that does not clearly beat the baselines.
Plot a Matplotlib learning curve with training iteration on the x-axis and loss on the y-axis:
uv run --with matplotlib python -m slack_priority learning-curve \
--local-only \
--output data/loss_curve.pngIf training loss keeps falling while validation loss rises, the model is probably overfitting. If both losses fall and stay close, more training is helping.
Build a local unsigned DMG:
scripts/build_dmg.shThe output is:
dist/Slack-Focus-macOS.dmg
The DMG contains:
- The Swift macOS app bundle.
- A clean copy of the Python runtime code.
- No local Slack data.
- No tokens.
- No trained weights.
When launched from the DMG/app bundle, Slack Focus copies the clean runtime into:
~/Library/Application Support/Slack Focus/Runtime
User-specific data/, models/, .venv/, settings, labels, and Slack history live there.
Push a tag:
git tag v0.1.0
git push origin v0.1.0GitHub Actions builds the DMG and attaches it to the release automatically.
The workflow also builds the DMG on main pushes as a validation check. It only uploads release assets for version tags.
These are intentionally ignored:
data/models/.venv/.build/dist/- local agent/editor/cache files
Before publishing a release, verify:
git status --short
rg -n "xox[pboa]-|xapp-|your-company|your-team-id|your-user-id" .MIT for the code in this repository. Third-party datasets, Slack APIs, Slack branding, and dependencies keep their own licenses and terms.