Skip to content

fix: update Claude Code prompt prefix for v2.1+#1

Open
kingb wants to merge 1 commit intomainfrom
fix/claude-code-prompt-prefix
Open

fix: update Claude Code prompt prefix for v2.1+#1
kingb wants to merge 1 commit intomainfrom
fix/claude-code-prompt-prefix

Conversation

@kingb
Copy link
Owner

@kingb kingb commented Jan 20, 2026

Summary

  • Update default ready_prompt_prefix for Claude provider from "> " to "❯"
  • Claude Code v2.1+ changed the input prompt character in the UI refresh

Problem

Agents (refinery, witness, polecats) were timing out during startup with:

Error: starting refinery: waiting for refinery to start: timeout waiting for runtime prompt

The gastown startup detection was looking for "> " but Claude Code now displays "❯" as its prompt indicator.

Solution

Simple one-line fix to update the default prompt prefix. Users on older Claude Code versions can override via settings/config.json if needed.

Test plan

  • Existing config tests pass
  • Manually verified refinery startup succeeds with this change

🤖 Generated with Claude Code

Claude Code v2.1+ changed the input prompt character from "> " to "❯".
This was causing refinery and other agents to timeout during startup
when waiting for the runtime prompt to appear.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
kingb pushed a commit that referenced this pull request Feb 18, 2026
Three fixes to eliminate stale hooks from dead polecats blocking re-sling:

1. sling.go: When a bead is already hooked, check if the assignee tmux
   session is dead. If dead, auto-force the re-sling instead of requiring
   --force. This eliminates the #1 friction in convoy feeding.

2. stale_hooks.go: Check session liveness for ALL hooked beads regardless
   of age. Previously only checked beads older than MaxAge (1h). Now
   unhooks immediately when the agent session is confirmed dead.

3. manager.go: Kill any lingering tmux session for an allocated name
   before returning it, preventing session already running errors
   when reusing names from dead polecats.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
kingb pushed a commit that referenced this pull request Mar 5, 2026
steveyegge#2075)

* feat(quota): context-preserving rotation via keychain token swap

Replace config-dir-switching with macOS Keychain token swapping.
Instead of changing CLAUDE_CONFIG_DIR (which breaks /resume), swap
the OAuth token from an available account into the rate-limited
account's keychain entry and respawn with the same config dir.
This lets /resume naturally find previous session transcripts.

