-
Notifications
You must be signed in to change notification settings - Fork 0
Configuration
Config lives at ~/.config/slk/config.toml.
[general]
default_workspace = "work" # the slug, not the team ID
use_slack_sections = true # use real Slack sidebar sections (default).
# set false to use [sections.*] globs instead.
keep_focus_on_list = false # keep focus on the list when you select a
# channel / open a thread (default false).
# See "Browsing without losing focus" below.
[appearance]
theme = "dracula"
timestamp_format = "3:04 PM"
image_protocol = "auto" # auto | kitty | sixel | halfblock | off
max_image_rows = 20 # cap inline image height in terminal rows
panel_borders = true # draw the box border around each pane;
# false = a thin left accent bar instead
[animations]
enabled = true
smooth_scrolling = true
typing_indicators = true
[notifications]
enabled = true
on_mention = true
on_dm = true
on_keyword = ["deploy", "incident"]
quiet_hours = "22:00-08:00" # planned
[cache]
message_retention_days = 30
max_db_size_mb = 500
max_image_cache_mb = 200
identity_ttl_days = 7 # re-fetch a user/bot's name + avatar after N days
# so renames and new avatars show up; 0 = never refresh
# Glob-based channel sections — only consulted when use_slack_sections
# is false (globally or per-workspace), or when Slack's section API is
# unreachable. Otherwise slk reads the user's actual Slack sections.
[sections.Alerts]
channels = ["alerts", "ops", "*-alerts"]
order = 1
# Channels can carry an optional ":<N>" suffix to pin their order
# within the section. Lower numbers sort higher. Entries without a
# suffix fall after annotated ones, in the order they appear.
# This syntax is only honored when use_slack_sections = false;
# in Slack-native mode, channel order comes from Slack.
[sections.Engineering]
channels = ["eng-general:1", "eng-alerts:2", "eng-*"]
order = 2
# Per-workspace settings: keyed by a slug you choose at --add-workspace
# time. team_id ties the slug to the underlying Slack workspace.
[workspaces.work]
team_id = "T01ABCDEF"
order = 1 # rail position; 1-based, used by 1-9 keys
theme = "dracula" # overrides [appearance].theme
use_slack_sections = false # this workspace uses [sections.*] globs;
# other workspaces still use Slack sections
[workspaces.work.sections.Alerts]
channels = ["alerts", "*-alerts"]
order = 1
[workspaces.work.sections.Engineering]
channels = ["eng-*", "deploys"]
order = 2
# A second workspace with no per-workspace sections — falls back to
# the global [sections.*] above.
[workspaces.side]
team_id = "T02XYZ"
order = 2
# Inline color overrides on top of the active theme
[theme]
primary = "#4A9EFF"
accent = "#50C878"
background = "#1A1A2E"
text = "#E0E0E0"Run a program against the selected message with the x key (pick from a
list), or automatically when an incoming message matches a trigger.
# Manual: select a message, press `x`, pick this command.
[[external_commands]]
name = "Create task"
argv = ["/home/me/bin/slk-task"] # run directly (no shell)
[[external_commands]]
name = "OCR image"
argv = ["tesseract", "-", "-"]
capture_output = true # show stdout in an overlay (else a toast)
[[external_commands]]
name = "Edit in editor"
argv = ["nvim"]
interactive = true # suspend the TUI and attach the terminal
confirm = true # ask before running
# Auto: runs in the background when an incoming message matches.
[[external_commands]]
name = "Notify on mention"
argv = ["/home/me/bin/notify"]
on_mention = true # fires when you are @-mentioned
[[external_commands]]
name = "Page on deploy fail"
argv = ["/home/me/bin/page"]
match = "deploy failed" # case-insensitive substring
# match_regex = "deploy (failed|errored)" # alternative: RE2 regex
channels = ["alerts", "ops"] # optional: only these channel names
include_self = false # set true to also fire on your own messagesHow the message reaches the command. argv is executed directly (no
shell, so no quoting hazards). The message text is piped to stdin, and
metadata is provided via environment variables:
| Variable | Value |
|---|---|
SLK_TEXT |
message text (also on stdin) |
SLK_USER / SLK_USER_ID
|
author display name / ID |
SLK_TS |
message timestamp |
SLK_CHANNEL / SLK_CHANNEL_NAME
|
channel ID / name |
SLK_PERMALINK |
archive link (when known) |
SLK_IMAGE_PATHS |
newline-separated on-disk paths of the message's cached images |
SLK_IMAGE_COUNT |
number of image paths |
Execution flags (manual or auto): capture_output shows stdout in a
scrollable overlay instead of a completion toast; interactive releases the
terminal to the command (editors / TUI tools) and restores slk on exit;
confirm prompts first. Auto-runs are always in the background (never
interactive).
Auto-triggers. A command with on_mention, match, and/or match_regex
also runs automatically on incoming messages (across all workspaces and
channels), firing if any of its triggers match. channels limits it to
named channels. Your own messages are skipped unless include_self = true. A
command can be both manual (x) and auto.
By default, picking a channel from the sidebar — or opening a thread — moves
keyboard focus into the content pane, so you can immediately j/k through
messages, react, and reply. The trade-off: to try another channel you have
to Tab/h back to the sidebar first.
Set keep_focus_on_list = true under [general] to keep focus on the list
instead. Selecting a channel or opening a thread still loads the content,
but keyboard focus stays put — so you can keep walking the list with j/k
and Enter without tabbing back each time. When you want to read or reply,
step into the content with Tab, l, or →. This applies to all three
list→content jumps: sidebar → channel, threads view → thread, and a channel
message → its thread. The default is false (focus follows selection).
When use_slack_sections = true (the default) and Slack's section endpoint
is reachable, slk reads the user's actual sidebar sections — names, emoji,
linked-list order, and channel membership — directly from Slack and keeps
them live via WebSocket events. Any [sections.*] or
[workspaces.<slug>.sections.*] blocks in config.toml are ignored in this
mode (a one-line info note is emitted to the debug log on first connect so
the shadowing isn't silent). Set use_slack_sections = false globally, or
per-workspace, to opt into glob-based sections instead.
Per-workspace [workspaces.<slug>.sections.*] blocks fully replace the
global [sections.*] for that workspace. Workspaces that define no
sections of their own fall back to the global table.
Each entry in a section's channels list may carry an optional :<N>
suffix where N is a non-negative integer. Channels matched by an
annotated pattern sort ahead of channels matched by un-annotated
patterns; among annotated channels, lower N wins; un-annotated
channels keep the order Slack returned them in.
[sections.Engineering]
channels = ["eng-general:1", "eng-alerts:2", "eng-*"]
order = 2In the example above, #eng-general is pinned to the top of the
Engineering section, followed by #eng-alerts, followed by every
other eng-* channel in Slack-API order.
This syntax is only honored when use_slack_sections = false (or
when Slack's section endpoint is unreachable and slk falls back to
glob mode). In Slack-native mode, channel order within a section
comes from Slack and the :<N> suffix is ignored along with the
rest of the [sections.*] block.
v1 is read-only — section editing still happens in the official client; slk
reflects the results. Your Starred conversations (Slack's stars
section) are surfaced read-only and pinned to the top of the sidebar: star
or unstar in the official client and slk reflects it live. Other system
sections (slack_connect, salesforce_records, agents) remain hidden.
Because this comes from Slack's section data, the Starred section only
appears in Slack-native mode (use_slack_sections = true, the default); it
is not available in glob-section mode. Sections with more than 10 channels
may be returned only partially by Slack's API on initial load; the missing
channels temporarily fall into the catch-all bucket and migrate into their
correct section as WebSocket events fire or the workspace reconnects. A
debug-log warning identifies which sections were truncated.
The order field controls workspace position in the rail and the mapping
for the 1–9 digit keys. Positive values sort ascending (lowest first);
workspaces without an order (or with order = 0) sort after explicitly
ordered ones, alphabetically by slug. Tokens on disk that have no
[workspaces.<slug>] block at all sort last, alphabetically by team ID.
The order is stable across runs. Previously the rail order depended on
which workspace's WebSocket connected first; it is now deterministic
regardless of network timing, even without an explicit order set.
Legacy configs that key the block by raw team ID
([workspaces.T01ABCDEF]) keep working unchanged.
Two built-in themes use ANSI 16 color codes exclusively rather than fixed RGB values. They inherit the user's terminal color palette, so changing your terminal colorscheme (light/dark, solarized, accessibility palettes, etc.) immediately changes slk's UI colors to match.
[appearance]
theme = "ANSI Dark" # or "ANSI Light"Pick the variant whose background matches your terminal's background.
Trade-off: selection-row highlights and compose-input tints are still computed as RGB approximations, so the tint regions of those elements use truecolor rather than your palette. The rest of the UI honors the palette.
Drop .toml files into ~/.config/slk/themes/:
name = "My Theme"
[colors]
primary = "#BD93F9"
accent = "#50FA7B"
warning = "#FFB86C"
error = "#FF5555"
background = "#282A36"
surface = "#343746"
surface_dark = "#21222C"
text = "#F8F8F2"
text_muted = "#6272A4"
border = "#44475A"
# Optional sidebar/rail overrides — lets you have a darker sidebar with a
# lighter message pane (Slack's default look). Fall back to
# background/text/text_muted/surface_dark when omitted.
sidebar_background = "#19171D"
sidebar_text = "#D1D2D3"
sidebar_text_muted = "#9A9B9E"
rail_background = "#19171D"Every built-in theme now sets a channels-panel (sidebar) background that is
perceptibly distinct from the message pane. When writing a custom theme,
set sidebar_background to a clearly darker (or, on near-black themes, a
slightly lighter) shade than background for the same effect.
Switch themes live with Ctrl+y.
| Path | Contents |
|---|---|
~/.config/slk/ |
Configuration, custom themes |
~/.local/share/slk/ |
SQLite cache, tokens |
~/.cache/slk/ |
Avatars, image cache |
Getting started
Using slk
Reference