Contributing to Limpid
June 7, 2026 · View on GitHub
Thanks for your interest! Limpid is in pre-alpha, so things move fast and APIs are unstable.
By participating, you agree to follow the Code of Conduct.
Before you start
- For non-trivial changes, open an issue first to discuss the approach.
- Bug fixes and small improvements: PR directly is fine.
Setup
Prerequisites
- macOS 26 (Tahoe) or later
- Xcode 26 with the Metal Toolchain component
xcodebuild -downloadComponent MetalToolchain - Homebrew
- XcodeGen and Zig 0.15.2
brew install xcodegen zig@0.15zig@0.15is keg-only; the build script invokes it by its full path (/opt/homebrew/opt/zig@0.15/bin/zig), so noPATHchange is required.
Build
# 1. Clone with submodules (Ghostty lives at vendor/ghostty)
git clone --recursive https://github.com/nek0der/limpid.git
cd limpid
# If you already cloned without --recursive, pull the submodules
# now — `make ghostty` fails on an empty vendor/ghostty/ checkout:
# git submodule update --init --recursive
# 2. Confirm Zig is reachable (the build script hard-codes the
# Homebrew keg path, so missing zig@0.15 yields a clear error
# rather than silently picking up a system Zig):
# ls /opt/homebrew/opt/zig@0.15/bin/zig
# 3. Build libghostty as an xcframework (10–20 min on first run; cached after)
make ghostty
# 4. Generate the Xcode project
make xcodegen
# 5. Build + launch (Debug)
make dev
make help lists every available target (build, run, test, dmg, screenshot, clean, …).
You should see a Limpid window with a working terminal pane (zsh by default), a sidebar, and the tab list. The embedded libghostty version is logged via os_log under the dev.limpid subsystem (log stream --predicate 'subsystem == "dev.limpid"').
First-launch tip: a locally-built Debug binary is ad-hoc signed, so macOS may quarantine it. Right-click → Open the first time.
Open in Xcode
make xcodegen
open Limpid.xcodeproj
Limpid.xcodeproj is gitignored — always regenerate from project.yml.
Language policy
- Source code, comments, and doc strings: English only.
- User-facing documentation (
README.md,CONTRIBUTING.md,SECURITY.md,CODE_OF_CONDUCT.md): English. - Internal design documents under
docs/: Japanese is the convention. Mixed-language research notes (e.g.key-handling-research.md) are kept as-is; new docs follow this rule. - Chat, commit message bodies, and PR descriptions: either language is fine. The commit summary line stays English so
git logreads cleanly. - In-app strings are bilingual. Every user-visible label, tooltip, alert, and accessibility annotation has to land in
Limpid/Resources/Localizable.xcstringswith both anenand ajaentry. English-only strings are treated as incomplete at review time — there is no CI gate, only the PR checklist and the maintainer's eye.
Pull requests
- Branch off
mainusing a conventional prefix:feat/<short>,fix/<short>,docs/<short>,chore/<short>,refactor/<short>,test/<short>. - Keep commits focused. Use Conventional Commits (
feat:,fix:,docs:, etc.). - Code comments must be in English.
- Code style: see
AGENTS.md§3 for the conventions a linter can't enforce (comments explain why, file banners, naming). CHANGELOG.mdis auto-generated byrelease-pleasefrom commit messages — don't add it by hand.
Before opening the PR
Run the following from the repo root and make sure all pass:
make ghostty # Only if vendor/ghostty/macos/GhosttyKit.xcframework is
# missing (fresh clone). `make test` does not rebuild it
# and will fail at link time without the xcframework.
make xcodegen # Regenerate the Xcode project from project.yml.
make test # Build + run the full test suite.
make lint # swiftformat --lint + swiftlint --strict.
make help lists every available target.
If you are editing vendor/ghostty/ content, you're probably on the wrong path — that's a pinned upstream submodule. Bump it by updating the submodule ref, never by editing in place.
Opening the PR
Create the PR with gh pr create --body-file <file> so
.github/pull_request_template.md is honored verbatim. The
template has four substantive sections (Summary, Related issue,
How verified, Screenshots/video) plus a checklist; fill each one,
and write Skip. literally when a section genuinely doesn't
apply (e.g. an empty Related issue). gh pr create --fill and
the GitHub web UI both skip the template silently, which is the
single most common first-PR review nit.
Tests
- New tests use Swift Testing (
import Testing,@Test,#expect). Existing XCTest tests are kept and migrated opportunistically — don't mix the two styles in one file. - Reuse fixtures from
LimpidTests/Support/(RepoFixture,TempGitRepo,WithTempDir,FakeGit,WindowSessionFixture,Tags) instead of rolling your own. - Smoke tests that touch the local filesystem or shell out to
gitshould gate themselves onRepoFixture.hasLocalReposo they no-op cleanly outside the source tree. - Never write to
~/Library/Application Support/Limpid/from a test. Production stores (SessionStore,NotificationHistoryStore) accept aninit(directory:)override; pair it withWithTempDirso each test runs against an isolated temp directory. Real user data has been corrupted by tests that called the no-arginit()and thenclearAll().
Regenerating the hero screenshot
The .github/assets/hero.png shown at the top of README.md is captured from the app launched in demo mode. To refresh it after a UI change:
make screenshot # Release build → launch in demo mode → capture → quit.
# Output lands at .github/assets/hero.png.
The script sets LIMPID_DEMO=1 so the app loads DemoFixture instead of the user's real state.json — persistence is disabled while the env var is set.
One-time setup: grant your terminal Screen Recording permission so the script can both query Limpid's window bounds via CGWindowListCopyWindowInfo and capture the pixels via screencapture (one permission covers both).
System Settings → Privacy & Security → Screen Recording → click
+→ add your terminal app (Terminal, Ghostty, etc.) → toggle on.
If Tab, SplitTree, or SessionSnapshot shapes change, run DemoFixtureTests first; the fixture is anchored on stable UUIDs and a JSON round-trip.
AI tools
Using AI assistants (Claude, Copilot, Cursor, etc.) is welcome. Please be able to explain what your PR does without leaning on the tool.
See AGENTS.md for the conventions agents (and humans) should follow inside the repository.
Reporting security issues
See SECURITY.md.
Questions
Open a Discussion or an issue.