Wave 1 (correctness/trust):
- Fix paid large-model dead path: engine reads the persisted selected repo via a
thread-safe ModelManager.activeRepo(), so download/load/isReady now agree.
- Lemon Squeezy store/product check fails closed in release builds — the
placeholder-ID 'any key unlocks paid' bypass can no longer ship.
- Replace force-unwrapped Hugging Face URLs with safe URLComponents construction.
- Never read/replace/scan secure (password) fields via AX.isSecureTextField.
Wave 2 (compounding seam):
- Extract AIPromptKit (prompts/temperature/maxTokens/cleanup) out of MLXEngine so
every backend produces identical prompts and output.
- AIEngine gains protectedTerms (personal dictionary now actually injected into the
LLM), warmup(), and runStreaming(); add makeAIEngine() factory.
- Honor WritingMode.lengthCap for maxTokens; warm the model on launch/after download.
- Guard custom modes against empty prompts and duplicate names.
Wave 3: OllamaEngine — a URLSession-only AIEngine backend (cross-platform, opt-in
via GRAMMARGEM_AI_BACKEND=ollama).
Wave 4: LocalEngineServer — loopback-only, token-authed HTTP bridge (Network.framework,
no new deps), off by default, exposing Harper + the LLM + the entitlement gate to a
browser extension.
Tests: 6 new unit tests for the AIPrompts seam and custom-mode guard (19 pass).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Added LiveMonitor class to scan text areas in the frontmost window for grammar and spelling issues.
- Introduced DetectedField struct to represent text areas with issues, including suggestions for corrections.
- Implemented functionality to apply corrections and dismiss suggestions.
- Added Permissions class to manage Accessibility permissions required for monitoring.
- Created SnippetStore for managing text-expansion snippets, including persistence to UserDefaults.
- Developed TextCapture class to read and write text from the frontmost application using Accessibility API and clipboard fallback.
- Implemented TextFieldScanner to traverse the Accessibility tree and collect editable text elements.
- Added TextReplacementCoordinator to orchestrate the text correction process.
- Integrated UpdaterManager for handling application updates using Sparkle.
- Created comprehensive unit tests for various components, including grammar corrections and entitlement checks.
Installed apps now show an 'Update available' button (menu bar) and can
one-click install signed updates. Adds the Sparkle dependency, an
UpdaterManager wired into AppState, the menu-bar banner + 'Check for
Updates', and Info.plist SUFeedURL (https://grammagem.app/appcast.xml),
SUPublicEDKey, and scheduled background checks.
Build / release:
- build.sh embeds + rpaths Sparkle.framework; sign-app.sh signs it
inside-out (XPC services, Updater.app, Autoupdate, framework, then app)
for a valid, notarizable signature.
- release.sh stamps the version, builds, signs, notarizes, staples, zips,
and emits the EdDSA-signed appcast.xml to upload to grammagem.app.
- A hidden --update-selftest checks a feed headlessly (CI/build pipeline).
Verified: a v0.1.0 build detects a v0.2.0 update against a local signed
appcast (UPDATE_FOUND: 0.2.0); the signed app passes codesign --strict.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the MLXEngine stub with real Qwen2.5-3B-4bit inference via
mlx-swift-examples (2.25.9): loads the local model snapshot on disk (no
network), chat-templated generation per AIAction, and tone classification.
Wired behind the existing AIEngine seam; AppState resolves the model dir
through ModelManager.
Build notes:
- arm64-only now (MLX is Metal / Apple-Silicon only; matches the spec).
- swift build (CLI) cannot compile mlx-swift's .metal kernels, so the
metallib is generated once via scripts/build-metallib.sh (xcodebuild) and
vendored at AppSupport/mlx.metallib; scripts/build.sh ships it as
Contents/Resources/mlx-swift_Cmlx.bundle/Contents/Resources/default.metallib
(the path mlx resolves, and it codesigns cleanly).
- A hidden --mlx-selftest entrypoint runs one headless generation to verify
the runtime end-to-end.
Verified: the signed app's self-test produces a real rewrite; full test
suite green (the MLX integration test is gated behind GRAMMAGEM_MLX_TEST).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements the highest-leverage items from docs/grammarly-value-plan.md:
- Live Check: on-device 0-100 writing score, Correctness/Clarity/Polish
count chips, and per-suggestion Dismiss / Add-to-dictionary (a cheap,
always-available 'no' is what makes 'Apply' feel safe).
- Value loop: day-streak, correction milestones and a 'subscription you
didn't pay' figure on the dashboard + a streak line in the menu bar
(all local, self-vs-self, no telemetry).
- First-run demo: a planted sample the real engine cleans in one click,
before any permission ask.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the NSSpellChecker + regex-stub engine with harper-core 2.5.0:
real subject-verb agreement, verb forms, punctuation, repetition and
spelling. New harper-ffi crate (staticlib) exposes harper_check over a C
ABI; the CHarper module bridges it to Swift; HarperEngine maps Harper's
char spans onto UTF-16 ranges and de-dups overlapping lints. NSSpellChecker
stays only as a case-preserving spelling supplement, which fixes the
problemm->Problem mis-casing. Bulk apply now skips no-op suggestions.
build.sh / scripts/build.sh build the universal static lib; target/ and
lib/ are gitignored (regenerate via harper-ffi/build.sh).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AXUIElementCreateSystemWide() returns kAXErrorCannotComplete (-25204) for
a background/menu-bar app on recent macOS, so the focused-element read
returned nil and GrammaGem recognised no text or text areas in any app.
Add AX.focusedElement(), which falls back to the frontmost app's PID-based
AX element (reliable from an accessory process), and route TextCapture's
selected-text and focused-field reads through it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stability (the "super buggy" feel):
- Move the Accessibility-tree traversal OFF the main thread with a per-message
timeout, so a busy/wedged target app can't beachball or hang GrammaGem.
- Replace every unchecked `as! AXUIElement` force-cast with a CFTypeID-checked
safe cast (AXSupport), eliminating crashes from misbehaving AX servers.
- Never scan GrammaGem's own windows (self-PID exclusion).
- Generation tokens + in-flight guard so superseded scans/downloads can't clobber state.
Text safety:
- Live fixes now read the element's CURRENT value before writing (and only apply a
span edit when the field still matches the snapshot), so edits made since the last
scan are never overwritten. Cap per-field check length.
- Stable per-field identity (AX id / position) so the Live Check list no longer churns.
- Include focus + identity in change-detection so tabbing updates the focused field.
Other audited fixes:
- Page blocker: conservative skip when a browser's URL can't be read.
- Model download: validate on-disk size (reject HTML/truncated 200s), clean up
partials on cancel/failure, handle URLError.cancelled.
- Licensing: clear a server-confirmed-invalid license instead of honoring grace;
show the real device limit instead of "0 Macs".
- Hotkey re-register falls back to the default if a chosen combo is taken.
- Daily free-tier counter tracks the live timezone.
- "Fix focused field" ignores non-text controls.
Feature: auto-install the on-device model in the background on first launch.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- SystemGrammarEngine: on-device spelling + grammar via NSSpellChecker (no deps,
no network), augmented with deterministic Harper-style rules. Now the default.
- TextFieldScanner: walks the frontmost window's Accessibility tree to find every
editable text area (bounded for huge web/Electron trees), not just the focused one.
- LiveMonitor now publishes per-field issues across the whole window + fix methods
(apply one suggestion, fix a field, fix everything) via AX value writes.
- LiveCheckView: a panel listing each text area with errors highlighted inline and
one-click fixes; opened from the menu bar (Show live issues / Show issues / Fix all).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- ModelManager now performs a real Hugging Face download (file list + sizes via
the tree API, sequential download with progress, completion marker, cancel).
- Onboarding observes Permissions/ModelManager directly and auto-polls, so the
step + button flip to "granted/Done" the moment Accessibility is enabled —
even when toggled directly in System Settings. Polling is idempotent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- LiveMonitor: menu-bar–resident; reads the focused field via the Accessibility
API, runs the offline grammar engine, and shows live suggestions + a "Fix all".
- Menu-bar icon now reflects status (issue count / paused); added Pause/Resume.
- Page Blocker: exclude apps + websites (Safari domain detection) where GrammaGem
stays out of the way; hotkeys + monitoring suppressed there.
- build.sh now produces a universal (arm64 + x86_64) bundle; sign-notarize.sh is a
turnkey codesign→notarize→staple flow reading creds from env/keychain (no secrets).
- Clean entitlements plist (AMFI-compatible).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Shortcuts screen now records custom global hotkeys (Fix + Ask), persisted and
re-registered live via Preferences + Combine.
- Add branded AppIcon.icns (gold nib on ink), wired into Info.plist + build.sh.
- "Restore my clipboard after a fix" preference now drives the clipboard fallback.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Management app: sidebar window with Dashboard (usage insights), Devices
(slot usage, deactivate this Mac), Writing Modes (custom + app-aware map),
Dictionary, Snippets, Shortcuts, AI Model, Privacy, General, License, About.
- New local stores: Preferences (launch-at-login, appearance), UsageStats
(privacy-friendly local insights), CustomModesStore, SnippetStore.
- License model now tracks device name + activation usage/limit for the
Devices screen.
- README rewritten as a conversion-focused product page (no internal cost/setup).
- Legal entity set to FounderGem LLC across README, LICENSE, Info.plist, About.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Native SwiftUI menu-bar app: global hotkey → capture selected text (AX API /
clipboard fallback) → grammar (Harper) / local AI (MLX) → replace in place.
Free/paid entitlement gate + Lemon Squeezy device-cap licensing, $0 marginal
cost per user. Heavyweight engines behind protocol stubs so it builds out of the box.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>