CLI
This commit is contained in:
42
AGENTS.md
42
AGENTS.md
@@ -34,6 +34,9 @@ snotes/
|
||||
│ ├── app.js # SPA client + TipTap editor (esm.sh CDN)
|
||||
│ ├── app.css # Hyper-inspired stylesheet
|
||||
│ └── snotes-main.png # logo + favicon (copied from assets/)
|
||||
├── cli/
|
||||
│ ├── snotes.py # single-file CLI/TUI (PEP 723: uv run, deps textual+httpx)
|
||||
│ └── install.sh # install template served at /{user}/cli (placeholders substituted)
|
||||
├── tests/
|
||||
│ ├── conftest.py # LAN + remote ASGI client fixtures (temp DB + data dir)
|
||||
│ └── test_main.py
|
||||
@@ -54,9 +57,14 @@ snotes/
|
||||
| `GET/POST` | `/api/notes` | List (current user) / create |
|
||||
| `GET` | `/api/notes/recent` | Recent notes across all users |
|
||||
| `GET/PUT/DELETE` | `/api/notes/{id}` | Read / update / soft-delete |
|
||||
| `GET` | `/api/notes/lookup?title=` | Exact-title match in current user's space |
|
||||
| `GET` | `/api/notes/{id}/history` | Version history |
|
||||
| `GET` | `/api/tags` | Tag frequency |
|
||||
| `GET` | `/api/search?q=` | FTS5 search |
|
||||
| `GET` | `/api/templates` | `$name` templates for current user + common |
|
||||
| `GET` | `/api/templates/{name}` | Raw template body (own space beats common, newest wins) |
|
||||
| `GET` | `/cli/snotes.py` | The CLI file itself (public, for the installer) |
|
||||
| `GET` | `/{username}/cli` | Personalized bash install script (public) |
|
||||
|
||||
## Non-obvious Behaviors
|
||||
|
||||
@@ -131,3 +139,37 @@ and `npm install -g web-ext`.
|
||||
only works if the user has already logged into snotes in that browser (the
|
||||
`snotes_auth` cookie is reused). On LAN no auth is needed.
|
||||
- `dist/` is gitignored; committed artifact lives in `static/` instead.
|
||||
|
||||
## CLI (cli/)
|
||||
|
||||
A single-file Python TUI/CLI installed via `curl -fsSL <server>/<user>/cli | bash`
|
||||
(the SPA's "CLI" view shows the exact line for the current user).
|
||||
|
||||
- **Distribution**: `cli/install.sh` is rendered by `GET /{username}/cli` with
|
||||
`__SNOTES_URL__` / `__SNOTES_USER__` substituted from the request Host header.
|
||||
It downloads `cli/snotes.py` (served at `/cli/snotes.py`) to `~/.local/bin/snotes`
|
||||
and writes `~/.config/snotes/config.json` (0600) with `{url, user}`.
|
||||
- **Runtime**: PEP 723 script (`#!/usr/bin/env -S uv run --script`) — `uv` resolves
|
||||
textual + httpx on first run. Auth = the same cookies as the webapp: `snotes_user`
|
||||
always, `snotes_auth` after `snotes login` (needed only off-LAN).
|
||||
- **Commands**: bare `snotes` (Textual TUI: recent pane, list, `/` search, `n` new,
|
||||
enter edit, `d` delete), `recent` (plain list), `find <term>`, `new`,
|
||||
`templates`, `login`, and `<template> [date]`.
|
||||
- **Templates**: any note containing `$name` (regex `\$[A-Za-z][A-Za-z0-9_-]*`)
|
||||
defines a template, tracked in the `note_templates` table (synced on save/delete
|
||||
like tags; one-time backfill at startup guarded by `PRAGMA user_version`).
|
||||
`snotes <template> [date]` renders `%date%`/`%time%`/`%datetime%`/`%user%`,
|
||||
strips standalone `$marker` lines (otherwise rendered notes would redefine the
|
||||
template), and reopens an existing note with the same rendered title instead of
|
||||
duplicating. Dates: today (default), yesterday, tomorrow, YYYY-MM-DD, +/-N.
|
||||
- **Editing** is an `$EDITOR` round-trip: temp `.md` file, save back via PUT.
|
||||
- **Tests**: `tests/test_cli.py` (pure functions; textual imported lazily inside
|
||||
`make_app` so the module imports without it) and `tests/test_tui.py` (headless
|
||||
Textual pilot with a fake Api).
|
||||
|
||||
### Gotchas
|
||||
|
||||
- `/api/notes/lookup` is declared BEFORE `/api/notes/{note_id}` — FastAPI matches
|
||||
routes in order, otherwise "lookup" would be treated as a note id.
|
||||
- `note_templates` ordering uses `updated_at DESC, rowid DESC` — `datetime('now')`
|
||||
has second precision, so same-second inserts need the rowid tiebreak.
|
||||
|
||||
Reference in New Issue
Block a user