- Add keychain.go: read/write/swap macOS Keychain credentials
- Add keychain_stub.go: no-op stubs for non-darwin builds
- Add config dir grouping: one keychain swap per shared config dir
- Add interactive /resume after respawn for context recovery
- Add backup/rollback support for keychain operations
- Persist detected rate-limit statuses during rotate (#1)
- Return structured JSON in all rotate exit branches (steveyegge#2)
- Tighten default scan patterns to avoid false positives (steveyegge#4)
- Consolidate ExpandHome duplicates via util.ExpandHome (steveyegge#6)
- Add state persistence and dry-run plan tests (steveyegge#7)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(quota): add --from and --idle flags for preemptive rotation

Add preemptive rotation support so sessions can be swapped to a fresh
account before hitting rate limits, targeting only idle agents to avoid
disrupting active work.

- --from <handle>: rotate all sessions using the specified account
  regardless of rate-limit status
- --idle: only rotate sessions at the idle prompt (❯), skip busy agents
- Add Tmux.IsIdle() for point-in-time idle detection
- Add PlanRotation fromAccount parameter for account-based targeting
- Skip marking accounts as "limited" for preemptive rotations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(quota): --continue flag was never injected into restart command

strings.Replace looked for "exec claude " but runtimeCmd doesn't contain
"exec" — that prefix is added later in the Sprintf. The replacement silently
matched nothing, so agents always started fresh sessions instead of resuming.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(quota): swap oauthAccount identity + fix misleading unassignable message

Two fixes:

1. Keychain swap was only changing the auth token but not the account
   identity stored in .claude.json's oauthAccount field. Claude Code
   sends accountUuid/organizationUuid with API requests, so the server
   still rate-limited based on the original account. Now SwapOAuthAccount
   also copies the source's oauthAccount into the target's .claude.json.

2. "not enough available accounts" message was shown for sessions with
   unknown accounts (scanner couldn't resolve their CLAUDE_CONFIG_DIR).
   Now distinguishes between "account unknown" and genuinely having no
   available accounts to rotate to.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(quota): scanner false positives from stale rate-limit messages

Only check the bottom 10 lines of captured pane output for rate-limit
patterns instead of all 30. When a rate limit is resolved (e.g., /login),
subsequent output pushes the message up and out of the check window.
Previously, old rate-limit messages anywhere in the 30-line capture
caused false positives on sessions that had already recovered.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(quota): rotate unknown-account sessions using their CLAUDE_CONFIG_DIR

Sessions with unresolvable accounts (e.g., la-witness) were skipped
entirely during rotation. Now the scanner captures CLAUDE_CONFIG_DIR
for every session, and PlanRotation includes unknown-account sessions
in the plan as long as they have a config dir to swap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(quota): use ~/.claude default when CLAUDE_CONFIG_DIR not set

Sessions using Claude Code's default config dir (no CLAUDE_CONFIG_DIR
env var) had empty ConfigDir, making them unrotatable. Now falls back
to ~/.claude so the keychain swap can target the correct entry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(quota): fall back to ~/.claude in executeKeychainRotation too

The scanner had the ~/.claude fallback but executeKeychainRotation
still failed when CLAUDE_CONFIG_DIR wasn't set in the tmux environment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(quota): skip oauthAccount swap when .claude.json doesn't exist

Default config dir (~/.claude) may not have .claude.json. The keychain
token is what authenticates; oauthAccount is just cached identity
metadata. Skip silently instead of warning.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(quota): add SwapOAuthAccount stub for non-darwin builds

Linux/Windows CI failed because SwapOAuthAccount and RestoreOAuthAccount
are defined in keychain.go (darwin-only). Added stubs to keychain_stub.go.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(quota): detect OAuth token revoked/expired in scanner

After keychain swap, the old account's token may be revoked or expire.
Added patterns for "OAuth token revoked" and "OAuth token has expired"
so these sessions get picked up by gt quota rotate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(quota): validate source tokens before keychain swap

Prevents rotating to accounts with expired or revoked OAuth tokens.
ValidateKeychainToken checks token validity via JSON expiry, JWT exp
claim, or lightweight HTTP probe before the swap occurs. Accounts with
bad tokens are skipped with instructions to run /login.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(quota): don't mark from-account as limited during preemptive rotation

--from targets sessions for preemptive rotation but the account isn't
actually rate-limited. Marking it limited in state removed it from the
available pool and prevented rotation back to it later.

Also fixes misleading "rate-limited" message when using --from, and
surfaces skipped accounts (invalid tokens) when no accounts are available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(quota): exclude from-account from available pool in preemptive rotation

When using --from, the source account should never be a candidate for
rotation targets. Previously it appeared in the available pool, got
validated, and showed as "skipped" — confusing output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(quota): remove HTTP token validation that rejects valid OAuth tokens

Claude Code uses OAuth tokens that authenticate through a different flow
than Bearer tokens against the Anthropic API. Sending them as Bearer to
api.anthropic.com/v1/messages always returns 401, causing every account
to be skipped as "token rejected by API" even after fresh login.

Strategies 1 (JSON expires_at) and 2 (JWT exp) still validate expiry
when the token format supports it. For opaque tokens, assume valid if
present in keychain.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(quota): widen scanner check window to full capture (30 lines)

The 10-line check window was too aggressive — rate-limit messages
scrolled out of view when agents continued working (running bash
commands, /login, etc.), causing rotate to miss sessions that scan
had detected moments earlier.

The tightened patterns (e.g., "limit · resets" instead of bare
"resets") already prevent false positives from stale messages,
so the reduced window is no longer needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(quota): don't let scan detections poison available account pool

Two bugs fixed:

1. PlanRotation marked accounts as limited in-memory based on scan
   results before computing available accounts. Stale sessions (e.g.,
   parked rigs with old rate-limit messages in the pane) would mark
   their accounts as limited, shrinking the pool and blocking rotation
   of sessions that actually need it. Removed in-memory state update —
   available accounts now come from persisted state only.

2. Assignment loop didn't re-read candidate after skipping same-account.
   After `availIdx++` to skip, `candidate` still held the old value,
   so a limited session could be assigned back to its own account.
   This was masked by bug #1 (the account was removed from the pool).

Also removed the caller's automatic state persistence from scan
detections — account state should only be updated after confirmed
rotation execution, not from potentially stale scan results.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant