Contributing
Development setup, checks, code style, commit conventions, and the release process for phi.
Thanks for your interest in contributing! phi is an agent harness for coding work, written in Go with a terminal UI. This guide covers how to set up the project, run checks, and submit changes.
Development setup
Requirements:
- Go 1.26.3 or newer (see
go.mod) - A terminal that supports the features phi uses (the TUI is not a web UI)
Clone and build:
git clone git@github.com:pulseaiclub/phi.git
cd phi
make build # produces ./phi
make run # build and run
make install # build and install into $GOBIN
Sessions are persisted per project directory under
~/.phi/session/<encoded-cwd>/.
Running checks
Before submitting, make sure everything passes locally:
make test # go test ./...
make fmt # apply gofumpt / goimports / golines
make fmt-check # fail if formatting would change files (same as CI)
make lint # golangci-lint run ./...
make deadcode # unreachable functions vs baseline (deadcode -test)
make check # fmt-check + lint + deadcode (same as CI)
Install golangci-lint (required for fmt / fmt-check / lint / check):
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
If you add or change dependencies, run go mod tidy so go.mod/go.sum
stay clean.
Code style
- Format with
make fmt(gofumpt / goimports / golines via.golangci.yml). CI runsmake fmt-check. - Write tests alongside code (testify is used; see existing
*_test.gofiles). - Prefer small, focused packages. The layout under
internal/is deliberately granular — when adding a feature, put it where it fits and keep the public surface small. - Keep UI code decoupled: components render, the controller wires things up.
- English comments only. The repo was migrated from Chinese comments; please don’t introduce new non-English comments.
- Run the existing tests for the package you touch and keep them green.
Commit conventions
We use Conventional Commits. Prefix the summary with a type and, when relevant, a scope:
feat(scope): ...— new featurefix(scope): ...— bug fixrefactor(scope): ...— behavior-preserving changesdocs: ...— documentationtest: ...— testschore: ...— maintenance (deps, tooling)ci: ...— CI changestui: .../session: .../agent: ...— common scopes used in this repo
Examples from the history:
feat(session): persist sessions and add /resume, /sessions slash commands
fix(session): restore mutex on chain manager lost during panda migration
refactor(config): replace internal/config with project workspace
Keep the summary lowercase, imperative, and under ~72 characters. One logical change per commit.
Submitting changes
- Open an issue first for non-trivial changes, or link to an existing one in your pull request description.
-
Create a branch off
main(or the current default branch):git checkout -b feat/my-change - Make your change, add/update tests, and run
make fmt,make test, andmake lint. - For user-visible changes, add an entry under
## [Unreleased]inCHANGELOG.md(Added / Changed / Deprecated / Removed / Fixed / Security). You may omit the PR number until the PR exists, then update the entry before merge (e.g.(#123)). - Commit with a conventional message (see above).
- Push and open a pull request against the main branch. Describe what changed and why, and reference the issue number if there is one.
- Address review feedback with follow-up commits; the diff should stay focused on the change.
CI requires every PR to touch CHANGELOG.md unless you skip the check by:
- adding the
Skip Changeloglabel, or - adding the
dependencieslabel (Dependabot PRs get this automatically), or - putting
[chore]in the pull request title.
Do not edit text under <!-- Released section --> except in a release PR
(see below).
Release process
CHANGELOG.md is the source of truth for user-facing release notes.
- Open a release PR that moves entries from
## [Unreleased]into a new version section under<!-- Released section -->(for example## [0.12.0] - YYYY-MM-DD), leaves empty Unreleased headings for the next cycle, and updates the compare/tag links at the bottom. - Apply the
Unlock Released Changeloglabel so CI allows editing the released section. - After merge, push a tag matching
v*(for examplev0.12.0orv0.12.0-rc1). That triggers.github/workflows/release.yml, which runs tests and GoReleaser. Release notes are extracted from the matchingCHANGELOG.mdsection viascripts/changelog-extract.sh.
Code of conduct
Be respectful and constructive in issues, PRs, and reviews. This project is
MIT-licensed (see LICENSE); by contributing you agree to license your
contributions under the same terms.