Skip to content

Add findmy ring, findmy phone and device aliases - #26

Merged
omarshahine merged 2 commits into
mainfrom
omarshahine/ring-and-aliases
Sep 7, 2026
Merged

omarshahine merged 2 commits into
mainfrom
omarshahine/ring-and-aliases

Conversation

@omarshahine

Copy link
Copy Markdown
Owner

Resurrects the two features from feat/locale-support (May 15) that never reached main. Main has since re-implemented that branch's locale, zoom and Space handling independently, so a merge conflicts in five files including an add/add on locale.go. These are ported fresh against today's code instead.

findmy ring <device|alias> [--confirm]

Scrolls the Devices sidebar until it OCRs the target row, clicks it, double-clicks the map to raise the pin's detail card, and clicks Play Sound. Nothing in that sequence reports completion, so every step retries: the double-click runs up to five times with growing delays, and the button is polled for rather than slept on. That ladder is the original branch's reliability work, kept as-is.

Dry run unless you pass --confirm. The original had phone ring immediately while ring was gated, so the safety depended on which name you typed. Here both go through the same gate, and phone is just the shorthand that defaults to the phone alias.

Aliases

~/.config/findmy-cli/aliases.json, resolved case-insensitively:

findmy alias phone "Omar's iPhone"
findmy ring phone --confirm
findmy phone --confirm          # same thing

Verification

Builds, go vet clean, 9 new tests pass (localized button labels, scale fallback, alias round-trip, and a corrupt aliases.json — which must never take the CLI down, since every command resolves aliases). The Swift helper compiles and advertises its new scroll subcommand. The alias surface is exercised end to end against a temp HOME: set, list, case-insensitive overwrite, delete, and both usage errors.

The ring path is not verified end-to-end. It drives the Find My UI, and verifying it means audibly ringing a device. Dry-run it with findmy ring "<device>" (no --confirm) — it will locate the button and print the coordinates it would have clicked.

Not ported

The branch's BetterDisplay virtual-display mode (depends on a third-party app) and six benchmark/optimization shell scripts.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SdvrUy3dukjMh6GNaUC9iq

Ported from feat/locale-support, a May branch that never landed. Main has
since re-implemented that branch's locale, zoom and Space handling
independently, so merging it conflicts in five files including an add/add on
locale.go. These two features are the part that was never rebuilt, so they are
ported fresh against today's code rather than merged.

`ring` scrolls the Devices sidebar until it OCRs the target row, clicks it,
double-clicks the map to raise the pin's detail card, and clicks "Play Sound".
None of those steps report completion, so each one retries: the double-click
runs up to five times with growing delays, and the button is polled for rather
than waited on. That retry ladder is what the original branch's reliability
work produced, and it is kept verbatim.

It is a **dry run unless you pass --confirm**. The original had `phone` ring
immediately while `ring` was gated, which meant the safety depended on which
name you typed; here both go through the same gate and `phone` is simply the
shorthand that defaults to the "phone" alias.

Aliases live in ~/.config/findmy-cli/aliases.json and resolve
case-insensitively, so `findmy ring phone --confirm` works once set.

Verified: builds, `go vet` clean, the Swift helper compiles and reports its new
`scroll` subcommand, and the alias surface is exercised end to end against a
temp HOME (set, list, case-insensitive overwrite, delete, and the two usage
errors). Nine new tests, including the localized button labels and a corrupt
aliases.json, which must never take the CLI down since every command resolves
aliases. The ring path itself drives the Find My UI and is NOT verified
end-to-end -- run `findmy ring <device>` without --confirm to dry-run it.

Not ported from that branch: the BetterDisplay virtual-display mode and six
benchmark/optimization shell scripts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdvrUy3dukjMh6GNaUC9iq
@greptile-apps

greptile-apps Bot commented Sep 7, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds device ringing through Find My UI automation, a phone shorthand, persistent aliases, and a continuous-scroll helper.

  • Adds dry-run and confirmed Play Sound workflows.
  • Adds case-insensitive alias storage and CLI management.
  • Extends the Swift helper with synthesized scrolling.
  • Adds localized-label, scale-fallback, and alias persistence tests.
  • The ring path currently has multiple ways to select or ring a device other than the requested one, and its display-scale calculation is invalidated by deleting the screenshot too early.

Confidence Score: 2/5

This PR is not safe to merge because confirmed ring operations can target a previously selected or ambiguously matched device, and device discovery uses an invalid display-scale fallback.

The existing-card fast path performs the protected action before associating the card with the requested device, substring matching can select another similarly named row, and the sidebar screenshot is deleted before its scale is measured.

Files Needing Attention: internal/findmy/ring.go, cmd/findmy/main.go, README.md

Important Files Changed

Filename Overview
internal/findmy/ring.go Implements the ring workflow, but stale-card reuse, first-substring matching, and remove-before-scale behavior can target the wrong device or fail lookup.
cmd/findmy/main.go Adds ring, phone, and alias commands; prior-application capture occurs too late and lookup errors bypass restoration.
internal/findmy/aliases.go Adds tolerant JSON-backed, case-insensitive alias persistence and resolution.
helpers/findmy-helper/main.swift Adds continuous phased scroll-event synthesis for the Find My sidebar.
internal/findmy/ring_test.go Covers label matching and alias persistence but does not exercise ambiguous device selection, stale cards, or command-level alias support.
README.md Documents ringing and aliases, including a broader alias-support claim than the implementation provides.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[findmy ring device or alias] --> B[Resolve alias]
  B --> C[Prepare Find My Devices tab]
  C --> D[Scroll and OCR sidebar]
  D --> E[Return matching DeviceHit]
  E --> F{Play Sound already visible?}
  F -- Yes --> G[Click or report existing button]
  F -- No --> H[Click matched sidebar row]
  H --> I[Double-click map and poll OCR]
  I --> J[Click or report Play Sound]
  G --> K[Switch tab and restore prior app]
  J --> K
Loading

Fix all with Greploop Fix All in Claude Code

Prompt To Fix All With AI
### Issue 1
internal/findmy/ring.go:211-216
**Stale Card Rings Wrong Device**

When another device’s detail card is already open, this fast path clicks its visible Play Sound button before selecting or verifying the requested device. A confirmed request for device B can therefore ring device A. Select the requested row before using the existing card, or verify that the card belongs to the requested device.

### Issue 2
internal/findmy/ring.go:171-174
**Partial Match Selects Wrong Device**

This returns the first substring match, so similarly named devices such as “Omar’s iPhone” and “Omar’s iPhone 15” can select and ring the wrong row depending on sidebar order. Prefer an exact normalized match across the visible frame before falling back to an explicitly handled partial match.

### Issue 3
internal/findmy/ring.go:163-166
**Screenshot Removed Before Scale Read**

The screenshot is removed before `imageScaleFor` reads it, so this calculation always uses the 2.0 fallback. On supported 1x or non-integer-scale displays, the resulting sidebar boundary is wrong and can admit detail-pane text as a device match or exclude valid sidebar rows, causing lookup failure or selection of the wrong coordinates.

### Issue 4
cmd/findmy/main.go:935-945
**Previous App Is Not Restored**

The prior application is remembered inside `RingDevice`, after `PrepareDevices` has already activated and foregrounded Find My, so successful runs record `com.apple.findmy` and restoration becomes a no-op. Device-search errors also exit before the inline cleanup. Capture the prior app before preparation and arrange cleanup for all post-activation returns to satisfy the documented promise to switch back.

### Issue 5
README.md:126
**Alias Support Is Overstated**

The documentation says any command that takes a device accepts aliases, but alias resolution is wired only into `ring` and `phone`. For example, `findmy device phone` still searches for the literal name `phone`, so users following this documentation get no matching device. Either resolve aliases in the existing device command paths or narrow this claim.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "Add `findmy ring`, `findmy phone` and de..." | Re-trigger Greptile

