feat: opt-in transcript auto-delete with retention window#77
Closed
arfrank wants to merge 197 commits into
Closed
Conversation
Fix overlay constraint crash
Models section shows each model with loaded/not loaded status. Taller settings window (580px) to fit all sections. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shows when any model isn't loaded. Downloads WhisperKit and/or cleanup models directly from Settings — no need to re-run onboarding. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LLM.swift's bundled llama.cpp doesn't support Qwen3/3.5 architecture. Reverted to Qwen 2.5 1.5B + 3B which work reliably. Will upgrade when LLM.swift updates its llama.cpp. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Info.plist was still at v1.1 so Sparkle thought the "update" was the same version. Now properly at v1.3 build 4. Fixes #2. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Improve post-paste learning via Accessibility
Picker in Settings under Input section. Switching models triggers re-download and reload. Default remains small.en for accuracy. tiny.en is ~75 MB and much faster for shorter recordings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…oggle) Matches the original Ghost Pepper behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Input Monitoring prompt doesn't reliably show the system dialog for debug-signed apps. Now attempts to start the hotkey monitor even without it — Accessibility alone is sufficient for Control key. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixed codesign verification failure reported in #4. Now verifies signature after extracting from DMG before release. Also includes: speech model picker, default Control shortcuts, non-blocking Input Monitoring check. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
macOS kills the app after Screen Recording is granted but doesn't relaunch it. Now spawns a background process that reopens the app after 3 seconds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace no-audio modal with status pill
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Addresses #5 — users should know about local transcript log and auto-launch behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
File-based logging was replaced by in-memory DebugLogStore. Nothing is written to disk. Updated disclosure accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
No longer blocks Continue button. Shown as "(optional)" with a bordered (not prominent) Enable button. Users can skip it and enable later in Settings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Matches WhisperFlow and other dictation tools. Toggle is Right Command + Right Option + Space. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Supports Spanish and 90+ other languages. Same size as small.en. Users can tweak the cleanup prompt for their language's filler words. Addresses #6. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…chat threading Context Bundler (renamed from Pepper Chat): - Trello integration via OAuth-style authorize flow (user's own API key) - "Add to Trello" creates cards with voice-parsed title, board, list matching - TrelloCommandParser extracts structured data from natural speech - Boards/lists fetched and cached, default list picker in Settings - Button only shows when Trello is configured Multi-window context capture: - Captures screenshot + OCR at recording START (not end) so it gets the right window, not the Ghost Pepper bubble - Continuously monitors for window switches during recording (every 1s) - Each new window gets its own screenshot + OCR, shown as thumbnails - All contexts combined into one bundle for Zo/Trello/clipboard Chat threading with Zo: - Conversation accumulates automatically — prior Q&As sent as context - Scrollable chat history in the bubble - "Clear context" wipes thread, "Save as note" exports to meetings - "Chatting with Zo..." with larger logo during processing UI improvements: - Keyboard navigation: arrow keys, Enter, Escape (via local event monitor) - Close button on all states - Full command text (no truncation) - Sound effects on hotkey press/release Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Native Granola importer: imports from local cache + fetches full notes & transcripts via Granola API with proper pagination (cursor/hasMore) - Reads summary_markdown from API (not just "summary" field) - Granola API key persisted to UserDefaults for re-sync - Sidebar search to filter meetings by name - Granola sync icon in sidebar header (shows when cache detected) - Fixed markdown parser to read Summary, Chapters, and YAML frontmatter - Fixed Summary tab showing "No transcript" even when summary exists - Fallback transcript parser for Granola format (plain text, no timestamps) - Writer now preserves Summary section on save - Improved summary prompt: topic-based headings instead of generic format, incorporates user notes as emphasis guide - PRIVACY_AUDIT.md: audit prompt + results for verifying 100% local default - README: privacy audit table with verify-it-yourself instructions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…media resume launching Apple Music - Set input device directly on audio unit via kAudioOutputUnitProperty_CurrentDevice instead of changing the system-wide default input device (#60) - Added manual mono downmix for aggregate devices with >2 channels, since AVAudioConverter can't handle non-standard channel counts - Persist selected device to UserDefaults instead of system default - Removed auto-resume (kMRPlay) from MediaPlaybackController — was unconditionally sending play command on recording stop, which launched Apple Music or resumed YouTube even when nothing was playing (#56, #57) Closes #60, closes #56, closes #57 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Centered hero with 128px logo, tagline, prominent download button - 2x2 feature grid (speech-to-text, meetings, cleanup, privacy) - Testimonials section with Ryan Hoover quote + Dave Morin, Nick Saltarelli, Michael Mignano tweets as screenshot images - Star on GitHub CTA - Streamlined layout: download first, details later Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…heading - SVG diagram: Your Voice → Your Mac (on device) → Your Text - Dynamic shields.io badges: GitHub stars, MIT license, 100% Local, 50+ Languages - Renamed bottom section to "Why Ghost Pepper?" Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- README reverted to standard project docs format - site/index.html is the Product Hunt landing page with hero, testimonials, privacy flow diagram, embedded tweets, and live star count Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Logo and favicon now use raw.githubusercontent.com URLs - Removed Your Voice → Your Mac → Your Text diagram - All image paths are absolute for GitHub Pages compatibility Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…targeted - Set isTranscribing=true before async Task to prevent bubble auto-dismiss during the gap between recording stop and transcription start - Show context review even when transcription is empty (screenshots still useful) - Set targetDeviceID on context bundler's AudioRecorder (was missing after the aggregate device fix changed away from system default) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolved conflicts: - AppState.swift: kept both context capture cleanup and hotkey binding update - MenuBarView.swift: kept pepperChatEnabled guard with our "Context Bundler" rename Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix Zoom meeting transcript lifecycle and window chrome
Per-meeting "auto-delete" flag (stored as `auto_delete: true` in YAML frontmatter) plus a global retention window in Settings. The sweeper runs at launch and every 6 hours, stripping the `## Transcript` section from flagged files whose folder date is older than the window; notes and summary are preserved. Unflagged meetings are never touched. The meeting window shows an "auto-delete" checkbox only when the retention window is set (i.e. not "Never"). The write path re-checks the on-disk expiry marker before saving so an open tab can't resurrect a swept transcript. Privacy copy is explicit that this is not secure erasure: APFS copy-on-write and Time Machine snapshots may retain previously written bytes; FileVault encrypts them at rest but does not prevent recovery once the disk is unlocked. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Settings Privacy card now has a second picker ("Applies to: Flagged
meetings only / All meetings") that shows up once retention is set.
The sweeper takes an `onlyFlagged` parameter (default true preserves
opt-in behavior); when set to false, every meeting past the retention
window is swept regardless of flag. The per-meeting auto-delete
checkbox hides in global mode since the flag is ignored there.
Also wires sweep-on-toggle so flagging a meeting triggers an immediate
sweep instead of waiting for the next 6h timer tick.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- P1: saveActiveTab was bypassing the on-disk expiry guard by calling renderMarkdown + markdown.write directly. Now routes through MeetingMarkdownWriter.write(transcript:to:existingFileURL:) so any tab interaction (notes edit, rename, flag toggle, switch) honors a swept file's marker instead of clobbering it. - P2: per-folder contentsOfDirectory failures are no longer swallowed by try?. They now record an error like the base-directory path does. - P2: Privacy card description is now coherent with retention state. When retention is "Never" the copy says "no transcripts are auto- deleted" instead of leaking whatever value the scope toggle held. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…expiry # Conflicts: # GhostPepper.xcodeproj/project.pbxproj # GhostPepper/UI/MeetingTranscriptWindow.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a privacy-focused, opt-in transcript retention feature to Ghost Pepper:
auto_delete: truein the markdown file's YAML frontmatter.## Transcriptsection — notes, summary, and trailing sections (e.g. Granola## Chapters) are preserved. A parseable marker<!-- ghost-pepper-transcript-expired: YYYY-MM-DD -->is stamped so the operation is idempotent.Why opt-in by default
An always-on global auto-delete is an easy footgun — users with important meetings could lose them silently. The opt-in flag forces an explicit choice per meeting. The global scope toggle is available for users who know they want it that way.
Privacy — honest about the caveat
This is not secure erasure.
String.write(atomically:)does an atomic rename-replace, so the previous file's blocks linger in APFS free space until reused, and Time Machine snapshots may retain earlier versions. FileVault encrypts these blocks at rest but does not prevent recovery once the disk is unlocked.The UI copy, the sweeper doc comment, and
PRIVACY_AUDIT.mdall say this explicitly. An earlier draft claimed FileVault provided secure erasure; that claim has been removed.How it was built
## Transcript), false privacy claim, silent directory-scan failures, sticky expired flag in renderer, DateFormatter thread-safety.saveActiveTabstill bypassed the new on-disk guard, per-folder scan failures were still swallowed, scope-picker description copy could leak stale state when retention was set to Never.Files
New
Modified
Tests
33 new unit tests, all green. Full app build clean. Touching `@MainActor` on AppState; sweep I/O runs on a detached Task and hops back to main for logging.
```
Executed 33 tests, with 0 failures in 0.07 seconds
** BUILD SUCCEEDED **
```
Test plan
🤖 Generated with Claude Code