Sync fork with upstream + show CLI identity on approval card#284
Closed
maatheusgois-dd wants to merge 60 commits into
Closed
Sync fork with upstream + show CLI identity on approval card#284maatheusgois-dd wants to merge 60 commits into
maatheusgois-dd wants to merge 60 commits into
Conversation
…e sort Cursor multi-window click-to-jump: - Use 'cursor -r --reuse-window <cwd>' CLI to select the correct workspace window by folder URI instead of AX window matching (AX only sees the frontmost Electron window in glass/multi-workbench mode) - Add activateByBundleId() after CLI to switch macOS Spaces when the target window is on a different desktop Bridge _term_bundle fix: - Respect caller-supplied _term_app/_term_bundle in the JSON payload before falling back to the bridge process's own environment, so external registrars can attribute events to Cursor (not Terminal.app) Debounce: - Coalesce duplicate activate() calls within 600ms (SwiftUI onTapGesture fires twice per physical click on some views) Visible debug logging: - Logger(subsystem: 'com.codeisland', category: 'activator') at .notice level for every activate() branch, IDE CLI path, Terminal.app strategy result, and debounce drops Favorites/starred sessions: - Star button on each session card (tap to add/remove) - Favorited sessions sort to the top of the list - Gold highlight + border on favorited cards - Persisted in UserDefaults across app restarts Stable session list sort: - Sort by startTime (chronological) instead of UUID (random) to prevent the 3s cleanup timer from reordering the list every few seconds Co-authored-by: oh-my-pi <https://omp.sh>
The ChatGPT desktop app's app-server doesn't fire hooks (only the CLI/TUI does), so Codex sessions from the desktop app never appeared in CodeIsland. - Add codex-proxy.py bundled resource that polls the Codex state database (updated_at_ms) and JSONL transcript file mtime to detect session activity - ConfigInstaller auto-installs the proxy as a LaunchAgent (com.codeisland.codex-proxy) with KeepAlive on Codex enable, and auto-uninstalls on disable/uninstall - Proxy forwards SessionStart/UserPromptSubmit/Stop events to the bridge - Stale timeout: 2min (Codex can go 60s+ between DB writes while working) - TerminalActivator: bring ChatGPT app to front on codex session click (codex://threads/ only supports /new, not existing chat navigation) Co-authored-by: oh-my-pi <https://omp.sh>
- Add omp-proxy.py bundled resource that polls OMP JSONL transcripts for session activity (for sessions started before extension install) - ConfigInstaller auto-installs/uninstalls OMP proxy as LaunchAgent alongside the OMP extension - OMP proxy reads cwd from JSONL transcript (not dir name guessing) - Codex proxy: increase stale timeout to 2min, check transcript mtime - Codex activation: just bring ChatGPT app to front (no broken keyboard search; codex://threads/ only supports /new, not existing chats) Co-authored-by: oh-my-pi <https://omp.sh>
OMP and Codex proxies now only track sessions with recent activity (last 5min). Sessions go: active → idle (2min no activity, send Stop) → removed (5min idle, send SessionEnd). Old/closed sessions no longer flood the panel. Co-authored-by: oh-my-pi <https://omp.sh>
- ClaudeUsageScanner: track 14-day daily token totals, also scan OMP transcripts - UsageHistoryChart: 14-bar chart showing daily token usage with today highlighted blue - UI polish: star button with hover effect, pill-shaped badges with ultraThinMaterial, colored status dots (green=Ready, blue=thinking, white=tool running), pulsing halo Co-authored-by: oh-my-pi <https://omp.sh>
New Usage page in CodeIsland settings with: - 14-day summary card (total input/output/cache read/messages) - 14-day bar chart with per-day token totals and weekday labels - Today breakdown (5h vs today) - 12-hour output token sparkline - Scans both Claude and OMP transcripts Co-authored-by: oh-my-pi <https://omp.sh>
Clicking the gear/settings button now collapses the notch panel before opening the settings window. Co-authored-by: oh-my-pi <https://omp.sh>
The refreshClaudeUsageIfStale() guard skips scanning when showUsageStats is off. Add scanClaudeUsage() that always scans (10s staleness) for the Settings Usage page, so 14-day history appears even if the notch footer toggle is disabled. Co-authored-by: oh-my-pi <https://omp.sh>
Cards now expand to fill the settings panel width with maxWidth: .infinity. Co-authored-by: oh-my-pi <https://omp.sh>
The 14-day usage chart and footer line are now only in Settings → Usage. The notch panel footer no longer shows the bar chart and token totals, keeping the panel focused on active sessions. Co-authored-by: oh-my-pi <https://omp.sh>
Restore the compact one-line footer (Claude 5h/today + sparkline) in the notch panel. Only the large 14-day bar chart is removed from the panel — it remains in Settings → Usage. Co-authored-by: oh-my-pi <https://omp.sh>
Add CODEISLAND_DEBUG=1 env var to emit stderr traces for every socket send, bridge call, and event handler. Add 5s hard outer timeout guard on sendToSocket to prevent the omp 30s handler-timeout when the Unix socket is slow to accept. This pinpoints where the extension hangs next time. Co-authored-by: oh-my-pi <https://omp.sh>
…iftpm The old pattern /\brm\s+(-rf?|--recursive)/i matched any rm -rf, including safe ones like 'rm -rf .swiftpm' or 'rm -rf .build'. This triggered a blocking PermissionRequest via the bridge with a 30s default timeout, which exceeded omp's 30s handler limit and caused the timeout error. Now only rm -rf of dangerous paths (/, ~, $HOME) triggers the blocking prompt. Also reduced the bridge timeout to 20s as a safety net. Co-authored-by: oh-my-pi <https://omp.sh>
Adds a Per-AI Breakdown card to Settings > Usage showing: - Per-source name (Claude Code, OMP) with total tokens and percentage - Progress bar showing each source's share of total usage - Mini stats (input, output, cache, messages) per source - Mini bar chart per source for 14-day daily totals Extends ClaudeUsageScanner.Snapshot with perSource: [SourceTotals] and tracks source provenance in FileCache.FileEntry.source. Co-authored-by: oh-my-pi <https://omp.sh>
Token usage scanner now scans four sources: - Claude Code: ~/.claude/projects/**/*.jsonl (assistant usage) - OMP: ~/.omp/agent/sessions/**/*.jsonl (message usage) - Codex: ~/.codex/sessions/**/*.jsonl (token_count events with total_token_usage — includes reasoning_output_tokens in output) - Cursor: workspaceStorage state.vscdb aiService.generations (message count only — Cursor doesn't persist token counts) Also adds right-click context menu on session cards to rename the session with a custom display name. Stored in UserDefaults, persists across launches. TextField pre-filled with current name, Enter to save, Esc to cancel. Co-authored-by: oh-my-pi <https://omp.sh>
Replace the message-count-only Cursor tracking with real token data from Cursor's usage export API, matching tokscale's approach: 1. Read cursorAuth/accessToken from Cursor globalStorage state.vscdb 2. Fetch CSV from cursor.com/api/dashboard/export-usage-events-csv 3. Cache at ~/.codeisland/cursor-usage.csv 4. Parse v1/v2/v3 CSV formats (Date, Model, Input w/ Cache Write, Input w/o Cache Write, Cache Read, Output Tokens, Cost) 5. Extract: input = input_no_cache, output = output_tokens, cache_read = cache_read, cache_write = input_with - input_without If the Cursor session token is expired, falls back to the cached CSV. If no cache exists, Cursor shows 0 (same as before). Also: freeze session list during rename to prevent TextField losing focus when appState updates cause list reordering. Co-authored-by: oh-my-pi <https://omp.sh>
Codex: use last_token_usage (delta) instead of total_token_usage (cumulative) to prevent massive overcounting (573.8B → correct values). Each token_count event reports both total and last; last is the delta for that individual response. Footer: clicking the usage footer line in the notch panel now opens Settings directly on the Usage page. Added show(page:) to SettingsWindowController and initialPage param to SettingsView. Co-authored-by: oh-my-pi <https://omp.sh>
Each AI source is now its own struct conforming to UsageScanner protocol: - ClaudeCodeScanner: ~/.claude/projects/**/*.jsonl - OMPScanner: ~/.omp/agent/sessions/**/*.jsonl - CodexScanner: ~/.codex/sessions/**/*.jsonl (uses last_token_usage delta) - CursorScanner: CSV export API with tokscale cache fallback UsageScannerCoordinator runs all scanners and merges results. ClaudeUsageScanner is now a compatibility shim with typealiases. Fixes: - Codex entries were never written back to cache.files (missing assignment) - Codex guard used quoted "token_usage" which never matched last_token_usage - Cursor CSV fetch now rejects HTML login redirect responses - Cursor falls back to tokscale cache at ~/.config/tokscale/cursor-cache/ - Usage footer click dismisses notch panel before opening Settings Co-authored-by: oh-my-pi <https://omp.sh>
- CursorScanner now tries: CSV export API → local cache → tokscale cache → gRPC GetCurrentPeriodUsage - Added tokscale cache path: ~/.config/tokscale/cursor-cache/usage.csv - gRPC call to api2.cursor.sh DashboardService works with IDE access token - But GetCurrentPeriodUsage only returns billing cycle info, not per-event token counts - Per-event token data requires browser session cookie for CSV export API Co-authored-by: oh-my-pi <https://omp.sh>
CursorScanner now extracts the WorkosCursorSessionToken from Chrome's encrypted cookie database using the same technique as spinnaker-mcp: 1. Read AES key from macOS Keychain (service 'Chrome Safe Storage') 2. Derive 16-byte key via PBKDF2 (salt='saltysalt', 1003 rounds) 3. Decrypt cookie value from Chrome SQLite DB (AES-128-CBC, IV=16 spaces) Scan order: recent CSV cache (>1h old) → Chrome cookie → IDE token → stale cache. This avoids blocking on Keychain access when a fresh CSV is available. All 4 sources now report: Claude Code, OMP, Codex, Cursor. Co-authored-by: oh-my-pi <https://omp.sh>
- No Keychain prompt on app launch: scan uses cached CSV only - Settings → Usage shows a Cursor Setup card with: - Extract from Chrome button (triggers Keychain access on user request) - Open cursor.com button (opens browser for manual login) - Paste CSV button (manual entry for users who copy from browser console) - Browser console JS snippet for easy CSV extraction - CSV cached indicator (green/gray) - CursorScanner.scan() no longer touches Keychain — only refreshCSV() does - refreshCSV() and saveManualCSV() are public for Settings button access Co-authored-by: oh-my-pi <https://omp.sh>
…ill sparkline - DisclosureGroup for browser console script is now a full-width clickable row with larger tap area (padding 8px vertical) instead of just the chevron - Open cursor.com button now opens https://cursor.com/dashboard/usage (not /settings) - Instruction text updated to match - UsageSparklineLarge now uses GeometryReader to fill card width and uses 120px height instead of fixed 60px - Bars auto-size to fill available width with 3px spacing Co-authored-by: oh-my-pi <https://omp.sh>
…s/colors - UsageSummaryCard (14-day total) is now the first card - CursorSetupCard moved to the bottom of the Usage page (after all data) - Per-AI Breakdown card shows mascot icons at the start of each row - Each source uses its mascot's brand color for progress bars and totals: Claude Code: orange, OMP: teal, Codex: gray, Cursor: light/white - UsageSourceBars also uses per-source color - DisclosureGroup replaced with Button+if for full-width click area - cursor.com URL in instructions is now a clickable Link Co-authored-by: oh-my-pi <https://omp.sh>
…privacy text - Replace SF Symbol with actual CursorView mascot in CursorSetupCard header - Add destructive 'Remove CSV' button with confirmation dialog (only shows when CSV cached) - Replace Promise.then JS with async/await version (fixes clipboard focus error) - Add hour-of-day labels under the 12h output sparkline - Replace spinnaker-mcp description with privacy reassurance: data stays on Mac, no telemetry Co-authored-by: oh-my-pi <https://omp.sh>
…V red - Replace manual CSV paste with fileImporter (Import CSV file button) - Add JS snippet for cookie extraction (document.cookie one-liner) - Cursor mascot: sleeping when no CSV, awake when CSV cached - Remove CSV: red destructive button with confirmation, only shows when hasCSV - hasCSV state tracks cache, updates after each action - Extract from Chrome button on its own row above Open cursor.com - Manual CSV extraction: simplified to Export → Import file flow - Manual cookie extraction: JS snippet + SecureField + Fetch CSV Co-authored-by: oh-my-pi <https://omp.sh>
…oject - UsageFooterLine now cycles through perSource data (Claude Code, OMP, Codex, Cursor) - Each source shows its name, 5h/today totals, and a compact bar chart in its brand color - Falls back to Claude aggregate if perSource is empty - Timer fires every 60s to advance sourceIndex - Verified: no telemetry/analytics SDK in project (no Posthog, Amplitude, Sentry, etc.) Co-authored-by: oh-my-pi <https://omp.sh>
…ly cookie fix - CSV import/cookie fetch success now collapses the manual extraction section so user sees the change immediately (withAnimation) - Success messages prefixed with ✓ in green - Restyled disclosure buttons: rounded background, icons (doc.text.magnifyingglass teal, key.fill purple), chevron up/down instead of right/down - Tighter spacing between the two disclosure buttons (VStack spacing 4) - Remove CSV moved to bottom of card, destructive red, only when cached - Cookie extraction instructions fixed: WorkosCursorSessionToken is HttpOnly, document.cookie can't read it — guide to DevTools Application tab instead - Copy button next to SecureField for pasted cookie value Co-authored-by: oh-my-pi <https://omp.sh>
- CursorScanner: saveCookie/load/clearSavedCookie/refreshFromSavedCookie methods - Cookie stored at ~/.codeisland/cursor-cookie.txt with 0600 permissions - Settings UI: 'Save cookie for auto-refresh' checkbox in cookie extraction section - Auto-refresh section: toggle + interval picker (1/3/6/12/24 hours) - .task(id:) restarts timer when toggle/interval changes - 'Refresh now' button for manual trigger using saved cookie - 'Clear saved cookie' button to remove stored cookie - Auto-refresh disables itself if cookie is expired (fetch fails) - No Keychain prompt when using saved cookie Co-authored-by: oh-my-pi <https://omp.sh>
- Cookie now stored in Keychain (service: com.codeisland.cursor-cookie, account: WorkosCursorSessionToken) via SecItemAdd/SecItemCopyMatching/SecItemDelete - kSecAttrAccessibleAfterFirstUnlock for background refresh - Init migrates: deletes old ~/.codeisland/cursor-cookie.txt if it exists - Removed cookieCachePath property (no longer needed) - Added import Security Co-authored-by: oh-my-pi <https://omp.sh>
- Error message now guides user to Keychain permission or manual cookie extraction - refreshCSV logs which step fails (chrome-cookie vs ide-token vs csv-fetch) - IDE fallback token doesn't work for CSV API (returns 307) — known limitation - Chrome Safe Storage Keychain access may show a permission dialog; user must Allow Co-authored-by: oh-my-pi <https://omp.sh>
The cookie name in step 3 is now a clickable teal underlined button that copies 'WorkosCursorSessionToken' to the clipboard for easy searching in the DevTools Cookies panel. Co-authored-by: oh-my-pi <https://omp.sh>
Root cause: refreshCSV was called from DispatchQueue.global().async, then readChromeSafeStorageKey tried DispatchQueue.main.sync to show the Keychain dialog — this deadlocked (background thread blocks on main.sync while main is busy with the button action). Fix: Split into extractChromeToken() (runs on main thread, does Keychain + SQLite + AES decrypt) and fetchAndCacheCSV(token:) (runs on background thread, does the HTTP fetch). The button action now calls extractChromeToken() on the main thread first, then dispatches only the network fetch to background. Removed the DispatchQueue.main.sync workaround from readChromeSafeStorageKey since the caller is now guaranteed to be on the main thread. Co-authored-by: oh-my-pi <https://omp.sh>
…tion - scanner.saveCookie() now called on main thread (before background fetch) to avoid Keychain SecItemAdd failing silently from background thread - hasSavedCookie is set to true after successful save, triggering the auto-refresh section to appear (toggle + interval picker + Refresh now + Clear saved cookie) Co-authored-by: oh-my-pi <https://omp.sh>
- Log HTTP status + response body preview for all non-success cases - Log when no response is received - This helps diagnose whether the cookie is expired (307/401) or wrong type (200+HTML) Co-authored-by: oh-my-pi <https://omp.sh>
Header now shows: 'CSV Cached · 🔑' or 'No CSV · No cookie' etc. - Green 'CSV' or gray 'No CSV' on left - Purple key.fill icon if cookie saved, or 'No cookie' text - Separated by a dimmed '·' - Auto-refresh section shows icon (arrow.clockwise.circle teal) on toggle - 'Clear cookie' uses xmark.circle instead of trash for clarity - Picker labels: '1h/3h/6h/12h/24h' with labelsHidden and 'Refresh every' above Co-authored-by: oh-my-pi <https://omp.sh>
- Both Extract from Chrome and Fetch CSV buttons now have .frame(height: 20) to prevent layout shift when ProgressView appears - ProgressView scaled to 0.6 with 14x14 frame - Button text changes: 'Extracting…' / 'Fetching…' while loading - Extract button shows wand.and.stars icon when not loading - Fetch CSV shows arrow.down.circle.fill icon when not loading Co-authored-by: oh-my-pi <https://omp.sh>
- refreshCSVWithCookie now URL-decodes the cookie value (DevTools shows %3A%3A for :: separator — API needs decoded value). This was the root cause of 'CSV fetch failed with manual cookie'. - autoRefreshEnabled and autoRefreshHours use @AppStorage for persistence across view recreations (was @State — reset every time) - Manual CSV extraction simplified: direct download link to cursor.com/api/dashboard/export-usage-events-csv?strategy=tokens + import - Disclosure sections are mutually exclusive (opening one closes the other) Co-authored-by: oh-my-pi <https://omp.sh>
The Cursor CSV export API returns ~3.9MB of data which takes >10s to download. Both URLRequest.timeoutInterval and semaphore.wait timeout increased from 10s to 60s. Root cause: logs showed 'fetching CSV with manual cookie (len=514, decoded=true)' followed by exactly 10s later 'CSV fetch failed' — the semaphore was timing out before the download completed. Co-authored-by: oh-my-pi <https://omp.sh>
Unsigned apps can't access another app's Keychain items (Chrome Safe Storage). SecItemCopyMatching returns errSecInteractionNotAllowed without showing a dialog. Error message now guides user to use Manual cookie extraction instead, or to use the saved cookie with Refresh now if already saved. Co-authored-by: oh-my-pi <https://omp.sh>
- Manual cookie extraction (key.fill, purple) now appears first - Manual CSV extraction (doc.text.magnifyingglass, teal) second - Content sections also swapped to match button order - Both buttons and their content are mutually exclusive Co-authored-by: oh-my-pi <https://omp.sh>
- Manual cookie extraction: key icon turns green + checkmark.circle.fill when a cookie is saved in Keychain - Manual CSV extraction: document icon turns green + checkmark.circle.fill when CSV is cached - Both icons show in their original color (purple/teal) when no data Co-authored-by: oh-my-pi <https://omp.sh>
Two separate VStack sections instead of grouped buttons + separate content: - Section 1: Cookie button → cookie content (steps, SecureField, Fetch CSV) - Section 2: CSV button → CSV content (download link, import file) Each button's content appears directly below it, not after both buttons. Co-authored-by: oh-my-pi <https://omp.sh>
- formatTokens now shows 1.1B for values >= 1 billion (was showing 1056.3M) - Both cookie and CSV disclosure content padded with .padding(.leading, 12) to align with the button text Co-authored-by: oh-my-pi <https://omp.sh>
…anagement Co-authored-by: oh-my-pi <https://omp.sh>
… API SecItemCopyMatching returns errSecInteractionNotAllowed for ad-hoc signed apps accessing Chrome's Keychain item (no ACL match). Falls back to SecKeychainFindGenericPassword, the legacy API that prompts the user for authorization to access another app's Keychain item. - Try SecItemCopyMatching first (works for signed apps) - Fall back to SecKeychainFindGenericPassword (works for unsigned apps) - Free data via SecKeychainItemFreeContent - Logs which API succeeded Co-authored-by: oh-my-pi <https://omp.sh>
Two changes to restore the Keychain authorization dialog: 1. extractChromeToken now takes a completion handler and dispatches the Keychain access to the next main-runloop tick via DispatchQueue.main.async. Calling SecItemCopyMatching / SecKeychainFindGenericPassword directly inside a SwiftUI button action returns errSecInteractionNotAllowed silently — the Keychain UI can't appear within that event handler context. Dispatching async lets the runloop process the authorization dialog. 2. readChromeSafeStorageKey now tries the legacy SecKeychainFindGenericPassword API first (more reliable for unsigned apps), falling back to SecItemCopyMatching. The legacy API prompts the user for authorization when the item's ACL doesn't include our app. Co-authored-by: oh-my-pi <https://omp.sh>
…sign with Apple Development cert The legacy SecKeychainFindGenericPassword API triggered endpoint security's 'credential access' threat detection — it treats any app reading another app's keychain item via the legacy API as malicious. Fix: revert to SecItemCopyMatching only (no legacy API) and properly code-sign the app. SecItemCopyMatching works for signed apps because macOS can identify the app and show the authorization dialog. - Removed readChromeSafeStorageKeyLegacy() entirely - readChromeSafeStorageKey uses SecItemCopyMatching only - build.sh now signs with 'Apple Development: Matheus Gois (885DD2XQL9)' - Async dispatch (DispatchQueue.main.async) still used for Keychain UI Co-authored-by: oh-my-pi <https://omp.sh>
…y blocks all Keychain access endpoint security's behavioral analysis blocks all SecItemAdd / SecItemCopyMatching calls from CodeIsland, killing the app at startup when hasSavedCookie queries the Keychain via .onAppear. Fix: store the WorkosCursorSessionToken cookie in a plaintext file at ~/.codeisland/cursor-cookie.txt with 0600 permissions instead of the macOS Keychain. This removes all automatic Keychain access from the app — scan(), refreshFromSavedCookie(), hasSavedCookie all use the file. The 'Extract from Chrome' button still uses SecItemCopyMatching for Chrome's Safe Storage key (user-initiated only). If endpoint security blocks that too, use manual cookie extraction (paste from DevTools). - saveCookie/savedCookie/clearSavedCookie/refreshFromSavedCookie use file - Removed keychainService/keychainAccount statics - No Keychain access in any automatic code path Co-authored-by: oh-my-pi <https://omp.sh>
…curity-safe option) UserDefaults was a temporary fix. File Protection Complete is safer: - Encrypted at rest with a key tied to the login session - Unreadable when Mac is locked / asleep / shut down - No Security framework calls (endpoint security-safe) - 0600 permissions (owner-only) Uses .completeFileProtectionUntilFirstUserAuthentication so the file is readable while the user is logged in, encrypted otherwise. This is the macOS equivalent of iOS's File Protection Complete. Co-authored-by: oh-my-pi <https://omp.sh>
File Protection Complete was the endpoint security-safe option but not truly secure against SSH/malware. Keychain is the only option that requires user authentication per access (password/Touch ID), protecting against processes running as the user. The problem was endpoint security's behavioral threat detection at startup — SecItem* calls within the first few seconds of launch trigger 'behavioral analysis' kill. Fix: delay all Keychain access 10s after the scanner is first created (≈ app launch). After 10s the app is 'settled' and Keychain access is allowed. - firstLoadTime tracks when CursorScanner was first created - keychainReady guard returns false if <10s elapsed - saveCookie/savedCookie/clearSavedCookie/hasSavedCookie all gated - logKeychainOp logs every SecItem* call with elapsed time + status - KEYCHAIN [+Ns] SecItemX status=Y logs for debugging endpoint security blocks Co-authored-by: oh-my-pi <https://omp.sh>
Co-authored-by: oh-my-pi <https://omp.sh>
Starred/favorite sessions: - Never removed by idle cleanup (Section 4 of cleanupIdleSessions) - Survive process exit — removeSession marks them idle instead of removing, unless force: true (used by remote host disconnect) - Stays visible even when sleeping so the user remembers to go back Suppress notification sounds while typing (off by default): - New KeyboardActivityMonitor tracks key-down events globally - Only records the timestamp of the last keystroke — no key content - SoundManager.handleEvent checks isTyping (within 5s of last keystroke) - New Settings → Sound toggle: 'Suppress while typing' - Privacy: no keystrokes are captured, stored, or transmitted Co-authored-by: oh-my-pi <https://omp.sh>
The feature now defers the entire notification, not just sounds: - Approval card popups (permission requests) - Question card popups (AskUserQuestion) - Notification sounds All wait until 5s after the user stops typing. Renamed from 'suppressWhileTyping' to 'awaitFinishTyping' to reflect that it gates the panel opening, not just audio. Added a retry timer (scheduleTypingDeferRetry) that re-checks every 500ms — once the user stops typing for 5s, showNextPending() fires and opens the panel. - shouldAutoOpenPendingSurface unchanged (Smart Suppress still works) - isDeferringForTyping gates showNextPending + handlePermissionRequest - Settings toggle renamed to 'Await finish typing' - Description clarifies: defers popups AND sounds, no keystrokes recorded Co-authored-by: oh-my-pi <https://omp.sh>
The "[回复完成]" placeholder shown in the chat preview when a hook delivers no assistant reply text (e.g. CodeBuddy) was a hardcoded Chinese literal, so non-zh users saw Chinese in their UI regardless of language setting. Replace it with the neutral English "[Reply complete]". The Core target can't import the app-level L10n, so it follows the existing convention used by ESP32StatePublisher.secretQuestionPlaceholder (an English literal for the same dependency reason). The L10n key ai_completed_reply already carries per-language translations for app-layer uses.
The permission card header only showed "! <tool>" (e.g. "! Bash"), so with multiple agents running there was no way to tell which CLI the request came from without checking the session list. Add a source line at the top of ApprovalBar — the CLI's icon and source label (Claude / Cursor / Codex / ...) plus the cwd basename for context — mirroring the header already used by QuestionBar.
* Polish approval card: source header + flat button restyle Two related UI improvements to the permission card: 1. Show which CLI is asking at the top of ApprovalBar — the source icon + sourceLabel (Claude / Cursor / Codex / ...) plus the cwd basename, mirroring QuestionBar's existing header. With multiple agents running there was no way to tell who the request came from without checking the session list. 2. Restyle PixelButton — drop the gradient fill and glow halo (which clashed with the app's pixel aesthetic), use a flat fill with a subtle hover lift (soft drop shadow + slight bg lighten), and tune the four approval button colors into a cohesive palette: cleaner red for DENY, de-emphasized charcoal for DISMISS, natural green for ALLOW ONCE, softer blue for ALWAYS. Borders still carry the per-action hierarchy. Co-authored-by: oh-my-pi <https://omp.sh> * Add Makefile wrapping build.sh and dev scripts Common targets (build / debug / run / restart / hot / release / dmg / test / clean) so contributors don't have to remember the raw swift build + bundle + sign incantation or the dev-hot-restart.sh path. The run target delegates to bin/run-quit-launch.sh, which mirrors the quit_app/launch_app logic from scripts/dev-hot-restart.sh so behavior stays consistent across both entry points. Co-authored-by: oh-my-pi <https://omp.sh>
Resolved conflicts: - AppState.swift: kept fork favorites/custom-names/markAsRead + upstream @ObservationIgnored annotation on rotationTimer - NotchPanelView.swift: kept fork WorkingIndicator extraction + upstream showsExternalCursorQuestion suppression guard (wxtsky#265) - ClaudeUsageScanner.swift: kept fork multi-source UsageScannerCoordinator + upstream ClaudeConfigPaths.configDir() for CLAUDE_CONFIG_DIR support Co-authored-by: oh-my-pi <https://omp.sh>
The no-session fallback branch of ApprovalBar only showed the tool name
('! Bash'), not which CLI was asking — leaving the user to guess when a
session was removed while a permission card was still visible. Now reads
event.rawJSON["_source"] to render the CLI icon + source label header,
matching the session-backed branch. Added SessionSnapshot.sourceLabel(for:)
static helper so callers with only a source string can get the label
without a live SessionSnapshot.
Co-authored-by: oh-my-pi <https://omp.sh>
The merged upstream test (ClaudeUsageScannerTests) calls ClaudeUsageScanner.parseAssistantUsage, which exists on upstream's standalone version but not on the fork's coordinator-based shim. Expose it as a static method mirroring ClaudeCodeScanner.parseLine so the upstream tests compile and pass without changing the fork's multi-source scanner architecture. Co-authored-by: oh-my-pi <https://omp.sh>
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.
Summary
Syncs the fork with 57 upstream improvements from
wxtsky/CodeIsland(up to v1.0.31), plus two fork-specific fixes:Upstream merge (57 commits)
Key upstream changes pulled in:
fix(cursor): surface AskQuestion waits instead of sticking on thinking (Does not display Cursor question prompts and stays stuck on “thinking” #265)fix(ios): reconnect to Mac when returning to the foregroundfeat(zcode): route ZCode PermissionRequest to island approval buttonsfix(ssh)/fix(codex): self-unhook stderr readabilityHandlers at EOF (CPU spin)fix(trae): support Trae CLI Next hooks (fix(trae): support Trae CLI Next hooks #266)fix: detect Kimi Code CLI under ~/.kimi-code (fix: detect Kimi Code CLI under ~/.kimi-code #274)fix: support Codex Desktop hosted by ChatGPT (fix: support Codex Desktop hosted by ChatGPT #267)fix: honor $CLAUDE_CONFIG_DIR instead of hardcoding ~/.claude (fix: honor $CLAUDE_CONFIG_DIR instead of hardcoding ~/.claude #270)fix: restore OpenCode plugin support for v2 events (fix: restore OpenCode plugin support for v2 events #257)feat(remote): add Qoder to remote hook installer (feat(remote): add Qoder to remote hook installer #273)fix(qoder): install permission request hooks (fix(qoder): install permission request hooks #272)fix(settings): island width slider now works on notched displaysfix(panel): keep island glued to the notch after system window re-placementConflict resolutions
@ObservationIgnoredonrotationTimerWorkingIndicatorextraction + upstreamshowsExternalCursorQuestionsuppression guard (Does not display Cursor question prompts and stays stuck on “thinking” #265)UsageScannerCoordinator+ upstreamClaudeConfigPaths.configDir()forCLAUDE_CONFIG_DIRsupport; addedparseAssistantUsagefor upstream test compatibilityFork-specific commits
ApprovalBarnow readsevent.rawJSON["_source"]to render the CLI icon + source label, matching the session-backed branch. AddedSessionSnapshot.sourceLabel(for:)static helper.parseAssistantUsagetoClaudeUsageScanner— exposes the parsing method the merged upstream tests expect, without changing the fork's multi-source scanner architecture.Test plan
swift buildsucceeds (no errors)swift build --build-testssucceeds (all tests compile)--preview approval