Skip to content

AgentPlay Companion: native menu-bar app with Pip, plan limits, fixes and your Claude setup - #53

Closed
hsrambo07 wants to merge 106 commits into
mainfrom
feat/companion-macos
Closed

hsrambo07 wants to merge 106 commits into
mainfrom
feat/companion-macos

Conversation

@hsrambo07

@hsrambo07 hsrambo07 commented Oct 28, 2026 •

Copy link
Copy Markdown

AgentPlay Companion: a native macOS menu-bar app for your coding agents

Pip lives in the menu bar and tells you what your agents are doing, how much of your Claude plan is left, what's going wrong and how to fix it, and what your own Claude Code setup is actually doing for you. Everything is read on your Mac; nothing is uploaded.

What's in the app

Area What it does
Menu: Usage tab Pip's one-line status, plan limits as full-width bars (session and weekly) with a weekday-weighted forecast ("Runs out Sat at your pace. Keep to 11% a day"), account switcher, live sessions with what each is doing, the best next fix, today and 30-day spend
Menu: Fixes tab Every next step, plain words first: failing hooks (with a one-click, previewed, undoable fix), tools that keep failing, tools flooding context, sessions running out of room, idle setup; Claude's suggestions (opt-in); Run Health; How Your Prompts Land
Menu: Tools tab Your skills, subagents, MCP servers and hooks, measured from every session map: calls, failures, tokens a session, sessions that changed nothing, one next step. Turn each off or on from the menu. Unused setup listed with descriptions
Turn things off and on Claude Code's own switches where they exist (skillOverrides, deniedMcpServers); subagents and single hooks set aside in ~/.agentplay and restored exactly. Confirmed first, write-locked, settings.json backed up
How Your Prompts Land Correction, frustration, interruption and refusal rates from what you type, plus findings from your sessions (opening detail vs follow-ups, spend on retries and re-reads, blind edits), each with a next step
Suggestions from Claude Off by default. Once a day, Haiku through your own Claude Code (no tools, no hooks, no MCP, no session saved) reads counts and skill names, never prompts or code; replies are filtered (no suggesting what you already use, no repeats, must cite a number)
Alerts AgentPlay's own animated Pip banner (needs-you, finished, failing, limit), macOS notifications, or both; optional walking Pip while agents work
Insights window One page: Run Health, Do this next, What changed (applied fixes measured before/after), Your setup, Autotune, prompts, usage
Onboarding Five steps told by Pip: wake, read your sessions, what it learnt, practice the alert, move in
Settings General, Limits, Notifications, Privacy
Design Entelligence sage accent; menu glass tinted solid (backdrop bleed measured 0.1/255, from 60); SF Pro at macOS sizes; Light and Dark
Distribution Sparkle 2 updates from GitHub Releases (on once a signing key is set), release job in CI, team preview installer script

Core (Python) additions

Module Purpose
companion.py Snapshot for the app; fix entries in plain words; coach and setup commands
impact.py Per-tool measurement from session maps; your sessions vs one-prompt runs; tips
setup_inventory.py Installed skills, subagents, commands, MCP servers, hooks; descriptions; what drives what
setup_control.py Turning setup off and on, with backups and exact undo
prompting.py Prompting findings from the session index
coach.py The opt-in daily Haiku suggestions
usage.py, health.py Usage report additions; checks must test, lint, type-check or build

Testing

  • ./scripts/companion-test.sh: Python 1,447 + companion 90, Swift 96, fixtures regenerate identically, repo guards, bundle self-test. All pass.
  • Real-screen checks (dev build): --contrast-check (menu, every Tools and Fixes submenu, Light and Dark), --banner-check (Pip banner and walker), --render-live, --notify-check.
  • Real-path checks (dev build, real data): --switch-check <item> confirm|cancel turns a skill, MCP server, subagent or hook off through the menu, finds it under Turned Off, turns it on from there and checks settings.json and the agents folder are byte-identical; --menu-check fire [coach] walks every row and submenu, fires copy, Insights, the Pip banner, tab switching in an open menu, Apply This Fix… (preview, Cancel) and Ask Claude, and checks every tab fits the screen.
  • Privacy: tests assert no prompt text, arguments, paths or secrets reach the snapshot, the setup inventory or the Claude request. See docs/companion/PRIVACY.md.

Before release

  • Apple Developer ID and notarization (the app is ad-hoc signed today; scripts/companion-install.sh is the team preview path).
  • Sparkle EdDSA keys and the GitHub secrets listed in docs/companion/RELEASE_CHECKLIST.md.

🤖 Generated with Claude Code

hsrambo07 and others added 30 commits October 23, 2026 21:31
agentplay companion snapshot --json prints one versioned document
(agentplay.companion/v1) built from the local store: recent runs with
their last recorded events, process-backed activity evidence, context
and plan data as typed DataPoints with source and freshness, repeated
failure candidates, and one cross-session Learn card.

No prompt text, titles, transcript paths, arguments, remotes or PR URLs.
No listener: the companion runs it as a child process.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AgentPlayCore decodes agentplay.companion/v1 tolerantly (unknown values
degrade to unavailable), builds viewer deep links with strict encoding,
runs the Python core as an isolated child process with a minimal
environment, and decides notifications: off by default, first-install
watermark, dedupe, pattern cooldown, hourly budget, quiet hours in the
current time zone, snooze, mutes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SwiftUI MenuBarExtra with Now, Limits and Learn, the three prototype
directions behind a dev switch, five-step onboarding, Settings, local
notifications, FSEvents-driven refresh and an app-owned viewer.

scripts/companion-build.sh assembles AgentPlay Companion.app with a
pruned python-build-standalone runtime and the agentplay package
inside (44 MB), precompiled so nothing writes into the signed bundle,
and signs it inside out.

