feat: track Claude Code Desktop sessions - #14
Conversation
vibe init only wraps the claude terminal command, so Desktop app sessions were never tracked. Add vibe hooks install/uninstall that register SessionStart/UserPromptSubmit/PostToolUse/Stop/SessionEnd hooks in ~/.claude/settings.json to record Desktop sessions the same way: idle-excluded duration, git-based scoring, wrapper dedup via VIBE_SESSION, absolute hook paths for Dock launches. No server changes.
|
Thanks for this. The approach is exactly right: hooks are how Desktop exposes itself to tooling, the privacy model is preserved (session_id and cwd only), no new dependencies, and no server changes needed. I verified the VIBE_SESSION=1 dedup guard works, the settings merge is safe and idempotent on a normal install, and tsc is clean on the branch. One blocker. reapOrphanedSessions finalizes any exitCode: -1 session idle over 30 minutes. The terminal wrapper survives that because its poller rewrites the row every 30s. Hook sessions only write on hook events, and the reaper runs on every vibe status / log / share and inside onSessionStart. So: Desktop session idle 40 min, vibe status runs, session gets marked interrupted. User comes back and commits, but activity and session-end both bail on the exitCode !== -1 check. Session ends commits: 0, momentum: interrupted even though work shipped. Reproduced end to end. A lunch break triggers it. Fix I'd take: in onActivity, if the id matches a session the reaper closed, reopen it. exitCode back to -1, clear submittedAt so the final state resubmits (server upserts on id, so that's safe). activeSecondsSince already caps the gap. Two nits:
|
onActivity and onSessionEnd now reopen a hook session that reapOrphanedSessions finalized as interrupted after 30 min idle, so work shipped after a lunch break is finalized instead of lost; the reopened state clears submittedAt and resubmits since the server upserts on id. Also match our hooks by the __hook token alone so reinstall and uninstall stay reliable on dev clones, plus a README note to re-run vibe hooks install after a Node upgrade.
|
Great catch on the reaper race. Fixed: |
iamnotstatic
left a comment
There was a problem hiding this comment.
Verified on the branch. Ran the original repro: 40 min idle, reap via vibe status, commit, then activity and session-end. The session reopens, the commit comes back, duration stays at the reaper's cap, and the corrected final state resubmits. Also confirmed install is idempotent and uninstall is clean on a clone path with no lowercase "vibe" in it, which was the failing case. tsc clean.
Nice work on the recovery logic, the restart-the-clock approach keeps durations honest without re-counting the gap. Merging.
Track Claude Code Desktop sessions
Problem
vibe initwraps theclaudeterminal command via shell hooks, so a session is only tracked when you launchclaudefrom a shell. The Claude Code Desktop app never invokes that command, sovibe __wrapnever fires and Desktop work is invisible to vibetime. For people who work mostly in Desktop, none of their time is tracked.Approach
Track Desktop the way Claude Code exposes itself to tooling: session hooks.
vibe hooks installregisters hooks in~/.claude/settings.json(the same settings the Desktop app reads) that call a hiddenvibe __hook <event>on each session lifecycle event:SessionStartUserPromptSubmit/PostToolUse/StopSessionEndDesktop sessions land in the same
~/.vibe/sessions.jsonwithtool: "claude", so they show up invibe status,vibe log,vibe share, and the leaderboard exactly like terminal sessions. No server changes — the payload is unchanged and the Claudesession_idis already a UUID the API accepts.Duration + idle
Active time is accumulated across hook events and each idle gap is capped at the 30-minute inactivity timeout — the same idle-exclusion rule the terminal poller uses, so Desktop and terminal durations are comparable. A single git baseline sha is persisted on the session (
startSha) because start and end now happen in separate processes.Notes / decisions
claudetoo. The handler short-circuits whenVIBE_SESSION=1, so if you run bothvibe initandvibe hooks install, terminal sessions are counted once.PATH, so a barevibewould not resolve.vibe hooks installpins absolute paths to Node andcli.js.session_idandcwdfrom the payload — never the transcript, prompts, or model output — and derive the same git metadata as the wrapper. Updated the Privacy section to say so.~/.claude/settings.jsonwithout touching other keys or the user's own hooks, is idempotent, and refuses to write if the file is not valid JSON.vibe hooks uninstallremoves only vibe's hook entries.vibe __hookwrites nothing to stdout (SessionStart stdout is fed to the model), swallows all errors, and always exits 0, so it can never disrupt a session.Testing
Verified end-to-end against a throwaway
$HOMEand a real git repo: install/merge/idempotency/uninstall, session-start → activity → session-end lifecycle, git-diff scoring, idle-gap capping, theVIBE_SESSIONdedup, non-UUID/empty-stdin/unknown-id/non-git edge cases.tscclean.Usage