honcho-cli 0.1.2__tar.gz → 0.1.4__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.
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/.gitignore +3 -3
- honcho_cli-0.1.4/CHANGELOG.md +58 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/PKG-INFO +39 -7
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/README.md +37 -5
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/pyproject.toml +5 -1
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/scripts/generate_cli_docs.py +1 -1
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/src/honcho_cli/__init__.py +1 -1
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/src/honcho_cli/_help.py +18 -8
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/src/honcho_cli/commands/message.py +7 -4
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/src/honcho_cli/commands/session.py +247 -6
- honcho_cli-0.1.4/src/honcho_cli/commands/stack.py +400 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/src/honcho_cli/common.py +9 -0
- honcho_cli-0.1.4/src/honcho_cli/local/__init__.py +12 -0
- honcho_cli-0.1.4/src/honcho_cli/local/docker.py +436 -0
- honcho_cli-0.1.4/src/honcho_cli/local/env.py +183 -0
- honcho_cli-0.1.4/src/honcho_cli/local/health.py +53 -0
- honcho_cli-0.1.4/src/honcho_cli/local/profile.py +157 -0
- honcho_cli-0.1.4/src/honcho_cli/local/setup.py +531 -0
- honcho_cli-0.1.4/src/honcho_cli/local/templates/__init__.py +1 -0
- honcho_cli-0.1.4/src/honcho_cli/local/templates/docker-compose.yml +100 -0
- honcho_cli-0.1.4/src/honcho_cli/local/templates/init.sql +1 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/src/honcho_cli/main.py +6 -0
- honcho_cli-0.1.4/src/honcho_cli/output.py +235 -0
- honcho_cli-0.1.4/src/honcho_cli/update_check.py +67 -0
- honcho_cli-0.1.4/tests/test_commands.py +480 -0
- honcho_cli-0.1.4/tests/test_local.py +127 -0
- honcho_cli-0.1.4/tests/test_output.py +139 -0
- honcho_cli-0.1.4/tests/test_setup.py +94 -0
- honcho_cli-0.1.4/tests/test_start.py +159 -0
- honcho_cli-0.1.2/CHANGELOG.md +0 -29
- honcho_cli-0.1.2/src/honcho_cli/output.py +0 -104
- honcho_cli-0.1.2/tests/test_commands.py +0 -195
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/src/honcho_cli/branding.py +0 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/src/honcho_cli/commands/__init__.py +0 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/src/honcho_cli/commands/conclusion.py +0 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/src/honcho_cli/commands/config_cmd.py +0 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/src/honcho_cli/commands/peer.py +0 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/src/honcho_cli/commands/setup.py +0 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/src/honcho_cli/commands/workspace.py +0 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/src/honcho_cli/config.py +0 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/src/honcho_cli/oauth.py +0 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/src/honcho_cli/validation.py +0 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/tests/__init__.py +0 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/tests/conftest.py +0 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/tests/test_common.py +0 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/tests/test_config.py +0 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/tests/test_oauth.py +0 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/tests/test_validation.py +0 -0
- {honcho_cli-0.1.2 → honcho_cli-0.1.4}/uv.lock +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
|
6
|
+
and this project adheres to [Semantic Versioning](http://semver.org/).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.4] - 2026-08-26
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- A TTY notice when a newer `honcho-cli` is on PyPI (`uv tool upgrade honcho-cli`). Skipped in JSON mode; disable with `HONCHO_NO_UPDATE_CHECK`
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- `--setup` for openai-compatible writes `EMBEDDING_MODEL_CONFIG__OVERRIDES__BASE_URL` into the profile `.env` alongside `LLM_OPENAI_BASE_URL` (#1068)
|
|
19
|
+
- `--setup` API key prompts echo `*` per character so a paste is visibly received instead of a blank getpass field
|
|
20
|
+
|
|
21
|
+
## [0.1.3] - 2026-08-25
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- `honcho start`, `honcho stop`, and `honcho status` — run a personal Honcho stack in Docker (API, deriver, Postgres, Redis). Profiles live under `~/.honcho/profiles/`. First start pins `ghcr.io/plastic-labs/honcho:latest` by digest and copies the image `config.toml`. Optional `--setup basic` / `--setup advanced` wizard writes LLM overrides to `.env` (#1029)
|
|
26
|
+
- `honcho session view` — session transcript table (`--last N`, `--page N --size M`, `--all`, `--reverse`, `--ids`, peer filter via `-p`). Content is shown verbatim, timestamps are normalized to UTC, and the command is read-only: unlike the other session commands it never get-or-creates the session (#1006)
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- `honcho message list --last N` no longer stops at the first page of 50 — it walks pages to fill the requested window (#1006)
|
|
31
|
+
|
|
32
|
+
## [0.1.2] - 2026-07-20
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- Device-code OAuth login for managed Honcho servers. `honcho init` now offers browser-based login (RFC 8628 device authorization grant) when the host advertises the device grant in its OAuth authorization-server metadata; tokens are persisted to `~/.honcho/config.json` and auto-refreshed (#891)
|
|
37
|
+
- `HONCHO_CONFIG_DIR` environment variable for pointing the CLI at an alternate config directory (#891)
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- An OAuth grant now records the host it was minted against and is ignored — neither used nor refreshed — when `base_url` points elsewhere, so a staging grant is never sent to production. A live OAuth token takes precedence over a stored `apiKey`, and a dead grant degrades to the saved key with a warning instead of aborting. Device login no longer deletes the shared `apiKey`, which sibling tools read from the same config file (#891)
|
|
42
|
+
|
|
43
|
+
## [0.1.1] - 2026-06-15
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
|
|
47
|
+
- Declare `click` as an explicit dependency. The CLI imported `click` directly but relied on it being pulled in transitively, so installs without it on the path could fail at runtime (#787)
|
|
48
|
+
|
|
49
|
+
## [0.1.0] - 2026-04-20
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
|
|
53
|
+
- Initial release of `honcho-cli` — a terminal for inspecting and managing a Honcho deployment (#424)
|
|
54
|
+
- `workspace`, `peer`, `session`, `message`, `conclusion`, and `config` command groups for managing resources against any Honcho server
|
|
55
|
+
- `init` onboarding flow that prompts for and persists connection settings, with flag/env-var pre-seeding for non-interactive use
|
|
56
|
+
- Per-command flags, environment variables, and a config file for pointing the CLI at different servers (local, self-hosted, or hosted)
|
|
57
|
+
- Rich terminal output and an agent-usage mode for scripting against the CLI
|
|
58
|
+
- Documentation and an agent skill for the CLI (#589)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: honcho-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: A terminal for Honcho — memory that reasons.
|
|
5
5
|
Project-URL: Homepage, https://github.com/plastic-labs/honcho
|
|
6
6
|
Project-URL: Repository, https://github.com/plastic-labs/honcho
|
|
@@ -47,22 +47,50 @@ uv tool install honcho-cli
|
|
|
47
47
|
## Quick Start
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
|
-
honcho init
|
|
51
|
-
honcho
|
|
52
|
-
honcho
|
|
50
|
+
honcho init # confirm/set apiKey + Honcho URL in ~/.honcho/config.json
|
|
51
|
+
honcho start --setup basic # local stack: LLM provider key + Docker
|
|
52
|
+
honcho doctor # verify config + connectivity
|
|
53
|
+
honcho # show banner + command list
|
|
53
54
|
```
|
|
54
55
|
|
|
55
|
-
`honcho init`
|
|
56
|
+
`honcho init` writes `apiKey` and `environmentUrl` to the top-level of `~/.honcho/config.json` (the same file other Honcho tools — plugins, host integrations — share) so the CLI can call a Honcho server. If both are present, it confirms them with you; if either is missing (or you decline), it prompts and writes them back. Host-specific entries under `hosts` are left untouched. It does **not** set the LLM provider key the local deriver needs — that is `honcho start --setup` (or `LLM_*_API_KEY` in the environment).
|
|
56
57
|
|
|
57
58
|
Per-command scoping (workspace / peer / session) is handled via `-w` / `-p` / `-s` flags or `HONCHO_*` env vars — not persisted as CLI defaults.
|
|
58
59
|
|
|
60
|
+
### Local stack
|
|
61
|
+
|
|
62
|
+
`honcho start --setup basic` runs a personal Honcho server on your machine (API, deriver, Postgres, Redis) via Docker. The wizard writes the LLM provider key into the profile `.env` — `honcho init` cannot do this; its `apiKey` is for calling a Honcho server, not for deriver/dialectic inference. You can also pass `LLM_OPENAI_API_KEY`, `LLM_ANTHROPIC_API_KEY`, or `LLM_GEMINI_API_KEY` in the environment and skip `--setup`. Stack files live under `~/.honcho/profiles/local/` and are not committed to a project.
|
|
63
|
+
|
|
64
|
+
On first start, the CLI pulls `ghcr.io/plastic-labs/honcho:latest` and **pins that digest** in `profile.json`, then copies the image's `config.toml.example` to `config.toml` in the same directory. `honcho start` never overwrites `config.toml` after that — including when you re-pin the image. Delete the file yourself if you want a fresh copy from a new image.
|
|
65
|
+
|
|
66
|
+
Pass `--setup basic` or `--setup advanced` for an interactive wizard that writes curated LLM/feature overrides into the profile `.env` (env wins over `config.toml`). TTY only; re-runnable. `basic` asks provider + chat model; `advanced` also covers embeddings, deriver/dialectic models, dreams, and snappy deriver flush. Everything else stays in `config.toml`.
|
|
67
|
+
|
|
68
|
+
`honcho start` does **not** change `environmentUrl` in `~/.honcho/config.json` (that file is shared with plugins). To talk to the local stack for one command:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
HONCHO_BASE_URL=http://127.0.0.1:8000 honcho workspace list
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
To make local the default, run `honcho init --base-url http://127.0.0.1:8000`.
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
honcho start --setup basic
|
|
78
|
+
honcho start --setup advanced
|
|
79
|
+
honcho status
|
|
80
|
+
honcho stop # keep data
|
|
81
|
+
honcho stop --wipe # also delete volumes
|
|
82
|
+
```
|
|
83
|
+
|
|
59
84
|
## Commands
|
|
60
85
|
|
|
61
86
|
### Onboarding
|
|
62
87
|
|
|
63
88
|
| Command | Description |
|
|
64
89
|
|---------|-------------|
|
|
65
|
-
| `honcho init` | Confirm/set `apiKey` + `environmentUrl` in `~/.honcho/config.json
|
|
90
|
+
| `honcho init` | Confirm/set `apiKey` + `environmentUrl` in `~/.honcho/config.json`. |
|
|
91
|
+
| `honcho start` | Start a local Honcho stack (API, deriver, Postgres, Redis). Requires Docker and a cloud LLM key. `--setup basic` / `--setup advanced` runs an interactive config wizard (TTY only). Does not change `environmentUrl`. |
|
|
92
|
+
| `honcho stop` | Stop the local stack. `--wipe` also deletes volumes. |
|
|
93
|
+
| `honcho status` | Show every local stack (or `--profile` for one). |
|
|
66
94
|
| `honcho doctor` | Health check: config, connectivity, workspace, peer, queue |
|
|
67
95
|
|
|
68
96
|
### Workspaces
|
|
@@ -96,6 +124,7 @@ Per-command scoping (workspace / peer / session) is handled via `-w` / `-p` / `-
|
|
|
96
124
|
| `honcho session list` | List sessions in the workspace (filter with `--peer/-p`) |
|
|
97
125
|
| `honcho session create <id>` | Create or get a session (optionally `--peers` to add peers, `--metadata`) |
|
|
98
126
|
| `honcho session inspect <id>` | Peers, message count, summaries, config |
|
|
127
|
+
| `honcho session view <id>` | Transcript table (`--last N`, `--page N --size M`, `--all`, `--reverse`, `--ids`, `-p`) |
|
|
99
128
|
| `honcho session context <id>` | What an agent would see |
|
|
100
129
|
| `honcho session summaries <id>` | Short + long summaries |
|
|
101
130
|
| `honcho session peers <id>` / `add-peers` / `remove-peers` | Peer management |
|
|
@@ -181,6 +210,9 @@ Precedence (highest first): **flag → env var → config file → default**.
|
|
|
181
210
|
| `HONCHO_PEER_ID` | `-p` / `--peer` | Peer scope |
|
|
182
211
|
| `HONCHO_SESSION_ID` | `-s` / `--session` | Session scope |
|
|
183
212
|
| `HONCHO_JSON` | `--json` | Force JSON output (`1` / `true`) |
|
|
213
|
+
| `HONCHO_NO_UPDATE_CHECK` | — | Disable the once-a-day upgrade notice (`1` / `true`) |
|
|
214
|
+
| `HONCHO_PROFILE` | `--profile` (start/stop/status) | Local stack profile (default: `local`) |
|
|
215
|
+
| `LLM_OPENAI_API_KEY` | — | Provider key for `honcho start` (also `LLM_ANTHROPIC_API_KEY`, `LLM_GEMINI_API_KEY`) |
|
|
184
216
|
|
|
185
217
|
```bash
|
|
186
218
|
# Per-command flags
|
|
@@ -22,22 +22,50 @@ uv tool install honcho-cli
|
|
|
22
22
|
## Quick Start
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
honcho init
|
|
26
|
-
honcho
|
|
27
|
-
honcho
|
|
25
|
+
honcho init # confirm/set apiKey + Honcho URL in ~/.honcho/config.json
|
|
26
|
+
honcho start --setup basic # local stack: LLM provider key + Docker
|
|
27
|
+
honcho doctor # verify config + connectivity
|
|
28
|
+
honcho # show banner + command list
|
|
28
29
|
```
|
|
29
30
|
|
|
30
|
-
`honcho init`
|
|
31
|
+
`honcho init` writes `apiKey` and `environmentUrl` to the top-level of `~/.honcho/config.json` (the same file other Honcho tools — plugins, host integrations — share) so the CLI can call a Honcho server. If both are present, it confirms them with you; if either is missing (or you decline), it prompts and writes them back. Host-specific entries under `hosts` are left untouched. It does **not** set the LLM provider key the local deriver needs — that is `honcho start --setup` (or `LLM_*_API_KEY` in the environment).
|
|
31
32
|
|
|
32
33
|
Per-command scoping (workspace / peer / session) is handled via `-w` / `-p` / `-s` flags or `HONCHO_*` env vars — not persisted as CLI defaults.
|
|
33
34
|
|
|
35
|
+
### Local stack
|
|
36
|
+
|
|
37
|
+
`honcho start --setup basic` runs a personal Honcho server on your machine (API, deriver, Postgres, Redis) via Docker. The wizard writes the LLM provider key into the profile `.env` — `honcho init` cannot do this; its `apiKey` is for calling a Honcho server, not for deriver/dialectic inference. You can also pass `LLM_OPENAI_API_KEY`, `LLM_ANTHROPIC_API_KEY`, or `LLM_GEMINI_API_KEY` in the environment and skip `--setup`. Stack files live under `~/.honcho/profiles/local/` and are not committed to a project.
|
|
38
|
+
|
|
39
|
+
On first start, the CLI pulls `ghcr.io/plastic-labs/honcho:latest` and **pins that digest** in `profile.json`, then copies the image's `config.toml.example` to `config.toml` in the same directory. `honcho start` never overwrites `config.toml` after that — including when you re-pin the image. Delete the file yourself if you want a fresh copy from a new image.
|
|
40
|
+
|
|
41
|
+
Pass `--setup basic` or `--setup advanced` for an interactive wizard that writes curated LLM/feature overrides into the profile `.env` (env wins over `config.toml`). TTY only; re-runnable. `basic` asks provider + chat model; `advanced` also covers embeddings, deriver/dialectic models, dreams, and snappy deriver flush. Everything else stays in `config.toml`.
|
|
42
|
+
|
|
43
|
+
`honcho start` does **not** change `environmentUrl` in `~/.honcho/config.json` (that file is shared with plugins). To talk to the local stack for one command:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
HONCHO_BASE_URL=http://127.0.0.1:8000 honcho workspace list
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
To make local the default, run `honcho init --base-url http://127.0.0.1:8000`.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
honcho start --setup basic
|
|
53
|
+
honcho start --setup advanced
|
|
54
|
+
honcho status
|
|
55
|
+
honcho stop # keep data
|
|
56
|
+
honcho stop --wipe # also delete volumes
|
|
57
|
+
```
|
|
58
|
+
|
|
34
59
|
## Commands
|
|
35
60
|
|
|
36
61
|
### Onboarding
|
|
37
62
|
|
|
38
63
|
| Command | Description |
|
|
39
64
|
|---------|-------------|
|
|
40
|
-
| `honcho init` | Confirm/set `apiKey` + `environmentUrl` in `~/.honcho/config.json
|
|
65
|
+
| `honcho init` | Confirm/set `apiKey` + `environmentUrl` in `~/.honcho/config.json`. |
|
|
66
|
+
| `honcho start` | Start a local Honcho stack (API, deriver, Postgres, Redis). Requires Docker and a cloud LLM key. `--setup basic` / `--setup advanced` runs an interactive config wizard (TTY only). Does not change `environmentUrl`. |
|
|
67
|
+
| `honcho stop` | Stop the local stack. `--wipe` also deletes volumes. |
|
|
68
|
+
| `honcho status` | Show every local stack (or `--profile` for one). |
|
|
41
69
|
| `honcho doctor` | Health check: config, connectivity, workspace, peer, queue |
|
|
42
70
|
|
|
43
71
|
### Workspaces
|
|
@@ -71,6 +99,7 @@ Per-command scoping (workspace / peer / session) is handled via `-w` / `-p` / `-
|
|
|
71
99
|
| `honcho session list` | List sessions in the workspace (filter with `--peer/-p`) |
|
|
72
100
|
| `honcho session create <id>` | Create or get a session (optionally `--peers` to add peers, `--metadata`) |
|
|
73
101
|
| `honcho session inspect <id>` | Peers, message count, summaries, config |
|
|
102
|
+
| `honcho session view <id>` | Transcript table (`--last N`, `--page N --size M`, `--all`, `--reverse`, `--ids`, `-p`) |
|
|
74
103
|
| `honcho session context <id>` | What an agent would see |
|
|
75
104
|
| `honcho session summaries <id>` | Short + long summaries |
|
|
76
105
|
| `honcho session peers <id>` / `add-peers` / `remove-peers` | Peer management |
|
|
@@ -156,6 +185,9 @@ Precedence (highest first): **flag → env var → config file → default**.
|
|
|
156
185
|
| `HONCHO_PEER_ID` | `-p` / `--peer` | Peer scope |
|
|
157
186
|
| `HONCHO_SESSION_ID` | `-s` / `--session` | Session scope |
|
|
158
187
|
| `HONCHO_JSON` | `--json` | Force JSON output (`1` / `true`) |
|
|
188
|
+
| `HONCHO_NO_UPDATE_CHECK` | — | Disable the once-a-day upgrade notice (`1` / `true`) |
|
|
189
|
+
| `HONCHO_PROFILE` | `--profile` (start/stop/status) | Local stack profile (default: `local`) |
|
|
190
|
+
| `LLM_OPENAI_API_KEY` | — | Provider key for `honcho start` (also `LLM_ANTHROPIC_API_KEY`, `LLM_GEMINI_API_KEY`) |
|
|
159
191
|
|
|
160
192
|
```bash
|
|
161
193
|
# Per-command flags
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "honcho-cli"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.4"
|
|
4
4
|
description = "A terminal for Honcho — memory that reasons."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -38,6 +38,10 @@ build-backend = "hatchling.build"
|
|
|
38
38
|
[tool.hatch.build.targets.wheel]
|
|
39
39
|
packages = ["src/honcho_cli"]
|
|
40
40
|
|
|
41
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
42
|
+
"src/honcho_cli/local/templates/docker-compose.yml" = "honcho_cli/local/templates/docker-compose.yml"
|
|
43
|
+
"src/honcho_cli/local/templates/init.sql" = "honcho_cli/local/templates/init.sql"
|
|
44
|
+
|
|
41
45
|
[tool.pytest.ini_options]
|
|
42
46
|
testpaths = ["tests"]
|
|
43
47
|
|
|
@@ -230,7 +230,7 @@ def build() -> str:
|
|
|
230
230
|
body: list[str] = []
|
|
231
231
|
for name in sorted(root.commands):
|
|
232
232
|
body.extend(_render_top(root.commands[name], ["honcho", name]))
|
|
233
|
-
return HEADER + "\n".join(body) + "\n"
|
|
233
|
+
return HEADER + "\n".join(body).rstrip("\n") + "\n"
|
|
234
234
|
|
|
235
235
|
|
|
236
236
|
def main() -> int:
|
|
@@ -24,6 +24,7 @@ from typer.core import TyperGroup
|
|
|
24
24
|
from honcho_cli import __version__
|
|
25
25
|
from honcho_cli.branding import BANNER, BRAND
|
|
26
26
|
from honcho_cli.output import use_json
|
|
27
|
+
from honcho_cli.update_check import maybe_print_update_nag
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
# Theme Typer's rich help renderer. Module-level side effect limited to
|
|
@@ -59,15 +60,21 @@ def _welcome_panel(title: str, rows: list[tuple[str, str]]) -> Panel:
|
|
|
59
60
|
|
|
60
61
|
|
|
61
62
|
def print_welcome(console: Console) -> None:
|
|
62
|
-
"""Render the curated
|
|
63
|
+
"""Render the curated welcome (banner + getting started / local stack / commands / memory)."""
|
|
63
64
|
if use_json():
|
|
64
65
|
return
|
|
65
66
|
console.print(f"[bold {BRAND}]{BANNER}[/bold {BRAND}]")
|
|
66
67
|
console.print(f" [dim]v{__version__}[/dim]\n", highlight=False)
|
|
67
68
|
|
|
68
69
|
start_rows = [
|
|
69
|
-
("honcho init",
|
|
70
|
-
("honcho
|
|
70
|
+
("honcho init", "configure API key and server URL"),
|
|
71
|
+
("honcho start [--setup basic]", "run a local Honcho stack (Docker)"),
|
|
72
|
+
("honcho doctor", "verify connection and workspace health"),
|
|
73
|
+
]
|
|
74
|
+
stack_rows = [
|
|
75
|
+
("honcho start / status / stop", "lifecycle for the local Docker stack"),
|
|
76
|
+
("honcho start --setup basic", "interactive LLM + feature wizard"),
|
|
77
|
+
("HONCHO_BASE_URL=http://127.0.0.1:8000", "prefix any command — CLI stays on api.honcho.dev until you set this"),
|
|
71
78
|
]
|
|
72
79
|
cmd_rows = [
|
|
73
80
|
("[dim]pattern[/dim]", r"[dim]honcho <command> \[args] \[-w workspace] \[-p peer] \[-s session][/dim]"),
|
|
@@ -76,7 +83,7 @@ def print_welcome(console: Console) -> None:
|
|
|
76
83
|
("workspace", "list · create · search · delete · inspect · queue-status"),
|
|
77
84
|
("peer", "list · create · search · inspect · card · chat"),
|
|
78
85
|
("", "get-metadata · set-metadata · representation"),
|
|
79
|
-
("session", "list · create · search · delete · inspect · add-peers"),
|
|
86
|
+
("session", "list · create · search · delete · inspect · view · add-peers"),
|
|
80
87
|
("", "context · get-metadata · set-metadata · peers"),
|
|
81
88
|
("", "remove-peers · representation · summaries"),
|
|
82
89
|
("message", "list · create · get"),
|
|
@@ -84,14 +91,15 @@ def print_welcome(console: Console) -> None:
|
|
|
84
91
|
("config", "inspect current configuration"),
|
|
85
92
|
]
|
|
86
93
|
memory_rows = [
|
|
87
|
-
("honcho peer chat \"...\" -p <peer> -w <workspace>","query the Dialectic about a peer"),
|
|
88
|
-
("honcho peer inspect -p <peer> -w <workspace>","dashboard: peer card + recent conclusions + configuration"),
|
|
94
|
+
("honcho peer chat \"...\" -p <peer> -w <workspace>", "query the Dialectic about a peer"),
|
|
95
|
+
("honcho peer inspect -p <peer> -w <workspace>", "dashboard: peer card + recent conclusions + configuration"),
|
|
89
96
|
("honcho peer representation -p <peer> -w <workspace>", "global peer representation"),
|
|
90
97
|
("honcho peer representation -p <peer> -w <workspace> -s <session>", "session-scoped peer representation"),
|
|
91
98
|
("honcho peer card -p <peer> -w <workspace>", "synthesized identity: traits, preferences, instructions"),
|
|
92
|
-
("honcho conclusion list -p <peer> -w <workspace>",
|
|
99
|
+
("honcho conclusion list -p <peer> -w <workspace>", "browse peer conclusions"),
|
|
100
|
+
("honcho session view / context -s <session>", "transcript, or what an agent would see"),
|
|
101
|
+
("honcho workspace queue-status", "is the deriver processing?"),
|
|
93
102
|
]
|
|
94
|
-
|
|
95
103
|
option_rows = [
|
|
96
104
|
("-w / --workspace", "scope to a workspace"),
|
|
97
105
|
("-p / --peer", "scope to a peer"),
|
|
@@ -101,10 +109,12 @@ def print_welcome(console: Console) -> None:
|
|
|
101
109
|
]
|
|
102
110
|
|
|
103
111
|
console.print(_welcome_panel("getting started", start_rows))
|
|
112
|
+
console.print(_welcome_panel("local stack", stack_rows))
|
|
104
113
|
console.print(_welcome_panel("commands", cmd_rows))
|
|
105
114
|
console.print(_welcome_panel("memory", memory_rows))
|
|
106
115
|
console.print(_welcome_panel("options", option_rows))
|
|
107
116
|
console.print()
|
|
117
|
+
maybe_print_update_nag()
|
|
108
118
|
|
|
109
119
|
|
|
110
120
|
class HonchoTyperGroup(TyperGroup):
|
|
@@ -10,7 +10,7 @@ import typer
|
|
|
10
10
|
|
|
11
11
|
from honcho.api_types import MessageCreateParams
|
|
12
12
|
|
|
13
|
-
from honcho_cli.commands.session import _get_session_id
|
|
13
|
+
from honcho_cli.commands.session import _fetch_recent_messages, _get_session_id
|
|
14
14
|
from honcho_cli.commands.workspace import _handle_error
|
|
15
15
|
from honcho_cli.output import print_error, print_result, status
|
|
16
16
|
from honcho_cli.validation import validate_resource_id
|
|
@@ -37,15 +37,18 @@ def list_messages(
|
|
|
37
37
|
|
|
38
38
|
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer, session=session)
|
|
39
39
|
sid = _get_session_id(session_id)
|
|
40
|
+
if last < 1:
|
|
41
|
+
print_error("INVALID_FLAGS", "--last must be >= 1", {"last": last})
|
|
42
|
+
raise typer.Exit(1)
|
|
40
43
|
client, config = get_client()
|
|
41
44
|
sess = client.session(sid)
|
|
42
45
|
|
|
43
46
|
try:
|
|
44
47
|
filters = {"peer_id": config.peer_id} if config.peer_id else None
|
|
45
|
-
# Fetch newest-first so
|
|
46
|
-
#
|
|
48
|
+
# Fetch newest-first so we always get the most recent N messages, then
|
|
49
|
+
# flip to oldest-at-top / newest-at-bottom for readable display.
|
|
47
50
|
# --reverse keeps the raw server order (oldest first, descending in table).
|
|
48
|
-
msgs = sess
|
|
51
|
+
msgs, _ = _fetch_recent_messages(sess, filters, last)
|
|
49
52
|
if not reverse:
|
|
50
53
|
msgs = list(reversed(msgs))
|
|
51
54
|
|
|
@@ -1,22 +1,29 @@
|
|
|
1
|
-
"""Session commands: list, inspect, context, summaries, peers, search, representation, metadata."""
|
|
1
|
+
"""Session commands: list, inspect, view, context, summaries, peers, search, representation, metadata."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
+
import shlex
|
|
6
7
|
from typing import List, Optional
|
|
7
8
|
|
|
8
9
|
import typer
|
|
9
10
|
|
|
10
|
-
from honcho import HonchoError
|
|
11
|
+
from honcho import HonchoError, Session
|
|
11
12
|
|
|
12
13
|
from honcho_cli.commands.workspace import _config_to_dict, _handle_error, _raw_list
|
|
13
|
-
from honcho_cli.output import print_error, print_result, status, use_json
|
|
14
|
+
from honcho_cli.output import print_error, print_result, print_transcript, status, use_json
|
|
14
15
|
from honcho_cli.validation import validate_resource_id
|
|
15
16
|
|
|
16
17
|
from honcho_cli._help import HonchoTyperGroup
|
|
17
|
-
from honcho_cli.common import
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
from honcho_cli.common import (
|
|
19
|
+
add_common_options,
|
|
20
|
+
get_client,
|
|
21
|
+
get_flag_overrides,
|
|
22
|
+
get_resolved_config,
|
|
23
|
+
handle_cmd_flags,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
app = typer.Typer(cls=HonchoTyperGroup, help="List, inspect, view, create, delete, and manage conversation sessions and their peers.")
|
|
20
27
|
add_common_options(app)
|
|
21
28
|
|
|
22
29
|
|
|
@@ -135,6 +142,240 @@ def inspect(
|
|
|
135
142
|
_handle_error(e, "session", sid)
|
|
136
143
|
|
|
137
144
|
|
|
145
|
+
# Server-side ceiling on page size (fastapi-pagination's default ``Params``
|
|
146
|
+
# declares ``size`` as ``Query(50, ge=1, le=100)``).
|
|
147
|
+
MAX_PAGE_SIZE = 100
|
|
148
|
+
DEFAULT_PAGE_SIZE = 50
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _fetch_recent_messages(sess, filters: dict | None, last: int) -> tuple[list, int | None]:
|
|
152
|
+
"""Fetch the ``last`` most recent messages, newest first.
|
|
153
|
+
|
|
154
|
+
Walks as many newest-first server pages as it takes to fill the window.
|
|
155
|
+
Returns the messages plus the session's total message count (if reported).
|
|
156
|
+
"""
|
|
157
|
+
page = sess.messages(
|
|
158
|
+
filters=filters,
|
|
159
|
+
reverse=True,
|
|
160
|
+
size=min(max(last, 1), MAX_PAGE_SIZE),
|
|
161
|
+
)
|
|
162
|
+
total = page.total
|
|
163
|
+
msgs = list(page.items)
|
|
164
|
+
while len(msgs) < last and page.has_next_page():
|
|
165
|
+
page = page.get_next_page()
|
|
166
|
+
if page is None:
|
|
167
|
+
break
|
|
168
|
+
msgs.extend(page.items)
|
|
169
|
+
return msgs[:last], total
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _next_page_command(
|
|
173
|
+
session_id: str,
|
|
174
|
+
next_page: int,
|
|
175
|
+
size: int,
|
|
176
|
+
*,
|
|
177
|
+
reverse: bool,
|
|
178
|
+
show_ids: bool,
|
|
179
|
+
workspace: str | None,
|
|
180
|
+
peer: str | None,
|
|
181
|
+
) -> str:
|
|
182
|
+
"""Continuation command for the next page, carrying this invocation's scope.
|
|
183
|
+
|
|
184
|
+
Scoping flags are echoed only when passed as flags; anything resolved from
|
|
185
|
+
the environment or config file resolves the same way on the next run. IDs
|
|
186
|
+
are shell-quoted — they may contain spaces and metacharacters, and this
|
|
187
|
+
string is meant to be pasted into a shell.
|
|
188
|
+
"""
|
|
189
|
+
parts = [
|
|
190
|
+
"honcho",
|
|
191
|
+
"session",
|
|
192
|
+
"view",
|
|
193
|
+
session_id,
|
|
194
|
+
"--page",
|
|
195
|
+
str(next_page),
|
|
196
|
+
"--size",
|
|
197
|
+
str(size),
|
|
198
|
+
]
|
|
199
|
+
if reverse:
|
|
200
|
+
parts.append("--reverse")
|
|
201
|
+
if show_ids:
|
|
202
|
+
parts.append("--ids")
|
|
203
|
+
if workspace:
|
|
204
|
+
parts += ["-w", workspace]
|
|
205
|
+
if peer:
|
|
206
|
+
parts += ["-p", peer]
|
|
207
|
+
return shlex.join(parts)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _fetch_all_messages(sess, filters: dict | None) -> tuple[list, int | None]:
|
|
211
|
+
"""Fetch every message in the session, oldest first."""
|
|
212
|
+
page = sess.messages(filters=filters, reverse=False, size=MAX_PAGE_SIZE)
|
|
213
|
+
total = page.total
|
|
214
|
+
msgs = list(page.items)
|
|
215
|
+
while page.has_next_page():
|
|
216
|
+
page = page.get_next_page()
|
|
217
|
+
if page is None:
|
|
218
|
+
break
|
|
219
|
+
msgs.extend(page.items)
|
|
220
|
+
return msgs, total
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@app.command()
|
|
224
|
+
def view(
|
|
225
|
+
session_id: Optional[str] = typer.Argument(None, help="Session ID (uses default if omitted)"),
|
|
226
|
+
last: Optional[int] = typer.Option(
|
|
227
|
+
None,
|
|
228
|
+
"--last",
|
|
229
|
+
help=f"Show only the N most recent messages (default when no --page/--all: {DEFAULT_PAGE_SIZE})",
|
|
230
|
+
),
|
|
231
|
+
page_number: Optional[int] = typer.Option(
|
|
232
|
+
None,
|
|
233
|
+
"--page",
|
|
234
|
+
help="1-indexed page of the full transcript. Use for page 2+.",
|
|
235
|
+
),
|
|
236
|
+
size: Optional[int] = typer.Option(
|
|
237
|
+
None,
|
|
238
|
+
"--size",
|
|
239
|
+
help=f"Messages per page; requires --page (1-{MAX_PAGE_SIZE}, default: {DEFAULT_PAGE_SIZE})",
|
|
240
|
+
),
|
|
241
|
+
all_messages: bool = typer.Option(False, "--all", help="Show the full transcript (every page)"),
|
|
242
|
+
reverse: bool = typer.Option(
|
|
243
|
+
False,
|
|
244
|
+
"--reverse",
|
|
245
|
+
help="Newest first (default is chronological: oldest at top)",
|
|
246
|
+
),
|
|
247
|
+
show_ids: bool = typer.Option(False, "--ids", help="Include message IDs in the transcript"),
|
|
248
|
+
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
|
|
249
|
+
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Filter by peer ID"),
|
|
250
|
+
session: Optional[str] = typer.Option(None, "--session", "-s", help="Override session ID"),
|
|
251
|
+
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
|
|
252
|
+
) -> None:
|
|
253
|
+
"""View a session transcript as a chat log.
|
|
254
|
+
|
|
255
|
+
Modes (pick one):
|
|
256
|
+
|
|
257
|
+
- default / --last N: tail of the conversation (most recent N)
|
|
258
|
+
- --page N [--size M]: page through the full transcript
|
|
259
|
+
- --all: every message
|
|
260
|
+
|
|
261
|
+
Paging follows the requested order: --page 1 starts at the oldest message,
|
|
262
|
+
or the newest with --reverse.
|
|
263
|
+
|
|
264
|
+
Human mode prints a row-delimited table. JSON mode emits the message list
|
|
265
|
+
(same shape as message list).
|
|
266
|
+
"""
|
|
267
|
+
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer, session=session)
|
|
268
|
+
sid = _get_session_id(session_id)
|
|
269
|
+
|
|
270
|
+
# Validate every flag before touching the network.
|
|
271
|
+
modes = sum([
|
|
272
|
+
last is not None,
|
|
273
|
+
page_number is not None,
|
|
274
|
+
all_messages,
|
|
275
|
+
])
|
|
276
|
+
if modes > 1:
|
|
277
|
+
print_error(
|
|
278
|
+
"INVALID_FLAGS",
|
|
279
|
+
"--last, --page, and --all are mutually exclusive",
|
|
280
|
+
{"last": last, "page": page_number, "all": all_messages},
|
|
281
|
+
)
|
|
282
|
+
raise typer.Exit(1)
|
|
283
|
+
|
|
284
|
+
if page_number is not None and page_number < 1:
|
|
285
|
+
print_error("INVALID_FLAGS", "--page must be >= 1", {"page": page_number})
|
|
286
|
+
raise typer.Exit(1)
|
|
287
|
+
if size is not None and page_number is None:
|
|
288
|
+
print_error("INVALID_FLAGS", "--size only applies with --page", {"size": size})
|
|
289
|
+
raise typer.Exit(1)
|
|
290
|
+
if size is not None and not 1 <= size <= MAX_PAGE_SIZE:
|
|
291
|
+
print_error(
|
|
292
|
+
"INVALID_FLAGS",
|
|
293
|
+
f"--size must be between 1 and {MAX_PAGE_SIZE}",
|
|
294
|
+
{"size": size},
|
|
295
|
+
)
|
|
296
|
+
raise typer.Exit(1)
|
|
297
|
+
if last is not None and last < 1:
|
|
298
|
+
print_error("INVALID_FLAGS", "--last must be >= 1", {"last": last})
|
|
299
|
+
raise typer.Exit(1)
|
|
300
|
+
|
|
301
|
+
# Default: tail of conversation (most recent 50).
|
|
302
|
+
mode = "page" if page_number is not None else ("all" if all_messages else "last")
|
|
303
|
+
tail = last if last is not None else DEFAULT_PAGE_SIZE
|
|
304
|
+
page_size = size if size is not None else DEFAULT_PAGE_SIZE
|
|
305
|
+
|
|
306
|
+
client, config = get_client()
|
|
307
|
+
# Read-only: client.session() is a get-or-create POST, so build the Session directly.
|
|
308
|
+
sess = Session(sid, client)
|
|
309
|
+
|
|
310
|
+
try:
|
|
311
|
+
filters = {"peer_id": config.peer_id} if config.peer_id else None
|
|
312
|
+
page_meta: int | None = None
|
|
313
|
+
pages_meta: int | None = None
|
|
314
|
+
|
|
315
|
+
if mode == "page":
|
|
316
|
+
# Page in the order the caller asked for.
|
|
317
|
+
result_page = sess.messages(
|
|
318
|
+
filters=filters,
|
|
319
|
+
page=page_number,
|
|
320
|
+
size=page_size,
|
|
321
|
+
reverse=reverse,
|
|
322
|
+
)
|
|
323
|
+
msgs = list(result_page.items)
|
|
324
|
+
total = result_page.total
|
|
325
|
+
page_meta = result_page.page if result_page.page is not None else page_number
|
|
326
|
+
pages_meta = result_page.pages
|
|
327
|
+
elif mode == "all":
|
|
328
|
+
msgs, total = _fetch_all_messages(sess, filters)
|
|
329
|
+
if reverse:
|
|
330
|
+
msgs = list(reversed(msgs))
|
|
331
|
+
else:
|
|
332
|
+
# Tail window: fetched newest-first, flipped to chronological unless --reverse.
|
|
333
|
+
msgs, total = _fetch_recent_messages(sess, filters, tail)
|
|
334
|
+
if not reverse:
|
|
335
|
+
msgs = list(reversed(msgs))
|
|
336
|
+
|
|
337
|
+
items = [
|
|
338
|
+
{
|
|
339
|
+
"id": m.id,
|
|
340
|
+
"peer_id": m.peer_id,
|
|
341
|
+
"content": m.content,
|
|
342
|
+
"token_count": m.token_count,
|
|
343
|
+
"metadata": m.metadata,
|
|
344
|
+
"created_at": str(m.created_at),
|
|
345
|
+
}
|
|
346
|
+
for m in msgs
|
|
347
|
+
]
|
|
348
|
+
except Exception as e:
|
|
349
|
+
_handle_error(e, "session", sid)
|
|
350
|
+
raise # unreachable: _handle_error always exits
|
|
351
|
+
|
|
352
|
+
next_page_hint = None
|
|
353
|
+
if page_meta is not None and pages_meta is not None and page_meta < pages_meta:
|
|
354
|
+
# Effective overrides, not the command-level params: -w/-p also parse at
|
|
355
|
+
# group and top level.
|
|
356
|
+
overrides = get_flag_overrides()
|
|
357
|
+
next_page_hint = _next_page_command(
|
|
358
|
+
sid,
|
|
359
|
+
page_meta + 1,
|
|
360
|
+
page_size,
|
|
361
|
+
reverse=reverse,
|
|
362
|
+
show_ids=show_ids,
|
|
363
|
+
workspace=overrides["workspace"],
|
|
364
|
+
peer=overrides["peer"],
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
# Rendered outside the try: output failures aren't session API errors.
|
|
368
|
+
print_transcript(
|
|
369
|
+
items,
|
|
370
|
+
session_id=sid,
|
|
371
|
+
total=total,
|
|
372
|
+
page=page_meta,
|
|
373
|
+
pages=pages_meta,
|
|
374
|
+
show_ids=show_ids,
|
|
375
|
+
next_page_hint=next_page_hint,
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
|
|
138
379
|
@app.command()
|
|
139
380
|
def context(
|
|
140
381
|
session_id: Optional[str] = typer.Argument(None, help="Session ID (uses default if omitted)"),
|