The core now reports how far a map lags its transcript, and a call
with no result counts as running only on a live, current map.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Launch testing the real bundle found three problems, fixed here:
- every file event restarted the debounce, so an agent writing every
  second starved the refresh; a queued refresh is now never pushed back
- the gap between refreshes scales with the measured cost (4x the last
  refresh, 3x more in Low Power Mode): a 34 MB live transcript takes
  about 10 s to rebuild
- SIGTERM skipped cleanup; the snapshot child and the viewer are now
  stopped synchronously, and a viewer left by a crash is reaped on the
  next launch, only if it is this app's own Python

Also: keep cards on screen during a rescan, and explain a scan stalled
on a macOS Files and Folders prompt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- docs/companion/screens: every state and prototype, light and dark,
  rendered from the real views over synthetic fixtures
- docs/companion/media: 10 to 14 s keyframe clips per direction, a v1
  tour, and the proof clip ending on the real viewer at the deep link
- companion-dmg.sh: always a release build; signs, notarizes and staples
  only with credentials, otherwise prints BLOCKED and exits 2
- alerts carry firstAt, so "Since" means the first failure
- PrefsStore uses .standard inside the bundle (macOS refuses the app's
  own id as a suite name)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nstall, release

Also: a macOS CI job (fixtures must regenerate identically, Swift
tests), and an uninstall message that only claims what it removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Claude limits

Replace the tabbed popover with one native menu column on the system
material: system colours and the accent, hover and arrow-key highlight,
Return, Cmd-R/O/,/Q and right-click actions. The status item draws two
template bars (5-hour and weekly) with a dot when a session needs you.

- Claude and Codex 5-hour and weekly meters with reset countdowns, tinted
  by pace, with an "At this pace, runs out in" line.
- Usage estimated from local maps (python/src/agentplay/usage.py): current
  5-hour block, today, 7 and 30 days, by model and project, 14-day chart.
  Unpriced models are counted as unpriced, never $0.
- Opt-in live Claude limits through the status line bridge, with the
  exact settings.json change shown first, the previous status line kept,
  and a clean undo.
- Conflicting Codex plans are a structured field and say so in the menu.
- Sessions sit above usage so expanding usage never hides them; the menu
  is as tall as the screen allows.
- Fix two sessions sharing one "context near full" alert key.
- Renderer falls back to the bundled fixtures; screens regenerated.
- Test script reuses one build dir and passes the macro plugin path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…k with it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The menu was a SwiftUI window imitating a menu, with hand-built hover,
keys and expanding rows. It is now a real NSMenu from an NSStatusItem,
like CodexBar, Tailscale and Apple's own extras: hover, arrow keys,
submenus, dismissal and key equivalents are the system's. Every
clickable row is a native item; the provider cards, usage summary and
usage chart are display-only hosted views.

- Order follows attention: Needs You first, then plan limits, sessions,
  usage, actions. Sessions are titled by project with the agent and
  activity as the subtitle.
- Limit cards: name, plan, freshness and a weekly pace verdict, then one
  line and a thin bar per window. Colour only when ahead of pace.
- Usage is two lines; the chart and breakdowns moved into a Usage
  Details submenu, recent errors into a Recent Errors submenu.
- Clicking a session brings its terminal app forward (walks up from the
  agent's pid); Option-click opens the replay. The snapshot now carries
  the live session pid.
- Settings uses toolbar tabs (NSTabViewController) with grouped forms.
- Renders capture the real NSMenu and the real Settings window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… activation, remember the Settings pane

Also keep hidden alternates from widening the menu and give Usage Details
an icon to match Recent Errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion

- The app reads Claude Code's live-process records (~/.claude/sessions)
  itself and watches that folder with a 0.3 s latency, so a session that
  starts waiting shows in the menu and icon without waiting for a scan.
  Same privacy rule as the core: folder name of cwd only.
- New "Session needs you" alert, offered in onboarding: skipped when the
  session's terminal is already frontmost, cleared once it moves on,
  click brings the terminal forward, Mute This Session on the banner.
  Names the project only when repo names in notifications are allowed.
- The session tooltip names the app a click will bring forward; the
  self-test lists what each live session resolves to.
- Settings General trimmed to real settings; plural fixes; renders
  capture onboarding as a real window in both appearances.
- Removed code only the old popover used (pulse, pin, copy actions).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hat can't pass for yours

Usage was wrong: today read about a quarter of what the transcripts held.
- Subagent sessions were skipped, and they were most of today's work.
- The biggest session had passed the build's 512 MB limit, so its map
  froze and stopped counting.
- The first-run "full scan" passed --refresh with no window, so the core
  indexed two days, never 30. There is now --refresh-days (0: all).
- Resumed sessions repeat earlier requests across files.

Claude usage now comes from Claude Code's raw transcripts, read
incrementally: one request per message id and request id, the finished
line's tokens and the first line's time; other agents from maps, with
map repeats of a counted request dropped. Checked against an independent
count: within 0.6% for today, 7 and 30 days.

Sample data: the menu opens with "Sample Data, Not Yours" and Show My
Data, cards say "Sample data" instead of an update time, and the
sample's live sessions are rebased like everything else (it said
"Waiting 19h"). Codex on a plan without windows says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… writes where the app reads

- Claude Code started from an editor's agent panel (entrypoint sdk-ts, as
  Zed's claude-acp does) has no statusline, so the bridge can never fire.
  The card now says so and names the editor, instead of waiting forever.
- When Claude Code is refused for a plan limit it records which window and
  when it resets, from any entrypoint: that now shows as a full window with
  its reset time.
- The bridge script used $AGENTPLAY_HOME, which Claude Code doesn't have:
  the folder is fixed at install, and installed scripts are refreshed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ent panels

