Skip to content
Merged
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@ Upgrade notes:
- A server without `GET /api/v1/groups/unread` (older than read-state-sync phase 5b) shows no
unread markers at all, where the local rule used to light them.

### Added

- Agent skill: an agent token can also run `notes list|search|show|edit --room <room>` on the
text notes of a room it belongs to (pepper#5537); the guide now lists seventeen commands, keeps
personal notes, other artifact kinds and note creation/deletion out of scope, and tells an agent
to read the whole note with `notes show` before answering a comment.
- Room messages show note comments for agents: `messages list` and `agents claude-channel` events
print an anchored comment's quoted passage (about 160 chars) above its body and a one-line
header on a note's comments topic pointing at `comments list --note`; `--json` passes `anchor`,
`resolved_at`, `resolved_by_name` and the topic's `subject_artifact_id` through. The agent skill
says to reply under the thread root.
- `pepper comments list|create --note N`: comments on a room note, optionally on a quoted passage;
`--reply-to`, `--mention`, `--idempotency-key`, `--json`. Prototype against the new
`/api/v1/artifacts/{id}/comments` routes.
- `pepper comments resolve|reopen --note N THREAD_ID`; `comments list` hides resolved threads
(`--all` shows them, marked "resolved by X"; `--json` always has every thread plus
`resolved_count`). `resolve` joins the verb set (IA §3.1).

### Changed

- Unread dots and `@` mention markers come from the server's `unread` rows (`unread_count`,
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ Every command except `login` and the interactive REPL takes `--json`, and also s
| `pepper notes show <note> [--room <group>] [--json]` | Print one note's raw markdown body to stdout; `--json` for the full detail (labels, `updated_at`, `etag`, `shared`). |
| `pepper notes create "<name>" [--room <group>] [--file PATH \| -]` | Create a note. Body from `$EDITOR`, or `--file PATH`/`--file -` for stdin. |
| `pepper notes edit <note> [--room <group>] [--file PATH \| -] [--name NAME] [--if-match ETAG \| --force]` | Edit a note; sends nothing when the text is unchanged. |
| `pepper comments list --note <note> [--room <room>] [--all] [--json]` | A room note's comment threads, oldest first: the quoted passage (or whole note), a `detached` marker when the quote is gone from the note, then author, time and body, with replies indented. Resolved threads are hidden (footer counts them); `--all` shows them marked "resolved by X"; `--json` always has every thread and `resolved_count`. |
| `pepper comments create --note <note> [--room <room>] [--quote TEXT] [--reply-to ID] [--mention PERSON_ID]... [MESSAGE \| -] [--idempotency-key K] [--json]` | Comment on a room note: on an exact passage (`--quote`, which must appear once; exit 2 otherwise), or the whole note, or reply to a thread. Personal notes cannot be commented on (exit 2). |
| `pepper comments resolve\|reopen --note <note> [--room <room>] THREAD_ID [--json]` | Mark a thread resolved, or reopen it (idempotent; `THREAD_ID` is the root message id from `comments list --json`). A reply to a resolved thread reopens it. |
| `pepper messages list` on a note's comments topic | Prints a one-line header naming the note (and `pepper comments list --note <id>`), and a `> "quoted passage"` line above each anchored comment. |
| `pepper notes delete <note> [--room <group>] [--yes]` | Delete a note; asks for confirmation unless `--yes` (off a terminal, `--yes` is required). |
| `pepper artifacts list [--kind note\|link\|file] [--label L] [--search TEXT] [--limit N] [--room R] [--json]` | Everything you have asked Pepper to keep, of any kind (pictures, PDFs, recordings and recipe photos are files; reports and recipes are notes), newest first, numbered. `--kind`, `--label` and `--search` go to the server as query parameters (`--search` is its substring match over names and note bodies); the CLI filters nothing. The numbers are saved per profile (`artifacts-view.json`, next to the credentials, removed at `logout` and `login`) for `show`/`download`/`delete N`; if they cannot be saved the list says so and the numbers do not resolve; in `--json` each item carries `n`. `--room` lists a room's. |
| `pepper artifacts show ARTIFACT [--json]` | One artifact: kind, type, size, labels, and its text if it has any (a note's body, a text file's preview). `ARTIFACT` is a number from the last `list` or a full id. `--json` is the server's detail (minus the rendered `body_html`). |
Expand All @@ -293,7 +297,7 @@ layer on top. Note this is unrelated to `pepper skills`, which manages your
account's own skills. A test holds the document to the real parser: every command in it
must be a full `pepper …` invocation whose subcommand and flags exist, every
`--flag` it mentions anywhere must exist, and outside its "cannot run" section
it may only show the `rooms`, `topics`, `messages`, `attachments` and `members list` commands the server accepts an agent token for.
it may only show the `rooms`, `topics`, `messages`, `attachments`, `members list`, `comments` and room-scoped `notes list|search|show|edit` commands the server accepts an agent token for.
A renamed flag, a shortened example or a personal-token-only command fails CI
until the document is updated.

Expand Down
4 changes: 3 additions & 1 deletion planning/cli-information-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ picks one and names the evidence on both sides. Paths are in the pepper server r
| `trajectory` | "Trajectories", "Activity history" | `trajectories_list.html` | **`trajectories`** |
| `scheduled-tasks`, `runs`, `thread`, `destinations` | "Scheduled tasks", "Pause task", "Resume task", "Keep it going", "Run history", "Thread", "Delivery" | `scheduled_tasks_list.html`, `scheduled_task_detail.html`, `settings/_scheduled_tasks.html` | `scheduled-tasks`; `runs`, `thread` kept; `destinations` folds into `edit --to` (the product has one "Delivery" setting, not a separate object); `renew` for "Keep it going" |
| `skills` | "Skills"; "Sharing a skill with a group" | `settings/_skills.html`, `reference/skills.md` | `skills` with `share`/`revoke` |
| `comments` (remarks on a room note, optionally on a quoted passage) | "Comments" on the notes applet | notes applet | **`comments`** with `list` (`--all`; resolved threads are hidden by default, a display filter only), `create` (`--quote`, `--reply-to`, `--mention`), `resolve`/`reopen THREAD_ID`; `--note` container flag. The server finds the quote and builds the anchor; the CLI only sends text. Prototype |
| `notes` (kind `inline_text`) | "Notes" (sidebar, room "Notes board"); "Artifacts" on the settings page | `_sidebar.html`, `reference/artifacts.md`, `settings/_artifacts.html` | `notes`, the text-editing surface: `notes edit` takes the body (`$EDITOR`, `--file`, `If-Match`), which no other kind has. It is not the whole collection: see the `artifacts` row |
| `artifacts` (every kind: `inline_text`, `external_url`, `blob`) | "Artifacts": the app is titled "Artifacts" (`agent/apps/registry.py` `ARTIFACTS_APP`), as are the room page surface (`features/groups/surfaces.py`, `key="artifacts"`), the settings section ("The notes, links and files you've asked Pepper to keep", `settings/_artifacts.html`) and the help topic (`features/help/content.py`, `reference/artifacts.md`). The kinds are "Note", "Link", "File" (`KIND_LABELS`, `agent/artifacts/applet.py`); images, PDFs, recordings, recipe photos are Files, a research report or a recipe is a Note. The iOS app has `ArtifactDetail.swift` | as cited | **`artifacts`** with `list` (`--kind note\|link\|file`, `--label`, `--search`, `--limit`, `--room`; the server filters, the CLI never does), `show`, `download`, `create` (uploads a file), `delete`. Replaces the earlier "never `artifacts`" rule (maintainer, 2026-10-16: all kinds are supported). `notes` stays for writing note text; `artifacts` is for finding, reading, fetching, uploading and deleting anything. Text search is `artifacts list --search` (the maintainer's spelling), not a separate `search` leaf. Routes: `GET /api/v1/artifacts`, `/{id}/detail`, `/{id}/raw`, `POST /upload`, and the `artifacts` app's `delete` action |
| timezone, calendar preferences | "Preferences" (Calendar sub-page, gear titled "Preferences", `<h1>Preferences</h1>`) holds Home address, Work address, **Timezone** ("The same timezone as in Settings."), work days, hours, meals, workouts, weather (`web/apps/defaults/calendar.html`); Settings > Profile also has "Timezone" (`settings/_profile.html`) | as cited | **`preferences`** with `show` and `edit` (`--timezone`, `--home-address`, `--work-days`, ...). No `timezone` noun: the Preferences route takes the account timezone and the page says it is the same one, so one noun covers both. `whoami` keeps printing the timezone as a read. Which calendars count as busy is not on this route (it is per booking page, `booking.set_conflicts`) and is not covered |
Expand Down Expand Up @@ -112,7 +113,7 @@ pepper [--profile P] <noun> <verb> [--room ROOM [--topic TOPIC]] [--<container>
| Read | `list`, `show`, `search`, `watch`, `activity`, `runs` | `list` = many, filterable; `show OBJECT` = one; `search QUERY` = list by text (help names the fields matched); `watch` = stream; `runs OBJECT` = the run history of an object that runs (`scheduled-tasks`, `research-projects`), a J3 list paged with `--before` |
| Write | `create`, `edit`, `delete` | `create` makes, `edit` changes fields, `delete` destroys the object |
| Association | `invite`/`remove`, `share`/`revoke`, `install`/`uninstall`, `leave` | break a link, never destroy the object |
| Domain | `post`, `react`/`unreact`, `archive`/`unarchive`, `mute`/`unmute`, `pause`/`resume`/`renew`, `done`/`dismiss`/`reopen`, `download`, `run`, `guide`, `steer`, `take`, `reconsider` | one each, product-named. `run` starts work and returns its receipt (J5); `--wait` waits for it (§3.5). `steer OBJECT [TEXT…\|-]` queues guidance the object's next run reads (research projects). `take ITEM` marks a delivered item as taken; `reconsider ITEM [--reason]` puts a rejected item back as a candidate (research items) |
| Domain | `post`, `react`/`unreact`, `archive`/`unarchive`, `mute`/`unmute`, `pause`/`resume`/`renew`, `done`/`dismiss`/`resolve`/`reopen`, `download`, `run`, `guide`, `steer`, `take`, `reconsider` | one each, product-named. `run` starts work and returns its receipt (J5); `--wait` waits for it (§3.5). `steer OBJECT [TEXT…\|-]` queues guidance the object's next run reads (research projects). `take ITEM` marks a delivered item as taken; `reconsider ITEM [--reason]` puts a rejected item back as a candidate (research items) |

`rename` is gone as a verb: it is `edit --name`. Destructive = `delete`, `remove`, `revoke`,
`uninstall`, `leave`, and `members edit --role`. They prompt on a TTY; off a TTY or under `--json`
Expand Down Expand Up @@ -316,6 +317,7 @@ today); **S** needs a server change (or verification first); **N** a room is not
| `skills list/show/share/revoke` | optional / optional / required / required | R | `/groups/{gid}/skills…` |
| `skills create/edit/delete` | — | N | a room holds a grant of someone's skill, not a skill; `share` is the room verb |
| `notes *` | optional | R | `/artifacts…?principal=`, `/notes/{id}…?principal=` |
| `comments list/create/resolve/reopen` | optional (scopes `--note` name resolution) | R | `/artifacts/{id}/comments[/{thread}/resolve\|reopen]`; `--note` is the required container flag (§3 rule 2); room notes only, a personal note answers 409 |
| `trajectories list/show` | optional | S-verify | `/trajectories` has no `principal`; help claims room-shared ones list — verify, else add `?principal=` |
| `calendar --planning` | optional | C | `/calendars/planning?principal=` |
| `calendar` (range) | optional | S | `/calendars/range` has no `principal`; or defer to `apps show calendar --room` |
Expand Down
Loading
Loading