Metadata-Version: 2.5
Name: quaestor-cli
Version: 0.2.1
Summary: Command-line client for the Quaestor API — built for humans and agents.
Project-URL: Homepage, https://quaestor.app
Project-URL: Repository, https://github.com/neuromaxer/quaestor-lite
Project-URL: Documentation, https://github.com/neuromaxer/quaestor-lite/blob/main/apps/cli/README.md
Project-URL: Issues, https://github.com/neuromaxer/quaestor-lite/issues
Keywords: agent,ai,cli,habit-tracker,quaestor
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Utilities
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: httpx>=0.27
Requires-Dist: typer>=0.16
Description-Content-Type: text/markdown

# quaestor-cli

`qst` — a command-line client for [Quaestor](https://quaestor.app), a habit tracker.

Built for two users at once: a person in a terminal, and an AI assistant acting on their
behalf. That second audience shapes most of the design — commands are task-shaped rather
than endpoint-shaped, streaks are addressed by name, failures are distinguishable by exit
code, and the server's own error message is printed verbatim so an agent can relay
something true.

```bash
uv tool install quaestor-cli
```

## Setup

Create an API key in the Quaestor web app under **Profile → Developer Access**, then:

```bash
export QUAESTOR_API_KEY="qst_live_..."
export QUAESTOR_URL="https://your-quaestor-backend"   # defaults to http://localhost:8000
qst doctor
```

Or store it instead of exporting it — read from stdin, so it never lands in shell history:

```bash
pbpaste | qst auth login
```

The key is stored at `~/.config/quaestor/credentials.json` with mode `0600`. The
environment variable wins if both are present.

`qst` refuses to send a key over plain HTTP to anything but a loopback host. Set
`QUAESTOR_ALLOW_INSECURE=1` only if you are knowingly running a plaintext self-hosted
backend.

## Commands

```
qst streaks                      List streaks with mode, unit and target
qst show <streak>                Full configuration plus per-day totals, including missed days
qst log <streak> <value>         Log a COUNT amount
qst log <streak> --minutes 45    Log a TIME block (45, 45m, 1h30m, 1.5h)
qst events <streak>              Individual entries, newest first, with the source of each
qst review                       Totals, active days and current streak per streak
qst timer start|stop|status      Control the single running timer
qst auth login|whoami|logout     Manage the stored credential
qst doctor                       Check URL, reachability, credential and granted scopes
```

Every command takes `--json` for machine-readable output on stdout. Every write takes
`--dry-run`, which prints the request and sends nothing.

### Windows

`events`, `show` and `review` share one set of window flags: `--today`, `--week`
(default), `--days N`, or an explicit `--from` / `--to`. The explicit and relative forms
cannot be combined. Dates accept `today`, `yesterday`, a weekday (`mon`..`sun`, meaning
the most recent past one), `YYYY-MM-DD`, or an offset like `-3d`.

### Streaks are named, never UUIDs

`<streak>` is a name. Resolution tries exact match, then unique prefix, then unique
substring. On zero or multiple matches it exits `3`, lists the candidates, and writes
nothing — it never guesses.

```
$ qst log r 1
Streak 'r' is ambiguous — candidates: Reading, Running. Nothing was written.
```

### `--unit` is a safety check, not data

Units belong to the streak, not the event, so `--unit` is never sent to the API. It
asserts what you believe the streak measures and fails before writing if you are wrong —
which is what stops an assistant logging "5 miles" as 5 km.

```
$ qst log running 5 --unit miles
'Running' is measured in km, not miles.
```

## Exit codes

An agent's only reliable error channel, so they are stable:

| Code | Meaning |
| --- | --- |
| 0 | Success |
| 1 | Bad arguments or unparseable input |
| 2 | Authentication failed, or the key lacks the required scope |
| 3 | Unknown or ambiguous streak |
| 4 | Server or network error |

On failure the server's `detail` message is printed verbatim to stderr.

## Scopes

A key only carries the permissions granted when it was created, and the server enforces
them. Insufficient scope returns a message naming what is missing, so it is clear whether
something is a bug or a deliberate restriction:

```
$ qst log reading 5
API key is missing required scope(s): events:write
```

Deleting a streak has no scope at all — it is web-app only, because it also destroys every
event, total and milestone beneath it. `qst doctor` lists the scopes a key actually holds.

## Using this with an AI assistant

The repository ships an [Agent
Skill](https://github.com/neuromaxer/quaestor-lite/blob/main/skills/quaestor/SKILL.md)
that teaches an assistant when and how to use these commands, following the
[agentskills.io](https://agentskills.io) format. Copy it to `~/.agents/skills/` (OpenClaw)
or `~/.hermes/skills/` (Hermes Agent).

Design notes and the security model are in the [feature
documentation](https://github.com/neuromaxer/quaestor-lite/blob/main/docs/features/agent-connect/agent-connect.md).

## Requirements

Python 3.12+. Depends only on `httpx` and `typer` — it speaks HTTP and never imports the
Quaestor backend, so it installs in seconds and tolerates version skew against the server.
