Skip to main content

Claude Saga

A side-effect manager for Python scripts, specifically designed for building maintainable (easy to build, test, debug) Claude Code hooks, inspired by Redux Saga.

Disclaimer:

Unstable - API subject to change.

Quick Start

Conceptual overview

from claude_saga import (
    BaseSagaState, SagaRuntime,
    Call, Put, Select, Log, Stop, Complete,
    run_command_effect
)

def add(x, y):
    return x + y

class State(BaseSagaState):
    math_result: int = 2
    command_result: str = ""

def my_saga():
    yield Log("info", "Starting saga")
    initial_state = yield Select()
    math_result = yield Call(add, initial_state.math_result, 3)
    command_result = yield Call(run_command_effect, "echo 'Hello World'")
    if command_result is None:
        yield Log("error", "unable to run command")
        yield Stop("hook failed, exited early")
    yield Put({"command_result": command_result.stdout, "math_result": math_result})
    yield Complete("Saga completed successfully")

runtime = SagaRuntime(State())
final_state = runtime.run(my_saga())
print(final_state.to_json())

Building Claude Code Hooks

Claude Saga handles input/output conventions of claude code hooks:

#!/usr/bin/env python
import json
import sys
from claude_saga import (
    BaseSagaState, SagaRuntime,
    validate_input_saga, parse_json_saga,
    Complete
)

class HookState(BaseSagaState):
    # Add your custom state fields
    pass

def main_saga():
    # Validate and parse input
    # https://docs.anthropic.com/en/docs/claude-code/hooks#hook-input
    yield from validate_input_saga()
    # Adds input data to state
    yield from parse_json_saga()
    
    # Your hook logic here
    
    # Complete
    yield Complete("Hook executed successfully")

def main():
    runtime = SagaRuntime(HookState())
    # Final state is an object that conforms to common json fields:
    # https://docs.anthropic.com/en/docs/claude-code/hooks#common-json-fields
    final_state = runtime.run(main_saga())
    # Claude Code exit code behavior:
    # https://docs.anthropic.com/en/docs/claude-code/hooks#simple%3A-exit-code
    print(json.dumps(final_state.to_json()))
    sys.exit(0 if final_state.continue_ else 1)

if __name__ == "__main__":
    main()

Effect Types

Call

Execute functions, including(and especially) those with side-effects:

result = yield Call(function, arg1, arg2, kwarg=value)

Put

Update the state:

yield Put({"field": "value"})
# or with a function
yield Put(lambda state: MyState(counter=state.counter + 1))

Select

Read from the state:

state = yield Select()
# or with a selector
counter = yield Select(lambda state: state.counter)

Log

Log messages at different levels:

yield Log("info", "Information message")
yield Log("error", "Error message")
yield Log("debug", "Debug message")  # Only shown with DEBUG=1

Stop

Stop execution with an error, hook output contains continue:false:

yield Stop("Error message")

Complete

Complete saga successfully, hook output contains continue:true:

yield Complete("Success message")

Common Effects

The library includes common effects

  • log_info, log_error, log_debug
  • run_command_effect(cmd, cwd=None, capture_output=True) - Run shell commands
  • write_file_effect(path, content) - Write files
  • change_directory_effect(path) - Change working directory
  • create_directory_effect(path) - Create directories
  • connect_pycharm_debugger_effect() - Connect to PyCharm debugger

Notes:

  • When you write your own effects - you don't need to implement error handling - the saga runtime handles Call errors (logs them to stdout) and returns None on failure.
    • If you want to terminate the saga on effect failure, check if the Call result is None and yield a Stop.

Common Sagas

Pre-built sagas for common tasks:

  • validate_input_saga() - Validate stdin input is provided
  • parse_json_saga() - Parse JSON from stdin into hook state (parses specifically for Claude Code hook input)

Development

Setup

uv pip install -e .

Examples

The examples/ directory contains a practical demonstration:

  • simple_command_validator.py - Claude Code hook for validating bash commands (saga version of the official example)
# This will fail since the expected input to stdin is not provided
uv run examples/simple_command_validator.py

# Handle claude code stdin conventions, this command passes validation 
echo '{"tool_name": "Bash", "tool_input": {"command": "ls -la"}}' | uv run examples/simple_command_validator.py

# This command fails validation (uses grep instead of rg)
echo '{"tool_name": "Bash", "tool_input": {"command": "grep pattern file.txt"}}' | uv run examples/simple_command_validator.py

Running Tests

Unit Tests

Test the core saga framework components:

uv run pytest tests/test_claude_saga.py -v

E2E Tests

Test complete example hook behavior:

uv run pytest tests/test_e2e_simple_command_validator.py -v

All Tests

Run the complete test suite:

uv run pytest tests/ -v

Building

uv build

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. I'd like to hear what common effects can be added to the core lib. e.g.

  • http_request_effect
  • mcp_request_effect

Future work must incorporate

  • parsing & validation for each hook's unique input/output behaviors, fields etc...
  • retry-able effects
  • cancel-able effects
  • parallel effects (e.g. All), see hypothetical async effects like mcp_request etc...
  • concurrent effects

Metadata

Release files for claude-saga 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for claude-saga 0.1.2
File Size Uploaded
claude_saga-0.1.2.tar.gz 11.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for claude-saga 0.1.2
File Interpreter ABI Platform
claude_saga-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 18.8 kB

Release files / claude_saga-0.1.2.tar.gz

Download URL claude_saga-0.1.2.tar.gz
Size 11.1 kB
Tags Source
SHA-256 checksum
How to use checksums
f011da85bc329969032853304d9a013685854bac3ffdf3209d17c1bcae404431
BLAKE2b-256 checksum
How to use checksums
8b8f33283e2b8cdc2a6ee5dd5ba15487d23a97b6c4a354b2113309cdc8a4148f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2025.

Transparency log

Release files / claude_saga-0.1.2-py3-none-any.whl

Download URL claude_saga-0.1.2-py3-none-any.whl
Size 7.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b2d8ecc0920c03dc9bad23d06295d9f46f8a26b7c42b2a3a6c46f817a8dae88e
BLAKE2b-256 checksum
How to use checksums
125c6f87726b415f46d8e413a4105c09c858f5d96108dbdf823c47181abfe114
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2025.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page