Modifying System Prompts

View Source

Learn how to customize Claude's behavior by modifying system prompts using output styles, appended instructions, and custom system prompts.

Official Documentation: This guide is based on the official Claude Agent SDK documentation. Examples are adapted for Elixir.


System prompts define Claude's behavior, capabilities, and response style. The Elixir SDK provides three primary ways to customize system prompts: using output styles (persistent, file-based configurations), appending to Claude Code's prompt, or using a fully custom prompt. You can also use CLAUDE.md files for project-level instructions and pass settings directly via the :settings option.

Understanding system prompts

A system prompt is the initial instruction set that shapes how Claude behaves throughout a conversation.

Default behavior: The Agent SDK uses a minimal system prompt by default. It contains only essential tool instructions but omits Claude Code's coding guidelines, response style, and project context. To include the full Claude Code system prompt, use :system_prompt with a preset value. To customize behavior, use :system_prompt to replace the default entirely, or :append_system_prompt to add instructions while keeping the defaults.

Claude Code's system prompt includes:

  • Tool usage instructions and available tools
  • Code style and formatting guidelines
  • Response tone and verbosity settings
  • Security and safety instructions
  • Context about the current working directory and environment

Methods of modification

Method 1: CLAUDE.md files (project-level instructions)

CLAUDE.md files provide project-specific context and instructions that are automatically read by the Agent SDK when it runs in a directory. They serve as persistent "memory" for your project.

How CLAUDE.md works with the SDK

Location and discovery:

  • Project-level: CLAUDE.md or .claude/CLAUDE.md in your working directory
  • User-level: ~/.claude/CLAUDE.md for global instructions across all projects

Important: The SDK only reads CLAUDE.md files when you explicitly configure :setting_sources:

  • Include "project" to load project-level CLAUDE.md
  • Include "user" to load user-level CLAUDE.md (~/.claude/CLAUDE.md)

Using :system_prompt alone does not automatically load CLAUDE.md -- you must also specify setting sources.

Content format: CLAUDE.md files use plain markdown and can contain:

  • Coding guidelines and standards
  • Project-specific context
  • Common commands or workflows
  • API conventions
  • Testing requirements

Example CLAUDE.md

# Project Guidelines

## Code Style
- Use Elixir 1.18+ features
- Prefer pattern matching over conditionals
- Always include @moduledoc and @doc attributes

## Testing
- Run `mix test` before committing
- Maintain >80% code coverage
- Use ExUnit with Mox for mocking

## Commands
- Quality checks: `mix quality`
- Dev server: `iex -S mix phx.server`
- Type check: `mix dialyzer`

Using CLAUDE.md with the SDK

# You must specify setting_sources to load CLAUDE.md
{:ok, result} = ClaudeCode.query("Add a new GenServer module for user sessions",
  setting_sources: ["project"]
)

When to use CLAUDE.md

Best for:

  • Team-shared context -- Guidelines everyone should follow
  • Project conventions -- Coding standards, file structure, naming patterns
  • Common commands -- Build, test, deploy commands specific to your project
  • Long-term memory -- Context that should persist across all sessions
  • Version-controlled instructions -- Commit to git so the team stays in sync

Key characteristics:

  • Persistent across all sessions in a project
  • Shared with team via git
  • Automatic discovery (no code changes needed)
  • Requires loading settings via :setting_sources

Method 2: Output styles (persistent configurations)

Output styles are saved configurations that modify Claude's system prompt. They are stored as markdown files and can be reused across sessions and projects.

Creating an output style

Output style files are markdown files with YAML frontmatter, stored in ~/.claude/output-styles/ (user-level) or .claude/output-styles/ (project-level):

---
name: Code Reviewer
description: Thorough code review assistant
keep-coding-instructions: true
---

You are an expert code reviewer.

For every code submission:
1. Check for bugs and security issues
2. Evaluate performance
3. Suggest improvements
4. Rate code quality (1-10)

Supported frontmatter fields:

FieldPurposeDefault
nameName of the output style, if not the file nameInherits from file name
descriptionDescription shown in the UI of /output-styleNone
keep-coding-instructionsWhether to keep the parts of Claude Code's system prompt related to codingfalse

Using output styles

Once created, activate output styles via:

  • CLI: /output-style [style-name]
  • Settings: .claude/settings.local.json
  • Create new: /output-style:new [description]

Note for SDK users: Output styles are loaded when you include "user" or "project" in your :setting_sources option:

{:ok, result} = ClaudeCode.query("Review this module for issues",
  setting_sources: ["user"]
)

When to use output styles

Best for:

  • Persistent behavior changes across sessions
  • Team-shared configurations
  • Specialized assistants (code reviewer, data scientist, DevOps)
  • Complex prompt modifications that need versioning

Examples:

  • Creating a dedicated SQL optimization assistant
  • Building a security-focused code reviewer
  • Developing a teaching assistant with specific pedagogy

