From gosu-mcp-core
This skill should be used when the user asks to "create a session hook", "add a temporary hook", "create a hook for this session", "set up a hook that activates immediately", "configure a one-time hook", or mentions "session-specific hook", "per-session hook", or "temporary hook". Provides guidance for creating session-scoped hooks that activate immediately without restarting Claude Code.
npx claudepluginhub gosu-code/claude-plugin --plugin gosu-mcp-coreThis skill uses the workspace's default tool permissions.
Create temporary, session-scoped hooks that activate immediately without restarting Claude Code. Session hooks enable dynamic hook configuration for the current session only, perfect for one-time workflows, debugging, or experimentation.
Guides creation of Claude Code plugin hooks with prompt-based and bash command types for PreToolUse, PostToolUse, Stop, and other events. Covers plugin hooks.json and settings.json formats.
Generates bash hook scripts and settings.json configs for Claude Code events like PreToolUse, PostToolUse, SessionStart from user requirements; handles chmod +x and validation via /hooks-check.
Guides development of event-driven hooks for Claude Code plugins using prompt-based and command-based configurations in hooks.json for events like PreToolUse, PostToolUse, Stop, and SessionStart to validate tools and automate workflows.
Share bugs, ideas, or general feedback.
Create temporary, session-scoped hooks that activate immediately without restarting Claude Code. Session hooks enable dynamic hook configuration for the current session only, perfect for one-time workflows, debugging, or experimentation.
./.claude/hooks/ (project) or ~/.claude/hooks/ (global)Session hooks are JSON configuration files named hooks.{session_id}.json stored in:
./.claude/hooks/hooks.{session_id}.json (local project, checked first)~/.claude/hooks/hooks.{session_id}.json (user home, fallback)The session_hook.py hook script (already registered in gosu-mcp-core) reads these files and executes the configured hooks for each event.
The session ID may be available in the Claude Code environment or current context. If you can't find it, MUST use AskUserQuestion tool to ask user to provide a value for the session_id. The value must be a valid UUID, repeat the question until user provide a valid UUID value.
Create .claude/hooks/hooks.{session_id}.json with hook configuration:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "json",
"json": {
"decision": "block",
"reason": "Please run tests before stopping"
}
}
]
}
]
}
}
The hook activates immediately. Trigger the event to verify it works.
{
"hooks": {
"EventName": [
{
"matcher": "optional-matcher-value",
"hooks": [
{
"type": "command|json",
...hook-specific-fields
}
]
}
]
}
}
commandExecute a shell command. The command receives the hook input via stdin.
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Must be "command" |
command | string | Yes | Shell command to execute |
timeout | number | No | Timeout in seconds (default: 15, max: 60) |
{
"type": "command",
"command": "python3 /path/to/script.py",
"timeout": 30
}
jsonReturn a fixed JSON response. Useful for blocking operations or injecting messages.
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Must be "json" |
json | object | No | JSON object to return (default: {}) |
exitcode | number | No | Exit code to return (default: 0) |
{
"type": "json",
"json": {"decision": "block", "reason": "Not allowed"},
"exitcode": 0
}
| Event | When Triggered | Common Use Cases |
|---|---|---|
| PreToolUse | Before any tool executes | Validation, blocking |
| PostToolUse | After tool completes | Logging, feedback |
| UserPromptSubmit | User submits prompt | Context injection |
| Stop | Main agent stopping | Completeness checks |
| SubagentStop | Subagent stopping | Task validation |
| SessionStart | Session begins | Context loading |
| SessionEnd | Session ends | Cleanup |
| PreCompact | Before compaction | Preserve context |
| Notification | User notified | Logging |
| PermissionRequest | Permission requested | Auto-approval/denial |
Two events support optional matcher field for filtering:
Match against source field values:
| Matcher Value | When It Matches |
|---|---|
startup | New session started |
resume | Resumed existing session |
clear | Session cleared with /clear |
compact | Session resumed after compaction |
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{"type": "json", "json": {"systemMessage": "Welcome to new session!"}}
]
}
]
}
}
Match against trigger field values:
| Matcher Value | When It Matches |
|---|---|
manual | User manually triggered compaction |
auto | Automatic context compaction |
{
"hooks": {
"PreCompact": [
{
"matcher": "auto",
"hooks": [
{"type": "json", "json": {"systemMessage": "Auto-compacting context..."}}
]
}
]
}
}
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "json",
"json": {
"decision": "block",
"reason": "Run `make test` before completing the task"
}
}
]
}
]
}
}
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "json",
"json": {
"systemMessage": "Remember: This project uses strict TypeScript. Always add types."
}
}
]
}
]
}
}
{
"hooks": {
"PreToolUse": [
{
"hooks": [
{
"type": "command",
"command": "python3 ./scripts/validate_tool_use.py",
"timeout": 10
}
]
}
]
}
}
{
"hooks": {
"PostToolUse": [
{
"hooks": [
{
"type": "command",
"command": "tee -a /tmp/claude-tool-log.json",
"timeout": 5
}
]
}
]
}
}
To create a session hook file for the current session:
# Get session ID (replace with actual session ID)
SESSION_ID="32502be3-59b3-4176-94c4-fd851d460417"
# Create .claude/hooks directory if needed
mkdir -p .claude/hooks
# Create session hook file
cat > ".claude/hooks/hooks.${SESSION_ID}.json" << 'EOF'
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "json",
"json": {
"decision": "block",
"reason": "Please ensure all tests pass before stopping"
}
}
]
}
]
}
}
EOF
{
"session_id": "32502be3-59b3-4176-94c4-fd851d460417",
"hook_event_name": "PreToolUse",
"tool_name": "Write",
"tool_input": {"file_path": "/path/to/file", "content": "..."},
"cwd": "/current/working/dir",
"transcript_path": "/path/to/transcript.txt"
}
{
"decision": "approve|block",
"reason": "Explanation for the decision",
"systemMessage": "Additional context for Claude"
}
{
"hookSpecificOutput": {
"permissionDecision": "allow|deny|ask"
},
"systemMessage": "Explanation"
}
Session hook files remain until manually deleted. To clean up:
# Remove specific session hook
rm .claude/hooks/hooks.{session_id}.json
# Remove all session hooks in project
rm .claude/hooks/hooks.*.json
# Remove all session hooks in home directory
rm ~/.claude/hooks/hooks.*.json
/id command).claude/hooks/hooks.{session_id}.jsonjq . .claude/hooks/hooks.*.json# Validate session hook file
cat .claude/hooks/hooks.{session_id}.json | jq .
Available in scripts/:
create_session_hook.py - Create session hooks programmatically# Block stopping with a message
python3 scripts/create_session_hook.py SESSION_ID Stop json --decision block --reason "Run tests first"
# Inject context on session start
python3 scripts/create_session_hook.py SESSION_ID SessionStart json --matcher startup --message "Welcome!"
# Add command hook for logging
python3 scripts/create_session_hook.py SESSION_ID PostToolUse command --command "tee -a /tmp/log.json"
# Dry run to preview
python3 scripts/create_session_hook.py SESSION_ID Stop json --decision block --reason "Test" --dry-run
Working examples in examples/:
block-stop-until-tests.json - Block stopping until tests passinject-context-startup.json - Inject context on session startlog-tool-usage.json - Log all tool usage to fileprecompact-reminder.json - Add reminder before context compactionFor advanced patterns, see:
references/advanced-patterns.md - Complex hook configurationsreferences/hook-output-formats.md - Complete output format reference