feat(claude-code): add opt-in trail status line#1479
Conversation
Add a native Go status line for Claude Code that shows a clickable "Trail #N <status>" OSC-8 hyperlink when the current branch has an Entire trail, replacing the standalone Pi `.mjs` script. - New `entire hooks claude-code statusline` verb: serves a cached result on every poll (fast, in-process branch/remote resolution) and spawns a detached, throttled background refresh that does the authenticated trail lookup and rewrites the cache. No Node dependency; reuses GetCurrentBranch, gitremote.ResolveRemoteRepo, NewAuthenticatedAPIClient and findTrailByBranch. - The verb is fast-pathed in hook_registry.go, bypassing the lifecycle dispatcher's enabled-checks/perf-spans/per-invocation logging since it is polled several times per second. - Opt-in via `entire enable [--agent claude-code] --claude-statusline`. Never clobbers an existing statusLine (errors with ErrStatuslineConflict unless -f/--force); removed by `entire agent remove claude-code`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: a6deb758f25e
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 54c3a32. Configure here.
| } | ||
| var successfullyAddedAgents []agent.Agent | ||
| for _, ag := range addedAgents { | ||
| if _, err := setupAgentHooks(ctx, w, ag, opts.LocalDev, opts.ForceHooks); err != nil { |
There was a problem hiding this comment.
Statusline flag skips existing installs
Medium Severity
--claude-statusline only runs inside setupAgentHooks, which applyAgentChanges calls for newly added or force-reinstalled agents. On an already-enabled repo, re-selecting Claude Code does not reinstall hooks, so the flow often prints “No changes made.” and exits while Entire is already enabled—never writing the status line.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 54c3a32. Configure here.
| // It uses the lifecycle dispatcher (ParseHookEvent → DispatchLifecycleEvent) as the primary path. | ||
| // PostTodo is handled directly as it's Claude-specific and not part of the lifecycle dispatcher. | ||
| func newAgentHookVerbCmdWithLogging(agentName types.AgentName, hookName string) *cobra.Command { | ||
| // The status line is polled several times per second and must stay fast, so |
There was a problem hiding this comment.
Parent hook logging still runs
Medium Severity
The status line subcommand is meant to bypass per-invocation hook logging, but it remains under hooks claude-code, whose PersistentPreRunE still calls initHookLogging. With Cobra v1.10.2, ancestor persistent hooks run for subcommands, so each poll can still hit session discovery and logging setup—not just the no-op child pre-run.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 54c3a32. Configure here.
There was a problem hiding this comment.
Pull request overview
Adds an opt-in Claude Code status line segment that shows the current branch’s Entire Trail (as a cyan OSC-8 hyperlink) by introducing a fast “serve-from-cache” hook command plus a detached, throttled background refresh to populate the cache—integrated into entire enable/agent remove flows.
Changes:
- Introduces
entire hooks claude-code statusline(serve +--refresh) with a local cache and detached refresh process. - Adds Claude Code settings.json install/uninstall logic for
statusLine, gated behindentire enable --claude-statusline(and removed onUninstallHooks). - Wires the new verb into the hook registry and enable flow, with unit tests covering cache/render/install behaviors.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| cmd/entire/cli/setup.go | Adds --claude-statusline enable flag and threads hook-install options through agent setup to install the Claude Code status line. |
| cmd/entire/cli/hook_registry.go | Fast-paths the claude-code statusline verb to a dedicated command instead of the generic hook dispatcher. |
| cmd/entire/cli/claude_statusline.go | Implements the status line serve/refresh logic, cache format, rendering (OSC-8 hyperlink), and detached refresh spawn. |
| cmd/entire/cli/claude_statusline_test.go | Adds tests for render/url/cache logic and serve-path behavior (fresh cache vs stale/miss refresh spawning). |
| cmd/entire/cli/agent/claudecode/statusline.go | Implements install/uninstall/is-installed for the statusLine block in .claude/settings.json without clobbering foreign configs by default. |
| cmd/entire/cli/agent/claudecode/statusline_test.go | Adds tests for install/idempotency/force behavior and uninstall behavior. |
| cmd/entire/cli/agent/claudecode/lifecycle.go | Exposes the statusline verb via HookNames() so it gets registered under entire hooks claude-code. |
| cmd/entire/cli/agent/claudecode/hooks.go | Adds the statusline verb constant and ensures UninstallHooks also removes the Entire-owned status line. |
| var payload statuslineCachePayload | ||
| if json.Unmarshal(data, &payload) != nil || payload.TS == 0 { | ||
| return nil, statuslineInfiniteAge | ||
| } | ||
| return &payload.Result, time.Since(time.UnixMilli(payload.TS)) | ||
| } |
| if len(line) > 60 { | ||
| line = line[:60] | ||
| } |
| // newClaudeStatuslineCmd builds the `entire hooks claude-code statusline` verb. | ||
| // It bypasses the generic hook dispatcher (executeAgentHook) and its | ||
| // per-invocation logging because the status line is polled very frequently and | ||
| // must stay fast. The no-op PersistentPreRunE/PostRunE shadow the parent | ||
| // claude-code hooks command's logging setup. |
| return forge, owner, repo, branch, repoDir | ||
| } | ||
|
|
||
| func TestClaudeStatuslineServe_FreshCacheHit(t *testing.T) { |
| } | ||
| } | ||
|
|
||
| func TestClaudeStatuslineServe_StaleCacheSpawnsRefresh(t *testing.T) { |


https://entire.io/gh/entireio/cli/trails/621
Summary
Ships the Entire trail status line for Claude Code as a native CLI feature, replacing the standalone Pi
entire-trail-statusline.mjsscript. When the current branch has an Entire trail, Claude Code's status bar shows a clickable, cyanTrail #N <status>OSC-8 hyperlink to the trail on entire.io.It's opt-in and no Node dependency — it reuses the CLI's own trail/auth/git internals.
How a user activates it
entire enable --agent claude-code --claude-statuslineThis writes a
statusLineblock into.claude/settings.jsonpointing atentire hooks claude-code statusline. Behavior:-f/--forceto replace.entire agent remove claude-code.When logged out it renders a dim
Trail: run \entire login`` hint; with no trail for the branch the segment is absent.How it works
Claude Code polls the status-line command several times per second, so the design keeps every poll fast and non-blocking (ported from the original
.mjs):~/.cache/entire/statusline/, and renders it immediately.findTrailByBranch) and rewrites the cache, so the link appears/updates a poll later without ever stalling the visible command.Implementation notes:
newClaudeStatuslineCmd) registered underentire hooks claude-codebut bypassing the lifecycle dispatcher's enabled-checks / perf-spans / per-invocation logging (no-opPersistentPreRunE).GetCurrentBranch,gitremote.ResolveRemoteRepo,NewAuthenticatedAPIClient,findTrailByBranch,userdirs.Cache().claudecode/statusline.go; ownership detected by thehooks claude-code statuslinecommand substring, so uninstall only removes Entire's own status line and leaves a foreign one intact.Files
cmd/entire/cli/claude_statusline.go(+ test) — serve/refresh handler, render, cache, detached spawncmd/entire/cli/agent/claudecode/statusline.go(+ test) —InstallStatusline/UninstallStatuslinecmd/entire/cli/agent/claudecode/{hooks,lifecycle}.go—statuslineverb const +HookNames(); uninstall wired intoUninstallHookscmd/entire/cli/hook_registry.go— fast-path the verbcmd/entire/cli/setup.go—--claude-statuslineflag + install wiringTesting
mise run fmt && mise run lint→ 0 issuesmise run test→ all pass (newstatusline_test.go+claude_statusline_test.gocover render, cache, foreign-skip/force, idempotency, uninstall, serve fresh-hit / stale-spawn)mise run test:integrationand e2e canary passagent removestrips both hooks and the status line🤖 Generated with Claude Code
Note
Cursor Bugbot is generating a summary for commit 54c3a32. Configure here.