precursor-kanban
A GitHub Projects v2 kanban board for Precursor — and the reference implementation of a Precursor plugin.
What it adds
One package, three contributions:
- A section — sidebar entry, home card, command-palette entry and a
/kanbanroute, with columns derived from each project's Status single-select field, drag-and-drop card moves (updateProjectV2ItemFieldValue), and a card preview showing the issue body, labels and comments plus a jump to the linked Precursor topic. The UI is a built ES module shipped inside this wheel, loaded by the app at runtime. - MCP tools — a
kanban.boardserver (list_boards,get_board,board_summary) so the assistant can read your boards mid-conversation. - API routes —
/api/github/projects, backing the board.
Remove the package and all three disappear; core keeps no knowledge of them.
Install
uv pip install precursor-kanban # into an existing Precursor install
uv tool install "precursor-ai[kanban]" # or alongside the app
…or from Settings → Plugins inside the app, which also offers the restart.
Precursor discovers it at startup through the precursor.plugins entry-point
group; no configuration is needed.
Compatibility
The plugin targets two versioned contracts — precursor.plugin_api on the
Python side and @precursor/host on the frontend side. They move independently
of each other and of the host's own version.
| precursor-kanban | Python contract | Frontend contract |
|---|---|---|
2026.9.* |
PLUGIN_API_VERSION 1 |
HOST_API_VERSION 2 |
Requires a Precursor newer than
2026.7.0.precursor.plugin_api— the entire surface this package compiles against — landed on Precursor'smainafter that release, so2026.7.0cannot load this plugin at all. Use a host built frommain, or the next release.
Requirements
-
Issue associations enabled in Settings → GitHub — the master switch for the whole GitHub surface; the board's endpoints answer 403 without it.
-
A GitHub repository is optional. When set it contributes its owner's boards, which is all it is ever read for; otherwise add projects from the board's +.
-
A token carrying the
projectscope (a superset ofread:project), which thereposcope does not imply:gh auth refresh -h github.com -s project
Without it the board returns a 403 explaining exactly this.
Layout
| Path | Role |
|---|---|
src/precursor_kanban/plugin.py |
register(registry) — router, section, MCP server |
src/precursor_kanban/router.py |
/api/github/projects endpoints |
src/precursor_kanban/client.py |
ProjectsV2 GraphQL queries (extends core's GitHubClient) |
src/precursor_kanban/schemas.py |
Board read models |
src/precursor_kanban/mcp_server.py |
The kanban.board MCP tools |
web/src/ |
Frontend source — index.tsx calls registerSection |
web/src/styles.css |
The bundle's own Tailwind utilities (see below) |
web/types/precursor-host.d.ts |
Our declaration of the host SDK we compile against |
src/precursor_kanban/web/ |
Built frontend, shipped in the wheel (gitignored) |
scripts/dev-host.sh |
Provisions and runs a host to load this plugin into |
.github/github-app.yml |
GitHub Copilot app project settings (setup + dev server) |
See Precursor's plugin documentation for the full contract.
Development
make sync # uv sync + npm ci + build the frontend bundle
make check # every gate CI runs
make test # the test suite alone
make build # rebuild the frontend into the Python package
Running it
A plugin has nothing to run on its own. The section, the routes and the MCP
server only exist inside a host that discovered them through the
precursor.plugins entry point, so seeing the board means running a Precursor
with this working tree installed into it:
make dev-host # provision a host beside this checkout, plugin installed into it
make dev # run its dev stack — Vite HMR + uvicorn --reload
scripts/dev-host.sh clones the host's main into .precursor-host/
(gitignored, disposable), syncs its environment, and installs this repository
into it editable — so Python changes need only a restart and frontend
changes only make build. make dev runs with --port 0, meaning the OS picks
a free port and several checkouts can serve their own board at once; the URL is
in the startup banner. Override the host with PRECURSOR_HOST_REPO,
PRECURSOR_HOST_REF, PRECURSOR_HOST_DIR or PRECURSOR_HOST_EXTRAS.
The host from uv sync is deliberately not what this runs. That one is
resolved from git as a wheel built without its npm step, so it has the backend
and no SPA — enough for the test suite to boot an app, nothing to look at.
On a device that routes packages through a mirror, uv sync --frozen cannot
work here: it downloads the files.pythonhosted.org URLs recorded in uv.lock,
which such devices block, and --index cannot redirect a URL the lockfile
already pinned. The script falls back to uv export + uv pip install, which
keeps the locked versions and their hashes but lets the configured index serve
them; npm ci falls back to npm install --no-package-lock for the same reason.
Neither lockfile is ever written — CI owns both. If the mirror is behind on a
pinned version the plugin's own environment may still fail; the host is set up
first, so make dev works regardless and only make test has to wait.
In the GitHub Copilot app both steps are already wired up in
.github/github-app.yml: Setup env runs on session
create, and Dev Server is one click away. Accept the configuration when the
app offers it.
Four things are worth knowing before changing anything.
The frontend is a build product. src/precursor_kanban/web/ is generated and
gitignored. Without it the backend still advertises the section, the app has
nothing to import, and the section silently doesn't appear — so make sync and
make check both build it for you.
The bundle borrows the host's React. react, react-dom, both JSX runtimes
and @precursor/host are marked external; an import map injected by Precursor
resolves them to its own runtime. That is what keeps a single React on the page.
Bundling our own would give the app two dispatchers and every hook here would
throw — so CI asserts the built bundle still imports them rather than containing
them.
The bundle ships its own Tailwind utilities. Inside the Precursor monorepo
this was free: the host's Tailwind build scanned these sources and its stylesheet
already carried every class they used. Out of tree it cannot — the sources ship
compiled — so web/src/styles.css builds the utilities this plugin needs and
styles.ts injects them. They resolve against the host's theme variables, so the
board stays themed with the app and follows dark mode with it.
Lockfiles are generated by CI, never locally — a corporate mirror rewrites
artifact URLs and downgrades their hashes. Run make hooks once to have that
blocked at commit time.
More detail, including how the test suite pins the host and how to keep the SDK declaration honest, is in CONTRIBUTING.md.
Licence
MIT, same as Precursor.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file precursor_kanban-2026.9.0.tar.gz.
File metadata
- Download URL: precursor_kanban-2026.9.0.tar.gz
- Upload date:
- Size: 223.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
92c2949db74e91c2d64aeef2723f3e73669a8e385d822a5fb5f7e5ccaebc0380
|
|
| MD5 |
86319a57a688ba6c00144c245fe3fef8
|
|
| BLAKE2b-256 |
9a3c5f815f0e11b33dddcf9a56fda80fb89ee8e4741e53f666f3c842390c25b2
|
Provenance
The following attestation bundles were made for precursor_kanban-2026.9.0.tar.gz:
Publisher:
publish.yml on lrivallain/precursor-kanban
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
precursor_kanban-2026.9.0.tar.gz -
Subject digest:
92c2949db74e91c2d64aeef2723f3e73669a8e385d822a5fb5f7e5ccaebc0380 - Sigstore transparency entry: 2683778374
- Sigstore integration time:
-
Permalink:
lrivallain/precursor-kanban@5145d8c5e832753f4108f14a6a6a800298876d0d -
Branch / Tag:
refs/tags/v2026.9.0 - Owner: https://github.com/lrivallain
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5145d8c5e832753f4108f14a6a6a800298876d0d -
Trigger Event:
push
-
Statement type:
File details
Details for the file precursor_kanban-2026.9.0-py3-none-any.whl.
File metadata
- Download URL: precursor_kanban-2026.9.0-py3-none-any.whl
- Upload date:
- Size: 37.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8e750026937e215481f5f2c2caa1109602b88cd1058d5498a55156492d4ba101
|
|
| MD5 |
2f925148d6e2dab4688f2b5d667d7f9f
|
|
| BLAKE2b-256 |
5e4fac92479de0380b6e24b31018af88c099fcc4720e823ae17dcf8ae6582f1c
|
Provenance
The following attestation bundles were made for precursor_kanban-2026.9.0-py3-none-any.whl:
Publisher:
publish.yml on lrivallain/precursor-kanban
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
precursor_kanban-2026.9.0-py3-none-any.whl -
Subject digest:
8e750026937e215481f5f2c2caa1109602b88cd1058d5498a55156492d4ba101 - Sigstore transparency entry: 2683778446
- Sigstore integration time:
-
Permalink:
lrivallain/precursor-kanban@5145d8c5e832753f4108f14a6a6a800298876d0d -
Branch / Tag:
refs/tags/v2026.9.0 - Owner: https://github.com/lrivallain
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5145d8c5e832753f4108f14a6a6a800298876d0d -
Trigger Event:
push
-
Statement type: