driangle-agentrunner 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. driangle_agentrunner-0.0.1/.gitignore +15 -0
  2. driangle_agentrunner-0.0.1/PKG-INFO +219 -0
  3. driangle_agentrunner-0.0.1/README.md +201 -0
  4. driangle_agentrunner-0.0.1/channel/pyproject.toml +24 -0
  5. driangle_agentrunner-0.0.1/channel/src/agentrunner_channel/__init__.py +41 -0
  6. driangle_agentrunner-0.0.1/channel/src/agentrunner_channel/py.typed +0 -0
  7. driangle_agentrunner-0.0.1/channel/tests/test_binary.py +19 -0
  8. driangle_agentrunner-0.0.1/pyproject.toml +43 -0
  9. driangle_agentrunner-0.0.1/src/agentrunner/__init__.py +35 -0
  10. driangle_agentrunner-0.0.1/src/agentrunner/claudecode/__init__.py +11 -0
  11. driangle_agentrunner-0.0.1/src/agentrunner/claudecode/args.py +51 -0
  12. driangle_agentrunner-0.0.1/src/agentrunner/claudecode/mapping.py +59 -0
  13. driangle_agentrunner-0.0.1/src/agentrunner/claudecode/options.py +33 -0
  14. driangle_agentrunner-0.0.1/src/agentrunner/claudecode/parser.py +152 -0
  15. driangle_agentrunner-0.0.1/src/agentrunner/claudecode/process.py +68 -0
  16. driangle_agentrunner-0.0.1/src/agentrunner/claudecode/runner.py +270 -0
  17. driangle_agentrunner-0.0.1/src/agentrunner/claudecode/types.py +131 -0
  18. driangle_agentrunner-0.0.1/src/agentrunner/claudecode/version.py +47 -0
  19. driangle_agentrunner-0.0.1/src/agentrunner/errors.py +36 -0
  20. driangle_agentrunner-0.0.1/src/agentrunner/ollama/__init__.py +14 -0
  21. driangle_agentrunner-0.0.1/src/agentrunner/ollama/accessors.py +22 -0
  22. driangle_agentrunner-0.0.1/src/agentrunner/ollama/options.py +41 -0
  23. driangle_agentrunner-0.0.1/src/agentrunner/ollama/runner.py +327 -0
  24. driangle_agentrunner-0.0.1/src/agentrunner/ollama/types.py +87 -0
  25. driangle_agentrunner-0.0.1/src/agentrunner/types.py +188 -0
  26. driangle_agentrunner-0.0.1/tests/__init__.py +0 -0
  27. driangle_agentrunner-0.0.1/tests/claudecode/__init__.py +0 -0
  28. driangle_agentrunner-0.0.1/tests/claudecode/test_args.py +105 -0
  29. driangle_agentrunner-0.0.1/tests/claudecode/test_mapping.py +32 -0
  30. driangle_agentrunner-0.0.1/tests/claudecode/test_parser.py +199 -0
  31. driangle_agentrunner-0.0.1/tests/claudecode/test_runner.py +657 -0
  32. driangle_agentrunner-0.0.1/tests/claudecode/test_version.py +25 -0
  33. driangle_agentrunner-0.0.1/tests/ollama/__init__.py +0 -0
  34. driangle_agentrunner-0.0.1/tests/ollama/test_runner.py +342 -0
@@ -0,0 +1,15 @@
1
+ settings.local.json
2
+ node_modules
3
+ dist
4
+ docs/.vitepress/dist
5
+ docs/.vitepress/cache
6
+ __pycache__/
7
+ *.pyc
8
+
9
+ # Compiled Go binaries
10
+ examples/go/claudecode/claudecode
11
+ examples/go/ollama/ollama
12
+
13
+ # Platform binaries (placed by CI before publishing)
14
+ npm/*/bin/
15
+ python/channel/src/agentrunner_channel/bin/
@@ -0,0 +1,219 @@
1
+ Metadata-Version: 2.4
2
+ Name: driangle-agentrunner
3
+ Version: 0.0.1
4
+ Summary: Python library for programmatically invoking AI coding agents
5
+ Project-URL: Homepage, https://github.com/driangle/agentrunner
6
+ Project-URL: Repository, https://github.com/driangle/agentrunner
7
+ Project-URL: Issues, https://github.com/driangle/agentrunner/issues
8
+ Author: driangle
9
+ License-Expression: MIT
10
+ Requires-Python: >=3.11
11
+ Provides-Extra: channel
12
+ Requires-Dist: agentrunner-channel>=0.0.1; extra == 'channel'
13
+ Provides-Extra: dev
14
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
15
+ Requires-Dist: pytest>=8.0; extra == 'dev'
16
+ Requires-Dist: ruff>=0.8; extra == 'dev'
17
+ Description-Content-Type: text/markdown
18
+
19
+ # agentrunner (Python)
20
+
21
+ Python library for programmatically invoking AI coding agents. Part of the [agentrunner](../) monorepo.
22
+
23
+ ## Supported CLIs
24
+
25
+ | Runner | CLI Version | Status |
26
+ |-------------|-------------|--------|
27
+ | Claude Code | >= 1.0.12 | ✅ |
28
+
29
+ ## Requirements
30
+
31
+ - Python >= 3.11
32
+ - Claude Code CLI >= 1.0.12
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install agentrunner
38
+ ```
39
+
40
+ ## Quick Start
41
+
42
+ ```python
43
+ import asyncio
44
+ from agentrunner.claudecode import create_claude_runner, ClaudeRunOptions
45
+
46
+ runner = create_claude_runner()
47
+
48
+ async def main():
49
+ # Simple run
50
+ result = await runner.run("What files are in this directory?", ClaudeRunOptions(
51
+ working_dir="/path/to/project",
52
+ skip_permissions=True,
53
+ ))
54
+ print(result.text)
55
+
56
+ # Streaming
57
+ stream = await runner.run_stream("Explain this codebase")
58
+ async for message in stream:
59
+ print(message.type, message.raw)
60
+
61
+ asyncio.run(main())
62
+ ```
63
+
64
+ ## API
65
+
66
+ ### `create_claude_runner(config?)`
67
+
68
+ Creates a runner for the Claude Code CLI.
69
+
70
+ **Config options (`ClaudeRunnerConfig`):**
71
+
72
+ | Field | Type | Default | Description |
73
+ |----------|------------|------------|--------------------------------------|
74
+ | `binary` | `str` | `"claude"` | CLI binary name or path |
75
+ | `spawn` | `SpawnFn` | — | Custom spawn function (for testing) |
76
+ | `logger` | `Logger` | — | Logger for debug output (opt-in) |
77
+
78
+ ### `runner.run(prompt, options?)`
79
+
80
+ Execute a prompt and return the final `Result`.
81
+
82
+ ### `runner.run_stream(prompt, options?)`
83
+
84
+ Execute a prompt and stream messages as they arrive. Returns `AsyncIterable[Message]`.
85
+
86
+ ### `runner.start(prompt, options?)`
87
+
88
+ Launch an agent process and return a `Session` for full lifecycle control.
89
+
90
+ ### Run Options
91
+
92
+ Common options (`RunOptions`):
93
+
94
+ | Field | Type | Description |
95
+ |-----------------------|------------------|--------------------------------------|
96
+ | `model` | `str` | Model name or alias |
97
+ | `system_prompt` | `str` | System prompt override |
98
+ | `append_system_prompt`| `str` | Appended to default system prompt |
99
+ | `working_dir` | `str` | Working directory for subprocess |
100
+ | `env` | `dict[str, str]` | Additional environment variables |
101
+ | `max_turns` | `int` | Maximum agentic turns |
102
+ | `timeout` | `float` | Timeout in milliseconds |
103
+ | `skip_permissions` | `bool` | Skip permission prompts |
104
+
105
+ Claude-specific options (`ClaudeRunOptions` extends `RunOptions`):
106
+
107
+ | Field | Type | Description |
108
+ |--------------------------|-------------|------------------------------------|
109
+ | `allowed_tools` | `list[str]` | Tools the agent may use |
110
+ | `disallowed_tools` | `list[str]` | Tools the agent may not use |
111
+ | `mcp_config` | `str` | Path to MCP server config |
112
+ | `json_schema` | `str` | JSON Schema for structured output |
113
+ | `max_budget_usd` | `float` | Cost limit in USD |
114
+ | `resume` | `str` | Session ID to resume |
115
+ | `continue_session` | `bool` | Continue most recent session |
116
+ | `session_id` | `str` | Specific session ID |
117
+ | `include_partial_messages`| `bool` | Stream partial/incremental messages|
118
+ | `on_message` | `callable` | Callback for each streamed message |
119
+
120
+ ### Result
121
+
122
+ | Field | Type | Description |
123
+ |--------------|---------|---------------------------------|
124
+ | `text` | `str` | Final response text |
125
+ | `is_error` | `bool` | Whether the run ended in error |
126
+ | `exit_code` | `int` | Process exit code |
127
+ | `usage` | `Usage` | Token counts |
128
+ | `cost_usd` | `float` | Estimated cost in USD |
129
+ | `duration_ms`| `float` | Wall-clock duration in ms |
130
+ | `session_id` | `str` | Session ID for resumption |
131
+
132
+ ### Session
133
+
134
+ | Attribute | Type | Description |
135
+ |------------|--------------------------|---------------------------------------|
136
+ | `messages` | `AsyncIterable[Message]` | Iterate messages as they arrive |
137
+ | `result` | `Future[Result]` | Resolves when the agent finishes |
138
+ | `abort()` | — | Terminate the agent process |
139
+ | `send()` | — | Reserved (raises `RuntimeError`) |
140
+
141
+ ### Error Classes
142
+
143
+ All errors extend `RunnerError`:
144
+
145
+ - `NotFoundError` — CLI binary not found
146
+ - `TimeoutError` — execution timed out
147
+ - `NonZeroExitError` — CLI exited with non-zero code (has `.exit_code`)
148
+ - `ParseError` — failed to parse CLI output
149
+ - `CancelledError` — execution cancelled
150
+ - `NoResultError` — stream ended without a result message
151
+
152
+ ```python
153
+ from agentrunner import TimeoutError
154
+
155
+ try:
156
+ await runner.run("complex task", ClaudeRunOptions(timeout=30_000))
157
+ except TimeoutError:
158
+ print("Timed out!")
159
+ ```
160
+
161
+ ## Usage Examples
162
+
163
+ ### Session Resume
164
+
165
+ ```python
166
+ # First run — capture the session ID.
167
+ result = await runner.run("Set up the project structure")
168
+ session_id = result.session_id
169
+
170
+ # Resume the same session later.
171
+ result = await runner.run("Now add tests", ClaudeRunOptions(resume=session_id))
172
+ ```
173
+
174
+ ### Session Object
175
+
176
+ ```python
177
+ session = runner.start("Explain this code", ClaudeRunOptions(max_turns=1, timeout=30_000))
178
+
179
+ async for msg in session.messages:
180
+ print(f"[{msg.type}] {msg.raw[:80]}")
181
+
182
+ result = await session.result
183
+ print(f"Response: {result.text}")
184
+ ```
185
+
186
+ ### Streaming with Partial Messages
187
+
188
+ ```python
189
+ from agentrunner.claudecode import parse
190
+
191
+ stream = await runner.run_stream("List fun facts", ClaudeRunOptions(
192
+ include_partial_messages=True,
193
+ ))
194
+ async for msg in stream:
195
+ if msg.type == "assistant":
196
+ parsed = parse(msg.raw)
197
+ if parsed.type == "stream_event":
198
+ import json
199
+ raw = json.loads(msg.raw)
200
+ delta = raw.get("event", {}).get("delta", {})
201
+ if delta.get("type") == "text_delta":
202
+ print(delta["text"], end="", flush=True)
203
+ ```
204
+
205
+ ## Development
206
+
207
+ ```bash
208
+ cd python
209
+ pip install -e ".[dev]" # install with dev dependencies
210
+ ruff check src/ tests/ # lint
211
+ python -m pytest # run tests
212
+ ```
213
+
214
+ Or from the repo root:
215
+
216
+ ```bash
217
+ make check-python # build + lint + test
218
+ make check # all libraries
219
+ ```
@@ -0,0 +1,201 @@
1
+ # agentrunner (Python)
2
+
3
+ Python library for programmatically invoking AI coding agents. Part of the [agentrunner](../) monorepo.
4
+
5
+ ## Supported CLIs
6
+
7
+ | Runner | CLI Version | Status |
8
+ |-------------|-------------|--------|
9
+ | Claude Code | >= 1.0.12 | ✅ |
10
+
11
+ ## Requirements
12
+
13
+ - Python >= 3.11
14
+ - Claude Code CLI >= 1.0.12
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ pip install agentrunner
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```python
25
+ import asyncio
26
+ from agentrunner.claudecode import create_claude_runner, ClaudeRunOptions
27
+
28
+ runner = create_claude_runner()
29
+
30
+ async def main():
31
+ # Simple run
32
+ result = await runner.run("What files are in this directory?", ClaudeRunOptions(
33
+ working_dir="/path/to/project",
34
+ skip_permissions=True,
35
+ ))
36
+ print(result.text)
37
+
38
+ # Streaming
39
+ stream = await runner.run_stream("Explain this codebase")
40
+ async for message in stream:
41
+ print(message.type, message.raw)
42
+
43
+ asyncio.run(main())
44
+ ```
45
+
46
+ ## API
47
+
48
+ ### `create_claude_runner(config?)`
49
+
50
+ Creates a runner for the Claude Code CLI.
51
+
52
+ **Config options (`ClaudeRunnerConfig`):**
53
+
54
+ | Field | Type | Default | Description |
55
+ |----------|------------|------------|--------------------------------------|
56
+ | `binary` | `str` | `"claude"` | CLI binary name or path |
57
+ | `spawn` | `SpawnFn` | — | Custom spawn function (for testing) |
58
+ | `logger` | `Logger` | — | Logger for debug output (opt-in) |
59
+
60
+ ### `runner.run(prompt, options?)`
61
+
62
+ Execute a prompt and return the final `Result`.
63
+
64
+ ### `runner.run_stream(prompt, options?)`
65
+
66
+ Execute a prompt and stream messages as they arrive. Returns `AsyncIterable[Message]`.
67
+
68
+ ### `runner.start(prompt, options?)`
69
+
70
+ Launch an agent process and return a `Session` for full lifecycle control.
71
+
72
+ ### Run Options
73
+
74
+ Common options (`RunOptions`):
75
+
76
+ | Field | Type | Description |
77
+ |-----------------------|------------------|--------------------------------------|
78
+ | `model` | `str` | Model name or alias |
79
+ | `system_prompt` | `str` | System prompt override |
80
+ | `append_system_prompt`| `str` | Appended to default system prompt |
81
+ | `working_dir` | `str` | Working directory for subprocess |
82
+ | `env` | `dict[str, str]` | Additional environment variables |
83
+ | `max_turns` | `int` | Maximum agentic turns |
84
+ | `timeout` | `float` | Timeout in milliseconds |
85
+ | `skip_permissions` | `bool` | Skip permission prompts |
86
+
87
+ Claude-specific options (`ClaudeRunOptions` extends `RunOptions`):
88
+
89
+ | Field | Type | Description |
90
+ |--------------------------|-------------|------------------------------------|
91
+ | `allowed_tools` | `list[str]` | Tools the agent may use |
92
+ | `disallowed_tools` | `list[str]` | Tools the agent may not use |
93
+ | `mcp_config` | `str` | Path to MCP server config |
94
+ | `json_schema` | `str` | JSON Schema for structured output |
95
+ | `max_budget_usd` | `float` | Cost limit in USD |
96
+ | `resume` | `str` | Session ID to resume |
97
+ | `continue_session` | `bool` | Continue most recent session |
98
+ | `session_id` | `str` | Specific session ID |
99
+ | `include_partial_messages`| `bool` | Stream partial/incremental messages|
100
+ | `on_message` | `callable` | Callback for each streamed message |
101
+
102
+ ### Result
103
+
104
+ | Field | Type | Description |
105
+ |--------------|---------|---------------------------------|
106
+ | `text` | `str` | Final response text |
107
+ | `is_error` | `bool` | Whether the run ended in error |
108
+ | `exit_code` | `int` | Process exit code |
109
+ | `usage` | `Usage` | Token counts |
110
+ | `cost_usd` | `float` | Estimated cost in USD |
111
+ | `duration_ms`| `float` | Wall-clock duration in ms |
112
+ | `session_id` | `str` | Session ID for resumption |
113
+
114
+ ### Session
115
+
116
+ | Attribute | Type | Description |
117
+ |------------|--------------------------|---------------------------------------|
118
+ | `messages` | `AsyncIterable[Message]` | Iterate messages as they arrive |
119
+ | `result` | `Future[Result]` | Resolves when the agent finishes |
120
+ | `abort()` | — | Terminate the agent process |
121
+ | `send()` | — | Reserved (raises `RuntimeError`) |
122
+
123
+ ### Error Classes
124
+
125
+ All errors extend `RunnerError`:
126
+
127
+ - `NotFoundError` — CLI binary not found
128
+ - `TimeoutError` — execution timed out
129
+ - `NonZeroExitError` — CLI exited with non-zero code (has `.exit_code`)
130
+ - `ParseError` — failed to parse CLI output
131
+ - `CancelledError` — execution cancelled
132
+ - `NoResultError` — stream ended without a result message
133
+
134
+ ```python
135
+ from agentrunner import TimeoutError
136
+
137
+ try:
138
+ await runner.run("complex task", ClaudeRunOptions(timeout=30_000))
139
+ except TimeoutError:
140
+ print("Timed out!")
141
+ ```
142
+
143
+ ## Usage Examples
144
+
145
+ ### Session Resume
146
+
147
+ ```python
148
+ # First run — capture the session ID.
149
+ result = await runner.run("Set up the project structure")
150
+ session_id = result.session_id
151
+
152
+ # Resume the same session later.
153
+ result = await runner.run("Now add tests", ClaudeRunOptions(resume=session_id))
154
+ ```
155
+
156
+ ### Session Object
157
+
158
+ ```python
159
+ session = runner.start("Explain this code", ClaudeRunOptions(max_turns=1, timeout=30_000))
160
+
161
+ async for msg in session.messages:
162
+ print(f"[{msg.type}] {msg.raw[:80]}")
163
+
164
+ result = await session.result
165
+ print(f"Response: {result.text}")
166
+ ```
167
+
168
+ ### Streaming with Partial Messages
169
+
170
+ ```python
171
+ from agentrunner.claudecode import parse
172
+
173
+ stream = await runner.run_stream("List fun facts", ClaudeRunOptions(
174
+ include_partial_messages=True,
175
+ ))
176
+ async for msg in stream:
177
+ if msg.type == "assistant":
178
+ parsed = parse(msg.raw)
179
+ if parsed.type == "stream_event":
180
+ import json
181
+ raw = json.loads(msg.raw)
182
+ delta = raw.get("event", {}).get("delta", {})
183
+ if delta.get("type") == "text_delta":
184
+ print(delta["text"], end="", flush=True)
185
+ ```
186
+
187
+ ## Development
188
+
189
+ ```bash
190
+ cd python
191
+ pip install -e ".[dev]" # install with dev dependencies
192
+ ruff check src/ tests/ # lint
193
+ python -m pytest # run tests
194
+ ```
195
+
196
+ Or from the repo root:
197
+
198
+ ```bash
199
+ make check-python # build + lint + test
200
+ make check # all libraries
201
+ ```
@@ -0,0 +1,24 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "agentrunner-channel"
7
+ version = "0.0.1"
8
+ description = "agentrunner-channel binary distribution"
9
+ requires-python = ">=3.11"
10
+ license = "MIT"
11
+
12
+ [project.optional-dependencies]
13
+ dev = [
14
+ "pytest>=8.0",
15
+ ]
16
+
17
+ [tool.hatch.build.targets.wheel]
18
+ packages = ["src/agentrunner_channel"]
19
+
20
+ [tool.hatch.build]
21
+ artifacts = ["src/agentrunner_channel/bin/*"]
22
+
23
+ [tool.pytest.ini_options]
24
+ testpaths = ["tests"]
@@ -0,0 +1,41 @@
1
+ """Resolve the agentrunner-channel binary path."""
2
+
3
+ import os
4
+ import platform
5
+ import shutil
6
+ from pathlib import Path
7
+
8
+
9
+ def binary_path() -> str:
10
+ """Return the path to the agentrunner-channel binary.
11
+
12
+ Resolution order:
13
+ 1. AGENTRUNNER_CHANNEL_BIN environment variable
14
+ 2. Bundled binary in this package's bin/ directory
15
+ 3. agentrunner-channel on $PATH
16
+
17
+ Raises:
18
+ FileNotFoundError: if the binary cannot be found.
19
+ """
20
+ env_path = os.environ.get("AGENTRUNNER_CHANNEL_BIN")
21
+ if env_path:
22
+ return env_path
23
+
24
+ name = (
25
+ "agentrunner-channel.exe"
26
+ if platform.system() == "Windows"
27
+ else "agentrunner-channel"
28
+ )
29
+ bundled = Path(__file__).parent / "bin" / name
30
+ if bundled.exists():
31
+ return str(bundled)
32
+
33
+ found = shutil.which("agentrunner-channel")
34
+ if found:
35
+ return found
36
+
37
+ raise FileNotFoundError(
38
+ "agentrunner-channel binary not found. "
39
+ "Install the agentrunner-channel package for your platform, "
40
+ "add it to $PATH, or set AGENTRUNNER_CHANNEL_BIN."
41
+ )
@@ -0,0 +1,19 @@
1
+ """Tests for channel binary resolution."""
2
+
3
+ import os
4
+
5
+ import pytest
6
+
7
+ from agentrunner_channel import binary_path
8
+
9
+
10
+ class TestBinaryPath:
11
+ def test_env_override(self, monkeypatch):
12
+ monkeypatch.setenv("AGENTRUNNER_CHANNEL_BIN", "/custom/agentrunner-channel")
13
+ assert binary_path() == "/custom/agentrunner-channel"
14
+
15
+ def test_not_found_raises(self, monkeypatch):
16
+ monkeypatch.delenv("AGENTRUNNER_CHANNEL_BIN", raising=False)
17
+ monkeypatch.setattr("shutil.which", lambda _: None)
18
+ with pytest.raises(FileNotFoundError, match="agentrunner-channel binary not found"):
19
+ binary_path()
@@ -0,0 +1,43 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "driangle-agentrunner"
7
+ version = "0.0.1"
8
+ description = "Python library for programmatically invoking AI coding agents"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ authors = [
13
+ { name = "driangle" },
14
+ ]
15
+
16
+ [project.urls]
17
+ Homepage = "https://github.com/driangle/agentrunner"
18
+ Repository = "https://github.com/driangle/agentrunner"
19
+ Issues = "https://github.com/driangle/agentrunner/issues"
20
+
21
+ [project.optional-dependencies]
22
+ channel = [
23
+ "agentrunner-channel>=0.0.1",
24
+ ]
25
+ dev = [
26
+ "pytest>=8.0",
27
+ "pytest-asyncio>=0.24",
28
+ "ruff>=0.8",
29
+ ]
30
+
31
+ [tool.hatch.build.targets.wheel]
32
+ packages = ["src/agentrunner"]
33
+
34
+ [tool.pytest.ini_options]
35
+ asyncio_mode = "auto"
36
+ testpaths = ["tests"]
37
+
38
+ [tool.ruff]
39
+ target-version = "py311"
40
+ line-length = 100
41
+
42
+ [tool.ruff.lint]
43
+ select = ["E", "F", "I", "W"]
@@ -0,0 +1,35 @@
1
+ """agentrunner — Python library for programmatically invoking AI coding agents."""
2
+
3
+ from .claudecode import ClaudeRunner, ClaudeRunOptions
4
+ from .errors import (
5
+ CancelledError,
6
+ NonZeroExitError,
7
+ NoResultError,
8
+ NotFoundError,
9
+ ParseError,
10
+ RunnerError,
11
+ TimeoutError,
12
+ )
13
+ from .ollama import OllamaRunner, OllamaRunnerConfig, OllamaRunOptions
14
+ from .types import Message, Result, Runner, RunOptions, Session, Usage
15
+
16
+ __all__ = [
17
+ "CancelledError",
18
+ "ClaudeRunner",
19
+ "ClaudeRunOptions",
20
+ "Message",
21
+ "NoResultError",
22
+ "NonZeroExitError",
23
+ "NotFoundError",
24
+ "OllamaRunner",
25
+ "OllamaRunnerConfig",
26
+ "OllamaRunOptions",
27
+ "ParseError",
28
+ "Result",
29
+ "RunOptions",
30
+ "Runner",
31
+ "RunnerError",
32
+ "Session",
33
+ "TimeoutError",
34
+ "Usage",
35
+ ]
@@ -0,0 +1,11 @@
1
+ """Claude Code runner for agentrunner."""
2
+
3
+ from .options import ClaudeRunOptions
4
+ from .runner import ClaudeRunner
5
+ from .version import MIN_VERSION
6
+
7
+ __all__ = [
8
+ "ClaudeRunner",
9
+ "ClaudeRunOptions",
10
+ "MIN_VERSION",
11
+ ]
@@ -0,0 +1,51 @@
1
+ """Build CLI arguments from prompt and options."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .options import ClaudeRunOptions
6
+
7
+
8
+ def build_args(prompt: str, options: ClaudeRunOptions | None = None) -> list[str]:
9
+ """Build CLI arguments from prompt and options."""
10
+ args = ["--print", "--output-format", "stream-json", "--verbose"]
11
+
12
+ if options is None:
13
+ args.extend(["--", prompt])
14
+ return args
15
+
16
+ # Common options.
17
+ if options.model:
18
+ args.extend(["--model", options.model])
19
+ if options.system_prompt:
20
+ args.extend(["--system-prompt", options.system_prompt])
21
+ if options.append_system_prompt:
22
+ args.extend(["--append-system-prompt", options.append_system_prompt])
23
+ if options.max_turns is not None and options.max_turns > 0:
24
+ args.extend(["--max-turns", str(options.max_turns)])
25
+ if options.skip_permissions:
26
+ args.append("--dangerously-skip-permissions")
27
+
28
+ # Claude-specific options.
29
+ if options.allowed_tools:
30
+ for tool in options.allowed_tools:
31
+ args.extend(["--allowedTools", tool])
32
+ if options.disallowed_tools:
33
+ for tool in options.disallowed_tools:
34
+ args.extend(["--disallowedTools", tool])
35
+ if options.mcp_config:
36
+ args.extend(["--mcp-config", options.mcp_config])
37
+ if options.json_schema:
38
+ args.extend(["--json-schema", options.json_schema])
39
+ if options.max_budget_usd is not None and options.max_budget_usd > 0:
40
+ args.extend(["--max-budget-usd", str(options.max_budget_usd)])
41
+ if options.resume:
42
+ args.extend(["--resume", options.resume])
43
+ if options.continue_session:
44
+ args.append("--continue")
45
+ if options.session_id:
46
+ args.extend(["--session-id", options.session_id])
47
+ if options.include_partial_messages:
48
+ args.append("--include-partial-messages")
49
+
50
+ args.extend(["--", prompt])
51
+ return args