Batch of in-progress app work from the working tree. - feat(keyboard): CursorNavigation + CursorDragPad for caret movement; KeyboardSoundFeedback for system key click sounds - feat(dictionary): DictionaryAliasGenerator + PersonalDictionaryEntrySheet; TranscriptPostProcessor quality gate; retire DictionaryLearner - feat(ui): TabBarVisibility handling; drop PageHeaderRow / PageHeaderConfirmButton; refresh views and localizable strings - fix(security): move the hardcoded DeepSeek key out of PreconfiguredKeys.swift into a gitignored PreconfiguredKeys.local.swift (seeded from .example by generate-xcodeproj.sh) - docs(agents): add Conventional Commits versioning + bilingual changelog rules - chore(gitignore): ignore PreconfiguredKeys.local.swift, .cache/, pycache Custom language model / lexicon work stays on feature/custom-language-model-asr. Changelog bullets added under [Unreleased]; no version bump.
6.4 KiB
AGENTS.md
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 (0.x stage)
While MARKETING_VERSION is 0.x.y, treat the project as pre-1.0:
| Field | File | Rule |
|---|---|---|
| Marketing version | project.yml → MARKETING_VERSION |
0.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 |
0.3.6 → 0.4.0 |
fix:, perf: (user-visible) |
PATCH | 0.3.6 → 0.3.7 |
feat!: or footer BREAKING CHANGE: |
MINOR (pre-1.0; reserve 1.0.0 for a deliberate GA) |
0.3.6 → 0.4.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:
## [0.4.0] - 2026-07-06
### Added
- **Cursor navigation**: drag pad on the keyboard for precise caret movement. / **光标导航**:键盘拖动手势区,精确移动光标。
### Fixed
- **API key handling**: move DeepSeek key into gitignored local file. / **API 密钥**:将 DeepSeek 密钥移至 gitignore 的本地文件。
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→ new0.x.yCURRENT_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 iOS-only project on a Linux VM
OSGKeyboard is a native iOS 18+ app (main app + custom keyboard extension + shared framework, all Swift 6 / SwiftUI). The Cursor Cloud VM is Linux x86_64. iOS development is fundamentally 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 —
OSGKeyboardTests/OSGKeyboardExtTestsrun viaxcodebuild testagainst the iOS Simulator (macOS only).
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 16+
(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.
CI note
.github/workflows/ci.yml runs on macos-14 and currently fails at the xcode-select -s /Applications/Xcode_16.0.app step because that Xcode version is absent from GitHub's current
macos-14 image — this is a CI runner-image issue, unrelated to the code or this Linux setup.