Use authenticated server invitation URLs with account-scoped caching and bounded state transitions, then advance the iOS build metadata to 86.
6.5 KiB
AGENTS.md
License boundary
OSGKeyboard is source available, not open source. LICENSE permits personal,
non-commercial local use and forbids unauthorized redistribution or public derivative
versions. Do not describe the project as MIT-licensed, open source, or freely forkable.
Versioning and releases
OSGKeyboard uses Conventional Commits as the single source of truth for version bumps
and CHANGELOG.md entries. Agents must follow this section whenever cutting a release or
writing commit messages that will ship to users.
Version format
The current source-of-truth version is 2.0.0 (build 86). Releases use stable SemVer:
| Field | File | Rule |
|---|---|---|
| Marketing version | project.yml → MARKETING_VERSION |
MAJOR.MINOR.PATCH (SemVer) |
| Build number | project.yml → CURRENT_PROJECT_VERSION |
Monotonic integer; +1 on every release cut, never decrease |
Bump rules (evaluate all commits since the last tagged/released version; take the highest bump):
| Commit prefix | Version bump | Example |
|---|---|---|
feat: |
MINOR + reset PATCH → 0 |
1.7.0 → 1.8.0 |
fix:, perf: (user-visible) |
PATCH | 1.7.0 → 1.7.1 |
feat!: or footer BREAKING CHANGE: |
MAJOR | 1.7.0 → 2.0.0 |
refactor:, style:, docs:, test:, chore:, ci: |
no bump by itself | group with user-facing commits or skip release |
Pragmatic overrides (experienced-maintainer judgment, still objective):
- A release that is only internal/tooling (
chore,ci, lexicon scripts with no app wiring) → do not cut a user-facing version; keep[Unreleased]in the changelog. - A release that mixes
feat+fix→ bump MINOR (thefeatwins). - Security fixes that change behavior (
fix(security):) → PATCH minimum; bump MINOR if users must change setup (e.g. new local key file). - Whitespace-only or comment-only diffs → no release entry.
Conventional Commit format
<type>(<optional scope>): <imperative summary>
[optional body]
[optional footer: BREAKING CHANGE: ...]
Allowed types: feat, fix, perf, refactor, style, docs, test, chore, ci.
Examples:
feat(keyboard): add cursor drag pad for precise caret movement
fix(home): use View-backed gradient on stats card
chore(lexicon): add offline SFCustomLanguageModelData export scripts
Changelog (CHANGELOG.md)
- Format: Keep a Changelog.
- Bilingual: every bullet is English first, then
/, then 简体中文. - Sections per release:
Added,Changed,Deprecated,Removed,Fixed,Security. - Workflow:
- During development, add bullets under
## [Unreleased](bilingual). - On release cut, rename
[Unreleased]→[X.Y.Z] - YYYY-MM-DD, insert a fresh empty[Unreleased]above it. - Derive section headings and bullets from Conventional Commits in the release range.
- During development, add bullets under
Bullet template:
### Added
- **Short title**: English sentence. / **简短标题**:中文句子。
Example entry:
## [1.8.0] - 2026-09-01
### Added
- **Cursor navigation**: drag pad on the keyboard for precise caret movement. / **光标导航**:键盘拖动手势区,精确移动光标。
### Fixed
- **API key handling**: keep user-owned provider keys in Keychain. / **API 密钥**:将用户自备的服务商密钥保存在 Keychain。
Release checklist (agent)
When the user asks to release or bump version:
git logfrom last release tag/commit → classify commits → pick bump level.- Update
CHANGELOG.md([Unreleased]→[X.Y.Z] - date, bilingual bullets). - Update
project.yml:MARKETING_VERSION→ new SemVer versionCURRENT_PROJECT_VERSION→ previous build + 1
- Commit:
chore(release): bump version to X.Y.Z (build N)— or include in the release PR. - Do not bump version for work that stays on a feature branch until it merges to
main.
Single source of truth
| What | Where |
|---|---|
| Version numbers | project.yml (MARKETING_VERSION, CURRENT_PROJECT_VERSION) |
| Human-readable history | CHANGELOG.md |
| Machine-readable history | git log with Conventional Commit prefixes |
Info.plist files reference $(MARKETING_VERSION) / $(CURRENT_PROJECT_VERSION) — do not hardcode versions in plists.
Cursor Cloud specific instructions
Platform reality: this is an Apple-platform project on a Linux VM
OSGKeyboard contains a native iOS/iPadOS 26+ app and keyboard extension plus a
native macOS 15+ menu-bar app (Swift 6 / SwiftUI). The iOS host links
OSGKeyboardShared and the host-only OSGKeyboardHostSupport; the Mac target reuses
shared/host-support sources and links MLX Audio for Qwen3 streaming ASR. The Cursor Cloud VM
is Linux x86_64. Apple-platform development is macOS-only, so the following cannot run
in this environment:
- Build — needs
xcodebuild+ the iOS SDK (Xcode, macOS only). - Run — needs the iOS Simulator or a physical iPhone (macOS only).
- Tests — iOS/extension tests require an iOS Simulator;
OSGKeyboardMacTestsrequires macOS.
Nearly every source file imports iOS-only frameworks (SwiftUI, UIKit, AVFoundation,
Speech, Combine), so there is no meaningful subset that compiles with Swift-for-Linux.
Do not attempt to build/run/test on the Linux VM — escalate to a macOS host with Xcode 26+
(see README.md / CONTRIBUTING.md for the xcodegen generate + xcodebuild flow).
What does work on Linux: SwiftLint
swiftlint (the swiftlint-static Linux binary, installed to /usr/local/bin by the update
script) runs here because its rules are SwiftSyntax/source-based. Run it from the repo root:
swiftlint lint --quiet # used by CI
swiftlint lint --quiet --strict # used by CI; promotes warnings to errors
Caveats:
- On Linux, SourceKit is unavailable, so SourceKit-only rules are skipped (e.g. you will see
Skipping enabled rule 'statement_position' because it requires SourceKit). Lint results can therefore differ slightly from a macOS run. Treat macOS/CI lint as the source of truth. - The repo currently has pre-existing SwiftLint violations on
main; a non-zero exit fromswiftlint lintreflects code, not a broken environment.
Project generation
The .xcodeproj is gitignored; project.yml (XcodeGen) is the source of truth. On macOS run
xcodegen generate before any xcodebuild. XcodeGen is macOS-oriented and is not installed on
the Linux VM.