Practical Techniques for Automatically Configuring Claude Code CLI with SessionStart Hooks
- AI
- VibeCoding
- LLM
Introduction
Claude Code CLI has hooks (SessionStart hooks) that run automatically when a session starts. This mechanism can be used to automate tasks such as launching a proxy, synchronizing secrets, and performing security checks when a session starts.
This article presents insights gained from operating 11 SessionStart hooks and records of solutions to the problems encountered.
What Are SessionStart Hooks?
You can register commands to run when a session starts in the Claude Code configuration file (~/.claude/settings.json).
When Claude Code starts, the registered scripts are executed in order. stdout is sent to the Claude Code context and displayed in the terminal via /dev/tty or >&2.
Hook Configuration (11 hooks)
The role of each hook is explained below.
1. Launching the GLM Rate Proxy (start-glm-proxy.sh)
Launches the local proxy that serves as the core of LLM routing.
Processing details:
- Check whether it is already running (pgrep + health check)
- If it is running, verify ANTHROPIC_BASE_URL in settings.json
- If it is not running, launch the proxy in the background
- If startup fails, fall back to the direct Z.AI URL
The important point is that URL validation is performed even when the proxy is healthy. When settings.json is reset, such as during a PC migration, there was a problem where Claude Code would connect directly to Z.AI even while the proxy was running.
2. Secret synchronization (sync-secrets-to-settings.sh)
Loads environment variables from ~/.secrets.env and synchronizes them with the env section of settings.json. This is a mechanism that injects API keys via environment variables instead of writing them directly into the file.
3. Handoff loading (load-handoff.sh)
Loads handoff.md, which was generated at the end of the previous session, and sends it into Claude Code's context.
This allows Claude Code in a new session to carry over the work from the previous session.
4. Startup banner (startup-banner.sh)
Aggregates the status of each hook and displays it as a single banner in the terminal.
Issues Encountered and Solutions
Problem 1: The banner is not displayed
Symptom: When launching claude in a new terminal, the startup banner sometimes may not be displayed.
Cause: The shell within the SessionStart hookis executed as anon-interactive shell
, so output to /dev/tty or >&2 may not reach the terminal. Depending on the terminal's state or startup method, the ability to connect to /dev/tty may vary, resulting in the banner "sometimes appearing and sometimes not."Solution: I moved the banner output to the claude() shell function in .bashrc (the interactive shell side). The SessionStart hook now only writes the result to a status file (/tmp/claude-startup/*.status), and .bashrc reads and displays that file.
Problem 2: Fallback does not work even when the proxy is running
Symptom: After migrating to a new PC, the GLM→MiniMax fallback was not working.
Cause: When the proxy was already running, the health check exited with code 0 immediately after returning OK, without validating ANTHROPIC_BASE_URL in settings.json. The PC migration reset the settings, resulting in a URL that bypassed the proxy.
Solution: I changed it to call ensure_settings_url() in all three paths (healthy / successful startup / failed startup).
Problem 3: Regenerating MCP settings through secret synchronization
Symptom: Even if an MCP server is deleted, it is restored when the next session begins.
Cause: When sync-secrets-to-settings.sh synchronizes environment variables to settings.json, it regenerates entries for nonexistent MCP servers with null values.
Solution: Removed unnecessary jq entries for MCP servers from the script.
Hook design principles
1. Make each hook independent
It is important not to create dependencies between hooks. Ensure that the other hooks still run even if one hook fails.
2. Communicate status through files
Hook execution results are written to a status file under /tmp/claude-startup/. This makes them easier to reference from other hooks and from .bashrc.
3. Use stdout and /dev/tty appropriately
- stdout (default): Sent to Claude Code's context. Information read by Claude
- /dev/tty: Displayed directly in the terminal. Information seen by the user
- >&2 (stderr): Absorbed by the Claude Code process, but serves as a fallback when /dev/tty is unavailable
4. Incorporate fail-safes
For example, when the proxy fails to start, fall back to the direct Z.AI URL, so Claude Code itself remains usable even if a hook fails.
Checklist for migrating to a new PC
These are the minimum items that should be checked when setting up Claude Code in a new PC environment.
- Whether API keys are configured in ~/.secrets.env
- Check whether an ANTHROPIC_BASE_URL entry exists in settings.json
- Run start-glm-proxy.sh manually once to verify that it works
- grep "ANTHROPIC_BASE_URL" ~/.claude/settings.json → It is the proxy URL
- curl -s http://127.0.0.1:8787/proxy/status → Responds normally
Limitations of this configuration
- WSL2 only: Because the behavior of /dev/tty depends on the environment, adjustments are required in other environments
- Increased number of hooks: Since 11 hooks run sequentially, startup takes several seconds
- Managing two configuration file locations: You need to be aware of both ~/.claude.json and ~/.claude/settings.json
Conclusion
The SessionStart hook is a powerful mechanism for adapting the Claude Code CLI to your own development workflow. By automating proxy startup, secret management, session continuation, and more, it can eliminate manual work when starting a session.
However, because it runs in a non-interactive shell, measures such as displaying information intended for the user on the .bashrc side are necessary. When designing hooks, keeping three points in mind—“independence,” “fail-safe behavior,” and “separate use of stdout/tty”—will result in a stable configuration.
Related articles
- Investigation and optimization record for the problem of Claude Code's MCP tools consuming context
- A story of replacing the Claude Code CLI with GLM (Z.AI) (actual measurements of significant cost reduction)
- An operational report on LLM routing that processes 2.7 billion tokens per month
This article is also published onpersonal Zenn.
Go to list of users who liked
Go to list of comments
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information You can use dark theme