Method 3: Appending to the system prompt

Use :append_system_prompt to add your custom instructions while preserving all built-in functionality. This is the recommended approach for most use cases.

{:ok, result} = ClaudeCode.query("Help me process this large CSV file",
  append_system_prompt: "Focus on performance optimization. Prefer Stream over Enum for large datasets."
)

This keeps Claude's tool-usage instructions, safety guidelines, and environment context intact while adding your custom behavior.

Method 4: Custom system prompts

Use :system_prompt to replace the default system prompt entirely with your own instructions:

{:ok, result} = ClaudeCode.query("Create a data processing pipeline",
  system_prompt: """
  You are an Elixir coding specialist.
  Follow these guidelines:
  - Write clean, well-documented code
  - Use typespecs for all public functions
  - Include comprehensive @moduledoc and @doc attributes
  - Prefer functional programming patterns
  - Always explain your code choices
  """
)

Warning: Overriding the system prompt replaces Claude Code's default instructions, which include tool usage guidance, safety instructions, and environment context. Only use this when you need complete control over Claude's behavior.

Method 5: Settings configuration

Pass settings directly as a map or file path using the :settings option:

# As a map (auto-encoded to JSON for the CLI)
{:ok, session} = ClaudeCode.start_link(
  settings: %{
    "preferredLanguage" => "elixir",
    "codeStyle" => "functional"
  }
)

# Or as a path to a JSON file
{:ok, session} = ClaudeCode.start_link(
  settings: "/path/to/settings.json"
)

Per-query overrides

Both :system_prompt and :append_system_prompt can be overridden at the query level. Query-level options take precedence over session defaults for that single query only:

{:ok, session} = ClaudeCode.start_link(
  append_system_prompt: "You are a general coding assistant."
)

# This query uses a different system prompt override
session
|> ClaudeCode.stream("Review this module",
     system_prompt: "You are a security auditor. Focus on OWASP vulnerabilities.")
|> ClaudeCode.Stream.text_content()
|> Enum.each(&IO.write/1)

# Next query goes back to the session default
session
|> ClaudeCode.stream("Help me write tests")
|> ClaudeCode.Stream.text_content()
|> Enum.each(&IO.write/1)

Comparison of approaches

FeatureCLAUDE.mdOutput Styles:append_system_promptCustom :system_prompt:settings
PersistencePer-project fileSaved as filesSession onlySession onlySession only
ReusabilityPer-projectAcross projectsCode duplicationCode duplicationFile or code
ManagementOn filesystemCLI + filesIn codeIn codeIn code
Default toolsPreservedPreservedPreservedLost (unless included)Preserved
Built-in safetyMaintainedMaintainedMaintainedMust be addedMaintained
Environment contextAutomaticAutomaticAutomaticMust be providedAutomatic
Customization levelAdditions onlyReplace defaultAdditions onlyComplete controlKey-value config
Version controlWith projectYesWith codeWith codeFile or code
ScopeProject-specificUser or projectCode sessionCode sessionCode session

Use cases and best practices

When to use CLAUDE.md

Best for:

  • Project-specific coding standards and conventions
  • Documenting project structure and architecture
  • Listing common commands (build, test, deploy)
  • Team-shared context that should be version controlled
  • Instructions that apply to all SDK usage in a project

Examples:

  • "All API endpoints should use plug pipelines and proper error tuples"
  • "Run mix quality before committing"
  • "Database migrations are in the priv/repo/migrations/ directory"

Important: To load CLAUDE.md files, you must explicitly set :setting_sources to ["project"]. Using :system_prompt alone does not automatically load CLAUDE.md without this setting.

When to use output styles

Best for:

  • Persistent behavior changes across sessions
  • Team-shared configurations
  • Specialized assistants (code reviewer, data scientist, DevOps)
  • Complex prompt modifications that need versioning

Examples:

  • Creating a dedicated SQL optimization assistant
  • Building a security-focused code reviewer
  • Developing a teaching assistant with specific pedagogy

When to use :append_system_prompt

Best for:

  • Adding specific coding standards or preferences
  • Customizing output formatting
  • Adding domain-specific knowledge
  • Modifying response verbosity
  • Enhancing Claude Code's default behavior without losing tool instructions

When to use custom :system_prompt

Best for:

  • Complete control over Claude's behavior
  • Specialized single-session tasks
  • Testing new prompt strategies
  • Situations where default tools are not needed
  • Building specialized agents with unique behavior

Combining approaches

You can combine these methods for maximum flexibility:

# Assuming an output style is active (via /output-style),
# add session-specific focus areas
{:ok, result} = ClaudeCode.query("Review this authentication module",
  append_system_prompt: """
  For this review, prioritize:
  - OAuth 2.0 compliance
  - Token storage security
  - Session management
  """,
  setting_sources: ["project"]
)

See also