cinna-cli 0.2.2__tar.gz → 0.2.3__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.
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/PKG-INFO +25 -1
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/README.md +24 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/docs/README.md +31 -1
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/pyproject.toml +1 -1
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/account.py +30 -3
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/bootstrap.py +1 -0
- cinna_cli-0.2.3/src/cinna/chat.py +476 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/client.py +166 -22
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/context.py +27 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/main.py +368 -57
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/templates/ACCOUNT_CLAUDE.md.template +14 -1
- cinna_cli-0.2.3/src/cinna/templates/CHAT_TESTING.md +56 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/templates/CLAUDE.md.template +4 -1
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/tests/test_account.py +32 -0
- cinna_cli-0.2.3/tests/test_chat.py +406 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/tests/test_context.py +7 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/uv.lock +1 -1
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/.github/workflows/publish.yml +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/.gitignore +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/LICENSE.md +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/docs/interface.md +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/docs/mutagen_capabilities.md +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/__init__.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/auth.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/config.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/console.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/doctor.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/errors.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/logging.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/mcp_proxy.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/mutagen_runtime.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/sync.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/sync_session.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/sync_ssh_shim.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/sync_tui.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/src/cinna/templates/__init__.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/tests/__init__.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/tests/conftest.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/tests/test_auth.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/tests/test_bootstrap.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/tests/test_client.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/tests/test_config.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/tests/test_doctor.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/tests/test_main.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/tests/test_mutagen_runtime.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/tests/test_sync.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/tests/test_sync_session.py +0 -0
- {cinna_cli-0.2.2 → cinna_cli-0.2.3}/tests/test_sync_ssh_shim.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cinna-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Local development CLI for Cinna Core agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/opencinna/cinna-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/opencinna/cinna-cli
|
|
@@ -255,6 +255,30 @@ cinna api PATCH agents/3fa85f64-5717-4562-b3fc-2c963f66afa6 --json '{"descriptio
|
|
|
255
255
|
cinna api POST tasks --data @task.json
|
|
256
256
|
```
|
|
257
257
|
|
|
258
|
+
### `cinna chat [--agent <ref>] [--resume <session_id>] [--file PATH ...] [MESSAGE...]`
|
|
259
|
+
|
|
260
|
+
Talk to an agent through a **real platform session** — the same conversation pipeline production uses (permission checks, agent-env calls, the model/SDK the platform selects), not a local mock. Built for a local coding agent to test the agent it is building: it can prepare a prompt, attach files, and read the reply back as structured data.
|
|
261
|
+
|
|
262
|
+
Run it from the account workspace (or any synced agent folder under it). The reply is observed by **polling** the backend rather than reading a live stream, so it is robust to streaming/transport quirks.
|
|
263
|
+
|
|
264
|
+
- `--agent <name|slug|id>` picks the agent; omit it inside a synced agent workspace to infer it. `--resume <session_id>` continues an existing conversation instead of opening a new one (default mode for a new session is `conversation`; `--mode building` opens a building session).
|
|
265
|
+
- The message is the positional argument; if omitted it is read from stdin, or you are prompted for it interactively in a TTY.
|
|
266
|
+
- `--file PATH` (repeatable) uploads a local file and attaches it to the message.
|
|
267
|
+
- Output is **NDJSON** by default — one JSON event per line (`session`, `upload`, `message`, `status`, `done`), trivially parseable by another agent. `--pretty` switches to a human-readable transcript.
|
|
268
|
+
- Each `message` carries the agent's reasoning/tool trace under **`events`** — an ordered list of the `thinking` blocks, `tool` calls (with their full `tool_input` payload) and tool results behind the reply, so you see *what the agent did*, not just its final `content`. Pass `--no-events` to drop the trace and keep only the final text.
|
|
269
|
+
- Files the agent attaches to its replies are downloaded under `./cinna-chat-files/<session_id>/` (override with `--download-dir`, or skip with `--no-download` to just report the file ids). Downloads are bounded by the api-proxy's 8 MiB response cap.
|
|
270
|
+
- `--interval` / `--timeout` tune the poll cadence and the maximum wait for a turn. Ctrl-C interrupts the agent's turn and exits.
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
cinna chat --agent crm-agent "Summarize today's leads"
|
|
274
|
+
cinna chat --agent crm-agent --file report.csv "Validate this export"
|
|
275
|
+
cinna chat --resume 3fa85f64-5717-4562-b3fc-2c963f66afa6 "Now break it down by region"
|
|
276
|
+
echo "ping" | cinna chat --agent crm-agent # message from stdin
|
|
277
|
+
cinna chat --agent crm-agent "hi" | jq -c 'select(.event=="message")'
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
The session id is printed in the first `session` event — capture it to drive a multi-turn conversation with `--resume`.
|
|
281
|
+
|
|
258
282
|
### `cinna dev`
|
|
259
283
|
|
|
260
284
|
Start a foreground dev session — creates / resumes the Mutagen sync session for this workspace and attaches the terminal to a two-tab TUI (status + raw Mutagen details). Ctrl-C terminates the session; sync does not outlive the TUI. To observe sync from another terminal without affecting it, use `cinna sync status`.
|
|
@@ -218,6 +218,30 @@ cinna api PATCH agents/3fa85f64-5717-4562-b3fc-2c963f66afa6 --json '{"descriptio
|
|
|
218
218
|
cinna api POST tasks --data @task.json
|
|
219
219
|
```
|
|
220
220
|
|
|
221
|
+
### `cinna chat [--agent <ref>] [--resume <session_id>] [--file PATH ...] [MESSAGE...]`
|
|
222
|
+
|
|
223
|
+
Talk to an agent through a **real platform session** — the same conversation pipeline production uses (permission checks, agent-env calls, the model/SDK the platform selects), not a local mock. Built for a local coding agent to test the agent it is building: it can prepare a prompt, attach files, and read the reply back as structured data.
|
|
224
|
+
|
|
225
|
+
Run it from the account workspace (or any synced agent folder under it). The reply is observed by **polling** the backend rather than reading a live stream, so it is robust to streaming/transport quirks.
|
|
226
|
+
|
|
227
|
+
- `--agent <name|slug|id>` picks the agent; omit it inside a synced agent workspace to infer it. `--resume <session_id>` continues an existing conversation instead of opening a new one (default mode for a new session is `conversation`; `--mode building` opens a building session).
|
|
228
|
+
- The message is the positional argument; if omitted it is read from stdin, or you are prompted for it interactively in a TTY.
|
|
229
|
+
- `--file PATH` (repeatable) uploads a local file and attaches it to the message.
|
|
230
|
+
- Output is **NDJSON** by default — one JSON event per line (`session`, `upload`, `message`, `status`, `done`), trivially parseable by another agent. `--pretty` switches to a human-readable transcript.
|
|
231
|
+
- Each `message` carries the agent's reasoning/tool trace under **`events`** — an ordered list of the `thinking` blocks, `tool` calls (with their full `tool_input` payload) and tool results behind the reply, so you see *what the agent did*, not just its final `content`. Pass `--no-events` to drop the trace and keep only the final text.
|
|
232
|
+
- Files the agent attaches to its replies are downloaded under `./cinna-chat-files/<session_id>/` (override with `--download-dir`, or skip with `--no-download` to just report the file ids). Downloads are bounded by the api-proxy's 8 MiB response cap.
|
|
233
|
+
- `--interval` / `--timeout` tune the poll cadence and the maximum wait for a turn. Ctrl-C interrupts the agent's turn and exits.
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
cinna chat --agent crm-agent "Summarize today's leads"
|
|
237
|
+
cinna chat --agent crm-agent --file report.csv "Validate this export"
|
|
238
|
+
cinna chat --resume 3fa85f64-5717-4562-b3fc-2c963f66afa6 "Now break it down by region"
|
|
239
|
+
echo "ping" | cinna chat --agent crm-agent # message from stdin
|
|
240
|
+
cinna chat --agent crm-agent "hi" | jq -c 'select(.event=="message")'
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
The session id is printed in the first `session` event — capture it to drive a multi-turn conversation with `--resume`.
|
|
244
|
+
|
|
221
245
|
### `cinna dev`
|
|
222
246
|
|
|
223
247
|
Start a foreground dev session — creates / resumes the Mutagen sync session for this workspace and attaches the terminal to a two-tab TUI (status + raw Mutagen details). Ctrl-C terminates the session; sync does not outlive the TUI. To observe sync from another terminal without affecting it, use `cinna sync status`.
|
|
@@ -199,6 +199,7 @@ main.py (CLI commands — Click)
|
|
|
199
199
|
├── bootstrap.py — setup orchestration
|
|
200
200
|
├── account.py — account workspace; `cinna login` (device auth), `cinna account`, `cinna agent`
|
|
201
201
|
├── doctor.py — `cinna doctor`: reconcile registry ↔ Mutagen, repair stale state, refresh tokens
|
|
202
|
+
├── chat.py — `cinna chat`: session-backed conversation testing (poll + NDJSON) over the api-proxy
|
|
202
203
|
├── config.py — .cinna/config.json: load/save/find
|
|
203
204
|
├── auth.py — JWT storage, Authorization headers
|
|
204
205
|
├── client.py — PlatformClient: HTTP + SSE stream_exec
|
|
@@ -356,6 +357,31 @@ To run an actual remote shell snippet (pipes, redirects, `&&`), pass it explicit
|
|
|
356
357
|
|
|
357
358
|
---
|
|
358
359
|
|
|
360
|
+
## Remote Chat (`cinna chat`)
|
|
361
|
+
|
|
362
|
+
`cinna chat` lets a local coding agent **test the agent it is building** by driving a real platform conversation session — exercising the production path (permission checks, agent-env calls, the model/SDK the platform selects) rather than a local mock. It lives in `chat.py` and runs entirely through the **account workspace's api-proxy** (`AccountClient`), so it needs an account workspace (`.cinna/account.json`) — found by walking up from the cwd, exactly like the other account verbs, so it works from a synced `agents/<slug>/` folder too.
|
|
363
|
+
|
|
364
|
+
### Why polling, not streaming
|
|
365
|
+
|
|
366
|
+
The platform's send-message route (`POST /sessions/{id}/messages/stream`) returns a **JSON ack immediately** and runs the agent turn asynchronously; the live events go out over a Socket.IO room *and* are persisted onto each message's `message_metadata.streaming_events`. The api-proxy is a buffered JSON hatch (it rejects `text/event-stream`), so `cinna chat` never reads the stream. Instead it:
|
|
367
|
+
|
|
368
|
+
1. Creates the session (`POST /sessions/`, mode `conversation` by default) — or resumes the one passed to `--resume`.
|
|
369
|
+
2. Uploads each `--file` and collects the returned file ids.
|
|
370
|
+
3. Records the current message count as a cursor, then sends the message (`file_ids` carry the attachments).
|
|
371
|
+
4. **Polls** `GET /sessions/{id}/messages?offset=<cursor>` (messages are ordered ascending by `sequence_number`, so `offset` is the cursor) and `GET …/messages/streaming-status` (`{is_streaming}`) until the turn settles — `is_streaming` false with no message flagged `streaming_in_progress`. A start-grace window covers env wake / queueing before the turn begins; an overall `--timeout` bounds the wait.
|
|
372
|
+
|
|
373
|
+
Each finalized message is emitted as one NDJSON line (`session` / `upload` / `message` / `status` / `done`); the in-progress assistant message is held back (its content is still growing) and emitted once final. Every `message` also carries the agent's reasoning/tool trace under **`events`** — the normalized `streaming_events` (thinking blocks, `tool` calls with their full `tool_input` payloads, tool results), with the bookkeeping/`attachment` entries stripped (attachments are surfaced separately). The final coalesced text stays in `content`; the trace shows *how* the agent got there. `--no-events` drops the trace; `--pretty` swaps NDJSON for a Rich transcript. Ctrl-C calls `POST …/messages/interrupt` and exits 130.
|
|
374
|
+
|
|
375
|
+
### Attachments
|
|
376
|
+
|
|
377
|
+
Agents attach workspace files to replies via `<cinna_attach>` tags; the backend materializes them and both injects an `attachment` streaming event (`metadata.file_id` / `filename` / `mime_type` / `size`) and lists them under the message's `files[]` with `source == "agent_attachment"`. `chat.py` collects attachments from the streaming events (preferred) with the `files[]` list as a replay fallback, dedups by file id, and downloads each via the proxy (`GET /files/{id}/download`) into `./cinna-chat-files/<session_id>/`. Because the proxy buffers the response, downloads are bounded by its **8 MiB** response cap; larger files surface a clear `PlatformError` instead of a partial write.
|
|
378
|
+
|
|
379
|
+
### File upload — the one dedicated route
|
|
380
|
+
|
|
381
|
+
The api-proxy is JSON-only and cannot carry a multipart body, and neither the account token nor a per-agent token may call `/files/upload` directly. So uploading a local attachment uses a dedicated account-CLI route, **`POST /api/v1/cli/account/files/upload`** (multipart, account-token auth), added alongside the other `/cli/account/*` routes; it creates a `File` owned by the account user and returns `FileUploadPublic` whose `id` goes into the message's `file_ids`. This is the only part of `cinna chat` that does not ride the api-proxy.
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
359
385
|
## Bootstrap Flow
|
|
360
386
|
|
|
361
387
|
```
|
|
@@ -439,8 +465,12 @@ When a CLI token expires (or is revoked) the normal remedy is `cinna set-token <
|
|
|
439
465
|
| POST | `/api/v1/cli/account/login/start` | None | Begin a `cinna login` device-authorization request |
|
|
440
466
|
| POST | `/api/v1/cli/account/login/poll` | None | Poll a `cinna login` request — always HTTP 200 + `status` |
|
|
441
467
|
| POST | `/api/v1/cli/account/agents/{id}/mint` | Account token | Mint a per-agent CLI token (`cinna agent sync`, `cinna doctor` re-mint) |
|
|
468
|
+
| POST | `/api/v1/cli/account/api-proxy` | Account token | Buffered JSON escape hatch — `cinna api`, and the transport for every `cinna chat` session/message call |
|
|
469
|
+
| POST | `/api/v1/cli/account/files/upload` | Account token | Multipart upload for `cinna chat --file` (the proxy can't carry multipart) |
|
|
470
|
+
|
|
471
|
+
`cinna chat` reaches the conversation API **through** the api-proxy (so these are inner routes, not CLI routes): `POST /sessions/`, `GET /sessions/{id}`, `GET /sessions/{id}/messages`, `POST /sessions/{id}/messages/stream`, `GET /sessions/{id}/messages/streaming-status`, `POST /sessions/{id}/messages/interrupt`, and `GET /files/{id}/download`.
|
|
442
472
|
|
|
443
|
-
The account-workspace surface adds the broader `/api/v1/cli/account/*` route group (login, agents, credentials, connect, schedules, status, api-proxy); only the routes the sync / login / doctor paths use are listed here.
|
|
473
|
+
The account-workspace surface adds the broader `/api/v1/cli/account/*` route group (login, agents, credentials, connect, schedules, status, api-proxy, files/upload); only the routes the sync / login / doctor / chat paths use are listed here.
|
|
444
474
|
|
|
445
475
|
Endpoints that were part of the old Docker-replica model (`build-context`, `workspace` POST, `workspace/manifest`, `credentials`) have been removed from the backend and from this CLI.
|
|
446
476
|
|
|
@@ -732,6 +732,12 @@ def _write_account_claude_md(account_root: Path, config: AccountConfig) -> None:
|
|
|
732
732
|
)
|
|
733
733
|
(account_root / "CLAUDE.md").write_text(claude_md)
|
|
734
734
|
|
|
735
|
+
# Companion testing guide (loaded by the orchestrator only when it tests an
|
|
736
|
+
# agent via `cinna chat`). Bundled + overwritten, like the orchestrator guide.
|
|
737
|
+
from cinna.context import write_chat_testing_guide
|
|
738
|
+
|
|
739
|
+
write_chat_testing_guide(account_root)
|
|
740
|
+
|
|
735
741
|
|
|
736
742
|
def _install_context_package(
|
|
737
743
|
account_cfg: AccountConfig, account_root: Path, *, replace: bool = False
|
|
@@ -864,9 +870,10 @@ def run_account_refresh_context() -> None:
|
|
|
864
870
|
|
|
865
871
|
The old context tree is only removed after a successful download, so a
|
|
866
872
|
failed refresh leaves the existing context intact (warn-don't-die). The
|
|
867
|
-
orchestrator `CLAUDE.md` is re-rendered from the bundled template too
|
|
868
|
-
|
|
869
|
-
|
|
873
|
+
orchestrator `CLAUDE.md` is re-rendered from the bundled template too — and
|
|
874
|
+
so is every synced agent's per-agent `CLAUDE.md` under `agents/<slug>/` — so
|
|
875
|
+
a CLI upgrade's new commands / guidance reach existing account workspaces
|
|
876
|
+
(orchestrator and child agents alike) without a full re-setup.
|
|
870
877
|
"""
|
|
871
878
|
account_root = find_account_root()
|
|
872
879
|
account_cfg = load_account_config(account_root)
|
|
@@ -888,6 +895,26 @@ def run_account_refresh_context() -> None:
|
|
|
888
895
|
# account workspaces (auto-generated infra — safe to overwrite).
|
|
889
896
|
_write_account_mcp_config(account_root)
|
|
890
897
|
|
|
898
|
+
# Re-render the per-agent CLAUDE.md for every synced child workspace from
|
|
899
|
+
# the same bundled template — offline (the local-dev guide is a pure
|
|
900
|
+
# function of the template + the agent's config), so one bad workspace never
|
|
901
|
+
# aborts the rest. BUILDING_AGENT.md is left untouched (it mirrors the
|
|
902
|
+
# platform's building prompt and is refreshed on sync, not from a template).
|
|
903
|
+
from cinna.context import regenerate_claude_md
|
|
904
|
+
|
|
905
|
+
refreshed = 0
|
|
906
|
+
for child, child_cfg in list_child_workspaces(account_root):
|
|
907
|
+
try:
|
|
908
|
+
regenerate_claude_md(child_cfg, child)
|
|
909
|
+
refreshed += 1
|
|
910
|
+
except Exception as exc: # one unreadable/locked workspace mustn't abort
|
|
911
|
+
console.warn(f"Could not regenerate CLAUDE.md for {child.name}: {exc}")
|
|
912
|
+
if refreshed:
|
|
913
|
+
console.status(
|
|
914
|
+
f"Regenerated CLAUDE.md for {refreshed} synced agent "
|
|
915
|
+
f"workspace{'s' if refreshed != 1 else ''}"
|
|
916
|
+
)
|
|
917
|
+
|
|
891
918
|
|
|
892
919
|
def run_account_agents(show_all: bool = False) -> None:
|
|
893
920
|
"""List accessible agents — called by `cinna account agents`.
|