feat(companion): add iPhone Buddy app and watch sync#1
Closed
fengye404 wants to merge 36 commits into
Closed
Conversation
…ext as answer key (wxtsky#191) Two bugs in the AskUserQuestion round-trip with Claude Code: 1. askUserQuestionUpdatedInput omitted the 'questions' key when originalQuestions was nil (cast failure). Claude Code's mapToolResultToToolResultBlockParam calls H.map() on questions directly, so a missing key causes 'undefined is not an object (evaluating H.map)'. Fix: always write questions, falling back to the raw toolInput value. 2. answerKey was derived from the question's header, but Claude Code looks up answers by question text (answers[question.question]). The key mismatch caused every answer to come back as an empty string. Fix: use questionText as the base key instead of header. Co-authored-by: shiyi20060618-cmd <202430841089@mail.scut.edu.cn>
wxtsky#191 switched AskUserQuestion answer keys from header to question text (matching Claude Code's `answers[question.question]` lookup) but left the existing AppStateQuestionFlowTests asserting old header-based keys, turning main red. Update the four affected assertions, rewrite the dedup/fallback cases to cover the new behavior (duplicate question text gets a suffixed key; header no longer participates in the key), and drop the now-unused `trimmedHeader` binding wxtsky#191 left behind so the build is warning-free again. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ky#182) verifyAndRepair treated any CLI whose hooks file was missing an event as a corrupt config and rewrote the full event list, clobbering a user who intentionally kept only a subset of (e.g. Codex) hooks. Add shouldPreservePartialHooks so repair only fires when nothing of ours remains or a stale `async` entry needs cleanup; a partial-but-intact config is left untouched. State queries and the explicit install/enable path are unchanged, so first-time install and manual re-enable still write every event. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The remote install script wired up claude/codex/codebuddy/traecli/opencode but omitted Hermes, so SSH-monitored Hermes sessions never sent events. Hermes is a Claude Code fork (same settings.json + hooks layout), so mirror install_claude into install_hermes and add it to the install list. Covered by a new script-content assertion; the existing python-compile test now exercises its syntax as well. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
wxtsky#169) mergeDuplicatePermissionRequest collapsed any queued request with a matching tool_use_id, denying the old waiter. But Claude Code can emit several parallel tool calls (e.g. "Read 4 files") that share an id while operating on different inputs — those are distinct requests, and merging them denied all but the last, surfacing as "denied by PermissionRequest hook" on tools the user never rejected. Only merge as a replay when the tool inputs also match; otherwise let the new request enqueue and get its own decision. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: 添加 Buddy 硬件桌宠的 README 文档 Co-authored-by: Copilot <copilot@github.com> * feat: 添加 Buddy 桌宠使用前提说明 * Add question alert functionality to various mascot modules - Introduced `*_Question` functions in multiple mascot header files to enable a question alert mode. - Updated `drawBang` function to render a question mark when `_questionMode` is active. - Modified `clawdSleep` to include a bored yawn animation based on global bored state. - Added global variables for bored state and eye offset to enhance mascot behavior. - Implemented tool icon classification and drawing functions for better visual representation of tools. * feat: enhance Buddy pairing process and add legacy support - Updated ESP32BridgeManager to handle legacy pairing fallback and timeout scenarios. - Improved error messages and status handling for pairing states. - Added new payload for clearing tool history. - Enhanced localization strings for better user guidance. - Updated README with clearer pairing instructions and hints for legacy firmware mode. - Added tests for new functionality in AppState and ESP32Protocol. * feat: implement write frame queue management for Buddy BLE communication * Improve Buddy Bluetooth recovery and signing * Fix Buddy recovery edge cases --------- Co-authored-by: Copilot <copilot@github.com>
Both integration tests capture a mutable var inside the tailer's serial onDelta callback; Swift 6 mode flags it as a data race even though the test only reads after wait()/sleep, so the access is ordered. Mark the two vars nonisolated(unsafe) to restore a warning-free build (surfaced by wxtsky#187's full recompile of CodeIslandCore; the warnings pre-dated it). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…jump (wxtsky#201) - wxtsky#193: the remote SSH socket was hardcoded to /tmp/codeisland.sock, so multiple OS users on a shared host collided. With StreamLocalBindUnlink a later user's reverse forward would unlink/rebind another user's socket, silently stealing their events. Probe the remote uid at connect time and route a per-user /tmp/codeisland-<uid>.sock through the SSH -R forward, the remote hook command (CODEISLAND_SOCKET_PATH), and the OpenCode plugin. Remote hook script also defaults to a uid-scoped path. Falls back to the legacy shared path if the probe fails. - wxtsky#190: force ControlMaster=no / ControlPath=none on the tunnel so `ssh -N` doesn't hand the forward to a pre-existing multiplexing master and exit 0 immediately, which the forwarder misreads as a failed connection ("ssh exited (0)"). - wxtsky#198: the iTerm2 jump now selects the matched *window* (not just tab+session) so a fullscreen / cross-Space session is raised and its Space switched to. Tests: per-user socket injection into the remote hook script and OpenCode plugin, plus the legacy fallback path. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#202) Follow-up hardening on wxtsky#201: - Guard each iTerm2 `select <window>` with its own `try` so an unexpected failure (e.g. a window mid fullscreen-transition) can't abort the surrounding script and skip the tab/session select — that would have made the jump worse than before. - Probe the remote uid with a bare `id -u` instead of a `$(...)` pipeline so it runs identically under non-POSIX login shells (fish/csh) instead of silently falling back to the shared socket path. Also drops the redundant cleanup round-trip (StreamLocalBindUnlink already clears stale sockets on bind), so a failing host no longer waits on a second SSH. - Remove the now-unused shellSingleQuoted helper. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- pi / Oh My Pi (OMP) coding agent integration (wxtsky#197) - per-user remote SSH socket isolation (wxtsky#193) - SSH tunnel ControlMaster=no fix for ssh exited(0) (wxtsky#190) - iTerm2 fullscreen/cross-Space jump fix (wxtsky#198) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…xtsky#199) (wxtsky#203) Cursor / Trae / Qoder / Factory etc. can hold several workspace windows at once. The native-app-bundle jump path only did app-level activation, so clicking a session card raised whichever window was most recently used rather than the one actually running the agent (wxtsky#199). Route these through activateIDEWindow, which AXRaises the window whose title contains the session's project folder and falls back to plain app activation when there's no cwd or no matching window — so single-window apps don't regress. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… (wxtsky#204) Remote install only handled the five built-in CLIs (claude/codex/codebuddy/ traecli/opencode) plus hermes; user-defined custom CLIs were never pushed to the remote, so a custom CLI running on the remote host got no hooks. Serialize the custom CLI configs into the remote install script and write their hooks alongside the built-ins via a new install_custom(). Limited to the claude/nested JSON-hook formats: their stdin carries hook_event_name, so the remote hook (codeisland-remote-hook.py) handles them with no --event flag. Cursor-style "flat" (needs --event) and the non-JSON formats (traecli/copilot/ kimi/kiroAgent/cline) are skipped remotely; they still work locally. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- IDE multi-window jump: match Cursor/Trae/Qoder/Factory workspace window by cwd (wxtsky#199) - install custom CLI hooks on SSH remote hosts (claude/nested formats) (wxtsky#192) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…) (wxtsky#207) macOS system SSH (LibreSSL) ignores StreamLocalBindUnlink=yes for -R remote socket forwarding, so a stale listen socket is left behind when a tunnel drops (sleep, network blip, disconnect). Reconnect then fails with "remote port forwarding failed", surfacing as ssh exited(255). Remove the stale socket with `rm -f` over a short-lived SSH before opening the tunnel. The cleanup runs off the main thread, so a blocking ConnectTimeout never freezes the menu-bar UI when the host is unreachable; the tunnel starts only after cleanup returns, so the -R bind never races the leftover socket. Closes wxtsky#206 Co-Authored-By: Stark-Lou <8287623+Stark-Lou@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drive Warp tab focus more reliably: - Replace blanket app activation with raiseAppWithoutQuickTerminal so we do not pop the Quick Terminal, and post the Cmd+digit keystroke to Warp's pid only once it is actually frontmost (retrying briefly otherwise). - Map tab index to Warp's shortcut semantics: Cmd+1..8 select tabs 1..8 and Cmd+9 selects the last tab. - Read Warp's SQLite without nolock=1 so WAL writes are honored (fresh tab state), match cwd case-insensitively on case-insensitive volumes, and expose tabCountInWindow / isActiveTab(cwd) for tab-level visibility. Verified against a live Warp database: tabIndexInWindow, tabCountInWindow, isActiveTab and case-insensitive matching all agree with the real layout. Co-Authored-By: rhinoc <9894617+rhinoc@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…on-prototype # Conflicts: # Info.plist # Sources/CodeIsland/ESP32StatePublisher.swift # Tests/CodeIslandCoreTests/JSONLTailerTests.swift # Tests/CodeIslandTests/AppStatePrimarySourceTests.swift
Owner
Author
|
Closing this fork-local PR because the branch is now submitted to the upstream repository: wxtsky#218 |
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
Adds the Code Island Buddy iPhone and Apple Watch experience for mirroring Code Island Mac agent sessions. The branch includes the Mac publishing bridge, iPhone app, Live Activity / Dynamic Island / StandBy surfaces, Apple Watch app and widget, and App Store release documentation for the first public iOS submission.
Changes
Added
Changed
Fixed
Motivation
Code Island now has a companion surface beyond the Mac: users can keep an eye on AI agent status, questions, tool activity, and recent session context from iPhone, Dynamic Island, StandBy, and Apple Watch. This also gives App Store reviewers a documented path to verify the iOS app with or without a paired Mac.
Testing
Commands run locally:
git diff --checkswift testswift test --filter AppleCompanionPayloadTestsswift test --filter 'AppleCompanionPayloadTests|AppStatePrimarySourceTests|AppStateQuestionFlowTests'scripts/check-companion-ui-regressions.shscripts/smoke-companion.shscripts/smoke-companion-ui.shRelease Notes
This PR does not include a signed Mac DMG artifact. The final Mac DMG should be produced by the maintainer after merge using the repository's Developer ID certificate, Apple notarization profile, Sparkle update signing key, and GitHub Release permissions.
Checklist