Contributing to senpi
July 6, 2026 · View on GitHub
senpi is an opinionated fork of badlogic/pi-mono. This guide covers the fork-specific contribution rules. For the upstream contribution culture, see pi-mono's CONTRIBUTING.md.
Philosophy
First things first: pi's core is minimal.
If your feature does not belong in the core, it should be an extension. PRs that bloat the core will likely be rejected.
Pi's core exists to be minimal and to be extensible so that it can be influenced and manipulated by extensions. Even hook points for extensions however should be well considered and discussed to avoid adding unmaintainable bloat and complex interactions.
The One Rule
You must understand your code. If you cannot explain what your changes do and how they interact with the rest of the system, your PR will be closed.
Using AI to write code is fine. Submitting AI-generated slop without understanding it is not.
If you use an agent, run it from the senpi repo root directory so it picks up AGENTS.md automatically. Your agent must follow the rules and guidelines in that file and in the nearest subdirectory AGENTS.md.
Fork Strategy (READ BEFORE EDITING src/)
senpi periodically rebases on upstream/main (i.e. badlogic/pi-mono). To keep rebases clean:
- Extension-first — every new feature lands as a builtin extension under
packages/coding-agent/src/core/extensions/builtin/, or as a user extension underpackages/coding-agent/examples/extensions/. Touchcore/only when no extension hook can do the job. changes.mdcontract — any modification to an upstream-tracked file MUST add a section to the nearestchanges.mddocumenting what changed, why, why an extension couldn't handle it, and expected merge-conflict zones. See the existing files for templates.- Skim the relevant
AGENTS.mdin the directory you are about to modify. It tells you which patterns are load-bearing.
Before Submitting a PR
node scripts/devenv-setup.mjs # default: npm prebuilds, no Rust toolchain required
node scripts/devenv-setup.mjs --with-native # optional: install pinned Rust toolchain and build native Rust workspace
npm run check # Biome + tsgo + browser-smoke + web-ui check (pre-commit equivalent)
npm test # Vitest across workspaces (skips live-API)
./pi-test.sh # Optional: live-API integration suite (env-gated; requires API keys)
The default dev setup intentionally avoids global Rust installation and uses checked-in/prebuilt native artifacts where available.
Use --with-native only when working on native Rust code; it reads rust-toolchain.toml, uses rustup to install the pinned
toolchain when rustup is available, and then runs the Rust native build/check path when a Cargo workspace exists. If Rust is
missing, the setup prints the exact rustup command to run instead of bootstrapping global tooling silently.
npm run check and npm test must pass. ./pi-test.sh is only required when your change touches a provider that the live tests exercise.
If you touch MCP dependencies, keep @modelcontextprotocol/sdk exact-pinned and verify the workspace install with
npm ls @modelcontextprotocol/sdk --workspace @code-yeongyu/senpi.
Do not edit CHANGELOG.md. Changelog entries are added by maintainers.
If you are adding a new provider to packages/ai, see packages/ai/src/providers/AGENTS.md for the 7-step checklist and required cross-provider tests.
Releasing
senpi uses CalVer (Calendar Versioning), distinct from upstream badlogic/pi-mono's semver line.
Cutting a release
Locally:
npm run release # uses today's UTC date
npm run release -- --dry-run # preview without committing
The release script (scripts/release.mjs) imports scripts/calver.mjs to compute the next version, then:
- Bumps all 5 workspace
package.jsonfiles in lockstep. - Updates each package's
CHANGELOG.md:## [Unreleased]→## [<version>] - YYYY-MM-DD. - Commits
release: v<version>, tagsv<version>, publishes to npm with--tag latest. - Pushes
mainand the tag toorigin. - Re-inserts a fresh
## [Unreleased]section to each changelog.
CalVer rules
- First release of the day:
YYYY.M.D(e.g.2026.5.13). - Same-day re-release:
YYYY.M.D-Nwhere N ≥ 2 (e.g.2026.5.13-2). - All 5 workspaces always share the version.
- Tags are
v<version>—build-binaries.ymltriggers on these.
Upstream sync
You do NOT manually merge upstream. .github/workflows/upstream-agent-merge.yml polls badlogic/pi-mono hourly and, when a new upstream release lands, runs Codex headless inside the runner to merge it on an automation branch.
The agent uses the committed merge-upstream skill and /cl changelog-audit command (under .github/agent/) plus the fork conflict-resolution rules in .github/agent/merge-driver.md. On a clean, building, changelog-audited merge, the workflow opens a PR, waits for checks and QA evidence, merges it with a merge commit, runs a fresh /cl audit on main, and then lets the release run only when package changelog entries make it release-worthy (scripts/release.mjs tags vX.Y.Z and pushes; build-binaries.yml publishes from the tag).
- Clean merge → PR branch is merged into
mainwith a merge commit, changelog audited, QA gates pass, and a new release is cut automatically when release-worthy. - Conflicts / QA failure → the agent aborts, writes
.github/agent/last-merge-report.md, and the workflow opens an issue labeledsync-conflict. Resolve manually following the per-file rules in.github/agent/merge-driver.md; thechanges.mdfiles in fork-modified subdirectories tell you what the fork preserves and why.
Requires UPSTREAM_AUTOMATION_TOKEN, CODEX_CONFIG_TOML_B64, and either CODEX_AUTH_JSON_B64 or QUOTIO_API_KEY. Optional QA/config secrets include CODEX_QUOTIO_CONFIG_TOML_B64, CODEX_CCAPI_CONFIG_TOML_B64, SENPI_AUTH_JSON_B64, SENPI_MODELS_JSON_B64 or SENPI_MODELS_JSON_GZ_B64, and SENPI_SETTINGS_JSON_B64. See .github/agent/README.md.
To trigger manually:
gh workflow run upstream-agent-merge.yml -f force=true
CHANGELOG entries
Do not edit
CHANGELOG.md. Changelog entries are added by maintainers.
This still applies. The release script will surface [Unreleased] entries; maintainers curate them before cutting a release.
Quality Bar for Issues
If you open an issue:
- Keep it concise. If it does not fit on one screen, it is too long.
- Write in your own voice. If you use an LLM to draft text, follow up with a clearly AI-labeled comment.
- State the bug or request clearly.
- Explain why it matters.
- Include a repro (minimal reproduction or exact command + observed vs expected).
- If you want to implement the change yourself, say so.
Philosophy
senpi's core stays minimal. Most "this could be a feature" requests should be implemented as extensions (packages/coding-agent/src/core/extensions/builtin/ for in-tree, packages/coding-agent/examples/extensions/ for external).
If your change must modify upstream-tracked source:
- Confirm no extension hook covers it (read docs/extensions.md).
- Add a
changes.mdsection before sending the PR.
PRs that bloat the core or skip changes.md will be asked to convert to an extension or document the modification.
Style
- TAB indent (Biome
indentWidth: 3). 120-column line width. - Match the style of the surrounding file. The codebase is largely consistent — Biome plus the existing patterns are the source of truth.
- Use the path aliases defined in root
tsconfig.json(@earendil-works/pi-*,@code-yeongyu/senpi*).
Why are weekend issues lower priority?
We triage the tracker during working hours. That means more issues can accumulate over the weekend. Anything submitted Friday through Sunday may be missed or given lower priority in the Monday review queue. If a problem is urgent, ask on Discord and include the short version, a repro, and the relevant logs.
Trademark and Brand References
Use third-party marks only to identify integrations, compatibility, providers, and required setup. Do not make senpi look endorsed by another project or vendor.
- Anthropic and Claude: use referentially for Anthropic APIs, Claude models, and Anthropic-specific extensions. Follow Anthropic's legal terms: https://www.anthropic.com/legal.
- OpenAI, ChatGPT, GPT, and GPT-4/GPT-5 model names: use referentially for OpenAI APIs and model compatibility. Follow OpenAI brand guidance: https://openai.com/brand/.
- GitHub and GitHub Actions: use referentially for repository and workflow behavior. Follow GitHub logo and trademark guidance: https://github.com/logos.
- Discord: use referentially for the upstream community link. Follow Discord branding guidance: https://discord.com/branding.
Prefer product-neutral wording in user-facing copy unless a specific provider or integration is technically required.
Communication
- Issue and PR discussion happens on GitHub: https://github.com/code-yeongyu/senpi.
- Upstream pi-mono discussions: https://discord.com/invite/3cU7Bz4UPx.
Is this gatekeeping?
No. It is a guardrail against burnout and tracker spam. Short, concrete, reproducible issues are welcome. Thoughtful contributions are welcome. Automated slop, entitlement, and large volumes of low-effort reports are not.
Where can I learn about plans?
Earendil uses RFCs to discuss larger changes. Not all of them are public, but quite a few are. They can be found at rfc.earendil.com.