orchid-cli 1.2.0__tar.gz → 1.3.0__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 (62) hide show
  1. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/CHANGELOG.md +26 -0
  2. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/PKG-INFO +98 -23
  3. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/README.md +96 -21
  4. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/auth/token_store.py +5 -4
  5. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/commands/index.py +5 -4
  6. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/pyproject.toml +2 -2
  7. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/.editorconfig +0 -0
  8. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/.github/workflows/ci.yml +0 -0
  9. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/.gitignore +0 -0
  10. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/.gitlint +0 -0
  11. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/.pre-commit-config.yaml +0 -0
  12. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/AGENTS.md +0 -0
  13. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/CLAUDE.md +0 -0
  14. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/CONTRIBUTING.md +0 -0
  15. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/LICENSE +0 -0
  16. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/icon.svg +0 -0
  17. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/__init__.py +0 -0
  18. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/_output.py +0 -0
  19. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/_typer_async.py +0 -0
  20. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/auth/__init__.py +0 -0
  21. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/auth/config.py +0 -0
  22. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/auth/flow.py +0 -0
  23. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/auth/middleware.py +0 -0
  24. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/auth/oidc.py +0 -0
  25. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/auth/pkce.py +0 -0
  26. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/bootstrap.py +0 -0
  27. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/commands/__init__.py +0 -0
  28. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/commands/_chat_helpers.py +0 -0
  29. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/commands/_chat_interactive.py +0 -0
  30. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/commands/_chat_send.py +0 -0
  31. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/commands/_session.py +0 -0
  32. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/commands/_skill_guardrails.py +0 -0
  33. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/commands/_skill_md.py +0 -0
  34. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/commands/_skill_text.py +0 -0
  35. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/commands/_skill_tools.py +0 -0
  36. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/commands/_tool_metadata.py +0 -0
  37. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/commands/auth.py +0 -0
  38. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/commands/chat.py +0 -0
  39. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/commands/config.py +0 -0
  40. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/commands/mcp.py +0 -0
  41. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/commands/skill.py +0 -0
  42. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/main.py +0 -0
  43. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/orchid_cli/slash_commands.py +0 -0
  44. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/conftest.py +0 -0
  45. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/fixtures/__init__.py +0 -0
  46. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/fixtures/fake_tools_dates.py +0 -0
  47. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/fixtures/fake_tools_math.py +0 -0
  48. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/test_auth.py +0 -0
  49. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/test_bootstrap.py +0 -0
  50. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/test_bootstrap_warm.py +0 -0
  51. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/test_browser_opener.py +0 -0
  52. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/test_chat_builtin_slash.py +0 -0
  53. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/test_commands_chat.py +0 -0
  54. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/test_commands_config.py +0 -0
  55. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/test_commands_skill.py +0 -0
  56. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/test_middleware_seams.py +0 -0
  57. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/test_output.py +0 -0
  58. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/test_pkce.py +0 -0
  59. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/test_resolve_session.py +0 -0
  60. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/test_slash_commands.py +0 -0
  61. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/test_token_store_security.py +0 -0
  62. {orchid_cli-1.2.0 → orchid_cli-1.3.0}/tests/test_tool_metadata.py +0 -0
