JotFlow
Turn team chat into GitHub issues.
JotFlow is a self-hosted, agent-agnostic bridge between a shared Slack channel and your GitHub repositories. Anyone on the team, engineer or not, drops an idea, bug, or request into the channel. JotFlow watches the conversation, connects follow-up messages and replies to the same topic, and has your AI agent draft the corresponding GitHub issue. A human approves it, or the channel is configured to let it through automatically. Either way, the result is posted back to Slack so everyone sees what was created.
Status: 0.1.0, the first release. Setup, drafting, approval cards, and GitHub writes all work: follow the Quickstart below to connect JotFlow to your Slack workspace. The roadmap says what's left, and docs/architecture.md is the full design.
Why
- Agents can now generate issues faster than a small team can track them. People still end up pinging each other on Slack to say "I filed this, go look."
- Non-engineers rarely have a coding agent in a terminal. A Slack channel is something they already use.
- GitHub's own Copilot-in-Slack integration does part of this, but it is closed source, only acts when mentioned, and requires a Copilot Business or Enterprise plan. JotFlow is free, open, runs on your infrastructure, and works with whatever agent your team already has.
How it works
JotFlow runs on your own machine as one Docker Compose project, with no port published. JotFlow's own container is a Slack app that listens to your channels, and the place your team's AI agent connects to, to read the work and propose issues. That connection uses MCP, a standard way for agents to use tools. The agent runs in two containers of its own: one drafts with a read-only GitHub token, and one writes only what a person approved. JotFlow holds no GitHub token.
flowchart LR
subgraph slack["Slack workspace"]
ch["#ideas channel<br/>anyone on the team"]
end
subgraph host["Your machine · one Docker Compose project, no published port"]
subgraph jf["JotFlow container · holds no GitHub token"]
direction TB
app["Slack app<br/>listens for messages,<br/>posts cards and replies"]
core["Orchestrator<br/>waits for threads to settle,<br/>tracks topics, starts runs"]
gate["Approval gate<br/>propose · auto · paused"]
mcp["MCP server<br/>JotFlow's tools for each run"]
db[("Store<br/>message pointers, and drafts<br/>until final; no message text")]
app --> core --> gate
core --- db
core --- mcp
end
subgraph drafting["Drafting agent container"]
direction TB
r1["Runner"] --> cli1["Agent CLI<br/>claude-code · codex · your own command"]
ro["GitHub MCP server, read-only<br/>read token: Issues read only"]
cli1 --> ro
end
subgraph execution["Execution agent container"]
direction TB
r2["Runner"] --> cli2["Agent CLI<br/>claude-code · your own command"]
rw["GitHub MCP server<br/>write token: approved cards only"]
cli2 --> rw
end
end
subgraph outside["Outside"]
gh["GitHub API<br/>your repos"]
model["Model API<br/>such as Anthropic's"]
end
ch -- "messages, replies, reactions" --> app
app -- "proposal cards, issue links" --> ch
core -- "internal network: drafting and question runs" --> r1
gate -- "internal network: approved proposals only" --> r2
cli1 <-- "MCP tools" --> mcp
cli2 <-- "MCP tools" --> mcp
ro --> gh
rw --> gh
cli1 --> model
cli2 --> model
One idea, start to finish:
sequenceDiagram
actor T as Teammate
participant S as Slack
participant U as JotFlow
participant D as Drafting run
participant E as Execution run
participant G as GitHub
T->>S: "Onboarding needs a progress bar" + a few replies
S->>U: message events
Note over U: thread goes quiet → start a drafting run
U->>D: run id + a token for this run only
D->>U: get_work → thread text + active topics
D->>G: search for duplicates (read token)
D->>U: propose_issue(repo, title, body, labels, assignees)
U->>S: proposal card in the thread
T->>S: Approve
S->>U: approval click
Note over U: Gate checks: the person is from this workspace and no guest or external user,<br/>the click is on this card in its own channel, the channel isn't paused,<br/>if approvers are on, the person is one and wrote none of the source messages,<br/>and the content hash matches what the card showed
U->>E: run id for the approved proposal
E->>U: get_work → the exact writes JotFlow planned
E->>G: look for the proposal's hidden marker in recent issues
alt marker found: an earlier attempt already wrote it
E->>U: record_result(that issue's URL)
else no marker
E->>G: create the issue (write token)
E->>U: record_result(issue URL)
end
Note over U: record_result checks the URL matches the approved repo,<br/>or the approved issue for an update
U->>S: reply with the issue link, react on the source messages
In auto mode the Approve step is skipped and the card is posted after the issue exists. Follow-up replies in the thread run the same loop and produce update proposals instead of new issues.
The pieces:
- Slack app. Each team creates their own from the included manifest, named and iconed however they like. It is how JotFlow hears messages and posts back. Slack only talks to installed apps, so this part is unavoidable.
- Orchestrator, the part of JotFlow that coordinates the rest. It waits for a thread to settle before acting, keeps a per-channel index of active topics so related messages land on the same issue, and holds proposals until they are approved.
- Whiteboard MCP server. The agent's only way to act. Tools to fetch the work, propose an issue or an update, link a message to a topic, ask the channel a question, and report the result of a write. Any MCP-capable agent can drive it.
- Your agent. JotFlow starts it in the background, with no window, connected to JotFlow's tools, using a preset for the agent program you use. It runs in two containers: drafting runs read, draft, and search your repos for duplicates with a read-only token, and execution runs perform the approved GitHub writes with the token that can write.
- Approval gate. Each channel is in
propose,auto, orpausedmode. It covers the whole lifecycle: creation, description edits, comments, assignment, and labels.
What a team needs
| Component | Needs | Does not need |
|---|---|---|
| JotFlow | A Slack app in your workspace, created from the included manifest. Over Socket Mode, it reads messages, reactions and channel details in the channels you invite it to, and looks up user names. It posts proposal cards and replies, adds reactions, pins a short help message, and answers the /jotflow slash command. manifests/README.md explains each permission. |
Any GitHub access |
| Your agent | A model login, such as an Anthropic API key for the claude-code preset, and two fine-grained GitHub tokens: one that reads issues, and one that can write them. The agent containers says how to make them |
Any Slack access |
| Your machine | Docker to run JotFlow, and uv or Python 3.12 or newer to run the setup once | A public URL or open ports: Socket Mode connects out to Slack |
Quickstart
You need Docker with Compose 2.24 or newer, uv, and permission to create apps in your Slack workspace. Turning the agent on also needs an Anthropic API key or a Claude account login (and an OpenAI API key for codex), and two fine-grained GitHub tokens. Setup takes ten to fifteen minutes, and the agent about ten more. docs/install.md has every step in full, with sample sessions, and docs/troubleshooting.md covers errors.
- Prerequisites. Install Docker and uv. Check with
docker compose version. - Make a folder for JotFlow. Run
mkdir jotflowandcd jotflow. On a server, use/srv/jotflow. - Run the guided setup. Run
uvx jotflow init. It creates the Slack app from a link, checks both tokens, asks which channels to watch and which repos each feeds, and writes.env,jotflow.toml, and the Compose files. - Invite the bot. In each channel, type
/invite @your-bot. - Check the setup. Run
uvx jotflow check. Every line should sayok, and awarnline says what's missing. - Start JotFlow. Run
docker compose up -d. To turn the agent on, fill in its two files, then follow Turn the agent on. - See it working. Run
docker compose logs -f jotflow, and post a message in a watched channel.
docs/usage.md covers what people in the channel can do: keywords, reactions, /jotflow commands, and asking from a terminal.
Principles
- Bring your own agent. JotFlow never calls an LLM itself. You supply the agent and its credentials.
- JotFlow's code never uses GitHub credentials. Your agent runs in its own container with its own GitHub access, and never sees JotFlow's Slack tokens. It emits structured proposals through JotFlow's tools. Once a proposal is approved, JotFlow hands it back to your agent, which performs the write with the GitHub access you already gave it and reports the issue URL back. JotFlow never asks the agent to write anything a human has not approved, unless the channel is in
automode. - Store pointers, not messages. JotFlow keeps Slack message timestamps, topic links, issue numbers, and proposal state. It does not store message bodies. Thread text is fetched from Slack on demand, passed to the agent, and discarded. Logs redact message content by default.
- The local store is a rebuildable cache. Every issue carries the Slack permalinks it came from; every linked Slack message gets a reply with the issue URL. That makes the correlation map rebuildable from Slack and GitHub; the
jotflow rebuildcommand that does it comes after 0.1.0 (#38), so keep backups until then. - No hosted component, no telemetry. Nothing phones home.
Roadmap
The plan lives in docs/roadmap.md: five milestones from "installable" to "ready for another team," then a sixth for what follows v0.1.0, the decisions each rests on, and what done means for each. Work is tracked as GitHub milestones and issues on this repo.
Contributing
Issues and pull requests are welcome. See CONTRIBUTING.md, and point your coding agent at AGENTS.md. Security concerns go through SECURITY.md, not public issues.
License
JotFlow is an independent open source project. It is not affiliated with or endorsed by Slack Technologies or GitHub, Inc.
Metadata
Release files for jotflow 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| jotflow-0.1.0.tar.gz | 881.2 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jotflow-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.2 MB
Release files / jotflow-0.1.0.tar.gz
| Download URL | jotflow-0.1.0.tar.gz |
|---|---|
| Size | 881.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ace9d64a4ebbac7b9b4878ea6ab4a6d9a678607cec65d5ca21eb5923b3394a57
|
|
BLAKE2b-256 checksum How to use checksums |
341d77395f2868c4c24eeef5301fefea6c2c66da44181242571a15238bbb142a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / jotflow-0.1.0-py3-none-any.whl
| Download URL | jotflow-0.1.0-py3-none-any.whl |
|---|---|
| Size | 326.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3403163e3bbb105d58dd612b5393d1451c2f3ac1e1b9ccc05a1e8f0f65b2afec
|
|
BLAKE2b-256 checksum How to use checksums |
6067792b5b3d1aa9d0d71568587067de3fa1bd888c9bc9ffc4d26b99111d05c9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for jotflow 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for jotflow-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.