How to Create Custom Output Styles Using SessionStart Hooks in Claude Code
Claude Code enables custom output styles by allowing plugins to inject instructions via SessionStart hooks, which append additionalContext to the system prompt at the beginning of every session.
Claude Code's plugin architecture lets you permanently modify Claude's response style using SessionStart hooks that execute when a new chat session begins. By creating a plugin with a JSON manifest and a bash handler script, you can define custom output styles—such as concise, explanatory, or domain-specific modes—that persist for the entire conversation. This guide references the actual implementation in the anthropics/claude-code repository to show you how to build these plugins from scratch.
How SessionStart Hooks Modify the System Prompt
The SessionStart hook runs exactly once when Claude Code initializes a new session. According to the source code in plugins/explanatory-output-style/hooks-handlers/session-start.sh, the hook outputs a JSON payload containing a hookSpecificOutput object with an additionalContext field.
When Claude detects this output, it augments the system message with your custom text, effectively switching output styles without requiring further user prompts. This one-time injection avoids performance penalties during normal execution while ensuring consistent behavior throughout the session.
The hook script must output only valid JSON to stdout using a heredoc structure, then exit with code 0 to signal success.
Plugin Architecture and Required Files
Every custom style plugin requires a specific directory structure that Claude Code discovers automatically:
my-custom-style/
├── .claude-plugin/
│ └── plugin.json
└── hooks-handlers/
└── session-start.sh
Plugin Manifest
The .claude-plugin/plugin.json file declares metadata that Claude uses to identify and load your plugin. As shown in plugins/explanatory-output-style/.claude-plugin/plugin.json, this file must include the plugin name, version, description, and author information.
Hook Handler
The hooks-handlers/session-start.sh file contains executable code (typically Bash) that returns the JSON payload defining your style. The filename is arbitrary—Claude discovers handlers by reading the script content rather than enforcing strict naming conventions.
Optional Registration
While auto-discovery works for simple plugins, you can optionally create a hooks.json file in the plugin root for explicit hook registration. The schema for this file is validated by plugins/plugin-dev/skills/hook-development/scripts/validate-hook-schema.sh in the source repository.
Creating a Minimal "Concise" Style Plugin
To create a plugin that forces Claude to keep responses under 80 characters, create the following files:
{
"name": "concise-output-style",
"version": "1.0.0",
"description": "Makes Claude keep responses short and to the point.",
"author": { "name": "Your Name", "email": "[email protected]" }
}
hooks-handlers/session-start.sh
#!/usr/bin/env bash
cat << 'EOF'
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "You are in 'concise' output style. Keep every reply under 80 characters and avoid unnecessary detail."
}
}
EOF
exit 0
Install the plugin by running:
cc --plugin-dir /path/to/my-custom-style
From the next session onward, Claude will automatically apply the concise style without requiring additional prompts.
Extending Built-in Styles
The anthropics/claude-code repository includes reference implementations for common styles. You can extend these by copying and modifying the existing handlers:
- Copy the original handler from
plugins/explanatory-output-style/hooks-handlers/session-start.shorplugins/learning-output-style/hooks-handlers/session-start.sh - Modify the
additionalContextstring to add domain-specific instructions (e.g., "When suggesting design patterns, always include ASCII diagrams") - Update the plugin name in
plugin.jsonto avoid conflicts with built-in plugins
This approach leverages the validated structure of official plugins while customizing the behavioral instructions.
Combining Multiple Output Styles
You can ship multiple SessionStart handlers within a single plugin to create hybrid styles. Claude merges these in alphabetical order by plugin name, concatenating the additionalContext strings.
For example, to combine "Learning" and "Explanatory" modes:
hooks-handlers/session-start.sh
#!/usr/bin/env bash
cat << 'EOF'
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "You are in 'learning' output style. Ask clarifying questions before answering and adapt explanations to the user's skill level."
}
}
EOF
exit 0
hooks-handlers/session-start-explanatory.sh
#!/usr/bin/env bash
cat << 'EOF'
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Additionally, provide educational insights explaining why you chose specific approaches."
}
}
EOF
exit 0
Both scripts execute during session startup, creating a combined style that teaches interactively while explaining reasoning.
Registering Hooks Explicitly via hooks.json
For complex plugins requiring specific execution parameters, create a hooks.json file in the plugin root:
{
"SessionStart": [
{
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks-handlers/session-start.sh",
"timeout": 5
}
]
}
]
}
The matcher field supports regex patterns to conditionally apply styles based on session context. The schema validator in plugins/plugin-dev/skills/hook-development/scripts/validate-hook-schema.sh rejects malformed configurations, ensuring your hooks execute safely.
Summary
SessionStarthooks inject custom instructions once at the beginning of each Claude Code session via theadditionalContextfield in JSON output.- Plugin structure requires
.claude-plugin/plugin.jsonfor metadata andhooks-handlers/session-start.shfor the executable payload. - Output style is defined by the text content of
additionalContext, which Claude appends to its system prompt. - Multiple styles can be combined by shipping several handler scripts or extending official plugins from
plugins/explanatory-output-style/. - Validation occurs through
validate-hook-schema.shwhen using explicithooks.jsonregistration.
Frequently Asked Questions
What happens if a SessionStart hook returns invalid JSON?
Claude Code silently ignores malformed hook output and proceeds with the default system prompt. The session starts normally, but your custom style is not applied. Always ensure your script exits with code 0 and outputs only the JSON payload to stdout.
Can I apply different styles based on the project directory?
Yes. When using explicit hooks.json registration, modify the matcher field to use regex patterns that match specific directory paths or git repositories. Claude evaluates these matchers against the session context before executing the hook handler.
How do I debug a SessionStart hook that isn't working?
Check that your script is executable (chmod +x hooks-handlers/session-start.sh) and that the JSON structure matches the expected schema with hookSpecificOutput containing hookEventName and additionalContext. You can validate hooks.json syntax using the validate-hook-schema.sh script from the plugin development tools in the anthropics/claude-code repository.
Do SessionStart hooks affect performance?
No. These hooks execute only once during session initialization, typically completing in milliseconds. The additionalContext is appended to the system prompt at startup, causing no overhead during subsequent turn-taking or tool execution.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →