iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🤖

Week 02: Eliminating 'Session Expiration' with Claude Code Hooks – Building a Foundation for Solo AI Development

に公開

Conclusion: Solving "Session Timeouts" with Automation

When conducting long-term development with Claude Code, the context window eventually overflows, triggering compression. Once compressed, the AI loses track of "what it was doing." This problem becomes even more severe when running multiple agents in parallel via Agent Teams.

In this article, I will introduce a system that uses Claude Code's hooks to automatically prevent context loss and establish a foundation for multiple agents to work in parallel safely.

What I built:

  • 3 Hooks: Automatic state saving -> Automatic recovery -> Automatic change logging
  • Git Branch Strategy: Isolate branches for each agent
  • CLAUDE.md: A rule file that the AI reads automatically (standards for self-review, branch operations, etc.)
  • Template + Initialization Script: Deploy to new projects with a single command

Everything is templated, so it can be reused in any project.


The Problem: Three Patterns of Context Loss

When developing with Claude Code, context is lost in the following patterns:

Pattern What happens Impact
Context Compression Automatically compressed during long conversations. The AI after compression doesn't know the immediate previous state Requires manual explanation for handover
Session Timeout Network disconnection, timeout, etc., breaks the session Same as above. With Agent Teams, the context for all agents is lost
Resuming across days No previous context when a new session is started Requires explaining "this is where we left off" every time

When using Agent Teams for solo development, the leader AI manages the worker AIs; if the leader's context is lost, the overall progress management collapses.

The traditional solution was to "manually write a handoff memo." However, manual work is forgettable, and the level of detail varies. I automated this using hooks.


Solution: Automating with 3 Hooks

Claude Code has a mechanism called hooks. You can associate shell scripts with specific events (context compression, session start, response completion, etc.) for automatic execution.

Configuration

.claude/
├── settings.json           # Hook definitions
├── CLAUDE.md               # Rules the AI reads automatically
├── hooks/
│   ├── pre-compact.sh      # Before compression: Auto-save state
│   ├── on-session-start.sh # After compression: Auto-restore state
│   └── on-stop.sh          # After response: Auto-log changes
└── state/
    ├── current-task.md     # Working state snapshot (auto-updated)
    ├── session-log.md      # Change history (auto-updated)
    └── progress.md         # Phase progress (manual + automatic)

settings.json

{
  "hooks": {
    "PreCompact": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/pre-compact.sh"
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/on-session-start.sh"
          }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/on-stop.sh"
          }
        ]
      }
    ]
  }
}

Hook 1: pre-compact.sh — Auto-saving State Before Compression

Immediately before context compression, the following information is written to .claude/state/current-task.md.

#!/bin/bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
STATE_DIR="$PROJECT_ROOT/.claude/state"
STATE_FILE="$STATE_DIR/current-task.md"
PROGRESS_FILE="$STATE_DIR/progress.md"

mkdir -p "$STATE_DIR"

{
  echo "# Working State Snapshot"
  echo "Save time: $(date '+%Y-%m-%d %H:%M:%S')"

  # Recent commits
  echo "## Recent Commits"
  echo '```'
  cd "$PROJECT_ROOT" && git log --oneline -5 2>/dev/null || echo "(Failed to retrieve)"
  echo '```'

  # Uncommitted changes
  echo "## Uncommitted Changes"
  echo '```'
  cd "$PROJECT_ROOT" && git diff --stat 2>/dev/null || echo "(No changes)"
  echo '```'

  # Phase progress
  if [ -f "$PROGRESS_FILE" ]; then
    echo "## Phase Progress"
    cat "$PROGRESS_FILE"
  fi

  # Recent session history
  SESSION_LOG="$STATE_DIR/session-log.md"
  if [ -f "$SESSION_LOG" ]; then
    echo "## Recent Session History"
    tail -30 "$SESSION_LOG"
  fi
} > "$STATE_FILE"

Saved content:

  • History of the last 5 commits
  • Uncommitted changes (staged + unstaged)
  • Untracked files
  • Phase progress (content of progress.md)
  • Recent session history (end of session-log.md)

This automatically snapshots "what I was doing," "how far I got," and "what is currently being changed."

Hook 2: on-session-start.sh — Automatic Restoration After Compression

When a session is resumed after compression, the content of the saved current-task.md is output to stdout. Since Claude Code automatically injects the stdout of the SessionStart hook into the AI's context, the AI resumes with awareness of the previous working state after compression.

#!/bin/bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
STATE_FILE="$PROJECT_ROOT/.claude/state/current-task.md"

if [ -f "$STATE_FILE" ]; then
  echo "=== Recovery Information After Context Compression ==="
  cat "$STATE_FILE"
  echo "=== End of Recovery Information ==="
  echo "The above is the working state automatically saved before compression. Please continue your work based on this information."
else
  echo "Note: Previous working state could not be restored."
fi

Point: The SessionStart matcher is set to "compact". This ensures it does not run during a normal session start, injecting state only when resuming after compression.

Hook 3: on-stop.sh — Automatic Change Logging per Response

Every time the AI's response completes, it checks git diff --stat, and if there are changes, appends them to session-log.md with a timestamp.

#!/bin/bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
STATE_DIR="$PROJECT_ROOT/.claude/state"
SESSION_LOG="$STATE_DIR/session-log.md"
LAST_HASH_FILE="$STATE_DIR/.last-diff-hash"

mkdir -p "$STATE_DIR"
cd "$PROJECT_ROOT"

DIFF_STAT=$(git diff --stat 2>/dev/null)
CACHED_STAT=$(git diff --cached --stat 2>/dev/null)

# Skip if there are no changes
if [ -z "$DIFF_STAT" ] && [ -z "$CACHED_STAT" ]; then
  exit 0
fi

# Skip if the diff is the same as the previous one (prevents duplicate logs)
CURRENT_HASH=$(echo "$DIFF_STAT$CACHED_STAT" | md5 2>/dev/null \
  || echo "$DIFF_STAT$CACHED_STAT" | md5sum 2>/dev/null | cut -d' ' -f1)
if [ -f "$LAST_HASH_FILE" ] && [ "$(cat "$LAST_HASH_FILE")" = "$CURRENT_HASH" ]; then
  exit 0
fi
echo "$CURRENT_HASH" > "$LAST_HASH_FILE"

# Append to session log
{
  echo "### $(date '+%Y-%m-%d %H:%M:%S')"
  [ -n "$CACHED_STAT" ] && echo '**Staged:**' && echo '```' && echo "$CACHED_STAT" && echo '```'
  [ -n "$DIFF_STAT" ] && echo '**Unstaged:**' && echo '```' && echo "$DIFF_STAT" && echo '```'
  echo ""
} >> "$SESSION_LOG"

Duplicate Prevention: The hash of the diff is maintained in .last-diff-hash, and it will not record if the diff is identical to the previous one. This prevents the log from becoming bloated when the AI repeatedly touches the same file.


Branch Strategy: Preventing Inter-Agent Conflicts

While hooks solved the context management issue, there is another problem: the risk of conflict where multiple agents edit the same file simultaneously.

The solution is simple: isolate agent work areas using Git branches.

main (stable)
├── phase-0          <- Phase 0: 1 person working
├── phase-1a         <- Phase 1: 2 people in parallel
├── phase-2a/auth    <- Phase 2: 4 people in parallel
├── phase-2b/home
├── phase-2c/mypage
└── phase-2d/item

Rules:

  • Each agent works only in their own branch
  • Do not touch other agents' files
  • Phase completion -> Self-review -> Merge to main (human approves)

By separating branches, even if one agent goes off the rails, it won't affect other agents' work. In the worst-case scenario, you can simply discard that branch.


CLAUDE.md: Rule File Automatically Read by AI

Even if you decide on hooks and a branch strategy, it's meaningless if the AI doesn't know the rules.

Claude Code automatically reads .claude/CLAUDE.md at the start of a session. If you write your rules here, the AI will operate according to them without needing instructions.

Contents:

  • Self-review checklist (data layer / screen implementation / final check)
  • Git branch operation rules
  • Leader/worker role distribution for multi-agent development
  • progress.md update rules
  • Commit/deploy rules
  • The principle of "do not implement based on speculation if not written in the design document"

This ensures that even when a new session is started, the AI understands the "rules of development" from the beginning.


Templating: Deploying to New Projects with One Command

It would be a waste to keep these mechanisms project-specific. I templated them to be reusable in any project.

_templates/
├── init-claude.sh              # Initialization script
└── claude-hooks/
    ├── CLAUDE.md               # Development rule template
    ├── settings.json           # Hooks configuration
    └── hooks/
        ├── pre-compact.sh
        ├── on-session-start.sh
        └── on-stop.sh

Deployment to a new project:

bash ~/work/Apps/_templates/init-claude.sh ~/work/Apps/NewProject

This simply:

  • Copies the 3 scripts to .claude/hooks/
  • Generates .claude/CLAUDE.md (AI automatically reads it)
  • Creates .claude/settings.json (merges hooks if one already exists)
  • Creates .claude/state/
  • Adds .claude/state/ to .gitignore

It also safely merges only the hooks portion in projects where settings.json already exists (e.g., with permissions set).


Result of Actual Use

I started using this foundation the day after building it, and the difference was clear.

Before:

  • Manually explain "what I was doing" every time compression runs
  • Write a handoff memo when the session cuts out (often forgetting to do so)
  • Trace back git logs when writing daily reports, asking "what did I do today?"

After:

  • AI automatically understands and resumes the previous state even when compression runs
  • Session timeouts are the same. Resume with just a word, "continue the work."
  • Since change history is automatically accumulated in session-log.md, for daily reports, I just ask the AI to "summarize based on this."

Pitfalls and Points to Note

Hooks are project-specific

Hooks written in .claude/settings.json only work when Claude Code is launched in that project directory. If you write them in a global configuration (~/.claude/settings.json), they will work in all projects, but the script placement must also be aligned. Managing it via templates + initialization scripts is more reliable.

CLAUDE.md is for rules, not memory

What should be written in CLAUDE.md are the rules for "how to develop." The working state of "what I am doing now" is the role of current-task.md managed by the hooks. Mixing these two will cause CLAUDE.md to bloat, burying the actual rules.

Clear session-log.md periodically

The Stop hook appends to the log every time there is a response, so it will bloat if left alone. It is better to clear it when you write your daily report.


Summary

Mechanism Solved Problem
PreCompact hook Automatically save working state before context compression
SessionStart hook Automatically restore state after compression. Resume with "continue"
Stop hook Automatically log changes per response. Materials for daily reports
Git branch isolation Prevent work conflicts between multiple agents
CLAUDE.md AI automatically reads rules at session start
Template + init script Deploy to new projects with 1 command

Although Claude Code's hooks are only briefly introduced in the official documentation, they are a very powerful mechanism in the context of solo development × AI agents. You can squash the structural weakness of "AI development"—context loss—with mechanics.

The source code has been extracted as a template, but it is not currently public. Please build it for your own environment based on the structure and script examples in this article.

Discussion