pyclaudecli 1.0.0__tar.gz → 1.0.2__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.
@@ -0,0 +1,424 @@
1
+ Metadata-Version: 2.5
2
+ Name: pyclaudecli
3
+ Version: 1.0.2
4
+ Summary: Python library wrapping the Claude Code CLI: prompts, background agents, auth, MCP, plugins, and more
5
+ Author-email: Suriya Ravichandran <suriyaravichandran@itrendsolution.com>
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Keywords: anthropic,automation,claude,claude-code,cli,sdk,wrapper
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.8
22
+ Description-Content-Type: text/markdown
23
+
24
+ # pyclaudecli
25
+
26
+ [![PyPI](https://img.shields.io/pypi/v/pyclaudecli.svg?color=blue)](https://pypi.org/project/pyclaudecli/)
27
+ [![Python versions](https://img.shields.io/pypi/pyversions/pyclaudecli.svg)](https://pypi.org/project/pyclaudecli/)
28
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
29
+ [![Downloads](https://img.shields.io/pypi/dm/pyclaudecli.svg)](https://pypi.org/project/pyclaudecli/)
30
+
31
+ A Python library that wraps the [Claude Code](https://claude.com/claude-code) CLI (`claude`) so you can drive it from Python instead of shelling out by hand: one-shot prompts, JSON/streaming output, background agents, authentication, MCP servers, plugins, and the rest of the CLI's surface.
32
+
33
+ It's a thin wrapper, not a reimplementation — every call runs the real `claude` binary, so it always reflects whatever version, auth, and config you have installed locally.
34
+
35
+ ## Contents
36
+
37
+ - [Install](#install)
38
+ - [Quickstart](#quickstart)
39
+ - [Setup](#setup)
40
+ - [Authentication](#authentication)
41
+ - [Command-line usage](#command-line-usage)
42
+ - [Errors](#errors)
43
+ - [`build_flags`](#build_flags)
44
+ - [Every method, with an example](#every-method-with-an-example)
45
+ - [Development](#development)
46
+ - [License](#license)
47
+
48
+ ## Install
49
+
50
+ ```bash
51
+ pip install pyclaudecli
52
+ ```
53
+
54
+ Requires the `claude` CLI itself to be installed and on `PATH` (see the [Claude Code docs](https://claude.com/claude-code)).
55
+
56
+ ## Quickstart
57
+
58
+ ```python
59
+ from pyclaudecli import ClaudeCLI
60
+
61
+ claude = ClaudeCLI()
62
+
63
+ # One-shot prompt
64
+ print(claude.prompt("Summarize this repo's README.", model="haiku"))
65
+ ```
66
+
67
+ ## Setup
68
+
69
+ ```python
70
+ from pyclaudecli import ClaudeCLI
71
+
72
+ # Defaults: binary="claude" on PATH, inherited cwd/env, no timeout
73
+ claude = ClaudeCLI()
74
+
75
+ # Pointing at a specific binary/project, with a default timeout for every call
76
+ claude = ClaudeCLI(
77
+ "claude",
78
+ cwd="/path/to/project",
79
+ env={"ANTHROPIC_API_KEY": "sk-ant-..."},
80
+ timeout=120,
81
+ )
82
+ ```
83
+
84
+ ## Authentication
85
+
86
+ `claude` handles auth itself, so `pyclaudecli` just drives it. You have two options.
87
+
88
+ ### API key
89
+
90
+ Pass the key through the environment — either inherited from your shell or set per client:
91
+
92
+ ```python
93
+ from pyclaudecli import ClaudeCLI
94
+
95
+ claude = ClaudeCLI(env={"ANTHROPIC_API_KEY": "sk-ant-..."})
96
+ ```
97
+
98
+ ### OAuth login (paste the code)
99
+
100
+ `auth_login()` runs `claude auth login`, prints the sign-in URL, waits for the CLI's
101
+ "Paste code here" prompt, and writes your code back to it. With no arguments it reads the
102
+ code from stdin, so this is the whole interactive flow:
103
+
104
+ ```python
105
+ from pyclaudecli import ClaudeCLI
106
+
107
+ claude = ClaudeCLI()
108
+
109
+ if not claude.auth_status().get("loggedIn"):
110
+ # Prints the sign-in URL, then asks: "Paste the code from the browser here:"
111
+ exit_code = claude.auth_login()
112
+ print("login exit code:", exit_code)
113
+
114
+ print(claude.auth_status()) # {"loggedIn": True, "email": "you@example.com", ...}
115
+ ```
116
+
117
+ To capture the URL yourself (open it in a browser, send it to a chat, log it) and paste the
118
+ code back without stdin, use `on_output` plus `code_provider`:
119
+
120
+ ```python
121
+ import re
122
+
123
+ URL_RE = re.compile(r"https://\S+")
124
+ login_url = None
125
+
126
+ def capture(chunk: str) -> None:
127
+ global login_url
128
+ print(chunk, end="", flush=True) # or log.info(chunk)
129
+ if login_url is None:
130
+ match = URL_RE.search(chunk)
131
+ if match:
132
+ login_url = match.group(0)
133
+
134
+ def supply_code(url: str) -> str:
135
+ # `url` is the sign-in URL pyclaudecli found in the CLI's output.
136
+ # Open it however you like, then return the code the browser shows.
137
+ print(f"\nOpen this URL and approve the login:\n{url}\n")
138
+ return input("Paste code here: ").strip()
139
+
140
+ claude.auth_login(
141
+ on_output=capture,
142
+ code_provider=supply_code,
143
+ console=True, # --console: force the URL/console flow instead of opening a browser
144
+ timeout=300, # how long to wait for the "Paste code here" prompt
145
+ )
146
+ ```
147
+
148
+ Fully non-interactive — when the code already came from somewhere else (a queue, a
149
+ browser-automation step, an operator pasting it into your own UI):
150
+
151
+ ```python
152
+ claude.auth_login(code="123456")
153
+
154
+ # Or fetch it programmatically from the printed sign-in URL
155
+ claude.auth_login(code_provider=lambda url: fetch_code_from_my_browser(url))
156
+
157
+ # Enterprise SSO, or pre-filling the account
158
+ claude.auth_login(sso=True)
159
+ claude.auth_login(email="you@example.com")
160
+ ```
161
+
162
+ `auth_login()` returns the CLI's exit code (`0` on success) and raises `ClaudeTimeoutError`
163
+ if the login prompt never appears within `timeout` seconds.
164
+
165
+ Signing out, and long-lived tokens for CI:
166
+
167
+ ```python
168
+ claude.auth_logout()
169
+ claude.setup_token() # interactive; sets up a long-lived auth token
170
+ ```
171
+
172
+ ## Command-line usage
173
+
174
+ The package also installs a minimal `pyclaudecli` command (and `python -m pyclaudecli`) for quick one-off prompts — for anything beyond that, use `ClaudeCLI` directly.
175
+
176
+ ```bash
177
+ pyclaudecli "What's 2+2?" # defaults to model "haiku"
178
+ pyclaudecli --model sonnet "Explain this diff"
179
+ pyclaudecli -m sonnet "Explain this diff"
180
+
181
+ python -m pyclaudecli "Hello, Claude!"
182
+ ```
183
+
184
+ ## Errors
185
+
186
+ All CLI failures raise `ClaudeCLIError` (or `ClaudeNotFoundError` / `ClaudeTimeoutError`), carrying `returncode`, `stdout`, `stderr`, and `cmd`.
187
+
188
+ ```python
189
+ from pyclaudecli import ClaudeCLI, ClaudeCLIError, ClaudeNotFoundError, ClaudeTimeoutError
190
+
191
+ claude = ClaudeCLI()
192
+
193
+ try:
194
+ claude.prompt("Do something", model="haiku", timeout=30)
195
+ except ClaudeTimeoutError as exc:
196
+ print("timed out:", exc.cmd)
197
+ except ClaudeNotFoundError as exc:
198
+ print("claude binary not on PATH:", exc)
199
+ except ClaudeCLIError as exc:
200
+ print(exc.returncode, exc.stdout, exc.stderr)
201
+ ```
202
+
203
+ ## `build_flags`
204
+
205
+ The helper `ClaudeCLI` uses internally to turn a `{python_name: value}` dict into CLI flags — handy if you're composing your own `extra_flags` or calling `.run()` directly. `None`/`False` are omitted, `True` becomes a bare flag, lists/tuples repeat the flag followed by each value, and anything else becomes the flag plus `str(value)`.
206
+
207
+ ```python
208
+ from pyclaudecli import build_flags
209
+
210
+ build_flags({"model": "haiku", "verbose": True, "quiet": False, "add_dir": ["a", "b"]})
211
+ # ["--model", "haiku", "--verbose", "--add-dir", "a", "b"]
212
+ ```
213
+
214
+ ## Every method, with an example
215
+
216
+ All examples assume:
217
+
218
+ ```python
219
+ from pyclaudecli import ClaudeCLI
220
+
221
+ claude = ClaudeCLI()
222
+ ```
223
+
224
+ ### Raw / internal
225
+
226
+ ```python
227
+ # .run() — lowest-level call; returns a CommandResult(returncode, stdout, stderr)
228
+ result = claude.run(["--version"], check=False)
229
+ print(result.returncode, result.stdout)
230
+ ```
231
+
232
+ ### Version / health
233
+
234
+ ```python
235
+ claude.version() # "claude --version" -> "1.2.3"
236
+ claude.doctor() # "claude doctor" -> health-check report
237
+ claude.update() # "claude update" -> checks for and installs updates
238
+
239
+ claude.install() # "claude install" (latest default)
240
+ claude.install("stable") # "claude install stable"
241
+ claude.install("1.2.3", force=True) # "claude install 1.2.3 --force"
242
+ ```
243
+
244
+ ### Prompting
245
+
246
+ ```python
247
+ # One-shot prompt -> plain text
248
+ claude.prompt("Summarize this repo's README.", model="haiku")
249
+
250
+ # Structured result (cost, session id, etc.)
251
+ result = claude.prompt_json("What's 2+2?", model="haiku")
252
+ print(result["result"], result["total_cost_usd"])
253
+
254
+ # Live streaming events
255
+ for event in claude.prompt_stream("Write a haiku about tests.", model="haiku"):
256
+ print(event["type"])
257
+
258
+ # Full option surface
259
+ claude.prompt(
260
+ "Refactor this function for clarity.",
261
+ model="sonnet",
262
+ output_format="json",
263
+ system_prompt="You are a terse senior engineer.",
264
+ append_system_prompt="Always answer in bullet points.",
265
+ allowed_tools=["Read", "Edit"],
266
+ disallowed_tools=["Bash"],
267
+ add_dir=["../shared-lib"],
268
+ permission_mode="acceptEdits",
269
+ mcp_config=["./mcp.json"],
270
+ settings="./claude-settings.json",
271
+ resume="session-id-123",
272
+ fork_session=True,
273
+ effort="high",
274
+ fallback_model="haiku",
275
+ max_budget_usd=0.50,
276
+ json_schema='{"type": "object"}',
277
+ betas=["some-beta-flag"],
278
+ no_session_persistence=True,
279
+ dangerously_skip_permissions=False,
280
+ restricted=True,
281
+ input_text="piped stdin content",
282
+ timeout=60,
283
+ extra_flags={"worktree": True}, # anything without a named parameter
284
+ )
285
+
286
+ # continue_session=True appends --continue (keeps typing in the latest session)
287
+ claude.prompt("And now add tests for it.", continue_session=True)
288
+ ```
289
+
290
+ ### Background sessions ("agents")
291
+
292
+ ```python
293
+ session_id = claude.start_background("Refactor the auth module", model="sonnet")
294
+
295
+ claude.list_agents() # all sessions, as a list of dicts
296
+ claude.list_agents(all=True, cwd="/repo") # include finished ones, scoped to a project
297
+
298
+ claude.attach(session_id) # interactive; requires a real TTY
299
+ claude.logs(session_id) # raw terminal snapshot
300
+ claude.logs(session_id, strip_ansi=True) # best-effort plain text
301
+
302
+ claude.stop(session_id) # stop, keep state
303
+ claude.respawn(session_id) # restart one stopped session
304
+ claude.respawn(all=True) # restart every stopped session
305
+ claude.rm(session_id) # delete a stopped session
306
+ ```
307
+
308
+ ### Auth
309
+
310
+ ```python
311
+ claude.auth_status() # {"loggedIn": True, "email": "...", ...}
312
+ claude.auth_status(as_json=False) # human-readable text instead
313
+
314
+ # Interactive OAuth: prints the sign-in URL, then reads the pasted code from stdin
315
+ claude.auth_login()
316
+
317
+ # Non-interactive login: supply the code yourself
318
+ claude.auth_login(code="123456")
319
+
320
+ # Or fetch the code programmatically from the printed sign-in URL
321
+ claude.auth_login(code_provider=lambda url: fetch_code_from_my_browser(url))
322
+
323
+ # Route the printed sign-in URL/output somewhere other than stdout
324
+ claude.auth_login(on_output=lambda chunk: log.info(chunk), console=True, timeout=120)
325
+
326
+ claude.auth_login(sso=True) # enterprise SSO
327
+ claude.auth_login(email="you@example.com") # pre-fill the account
328
+
329
+ claude.auth_logout()
330
+ claude.setup_token() # interactive; sets up a long-lived auth token
331
+ ```
332
+
333
+ ### MCP servers
334
+
335
+ ```python
336
+ claude.mcp_list()
337
+ claude.mcp_get("sentry")
338
+
339
+ claude.mcp_add("sentry", "https://mcp.sentry.dev/mcp", transport="http")
340
+ claude.mcp_add(
341
+ "local-tool", "node", "server.js",
342
+ transport="stdio", env=["API_KEY=abc"], scope="project",
343
+ )
344
+
345
+ claude.mcp_add_json("sentry", {"type": "http", "url": "https://mcp.sentry.dev/mcp"})
346
+ claude.mcp_add_from_claude_desktop(scope="user")
347
+
348
+ claude.mcp_remove("sentry", scope="project")
349
+ claude.mcp_login("sentry") # interactive OAuth
350
+ claude.mcp_logout("sentry")
351
+ claude.mcp_reset_project_choices()
352
+ ```
353
+
354
+ ### Plugins
355
+
356
+ ```python
357
+ claude.plugin_list()
358
+ claude.plugin_list(as_json=True)
359
+
360
+ claude.plugin_install("some-plugin", yes=True)
361
+ claude.plugin_install("some-plugin@my-marketplace", scope="user", as_json=True)
362
+
363
+ claude.plugin_uninstall("some-plugin", yes=True, prune=True)
364
+ claude.plugin_enable("some-plugin")
365
+ claude.plugin_disable("some-plugin")
366
+ claude.plugin_disable(all=True) # disable every plugin
367
+
368
+ claude.plugin_update("some-plugin", yes=True)
369
+ claude.plugin_details("some-plugin")
370
+ claude.plugin_validate("./my-plugin", strict=True, as_json=True)
371
+ claude.plugin_prune(dry_run=True)
372
+
373
+ claude.plugin_marketplace_list()
374
+ claude.plugin_marketplace_add("https://github.com/org/marketplace-repo")
375
+ claude.plugin_marketplace_remove("marketplace-name")
376
+ claude.plugin_marketplace_update() # update all
377
+ claude.plugin_marketplace_update("marketplace-name")
378
+ ```
379
+
380
+ ### Project state
381
+
382
+ ```python
383
+ claude.project_purge() # purge state for the current project
384
+ claude.project_purge("/path/to/repo") # or for a specific path
385
+ ```
386
+
387
+ ### Auto mode
388
+
389
+ ```python
390
+ claude.auto_mode_config() # effective classifier config, as a dict
391
+ claude.auto_mode_defaults() # shipped default rules, as a dict
392
+ claude.auto_mode_reset() # remove custom rules from user settings
393
+ claude.auto_mode_critique() # AI feedback on your custom rules
394
+ ```
395
+
396
+ ### Misc
397
+
398
+ ```python
399
+ claude.import_config("cursor", dry_run=True, yes=True) # source: codex, gemini, or cursor
400
+
401
+ claude.ultrareview() # review the current branch
402
+ claude.ultrareview("main") # review against a base branch
403
+ claude.ultrareview(482, as_json=True, post=True) # review a PR, post results, get JSON
404
+
405
+ # Long-running server: returns a Popen handle, doesn't block
406
+ gateway = claude.start_gateway(config="./gateway.json")
407
+ ...
408
+ gateway.terminate()
409
+ ```
410
+
411
+ See `ClaudeCLI`'s docstrings for the exact CLI flags behind each method — it covers every top-level `claude` command (`auth`, `mcp`, `plugin`, `project`, `agents`/background sessions, `auto-mode`, `doctor`, `update`, `install`, `import`, `ultrareview`, `gateway`) plus the main prompt flags. Anything not exposed as a named parameter can still be passed through via each method's `extra_flags` dict.
412
+
413
+ ## Development
414
+
415
+ ```bash
416
+ git clone https://github.com/Suriya-Ravichandran/pyclaudecli.git
417
+ cd pyclaudecli
418
+ pip install -e .
419
+ pytest
420
+ ```
421
+
422
+ ## License
423
+
424
+ MIT
@@ -0,0 +1,401 @@
1
+ # pyclaudecli
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/pyclaudecli.svg?color=blue)](https://pypi.org/project/pyclaudecli/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/pyclaudecli.svg)](https://pypi.org/project/pyclaudecli/)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
6
+ [![Downloads](https://img.shields.io/pypi/dm/pyclaudecli.svg)](https://pypi.org/project/pyclaudecli/)
7
+
8
+ A Python library that wraps the [Claude Code](https://claude.com/claude-code) CLI (`claude`) so you can drive it from Python instead of shelling out by hand: one-shot prompts, JSON/streaming output, background agents, authentication, MCP servers, plugins, and the rest of the CLI's surface.
9
+
10
+ It's a thin wrapper, not a reimplementation — every call runs the real `claude` binary, so it always reflects whatever version, auth, and config you have installed locally.
11
+
12
+ ## Contents
13
+
14
+ - [Install](#install)
15
+ - [Quickstart](#quickstart)
16
+ - [Setup](#setup)
17
+ - [Authentication](#authentication)
18
+ - [Command-line usage](#command-line-usage)
19
+ - [Errors](#errors)
20
+ - [`build_flags`](#build_flags)
21
+ - [Every method, with an example](#every-method-with-an-example)
22
+ - [Development](#development)
23
+ - [License](#license)
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ pip install pyclaudecli
29
+ ```
30
+
31
+ Requires the `claude` CLI itself to be installed and on `PATH` (see the [Claude Code docs](https://claude.com/claude-code)).
32
+
33
+ ## Quickstart
34
+
35
+ ```python
36
+ from pyclaudecli import ClaudeCLI
37
+
38
+ claude = ClaudeCLI()
39
+
40
+ # One-shot prompt
41
+ print(claude.prompt("Summarize this repo's README.", model="haiku"))
42
+ ```
43
+
44
+ ## Setup
45
+
46
+ ```python
47
+ from pyclaudecli import ClaudeCLI
48
+
49
+ # Defaults: binary="claude" on PATH, inherited cwd/env, no timeout
50
+ claude = ClaudeCLI()
51
+
52
+ # Pointing at a specific binary/project, with a default timeout for every call
53
+ claude = ClaudeCLI(
54
+ "claude",
55
+ cwd="/path/to/project",
56
+ env={"ANTHROPIC_API_KEY": "sk-ant-..."},
57
+ timeout=120,
58
+ )
59
+ ```
60
+
61
+ ## Authentication
62
+
63
+ `claude` handles auth itself, so `pyclaudecli` just drives it. You have two options.
64
+
65
+ ### API key
66
+
67
+ Pass the key through the environment — either inherited from your shell or set per client:
68
+
69
+ ```python
70
+ from pyclaudecli import ClaudeCLI
71
+
72
+ claude = ClaudeCLI(env={"ANTHROPIC_API_KEY": "sk-ant-..."})
73
+ ```
74
+
75
+ ### OAuth login (paste the code)
76
+
77
+ `auth_login()` runs `claude auth login`, prints the sign-in URL, waits for the CLI's
78
+ "Paste code here" prompt, and writes your code back to it. With no arguments it reads the
79
+ code from stdin, so this is the whole interactive flow:
80
+
81
+ ```python
82
+ from pyclaudecli import ClaudeCLI
83
+
84
+ claude = ClaudeCLI()
85
+
86
+ if not claude.auth_status().get("loggedIn"):
87
+ # Prints the sign-in URL, then asks: "Paste the code from the browser here:"
88
+ exit_code = claude.auth_login()
89
+ print("login exit code:", exit_code)
90
+
91
+ print(claude.auth_status()) # {"loggedIn": True, "email": "you@example.com", ...}
92
+ ```
93
+
94
+ To capture the URL yourself (open it in a browser, send it to a chat, log it) and paste the
95
+ code back without stdin, use `on_output` plus `code_provider`:
96
+
97
+ ```python
98
+ import re
99
+
100
+ URL_RE = re.compile(r"https://\S+")
101
+ login_url = None
102
+
103
+ def capture(chunk: str) -> None:
104
+ global login_url
105
+ print(chunk, end="", flush=True) # or log.info(chunk)
106
+ if login_url is None:
107
+ match = URL_RE.search(chunk)
108
+ if match:
109
+ login_url = match.group(0)
110
+
111
+ def supply_code(url: str) -> str:
112
+ # `url` is the sign-in URL pyclaudecli found in the CLI's output.
113
+ # Open it however you like, then return the code the browser shows.
114
+ print(f"\nOpen this URL and approve the login:\n{url}\n")
115
+ return input("Paste code here: ").strip()
116
+
117
+ claude.auth_login(
118
+ on_output=capture,
119
+ code_provider=supply_code,
120
+ console=True, # --console: force the URL/console flow instead of opening a browser
121
+ timeout=300, # how long to wait for the "Paste code here" prompt
122
+ )
123
+ ```
124
+
125
+ Fully non-interactive — when the code already came from somewhere else (a queue, a
126
+ browser-automation step, an operator pasting it into your own UI):
127
+
128
+ ```python
129
+ claude.auth_login(code="123456")
130
+
131
+ # Or fetch it programmatically from the printed sign-in URL
132
+ claude.auth_login(code_provider=lambda url: fetch_code_from_my_browser(url))
133
+
134
+ # Enterprise SSO, or pre-filling the account
135
+ claude.auth_login(sso=True)
136
+ claude.auth_login(email="you@example.com")
137
+ ```
138
+
139
+ `auth_login()` returns the CLI's exit code (`0` on success) and raises `ClaudeTimeoutError`
140
+ if the login prompt never appears within `timeout` seconds.
141
+
142
+ Signing out, and long-lived tokens for CI:
143
+
144
+ ```python
145
+ claude.auth_logout()
146
+ claude.setup_token() # interactive; sets up a long-lived auth token
147
+ ```
148
+
149
+ ## Command-line usage
150
+
151
+ The package also installs a minimal `pyclaudecli` command (and `python -m pyclaudecli`) for quick one-off prompts — for anything beyond that, use `ClaudeCLI` directly.
152
+
153
+ ```bash
154
+ pyclaudecli "What's 2+2?" # defaults to model "haiku"
155
+ pyclaudecli --model sonnet "Explain this diff"
156
+ pyclaudecli -m sonnet "Explain this diff"
157
+
158
+ python -m pyclaudecli "Hello, Claude!"
159
+ ```
160
+
161
+ ## Errors
162
+
163
+ All CLI failures raise `ClaudeCLIError` (or `ClaudeNotFoundError` / `ClaudeTimeoutError`), carrying `returncode`, `stdout`, `stderr`, and `cmd`.
164
+
165
+ ```python
166
+ from pyclaudecli import ClaudeCLI, ClaudeCLIError, ClaudeNotFoundError, ClaudeTimeoutError
167
+
168
+ claude = ClaudeCLI()
169
+
170
+ try:
171
+ claude.prompt("Do something", model="haiku", timeout=30)
172
+ except ClaudeTimeoutError as exc:
173
+ print("timed out:", exc.cmd)
174
+ except ClaudeNotFoundError as exc:
175
+ print("claude binary not on PATH:", exc)
176
+ except ClaudeCLIError as exc:
177
+ print(exc.returncode, exc.stdout, exc.stderr)
178
+ ```
179
+
180
+ ## `build_flags`
181
+
182
+ The helper `ClaudeCLI` uses internally to turn a `{python_name: value}` dict into CLI flags — handy if you're composing your own `extra_flags` or calling `.run()` directly. `None`/`False` are omitted, `True` becomes a bare flag, lists/tuples repeat the flag followed by each value, and anything else becomes the flag plus `str(value)`.
183
+
184
+ ```python
185
+ from pyclaudecli import build_flags
186
+
187
+ build_flags({"model": "haiku", "verbose": True, "quiet": False, "add_dir": ["a", "b"]})
188
+ # ["--model", "haiku", "--verbose", "--add-dir", "a", "b"]
189
+ ```
190
+
191
+ ## Every method, with an example
192
+
193
+ All examples assume:
194
+
195
+ ```python
196
+ from pyclaudecli import ClaudeCLI
197
+
198
+ claude = ClaudeCLI()
199
+ ```
200
+
201
+ ### Raw / internal
202
+
203
+ ```python
204
+ # .run() — lowest-level call; returns a CommandResult(returncode, stdout, stderr)
205
+ result = claude.run(["--version"], check=False)
206
+ print(result.returncode, result.stdout)
207
+ ```
208
+
209
+ ### Version / health
210
+
211
+ ```python
212
+ claude.version() # "claude --version" -> "1.2.3"
213
+ claude.doctor() # "claude doctor" -> health-check report
214
+ claude.update() # "claude update" -> checks for and installs updates
215
+
216
+ claude.install() # "claude install" (latest default)
217
+ claude.install("stable") # "claude install stable"
218
+ claude.install("1.2.3", force=True) # "claude install 1.2.3 --force"
219
+ ```
220
+
221
+ ### Prompting
222
+
223
+ ```python
224
+ # One-shot prompt -> plain text
225
+ claude.prompt("Summarize this repo's README.", model="haiku")
226
+
227
+ # Structured result (cost, session id, etc.)
228
+ result = claude.prompt_json("What's 2+2?", model="haiku")
229
+ print(result["result"], result["total_cost_usd"])
230
+
231
+ # Live streaming events
232
+ for event in claude.prompt_stream("Write a haiku about tests.", model="haiku"):
233
+ print(event["type"])
234
+
235
+ # Full option surface
236
+ claude.prompt(
237
+ "Refactor this function for clarity.",
238
+ model="sonnet",
239
+ output_format="json",
240
+ system_prompt="You are a terse senior engineer.",
241
+ append_system_prompt="Always answer in bullet points.",
242
+ allowed_tools=["Read", "Edit"],
243
+ disallowed_tools=["Bash"],
244
+ add_dir=["../shared-lib"],
245
+ permission_mode="acceptEdits",
246
+ mcp_config=["./mcp.json"],
247
+ settings="./claude-settings.json",
248
+ resume="session-id-123",
249
+ fork_session=True,
250
+ effort="high",
251
+ fallback_model="haiku",
252
+ max_budget_usd=0.50,
253
+ json_schema='{"type": "object"}',
254
+ betas=["some-beta-flag"],
255
+ no_session_persistence=True,
256
+ dangerously_skip_permissions=False,
257
+ restricted=True,
258
+ input_text="piped stdin content",
259
+ timeout=60,
260
+ extra_flags={"worktree": True}, # anything without a named parameter
261
+ )
262
+
263
+ # continue_session=True appends --continue (keeps typing in the latest session)
264
+ claude.prompt("And now add tests for it.", continue_session=True)
265
+ ```
266
+
267
+ ### Background sessions ("agents")
268
+
269
+ ```python
270
+ session_id = claude.start_background("Refactor the auth module", model="sonnet")
271
+
272
+ claude.list_agents() # all sessions, as a list of dicts
273
+ claude.list_agents(all=True, cwd="/repo") # include finished ones, scoped to a project
274
+
275
+ claude.attach(session_id) # interactive; requires a real TTY
276
+ claude.logs(session_id) # raw terminal snapshot
277
+ claude.logs(session_id, strip_ansi=True) # best-effort plain text
278
+
279
+ claude.stop(session_id) # stop, keep state
280
+ claude.respawn(session_id) # restart one stopped session
281
+ claude.respawn(all=True) # restart every stopped session
282
+ claude.rm(session_id) # delete a stopped session
283
+ ```
284
+
285
+ ### Auth
286
+
287
+ ```python
288
+ claude.auth_status() # {"loggedIn": True, "email": "...", ...}
289
+ claude.auth_status(as_json=False) # human-readable text instead
290
+
291
+ # Interactive OAuth: prints the sign-in URL, then reads the pasted code from stdin
292
+ claude.auth_login()
293
+
294
+ # Non-interactive login: supply the code yourself
295
+ claude.auth_login(code="123456")
296
+
297
+ # Or fetch the code programmatically from the printed sign-in URL
298
+ claude.auth_login(code_provider=lambda url: fetch_code_from_my_browser(url))
299
+
300
+ # Route the printed sign-in URL/output somewhere other than stdout
301
+ claude.auth_login(on_output=lambda chunk: log.info(chunk), console=True, timeout=120)
302
+
303
+ claude.auth_login(sso=True) # enterprise SSO
304
+ claude.auth_login(email="you@example.com") # pre-fill the account
305
+
306
+ claude.auth_logout()
307
+ claude.setup_token() # interactive; sets up a long-lived auth token
308
+ ```
309
+
310
+ ### MCP servers
311
+
312
+ ```python
313
+ claude.mcp_list()
314
+ claude.mcp_get("sentry")
315
+
316
+ claude.mcp_add("sentry", "https://mcp.sentry.dev/mcp", transport="http")
317
+ claude.mcp_add(
318
+ "local-tool", "node", "server.js",
319
+ transport="stdio", env=["API_KEY=abc"], scope="project",
320
+ )
321
+
322
+ claude.mcp_add_json("sentry", {"type": "http", "url": "https://mcp.sentry.dev/mcp"})
323
+ claude.mcp_add_from_claude_desktop(scope="user")
324
+
325
+ claude.mcp_remove("sentry", scope="project")
326
+ claude.mcp_login("sentry") # interactive OAuth
327
+ claude.mcp_logout("sentry")
328
+ claude.mcp_reset_project_choices()
329
+ ```
330
+
331
+ ### Plugins
332
+
333
+ ```python
334
+ claude.plugin_list()
335
+ claude.plugin_list(as_json=True)
336
+
337
+ claude.plugin_install("some-plugin", yes=True)
338
+ claude.plugin_install("some-plugin@my-marketplace", scope="user", as_json=True)
339
+
340
+ claude.plugin_uninstall("some-plugin", yes=True, prune=True)
341
+ claude.plugin_enable("some-plugin")
342
+ claude.plugin_disable("some-plugin")
343
+ claude.plugin_disable(all=True) # disable every plugin
344
+
345
+ claude.plugin_update("some-plugin", yes=True)
346
+ claude.plugin_details("some-plugin")
347
+ claude.plugin_validate("./my-plugin", strict=True, as_json=True)
348
+ claude.plugin_prune(dry_run=True)
349
+
350
+ claude.plugin_marketplace_list()
351
+ claude.plugin_marketplace_add("https://github.com/org/marketplace-repo")
352
+ claude.plugin_marketplace_remove("marketplace-name")
353
+ claude.plugin_marketplace_update() # update all
354
+ claude.plugin_marketplace_update("marketplace-name")
355
+ ```
356
+
357
+ ### Project state
358
+
359
+ ```python
360
+ claude.project_purge() # purge state for the current project
361
+ claude.project_purge("/path/to/repo") # or for a specific path
362
+ ```
363
+
364
+ ### Auto mode
365
+
366
+ ```python
367
+ claude.auto_mode_config() # effective classifier config, as a dict
368
+ claude.auto_mode_defaults() # shipped default rules, as a dict
369
+ claude.auto_mode_reset() # remove custom rules from user settings
370
+ claude.auto_mode_critique() # AI feedback on your custom rules
371
+ ```
372
+
373
+ ### Misc
374
+
375
+ ```python
376
+ claude.import_config("cursor", dry_run=True, yes=True) # source: codex, gemini, or cursor
377
+
378
+ claude.ultrareview() # review the current branch
379
+ claude.ultrareview("main") # review against a base branch
380
+ claude.ultrareview(482, as_json=True, post=True) # review a PR, post results, get JSON
381
+
382
+ # Long-running server: returns a Popen handle, doesn't block
383
+ gateway = claude.start_gateway(config="./gateway.json")
384
+ ...
385
+ gateway.terminate()
386
+ ```
387
+
388
+ See `ClaudeCLI`'s docstrings for the exact CLI flags behind each method — it covers every top-level `claude` command (`auth`, `mcp`, `plugin`, `project`, `agents`/background sessions, `auto-mode`, `doctor`, `update`, `install`, `import`, `ultrareview`, `gateway`) plus the main prompt flags. Anything not exposed as a named parameter can still be passed through via each method's `extra_flags` dict.
389
+
390
+ ## Development
391
+
392
+ ```bash
393
+ git clone https://github.com/Suriya-Ravichandran/pyclaudecli.git
394
+ cd pyclaudecli
395
+ pip install -e .
396
+ pytest
397
+ ```
398
+
399
+ ## License
400
+
401
+ MIT
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pyclaudecli"
7
- version = "1.0.0"
7
+ version = "1.0.2"
8
8
  description = "Python library wrapping the Claude Code CLI: prompts, background agents, auth, MCP, plugins, and more"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -3,7 +3,7 @@
3
3
  from .client import ClaudeCLI, build_flags
4
4
  from .exceptions import ClaudeCLIError, ClaudeNotFoundError, ClaudeTimeoutError
5
5
 
6
- __version__ = "1.0.0"
6
+ __version__ = "1.0.2"
7
7
 
8
8
  __all__ = [
9
9
  "ClaudeCLI",
@@ -1,94 +0,0 @@
1
- Metadata-Version: 2.5
2
- Name: pyclaudecli
3
- Version: 1.0.0
4
- Summary: Python library wrapping the Claude Code CLI: prompts, background agents, auth, MCP, plugins, and more
5
- Author-email: Suriya Ravichandran <suriyaravichandran@itrendsolution.com>
6
- License: MIT
7
- License-File: LICENSE
8
- Keywords: anthropic,automation,claude,claude-code,cli,sdk,wrapper
9
- Classifier: Development Status :: 3 - Alpha
10
- Classifier: Intended Audience :: Developers
11
- Classifier: License :: OSI Approved :: MIT License
12
- Classifier: Operating System :: OS Independent
13
- Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3 :: Only
15
- Classifier: Programming Language :: Python :: 3.8
16
- Classifier: Programming Language :: Python :: 3.9
17
- Classifier: Programming Language :: Python :: 3.10
18
- Classifier: Programming Language :: Python :: 3.11
19
- Classifier: Programming Language :: Python :: 3.12
20
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
- Requires-Python: >=3.8
22
- Description-Content-Type: text/markdown
23
-
24
- # pyclaudecli
25
-
26
- A Python library that wraps the [Claude Code](https://claude.com/claude-code) CLI (`claude`) so you can drive it from Python instead of shelling out by hand: one-shot prompts, JSON/streaming output, background agents, authentication, MCP servers, plugins, and the rest of the CLI's surface.
27
-
28
- It's a thin wrapper, not a reimplementation — every call runs the real `claude` binary, so it always reflects whatever version, auth, and config you have installed locally.
29
-
30
- ## Install
31
-
32
- ```bash
33
- pip install pyclaudecli
34
- ```
35
-
36
- Requires the `claude` CLI itself to be installed and on `PATH` (see the [Claude Code docs](https://claude.com/claude-code)).
37
-
38
- ## Quickstart
39
-
40
- ```python
41
- from pyclaudecli import ClaudeCLI
42
-
43
- claude = ClaudeCLI()
44
-
45
- # One-shot prompt
46
- print(claude.prompt("Summarize this repo's README.", model="haiku"))
47
-
48
- # Structured result (cost, session id, etc.)
49
- result = claude.prompt_json("What's 2+2?", model="haiku")
50
- print(result["result"], result["total_cost_usd"])
51
-
52
- # Live streaming events
53
- for event in claude.prompt_stream("Write a haiku about tests.", model="haiku"):
54
- print(event["type"])
55
- ```
56
-
57
- ## Background agents
58
-
59
- ```python
60
- session_id = claude.start_background("Refactor the auth module", model="sonnet")
61
- claude.list_agents()
62
- claude.logs(session_id, strip_ansi=True)
63
- claude.stop(session_id)
64
- claude.rm(session_id)
65
- ```
66
-
67
- ## Auth
68
-
69
- ```python
70
- claude.auth_status() # {"loggedIn": True, "email": "...", ...}
71
-
72
- # Prints the sign-in URL, then forwards the pasted code to finish login
73
- claude.auth_login()
74
- ```
75
-
76
- ## MCP servers and plugins
77
-
78
- ```python
79
- claude.mcp_add("sentry", "https://mcp.sentry.dev/mcp", transport="http")
80
- claude.mcp_list()
81
-
82
- claude.plugin_install("some-plugin", yes=True)
83
- claude.plugin_list(as_json=True)
84
- ```
85
-
86
- See `ClaudeCLI`'s docstrings for the full method list — it covers every top-level `claude` command (`auth`, `mcp`, `plugin`, `project`, `agents`/background sessions, `auto-mode`, `doctor`, `update`, `install`, `import`, `ultrareview`, `gateway`) plus the main prompt flags. Anything not exposed as a named parameter can still be passed through via each method's `extra_flags` dict.
87
-
88
- ## Errors
89
-
90
- All CLI failures raise `ClaudeCLIError` (or `ClaudeNotFoundError` / `ClaudeTimeoutError`), carrying `returncode`, `stdout`, and `stderr`.
91
-
92
- ## License
93
-
94
- MIT
@@ -1,71 +0,0 @@
1
- # pyclaudecli
2
-
3
- A Python library that wraps the [Claude Code](https://claude.com/claude-code) CLI (`claude`) so you can drive it from Python instead of shelling out by hand: one-shot prompts, JSON/streaming output, background agents, authentication, MCP servers, plugins, and the rest of the CLI's surface.
4
-
5
- It's a thin wrapper, not a reimplementation — every call runs the real `claude` binary, so it always reflects whatever version, auth, and config you have installed locally.
6
-
7
- ## Install
8
-
9
- ```bash
10
- pip install pyclaudecli
11
- ```
12
-
13
- Requires the `claude` CLI itself to be installed and on `PATH` (see the [Claude Code docs](https://claude.com/claude-code)).
14
-
15
- ## Quickstart
16
-
17
- ```python
18
- from pyclaudecli import ClaudeCLI
19
-
20
- claude = ClaudeCLI()
21
-
22
- # One-shot prompt
23
- print(claude.prompt("Summarize this repo's README.", model="haiku"))
24
-
25
- # Structured result (cost, session id, etc.)
26
- result = claude.prompt_json("What's 2+2?", model="haiku")
27
- print(result["result"], result["total_cost_usd"])
28
-
29
- # Live streaming events
30
- for event in claude.prompt_stream("Write a haiku about tests.", model="haiku"):
31
- print(event["type"])
32
- ```
33
-
34
- ## Background agents
35
-
36
- ```python
37
- session_id = claude.start_background("Refactor the auth module", model="sonnet")
38
- claude.list_agents()
39
- claude.logs(session_id, strip_ansi=True)
40
- claude.stop(session_id)
41
- claude.rm(session_id)
42
- ```
43
-
44
- ## Auth
45
-
46
- ```python
47
- claude.auth_status() # {"loggedIn": True, "email": "...", ...}
48
-
49
- # Prints the sign-in URL, then forwards the pasted code to finish login
50
- claude.auth_login()
51
- ```
52
-
53
- ## MCP servers and plugins
54
-
55
- ```python
56
- claude.mcp_add("sentry", "https://mcp.sentry.dev/mcp", transport="http")
57
- claude.mcp_list()
58
-
59
- claude.plugin_install("some-plugin", yes=True)
60
- claude.plugin_list(as_json=True)
61
- ```
62
-
63
- See `ClaudeCLI`'s docstrings for the full method list — it covers every top-level `claude` command (`auth`, `mcp`, `plugin`, `project`, `agents`/background sessions, `auto-mode`, `doctor`, `update`, `install`, `import`, `ultrareview`, `gateway`) plus the main prompt flags. Anything not exposed as a named parameter can still be passed through via each method's `extra_flags` dict.
64
-
65
- ## Errors
66
-
67
- All CLI failures raise `ClaudeCLIError` (or `ClaudeNotFoundError` / `ClaudeTimeoutError`), carrying `returncode`, `stdout`, and `stderr`.
68
-
69
- ## License
70
-
71
- MIT
File without changes
File without changes