docs(cli): regenerate command reference for `session view`

Adds the generated `session view` accordion to the docs snippet and points
the session-debugging workflows at it. Trims the docstring to plain prose —
the RST double-backticks were rendering literally in `--help`, where every
other command uses unmarked flag names — and stops the generator emitting a
trailing blank line that tripped end-of-file-fixer on every regeneration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Aakash Kattelu 2026-08-10 14:07:52 -04:00
parent 0decd2764e
commit 192c3edbff
7 changed files with 69 additions and 41 deletions

View File

@ -305,7 +305,7 @@ honcho peer set-metadata <metadata>
## honcho session
List, inspect, create, delete, and manage conversation sessions and their peers.
List, inspect, view, create, delete, and manage conversation sessions and their peers.
<AccordionGroup>
<Accordion title="add-peers">
@ -461,6 +461,48 @@ honcho session summaries [<session_id>]
<ParamField path="session_id" type="string" />
</Accordion>
<Accordion title="view">
View a session transcript as a chat log.
Modes (pick one):
- default / --last N: tail of the conversation (most recent N)
- --page N [--size M]: page through the full transcript
- --all: every message
Paging follows the requested order: --page 1 starts at the oldest message,
or the newest with --reverse.
Human mode prints a row-delimited table. JSON mode emits the message list
(same shape as message list).
```bash
honcho session view [<session_id>]
```
<ParamField path="session_id" type="string" />
<ParamField path="--last" type="number">
Show only the N most recent messages (default when no --page/--all: 50).
</ParamField>
<ParamField path="--page" type="number">
1-indexed page of the full transcript. Use for page 2+.
</ParamField>
<ParamField path="--size" type="number">
Messages per page; requires --page (1-100, default: 50).
</ParamField>
<ParamField path="--all" type="boolean">
Show the full transcript (every page).
</ParamField>
<ParamField path="--reverse" type="boolean">
Newest first (default is chronological: oldest at top).
</ParamField>
<ParamField path="--ids" type="boolean">
Include message IDs in the transcript.
</ParamField>
<ParamField path="--peer" type="string">
Filter by peer ID. Short alias: `-p`.
</ParamField>
</Accordion>
</AccordionGroup>
## honcho workspace

View File

@ -142,7 +142,7 @@ When you pick up a workspace and need to orient — start broad, narrow to the p
<Step title="Debug a session">
```bash
honcho session inspect <session_id> --json
honcho message list <session_id> --last 20 --json
honcho session view <session_id> --last 20
honcho session context <session_id> --json
honcho session summaries <session_id> --json
```
@ -150,7 +150,7 @@ When you pick up a workspace and need to orient — start broad, narrow to the p
</Steps>
<Tip>
`honcho session context` shows exactly what an agent would receive at inference time — check it before `honcho peer chat` if a response surprises you.
`honcho session context` shows exactly what an agent would receive at inference time — check it before `honcho peer chat` if a response surprises you. `honcho session view` shows the raw transcript that context was built from; it prints content verbatim, so tag-delimited and multi-line messages appear exactly as stored.
</Tip>
### A peer isn't learning
@ -176,7 +176,7 @@ When an agent's responses don't reflect what you expect it to know.
```bash
honcho session context <session_id> --json
honcho session summaries <session_id> --json
honcho message list <session_id> --last 50 --json
honcho session view <session_id> --last 50
```
### Dialectic returns bad answers

View File

@ -230,7 +230,7 @@ def build() -> str:
body: list[str] = []
for name in sorted(root.commands):
body.extend(_render_top(root.commands[name], ["honcho", name]))
return HEADER + "\n".join(body) + "\n"
return HEADER + "\n".join(body).rstrip("\n") + "\n"
def main() -> int:

View File

@ -42,9 +42,8 @@ def list_messages(
try:
filters = {"peer_id": config.peer_id} if config.peer_id else None
# Fetch newest-first so we always get the most recent N messages — the
# shared helper walks pages, so --last above one page isn't truncated —
# then flip to oldest-at-top / newest-at-bottom for readable display.
# Fetch newest-first so we always get the most recent N messages, then
# flip to oldest-at-top / newest-at-bottom for readable display.
# --reverse keeps the raw server order (oldest first, descending in table).
msgs, _ = _fetch_recent_messages(sess, filters, last)
if not reverse:

View File

@ -136,8 +136,7 @@ def inspect(
# Server-side ceiling on page size (fastapi-pagination's default ``Params``
# declares ``size`` as ``Query(50, ge=1, le=100)``), enforced locally so an
# out-of-range --size is a CLI error rather than an opaque 422.
# declares ``size`` as ``Query(50, ge=1, le=100)``).
MAX_PAGE_SIZE = 100
DEFAULT_PAGE_SIZE = 50
@ -145,8 +144,7 @@ DEFAULT_PAGE_SIZE = 50
def _fetch_recent_messages(sess, filters: dict | None, last: int) -> tuple[list, int | None]:
"""Fetch the ``last`` most recent messages, newest first.
Walks as many newest-first server pages as it takes to fill the window, so
``last`` above the page cap is honored instead of silently truncated.
Walks as many newest-first server pages as it takes to fill the window.
Returns the messages plus the session's total message count (if reported).
"""
page = sess.messages(
@ -211,21 +209,20 @@ def view(
Modes (pick one):
- default / ``--last N`` tail of the conversation (most recent N)
- ``--page N [--size M]`` page through the full transcript
- ``--all`` every message
- default / --last N: tail of the conversation (most recent N)
- --page N [--size M]: page through the full transcript
- --all: every message
Paging follows the requested order: ``--page 1`` starts at the oldest
message, or the newest with ``--reverse``.
Paging follows the requested order: --page 1 starts at the oldest message,
or the newest with --reverse.
Human mode prints a row-delimited table. JSON mode emits the message list
(same shape as ``message list``).
(same shape as message list).
"""
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer, session=session)
sid = _get_session_id(session_id)
# Validate every flag before touching the network, so a bad invocation never
# reaches the API.
# Validate every flag before touching the network.
modes = sum([
last is not None,
page_number is not None,
@ -256,14 +253,13 @@ def view(
print_error("INVALID_FLAGS", "--last must be >= 1", {"last": last})
raise typer.Exit(1)
# Default: tail of conversation (most recent 50), matching prior behavior.
# Default: tail of conversation (most recent 50).
mode = "page" if page_number is not None else ("all" if all_messages else "last")
tail = last if last is not None else DEFAULT_PAGE_SIZE
page_size = size if size is not None else DEFAULT_PAGE_SIZE
client, config = get_client()
# Read-only: build the Session directly rather than via client.session(),
# which is a get-or-create POST and would create a session on a typo.
# Read-only: client.session() is a get-or-create POST, so build the Session directly.
sess = Session(sid, client)
try:
@ -272,8 +268,7 @@ def view(
pages_meta: int | None = None
if mode == "page":
# Page in the order the caller asked for, so --reverse --page 1 is
# the newest page rather than the oldest one displayed backwards.
# Page in the order the caller asked for.
result_page = sess.messages(
filters=filters,
page=page_number,
@ -309,8 +304,7 @@ def view(
_handle_error(e, "session", sid)
raise # unreachable: _handle_error always exits
# Rendered outside the try so an output-side failure (e.g. a broken pipe
# from `| head`) isn't reported as a session API error.
# Rendered outside the try: output failures aren't session API errors.
print_transcript(
items,
session_id=sid,

View File

@ -126,11 +126,8 @@ TIMESTAMP_WIDTH = len("2026-01-01T00:00:00.000Z")
def _format_timestamp(value: Any) -> str:
"""Compact UTC timestamp: ``YYYY-MM-DDTHH:MM:SS.mmmZ``.
Offsets are *converted* to UTC rather than dropped, so a non-UTC
``created_at`` isn't relabelled as UTC. Naive values are assumed UTC, which
is what the API returns. Millisecond precision is kept so messages within
the same second stay distinguishable. Unparseable values pass through
verbatim.
Offsets are converted to UTC; naive values are assumed UTC. Unparseable
values pass through verbatim.
"""
if value is None:
return ""
@ -161,8 +158,7 @@ def print_transcript(
Each message dict must have ``peer_id``, ``content``, ``created_at``;
``id`` is optional and only shown when ``show_ids`` is set. ``size`` and
``reverse`` are echoed back in the next-page hint so following it lands on
the adjacent window rather than a differently-sized one.
``reverse`` are echoed back in the next-page hint.
"""
if use_json():
print_json(messages)
@ -193,8 +189,8 @@ def print_transcript(
expand=True,
pad_edge=False,
)
# time is fixed-width ISO-UTC; ids and peers wrap rather than truncate so a
# displayed ID is always a usable one; content takes the rest.
# time is fixed-width ISO-UTC; ids and peers wrap rather than truncate;
# content takes the rest.
table.add_column("time", style="dim", no_wrap=True, width=TIMESTAMP_WIDTH)
if show_ids:
table.add_column("id", style="dim", no_wrap=True)
@ -207,10 +203,7 @@ def print_transcript(
if peer not in peer_color:
peer_color[peer] = _PEER_COLORS[len(peer_color) % len(_PEER_COLORS)]
# Content and IDs go through Text, not Markdown or console markup: this is
# a debugging view, so it must show exactly what was stored — Markdown
# would reflow newlines and swallow the `<thinking>`-style tags that fill
# agent transcripts, and markup would eat bracketed text.
# Text, not Markdown or console markup: content renders verbatim.
row: list[Any] = [_format_timestamp(msg.get("created_at"))]
if show_ids:
row.append(Text(str(msg.get("id") or "")))

View File

@ -320,7 +320,7 @@ class TestJsonContract:
@pytest.mark.parametrize(
"args",
[
["--size", "10"], # --size without --page would be silently ignored
["--size", "10"], # --size requires --page
["--page", "1", "--size", "500"], # over the server's 100 ceiling
["--page", "0"],
["--last", "0"],