Metadata-Version: 2.4
Name: the-almanac
Version: 0.10.4
Summary: The CLI for hosted Almanac.
Requires-Python: >=3.12
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic<3,>=2.8

# Almanac CLI

The CLI reads and changes your personal knowledge and work through the hosted API.
The database owns pages, fields, relationships, citations, tasks and sessions.
There is no local wiki to initialize, clone or synchronize.

```sh
almanac login
almanac schema list
almanac schema describe person
almanac create person --input @sam.json --request-key create-sam
almanac read people/sam --json
almanac patch people/sam --expect 1 --stdin --request-key edit-sam < edit.patch
almanac set people/sam --expect 2 --input '{"description":"An investor friend."}'
almanac tasks list --state open
almanac projects list
almanac sources upload notes.pdf --description "Notes from the planning meeting."
almanac sources file SOURCE_ID
almanac sessions read SESSION_ID
almanac sessions track SESSION_ID --expect 1 --input @task.json
almanac accounts list
almanac tools describe granola.list_meetings
```

`patch` accepts contextual `*** Begin Patch` / `*** Update File: people/sam` /
`@@` / `*** End Patch` body edits. `write --stdin` replaces only the Markdown
body. `set --input` replaces supplied field values; `--unset FIELD` clears an
optional field. Arrays and maps replace the complete supplied value.

Writes use the revision you read. `--request-key` lets a retry reuse the exact
same request; if omitted a key is generated once for that invocation. A refresh
of an expired login reuses the same input and key. A revision conflict returns
the backend's dedicated error code and current revision; read before revising
an edit. No write silently retries against a new revision.

Owned commands print JSON snapshots by default; session output omits the runtime's
cached system prompt so actual conversation data stays readable. `--json` returns
the complete API response and also makes errors JSON.
`read --body` prints only Markdown. `--input` accepts inline JSON,
`@file.json`, or `@-` for stdin. `schema all` returns the backend's current schemas.

Collections are bounded. Wiki/tasks/projects take `--cursor` with the last
returned record ID; sessions and search take `--offset`.

`sources upload PATH --description TEXT [--title TEXT]` uploads one file to private
storage, then saves its verified source record. The title defaults to the filename.
The API verifies the bytes and reuses an existing source for the same owner's file
hash. Repeating an upload returns that canonical record; it does not rename it.
An expired-login retry preserves the prepared file metadata. If the file changes
during upload, verification can reject it; rerun the command after edits finish.
`sources file SOURCE_ID` returns a short-lived download link and its expiry, not
the file contents. Request another link after it expires. `sources register` and
`sources read` also support original web and provider locators without fetching
their content.

`email search` and `email read` use the owned Gmail API. `calendar calendars`
and `calendar list` use the owned Google Calendar API. Account selectors accept
IDs or unique labels (often email addresses); results preserve canonical identity.
These reads do not change provider state. Calendar list currently requires one
account/calendar, explicit offset-bearing `--from` and `--to` times, and an IANA
`--timezone`. Relative ranges, combined-calendar listing
and shorthand refs are not exposed by these CLI commands yet.

`email drafts` supports list/read/create/delete/send; updating a draft is not yet
supported. `calendar create`, `update` and `delete` change the selected original.
These write commands require an explicit `--request-key`. Inspect command help
and the shipped agent reference for input fields and provider restrictions.
Use the existing `tools` discovery for other capabilities; inspect each actual input schema.

From the product checkout: `uv run almanac --help`.
Focused behavior checks: `uv run pytest cli/tests/test_personal_http.py`.

## Generated API models

Run `uv run scripts/generate_models.py cli` from the product checkout after
changing an API response consumed by the CLI. Use `--check` to verify committed
output. The pinned generator consumes build-only route metadata; it starts no
server and changes no production schema visibility. CLI wheels contain the
resulting models and require neither the backend nor `almanac-contracts`.

OpenAPI describes wire shapes, not arbitrary Python validators. Notification
acknowledgement's exclusive target rule and the producer's exact integer version
check remain on the API. The CLI sends one explicit acknowledgement target and
validates notification JSON strictly, including count bounds and aware timestamps.
Generated optional collection fields can be absent instead of defaulting to empty
lists; displays handle that absence. Account JSON output uses `exclude_unset` so
producer-omitted fields stay omitted rather than becoming client-invented defaults.

`tools run TOOL --confirm --request-key KEY --input @input.json` runs an authorized
provider write. The key comes from the explicit flag, otherwise
`ALMANAC_IDEMPOTENCY_KEY`, otherwise a generated UUID. The CLI prints it to stderr
before a confirmed run and preserves it through login refresh. Input, including
stdin, is read only once. Keep the same key and exact account/tool/input when
recovering that action; a new key means a new intended action.

`tools action ACTION_ID` or `tools action --request-key KEY` reads its saved receipt,
including status and safe result, without contacting the provider. Receipts remain
readable by their owner after account disconnection. `requested` means unfinished;
`unknown` means the provider may have acted. Neither authorizes a fresh retry.
These are observations, not reconciliation or resend commands. No receipt found
means Almanac has no recorded action under that selector; it is not evidence that
an external operation failed.
