lekha-poth-cli 2.1.0__tar.gz → 2.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/PKG-INFO +32 -6
  2. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/README.md +31 -5
  3. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/pyproject.toml +1 -1
  4. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/scripts/generate_spec.py +12 -3
  5. lekha_poth_cli-2.2.0/skills/lp-cli/README.md +98 -0
  6. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/skills/lp-cli/SKILL.md +109 -66
  7. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/skills/lp-cli/references/admin.md +50 -50
  8. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/skills/lp-cli/references/auth-account.md +28 -28
  9. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/skills/lp-cli/references/chats.md +6 -6
  10. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/skills/lp-cli/references/legacy.md +19 -19
  11. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/skills/lp-cli/references/library.md +29 -29
  12. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/skills/lp-cli/references/public-content.md +16 -16
  13. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/skills/lp-cli/references/vocab.md +10 -4
  14. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/skills/lp-cli/scripts/generate_references.py +56 -13
  15. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/src/lekha_poth_cli/__init__.py +1 -1
  16. lekha_poth_cli-2.2.0/tests/test_contract_version_parity.py +34 -0
  17. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/.gitignore +0 -0
  18. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/scripts/generate_catalog_v2.py +0 -0
  19. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/skills/lp-cli/evals/evals.json +0 -0
  20. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/src/lekha_poth_cli/__main__.py +0 -0
  21. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/src/lekha_poth_cli/app.py +0 -0
  22. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/src/lekha_poth_cli/client.py +0 -0
  23. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/src/lekha_poth_cli/config.py +0 -0
  24. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/src/lekha_poth_cli/endpoints.json +0 -0
  25. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/src/lekha_poth_cli/errors.py +0 -0
  26. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/src/lekha_poth_cli/media.py +0 -0
  27. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/src/lekha_poth_cli/output.py +0 -0
  28. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/src/lekha_poth_cli/register.py +0 -0
  29. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/src/lekha_poth_cli/spec.py +0 -0
  30. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/tests/test_catalog_v2.py +0 -0
  31. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/tests/test_config.py +0 -0
  32. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/tests/test_coverage.py +0 -0
  33. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/tests/test_errors.py +0 -0
  34. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/tests/test_references_current.py +0 -0
  35. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/tests/test_spec_fields.py +0 -0
  36. {lekha_poth_cli-2.1.0 → lekha_poth_cli-2.2.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: lekha-poth-cli
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: Production Typer CLI covering every Lekha Poth HTTP API endpoint.
5
5
  Author: Hermes Agent
6
6
  License: MIT
@@ -26,14 +26,40 @@ Replaces the old stdlib `scripts/agent/lp.py` reader CLI.
26
26
 
27
27
  ## Install
28
28
 
29
+ Published on PyPI as [`lekha-poth-cli`](https://pypi.org/project/lekha-poth-cli/).
30
+ No checkout needed:
31
+
29
32
  ```bash
30
- uv venv
31
- uv pip install -e ~/.hermes/skills/software-development/lekha-poth/lpcli
32
- # or: uv tool install -e that-path
33
- lp --help
33
+ uv tool install lekha-poth-cli # → $HOME/.local/bin/lp
34
+ uv tool upgrade lekha-poth-cli # later, to pick up new routes and flags
35
+ $HOME/.local/bin/lp --version
34
36
  ```
35
37
 
36
- The wrapper `~/.local/bin/lp` should point at this package.
38
+ On macOS `/usr/bin/lp` is the print spooler and is on `PATH`, so call the
39
+ Lekha Poth CLI by its absolute path (`$HOME/.local/bin/lp`), or use the
40
+ alias entry point `lekha-poth`, which nothing else claims. To try it without
41
+ installing: `uvx --from lekha-poth-cli lp --help`.
42
+
43
+ An **agent skill** that teaches an LLM agent to drive this CLI ships in the
44
+ sdist under `skills/lp-cli/` (and in the repository at
45
+ `scripts/agent/lpcli/skills/lp-cli/`); its `README.md` says where to put it.
46
+
47
+ ### Developing against a checkout
48
+
49
+ ```bash
50
+ cd scripts/agent/lpcli
51
+ uv venv .venv && uv pip install --python .venv/bin/python -e ".[dev]"
52
+ .venv/bin/python -m lekha_poth_cli --version
53
+ # or, as a tool that tracks the working tree: uv tool install -e .
54
+ ```
55
+
56
+ ### Releasing
57
+
58
+ **Actions → Release (lekha-poth-cli) → Run workflow** (`bump` minor by default,
59
+ or an exact `version`). It stamps every copy of the contract version
60
+ (`scripts/ci/stamp-lpcli-version.py`), runs the tests, `uv build`s, commits
61
+ and tags `lpcli-vX.Y.Z` on `main`, then `uv publish`es to PyPI. See
62
+ `docs/CICD.md` → "Releasing the agent CLI".
37
63
 
38
64
  ## Configure (once)
39
65
 
@@ -10,14 +10,40 @@ Replaces the old stdlib `scripts/agent/lp.py` reader CLI.
10
10
 
11
11
  ## Install
12
12
 
13
+ Published on PyPI as [`lekha-poth-cli`](https://pypi.org/project/lekha-poth-cli/).
14
+ No checkout needed:
15
+
13
16
  ```bash
14
- uv venv
15
- uv pip install -e ~/.hermes/skills/software-development/lekha-poth/lpcli
16
- # or: uv tool install -e that-path
17
- lp --help
17
+ uv tool install lekha-poth-cli # → $HOME/.local/bin/lp
18
+ uv tool upgrade lekha-poth-cli # later, to pick up new routes and flags
19
+ $HOME/.local/bin/lp --version
18
20
  ```
19
21
 
20
- The wrapper `~/.local/bin/lp` should point at this package.
22
+ On macOS `/usr/bin/lp` is the print spooler and is on `PATH`, so call the
23
+ Lekha Poth CLI by its absolute path (`$HOME/.local/bin/lp`), or use the
24
+ alias entry point `lekha-poth`, which nothing else claims. To try it without
25
+ installing: `uvx --from lekha-poth-cli lp --help`.
26
+
27
+ An **agent skill** that teaches an LLM agent to drive this CLI ships in the
28
+ sdist under `skills/lp-cli/` (and in the repository at
29
+ `scripts/agent/lpcli/skills/lp-cli/`); its `README.md` says where to put it.
30
+
31
+ ### Developing against a checkout
32
+
33
+ ```bash
34
+ cd scripts/agent/lpcli
35
+ uv venv .venv && uv pip install --python .venv/bin/python -e ".[dev]"
36
+ .venv/bin/python -m lekha_poth_cli --version
37
+ # or, as a tool that tracks the working tree: uv tool install -e .
38
+ ```
39
+
40
+ ### Releasing
41
+
42
+ **Actions → Release (lekha-poth-cli) → Run workflow** (`bump` minor by default,
43
+ or an exact `version`). It stamps every copy of the contract version
44
+ (`scripts/ci/stamp-lpcli-version.py`), runs the tests, `uv build`s, commits
45
+ and tags `lpcli-vX.Y.Z` on `main`, then `uv publish`es to PyPI. See
46
+ `docs/CICD.md` → "Releasing the agent CLI".
21
47
 
22
48
  ## Configure (once)
23
49
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "lekha-poth-cli"
7
- version = "2.1.0"
7
+ version = "2.2.0"
8
8
  description = "Production Typer CLI covering every Lekha Poth HTTP API endpoint."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -55,6 +55,8 @@ import uuid as _uuid
55
55
  from pathlib import Path
56
56
  from typing import Any
57
57
 
58
+ from pydantic import AwareDatetime, FutureDatetime, NaiveDatetime, PastDatetime
59
+
58
60
  REPO_ROOT = Path(__file__).resolve().parents[4]
59
61
  OUT = REPO_ROOT / "scripts" / "agent" / "lpcli" / "src" / "lekha_poth_cli" / "endpoints.json"
60
62
  API_V1_PREFIX = "/api/v1"
@@ -105,6 +107,15 @@ _SCALARS: dict[Any, str] = {
105
107
  bool: "bool",
106
108
  _uuid.UUID: "uuid",
107
109
  _dt.datetime: "datetime",
110
+ # Pydantic's constrained datetimes are distinct classes at runtime, not
111
+ # `Annotated[datetime, ...]`, so nothing above would unwrap them and the
112
+ # field would degrade to `str`. `publishedAt` is `AwareDatetime` — the API
113
+ # refuses a timezone-less value — and the CLI must still call it a
114
+ # datetime.
115
+ AwareDatetime: "datetime",
116
+ NaiveDatetime: "datetime",
117
+ PastDatetime: "datetime",
118
+ FutureDatetime: "datetime",
108
119
  _dt.date: "date",
109
120
  }
110
121
 
@@ -145,9 +156,7 @@ def _walk_annotation(annotation: Any) -> list[Any]:
145
156
  """The annotation and every type/metadata object nested inside it."""
146
157
  import typing
147
158
 
148
- seen: list[Any] = [annotation]
149
- for metadata in getattr(annotation, "__metadata__", ()):
150
- seen.append(metadata)
159
+ seen: list[Any] = [annotation, *getattr(annotation, "__metadata__", ())]
151
160
  for arg in typing.get_args(annotation):
152
161
  seen.extend(_walk_annotation(arg))
153
162
  return seen
@@ -0,0 +1,98 @@
1
+ # lp-cli — an agent skill for the Lekha Poth CLI
2
+
3
+ This directory is a self-contained [agent skill](https://docs.claude.com/en/docs/agents-and-tools/agent-skills)
4
+ that teaches an LLM agent to operate [Lekha Poth](https://lekhapoth.com) through
5
+ `lp`, the full-coverage command-line client published on PyPI as
6
+ [`lekha-poth-cli`](https://pypi.org/project/lekha-poth-cli/).
7
+
8
+ You do **not** need the Lekha Poth source repository to use it. You need:
9
+
10
+ 1. **`uv`** — `curl -LsSf https://astral.sh/uv/install.sh | sh` (macOS/Linux).
11
+ 2. **The CLI** — `uv tool install lekha-poth-cli`, which puts `lp` at
12
+ `$HOME/.local/bin/lp`. Keep it current with `uv tool upgrade lekha-poth-cli`.
13
+ 3. **This directory**, placed where your agent loads skills from.
14
+
15
+ ## Installing the skill
16
+
17
+ Copy the whole `lp-cli/` directory (keep `SKILL.md`, `references/`, `scripts/`
18
+ together) into your agent's skills location:
19
+
20
+ | Agent | Where |
21
+ |---|---|
22
+ | Claude Code, for one user | `~/.claude/skills/lp-cli/` |
23
+ | Claude Code, for one project | `<project>/.claude/skills/lp-cli/` |
24
+ | Any other agent that reads `SKILL.md` frontmatter | its skills directory |
25
+
26
+ Or package it as a single file with the skill-creator's
27
+ `package_skill.py` and install the resulting `lp-cli.skill`.
28
+
29
+ The agent reads `SKILL.md` first. It holds the install and pre-flight steps,
30
+ task recipes, the exit-code table and the wire conventions, and routes to one
31
+ of the `references/*.md` files for a command's exact flags.
32
+
33
+ ## Layout
34
+
35
+ ```
36
+ lp-cli/
37
+ ├── SKILL.md # entry point: pre-flight, recipes, conventions, errors
38
+ ├── README.md # this file (for humans)
39
+ ├── references/
40
+ │ ├── public-content.md # generated — items, collections, search, tags, sitemap, stats
41
+ │ ├── auth-account.md # generated — auth, /me, API keys
42
+ │ ├── library.md # generated — reader library + reports
43
+ │ ├── chats.md # generated — persona chat
44
+ │ ├── admin.md # generated — every /admin/* command
45
+ │ ├── legacy.md # generated — deprecated /chapters, /series aliases
46
+ │ └── vocab.md # hand-written — what each allowed value means
47
+ ├── scripts/
48
+ │ └── generate_references.py # rebuilds references/*.md from the CLI's endpoints.json
49
+ └── evals/
50
+ └── evals.json # test prompts used to evaluate the skill
51
+ ```
52
+
53
+ ## Keeping the references in step with your CLI
54
+
55
+ The six generated reference files describe the CLI at contract **2.2.0**. If
56
+ `lp --version` reports something newer, rebuild them from the spec that ships
57
+ inside the installed package — no checkout required:
58
+
59
+ ```bash
60
+ uv run --no-project --with lekha-poth-cli python3 scripts/generate_references.py # rewrite
61
+ uv run --no-project --with lekha-poth-cli python3 scripts/generate_references.py --check # report only
62
+ ```
63
+
64
+ `SKILL.md` and `references/vocab.md` are hand-written and are not touched by
65
+ the script; where they and `lp <command> --help` disagree, `--help` is current.
66
+
67
+ ## For maintainers (inside the Lekha Poth repository)
68
+
69
+ The skill lives at `scripts/agent/lpcli/skills/lp-cli/` and CI enforces that
70
+ it matches the API. After any backend route or parameter change, from the
71
+ repository root:
72
+
73
+ ```bash
74
+ cd backend && uv run python ../scripts/agent/lpcli/scripts/generate_spec.py # endpoints.json (types, choices, sentinels)
75
+ cd .. && python3 scripts/agent/lpcli/scripts/generate_catalog_v2.py # docs/agent/API_CATALOG_V2.md
76
+ python3 scripts/agent/lpcli/skills/lp-cli/scripts/generate_references.py # references/*.md
77
+ ```
78
+
79
+ `backend/tests/unit/test_agent_spec_current.py` compares the spec with the
80
+ live app; `scripts/agent/lpcli/tests/test_references_current.py` and
81
+ `test_catalog_v2.py` check the two derived documents. When the skill is run
82
+ from inside the checkout, `generate_references.py` reads the repository's
83
+ `endpoints.json` rather than the installed package's, so the check is against
84
+ the code under review. Allowed values are derived from the backend's
85
+ `Literal` and `pattern=` validators, so a new vocabulary appears on its own.
86
+ What still needs a human:
87
+
88
+ - **Behaviour that isn't visible from the schema.** Record it in
89
+ `ENDPOINT_NOTES` / `FIELD_VOCAB_HINTS` in `generate_references.py`, only
90
+ after reading the backend code.
91
+ - **`references/vocab.md` and `SKILL.md`.** Every claim should name the
92
+ source file it was checked against.
93
+ - **A new route** needs an entry in `NEW_IDENTITY` in `generate_spec.py`; the
94
+ generator refuses to invent command names.
95
+ - **A CLI behaviour change** means bumping `lpcli`'s `__version__` together
96
+ with the contract version in `SKILL.md`'s title and §11, the repo's
97
+ `agent/SKILL.md` `version` / `cli_package`, and publishing the new version
98
+ to PyPI — a user who only has `uv tool upgrade` sees nothing until then.
@@ -1,45 +1,77 @@
1
1
  ---
2
2
  name: lp-cli
3
- description: Use whenever a task involves `lp`, Lekha Poth's full-coverage API CLI, or anything that CLI does from a terminal. That covers publishing, editing, scheduling (future publish date), or previewing chapters/items; reordering a serial's chapters; marking a series completed; setting or clearing covers and media (uploads, attach); tag descriptions; content rating (safe/adult) and language (bn/en) filters in search; the reader library (favorites, watch-later, history, personal collections, follows, feed); auth, account, and API keys; persona chat; admin reports, takedown, and users; and calling any Lekha Poth HTTP endpoint. Also use when someone says "lp" or "lekha poth cli", pastes an `lp` error or exit code, or asks which flags an `lp` subcommand takes, even if they only describe the task ("publish this chapter via the API", "schedule chapter 12 for Friday", "put these chapters in order"). Not for editing the backend or frontend code itself. Not for the stdlib reader CLI `scripts/agent/lp.py` or `lp_media.py`.
3
+ description: 'Use whenever a task involves `lp`, the full-coverage CLI for Lekha Poth (lekhapoth.com, a Bangla serial-fiction platform), or anything that CLI does from a terminal, including installing or upgrading it (`uv tool install lekha-poth-cli`) and pointing it at an API with a personal API key. Covers publishing, editing, scheduling (future publish date) or previewing chapters; reordering a serial; marking a series completed; covers and media uploads; tag descriptions; content-rating (safe/adult) and language (bn/en) search filters; the reader library (favorites, watch-later, history, collections, follows, feed); auth, account and API keys; persona chat; admin reports, takedown and users; and calling any Lekha Poth HTTP endpoint. Also use when someone says "lp" or "lekha poth cli", pastes an `lp` error or exit code, or asks which flags a subcommand takes, even if they only describe the task ("schedule chapter 12 for Friday"). Needs only the CLI from PyPI, not the Lekha Poth repo. Not for editing Lekha Poth source.'
4
4
  ---
5
5
 
6
- # lp — Lekha Poth CLI (contract 2.1.0)
6
+ # lp — Lekha Poth CLI (contract 2.2.0)
7
7
 
8
- `lp` is a Typer + Pydantic + httpx CLI. Every HTTP route of the Lekha Poth API
9
- is a real subcommand with real flags. The commands are generated from
10
- `endpoints.json`, which is derived from the live FastAPI app, so
8
+ `lp` is a Typer + Pydantic + httpx CLI, published on PyPI as
9
+ `lekha-poth-cli`. Every HTTP route of the Lekha Poth API is a real subcommand
10
+ with real flags. The commands are generated from an `endpoints.json` that
11
+ ships inside the package and is derived from the live FastAPI app, so
11
12
  `lp endpoints` is the authoritative list; do not trust a number written
12
13
  anywhere else.
13
14
 
14
- This file is the map: pre-flight, task recipes, conventions, and errors. The
15
- per-command details (every flag, type, default, allowed values, and gotchas)
16
- are in `references/*.md`, generated from the same spec. **Before running an
17
- unfamiliar write, read its reference entry.** For a command you already know,
18
- `lp <group> <name> --help` is current and lists allowed values as
19
- `(one of: …)`.
20
-
21
- ## 0. Pre-flight — do this before the first command
22
-
23
- 1. **Use the right binary.** On macOS, `/usr/bin/lp` is the system *print
24
- spooler* and is on `PATH`, so a bare `lp` can end up there. Its reply is
25
- `lp: Error - unknown option`. Invoke the Lekha Poth CLI by absolute path:
26
- `$HOME/.local/bin/lp` (on this machine, `/Users/mahamudul/.local/bin/lp`).
27
- That is an editable `uv tool` install of `scripts/agent/lpcli/`, so it
28
- always runs the repository's current code. If that path doesn't exist, run
29
- it from the repo root instead:
30
- `uv run --isolated --with-editable ./scripts/agent/lpcli lp …`.
31
- The examples below write `lp` for brevity.
32
- 2. **Check the version:** `lp --version` should print `2.1.0` or newer. On an
33
- older CLI, the exit codes and typed-flag behaviour in §5–§6 do not hold.
34
- 3. **Know which API you are pointed at.** Run `lp config show` (secrets
15
+ This skill is self-contained: it needs the CLI (installed in §0) and nothing
16
+ else — not the Lekha Poth source repository, which is private. Where a note
17
+ below names a backend file, that is the provenance of the claim, not
18
+ something to open.
19
+
20
+ This file is the map: install and pre-flight, task recipes, conventions, and
21
+ errors. The per-command details (every flag, type, default, allowed values,
22
+ and gotchas) are in `references/*.md`, generated from the same spec. **Before
23
+ running an unfamiliar write, read its reference entry.** For a command you
24
+ already know, `lp <group> <name> --help` is current and lists allowed values
25
+ as `(one of: …)`.
26
+
27
+ ## 0. Install and pre-flight — do this before the first command
28
+
29
+ 1. **Install (or upgrade) the CLI from PyPI with `uv`.** No repository
30
+ checkout is involved; the package carries everything, including the
31
+ endpoint spec the commands are built from.
32
+ ```bash
33
+ uv tool install lekha-poth-cli # first time → $HOME/.local/bin/lp
34
+ uv tool upgrade lekha-poth-cli # later: pick up new routes and flags
35
+ $HOME/.local/bin/lp --version # confirm it answers
36
+ ```
37
+ If `uv` itself is missing: `curl -LsSf https://astral.sh/uv/install.sh | sh`
38
+ (macOS/Linux), then open a new shell. To run once without installing
39
+ anything permanently, `uvx --from lekha-poth-cli lp …` works too.
40
+ 2. **Use the right binary — always the absolute path.** On macOS,
41
+ `/usr/bin/lp` is the system *print spooler* and is on `PATH`, so a bare
42
+ `lp` can end up there; its reply is `lp: Error - unknown option`. Invoke
43
+ the Lekha Poth CLI as `$HOME/.local/bin/lp …` in every real command (the
44
+ examples below write `lp` for brevity). If that path does not exist, step
45
+ 1 has not been done on this machine.
46
+ 3. **Check the version:** `lp --version` should print `2.2.0` or newer. On an
47
+ older CLI, the exit codes and typed-flag behaviour in §5–§6 do not hold —
48
+ run `uv tool upgrade lekha-poth-cli`. If it prints something *newer* than
49
+ the contract version in this file's title, the CLI may have commands or
50
+ flags this skill does not describe yet; see §11.
51
+ 4. **Point it at an API and a credential.** Nothing is compiled in: a fresh
52
+ install has an empty `api_base` and refuses every command with exit 2
53
+ until you set one.
54
+ ```bash
55
+ lp config set --api-base https://api.lekhapoth.com/api/v1 \
56
+ --site-url https://lekhapoth.com --api-key lpak_…
57
+ ```
58
+ That is the hosted production site. A self-hosted or local deploy uses its
59
+ own origin (the base must end in `/api/v1`, e.g.
60
+ `http://localhost:8000/api/v1`). A personal API key (`lpak_…`) is created
61
+ on the website under **Account → API keys** (needs a verified email; the
62
+ scopes offered are the ones the account holds) — or from the CLI with a
63
+ JWT session: `lp auth login`, store the tokens (§4), then
64
+ `lp keys create-api-key`. Anonymous reads (`items`, `collections`,
65
+ `search`, `tags`) work with no key at all.
66
+ 5. **Know which API you are pointed at.** Run `lp config show` (secrets
35
67
  masked) and `env | grep ^LEKHA_POTH_ | cut -d= -f1`. Environment variables
36
- beat the config file (§3). The default config on a developer machine often
37
- targets **production**.
38
- 4. **Confirm connectivity and identity:** run `lp status`. It returns
68
+ beat the config file (§3), and a config written earlier on this machine
69
+ may target **production**.
70
+ 6. **Confirm connectivity and identity:** run `lp status`. It returns
39
71
  health, readiness, and `/me` in one call, and reports an unreachable API
40
72
  instead of failing. Then run `lp keys permissions` to see what the current
41
73
  credential may actually do.
42
- 5. **Don't experiment against production.** Anything under `lp admin`,
74
+ 7. **Don't experiment against production.** Anything under `lp admin`,
43
75
  `library`, `me`, `keys`, `chats` or `reports` that isn't a read changes real
44
76
  data for real readers. To try a command, point it at a local API
45
77
  (`lp --api-base http://localhost:8000/api/v1 …`), or ask first.
@@ -71,7 +103,10 @@ lp admin items update <slug> --status published --published-at 2026-10-02T12:00:
71
103
  ```
72
104
  An item is live only when `status=published` **and** `publishedAt` ≤ now.
73
105
  With a future date, the item stays hidden and appears at that time with no
74
- further call. Always include a UTC offset (`Z`, or `+06:00` for Dhaka).
106
+ further call. The value **must** include a UTC offset (`Z`, or `+06:00` for
107
+ Dhaka) — a timezone-less `2026-10-02T12:00:00` is a 422, not a guess, because
108
+ a timestamp resolved in the wrong zone lands in the future and hides the item
109
+ everywhere but `admin items list`.
75
110
  Convert local times yourself: 18:00 in Dhaka is 12:00Z.
76
111
 
77
112
  **Reorder a serial's chapters** (one call, not N updates)
@@ -112,6 +147,10 @@ relevance order.
112
147
  **Edit a tag's description:**
113
148
  `lp admin tags list`, then `lp admin tags update "<tag>" --description "…"`.
114
149
  Omitting `--description` **clears** the description.
150
+ **Standing rule when publishing to Lekha Poth:** after publishing any item that introduces a tag,
151
+ list tags and PATCH a unique ~145–165 character Bangla SEO description for every
152
+ row with `description` null. Include the tag name, 18+, বাংলা চটি গল্প / চটি উপন্যাস /
153
+ পানু গল্প, plus tag-specific keywords. Use `--body-file` for Bangla. Never skip this.
115
154
 
116
155
  **Reader library (signed-in key):**
117
156
  - `lp library fav list`, `lp library watch list`, `lp library history list`
@@ -221,9 +260,9 @@ code you can branch on:
221
260
 
222
261
  Branch on the exit code and on `ERROR_CODE`, never on the message wording.
223
262
  Several conditions share one HTTP status. A 410 (`CONTENT_QUARANTINED`, a
224
- taken-down item on a public read) exits 1. Source:
225
- `scripts/agent/lpcli/src/lekha_poth_cli/errors.py`, and `ErrorCode` in
226
- `backend/src/app/core/exceptions.py`.
263
+ taken-down item on a public read) exits 1. The mapping is
264
+ `lekha_poth_cli/errors.py` inside the installed package; the codes themselves
265
+ are the backend's `ErrorCode` enum.
227
266
 
228
267
  ## 7. PATCH semantics
229
268
 
@@ -235,10 +274,11 @@ exceptions:
235
274
  - the sentinel fields in §2 clear with `null`;
236
275
  - `admin tags update` clears the description when `--description` is omitted;
237
276
  - `--attributes` and `--tag` replace the whole value you send;
238
- - `publishedAt` can be moved but never nulled.
277
+ - `publishedAt` can be moved but never nulled, and only to a value with a UTC
278
+ offset (`Z` / `+06:00`); a timezone-less timestamp is a 422.
239
279
 
240
- Source: `AdminService.update_chapter` in
241
- `backend/src/app/services/admin_service.py`.
280
+ Verified against the backend's `AdminService.update_chapter`
281
+ (`backend/src/app/services/admin_service.py` in the Lekha Poth repository).
242
282
 
243
283
  ## 8. Built-in commands (not generated)
244
284
 
@@ -263,10 +303,13 @@ Source: `AdminService.update_chapter` in
263
303
 
264
304
  ## 9. Where every generated command is documented
265
305
 
266
- Each file is generated by `scripts/generate_references.py`. For every command
267
- it lists the exact syntax, each path, query, header and body field with its
268
- type, required/optional status, default, allowed values and wire name, and
269
- any command-specific notes checked against the backend.
306
+ Each file is generated by `scripts/generate_references.py` from the CLI's
307
+ own `endpoints.json`. For every command it lists the exact syntax, each path,
308
+ query, header and body field with its type, required/optional status,
309
+ default, allowed values and wire name, and any command-specific notes checked
310
+ against the backend. The `Backend source: backend/src/app/…` footer under
311
+ each entry names the file that note was checked against in the (private)
312
+ Lekha Poth repository — provenance, not reading material.
270
313
 
271
314
  | File | Covers |
272
315
  |---|---|
@@ -313,28 +356,28 @@ text on `admin takedown`. `--sort` differs between `items list` and
313
356
 
314
357
  ## 11. Keeping this skill in sync
315
358
 
316
- Everything except this file and `references/vocab.md` is generated. After any
317
- backend route or parameter change:
318
-
319
- ```bash
320
- cd backend && uv run python ../scripts/agent/lpcli/scripts/generate_spec.py # endpoints.json (types, choices, sentinels)
321
- cd .. && python3 scripts/agent/lpcli/scripts/generate_catalog_v2.py # docs/agent/API_CATALOG_V2.md
322
- python3 scripts/agent/lpcli/skills/lp-cli/scripts/generate_references.py # references/*.md
323
- ```
324
-
325
- CI enforces all three. `backend/tests/unit/test_agent_spec_current.py`
326
- compares the spec with the live app, and `scripts/agent/lpcli/tests/`
327
- (`test_catalog_v2.py`, `test_references_current.py`) checks the two derived
328
- documents. Allowed values are derived from the backend's `Literal` and
329
- `pattern=` validators, so a new vocabulary appears on its own. What still
330
- needs a human:
331
-
332
- - **Behaviour that isn't visible from the schema.** Record it in
333
- `ENDPOINT_NOTES` / `FIELD_VOCAB_HINTS` in `generate_references.py`, only
334
- after reading the backend code.
335
- - **`references/vocab.md` and this file.** Every claim should name the source
336
- file it was checked against.
337
- - **A new route** needs an entry in `NEW_IDENTITY` in `generate_spec.py`; the
338
- generator refuses to invent command names.
339
- - **A CLI behaviour change** means bumping `lpcli`'s `__version__` together
340
- with `agent/SKILL.md`'s `version` and `cli_package`.
359
+ The CLI moves with the API; this skill is a snapshot of it at contract
360
+ **2.2.0**. Three things can drift, and each has a check:
361
+
362
+ - **The CLI on this machine is behind the skill.** `lp --version` is older
363
+ than the title above → `uv tool upgrade lekha-poth-cli`. Flags this file
364
+ describes (`--published-at` scheduling, `--content-rating`, typed
365
+ `--attributes`, `null` sentinels, the exit codes) do not exist on 2.0.x.
366
+ - **The CLI is ahead of the skill.** `lp --version` is newer than the title
367
+ → `lp endpoints` and `lp <cmd> --help` are the truth wherever they and a
368
+ reference file disagree. Rebuild the reference files from the installed
369
+ package so they match your CLI exactly:
370
+ ```bash
371
+ uv run --no-project --with lekha-poth-cli python3 scripts/generate_references.py
372
+ ```
373
+ The script reads `endpoints.json` out of the package (it needs no checkout;
374
+ `--check` reports staleness without writing). What it cannot refresh is the
375
+ hand-written prose — this file and `references/vocab.md` — so treat a value
376
+ that `--help` lists and `vocab.md` does not as real but unexplained.
377
+ - **The API is ahead of the CLI.** A value the API accepts but `--help` does
378
+ not list still works: the CLI shows choices but never enforces them (§2).
379
+ `lp api request` reaches any route that has no generated command yet.
380
+
381
+ Maintainers working inside the Lekha Poth repository regenerate
382
+ `endpoints.json` itself and the derived documents there; the procedure and
383
+ the CI checks that enforce it are in this skill's `README.md`.