CLI Sync
Synchronize the ClaudeCode Elixir SDK with the Claude CLI to detect schema changes, new options, and update the bundled version.
Overview
The Claude CLI evolves independently of this SDK. This skill provides a systematic workflow to:
- •Detect schema drift - Compare CLI JSON output against our message/content structs
- •Find new CLI options - Compare
--helpoutput against our Options module - •Check SDK documentation - Reference TypeScript/Python SDK docs for type information
- •Update bundled version - Sync the installer to the current CLI version
Important: The Python SDK (claude-agent-sdk-python) is a thin wrapper around the CLI — it spawns claude as a subprocess and communicates via streaming JSON, just like our SDK. This makes it the closest reference for what is possible and appropriate to implement. Its types.py defines the canonical message types, content blocks, and options that a CLI-wrapping SDK should support. When in doubt about whether a field or option belongs in our SDK, check the Python SDK first.
Quick Start
For a full sync, execute these steps in order:
- •Check current versions
- •Run test query to capture live schema
- •Compare against struct definitions
- •Check CLI help for new options
- •Update installer version if needed
Workflow
Step 1: Version Check
Determine installed CLI version and compare to bundled version.
# Get installed CLI version claude --version # Check bundled version in installer grep -E "@default_cli_version|cli_version:" lib/claude_code/installer.ex
Step 2: Schema Comparison
Run multiple test scenarios to capture different message types. Each scenario targets specific structs. The -p flag enables non-interactive (print) mode.
Scenario A: Basic query (triggers: system_message/init, assistant_message, result_message/success, text_block)
echo "What is 2+2?" | claude --output-format stream-json --verbose --max-turns 1 -p 2>/dev/null
Scenario B: Partial streaming (triggers: partial_assistant_message/stream_event)
echo "Count from 1 to 5" | claude --output-format stream-json --verbose --include-partial-messages --max-turns 1 -p 2>/dev/null
Scenario C: Tool use (triggers: tool_use_block, tool_result_block, user_message)
echo "Read the first 3 lines of mix.exs" | claude --output-format stream-json --verbose --max-turns 1 -p 2>/dev/null
Scenario D: Error result - max turns (triggers: result_message/error_max_turns)
echo "Create a file called /tmp/test_sync.txt with hello world, then read it back" | claude --output-format stream-json --verbose --max-turns 1 -p 2>/dev/null
This should hit the turn limit mid-task and produce a result with "subtype": "error_max_turns".
Scenario E: Hook messages (triggers: system_message/hook_started, system_message/hook_response)
If the project has hooks configured (e.g., SessionStart hooks), Scenario A will already emit these. Check the output for "subtype": "hook_started" and "subtype": "hook_response" messages. If no hooks are configured, skip this - but still ensure the parser handles unknown system subtypes gracefully.
Scenario F: Extended thinking (triggers: thinking_block)
echo "Think step by step about why 17 is prime" | claude --output-format stream-json --verbose --max-turns 1 --model claude-opus-4-6 -p 2>/dev/null
Note: Extended thinking availability depends on model and account configuration. If thinking blocks appear, compare against thinking_block.ex. The block has type, thinking, and signature fields.
Exceptional cases (rely on SDK docs, not live testing):
- •
compact_boundary_message- Only during context compaction in long conversations. Cannot be reliably triggered in a single query. Check the TypeScript SDKSDKCompactBoundaryMessagetype for the expected shape:type: "system",subtype: "compact_boundary",compact_metadata: {trigger, pre_tokens}. - •
result_message/error_max_budget_usd- Requires a budget to be exceeded. Check SDK docs for theerror_max_budget_usdanderror_max_structured_output_retriessubtypes.
Coverage Checklist
After running scenarios, verify you have captured output covering:
- •
SystemMessage(subtype: init) - Scenario A - •
SystemMessage(subtype: hook_started) - Scenario E (if hooks configured) - •
SystemMessage(subtype: hook_response) - Scenario E (if hooks configured) - •
AssistantMessagewith nested message.content - Scenario A - •
UserMessagewith tool results - Scenario C - •
ResultMessage(subtype: success) - Scenario A - •
ResultMessage(subtype: error_max_turns) - Scenario D - •
PartialAssistantMessage(stream_event) - Scenario B - •
TextBlock- Scenario A - •
ToolUseBlock- Scenario C - •
ToolResultBlock- Scenario C - •
ThinkingBlock- Scenario F (if available) - •
CompactBoundaryMessage- SDK docs only
Parse the output and compare against struct definitions in:
Message Types (in lib/claude_code/message/):
- •
system_message.ex- All system subtypes: init (with dedicated fields), hook_started, hook_response, etc. (non-init usedatamap) - •
assistant_message.ex- Messages with nestedmessage.contentblocks, optionalerrorfield - •
user_message.ex- User input with tool results, optionaltool_use_resultmetadata - •
result_message.ex- Final response with result text and is_error flag - •
partial_assistant_message.ex- Streaming partial content - •
compact_boundary_message.ex- Context compaction boundaries
Content Blocks (in lib/claude_code/content/):
- •
text_block.ex- Text content with type: "text" - •
tool_use_block.ex- Tool invocations with id, name, input - •
tool_result_block.ex- Tool outputs with tool_use_id, content, is_error - •
thinking_block.ex- Extended thinking with signature
For each message type in the CLI output:
- •Identify the message type from
"type"field - •Compare JSON keys against struct fields
- •Report new fields, missing fields, and type mismatches
- •Suggest snake_case atom names for new fields
See references/struct-definitions.md for complete field mappings.
Step 3: Options Comparison
Compare CLI help output against the Options module.
# Get CLI help claude --help
Options module comparison points in lib/claude_code/options.ex:
- •Check
@session_opts_schemafor option definitions - •Check
convert_option_to_cli_flag/2clauses for CLI mappings - •Look for new flags in
--helpnot in our schema - •Check for deprecated flags we still support
For each new flag found, suggest:
- •The
:snake_case_atomoption name - •NimbleOptions type definition
- •
convert_option_to_cli_flag/2clause
Step 4: SDK Documentation Reference
Check official SDK source code and documentation for type definitions and options. Note that CLI output is the authoritative source - SDK docs may lag behind.
Fetching Python SDK Source via gh
Use the gh CLI to fetch file contents directly from the Python SDK repository. This is more reliable than WebFetch and allows you to inspect any file in the repo:
# Fetch any file from the Python SDK repo: gh api repos/anthropics/claude-agent-sdk-python/contents/PATH/TO/FILE --jq '.content' | base64 -d # List directory contents to discover files: gh api repos/anthropics/claude-agent-sdk-python/contents/PATH/TO/DIR --jq '.[].name'
Key files to fetch for options comparison:
# CLI flag mapping (opts→CLI flags) - THE most useful file for options sync: gh api repos/anthropics/claude-agent-sdk-python/contents/src/claude_agent_sdk/_internal/transport/subprocess_cli.py --jq '.content' | base64 -d # Types/schema definitions (message types, content blocks, options) - THE canonical reference: gh api repos/anthropics/claude-agent-sdk-python/contents/src/claude_agent_sdk/types.py --jq '.content' | base64 -d
Key files to fetch for message/content type comparison:
# Discover available type modules: gh api repos/anthropics/claude-agent-sdk-python/contents/src/claude_agent_sdk --jq '.[].name' # Errors and type definitions: gh api repos/anthropics/claude-agent-sdk-python/contents/src/claude_agent_sdk/_errors.py --jq '.content' | base64 -d
The subprocess_cli.py file shows exactly which options are converted to CLI flags in _build_command().
The types.py file is the canonical reference for message schemas — since the Python SDK is also a CLI wrapper, its type definitions map directly to what our structs should support. Key types to compare:
- •
UserMessage— hastool_use_result: dict[str, Any] | None - •
AssistantMessage— haserror: AssistantMessageError | None - •
SystemMessage— genericsubtype: str+data: dict[str, Any](handles all subtypes) - •
ResultMessage,StreamEvent, content blocks (TextBlock,ThinkingBlock,ToolUseBlock,ToolResultBlock) - •
ClaudeAgentOptions— all available SDK options
SDK Documentation Pages
For additional context, these doc pages can be checked via WebFetch:
- •TypeScript SDK Options: https://platform.claude.com/docs/en/agent-sdk/typescript#options
- •Python SDK Options: https://platform.claude.com/docs/en/agent-sdk/python#claude-agent-options
What to Compare
- •Options comparison: Check which CLI flags the official SDKs expose as options. If a CLI flag is NOT present in either official SDK, it likely doesn't make sense for our SDK (e.g.,
--chrome,--ide,--replay-user-messagesare CLI-only). - •Message type definitions: Compare
SDKMessageunion types against our message structs. - •Content block schemas: Compare content block types against our content modules.
- •New option descriptions: Check for options added in official SDKs that we should also support.
Step 5: Update Bundled Version
After confirming compatibility, update the bundled CLI version.
The single source of truth for the CLI version is @default_cli_version in lib/claude_code/installer.ex. This is the only place that needs updating:
# Update @default_cli_version to match installed CLI @default_cli_version "X.Y.Z"
No other files should hardcode the CLI version. Docs, comments, and config examples use generic placeholders like "x.y.z".
Step 6: Verify and Test
After making changes:
# Run quality checks mix quality # Run tests mix test # Verify struct parsing with updated code mix test test/claude_code/message_test.exs
Pattern References
For detailed patterns on adding new fields and options:
- •Schema patterns - See
references/struct-definitions.mdfor field mappings, adding new fields, handling nested structures, and optional field patterns - •Options patterns - See
references/cli-flags.mdfor boolean flags, value flags, list flags, and CLI conversion clauses
Changelog Checking
Before making changes, check the CLI changelog for breaking changes:
# Check recent CLI releases gh release list --repo anthropics/anthropic-quickstarts --limit 10
Or check the changelog documentation if available at:
Common Issues
Flags to Ignore
When comparing --help output, ignore these flags that appear in CLI help but should NOT be added to options.ex:
SDK-internal flags (always set by the SDK automatically):
- •
--verbose- Already always enabled by SDK incli.ex(required for streaming) - •
--output-format- Already always set tostream-jsonby SDK - •
--input-format- Already always set tostream-jsonby SDK - •
--print/-p- Internal print mode flag
CLI-only flags (not in either official SDK, not relevant for programmatic use):
- •
--chrome/--no-chrome- Browser integration for interactive terminal - •
--ide- IDE auto-connect for interactive terminal - •
--replay-user-messages- Internal streaming protocol flag (handled transparently by SDKs)
Cross-reference against official SDK options pages to confirm whether new flags belong in the SDK.
camelCase vs snake_case
CLI uses camelCase, Elixir uses snake_case:
- •
"inputTokens"->:input_tokens - •
"isError"->:is_error - •
"toolUseId"->:tool_use_id
Nested Message Structure
Assistant and User messages have nested structure:
- •Access content via
message.message.content, notmessage.content - •The outer
messageis our struct, innermessageis from CLI JSON
Result vs Assistant Content
Final response text comes from ResultMessage, not AssistantMessage:
- •Use
result.resultfor the final answer - •AssistantMessage contains intermediate tool use and thinking
Additional Resources
Reference Files
- •
references/struct-definitions.md- Complete field mappings for all structs - •
references/cli-flags.md- CLI flag documentation and mappings
Scripts
Ensure scripts are executable before first use: chmod +x scripts/*.sh
- •
scripts/run-test-query.sh- Run test query and save JSON output - •
scripts/compare-versions.sh- Compare installed vs bundled versions (run from project root)
Updating Documentation
After syncing, update:
- •CLAUDE.md - If new options or message types added
- •CHANGELOG.md - Document schema alignment changes
- •Module docs - Update option descriptions in Options module