Contributing To Cotabby
July 20, 2026 · View on GitHub
Thanks for helping improve Cotabby. This guide is the contributor entry point for local setup, validation, and codebase orientation.
Cotabby is a macOS menu bar app that provides on-device inline autocomplete in other apps. The repo is split by responsibility so contributors can make small, reviewable changes without spreading platform-specific behavior across unrelated layers.
Please read and follow the Code of Conduct before participating.
Before You Start
- Read README.md for the product overview and end-user setup.
- Read ARCHITECTURE.md before changing the suggestion pipeline, runtime lifecycle, or Accessibility behavior.
- Check for an existing issue or open one before starting substantial work.
- Prefer small, atomic PRs with a single clear objective. Large mixed-purpose changes are harder to review, validate, and revert safely.
- Before implementing a change, make sure you can clearly explain:
- The problem being solved
- Why the current behavior is insufficient
- Why the proposed approach fits the existing architecture
Development Prerequisites
You need:
- macOS 14.0 or later for running the app and tests. Apple Intelligence runtime work requires macOS 26 or later.
- Xcode with Command Line Tools installed.
- An Apple ID added to Xcode (Settings > Accounts) if you want to launch the app from the IDE. A
free account is enough; the paid Apple Developer Program is not required for local development.
Contributors outside Cotabby's default development team run
scripts/dev-setup.shonce to configure a local override (see Local Setup). - SwiftLint for local lint checks. CI installs it with Homebrew when needed.
- XcodeGen if you need to change the project structure (targets, build settings, dependencies,
or scheme). Install it with
brew install xcodegen. CI installs it the same way.
Apple Silicon is strongly recommended for local model-runtime work.
Local Setup
Clone the repo and open the project. If your Apple ID is not on Cotabby's development team, run the one-time signing setup first:
git clone https://github.com/FuJacob/Cotabby.git
cd Cotabby
scripts/dev-setup.sh
open Cotabby.xcodeproj
The committed Config/Signing.xcconfig defaults to Cotabby's development team, which lets team
members build immediately without Xcode modifying the generated project. scripts/dev-setup.sh
writes a gitignored Config/Signing.local.xcconfig with a contributor's own Apple Development team
id; that local value overrides the shared default and persists across pulls and project
regeneration. If you have not added an Apple ID to Xcode yet, do that first under Settings >
Accounts (a free account is enough), then re-run the script. To set the team by hand instead, copy
Config/Signing.local.xcconfig.example to Config/Signing.local.xcconfig, or pass it explicitly:
DEVELOPMENT_TEAM=XXXXXXXXXX scripts/dev-setup.sh.
You do not need a paid Apple Developer account to build or run Cotabby locally; a free personal team can sign and launch it. The paid program is only needed to distribute notarized builds.
For everyday local work, use the Cotabby Dev scheme rather than Cotabby. It builds a separate
app identity (com.jacobfu.tabby.dev, its own icon, auto-update disabled), so the permissions you
grant your dev build never collide with a released copy of Cotabby you have installed, and your
Accessibility grant survives rebuilds. See Run.
The Xcode Project Is Generated
Cotabby.xcodeproj is generated from project.yml by XcodeGen.
It is committed to the repo so you can clone and open Cotabby.xcodeproj without any extra tooling,
but project.yml is the source of truth.
Source files under Cotabby/ and CotabbyTests/ are auto-discovered by folder, so adding a new
file (including a new test) needs no project edit — just create it and regenerate. Only structural
changes (targets, build settings, package dependencies, scheme) require editing project.yml.
After any structural change, regenerate and commit the result:
xcodegen generate
CI runs the XcodeGen workflow on every PR and fails if the committed Cotabby.xcodeproj differs
from what project.yml produces. If that check is red, run xcodegen generate and commit the diff.
Avoid hand-editing the project in Xcode without mirroring the change into project.yml.
How To Navigate The Repo
Start with these boundaries:
Cotabby/App/: app lifecycle, composition root, and top-level coordinatorsCotabby/UI/: SwiftUI presentation and menu/settings surfacesCotabby/Services/: OS integrations, async work, permissions, and runtime boundariesCotabby/Models/: shared value types, state snapshots, and protocol contractsCotabby/Support/: pure rules, prompt helpers, normalization, and low-level utilities
Read SOURCE_LAYOUT.md for the complete responsibility map. Swift folders do not
create namespaces, so nested folders exist for human navigation: keep a small cohesive subsystem
flat, and introduce a child folder only when at least two files form a stable, predictably named
responsibility. Put a direct unit test under the matching CotabbyTests/ responsibility whenever
possible.
If you are changing behavior, prefer this order:
- Pure logic in
Support/ - Side-effectful boundaries in
Services/ - Orchestration in
App/ - Presentation in
UI/
That separation keeps behavior easier to test and reduces regressions in Accessibility-heavy code.
Build
For a local compile check:
xcodebuild \
-project Cotabby.xcodeproj \
-scheme Cotabby \
-configuration Debug \
-destination 'platform=macOS' \
CODE_SIGNING_ALLOWED=NO \
build
CODE_SIGNING_ALLOWED=NO keeps the build command usable on machines that do not have the project
owner's signing certificate. Use the shared team or your gitignored local override when you need to
launch the app locally.
Run
From Xcode:
- Select the Cotabby Dev scheme (see Local Setup for why).
- Choose your Mac as the run destination.
- Build and run. The dev build is named "Cotabby Dev" and has its own menu bar icon.
- Complete onboarding.
- Grant Accessibility and Input Monitoring to "Cotabby Dev" when prompted, and optionally Screen Recording for visual context. These map to the features in README.md.
- Pick Apple Intelligence if available, or use the Open Source engine with a downloaded GGUF model.
Because the dev build signs with your own stable team, macOS remembers these grants across rebuilds. If a permission reads as enabled but the app behaves as if it is not (common after switching signing identity, or when an earlier unsigned build left a stale entry), reset it and grant again:
tccutil reset Accessibility com.jacobfu.tabby.dev
tccutil reset ListenEvent com.jacobfu.tabby.dev
Then toggle the app back on in System Settings > Privacy & Security. Avoid ad-hoc "Sign to Run Locally" builds for real testing: macOS ties the Accessibility grant to the code signature, so an ad-hoc build changes identity on every rebuild and loses the grant each time.
If a suggestion does not appear or the overlay is misplaced, start with the focus and geometry sections in ARCHITECTURE.md before changing coordinator logic.
Test
Run the unit test suite:
xcodebuild test \
-project Cotabby.xcodeproj \
-scheme Cotabby \
-destination 'platform=macOS' \
CODE_SIGNING_ALLOWED=NO
The CI test workflow uses the same macOS deployment target as the app, so tests should not require a macOS 26 runner unless a future change raises the app baseline again.
Lint
Run SwiftLint locally:
swiftlint --reporter github-actions-logging
The current CI lint gate is warnings-only. Treat warnings as cleanup work, but avoid bundling unrelated style rewrites into functional PRs.
Debugging
The shared Xcode scheme passes -cotabby-debug by default in Debug builds. This enables
developer-only diagnostics:
- Focus debug overlay: translucent panels showing caret geometry, element bounds, focus polling events, and visual-context pipeline status.
- Suggestion debug logger: color-coded console output for each generation cycle: prompt sent, raw model response, and normalized output.
- Screenshot capture: saves OCR debug screenshots to disk when the visual-context pipeline runs.
To disable it, uncheck -cotabby-debug in the scheme's Run → Arguments tab.
Pull Requests
Before opening or updating a PR:
- Keep the change scoped to one problem.
- Explain what changed and why.
- Link the relevant issue with
Fixes #NorRefs #N. - Include screenshots or short recordings for visible UI changes.
- Run the relevant validation command for your change:
- build for compile-only or docs-adjacent changes
- tests for logic or pipeline behavior
- SwiftLint for style-sensitive edits
- Call out skipped validation explicitly.
- Keep unrelated refactors out of the PR.
- Update docs when setup, release flow, permissions, architecture, or user-facing behavior changes.
Use the repository PR template and replace every placeholder section with concrete content grounded in the actual diff and validation output.
CI Expectations
PRs into main run:
- Build:
xcodebuildcompile check - Tests:
xcodebuild test - Lint: SwiftLint warnings surfaced as GitHub annotations
If CI fails because of your change, fix the root cause in the same PR. If the failure is unrelated infrastructure noise, note that clearly in the PR description.