All notable changes to Aby Claude Watcher are documented in this file.
The format follows Keep a Changelog, and this project adheres to Semantic Versioning.
- No toast in compact view when bell was off — toasts were gated on
the per-session bell pref (
prefs.modal) at the main-process level, so users in compact view who had never enabled the bell got no visual signal at all onwaiting/pendingtransitions. Theshow-notificationIPC now fires unconditionally; the renderer gates by view mode instead. Compact view always shows the toast — the bell there only controls sound + native OS notifications. Grid view behaviour is unchanged (bell still controls the toast). Micro view still skips the toast entirely (window is too small for an overlay).
- State color rework — adjusted the palette across all view modes for
better contrast between
running,thinking,waiting,pendinganderror.
- Compact branch truncation — long git branch names overflowed their slot in compact cards; now truncated with ellipsis.
- Subagent session-dir resolution —
SubagentTrackerwas deriving the agent session directory from a path that didn't always match what the watcher actually resolved, causing some subagents to be missed. The tracker now reuses the watcher's resolved JSONL path.
- Nested sub-rows in compact and micro views — the subagent sub-cards introduced in 1.7.0 now also render under their parent session in compact and micro layouts (not just grid).
- Live background subagents — the watcher now scans
~/.claude/projects/<session>/agent-*.jsonl+agent-*.meta.json, capturesAgenttool_use dispatches per session, derives state (running / completed / error) from the agent's last event plus mtime, and renders nested sub-cards under each parent session showing what background work is in flight. Includes aSubagentTrackerwired into the main process and serialized over IPC. subagents.jsshipped in the packaged build and added to the test suite (test/subagents.test.js).
- Popover height stuck regardless of session count — the tray popover
measured
.popover-body.scrollHeightto auto-resize, but that element hasheight: 100vh+overflow: hiddenand its scrollable child (.popover-list) scrolls internally without pushing the parent.scrollHeighttherefore always returned the current window height, so opening the popover with 1 session or 15 sessions produced the exact same ~360px window. Height is now computed fromheader.offsetHeight + popList.scrollHeight + footer.offsetHeight + 2px(body borders) so the window grows up to the existing 600px clamp before the inner list takes over scrolling.
Product pivot: the app now monitors only live Claude Code sessions.
Completed sessions vanish the moment Claude exits, the resume / clear
flows are gone, and the codebase is ~250 lines lighter. The value prop
becomes "ambient awareness of what's running right now" — anything else
(history, resume) belongs to the claude CLI itself.
- Completed state —
STATES.COMPLETED, the dark-grey badge, the "Anciennes sessions" / "Old sessions" divider, and every code path that transitioned a session into completion. When a session ends (file gone + PID dead), it's purged instantly from the UI and the config store. Legacy completed sessions persisted by older versions are migrated out on first launch. - Resume flow — the play button on completed cards, the resume
modal with its
--dangerously-skip-permissionstoggle, theresume-sessionIPC handler,focus.resumeSession(), and the best-effort hook installation that ran before resuming. - Clear-completed action — the "Tout effacer" / "Clear all" button,
its confirmation modal, and the
clear-completed-sessionsIPC. With no completed sessions left to accumulate, the bulk-clear has no job. - Cleanup —
endedAtfield,wasResumedflag, micro-view's completed filter, drag-drop completed restrictions, the--state-completed/--glow-completedCSS variables, allstate_completed/action_resume/resume_*/clear_completed_*/sessions_old_labeli18n strings,sanitizeSessionIdfrom focus.js (only used by resume).
- Notification cooldown reset — previously cleared on any state
transition out of waiting/pending, so a Claude tool-loop
(
waiting → running → waiting) re-rang the bell every cycle. Now cooldown only resets when state goes tothinking(= a real user prompt landed). One ding per "Claude is waiting for you" episode. - Error sessions stay visible with a manual X button. They're the only non-active state surfaced in the UI now — everything else is thinking / running / waiting / pending.
This is a UX-driven cleanup release. The cards lost about half their controls and the codebase about 200 net lines, while the experience becomes more direct: one click on a card opens its terminal, one click on the name renames it inline, no context menus, no "+" to add sessions manually, no remote-control bouton.
- Remote-control feature —
session.remoteUrl, the globe button on cards, the context-menu entry, and the watcher'sbridge_statusevent detection are gone. The internalopen-remoteIPC stays as the underlying transport foropenExternalUrl(still used for the GitHub link in About). - Manual "Add session"
+button — the toolbar action and its modal, along with theadd-session/launch-sessionIPC, thewatcher.addSession()method, andfocus.launchSession(). Sessions are auto-detected; the empty state now reads "Lancezclaudedans un terminal — les sessions apparaissent ici automatiquement". - Rename modal — replaced by inline rename (see Added).
- Right-click context menu —
showContextMenu/hideContextMenu, the<div class="context-menu">placeholder, theoncontextmenuattribute on cards, and the global click listener that hid the menu. Every action that lived there is now reachable directly: focus terminal = click on the card, rename = click on the name, resume / delete = the existing buttons on completed/error cards. - Terminal
>_button and "more"⋯button on macro and compact cards. Card click handles focus. X sessions activesheader above the list (was a 1.5.7 add).- Session slug display on cards (the small grey hex string under the
name).
handleCopyIdis left as dead code for now. - Pointer cursor on hover of
.card/.compact-card(it implied reorder), and thecursor: grabon draggable cards.cursor: grabbingis kept while a drag is actively happening.
- Inline rename — hover the project name and a pencil icon fades in;
click to convert the span into an
<input>in place. Enter saves, Esc cancels, blur saves. Skips the IPC roundtrip if the value didn't change. - Multi-window VS Code / Cursor focus — the editor case in
focus.jswas rewritten aroundopen -a "<App>" "<cwd>". macOS LaunchServices routes the call to the window that already has that workspace open, so clicking a card reliably brings the right Code/Cursor window forward even when several windows are open. Crucially, this needs no Accessibility nor Automation permission, which the previousSystem Events/AXRaiseapproach did, and which broke duringnpm run devbecause the dev Electron binary wasn't authorised. detectTerminalFromPidreadsps -o command=in addition tocomm=. The truncatedcomm=is justElectronfor both Code and Cursor main processes; the full command path (/Applications/Visual Studio Code.app/...) reliably distinguishes them.
- Compact view layout is now 3 lines:
name + actions/état · outil/branch. The branch line wraps freely when the name is long (no more ellipsis cutting it off — there's room on its own line). - Status bar typography unified — every label, percentage, reset
countdown and clock icon now inherits the bar's
10 px / text-muted, matching the5h/7Dlabels. One source of truth in.status-bar. - Card click = focus terminal (single click) on macro and compact
cards, matching the existing micro behaviour. Buttons inside the card
call
event.stopPropagation()so they keep their own actions.
- Compact view → mini cards — the previous compact view was a single line per session that landed visually almost on top of the micro view. It's now a true intermediate density: a small card with a header (project + slug + actions) and two metadata lines (state · duration · tool · tokens, then branch · model). The list uses the same responsive auto-fill grid as the macro view but with narrower minimum (260 px vs 320 px), so a typical 13" screen fits 8-10 cards vs 3-4 in macro. Macro and micro views are unchanged.
- Settings modal: fixed content height — the modal used to resize itself depending on which tab (Général, Notifications, À propos) was active. Cycling through tabs forced you to chase the tab buttons with the mouse. The content area is now a fixed 440 px with internal scrolling for tabs that overflow, so the tab row stays anchored.
- Active-session count header above the list (
X active sessions) so a glance at the dashboard tells you immediately how busy you are. - Old sessions divider — completed sessions are now visually separated
from active ones with a divider that includes a Clear all button. A
confirmation modal protects against fat-finger purges. The clear only
removes sessions from the dashboard's view (and their custom names /
notification preferences); the underlying Claude Code session files on
disk are untouched, so
claude --resumestill works. - About panel describing what the app does — replaces the keyboard shortcuts modal in the toolbar's "?" button.
- Update check cadence — first check at startup is now 10 s (was 30 s), and the app re-checks every 2 hours while it's running (previously only on launch). The internal 1 h rate limit on the manual "Check" button is unchanged.
- Removed Cmd- keyboard shortcuts* (⌘1-9, ⌘G, ⌘P, ⌘F, ⌘?). They were rarely used, took inventory in the modal that was better spent on app context, and added behaviour that interfered with macOS Cmd-key conventions. Esc still closes any open modal — that's UX baseline, not a "shortcut".
- Update banner padding and typography — the "Installer maintenant"
button felt cramped, especially during the long-text states
("Téléchargement 47 %", "Installation et redémarrage…"). Roughly
doubled the button's vertical padding, bumped font sizes by 1 px,
added
font-weight: 500andwhite-space: nowrapso the label doesn't wrap, and gave the banner itself more breathing room (gap 12→16, padding 8/14→12/20). Hover now lifts the button by 1 px for tactile feedback.
- 7-day window reset countdown in the status bar — the 5-hour bar already
showed the time-until-reset; the 7-day bar now does too. Same visual
treatment (clock icon + relative time like
2h30or3 days).
- DMG helper blocked by Gatekeeper on Sequoia — the
Fix Gatekeeper.commandshipped in 1.5.3 was itself flagged by macOS Gatekeeper on first launch (same quarantine attribute applied to every file in a downloaded DMG). Removed. The DMG background now shows the unlock command directly so the user can read it and paste it into Terminal (signed by Apple, no Gatekeeper prompt). This is only needed for the first install — subsequent updates go through the in-app updater which doesn't trigger quarantine. - Hidden DMG metadata files appearing in the layout —
.background.tiff,.VolumeIcon.icns,.fseventsd, and.Trasheswere placed by Finder right on top of the visible icons whenever the user hadCmd+Shift+.active. A post-build hook now repacks each DMG and moves these entries to (2000, 2000) in the.DS_Store, well outside the window's visible area.
- Card details: branch first, tool last — the tool chip can grow long
(e.g.
WebFetch,TodoWrite); promoting it to the last row of the 2-column grid (where it sits alone) gives it the full card width to breathe. Branch moves to the first row where its short text fits the narrower cell better.
build/push-hidden-offscreen.py— repacks a DMG with hidden metadata files moved off-screen. Wired intoelectron-buildervia theafterAllArtifactBuildhook inbuild/after-artifact-build.js. Requires Python 3 withds-storeandmac_aliasinstalled (pip3 install --user ds-store mac_alias).
- One-click in-app updates — the updater now downloads the matching DMG asset directly from the GitHub release and replaces the running app via a detached install script (mount → copy → unmount → relaunch). No more manual re-download on every release. Works with ad-hoc signing (no Apple Developer ID required). Falls back to the GitHub link if no DMG asset matches the running architecture.
- Custom DMG layout — visual install instructions (drag-to-Applications
arrow) and a
Fix Gatekeeper.commandscript bundled in the DMG that removes the macOS quarantine attribute and launches the app, for first-install users blocked by Gatekeeper.
checkForUpdates()response now includesdmgUrl,dmgName,dmgSize, and acanAutoInstallflag so the renderer can branch between in-app install and the legacy GitHub link.- The "About" panel and the update banner now show a download progress bar during the update download, and an "Installing…" state right before the app quits to relaunch on the new version.
/clearmade the card jump or stuck the session in WAITING — Claude Code does not updatesession.json'ssessionIdafter/clear; it stays frozen at the value the process was launched with. The watcher's previous heuristic — pick the newest JSONL by mtime in the project directory — produced false positives whenever an orphan JSONL from a previously-killed Claude was the freshest on disk, causing the tracked session to flap between sids. Detection is now driven by JSONL freshness per(pid, cwd): the watcher sticks with the tracked JSONL for as long as it keeps being written, and only re-attributes to a fresh unclaimed JSONL in the same project dir once the tracked one has gone stale (>30 s without writes). Multi-Claude attribution is resolved by sorting data rows bysession.jsonupdatedAt(most-recently-active first) and excluding JSONLs already claimed in the same scan. Migrations preserve UI position, custom name, and notification preferences (already handled bymigrateSession).
- The mtime-based
_resolveEffectiveSessionIdand the PID-identity migration helper that preceded this fix — both relied onsession.jsonupdating on/clear, which it doesn't.
- Stuck "in progress" sessions —
fastInitialLoadcould not determine state when the last assistant message line exceeded the 64 KB tail (long thinking blocks, large tool outputs). After the partial-line skip, no user/assistant event remained and the session stayed at whatever state was restored from config. Tail now expands iteratively (64 K → 16 M, ×4 per pass) until a usable assistant event is found. - State no longer persisted on initial load —
fastInitialLoadmutatedsession.statedirectly without going throughsetState, so the runtime state was correct butconfig.jsonkept the stale value. The next app restart re-restored that stale state and the bug snowballed. State determined at startup is now persisted. /clearmigration could clobber a concurrent session — when two Claude processes ran in the same project directory, the watcher treated the newer JSONL as a/clearof the tracked session and overwrote the other process's data. Migration now requires the old session's PID to match the active one or be dead.- Trailing
attachmentevents killed the WAITING transition — Claude appends metadata events right after every user/assistant message; their handler calledclearWaitingTimer, cancelling the 2 s transition fromend_turn→ WAITING and leaving sessions stuck in THINKING. Attachment is now a no-op for state. - Orphan config entries — stale
notifications,customNames, andsessionOrderentries for sessions deleted long ago accumulated inconfig.json. They are now pruned at startup against the saved sessions map.
test/watcher.test.js— 9 cases covering the five bugs above, integrated intonpm test.
- Micro view — a third view mode focused on ambient awareness. A single-line-per-session layout with just a state dot + name (click to focus the host terminal), a 36 px toolbar with Back + Pin, a minimum window size of 200 × 100, and its own saved bounds separate from grid/compact so switching back and forth restores each view's last-remembered size.
- Hover tooltip on each micro-view line showing
name — state. - In micro mode, the in-app toast notification is suppressed (the pulsing waiting dot is enough signal for a small ambient window). Sound alerts still fire if enabled per session.
- Pin button visual state on startup when
alwaysOnTopwastrue: the micro pin button was being rendered before the config was read. saveBoundsno longer writes bounds while the window is minimised.
First public release.
- Real-time monitoring of every Claude Code session in
~/.claude/sessions/. - Five color-coded states: thinking, running, waiting, error, completed.
- Grid and compact views with drag-to-reorder and always-on-top.
- Per-session notifications (in-app toast + sound, 30 s cooldown).
- One-click focus terminal for iTerm2, Terminal.app, Warp, VSCode, Cursor, Ghostty, kitty, WezTerm, Hyper.
- Resume session, remote-control URL indicator, tool pills, cost & token counters, Git branch display.
- Bilingual UI (French / English) with runtime switcher.
- Menu-bar tray icon (duck silhouette, template image) with popover summary.
- Dock badge for waiting sessions.
- Optional
cc/cwashell wrappers registering the host terminal over a Unix socket. - Manual update checker against the GitHub Releases API (no phone-home otherwise).
- Auto-launch at login (opt-in).
- Apple Silicon (arm64) and Intel (x64) builds.