Per-topic AI chat interface for work follow-up, where each topic maps to a GitHub issue.
Project description
Precursor
Opinionated approach to work follow-up, built as an AI assistant.
Precursor is a small AI assistant, built with an opinionated approach to tracking work-in-progress conversations alongside the issues they belong to. Every chat is scoped to a topic that can be linked to (or create) a GitHub issue; the assistant uses the issue body, comments, and labels as live context so newer updates outweigh older ones.
Highlights
- Collapsible, searchable, tree-organized topic sidebar
- Each topic optionally linked to a GitHub issue; issue labels tag the chat
- Multi-turn chat with SSE streaming and markdown rendering
- Powered by GitHub Models (OpenAI-compatible) with a mock provider for offline development
- MCP both ways: Precursor exposes its conversations as an MCP server and attaches external MCP tool servers per topic
- Agents mode (opt-in): hand long-running tasks to an autonomous Copilot
SDK agent attached to a topic/chat, followed in a workflow-style tab. Off by
default — needs the
agentsextra and a toggle in Settings → Agents (see Optional: Agents mode) - Single uvicorn process in production — FastAPI serves the API and mounts the built React SPA
- Plugin-ready: backend entry points + a frontend extension registry, designed for things like a future drawio preview/generator
Stack
| Layer | Tech |
|---|---|
| Tooling | uv for env, run, build & release |
| Backend | Python 3.12+, FastAPI, SQLAlchemy 2 (async), Alembic, sse-starlette |
| LLM | openai SDK pointed at https://models.github.ai/inference |
| MCP | mcp Python SDK (client + server scaffolding) |
| Frontend | Vite + React 19 + TypeScript, Tailwind CSS 3, Lucide React |
| DB | SQLite for dev (aiosqlite), PostgreSQL for prod (asyncpg, extra) |
Quick start
Precursor uses uv for everything Python — environment, running, building, and releasing. Install it once (instructions), then:
uv sync # backend: .venv + Python deps (uv manages the interpreter)
npm --prefix frontend install # frontend: Vite + React toolchain (needs Node.js)
cp .env.example .env
[!NOTE] The dev server (
precursor --dev) and the SPA build (make build) need Node.js + npm; the production runtime does not.make syncruns both install steps (uv sync+npm install) in one go.
GitHub credentials (optional). Precursor resolves a GitHub token in this
order: (1) a token saved in Settings → GitHub, then (2) your GitHub
CLI session (gh auth token) if you're signed in via gh auth login. So if
you already use gh, you don't need to set anything. A token needs the
models:read fine-grained permission (or Copilot access) for real model
responses. With no token at all, Precursor falls back to the MockProvider
so the chat flow stays usable offline.
Optional: Agents mode
Agents mode is opt-in and off by default. It is not installed by the
uv sync above — it lives behind its own agents extra:
uv sync --extra agents # adds github-copilot-sdk on top of the dev deps
uv run --extra agents precursor --dev # …or run the dev stack with it in one step (= make dev)
[!IMPORTANT] The
github-copilot-sdkwheel bundles the native Copilot CLI runtime binary (~90 MB download, ~145 MB on disk), which is why it is kept out of the default install. Installing the extra is the only step that pulls that payload — there is no separate, smaller "download the runtime later" path for the published1.0.xwheels.
Installing the extra only makes the runtime available. Agents stay disabled until you turn them on in Settings → Agents, where the UI also reports whether the runtime resolved on your platform.
Run it (one command)
uv run precursor --dev # uvicorn --reload + Vite HMR (Ctrl-C stops both)
# or: make dev
On startup Precursor prints a banner with the URL to open. --port is always
the URL you open in your browser — in --dev the UI runs there and Vite proxies
/api to the backend (which sits on a hidden port, by default --port + 1):
uv run precursor --dev --port 9000 # open :9000 (UI); API on :9001 behind it
uv run precursor --port 8100 --open # prod-style, opens the browser when ready
Running several instances at once? Just pick a different --port per
instance — or don't: a busy port automatically bumps to the next free one (pass
--strict-port to fail instead, or --port 0 to grab any free port). The
banner always tells you where the UI landed.
uv run resolves the project's environment on the fly — no manual activation.
Other entry points:
uv run precursor # single process: API + pre-built SPA on one port
uv run precursor --dev --no-frontend # backend only (uvicorn --reload)
npm --prefix frontend run dev # Vite only
Frontend prod build (served by FastAPI)
make build # npm --prefix frontend run build → frontend/dist
uv run precursor # serves API + SPA on :8000
The single-process run needs the SPA pre-built; uv run precursor then serves
it from frontend/dist. The SPA is also bundled inside the wheel, so an
installed build is self-contained:
uvx precursor-ai # run the latest published wheel, zero setup
# or pin it: uv tool install precursor-ai && precursor-ai
Automatic upgrades on startup
When you pull new code or upgrade Precursor, both the frontend and database are automatically upgraded when the app starts — no manual build or migration steps needed:
- Frontend: Built automatically if
frontend/distis missing or stale - Database: Migrations applied during app startup via Alembic
Just start Precursor and it handles the rest:
git pull
uv run precursor # frontend built + DB migrated automatically
Project layout
precursor/
├── precursor/backend/
│ ├── main.py # FastAPI app + SPA mount + lifespan
│ ├── config.py # pydantic-settings
│ ├── db.py # async SQLAlchemy engine + session
│ ├── models/ # Topic, Message, AppSetting
│ ├── schemas/ # Pydantic request/response models
│ ├── routers/ # topics, chat (SSE), settings, github, mcp
│ ├── services/
│ │ ├── llm/ # provider protocol + GH Models + mock
│ │ ├── github_client.py
│ │ └── mcp/ # server + client manager
│ ├── plugins/ # entry-point loader + registry
│ └── alembic/ # migrations
├── frontend/
│ ├── src/components/ # Sidebar, ChatPanel, SettingsPanel, MessageBubble
│ ├── src/lib/ # api, sse, plugins, theme, types
│ └── vite.config.ts # built to frontend/dist → bundled into the wheel
├── pyproject.toml # uv project: deps, build (hatch-vcs CalVer), tooling
├── uv.lock # uv-managed lockfile (committed)
├── Makefile # uv-based dev/build shortcuts
└── alembic.ini
Design principles
- Streaming-first chat with tool-call visualization
- Single process in production — no Node.js runtime required
- Each topic is an independent conversation context, hydrated from its linked GitHub issue (newer comments preferred over older)
- Extensible by design: see docs/plugins.md for the plugin contract — third parties can mount routers, contribute frontend panels, and register MCP tools without forking the core
Security & deployment model
[!IMPORTANT] Precursor is designed as a single-user, local-first app and ships with no authentication. Run it bound to
127.0.0.1(the default) and do not expose it to a network or the public internet without putting your own authenticating reverse proxy in front of it.
Specific things to keep local:
- The API and SPA have no auth — anyone who can reach the port has full access to your topics, settings, and stored tokens.
- The optional command-runner MCP tool can execute shell/Python/Node. Keep the Docker "jail" enabled; disabling it grants full local-disk access.
- The built-in MCP-over-HTTP transport is off by default and only binds to loopback — leave it that way unless you front it with auth.
- Secrets (the GitHub token, LLM provider keys) live in the local DB (set via
Settings) and are never echoed by the API. Don't commit
.env.
See SECURITY.md for vulnerability reporting.
Versioning & releases
Precursor uses CalVer (YYYY.M.MICRO, e.g. 2026.6.0). The version is a
single source of truth derived from git tags by hatch-vcs at build time — there
is no literal to edit. The running version is exposed at GET /api/version and
shown in the Settings panel.
Releases ship from a pushed v<version> tag via GitHub Actions. See
RELEASING.md and CHANGELOG.md.
Documentation
📖 Website & full documentation —
feature guides, installation, configuration, architecture, and contribution
docs, published from website/ on every push.
In-repo references:
License
Project details
Release history Release notifications | RSS feed
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_ai-2026.7.0.tar.gz.
File metadata
- Download URL: precursor_ai-2026.7.0.tar.gz
- Upload date:
- Size: 5.5 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
857ad6e82d613a3638e281520a41b03a7f1b0f5f300f4469ef5a624b3de0fbf9
|
|
| MD5 |
5e1e6b62b7c526ae88e433b2ead58383
|
|
| BLAKE2b-256 |
8c6c0ba7f0026979f2f527a39c7f2c0bddca4b5b958b19aea64d13ac744c2748
|
Provenance
The following attestation bundles were made for precursor_ai-2026.7.0.tar.gz:
Publisher:
release.yml on lrivallain/precursor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
precursor_ai-2026.7.0.tar.gz -
Subject digest:
857ad6e82d613a3638e281520a41b03a7f1b0f5f300f4469ef5a624b3de0fbf9 - Sigstore transparency entry: 2200749096
- Sigstore integration time:
-
Permalink:
lrivallain/precursor@e60f5ae01ba9327e918ab34a9608bd879309265c -
Branch / Tag:
refs/tags/v2026.7.0 - Owner: https://github.com/lrivallain
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e60f5ae01ba9327e918ab34a9608bd879309265c -
Trigger Event:
push
-
Statement type:
File details
Details for the file precursor_ai-2026.7.0-py3-none-any.whl.
File metadata
- Download URL: precursor_ai-2026.7.0-py3-none-any.whl
- Upload date:
- Size: 1.7 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2815210a4c642fd1afc5eee0083c8d238ccc43b95b857ec8a63534ed91549c8a
|
|
| MD5 |
6f3f189fda0906238dfc86b791fae6c2
|
|
| BLAKE2b-256 |
d4276174bd44d24d5e0d984f55eef918804c82e0eb5be0a3297faedd514ba48c
|
Provenance
The following attestation bundles were made for precursor_ai-2026.7.0-py3-none-any.whl:
Publisher:
release.yml on lrivallain/precursor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
precursor_ai-2026.7.0-py3-none-any.whl -
Subject digest:
2815210a4c642fd1afc5eee0083c8d238ccc43b95b857ec8a63534ed91549c8a - Sigstore transparency entry: 2200749159
- Sigstore integration time:
-
Permalink:
lrivallain/precursor@e60f5ae01ba9327e918ab34a9608bd879309265c -
Branch / Tag:
refs/tags/v2026.7.0 - Owner: https://github.com/lrivallain
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e60f5ae01ba9327e918ab34a9608bd879309265c -
Trigger Event:
push
-
Statement type: