Skip to content

feat(claude-code): add opt-in trail status line#1479

Open
matthiaswenz wants to merge 2 commits into
mainfrom
cli-claude-statusline
Open

feat(claude-code): add opt-in trail status line#1479
matthiaswenz wants to merge 2 commits into
mainfrom
cli-claude-statusline

Conversation

@matthiaswenz

@matthiaswenz matthiaswenz commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

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.mjs script. When the current branch has an Entire trail, Claude Code's status bar shows a clickable, cyan Trail #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-statusline

This writes a statusLine block into .claude/settings.json pointing at entire hooks claude-code statusline. Behavior:

  • No status line yet → added.
  • Existing status line → not clobbered; prints a skip hint. Use -f/--force to replace.
  • Re-running is idempotent. Removed by 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):

  • Serve path (every poll): reads the session JSON from stdin, resolves branch + GitHub remote in-process via go-git (no subprocess/network), reads a small cache under ~/.cache/entire/statusline/, and renders it immediately.
  • Background refresh (detached, throttled): does the authenticated trail API lookup (findTrailByBranch) and rewrites the cache, so the link appears/updates a poll later without ever stalling the visible command.

Implementation notes:

  • Dedicated fast-path command (newClaudeStatuslineCmd) registered under entire hooks claude-code but bypassing the lifecycle dispatcher's enabled-checks / perf-spans / per-invocation logging (no-op PersistentPreRunE).
  • Reuses GetCurrentBranch, gitremote.ResolveRemoteRepo, NewAuthenticatedAPIClient, findTrailByBranch, userdirs.Cache().
  • Install/uninstall in claudecode/statusline.go; ownership detected by the hooks claude-code statusline command 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 spawn
  • cmd/entire/cli/agent/claudecode/statusline.go (+ test) — InstallStatusline/UninstallStatusline
  • cmd/entire/cli/agent/claudecode/{hooks,lifecycle}.gostatusline verb const + HookNames(); uninstall wired into UninstallHooks
  • cmd/entire/cli/hook_registry.go — fast-path the verb
  • cmd/entire/cli/setup.go--claude-statusline flag + install wiring

Testing

  • mise run fmt && mise run lint → 0 issues
  • mise run test → all pass (new statusline_test.go + claude_statusline_test.go cover render, cache, foreign-skip/force, idempotency, uninstall, serve fresh-hit / stale-spawn)
  • mise run test:integration and e2e canary pass
  • Verified end-to-end against the built binary: install/idempotent/foreign-skip/force, serve renders the link from cache, and agent remove strips both hooks and the status line

🤖 Generated with Claude Code


Note

Cursor Bugbot is generating a summary for commit 54c3a32. Configure here.

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
@matthiaswenz matthiaswenz requested a review from a team as a code owner June 19, 2026 10:17
Copilot AI review requested due to automatic review settings June 19, 2026 10:17

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ 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.

Comment thread cmd/entire/cli/setup.go
}
var successfullyAddedAgents []agent.Agent
for _, ag := range addedAgents {
if _, err := setupAgentHooks(ctx, w, ag, opts.LocalDev, opts.ForceHooks); err != 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.

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)
Fix in Cursor Fix in Web

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 54c3a32. Configure here.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 behind entire enable --claude-statusline (and removed on UninstallHooks).
  • 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.

Comment on lines +287 to +292
var payload statuslineCachePayload
if json.Unmarshal(data, &payload) != nil || payload.TS == 0 {
return nil, statuslineInfiniteAge
}
return &payload.Result, time.Since(time.UnixMilli(payload.TS))
}
Comment on lines +357 to +359
if len(line) > 60 {
line = line[:60]
}
Comment on lines +79 to +83
// 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) {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants