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:

.claude-plugin/plugin.json

{
  "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:

  1. Copy the original handler from plugins/explanatory-output-style/hooks-handlers/session-start.sh or plugins/learning-output-style/hooks-handlers/session-start.sh
  2. Modify the additionalContext string to add domain-specific instructions (e.g., "When suggesting design patterns, always include ASCII diagrams")
  3. Update the plugin name in plugin.json to 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

  • SessionStart hooks inject custom instructions once at the beginning of each Claude Code session via the additionalContext field in JSON output.
  • Plugin structure requires .claude-plugin/plugin.json for metadata and hooks-handlers/session-start.sh for 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.sh when using explicit hooks.json registration.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →