Skip to content

fix: don't let Claude Code's workspace trust dialog block new sessions - #101

Open
ILobeMh wants to merge 2 commits into
six-ddc:mainfrom
ILobeMh:fix/trust-dialog
Open

ILobeMh wants to merge 2 commits into
six-ddc:mainfrom
ILobeMh:fix/trust-dialog

Conversation

@ILobeMh

@ILobeMh ILobeMh commented Sep 15, 2026

Copy link
Copy Markdown

Problem

Since Claude Code ~2.1.260, launching claude in a directory that isn't trusted yet shows this first:

 Quick safety check: Is this a project you created or one you trust? …
 ❯ No, exit
   Yes, I trust this folder
 Enter to confirm · Esc to cancel

The options have no numbers now, and "No, exit" is the default. ccbot never answers the dialog, so a new session breaks:

  1. The SessionStart hook doesn't fire, so the 5s wait_for_session_map_entry times out.
  2. The topic's pending first message is typed into the dialog. Enter on the default option exits Claude Code.
  3. The topic is left bound to a bare shell, and later messages (/model, /context, …) go to zsh (zsh: no such file or directory: /model).

Fix

  • Pre-trust before launch. The new claude_config.ensure_trusted_directory() sets projects[<path>].hasTrustDialogAccepted = true in ~/.claude.json (honours CLAUDE_CONFIG_DIR). This is the documented way to trust a folder. The key is the git repo root when inside a repo, otherwise the directory itself, which matches how Claude Code keys trust. The write is atomic and keeps the file mode. It does nothing if the folder is already trusted, and leaves a missing or malformed file untouched. Set CCBOT_AUTO_TRUST_DIRS=false to turn it off.
  • Fallback if the dialog still appears. One case is the home directory, whose trust Claude Code doesn't save. A TrustDialog UI pattern and TmuxManager.auto_answer_dialog() move the ❯ cursor onto "Yes, I trust", capture the pane again to check it landed there, then press Enter. It never types a digit, because the option order changed between versions and a blind 1/Enter now exits.
  • Ready wait. _create_and_bind_window now waits for Claude's input box (wait_for_claude_ready) before the hook wait and before forwarding the pending message. Status polling also answers the dialog in windows that already exist.

About 170 lines of source plus tests. No behaviour change for directories that are already trusted, apart from the short ready wait.

Testing

  • Unit tests use real tmux capture-pane fixtures from Claude Code 2.1.272 (trust dialog, idle prompt in manual and bypass mode). They cover the ❯ cursor being told apart from the shell's ❯ claude echo, and the answer not being confirmed when the cursor didn't move. test_claude_config.py covers the JSON edits (existing keys and file mode preserved, git-root key, missing and malformed files).
  • End-to-end on macOS (tmux 3.6b, Claude Code 2.1.272): claude in a fresh untrusted directory. The dialog was answered and the prompt was ready in about 3s.
  • pytest: 274 passed; pyright: 0 errors.

Since Claude Code ~2.1.260 the first launch in an untrusted directory
shows an unnumbered "Quick safety check" menu whose default option is
"No, exit". ccbot never answered it: the SessionStart hook doesn't fire,
the 5s wait times out, and the pending first message is typed into the
dialog — Enter on the default option exits Claude, leaving the topic
bound to a bare shell.

- claude_config.ensure_trusted_directory() sets
  projects[<git root or dir>].hasTrustDialogAccepted = true in
  ~/.claude.json (honours CLAUDE_CONFIG_DIR) before creating the window.
  Atomic write, file mode preserved, no-op if already trusted, missing or
  malformed files left untouched. CCBOT_AUTO_TRUST_DIRS=false disables it.
- Fallback for when the dialog still appears (e.g. the home directory,
  whose trust Claude Code doesn't persist): a TrustDialog UI pattern and
  TmuxManager.auto_answer_dialog(), which moves the ❯ cursor onto
  "Yes, I trust", re-captures to verify, then presses Enter. It never
  types a digit, since option order changed between versions.
- _create_and_bind_window waits for Claude's input box
  (wait_for_claude_ready) before the hook wait and before forwarding the
  pending message; status polling answers the dialog in existing windows.

Pane fixtures are real tmux captures from Claude Code 2.1.272.
Named sessions render the input box's top border as
'──────── my-session ─', which the plain all-dashes check rejected, so
wait_for_claude_ready timed out on otherwise-ready sessions. Fixture is
a real 2.1.272 capture.
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