Comment thread internal/findmy/ring.go Outdated
Comment on lines +211 to +216
// Fast path: the card may already be open from a previous run.
if lines, err := captureAndOCR(w, tmpDir); err == nil {
if btn := findPlaySoundButton(lines); btn != nil {
return clickOrDryRun(w, btn, scale, dryRun)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Stale Card Rings Wrong Device

When another device’s detail card is already open, this fast path clicks its visible Play Sound button before selecting or verifying the requested device. A confirmed request for device B can therefore ring device A. Select the requested row before using the existing card, or verify that the card belongs to the requested device.

Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/findmy/ring.go
Line: 211-216

Comment:
**Stale Card Rings Wrong Device**

When another device’s detail card is already open, this fast path clicks its visible Play Sound button before selecting or verifying the requested device. A confirmed request for device B can therefore ring device A. Select the requested row before using the existing card, or verify that the card belongs to the requested device.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

Comment thread internal/findmy/ring.go Outdated
Comment on lines +171 to +174
txt := strings.TrimSpace(l.Text)
if strings.Contains(strings.ToLower(txt), targetLower) {
return &DeviceHit{Name: txt, NameX: l.X, NameY: l.Y}, nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Partial Match Selects Wrong Device

This returns the first substring match, so similarly named devices such as “Omar’s iPhone” and “Omar’s iPhone 15” can select and ring the wrong row depending on sidebar order. Prefer an exact normalized match across the visible frame before falling back to an explicitly handled partial match.

Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/findmy/ring.go
Line: 171-174

Comment:
**Partial Match Selects Wrong Device**

This returns the first substring match, so similarly named devices such as “Omar’s iPhone” and “Omar’s iPhone 15” can select and ring the wrong row depending on sidebar order. Prefer an exact normalized match across the visible frame before falling back to an explicitly handled partial match.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

Comment thread internal/findmy/ring.go Outdated
Comment on lines +163 to +166
return nil, fmt.Errorf("ocr: %w", err)
}

sidebarRightPx := int(340 * imageScaleFor(w, shot))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Screenshot Removed Before Scale Read

The screenshot is removed before imageScaleFor reads it, so this calculation always uses the 2.0 fallback. On supported 1x or non-integer-scale displays, the resulting sidebar boundary is wrong and can admit detail-pane text as a device match or exclude valid sidebar rows, causing lookup failure or selection of the wrong coordinates.

Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/findmy/ring.go
Line: 163-166

Comment:
**Screenshot Removed Before Scale Read**

The screenshot is removed before `imageScaleFor` reads it, so this calculation always uses the 2.0 fallback. On supported 1x or non-integer-scale displays, the resulting sidebar boundary is wrong and can admit detail-pane text as a device match or exclude valid sidebar rows, causing lookup failure or selection of the wrong coordinates.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

Comment thread cmd/findmy/main.go Outdated
Comment on lines +935 to +945
w, err := findmy.PrepareDevices()
must(err)

match, err := findmy.FindDeviceByScroll(w, resolved, tmpDir())
must(err)
fmt.Fprintf(os.Stderr, "Found: %s\n", match.Name)

err = findmy.RingDevice(w, match, tmpDir(), !opts.confirm)
_ = findmy.SwitchTab(findmy.GetAppStrings().PeopleTab)
findmy.RestoreUserSpace()
must(err)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Previous App Is Not Restored

The prior application is remembered inside RingDevice, after PrepareDevices has already activated and foregrounded Find My, so successful runs record com.apple.findmy and restoration becomes a no-op. Device-search errors also exit before the inline cleanup. Capture the prior app before preparation and arrange cleanup for all post-activation returns to satisfy the documented promise to switch back.

Prompt To Fix With AI
This is a comment left during a code review.
Path: cmd/findmy/main.go
Line: 935-945

Comment:
**Previous App Is Not Restored**

The prior application is remembered inside `RingDevice`, after `PrepareDevices` has already activated and foregrounded Find My, so successful runs record `com.apple.findmy` and restoration becomes a no-op. Device-search errors also exit before the inline cleanup. Capture the prior app before preparation and arrange cleanup for all post-activation returns to satisfy the documented promise to switch back.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

Comment thread README.md
findmy alias phone "Omar's iPhone" # set
findmy alias # list
findmy alias --delete phone # remove
findmy ring phone --confirm # any command that takes a device accepts one

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Alias Support Is Overstated

The documentation says any command that takes a device accepts aliases, but alias resolution is wired only into ring and phone. For example, findmy device phone still searches for the literal name phone, so users following this documentation get no matching device. Either resolve aliases in the existing device command paths or narrow this claim.

Prompt To Fix With AI
This is a comment left during a code review.
Path: README.md
Line: 126

Comment:
**Alias Support Is Overstated**

The documentation says any command that takes a device accepts aliases, but alias resolution is wired only into `ring` and `phone`. For example, `findmy device phone` still searches for the literal name `phone`, so users following this documentation get no matching device. Either resolve aliases in the existing device command paths or narrow this claim.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code

All four Greptile raised are real; three would ring or select the wrong thing.

1. The scale was read from a screenshot I had already deleted, so
   `imageScaleFor` always fell back to 2.0 and the sidebar boundary was wrong
   on any display that is not 2x. Read the scale before OCR removes the file,
   and only once per search. This one was mine, not the original branch's.

2. Device matching returned the first substring hit, so "Omar's iPhone" could
   select "Omar's iPhone 15" purely on sidebar order. An exact name now wins
   over a substring anywhere in the frame, via `matchSidebarDevice`.

3. The fast path clicked whatever "Play Sound" button was already on screen. A
   card left open for another device shows its own button, so a confirmed ring
   for device B could ring device A. `cardShowsDevice` now requires the open
   card -- text to the RIGHT of the sidebar, since the sidebar always contains
   the name once we have scrolled to it -- to name the requested device.

4. The frontmost app was recorded inside RingDevice, after PrepareDevices had
   already activated Find My, so it recorded Find My and the restore was a
   no-op. It is captured before preparation now. The restore also sat on the
   happy path only; it is a defer, and `ring` returns its errors instead of
   calling `must`, because os.Exit does not run deferred functions.

Seven more tests. Verified they bite: reverting the match to substring-first
and stubbing the card check to true fails exactly the three tests that cover
those behaviours.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdvrUy3dukjMh6GNaUC9iq
@omarshahine
omarshahine merged commit 1206b05 into main Sep 7, 2026
1 check passed
@omarshahine
omarshahine deleted the omarshahine/ring-and-aliases branch September 7, 2026 01:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant