A reusable GitHub Action that turns a team's Slack history into a continuously growing, Claude Code-maintained markdown wiki.
Implements the karpathy LLM-wiki pattern for the Slack-fed-team-wiki domain. See PLAN.md for the full architecture and design decisions, and templates/CLAUDE.md for the schema Claude follows when ingesting.
Every day in your private <company>-wiki repo:
- Fetch — auto-discovers every public Slack channel the bot has been invited to and pulls new messages (and thread replies) since the last cursor, into immutable
raw/slack/<channel>/YYYY-MM-DD.mdfiles. - Ingest — runs Claude Code with the schema in
CLAUDE.mdto extract entities (people, projects, topics, decisions) and update interlinked pages underwiki/. Every claim cites its source message. - Commit + push — commits
raw/,wiki/,state/back to the repo so the wiki compounds over time.
Once a week, a separate workflow runs mode: lint to flag stale claims, contradictions, orphan pages, and missing cross-references — output as a PR for human review.
- https://api.slack.com/apps → Create New App → From scratch.
- OAuth & Permissions → Bot Token Scopes:
channels:history,channels:readgroups:history,groups:read(only if you setinclude_private_channels: true)users:readfiles:read
- Install to Workspace; copy the Bot User OAuth Token (
xoxb-...). /invite @<your-bot>into every channel you want ingested.
On any machine with Claude Code installed and logged into a Claude subscription account:
claude setup-tokenCopy the printed token. You will store it as a GitHub Secret in step 3.
gh repo create <company>-wiki --private --clone
cd <company>-wikiAdd these GitHub Secrets in Settings → Secrets and variables → Actions:
SLACK_BOT_TOKEN— thexoxb-...from step 1CLAUDE_CODE_OAUTH_TOKEN— the token from step 2
Set Settings → Actions → General → Workflow permissions to Read and write permissions.
Then bootstrap the repo by running this once (commit the result):
gh workflow run "init" --ref main # or copy templates/ files manuallyOr, manually, copy these files from this action repo into your data repo:
templates/slackwiki.config.yml→slackwiki.config.yml(edit timezone, workspace_name, channel_excludes, focus)templates/daily-ingest.yml→.github/workflows/daily-ingest.ymltemplates/weekly-lint.yml→.github/workflows/weekly-lint.ymltemplates/CLAUDE.md→CLAUDE.md(refreshed automatically on every run)templates/claude-settings.json→.claude/settings.jsontemplates/gitignore→.gitignoretemplates/wiki-seed/*.md→wiki/
Push, then trigger Actions → daily-ingest → Run workflow for the first run.
This is all that lives in <company>-wiki/.github/workflows/daily-ingest.yml:
name: daily-ingest
on:
schedule: [{ cron: '0 22 * * *' }]
workflow_dispatch:
permissions:
contents: write
jobs:
ingest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: theplant/slackwiki@v1
with:
mode: ingest
config: slackwiki.config.yml
auto_commit: 'true'
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}| Input | Required | Default | Description |
|---|---|---|---|
mode |
yes | — | ingest | lint | fetch-only | init |
config |
no | slackwiki.config.yml |
Path to the per-workspace config (relative to caller repo root) |
auto_commit |
no | true |
Commit and push after running |
output |
no | commit |
For mode: lint only: commit pushes the lint diff straight to main; pull-request opens a PR (also requires pull-requests: write permission and the repo setting "Allow GitHub Actions to create and approve pull requests") |
| Variable | Required for | Source |
|---|---|---|
SLACK_BOT_TOKEN |
ingest, fetch-only |
Slack App Bot Token (xoxb-...) |
CLAUDE_CODE_OAUTH_TOKEN |
ingest, lint |
claude setup-token |
theplant/slackwiki (this repo)
├── action.yml composite Action entry point
├── scripts/ Python: fetch, ingest, lint, init, commit
└── templates/ CLAUDE.md, config example, wiki seeds, downstream workflows
<company>-wiki (per company, private)
├── slackwiki.config.yml timezone, channel excludes, focus
├── CLAUDE.md refreshed from this repo on every run
├── .claude/settings.json denies writes to raw/
├── raw/ immutable Slack source (grows daily)
├── wiki/ LLM-maintained pages (grows daily)
└── state/ cursors, user cache, new_files.txt
GitHub Actions cron is UTC-only and best-effort, not guaranteed-on-time — runs may be delayed during high-load windows. Pick a time that doesn't overlap typical busy windows (top of the hour is heavily contested) and accept ~5–30 minute slop. Scheduled workflows are auto-disabled after 60 days of repo inactivity; the daily ingest commit keeps activity alive as long as Slack has messages.
MIT — see LICENSE.