Claude Code run from an editor's agent panel (Zed's claude-acp and other
Agent SDK hosts) has no statusline, so it never reports plan usage on
the Mac. Settings > Live Limits now has an opt-in switch that reads the
login Claude Code keeps in the Keychain through /usr/bin/security (macOS
asks once) and asks api.anthropic.com/api/oauth/usage for the 5-hour and
weekly windows, as CodexBar and the other usage menus do. Every 5
minutes and on menu open, backing off on 429; the token is never stored
or renewed. Account rows replace the other Claude rows while fresh, and
failures say what to do. The menu offers it when every Claude session
comes from an editor. Privacy and provenance docs say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…stale one too

Claude Code can leave an old "Claude Code-credentials" item (account
"unknown") next to the live one under the user's name, and keeps
"Claude Code-credentials-<hash>" items for other config folders.
security -s returned the first match, the stale one, so the app said no
login was found. Items are now listed by attributes (no prompt), the
user's own account and then the newest are tried in turn, each read with
an explicit account.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…version lookup can't deadlock

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…agents' state

- Pip fills with the 5-hour session and stands on the week; its pose is
  what matters now: waving (needs you), hourglass (window spent), X eyes
  (failing command), arms up (finished), sweat (ahead of pace), typing
  (working, frame steps on real agent writes), asleep. Pixel-grid
  template art, legible at 18 pt, light and dark.
- When something happens a short line shows beside Pip for 8 s, capped
  near 110 pt for notched menu bars. Clicking it acts: go to the session's
  terminal, open the replay at the failure, or the usage page. Otherwise
  clicks open the menu; right-click always does; Option-click opens
  AgentPlay. The status item now opens its menu itself to see clicks.
- The menu opens with Pip large and one sentence about now.
- Needs-you notifications get Go to Terminal and Snooze 15 Minutes.
- No timer while Pip is still; nothing moves under Reduce Motion or Low
  Power Mode. Pip and the line each have a switch in Settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…draw red, live renders use real settings

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ssions keep updating

Subagent transcripts count toward a session's size, and a busy session passed
512 MB: its map froze and silently stopped updating. The reader streams, so a
session just past 512 MB builds in about 10 s with a small memory peak.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he run went

Fires when a live session goes from working to idle after at least two
minutes of work: how long, tool calls, errors and the last step, from the
map. Go to Terminal and Open Replay on the banner. Same snooze, quiet
hours, presenting and terminal-in-front rules as Session needs you; the
repo name only when allowed. Allowance-low copy now names Claude sources.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After onboarding on another store, or with ~/.agentplay cleared, launch ran
the incremental refresh and indexed two days. Now it scans everything.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Email and organization from oauthAccount in ~/.claude.json, local only,
on the Claude card.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p way

Save the signed-in account, then switch with one click: the outgoing
login is saved first (Claude Code rotates tokens), the chosen one is
written to Claude Code's Keychain item keeping this Mac's MCP logins,
and only oauthAccount in ~/.claude.json changes, after a backup. Through
/usr/bin/security with the login on stdin, as Claude Code itself writes
it, so access lists and prompts don't change. Refused mid-step or on an
expired login. The saved copy follows token rotations; each account
shows its last-seen usage; remembering new sign-ins is opt-in.
End-to-end tested on throwaway Keychain items.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… bar's wallpaper tint

