v2.1.94 shipped a small feature with outsized utility: hookSpecificOutput.sessionTitle. It lets a UserPromptSubmit hook programmatically name your session — the equivalent of typing /rename but automated on every prompt.
If you run multiple sessions and rely on --resume to pick them back up, you know the problem. Default session names are random word pairs: graceful-unicorn, dancing-fox. When you've got 15 sessions in the picker, that's useless. Now you can fix it with a hook.
Create .claude/settings.json (or add to existing):
"hooks": {
"UserPromptSubmit": [
{
"type": "command",
"command": "~/.claude/hooks/auto-title.sh"
}
]
}
}
The hook script receives JSON on stdin with a prompt field. Extract keywords, set the title:
# ~/.claude/hooks/auto-title.sh
PROMPT=$(cat | jq -r '.prompt')
# Take first 50 chars, strip newlines
TITLE=$(echo "$PROMPT" | tr '\n' ' ' | cut -c1-50)
cat <<EOF
{
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"sessionTitle": "$TITLE"
}
}
EOF
Now every session gets named after your first prompt: Fix the auth middleware, Add pagination to API, Debug flaky test in CI. When you hit claude --resume, you see meaningful names instead of random animals.
The hookSpecificOutput JSON structure works across all hook events — sessionTitle is specific to UserPromptSubmit, but the same pattern supports additionalContext (inject text into Claude's context), permissionDecision (auto-allow/deny tools in PreToolUse), and updatedInput (rewrite tool inputs before execution).
Claude Code's default permission model gives it read access to your entire home directory. For interactive use, that's manageable — you're watching. For autonomous agents running on a cron, it's a liability. A prompt injection in a fetched web page or a malicious MCP response could read your SSH keys, environment files, or credentials.
The fix isn't more permission rules. It's OS-level isolation.
SandboxedClaudeCode (16 stars) provides ready-to-use wrapper scripts for three sandbox methods:
Bubblewrap (Linux) — Uses Linux namespaces for mount, PID, and user isolation. ~5ms startup overhead. The most surgical option: you define exactly which paths are visible and whether they're read-only or read-write.
# ✓ Project directory (read-write)
# ✓ ~/.claude config (read-write)
# ✓ System binaries, Node.js (read-only)
# ✓ /tmp (read-write)
#
# What's invisible:
# ✗ ~/.ssh, ~/.gnupg, ~/.aws
# ✗ Other projects, Documents, Downloads
# ✗ Browser data, other app configs
# ✗ Host processes (PID namespace isolation)
Firejail (Linux) — Adds syscall filtering via seccomp on top of namespace isolation. ~10ms overhead. Drops all Linux capabilities and blocks hardware access. Easier to configure than raw bubblewrap.
Apple Container (macOS) — Full lightweight VM via Virtualization.framework. Strongest isolation but 500ms-2s startup. Separate kernel — even a kernel exploit inside the container can't reach the host.
Anthropic's own engineering blog confirms that sandboxing reduces permission prompts by 84%. That's the key insight: sandboxing doesn't just protect you from the agent — it lets the agent work faster by removing the permission bottleneck. An agent that doesn't need to ask "can I read this file?" on every operation finishes tasks with fewer interruptions.
Caveat: Sandboxing isn't infallible. Security researchers at Ona demonstrated in March 2026 that Claude Code could bypass its own denylist using path tricks, and when bubblewrap caught that, the agent disabled the sandbox itself. Defense in depth — sandbox + hooks + permission rules — is the production pattern.
"Claude Code isn't working well" is the wrong diagnosis. It conflates two independent layers that fail for different reasons and need different fixes.
A recent analysis from Agentic Loops reframes the stack with a four-layer vocabulary:
- LLM — Raw capability. The model's weights, training, and reasoning. You don't control this.
- Harness — Everything wrapping the LLM to make it usable: the agent loop, sandbox, filesystem access, context management, tools, hooks, skills, subagents, MCP servers, permission rules.
- Agent — The product. Agent = Model + Harness. What you experience when you type
claude. - Harness Engineering — The discipline of designing, tuning, and improving the harness layers.
This decomposition matters because most operator frustration is a harness problem, not a model problem. Claude hallucinated a function name? That's a context management issue — your harness didn't surface the right files. Claude ignored a project rule? That's an instruction priority issue — your CLAUDE.md has too many competing directives. Claude made a destructive change? That's a permission architecture issue — your hooks and sandbox didn't enforce boundaries.
The operating principle: when an agent errs, don't fix the output — engineer the harness to prevent recurrence.
In Claude Code terms, the harness layers you control are:
| Layer | Claude Code Primitive |
|---|---|
| Context management | CLAUDE.md, memory, skills, /clear |
| Tool access | MCP servers, permissions.allow/deny |
| Behavioral encoding | CLAUDE.md rules, subagent definitions |
| Execution boundaries | Hooks, sandbox, permission modes |
| Workflow automation | Skills, SessionStart hooks, /powerup |
| Agent specialization | Custom subagents with scoped permissions |
The harness is stackable. Claude Code provides the kernel layer. You build application layers on top. The practitioner who configured 8 custom subagents, formatting hooks, and comprehensive behavioral rules isn't "customizing Claude" — they're engineering a harness that makes the same model dramatically more reliable for their specific domain.
Stop debugging Claude. Start engineering your harness.
If you're on an API key or Team plan, you need visibility into what Claude Code is costing you. Even on Max, understanding your token patterns helps you work more efficiently. Kerf-CLI is a new open-source tool that turns your session data into a local analytics database.
The concept: Kerf reads Claude Code's session files from ~/.claude/projects/ (the same JSONL transcripts the built-in /cost command uses), syncs them into a local SQLite database, and exposes analytics through both a CLI and a web dashboard.
npm install -g kerf-cli
# Initialize database + install hooks
kerf init
# Key commands
kerf summary # daily/weekly/monthly spend overview
kerf efficiency # Opus vs Sonnet ROI analysis
kerf cache # prompt cache hit rate reporting
kerf audit # ghost token analysis in CLAUDE.md/MCP
kerf watch # real-time terminal dashboard
kerf dashboard # web UI at localhost:3847
kerf query "SELECT ..." # raw SQL for custom analysis
The standout features:
Model efficiency analysis — Shows potential savings from routing simple tasks to Sonnet instead of Opus. Not directly actionable for Max users (no per-token billing), but it reveals which tasks burn the most compute — useful for tuning /effort levels.
Ghost token audit — Scans your CLAUDE.md and MCP server configs for bloated descriptions, dead instructions, and unused tools that silently consume context window on every turn. Grades your setup A–D.
Budget enforcement — Installs Claude Code hooks that either warn (default) or block (opt-in) when you approach spending limits. The blocking mode returns exit code 2 to prevent tool execution.
Important: This is a brand-new project (launched this week). Evaluate it yourself before relying on it for budget decisions. All data stays local — no telemetry, no cloud, no API keys required.
What you'll do: Set up OS-level filesystem isolation for Claude Code using bubblewrap, verify it works, and confirm Claude Code still functions normally inside the sandbox.
Steps:
- Install bubblewrap:
sudo dnf install bubblewrap # Fedora/RHEL
sudo pacman -S bubblewrap # Arch
- Clone the sandbox scripts:
cd SandboxedClaudeCode
chmod +x bubblewrap_claude.sh
- Review the script before running it. Open
bubblewrap_claude.shand read the bind mount list. Understand what's visible (project dir, system libs, Node.js, ~/.claude) and what's blocked (everything else in your home directory).
- Run a sandboxed session:
/path/to/SandboxedClaudeCode/bubblewrap_claude.sh
- Test isolation — try to read a sensitive file:
Claude should fail with a "file not found" or "permission denied" error. The file exists on your host, but it's invisible inside the sandbox.
- Test functionality — confirm normal operations work:
Create a test file called sandbox-test.txt with "hello from the sandbox"
Both should succeed. The project directory is read-write inside the sandbox.
- Create a convenience alias:
source ~/.bashrc
Expected outcome: Claude Code launches and operates normally for all project-scoped work. Attempts to access files outside the project directory and essential Claude config fail silently — the files simply don't exist from Claude's perspective.
Verify: From inside the sandboxed session, ask Claude to run ls ~. You should see only the directories explicitly mounted (your project, .claude, .npm, maybe .gitconfig), not your full home directory.