git clone https://github.com/<owner>/plexus.git
cd plexus
bun installRunning bun install automatically installs git hooks via Lefthook (the prepare script runs lefthook install). No extra setup is needed.
Plexus uses Drizzle ORM with dual-dialect support (SQLite and PostgreSQL). Schema definitions live in:
packages/backend/drizzle/schema/sqlite/-- SQLite table definitionspackages/backend/drizzle/schema/postgres/-- PostgreSQL table definitions
- Edit the schema
.tsfiles in the appropriate dialect directories - If adding a new table, update the index exports in
packages/backend/drizzle/schema/index.ts - (Optional) Validate locally by running
bun run generate-migrationsfrompackages/backend/-- this confirms your schema produces valid migration SQL. The name is auto-derived from your branch; you can also specify--name <descriptive-name>explicitly. - Do NOT commit migration artifacts (
.sqlfiles,meta/_journal.json, snapshot files). Only commit the schema.tsfiles. - Open your PR. Migrations are auto-generated by CI after your PR merges to
main.
Drizzle migrations use sequential numbering. When multiple PRs include migrations, they collide on merge (e.g., two PRs both generate migration 0030). By keeping migration generation as a post-merge CI step, conflicts are impossible.
The pre-commit hook prevents accidentally committing migration files. If it fires:
# Remove migration files from your staged changes
git reset HEAD -- packages/backend/drizzle/migrations/ packages/backend/drizzle/migrations_pg/Then commit again with only your schema .ts files.
The hooks are skipped automatically when CI=true (GitHub Actions). To skip locally in exceptional circumstances:
LEFTHOOK=0 git commit -m "..."The project uses separate Drizzle ORM config files for each database dialect:
drizzle.config.sqlite.ts-- SQLite configurationdrizzle.config.postgres.ts-- PostgreSQL configuration
When running Drizzle Kit commands, specify the appropriate config file with --config.
Plexus uses Pi Assistant and PR Agent for automated coding assistance and code reviews:
- Pi Assistant (
.github/workflows/pi-assistant-issue.ymland.github/workflows/pi-assistant-pr.yml): Triggered by commenting/pion an issue or PR. It uses themcowger/pi-actionaction to run an AI-agent-driven coding session directly in the repository context to address requested changes. System prompts live in.github/prompts/pi-assistant-issue.mdand.github/prompts/pi-assistant-pr.md. - PR Agent Review (
.github/workflows/pr-agent-review.yml): Triggered automatically when non-draft pull requests are opened/reopened/ready for review, or when comments are made. It performs thorough automated code reviews using thethe-pr-agent/pr-agentaction, configured via.pr_agent.toml.
A pre-push Lefthook hook can run PR-Agent locally on the diff you are about
to push, in plain-diff mode — it pipes a unified diff to PR-Agent's stdin,
so it needs no GitHub credentials and no PR URL. The review is printed to
your terminal and is purely advisory: the hook always exits 0 and can never
block a push. If PR-Agent isn't installed, its config is missing, or the LLM
call fails, you just get a warning and the push proceeds.
One-time setup (idempotent — safe to re-run), using mise-managed Python:
mise install # installs the pinned Python 3.12 (and other tools)
mise run pr-agent:setup # creates .venv and pip-installs a pinned pr-agentEquivalent explicit commands, if you prefer to do it by hand:
mise use python
mise exec -- python -m venv .venv
mise exec -- .venv/bin/python -m pip install pr-agentConfiguration is read from the repo's .pr_agent.toml plus environment
variables — supply values in your shell profile or a local, git-ignored file.
Never commit keys or secrets.
| Variable | Purpose |
|---|---|
OPENAI__KEY |
API key for your LLM provider (not needed when CONFIG__MODEL points at a local model, e.g. Ollama) |
OPENAI__API_BASE |
Optional endpoint override (proxy, Azure, or a local server) |
CONFIG__MODEL |
Optional model override (defaults to the PR-Agent built-in default) |
Plain-diff mode works fully offline from GitHub, but still requires an LLM:
either a provider key (OPENAI__KEY) or a locally hosted model
(CONFIG__MODEL + OPENAI__API_BASE, e.g. an OpenAI-compatible Ollama server).
To run a review manually (no push needed), use the package script — it diffs
origin/HEAD...HEAD by default, or any base you pass:
bun run review # review origin/HEAD...HEAD
bun run review origin/main # review a custom baseOr invoke PR-Agent directly on any diff:
git diff origin/main...HEAD | .venv/bin/python -m pr_agent.cli --stdin reviewAll code must be formatted with Biome before committing:
bun run formatFrom the repo root:
bun run testOr from the backend package:
cd packages/backend
bun run testNote:
bun testis intentionally blocked both at repo root and inpackages/backend. Usebun run testinstead.
The pre-commit hook runs backend tests automatically.
bun run devThis starts the backend (with file watching) and the frontend builder in parallel.
The port is derived automatically from the worktree directory name, so two worktrees can run simultaneously without collision. The port and database are printed at startup:
Starting Plexus Dev Stack...
PORT: 14641
DATABASE_URL: sqlite:///tmp/plexus-browsertesting.db
ADMIN_KEY: password
Override any of these with environment variables:
PORT=4000 ADMIN_KEY=mysecret bun run devTo run against an in-process PGlite database instead of SQLite — useful when testing Postgres-specific behaviour without running a real server:
bun run dev:pgliteThe startup output shows the data directory instead of a database URL:
Starting Plexus Dev Stack...
PORT: 14641
DB Driver: PGlite
DB Data Dir: /tmp/plexus-browsertesting.pglite
ADMIN_KEY: password
The data directory persists across restarts (same worktree isolation as SQLite).
Override it with PLEXUS_PGLITE_DATA_DIR=/path/to/dir bun run dev:pglite.
After starting the dev server, seed it with a realistic baseline configuration (providers, model aliases, quota definitions, and API keys) that exercises the full feature set without requiring any real external credentials:
bun run populate-devThis is idempotent — safe to re-run at any time. It uses PUT throughout, so
existing resources are replaced rather than duplicated.
| Category | Count | Notes |
|---|---|---|
| Providers | 11 | Frontier cloud providers (Kilocode, Wisgate, Neuralwatt, OpenRouter, Google, OpenAI, Anthropic, CC, CC-Sigma, OpenLimits, Ozore) |
| Quotas | 2 | Daily, weekly budgets and limits |
| Model aliases | 19 | Frontier model architectures (gpt-5.x, claude-4.5/5, gemini-3.5, deepseek-v4) for chat, embeddings, speech, transcriptions |
| API keys | 4 | Default, OWUI, GithubKey, GHAKey |
All provider URLs are mapped to their real production API gateways, but with safe, redacted mock keys by default. No real secrets are checked into the codebase.
Create scripts/user-populate.json (git-ignored — see scripts/user-populate.example.json
for the format). Anything in that file is merged over the defaults when you run
bun run populate-dev, so you can add real provider keys or personal aliases
without touching committed files and without risk of accidentally leaking secrets.
| Variable | Default | Purpose |
|---|---|---|
PLEXUS_URL |
http://localhost |
Base URL of the target instance |
PLEXUS_PORT |
derived from cwd | Port (matches bun run dev automatically) |
PLEXUS_ADMIN_KEY |
password |
Admin key |
To wipe the database and restart the backend in one step:
bun run clear-devThis deletes the SQLite file from /tmp and sends SIGHUP to the running dev
server, which gracefully shuts down the backend and immediately relaunches it
against the empty database. The frontend is unaffected. The whole cycle takes
about two seconds.
After clearing, re-run bun run populate-dev to restore the baseline config.
Note:
clear-devrelies on a PID file written bybun run devto/tmp/plexus-<worktree>.pid. If the server is not running, it will delete the database file and exit cleanly without error.