Opening the menu ourselves from the status button (for Pip's clicks) made it
take the menu bar's appearance, which on macOS 26 follows the wallpaper:
a light wallpaper gave a light menu in Dark Mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add Another Account saves the account you're on, opens claude auth login
in Terminal (a .command file, so no automation permission), and saves the
new account the moment Claude Code is signed in as someone else. No
dialog, no /logout, nothing to come back for.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The placeholder SwiftUI Settings scene opened as an empty window when the
app was launched again. Every window is AppKit already, so the SwiftUI app
shell is gone: an AppKit entry point, a hidden main menu for Command-comma,
Command-W, Command-Q and the Edit commands, and relaunching shows nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ew to nine

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ld raise it, and errors in full

Uses the Run health report merged from main (#19): the snapshot carries the
last 30 days' score against the 30 before, its five parts, and up to six
fixes that name the command, tool, hook or server behind them, cached 15
minutes. The menu shows Run Health with its weakest parts, up to three
fixes and three errors, each with its details one level down and actions:
preview and apply in AgentPlay, open an example session, open the replay at
the failing step for every run a pattern failed in, copy, mute or hide.
Pattern evidence now carries the project. Providers with nothing to show are
left out so the menu fits a laptop screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hsrambo07 and others added 21 commits October 27, 2026 19:42
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/companion-install.sh takes a DMG file or URL, installs the app,
clears the quarantine flag that blocks an un-notarized app, and opens it.
INSTALL.md says how one person builds and shares the DMG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…notifications, quieter Insights

Session above Weekly whatever resets first; every meter bar the same
height; the forecast as one caption-size line. The Insights rows have
their hover cards again (run health as bars, the fix with Apply, the
steering numbers) with Open in Insights at the bottom. Notifications
carry Pip in the matching pose instead of the app icon. The Insights
window's sections are rows, not posters: fixes with one Apply, applied
changes as before → since lines, steering as a four-line table, usage as
three totals and two short lists.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Installed copies check the latest GitHub release's appcast once a day
and offer the update; nothing installs without a click, every update is
verified against the EdDSA key built into the app, and the check sends
no system profile. Off in Settings, Check for Updates on right-click.
Pushing a vX.Y.Z tag now builds, signs, notarizes and staples the DMG,
writes the appcast and attaches both to the release; the job skips with
a notice until the Apple and Sparkle secrets are set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…a Pip header without the gap

Plan bars are a fixed width beside a short reset label, so session and
week compare at a glance; the forecast is one readable line. Pointing at
a live session shows what it's doing, what it has cost, its tokens and
context, files touched, commits, your messages, errors and where its
effort goes, then Go to <terminal>; Open Replay is gone. The Pip header
is two lines sized to fit: what's going on and the one thing that matters
now; clicking Pip makes it say something cheeky about your numbers. The
core adds each session's cost, tokens, files, commits, PRs, turns and
subagents from its map.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lists the skills, subagents, slash commands, MCP servers and hooks Claude
Code loads on this Mac, with 30-day use counts read from the transcripts.
Only names that match something installed are kept; no prompt text,
arguments or paths. Shows as a Your Setup row in the menu with a hover card
and as a section in Insights, with unused items grouped since their
descriptions load into every session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lls help; suggestions from Claude

- Menu: a tab strip under Pip switches in place, the menu stays open. Measured with
  the film harness: top edge fixed, no scroll, one width for every tab.
- Improve: every fix, Claude's suggestions, what changed, run health, how you steer.
- Setup: what you use with its count and how sessions went with it, what sits unused
  by kind with a way to its folder, your hooks.
- Whether a skill, subagent or MCP server helps: sessions with it against sessions
  without, on corrections and failed tool calls. A verdict only with 5 sessions a side
  and a 3-point gap; otherwise "too few" or "no clear difference".
- Suggestions from Claude, off by default: once a day, Haiku through the user's own
  Claude Code with tools, settings, MCP and session saving off. Counts and skill names
  only. Checked before kept. Documented in PRIVACY.md and tested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts an hour; tab width measured once per snapshot

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ecast

The menu's Liquid Glass let about 60/255 of whatever was behind it through, so
text sat on whatever page was under the menu. The menu and submenu glass is now
tinted to the window background, with a solid backing in Dark: measured 0.1/255
over a white page against a black one, Light and Dark (--contrast-check, real
screen captures).

The weekly bar was red at 31% from a straight-line pace while the forecast under
it said something else. The bar follows the forecast: orange when it runs out
before the reset, red only from 90%. Numbers stay in plain text; the forecast
line gets one orange mark. Pip no longer repeats the forecast the card shows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ery session map

The Setup tab judged figma on 3 sessions from transcript text and showed one MCP
server. The maps AgentPlay builds at scan time record every step's tool, failure,
output size and time, for all 1,800 sessions. impact.py adds them up per item:
calls, failed calls, tokens its results put into context, time waited, the
sessions it ran in and your latest ones by id (click to copy claude --resume).

- Your sessions (2+ messages) apart from one-prompt runs (scripts, bots, claude -p):
  here 29 yours against 1,729 automated.
- claude.ai connectors and built-in subagents are listed with what they did.
- One next step per item from checkable rules: failing (3+ failed, 20%+), heavy
  (40k+ tokens a session), helps or watch (your sessions with it vs without, 5 a side).
- Hosted cards for the split and hooks, so they aren't greyed out; tabs in the
  accent colour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s headers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…exBar draw them

Each window is the name, a bar across the card, then "54% used" on the left and
"Resets in 1h 7m" on the right. Every bar spans the card, so session and week
always compare at a glance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ign-in

After a sign-in that worked, a detached helper asks Terminal to close the window
on the script's own terminal device. A failed sign-in keeps the window open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ures

Improve listed only failing hooks and commands, in Claude Code's own terms. Now:

- Every fix and tip is a title, what happened, what to do, and the technical names
  last, small. Hook events read as moments ("when a session starts", "when you send a
  message"); a hook fix is named for its script, with its real count.
- New tips from the session maps: a tool that keeps failing, a tool filling your
  context, sessions that run out of room, unused setup (only when it costs 2k+
  tokens a session), and a tool worth using more (your sessions with vs without).
  Each row's subtitle is the number behind it.
- A check is something that tests, lints, type-checks or builds; mkdir, echo or a
  bare npx or python is never offered as one.
- Pip, the Improve badge and the list say the same count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ng what you already use

- A skill a hook runs (validator-nudge.sh runs the validator) or whose subagents ran
  (judge) counts as in use. Tools lists your own first, with what runs it.
- Claude gets what's in use and what's already on screen, must quote a number for
  each suggestion, and may return none. Suggestions to use what you already use, fixes,
  and restatements of a line on screen are dropped before they're kept.
- Hooks read as moments: "When you send a message · validator-nudge.sh".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tool card was drawn 340 pt wide in a 300 pt row, so both edges were clipped,
and it grew with the menu, so its text was measured on one line and drawn on two,
clipping top and bottom. It now has a 340 pt row and a fixed 340 pt width; the
other cards size to their full height. Live renders now pop the first submenus of
Tools and Fixes as real menu windows and print each row's size against what it
needs. Also: "Its one call worked", and no "Into context" line under 1k tokens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…es for design files

--export-vectors <dir> writes each SF Symbol the app shows as a 4x mask PNG and
each Pip pose as a 12x PNG, used to rebuild every state in Figma as editable layers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…elligence sage accent

- Tool cards answer four things: what it is (its own description, or what built-ins and
  connectors are), what a session with it costs, how it fails, and how many of your
  sessions with it changed no file, made no commit and opened no PR. A skill or
  subagent whose sessions mostly change nothing is flagged as idle.
- Built-in subagents are Claude's own; no more "use general-purpose more".
- Hooks are rows that say when they run, what script, and whether they've failed.
- Unused subagents and skills are listed with their descriptions and what they are.
- How You Steer is now How Your Prompts Land: plain rates plus findings from your own
  sessions (opening detail vs follow-ups, spend on retries and re-reads, blind edits),
  each with a next step.
- Sessions split reads "You, typing" and "Scripts and bots".
- Accent is Entelligence sage (#B8D395 dark, #5E773C light) with dark text on it in dark
  mode, distinct from the system green that means good.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d on from Tools

Claude Code's own switches where it has them: skillOverrides "off" for a skill, a
deniedMcpServers entry for an MCP server or claude.ai connector (by its display name),
both in ~/.claude/settings.json. Subagents and single hooks have no switch, so AgentPlay
sets them aside in ~/.agentplay/set-aside and puts them back exactly. Each change is
confirmed first with what it writes, takes the write lock, and backs up settings.json.
Turned-off things stay listed under Turned Off, with Turn On. Plugin skills are left to
/plugin. Hooks your parallel-session and validator workflow depend on get a stronger
warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Pip while agents work

Notifications never showed because "Send Sample" posted without ever asking macOS for
permission, and macOS drops those silently (checked with --notify-check: once asked,
permission is granted and posts go through). Alerts now default to AgentPlay's own
banner: Pip at 2.5x, animated in the alert's pose, top-right where notifications sit,
clickable to go to the session. It needs no permission. Settings → Notifications picks
Pip on screen, macOS notifications, or both, and every sample asks permission first
when macOS is part of it. An optional strip under the menu bar shows Pip walking while
agents work. --banner-check captures the banner and strip on the real screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@entelligence-ai-pr-reviews

entelligence-ai-pr-reviews Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

EntelligenceAI PR Summary

Adds AgentPlay Companion, a native macOS menu-bar app that uses Pip to show agent activity, plan usage, failures, and setup insights from local AgentPlay data. It adds live Claude statusline and optional account-usage integrations, account switching, notifications, and links to sessions or fixes; changes are previewed before applying and can be undone. Fixtures and Swift core tests are added, along with macOS CI and a release workflow for building, signing, notarizing, and publishing updates when the required secrets are configured.

flowchart TD
    classDef newBehavior fill:#dcfce7,stroke:#16a34a,color:#14532d;
    Src["Local agent maps and transcripts"] --> Core["AgentPlay snapshot CLI"]
    Live["Claude live session records"] --> Model["Companion model"]
    Account["Optional Claude account usage"] --> Model
    Core --> Model
    Model --> Menu["Menu bar and Pip"]
    Model --> Alerts["Alerts and notifications"]
    Menu --> Actions["Session replay, terminal, and fix actions"]
    class Src,Core,Live,Account,Model,Menu,Alerts,Actions newBehavior;
Loading

🟢 Green = new or changed in this PR


Review Scorecard

Dimension Rating Basis
Code Quality ●●○○○ 2/5 — Poor 3 significant finding(s) — reviewer rated the code Poor
Blast Radius High here is referenced in 26 other file(s), touches auth/access-control, infra / deploy / config, 45 file(s) / ~18777 line(s) changed (size only — not a blast signal)
Merge Confidence ●●○○○ 2/5 — Changes Needed code quality 2/5 × High blast radius
Test Coverage — unavailable dependencies failed to install
Security Scan No findings on changed lines SonarQube · Semgrep · TruffleHog on 9 changed files

Issues found:

  • Significant python/src/agentplay/setup_inventory.py — Honor the selected hook matcher when switching
  • Significant apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/MenuItems.swift — Match every failing hook fix for the event
  • Significant apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/StatusMenu.swift — Keep unsupported commands out of the switch action
  • Unresolved .github/workflows/release.yml — Check every credential before enabling the job (unresolved from an earlier review)
  • Unresolved apps/macos/AgentPlayCompanion/Resources/claude-statusline-bridge.sh — Run the previous statusline when snapshot capture fails (unresolved from an earlier review)
  • Unresolved apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/Model.swift — Wait for consent before fetching Claude account usage (unresolved from an earlier review)
  • …plus 8 more, posted as inline comment(s)

Fix before merge; code quality rated Poor (2/5): 3 significant findings in 3 files, the most severe in python/src/agentplay/setup_inventory.py (Honor the selected hook matcher when switching). 11 comments from earlier reviews are still open.

Need to merge before these are addressed? Anyone with write access can comment @entelligence /approve to approve it now. @entelligence help lists every command.

Evaluated against
  • 9/9 changed files reviewed
  • criteria: correctness, security & access control, robustness & error handling, concurrency & data integrity, repo conventions / steering docs
  • steering docs: none found in repo
  • PR checks: unit tests (unavailable) · security scan (SonarQube, Semgrep, TruffleHog on 9 changed files)
Files requiring special attention
  • python/src/agentplay/setup_inventory.py
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/MenuItems.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/StatusMenu.swift

@entelligence-ai-pr-reviews

entelligence-ai-pr-reviews Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Walkthrough

Introduces AgentPlay Companion, a macOS menu-bar app for monitoring local agent sessions, usage limits, insights, alerts, and Claude account workflows. Adds the Swift core and UI, fixture-driven development and test tooling, privacy-conscious local integrations, and CI/release automation for signed and notarized distribution.

Changes

Files

  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • .gitignore
  • Makefile
  • apps/macos/AgentPlayCompanion/.gitignore
  • apps/macos/AgentPlayCompanion/Package.swift
  • apps/macos/AgentPlayCompanion/Package.resolved

Adds companion development, build, packaging, and test targets; configures macOS CI to verify generated fixtures and run Swift tests; and adds a secrets-gated release workflow for signing, notarization, Sparkle appcast generation, and release uploads. Ignores generated build and capture artifacts and defines the macOS 14 Swift package with Sparkle pinned.

Files

  • apps/macos/AgentPlayCompanion/Fixtures/conflicting-plans.json
  • apps/macos/AgentPlayCompanion/Fixtures/detected-idle.json
  • apps/macos/AgentPlayCompanion/Fixtures/many-sessions.json
  • apps/macos/AgentPlayCompanion/Fixtures/no-agents.json
  • apps/macos/AgentPlayCompanion/Fixtures/partial-unavailable.json
  • apps/macos/AgentPlayCompanion/Fixtures/stale-quota.json
  • apps/macos/AgentPlayCompanion/Fixtures/two-active.json

Adds versioned sample snapshots for multi-agent activity, idle and no-data states, partial source failures, stale quotas, conflicting plans, and concurrent sessions. These fixtures support UI previews, tests, and fixture-sync checks without changing runtime behavior.

Files

  • apps/macos/AgentPlayCompanion/Resources/Companion.entitlements
  • apps/macos/AgentPlayCompanion/Resources/Info.plist
  • apps/macos/AgentPlayCompanion/Resources/PRIVACY.txt
  • apps/macos/AgentPlayCompanion/Resources/claude-statusline-bridge.sh

Defines the menu-bar app bundle, update settings, hardened-runtime entitlements, and bundled privacy notice. Adds a Claude status-line bridge that stores local usage input by session, prunes old records, and forwards input to any previously configured status-line command.

Files

  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/App.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/Model.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/MenuItems.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/PipIcon.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/PipBanner.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/Theme.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/System.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/Updater.swift

Implements the menu-bar app lifecycle and observable model, data-driven Usage/Fixes/Tools menus, Pip status icons and banners, adaptive theme colors, filesystem and notification integrations, and packaged-build Sparkle updates. The model coordinates snapshots, live-session monitoring, refreshes, preferences, notifications, viewer navigation, and companion actions.

Files

  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/InsightsWindow.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/Onboarding.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/Settings.swift

Adds the insights dashboard, five-step first-run onboarding, and pane-based settings window. These views surface run health, fixes, learning and usage data, account and notification controls, privacy options, and actions such as applying or undoing changes.

Files

  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/Film.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/FilmRender.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/Renderer.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/SelfTest.swift

Adds development-only rendering, filming, and self-test workflows for UI states, launch-film assets, menus, app interactions, runtime health, viewer behavior, and setup controls. Fixture loading supports deterministic rendering by rebasing snapshot times; generated captures are written to the selected output directory.

Files

  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/AlertEngine.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/Bridge.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/ClaudeAccount.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/ClaudeBridge.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/ClaudeSwitch.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/Forecast.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/Links.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/LiveRecords.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/Pace.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/Pip.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/Prefs.swift
  • apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/Snapshot.swift

Adds the pure Swift companion core for snapshot decoding, session and process records, Pip state, provider pace and forecasts, redacted formatting and viewer links, persisted preferences, and alert selection/throttling. It also provides the Python runtime and viewer bridge, Claude usage retrieval, opt-in status-line integration, and guarded Claude account storage and switching.

Files

  • apps/macos/AgentPlayCompanion/Tests/AgentPlayCoreTests/AlertTests.swift
  • apps/macos/AgentPlayCompanion/Tests/AgentPlayCoreTests/ClaudeAccountTests.swift
  • apps/macos/AgentPlayCompanion/Tests/AgentPlayCoreTests/ClaudeSwitchTests.swift
  • apps/macos/AgentPlayCompanion/Tests/AgentPlayCoreTests/ForecastTests.swift
  • apps/macos/AgentPlayCompanion/Tests/AgentPlayCoreTests/LiveRecordsTests.swift
  • apps/macos/AgentPlayCompanion/Tests/AgentPlayCoreTests/PipTests.swift
  • apps/macos/AgentPlayCompanion/Tests/AgentPlayCoreTests/SnapshotTests.swift

Adds Swift Testing coverage for alert and preference behavior, Claude credentials and account switching, forecasts, live-record handling and redaction, Pip decisions, snapshot decoding, viewer safety, and process-environment restrictions.

permissions:
contents: write # attach the DMG and appcast to the release
env:
HAS_SECRETS: ${{ secrets.DEVELOPER_ID_P12 != '' && secrets.SPARKLE_PRIVATE_KEY != '' }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MAJOR RELEASE_CONFIG Check every credential before enabling the job

If only DEVELOPER_ID_P12 and SPARKLE_PRIVATE_KEY are set, this enables the job even when the P12 password or notary credentials are missing; key import or notary setup then fails instead of cleanly skipping the companion release.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

In .github/workflows/release.yml:71, make HAS_SECRETS require all six credentials listed in the Secrets present notice at line 79 and docs/companion/RELEASE_CHECKLIST.md:66-71. If any are absent, the job should take the existing skip path rather than entering signing and notarization.

Comment on lines +194 to +195
startAccountTimer()
refreshAccount(force: true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MAJOR CONSENT Wait for consent before fetching Claude account usage

Startup fetches Claude usage before onboarding finishes. CompanionPrefs.usesClaudeAccount defaults to true, so even a user who turns the onboarding toggle off may first get a Keychain prompt and an API request before that choice takes effect.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

Defer the initial account fetch and timer until the user explicitly enables Claude account usage. Ensure a first launch with the onboarding toggle off never reads the Keychain or sends the usage request.

}

public static func processIsAlive(_ pid: Int32) -> Bool {
kill(pid, 0) == 0 || errno == EPERM

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MAJOR CORRECTNESS Verify the recorded process identity before treating its PID as live

A stale <pid>.json becomes a false live session when its PID is reused: this check accepts any process with that PID, so refresh replaces the snapshot list with stale status and can send false alerts. Compare the record's procStart with the current process start before accepting it.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

In apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/LiveRecords.swift, update the liveness validation around line 59 so a successful kill(pid, 0) is not sufficient: compare the record's procStart with the current process start time, matching the existing validation in python/src/agentplay/companion.py. Reject mismatches before returning the session.


public enum ActivityState: String, TolerantEnum, Sendable {
case active, recent, idle
static var fallback: ActivityState { .idle }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MAJOR WRONG_STATE Preserve unknown activity states instead of decoding them as idle

Unknown activity and live statuses become .idle, so a newer core's active or needs-you state is silently shown as idle; active runs can be omitted and needs-you UI can be missed. Preserve an unknown or unavailable state instead.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

In apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/Snapshot.swift, lines 152–155 and 248–254, change unknown ActivityState and LiveStatus decoding so unknown values are preserved as unknown/unavailable rather than presented as idle. Update state consumers such as RunActivity.state and activeRuns to avoid silently hiding unknown activity.

Comment on lines +9 to +11
mkdir -p "$dir" 2>/dev/null || exit 0
tmp="$dir/.in.$$"
cat > "$tmp" 2>/dev/null || exit 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MAJOR ERROR_HANDLING Run the previous statusline when snapshot capture fails

If creating the directory or writing the snapshot fails, these exits skip the saved statusline at line 18, so users who enabled the bridge lose their existing status output on that invocation. Treat capture as best-effort and still run the previous command with the original input.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

In apps/macos/AgentPlayCompanion/Resources/claude-statusline-bridge.sh, lines 9-11, change the mkdir and stdin-capture failure paths so they do not exit before the saved statusline runs. Preserve and pass through the original stdin when snapshot storage fails.

/// `used` 0...100, `windowMinutes` length, `resetsAt` end of the window.
public static func of(used: Double, windowMinutes: Int?, resetsAt: Date?, now: Date) -> Pace? {
guard let minutes = windowMinutes, minutes > 0, let reset = resetsAt, reset > now else { return nil }
let length = TimeInterval(minutes * 60)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MAJOR ROBUSTNESS Bound minutes before multiplying by 60

An oversized but Int-decodable windowMinutes from a Codex rate-limit record passes the positive-value guard, then minutes * 60 overflows and traps when the usage row calls Pace.of, crashing the menu. Reject values above Int.max / 60 before multiplying.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

In apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/Pace.swift:15-16, prevent an oversized positive windowMinutes from overflowing during minutes * 60. Validate against the representable bound before multiplication, and add a test for an Int-decodable value above that bound.

Comment on lines +98 to +100
guard w.used.isAvailable, w.used.freshness != .stale, w.resetsAt.map({ $0 > now }) ?? true else { return false }
let used = w.used.value?.usedPercent ?? 0
let p = Pace.of(used: used, windowMinutes: w.windowMinutes, resetsAt: w.resetsAt, now: now)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MAJOR CORRECTNESS Apply the age-based stale check to Pip's windows

A reading older than six hours but still tagged fresh (or unknown) passes this guard and can keep Pip in .sweat; the same check in spentWindow can keep it in .limit. Use the age-based stale check in both functions.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

In apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/Pip.swift, update the freshness guards in hotWindow (line 98) and spentWindow (line 57) to reject readings when `used.isStale(now: now, maxAge: ProviderSummary.maxReadingAge)` is true, matching ProviderSummary.isCurrent. Preserve the existing reset checks.

Comment on lines +646 to +649
func refreshLive() {
guard var s = snapshot, !isSample else { return }
let merged = LiveRecords.merge(snapshot: s.live, records: LiveRecords.read(dir: claudeSessionsDir))
let news = LiveRecords.newlyWaiting(before: lastLive, after: merged)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MAJOR CORRECTNESS Preserve live sessions from custom Claude homes

The Python snapshot honors added Claude homes, but this fast refresh reads only ~/.claude/sessions; LiveRecords.merge returns only those records and drops unmatched snapshot entries. A session found in an added Claude home can therefore disappear from the model and its alerts after a live refresh.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

Read live records from every configured Claude home, or merge fresh records with snapshot sessions from homes not covered by the fast path. Add coverage for a custom-home session surviving `refreshLive()`.

guard isUsable(saved.login, now: now) else { return .failure(.expired) }
// 1. The outgoing account's newest login first, so its rotated token isn't lost.
let outgoing = ClaudeAccount.readIdentity(home: home)?.accountUuid
if outgoing != nil, outgoing != target.accountUuid, case .failure(let f) = saveCurrentLocked(env, now: now) { return .failure(f) }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MAJOR CORRECTNESS Preserve the live login when the target is already active

If Claude Code changes accounts after the menu is rendered, a click on the now-current target can reach this branch. It skips saving that account’s rotated live login, then still installs the older saved login, which can leave Claude Code signed out.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

In apps/macos/AgentPlayCompanion/Sources/AgentPlayCore/ClaudeSwitch.swift:251-252, handle outgoing == target.accountUuid without later overwriting the live login with the saved copy. Return a no-op or refresh the saved target from the live login before proceeding; preserve the latest rotated token.


func switchAccount(to target: ClaudeSwitch.Saved) {
guard !switching else { return }
let busy = (snapshot?.live ?? []).contains { $0.status == .busy }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MAJOR RACE Recheck live status immediately before switching accounts

This cached busy value can be false when a Claude session starts after the snapshot or while the detached switch runs, so the switch proceeds mid-step and may disrupt that session. Recheck live records as part of the switch operation.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

In apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/Model.swift at line 452, the switch guard uses a cached snapshot that can become stale before ClaudeSwitch writes the account. Make the busy check use current live records as close as possible to the write, and ensure the operation refuses a switch if a session starts mid-step.

hsrambo07 and others added 2 commits October 28, 2026 20:19
… its own ending

The confirmation ran modally from inside the menu item's action, while the menu was
still closing, and a menu-bar app never shows it there: the click did nothing. It now
runs once the menu has closed, as Apply does. Built-in subagents no longer offer a
switch. Writes keep settings.json's own final newline (or lack of one).

--switch-check <name> <confirm|cancel> drives the real path: the Tools menu over this
Mac's data, the item's Turn Off… through the menu, the confirmation found on screen and
answered, the result read back from ~/.claude/settings.json, and with confirm, turned
back on and compared byte for byte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lls and suggestions name the right thing

Validated end to end with new dev checks driving the real menu over real data:
--switch-check now covers skill, MCP, subagent and hook (off via the menu, listed under
Turned Off, Turn On from there, settings.json and agents byte-identical after), and
--menu-check walks every tab and submenu, fires copy, Insights, Pip banner, tab switching
in an open menu, Apply This Fix… preview and Cancel, and Ask Claude, and checks each tab
fits the screen.

Fixed on the way:
- Turning a hook back on put its event at the end of hooks; it now returns to its exact
  place (event, group, position), byte for byte.
- Hooks with a tool matcher (Read|Edit|Write) were missing from Tools.
- Skills go by their folder, as Claude Code calls them (17-review-the-work), so a used
  skill no longer also shows as unused.
- A hook event's failures sit on the script that fails, not every script on that event;
  failing hooks list first and the list shrinks a row at a time on short screens.
- Plugin subagents get no switch (the core refuses too); a "remove" suggestion offers
  that item's own Turn Off; a subagent suggestion copies @agent-name, not /name.
- Hook confirmations name the script; the guard warning says what it guards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
let hooks = live.filter { $0.kind == "hook" }.map { h -> SetupReport.Item in
var h = h
let event = h.name.components(separatedBy: ":")[0]
if let script = failing.first(where: { ($0.subject ?? "").hasPrefix(event) })?.hook?.script, script != h.detail { h.failed = 0 }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MAJOR CORRECTNESS Match every failing hook fix for the event

When two matcher groups under the same event have separate broken_hook fixes, this keeps only the first fix's script and zeroes failed on the other group's script, so a genuinely failing hook appears healthy in Tools.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

In `apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/MenuItems.swift:290`, avoid attributing failures to only the first `broken_hook` fix whose subject starts with the event. Match all relevant fixes so scripts from separate matcher groups retain their failure indicators, and ensure unrelated scripts are still cleared.

exit(code)
}
if LaunchOptions.isDev, let name = LaunchOptions.value("--switch-check") {
let confirm = LaunchOptions.args.contains("confirm")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

NIT ARGUMENT_PARSING Parse the mode after the item name

For an item named confirm, --switch-check confirm cancel sets this to true because the item name is included in args; switchCheck then takes the “Turn Off” path instead of “Cancel.”

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

In apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/App.swift at line 200, determine the switch-check mode from the argument following the item name, not from whether any argument equals `confirm`; this prevents an item literally named `confirm` from turning an explicit cancel into the confirm path.

Comment on lines +497 to +498
let a = (try? FileManager.default.contentsOfDirectory(atPath: home.appendingPathComponent(".claude/agents").path).sorted()) ?? []
let x = (try? FileManager.default.contentsOfDirectory(atPath: home.appendingPathComponent(".agentplay/set-aside/agents").path).sorted()) ?? []

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

NIT TEST Compare the agents' file contents, not just their names

These checks record only directory entry names. If turning an agent off and back on corrupts or changes a file without changing its name, state() still matches and the check prints restored PASS byte-identical.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

In apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/SelfTest.swift:495-499, make state() recursively capture relative paths and file bytes for both .claude/agents and .agentplay/set-aside/agents. Keep the before/after comparison so content changes with unchanged filenames are detected.

/// Apply This Fix…: the preview must show; Cancel must leave every file as it was.
@MainActor static func fixCheck(_ m: CompanionModel, _ all: [(String, NSMenuItem)]) async {
guard let (p, r) = all.first(where: { $0.1.title == "Apply This Fix…" }) else { print("menu-check: no fix to apply"); return }
let settings = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(".claude/settings.json")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

NIT TEST Check the file named by the fix preview

This snapshots only ~/.claude/settings.json, but fixes can target repo-level files such as .claude/settings.json or CLAUDE.md. If Cancel mistakenly writes the previewed target, same remains true and the check prints PASS; compare the actual target file instead.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

In apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/SelfTest.swift:762-784, make fixCheck capture and compare the actual file identified by the selected fix preview (and any other files that can be changed), rather than only ~/.claude/settings.json, so a write to a repo-level target cannot produce a false PASS.

@entelligence-ai-pr-reviews

Copy link
Copy Markdown

Findings outside the diff (2)

GitHub only accepts inline review comments on lines the PR changed. These findings are about code this PR did not change, so they are reported here instead.


python/src/agentplay/setup_inventory.py · line 136 · MAJOR

MAJOR CORRECTNESS Honor the selected hook matcher when switching

The inventory now gives each matcher its own hook item, but setup_control.switch uses only the event and script when disabling hooks. If the same script is registered for two matchers on one event, switching either item removes both registrations.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

Pass the selected matcher through to the setup command and restrict both hook removal and restoration to that matcher; add a test with the same script registered under two matchers for one event, and verify switching one leaves the other intact.

apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/StatusMenu.swift · lines 555-556 · MAJOR

MAJOR CONTRACT Keep unsupported commands out of the switch action

If the coach returns a remove suggestion for an installed command, this branch offers addSwitch for it. Clicking “Turn Off…” sends setup off command ..., which the switch API rejects, so the advertised action fails.

Prompt to fix with AI

Copy this prompt into your AI coding assistant to fix this issue.

In apps/macos/AgentPlayCompanion/Sources/AgentPlayCompanion/StatusMenu.swift:555-556, avoid passing command items to addSwitch, since setup_control.py does not support switching commands. Show an appropriate commands-folder action or a non-actionable explanation for command suggestions, while retaining the switch action for supported kinds.

@adityajha2005

Copy link
Copy Markdown
Contributor

hey @hsrambo07 can you check the review comments once?

@hsrambo07 hsrambo07 closed this Nov 2, 2026
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.

3 participants