@@ -2,6 +2,32 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v1.3.0 (2026-05-05)
6
+
7
+ ### Chores
8
+
9
+ - Bump `orchid-ai` dependency to >=1.6.0 in `orchid-cli` and `orchid-api`
10
+ ([`2aaedaf`](https://github.com/gadz82/orchid-cli/commit/2aaedafb417294f1780d80464b469467656fcd5d))
11
+
12
+ ### Documentation
13
+
14
+ - Update Orchid API and MCP gateway README
15
+ ([`9ff4750`](https://github.com/gadz82/orchid-cli/commit/9ff475005b06857afbc215b68c573b1a8c11a750))
16
+
17
+ ### Features
18
+
19
+ - **cli**: Enable RecursiveIngestion for RAG index workflows
20
+ ([`14bf9d4`](https://github.com/gadz82/orchid-cli/commit/14bf9d49b1eb6e5bf613497881765a0554d84f26))
21
+
22
+
23
+ ## v1.2.1 (2026-05-04)
24
+
25
+ ### Bug Fixes
26
+
27
+ - Broken unit tests by ensuring lock.
28
+ ([`64f59fa`](https://github.com/gadz82/orchid-cli/commit/64f59fa71f6efe7f2a5fd51c35ea2fb48f6cf8c4))
29
+
30
+
5
31
  ## v1.2.0 (2026-04-29)
6
32
 
7
33
  ### Bug Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orchid-cli
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: Orchid CLI — command-line interface for the Orchid agent framework
5
5
  Project-URL: Homepage, https://github.com/gadz82/orchid
6
6
  Project-URL: Repository, https://github.com/gadz82/orchid
@@ -26,7 +26,7 @@ Classifier: Typing :: Typed
26
26
  Requires-Python: >=3.11
27
27
  Requires-Dist: httpx>=0.28.0
28
28
  Requires-Dist: langchain-core>=0.3.0
29
- Requires-Dist: orchid-ai>=1.4.0
29
+ Requires-Dist: orchid-ai>=1.6.0
30
30
  Requires-Dist: pydantic-settings>=2.7.0
31
31
  Requires-Dist: pyyaml>=6.0
32
32
  Requires-Dist: rich>=13.0
@@ -46,9 +46,17 @@ Description-Content-Type: text/markdown
46
46
 
47
47
  <h1 align="center">Orchid CLI</h1>
48
48
 
49
- Command-line interface for the [Orchid](https://github.com/gadz82/orchid) multi-agent AI framework.
49
+ Command-line interface for the [Orchid](../orchid/) multi-agent AI framework.
50
50
 
51
- Provides terminal access to all chat operations, configuration validation, RAG indexing, and Claude Code skill generation. Mirrors the full functionality of [orchid-api](../orchid-api) but runs locally with no server, Docker, or external database required (defaults to SQLite).
51
+ Provides terminal access to all chat operations, configuration validation, RAG indexing, MCP server authorisation, and Claude Code skill generation. Mirrors the full functionality of [orchid-api](../orchid-api/) but runs locally with no server, Docker, or external database required (defaults to SQLite).
52
+
53
+ ## Why use the CLI
54
+
55
+ - **Embedded workflows** — the CLI runs the same Orchid runtime as the API but in-process. Useful for batch jobs, scripted pipelines, and offline environments.
56
+ - **Identical config surface** — point at any `orchid.yml` / `agents.yaml` pair (basketball, restaurant, helpdesk, your own) and the CLI behaves the way orchid-api does, minus the HTTP layer.
57
+ - **No infrastructure required** — defaults to SQLite chat storage and the in-process vector reader. Add Qdrant / Postgres only when you need them.
58
+ - **Plugin extensible** — register custom subcommands via Python entry points without forking.
59
+ - **Skill export** — turn an `agents.yaml` into a set of Claude Code skill folders so the same agents are usable from `claude` directly.
52
60
 
53
61
  ## Installation
54
62
 
@@ -75,6 +83,30 @@ orchid chat create -c orchid.yml -t "My Chat"
75
83
  orchid chat send <chat_id> "Hello!" -c orchid.yml
76
84
  ```
77
85
 
86
+ ## Common workflows
87
+
88
+ ```bash
89
+ # 1) Try a built-in example with no setup
90
+ orchid chat interactive -c examples/basketball/orchid.yml
91
+
92
+ # 2) Validate every example (CI-friendly)
93
+ for f in examples/**/agents.yaml examples/**/config/agents.yaml; do
94
+ orchid config validate "$f"
95
+ done
96
+
97
+ # 3) Pre-seed RAG with internal documentation
98
+ orchid index dir ./docs/internal -n knowledge_base -c orchid.yml --pattern '*.md'
99
+
100
+ # 4) Export agents as Claude Code skills for ad-hoc usage
101
+ orchid skill generate examples/restaurant/config/agents.yaml -o ~/.claude/skills
102
+
103
+ # 5) Drive a non-interactive chat from a shell script
104
+ chat_id=$(orchid chat create -c orchid.yml -t "batch-$(date +%s)" --json | jq -r .id)
105
+ for q in "$@"; do
106
+ orchid chat send "$chat_id" "$q" -c orchid.yml
107
+ done
108
+ ```
109
+
78
110
  ## Commands
79
111
 
80
112
  ### Authentication
@@ -116,7 +148,7 @@ orchid chat delete <chat_id> -c orchid.yml
116
148
  orchid chat delete <chat_id> -c orchid.yml --force
117
149
  ```
118
150
 
119
- Chat IDs support **prefix matching** -- type the first few characters of the UUID.
151
+ Chat IDs support **prefix matching** type the first few characters of the UUID.
120
152
 
121
153
  ### Messaging
122
154
 
@@ -128,6 +160,13 @@ orchid chat send <chat_id> "What is LangGraph?" -c orchid.yml
128
160
  orchid chat send <chat_id> "Explain RAG" -c orchid.yml -m ollama/llama3.2
129
161
  ```
130
162
 
163
+ The CLI honours every framework feature exposed by the YAML config:
164
+
165
+ - **Mini-agents** — when an agent has `mini_agent.enabled: true`, the CLI streams the same `mini_agent.{decomposed,started,finished,aggregated}` lifecycle markers the frontend renders, surfaced in the terminal as collapsible sections.
166
+ - **Parallel tools** — `parallel_tools: true` on an agent activates Phase A intra-round parallel dispatch; the CLI doesn't need any special flag.
167
+ - **Prompt customisation** — every `prompt_sections` and `transformer_prompts` override declared in YAML applies inside the CLI just like the API.
168
+ - **Sliding-window summarization** — `supervisor.history_summary_enabled: true` compresses older history before the LLM call. CLI users see the same effect on long chats.
169
+
131
170
  ### Interactive Mode
132
171
 
133
172
  ```bash
@@ -156,6 +195,8 @@ Slash commands available inside interactive mode:
156
195
  orchid config validate path/to/agents.yaml
157
196
  ```
158
197
 
198
+ Validation runs the full Pydantic schema, the defaults-merger pass, and any custom validators registered via the `OrchidAgentsConfig` plugin hooks. Errors include the exact field path and the offending value.
199
+
159
200
  ### MCP Server Authorization
160
201
 
161
202
  Manage per-server OAuth for MCP servers that declare `auth.mode: oauth` in `agents.yaml`.
@@ -257,6 +298,8 @@ orchid skill generate path/to/agents.yaml --zip
257
298
 
258
299
  Each agent skill includes a `scripts/` folder with standalone Python scripts that Claude Code can execute directly. Tools from the same source module are grouped into a single script file with a CLI wrapper that accepts `--arg value` arguments.
259
300
 
301
+ The skill generator pulls parameter metadata from the YAML `tools:` block when present (per ADR-017's parameter declaration), and falls back to Python signature introspection when omitted.
302
+
260
303
  ## Configuration
261
304
 
262
305
  The `--config` (`-c`) flag points to an `orchid.yml` file:
@@ -283,6 +326,10 @@ storage:
283
326
  class: orchid_ai.persistence.sqlite.OrchidSQLiteChatStorage
284
327
  dsn: ~/.orchid/chats.db
285
328
 
329
+ # Startup hook (e.g. seeds RAG, registers custom strategies / tools)
330
+ startup:
331
+ hook: examples.travel-agency.hooks.startup.bootstrap_travel
332
+
286
333
  # LangGraph checkpointer (optional) — enables persistent graph state,
287
334
  # required for Human-in-the-Loop tool approval
288
335
  checkpointer:
@@ -305,7 +352,7 @@ Chat data is stored in SQLite at `~/.orchid/chats.db` by default. OAuth tokens a
305
352
 
306
353
  ### Checkpointing
307
354
 
308
- The CLI supports LangGraph checkpointers for persistent graph state. This is **required** when any agent uses Human-in-the-Loop (`requires_approval: true` on tools).
355
+ The CLI supports LangGraph checkpointers for persistent graph state. This is **required** when any agent uses Human-in-the-Loop (`requires_approval: true` on tools) or relies on resume-after-interrupt flows.
309
356
 
310
357
  ```yaml
311
358
  # In orchid.yml
@@ -328,16 +375,16 @@ The CLI supports **OAuth 2.0 Authorization Code + PKCE** for authenticating with
328
375
 
329
376
  ### How It Works
330
377
 
331
- 1. `orchid auth login` opens the system browser to the provider's authorization page
332
- 2. User authenticates and consents
333
- 3. Provider redirects to a temporary `localhost` callback server
334
- 4. CLI exchanges the authorization code for access + refresh tokens (with PKCE verification)
335
- 5. Tokens are stored at `~/.orchid/tokens.json`
336
- 6. All subsequent `orchid chat` commands use the stored token automatically
378
+ 1. `orchid auth login` opens the system browser to the provider's authorization page.
379
+ 2. User authenticates and consents.
380
+ 3. Provider redirects to a temporary `localhost` callback server.
381
+ 4. CLI exchanges the authorization code for access + refresh tokens (with PKCE verification).
382
+ 5. Tokens are stored at `~/.orchid/tokens.json` with `0o600` permissions.
383
+ 6. All subsequent `orchid chat` commands use the stored token automatically.
337
384
 
338
385
  ### OIDC Discovery
339
386
 
340
- When `issuer` is set in the config, the CLI fetches `{issuer}/.well-known/openid-configuration` to auto-discover `authorization_endpoint` and `token_endpoint`. This is the recommended approach -- you only need the issuer URL.
387
+ When `issuer` is set in the config, the CLI fetches `{issuer}/.well-known/openid-configuration` to auto-discover `authorization_endpoint` and `token_endpoint`. This is the recommended approach you only need the issuer URL.
341
388
 
342
389
  ### Token Refresh
343
390
 
@@ -345,22 +392,19 @@ When the access token expires and a refresh token is available, the CLI refreshe
345
392
 
346
393
  ### Identity Resolution
347
394
 
348
- When `identity_resolver_class` is configured, the CLI calls the resolver after login to populate `tenant_key` and `user_id` from the OAuth token. These identity fields are cached in the token file so subsequent commands don't need the resolver. See the [orchid OrchidIdentityResolver ABC](../orchid/orchid_ai/core/identity.py) for the interface.
395
+ When `identity_resolver_class` is configured, the CLI calls the resolver after login to populate `tenant_key` and `user_id` from the OAuth token. These identity fields are cached in the token file so subsequent commands don't need the resolver. See the [`OrchidIdentityResolver` ABC](../orchid/orchid_ai/core/identity.py) for the interface.
349
396
 
350
397
  > **Note:** the CLI is an **independent OAuth client** — it runs its own
351
398
  > authorization-code + PKCE dance against the upstream IdP and calls the
352
399
  > identity resolver locally. It does NOT use the
353
400
  > centralised `/auth/exchange-code` / `/auth/resolve-identity` /
354
- > `/auth/refresh-token` endpoints that the MCP gateway uses (Phases 1–5
355
- > of the [auth-centralisation roadmap](../.knowledge/auth-centralisation.md)).
401
+ > `/auth/refresh-token` endpoints that the MCP gateway uses.
356
402
  > The CLI ships with the upstream secret `client_id` baked into its config
357
- > because it's a desktop app, not a network service. A future migration
358
- > could route the CLI through the same endpoints to remove the
359
- > CLI-side `client_secret` / userinfo coupling — out of scope today.
403
+ > because it's a desktop app, not a network service.
360
404
 
361
405
  ### Dev Fallback
362
406
 
363
- When `auth.dev_bypass: true` or `auth.cli` is absent, the CLI uses a dummy token (`cli-token`, tenant=`cli`, user=`cli-user`). This is fully backward compatible -- existing configs without OAuth continue to work unchanged.
407
+ When `auth.dev_bypass: true` or `auth.cli` is absent, the CLI uses a dummy token (`cli-token`, tenant=`cli`, user=`cli-user`). This is fully backward compatible existing configs without OAuth continue to work unchanged.
364
408
 
365
409
  ## Prerequisites
366
410
 
@@ -396,7 +440,7 @@ After `pip install mypackage`, the command is available as `orchid mycommand gre
396
440
  orchid_cli/
397
441
  main.py Typer entry point — registers built-in + plugin subcommands
398
442
  bootstrap.py Shared startup: load config, build graph, init storage,
399
- wire checkpointer (optional)
443
+ wire checkpointer (optional), invoke startup hook
400
444
  auth/ OAuth 2.0 authentication (self-contained)
401
445
  config.py Provider settings from orchid.yml
402
446
  oidc.py Shared OIDC discovery utility
@@ -415,7 +459,38 @@ orchid_cli/
415
459
  skill.py Generate Claude Code skills from agents.yaml
416
460
  ```
417
461
 
418
- The CLI is a thin layer that calls `orchid` SDK functions and displays results via Rich. The `auth/` subpackage is fully self-contained no OAuth logic leaks into chat commands or bootstrap. Interactive-mode slash commands use a dispatch table (`_SLASH_COMMANDS` in `chat.py`) so new slash commands can be added with a single handler function.
462
+ `bootstrap.py` mirrors the orchid-api lifespan: load and validate config, build the LangGraph runtime, initialise the storage backend, wire the checkpointer, fire the startup hook (if any). The chat commands then attach an `OrchidAuthContext` per call and enter the supervisor same primitives orchid-api uses, just without the FastAPI shell.
463
+
464
+ ## Embedded mode (using the SDK directly)
465
+
466
+ For applications that need Orchid as a library rather than a CLI:
467
+
468
+ ```python
469
+ from orchid_ai import Orchid, OrchidRuntime
470
+ from orchid_ai.config.loader import load_config
471
+
472
+ config = load_config("orchid.yml")
473
+ runtime = OrchidRuntime.from_config(config)
474
+ async with runtime:
475
+ answer = await runtime.ask(
476
+ "Hello!",
477
+ tenant_id="default",
478
+ user_id="me",
479
+ chat_id="chat-1",
480
+ )
481
+ print(answer.text)
482
+ ```
483
+
484
+ The CLI uses these primitives internally; embedded users get the same behaviour without the Typer shell. See `examples/embedded-python/` for end-to-end patterns.
485
+
486
+ ## Troubleshooting
487
+
488
+ - **`Cannot resolve chat storage class '…'`** — the dotted import path in `storage.class` failed to import. Confirm the package is installed and the module path is reachable from `PYTHONPATH`.
489
+ - **`No module named 'aiosqlite'`** — install the SQLite extra: `pip install orchid-ai[checkpoint-sqlite]`.
490
+ - **OAuth `redirect_uri_mismatch`** — register `http://localhost:<port>/callback` (the port the CLI prints on `auth login`) with your IdP. Some IdPs accept the loopback wildcard `http://127.0.0.1`; others require the literal port.
491
+ - **Tokens stored but `auth status` shows expired** — refresh failed. Inspect `~/.orchid/tokens.json` (chmod 600) and re-run `orchid auth login`.
492
+ - **Slow startup with custom LLM provider** — `bootstrap.py` initialises the chat model lazily, but startup hooks run synchronously. Move heavy work behind `if reader and reader.supports_writes:` guards inside the hook.
493
+ - **`No agents loaded`** — likely missing `agents.config_path` in `orchid.yml`. Inline-config users should switch to `agents:` (see `examples/embedded-python/06_inline_config.py`).
419
494
 
420
495
  ## Development
421
496
 
@@ -434,4 +509,4 @@ ruff check orchid_cli/
434
509
 
435
510
  ## License
436
511
 
437
- MIT -- see [LICENSE](LICENSE).
512
+ MIT see [LICENSE](LICENSE).
@@ -4,9 +4,17 @@
4
4
 
5
5
  <h1 align="center">Orchid CLI</h1>
6
6
 
7
- Command-line interface for the [Orchid](https://github.com/gadz82/orchid) multi-agent AI framework.
7
+ Command-line interface for the [Orchid](../orchid/) multi-agent AI framework.
8
8
 
9
- Provides terminal access to all chat operations, configuration validation, RAG indexing, and Claude Code skill generation. Mirrors the full functionality of [orchid-api](../orchid-api) but runs locally with no server, Docker, or external database required (defaults to SQLite).
9
+ Provides terminal access to all chat operations, configuration validation, RAG indexing, MCP server authorisation, and Claude Code skill generation. Mirrors the full functionality of [orchid-api](../orchid-api/) but runs locally with no server, Docker, or external database required (defaults to SQLite).
10
+
11
+ ## Why use the CLI
12
+
13
+ - **Embedded workflows** — the CLI runs the same Orchid runtime as the API but in-process. Useful for batch jobs, scripted pipelines, and offline environments.
14
+ - **Identical config surface** — point at any `orchid.yml` / `agents.yaml` pair (basketball, restaurant, helpdesk, your own) and the CLI behaves the way orchid-api does, minus the HTTP layer.
15
+ - **No infrastructure required** — defaults to SQLite chat storage and the in-process vector reader. Add Qdrant / Postgres only when you need them.
16
+ - **Plugin extensible** — register custom subcommands via Python entry points without forking.
17
+ - **Skill export** — turn an `agents.yaml` into a set of Claude Code skill folders so the same agents are usable from `claude` directly.
10
18
 
11
19
  ## Installation
12
20
 
@@ -33,6 +41,30 @@ orchid chat create -c orchid.yml -t "My Chat"
33
41
  orchid chat send <chat_id> "Hello!" -c orchid.yml
34
42
  ```
35
43
 
44
+ ## Common workflows
45
+
46
+ ```bash
47
+ # 1) Try a built-in example with no setup
48
+ orchid chat interactive -c examples/basketball/orchid.yml
49
+
50
+ # 2) Validate every example (CI-friendly)
51
+ for f in examples/**/agents.yaml examples/**/config/agents.yaml; do
52
+ orchid config validate "$f"
53
+ done
54
+
55
+ # 3) Pre-seed RAG with internal documentation
56
+ orchid index dir ./docs/internal -n knowledge_base -c orchid.yml --pattern '*.md'
57
+
58
+ # 4) Export agents as Claude Code skills for ad-hoc usage
59
+ orchid skill generate examples/restaurant/config/agents.yaml -o ~/.claude/skills
60
+
61
+ # 5) Drive a non-interactive chat from a shell script
62
+ chat_id=$(orchid chat create -c orchid.yml -t "batch-$(date +%s)" --json | jq -r .id)
63
+ for q in "$@"; do
64
+ orchid chat send "$chat_id" "$q" -c orchid.yml
65
+ done
66
+ ```
67
+
36
68
  ## Commands
37
69
 
38
70
  ### Authentication
@@ -74,7 +106,7 @@ orchid chat delete <chat_id> -c orchid.yml
74
106
  orchid chat delete <chat_id> -c orchid.yml --force
75
107
  ```
76
108
 
77
- Chat IDs support **prefix matching** -- type the first few characters of the UUID.
109
+ Chat IDs support **prefix matching** type the first few characters of the UUID.
78
110
 
79
111
  ### Messaging
80
112
 
@@ -86,6 +118,13 @@ orchid chat send <chat_id> "What is LangGraph?" -c orchid.yml
86
118
  orchid chat send <chat_id> "Explain RAG" -c orchid.yml -m ollama/llama3.2
87
119
  ```
88
120
 
121
+ The CLI honours every framework feature exposed by the YAML config:
122
+
123
+ - **Mini-agents** — when an agent has `mini_agent.enabled: true`, the CLI streams the same `mini_agent.{decomposed,started,finished,aggregated}` lifecycle markers the frontend renders, surfaced in the terminal as collapsible sections.
124
+ - **Parallel tools** — `parallel_tools: true` on an agent activates Phase A intra-round parallel dispatch; the CLI doesn't need any special flag.
125
+ - **Prompt customisation** — every `prompt_sections` and `transformer_prompts` override declared in YAML applies inside the CLI just like the API.
126
+ - **Sliding-window summarization** — `supervisor.history_summary_enabled: true` compresses older history before the LLM call. CLI users see the same effect on long chats.
127
+
89
128
  ### Interactive Mode
90
129
 
91
130
  ```bash
@@ -114,6 +153,8 @@ Slash commands available inside interactive mode:
114
153
  orchid config validate path/to/agents.yaml
115
154
  ```
116
155
 
156
+ Validation runs the full Pydantic schema, the defaults-merger pass, and any custom validators registered via the `OrchidAgentsConfig` plugin hooks. Errors include the exact field path and the offending value.
157
+
117
158
  ### MCP Server Authorization
118
159
 
119
160
  Manage per-server OAuth for MCP servers that declare `auth.mode: oauth` in `agents.yaml`.
@@ -215,6 +256,8 @@ orchid skill generate path/to/agents.yaml --zip
215
256
 
216
257
  Each agent skill includes a `scripts/` folder with standalone Python scripts that Claude Code can execute directly. Tools from the same source module are grouped into a single script file with a CLI wrapper that accepts `--arg value` arguments.
217
258
 
259
+ The skill generator pulls parameter metadata from the YAML `tools:` block when present (per ADR-017's parameter declaration), and falls back to Python signature introspection when omitted.
260
+
218
261
  ## Configuration
219
262
 
220
263
  The `--config` (`-c`) flag points to an `orchid.yml` file:
@@ -241,6 +284,10 @@ storage:
241
284
  class: orchid_ai.persistence.sqlite.OrchidSQLiteChatStorage
242
285
  dsn: ~/.orchid/chats.db
243
286
 
287
+ # Startup hook (e.g. seeds RAG, registers custom strategies / tools)
288
+ startup:
289
+ hook: examples.travel-agency.hooks.startup.bootstrap_travel
290
+
244
291
  # LangGraph checkpointer (optional) — enables persistent graph state,
245
292
  # required for Human-in-the-Loop tool approval
246
293
  checkpointer:
@@ -263,7 +310,7 @@ Chat data is stored in SQLite at `~/.orchid/chats.db` by default. OAuth tokens a
263
310
 
264
311
  ### Checkpointing
265
312
 
266
- The CLI supports LangGraph checkpointers for persistent graph state. This is **required** when any agent uses Human-in-the-Loop (`requires_approval: true` on tools).
313
+ The CLI supports LangGraph checkpointers for persistent graph state. This is **required** when any agent uses Human-in-the-Loop (`requires_approval: true` on tools) or relies on resume-after-interrupt flows.
267
314
 
268
315
  ```yaml
269
316
  # In orchid.yml
@@ -286,16 +333,16 @@ The CLI supports **OAuth 2.0 Authorization Code + PKCE** for authenticating with
286
333
 
287
334
  ### How It Works
288
335
 
289
- 1. `orchid auth login` opens the system browser to the provider's authorization page
290
- 2. User authenticates and consents
291
- 3. Provider redirects to a temporary `localhost` callback server
292
- 4. CLI exchanges the authorization code for access + refresh tokens (with PKCE verification)
293
- 5. Tokens are stored at `~/.orchid/tokens.json`
294
- 6. All subsequent `orchid chat` commands use the stored token automatically
336
+ 1. `orchid auth login` opens the system browser to the provider's authorization page.
337
+ 2. User authenticates and consents.
338
+ 3. Provider redirects to a temporary `localhost` callback server.
339
+ 4. CLI exchanges the authorization code for access + refresh tokens (with PKCE verification).
340
+ 5. Tokens are stored at `~/.orchid/tokens.json` with `0o600` permissions.
341
+ 6. All subsequent `orchid chat` commands use the stored token automatically.
295
342
 
296
343
  ### OIDC Discovery
297
344
 
298
- When `issuer` is set in the config, the CLI fetches `{issuer}/.well-known/openid-configuration` to auto-discover `authorization_endpoint` and `token_endpoint`. This is the recommended approach -- you only need the issuer URL.
345
+ When `issuer` is set in the config, the CLI fetches `{issuer}/.well-known/openid-configuration` to auto-discover `authorization_endpoint` and `token_endpoint`. This is the recommended approach you only need the issuer URL.
299
346
 
300
347
  ### Token Refresh
301
348
 
@@ -303,22 +350,19 @@ When the access token expires and a refresh token is available, the CLI refreshe
303
350
 
304
351
  ### Identity Resolution
305
352
 
306
- When `identity_resolver_class` is configured, the CLI calls the resolver after login to populate `tenant_key` and `user_id` from the OAuth token. These identity fields are cached in the token file so subsequent commands don't need the resolver. See the [orchid OrchidIdentityResolver ABC](../orchid/orchid_ai/core/identity.py) for the interface.
353
+ When `identity_resolver_class` is configured, the CLI calls the resolver after login to populate `tenant_key` and `user_id` from the OAuth token. These identity fields are cached in the token file so subsequent commands don't need the resolver. See the [`OrchidIdentityResolver` ABC](../orchid/orchid_ai/core/identity.py) for the interface.
307
354
 
308
355
  > **Note:** the CLI is an **independent OAuth client** — it runs its own
309
356
  > authorization-code + PKCE dance against the upstream IdP and calls the
310
357
  > identity resolver locally. It does NOT use the
311
358
  > centralised `/auth/exchange-code` / `/auth/resolve-identity` /
312
- > `/auth/refresh-token` endpoints that the MCP gateway uses (Phases 1–5
313
- > of the [auth-centralisation roadmap](../.knowledge/auth-centralisation.md)).
359
+ > `/auth/refresh-token` endpoints that the MCP gateway uses.
314
360
  > The CLI ships with the upstream secret `client_id` baked into its config
315
- > because it's a desktop app, not a network service. A future migration
316
- > could route the CLI through the same endpoints to remove the
317
- > CLI-side `client_secret` / userinfo coupling — out of scope today.
361
+ > because it's a desktop app, not a network service.
318
362
 
319
363
  ### Dev Fallback
320
364
 
321
- When `auth.dev_bypass: true` or `auth.cli` is absent, the CLI uses a dummy token (`cli-token`, tenant=`cli`, user=`cli-user`). This is fully backward compatible -- existing configs without OAuth continue to work unchanged.
365
+ When `auth.dev_bypass: true` or `auth.cli` is absent, the CLI uses a dummy token (`cli-token`, tenant=`cli`, user=`cli-user`). This is fully backward compatible existing configs without OAuth continue to work unchanged.
322
366
 
323
367
  ## Prerequisites
324
368
 
@@ -354,7 +398,7 @@ After `pip install mypackage`, the command is available as `orchid mycommand gre
354
398
  orchid_cli/
355
399
  main.py Typer entry point — registers built-in + plugin subcommands
356
400
  bootstrap.py Shared startup: load config, build graph, init storage,
357
- wire checkpointer (optional)
401
+ wire checkpointer (optional), invoke startup hook
358
402
  auth/ OAuth 2.0 authentication (self-contained)
359
403
  config.py Provider settings from orchid.yml
360
404
  oidc.py Shared OIDC discovery utility
@@ -373,7 +417,38 @@ orchid_cli/
373
417
  skill.py Generate Claude Code skills from agents.yaml
374
418
  ```
375
419
 
376
- The CLI is a thin layer that calls `orchid` SDK functions and displays results via Rich. The `auth/` subpackage is fully self-contained no OAuth logic leaks into chat commands or bootstrap. Interactive-mode slash commands use a dispatch table (`_SLASH_COMMANDS` in `chat.py`) so new slash commands can be added with a single handler function.
420
+ `bootstrap.py` mirrors the orchid-api lifespan: load and validate config, build the LangGraph runtime, initialise the storage backend, wire the checkpointer, fire the startup hook (if any). The chat commands then attach an `OrchidAuthContext` per call and enter the supervisor same primitives orchid-api uses, just without the FastAPI shell.
421
+
422
+ ## Embedded mode (using the SDK directly)
423
+
424
+ For applications that need Orchid as a library rather than a CLI:
425
+
426
+ ```python
427
+ from orchid_ai import Orchid, OrchidRuntime
428
+ from orchid_ai.config.loader import load_config
429
+
430
+ config = load_config("orchid.yml")
431
+ runtime = OrchidRuntime.from_config(config)
432
+ async with runtime:
433
+ answer = await runtime.ask(
434
+ "Hello!",
435
+ tenant_id="default",
436
+ user_id="me",
437
+ chat_id="chat-1",
438
+ )
439
+ print(answer.text)
440
+ ```
441
+
442
+ The CLI uses these primitives internally; embedded users get the same behaviour without the Typer shell. See `examples/embedded-python/` for end-to-end patterns.
443
+
444
+ ## Troubleshooting
445
+
446
+ - **`Cannot resolve chat storage class '…'`** — the dotted import path in `storage.class` failed to import. Confirm the package is installed and the module path is reachable from `PYTHONPATH`.
447
+ - **`No module named 'aiosqlite'`** — install the SQLite extra: `pip install orchid-ai[checkpoint-sqlite]`.
448
+ - **OAuth `redirect_uri_mismatch`** — register `http://localhost:<port>/callback` (the port the CLI prints on `auth login`) with your IdP. Some IdPs accept the loopback wildcard `http://127.0.0.1`; others require the literal port.
449
+ - **Tokens stored but `auth status` shows expired** — refresh failed. Inspect `~/.orchid/tokens.json` (chmod 600) and re-run `orchid auth login`.
450
+ - **Slow startup with custom LLM provider** — `bootstrap.py` initialises the chat model lazily, but startup hooks run synchronously. Move heavy work behind `if reader and reader.supports_writes:` guards inside the hook.
451
+ - **`No agents loaded`** — likely missing `agents.config_path` in `orchid.yml`. Inline-config users should switch to `agents:` (see `examples/embedded-python/06_inline_config.py`).
377
452
 
378
453
  ## Development
379
454
 
@@ -392,4 +467,4 @@ ruff check orchid_cli/
392
467
 
393
468
  ## License
394
469
 
395
- MIT -- see [LICENSE](LICENSE).
470
+ MIT see [LICENSE](LICENSE).
@@ -67,8 +67,7 @@ class StoredToken:
67
67
 
68
68
  def save_token(client_id: str, token: StoredToken) -> None:
69
69
  """Persist a token for the given client_id."""
70
- _ORCHID_DIR.mkdir(parents=True, exist_ok=True)
71
-
70
+ # Directory creation happens in _exclusive_lock() to respect monkeypatching
72
71
  with _exclusive_lock():
73
72
  all_tokens = _read_all()
74
73
  all_tokens[client_id] = asdict(token)
@@ -159,8 +158,10 @@ def _exclusive_lock() -> Iterator[None]:
159
158
  where ``fcntl`` is unavailable; concurrent CLI invocations there
160
159
  accept a small risk of last-writer-wins.
161
160
  """
162
- _ORCHID_DIR.mkdir(parents=True, exist_ok=True)
163
- lock_file = _ORCHID_DIR / _LOCK_FILENAME
161
+ # Read _ORCHID_DIR from globals() so monkeypatching in tests works
162
+ orchid_dir = globals()["_ORCHID_DIR"]
163
+ orchid_dir.mkdir(parents=True, exist_ok=True)
164
+ lock_file = orchid_dir / _LOCK_FILENAME
164
165
  fd = os.open(str(lock_file), os.O_RDWR | os.O_CREAT, 0o600)
165
166
  try:
166
167
  if _HAS_FCNTL:
@@ -36,6 +36,7 @@ from rich.console import Console
36
36
  from orchid_ai.core.repository import Document, OrchidVectorWriter
37
37
  from orchid_ai.documents.chunker import ChunkConfig
38
38
  from orchid_ai.documents.pipeline import ingest_document
39
+ from orchid_ai.documents.strategies import RecursiveIngestion
39
40
  from orchid_ai.rag.scopes import SHARED_TENANT, OrchidRAGScope
40
41
 
41
42
  from .._typer_async import async_command
@@ -143,7 +144,7 @@ async def _index_file(
143
144
  async with cli_context(config_path) as ctx:
144
145
  writer = await _require_writer(ctx)
145
146
  scope_obj = OrchidRAGScope(tenant_id=tenant_id, user_id=user, chat_id="", agent_id="")
146
- chunk_cfg = ChunkConfig(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
147
+ ingestion = RecursiveIngestion(ChunkConfig(chunk_size=chunk_size, chunk_overlap=chunk_overlap))
147
148
 
148
149
  console.print(f"[dim]Reading {file_path}...[/dim]")
149
150
  file_bytes = file_path.read_bytes()
@@ -154,7 +155,7 @@ async def _index_file(
154
155
  scope=scope_obj,
155
156
  namespace=namespace,
156
157
  writer=writer,
157
- chunk_config=chunk_cfg,
158
+ ingestion=ingestion,
158
159
  vision_model=vision_model,
159
160
  )
160
161
 
@@ -233,7 +234,7 @@ async def _index_dir(
233
234
  async with cli_context(config_path) as ctx:
234
235
  writer = await _require_writer(ctx)
235
236
  scope_obj = OrchidRAGScope(tenant_id=tenant_id, user_id=user, chat_id="", agent_id="")
236
- chunk_cfg = ChunkConfig(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
237
+ ingestion = RecursiveIngestion(ChunkConfig(chunk_size=chunk_size, chunk_overlap=chunk_overlap))
237
238
 
238
239
  total_chunks = 0
239
240
  successes = 0
@@ -248,7 +249,7 @@ async def _index_dir(
248
249
  scope=scope_obj,
249
250
  namespace=namespace,
250
251
  writer=writer,
251
- chunk_config=chunk_cfg,
252
+ ingestion=ingestion,
252
253
  vision_model=vision_model,
253
254
  )
254
255
  if count > 0:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "orchid-cli"
3
- version = "1.2.0"
3
+ version = "1.3.0"
4
4
  description = "Orchid CLI — command-line interface for the Orchid agent framework"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -36,7 +36,7 @@ keywords = [
36
36
  "orchestration",
37
37
  ]
38
38
  dependencies = [
39
- "orchid-ai>=1.4.0",
39
+ "orchid-ai>=1.6.0",
40
40
  "typer>=0.12.0",
41
41
  "rich>=13.0",
42
42
  "pyyaml>=6.0",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes