wagapi 0.1.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.
@@ -0,0 +1,15 @@
1
+ name: Publish to PyPI
2
+ on:
3
+ release:
4
+ types: [published]
5
+ permissions:
6
+ id-token: write
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment: pypi
11
+ steps:
12
+ - uses: actions/checkout@v5
13
+ - uses: astral-sh/setup-uv@v6
14
+ - run: uv build
15
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .venv/
7
+ .pytest_cache/
8
+ .eggs/
9
+ *.egg
10
+ .tox/
wagapi-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,470 @@
1
+ Metadata-Version: 2.4
2
+ Name: wagapi
3
+ Version: 0.1.0
4
+ Summary: CLI client for the Wagtail Write API, optimised for LLM orchestration
5
+ Project-URL: Repository, https://github.com/tomdyson/wagapi
6
+ Project-URL: Issues, https://github.com/tomdyson/wagapi/issues
7
+ Author-email: Tom Dyson <tom@torchbox.com>
8
+ License-Expression: MIT
9
+ Keywords: api,cli,cms,llm,wagtail
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: click>=8.0
22
+ Requires-Dist: httpx>=0.24
23
+ Requires-Dist: markdown-it-py>=3.0
24
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest-httpx>=0.20; extra == 'dev'
27
+ Requires-Dist: pytest>=7.0; extra == 'dev'
28
+ Requires-Dist: respx>=0.20; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # wagapi
32
+
33
+ A CLI client for [wagtail-write-api](https://github.com/tomdyson/wagtail-write-api), optimised for LLM orchestration.
34
+
35
+ `wagapi` is a thin, predictable HTTP client that translates CLI commands into wagtail-write-api HTTP calls and returns structured output. The intelligence lives in the LLM that orchestrates it.
36
+
37
+ ```bash
38
+ # Discover the content model
39
+ wagapi schema
40
+
41
+ # Learn the fields for a page type
42
+ wagapi schema testapp.BlogPage
43
+
44
+ # Create a page with markdown body
45
+ wagapi pages create testapp.BlogPage --parent /blog/ \
46
+ --title "Iris Murdoch" --body "A philosopher and novelist." --publish
47
+ ```
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ # One-shot via uvx (no install needed)
53
+ uvx wagapi schema
54
+
55
+ # Or install permanently
56
+ pip install wagapi
57
+ # or
58
+ uv tool install wagapi
59
+ ```
60
+
61
+ **Python 3.10+ required.**
62
+
63
+ ## Quick start
64
+
65
+ ### 1. Set up a Wagtail site with wagtail-write-api
66
+
67
+ Follow the [example app guide](https://tomdyson.github.io/wagtail-write-api/development/example-app/) to get a local Wagtail instance running:
68
+
69
+ ```bash
70
+ cd example
71
+ uv run python manage.py migrate
72
+ uv run python manage.py seed_demo
73
+ uv run python manage.py runserver
74
+ ```
75
+
76
+ `seed_demo` prints API tokens for several test users:
77
+
78
+ ```
79
+ --- API Tokens ---
80
+ admin: 25e620d83a9c4a591f5986b1b74bbd4b7365c4be
81
+ editor: 4fbeb9c8...
82
+ moderator: 6c8b9634...
83
+ reviewer: a2377491...
84
+ ```
85
+
86
+ ### 2. Configure wagapi
87
+
88
+ ```bash
89
+ export WAGAPI_URL=http://localhost:8000/api/write/v1
90
+ export WAGAPI_TOKEN=25e620d83a9c4a591f5986b1b74bbd4b7365c4be
91
+ ```
92
+
93
+ Or run `wagapi init` to save credentials to `~/.wagapi.toml`.
94
+
95
+ ### 3. Explore the content model
96
+
97
+ ```bash
98
+ wagapi schema
99
+ ```
100
+
101
+ ```
102
+ testapp.SimplePage — "simple page"
103
+ Fields: title, slug, alias_of, body, id
104
+ Parents: wagtailcore.Page, testapp.SimplePage, testapp.EventPage
105
+ Children: wagtailcore.Page, testapp.SimplePage, testapp.BlogIndexPage, testapp.EventPage
106
+
107
+ testapp.BlogPage — "blog page"
108
+ Fields: title, slug, alias_of, published_date, feed_image, body, authors, id
109
+ Parents: testapp.BlogIndexPage
110
+ Children: (none)
111
+ ...
112
+ ```
113
+
114
+ Get the full field schema for a type:
115
+
116
+ ```bash
117
+ wagapi schema testapp.BlogPage
118
+ ```
119
+
120
+ ### 4. Browse existing pages
121
+
122
+ ```bash
123
+ wagapi pages list
124
+ wagapi pages get 6
125
+ ```
126
+
127
+ ### 5. Create a page
128
+
129
+ ```bash
130
+ wagapi pages create testapp.BlogPage --parent 5 \
131
+ --title "Iris Murdoch" \
132
+ --body "## A Philosopher and Novelist
133
+
134
+ Iris Murdoch (1919–1999) was an Irish-British novelist and philosopher.
135
+
136
+ She argued that moral progress comes from **attention**."
137
+ ```
138
+
139
+ The `--body` flag accepts markdown, which is auto-converted to StreamField blocks (headings, paragraphs with rendered HTML).
140
+
141
+ ### 6. Publish, update, and manage
142
+
143
+ ```bash
144
+ # Publish a draft (use the page ID returned by create)
145
+ wagapi pages publish 15
146
+
147
+ # Update a page
148
+ wagapi pages update 15 --title "Iris Murdoch: The Sovereignty of Good"
149
+
150
+ # Create and publish in one step, using a URL path as parent
151
+ wagapi pages create testapp.BlogPage --parent /blog/ \
152
+ --title "Simone Weil" \
153
+ --body "Simone Weil was a French philosopher and mystic." \
154
+ --field published_date:2026-04-07 \
155
+ --publish
156
+
157
+ # Unpublish
158
+ wagapi pages unpublish 15
159
+
160
+ # Delete (prompts for confirmation)
161
+ wagapi pages delete 15
162
+ ```
163
+
164
+ ### 7. Inspect requests
165
+
166
+ ```bash
167
+ # See HTTP request/response details
168
+ wagapi -v pages get 6
169
+
170
+ # Preview without executing
171
+ wagapi --dry-run pages create testapp.SimplePage --parent 3 --title "Test"
172
+ ```
173
+
174
+ ### 8. Pipe-friendly JSON output
175
+
176
+ When piped, output is JSON automatically:
177
+
178
+ ```bash
179
+ wagapi pages list | jq '.items[].title'
180
+ wagapi schema testapp.BlogPage | cat
181
+ ```
182
+
183
+ Force a format with `--json` or `--human`:
184
+
185
+ ```bash
186
+ wagapi --human pages list
187
+ wagapi --json pages get 6
188
+ ```
189
+
190
+ ## Configuration
191
+
192
+ ### Config priority
193
+
194
+ Settings are resolved in this order (highest priority first):
195
+
196
+ | Priority | Source | Example |
197
+ |---|---|---|
198
+ | 1 (highest) | CLI flags | `--url`, `--token` |
199
+ | 2 | Environment variables | `WAGAPI_URL`, `WAGAPI_TOKEN` |
200
+ | 3 | Project dotfile | `./.wagapi.toml` |
201
+ | 4 (lowest) | User dotfile | `~/.wagapi.toml` |
202
+
203
+ ### Dotfile format
204
+
205
+ ```toml
206
+ # ~/.wagapi.toml
207
+ url = "https://cms.example.com/api/write/v1"
208
+ token = "abc123def456"
209
+ ```
210
+
211
+ ## Commands
212
+
213
+ ### Global flags
214
+
215
+ | Flag | Env var | Description |
216
+ |---|---|---|
217
+ | `--url URL` | `WAGAPI_URL` | API base URL |
218
+ | `--token TOKEN` | `WAGAPI_TOKEN` | Auth token |
219
+ | `--json` | — | Force JSON output |
220
+ | `--human` | — | Force human output |
221
+ | `--verbose` / `-v` | — | Print HTTP request/response details to stderr |
222
+ | `--dry-run` | — | Print the HTTP request that *would* be sent |
223
+
224
+ ### `wagapi init`
225
+
226
+ Interactive setup that writes `~/.wagapi.toml`:
227
+
228
+ ```
229
+ $ wagapi init
230
+ Wagtail Write API URL: https://cms.example.com/api/write/v1
231
+ API Token: ****************************
232
+ Testing connection... ✓ Connected (3 page types found)
233
+ Config written to ~/.wagapi.toml
234
+ ```
235
+
236
+ If `--url` and `--token` are both provided, skips interactive prompts.
237
+
238
+ ### `wagapi schema`
239
+
240
+ List all available page types, or show the full field schema for a specific type:
241
+
242
+ ```bash
243
+ wagapi schema # list all types
244
+ wagapi schema testapp.BlogPage # show fields, parents, children
245
+ ```
246
+
247
+ JSON output returns the raw schema from the API verbatim, including `create_schema`, `patch_schema`, and `read_schema`.
248
+
249
+ ### `wagapi pages list`
250
+
251
+ ```bash
252
+ wagapi pages list [OPTIONS]
253
+ ```
254
+
255
+ | Option | Description |
256
+ |---|---|
257
+ | `--type TYPE` | Filter by page type, e.g. `testapp.BlogPage` |
258
+ | `--parent ID` | Direct children of page ID |
259
+ | `--descendant-of ID` | All descendants of page ID |
260
+ | `--status STATUS` | `draft`, `live`, or `live+draft` |
261
+ | `--slug SLUG` | Exact slug match |
262
+ | `--path PATH` | Exact URL path match, e.g. `/blog/my-post/` |
263
+ | `--search QUERY` | Full-text search |
264
+ | `--order FIELD` | Sort field, e.g. `title`, `-first_published_at` |
265
+ | `--limit N` | Items per page (default: 20) |
266
+ | `--offset N` | Pagination offset |
267
+
268
+ ### `wagapi pages get`
269
+
270
+ ```bash
271
+ wagapi pages get 42
272
+ wagapi pages get 42 --version live
273
+ ```
274
+
275
+ ### `wagapi pages create`
276
+
277
+ ```bash
278
+ wagapi pages create <type> --parent ID_OR_PATH --title TITLE [OPTIONS]
279
+ ```
280
+
281
+ | Option | Description |
282
+ |---|---|
283
+ | `--parent ID_OR_PATH` | **Required.** Parent page ID or URL path (e.g. `/blog/`) |
284
+ | `--title TITLE` | **Required.** Page title |
285
+ | `--slug SLUG` | URL slug (auto-generated from title if omitted) |
286
+ | `--field KEY:VALUE` | Set a field value (repeatable) |
287
+ | `--body TEXT` | Body content as markdown. Use `-` for stdin |
288
+ | `--publish` | Publish immediately (default: create as draft) |
289
+ | `--raw` | Treat field values as raw JSON (no auto-wrapping) |
290
+
291
+ **Markdown body (auto-converted to StreamField):**
292
+
293
+ ```bash
294
+ wagapi pages create testapp.BlogPage --parent /blog/ \
295
+ --title "Iris Murdoch" \
296
+ --body "## Early Life
297
+
298
+ Iris Murdoch was born in Dublin in 1919.
299
+
300
+ ## Philosophy
301
+
302
+ She argued that moral progress comes from **attention**."
303
+ ```
304
+
305
+ **With extra fields:**
306
+
307
+ ```bash
308
+ wagapi pages create testapp.BlogPage --parent 5 \
309
+ --title "Iris Murdoch" --field published_date:2026-04-06
310
+ ```
311
+
312
+ **Raw mode for full StreamField control:**
313
+
314
+ ```bash
315
+ wagapi pages create testapp.BlogPage --parent 5 \
316
+ --title "Iris Murdoch" --raw \
317
+ --field 'body:[{"type":"paragraph","value":"<p>Hello</p>","id":"abc123"}]'
318
+ ```
319
+
320
+ **Reading body from stdin:**
321
+
322
+ ```bash
323
+ cat post.md | wagapi pages create testapp.BlogPage \
324
+ --parent /blog/ --title "Iris Murdoch" --body -
325
+ ```
326
+
327
+ ### `wagapi pages update`
328
+
329
+ ```bash
330
+ wagapi pages update 42 --title "New Title" --publish
331
+ ```
332
+
333
+ Same field options as `create` (minus `--parent` and type). Only specified fields are sent (PATCH semantics).
334
+
335
+ ### `wagapi pages delete`
336
+
337
+ ```bash
338
+ wagapi pages delete 42
339
+ wagapi pages delete 42 --yes # skip confirmation
340
+ ```
341
+
342
+ No confirmation prompt when piped (non-TTY).
343
+
344
+ ### `wagapi pages publish` / `unpublish`
345
+
346
+ ```bash
347
+ wagapi pages publish 42
348
+ wagapi pages unpublish 42
349
+ ```
350
+
351
+ ### `wagapi images list` / `get`
352
+
353
+ ```bash
354
+ wagapi images list [--search QUERY] [--limit N] [--offset N]
355
+ wagapi images get 7
356
+ ```
357
+
358
+ ## Markdown-to-StreamField conversion
359
+
360
+ When `--raw` is **not** set and a field is a StreamField, the CLI auto-converts markdown into blocks:
361
+
362
+ | Markdown | StreamField block |
363
+ |---|---|
364
+ | `# Heading` | `{"type": "heading", "value": {"text": "...", "size": "h1"}}` |
365
+ | `## Heading` | heading with `"size": "h2"` |
366
+ | `### Heading` | heading with `"size": "h3"` |
367
+ | Paragraph text | `{"type": "paragraph", "value": "<p>...</p>"}` |
368
+ | `![alt](wagapi:image/42)` | `{"type": "image", "value": 42}` |
369
+
370
+ Each block gets a generated UUID v4 `id`.
371
+
372
+ The auto-wrapper only produces `heading`, `paragraph`, and `image` blocks. For other block types (e.g. `quote`, `embed`, `code`), use `--raw` mode.
373
+
374
+ ## Error handling
375
+
376
+ Errors go to stderr. Exit codes:
377
+
378
+ | Code | Meaning |
379
+ |---|---|
380
+ | 0 | Success |
381
+ | 1 | General / unexpected error |
382
+ | 2 | Usage / argument error |
383
+ | 3 | Connection / network error |
384
+ | 4 | Authentication error (401) |
385
+ | 5 | Permission denied (403) |
386
+ | 6 | Not found (404) |
387
+ | 7 | Validation error (400/422) |
388
+
389
+ ## LLM integration
390
+
391
+ Include this in your LLM's system prompt to enable wagapi tool use:
392
+
393
+ ```
394
+ You have access to `wagapi`, a CLI for managing Wagtail CMS content.
395
+
396
+ Before creating or updating pages:
397
+ 1. Run `wagapi schema` to discover available page types
398
+ 2. Run `wagapi schema <type>` to see the exact fields and StreamField block types
399
+
400
+ Key commands:
401
+ wagapi schema [type] — discover content model and block schemas
402
+ wagapi pages list [--type T] [--slug S] [--path P] — list/find pages
403
+ wagapi pages get <id> — read page detail (latest draft)
404
+ wagapi pages create <type> --parent ID_OR_PATH --title T [--field K:V]... [--body MD] [--publish]
405
+ wagapi pages update <id> [--field K:V]... [--publish]
406
+ wagapi pages delete <id> --yes
407
+ wagapi pages publish <id>
408
+ wagapi images list
409
+
410
+ The --parent flag accepts a page ID or a URL path (e.g. --parent /blog/).
411
+ Body text accepts markdown by default. Use --raw for full StreamField JSON control.
412
+ Pages are created as drafts unless --publish is passed.
413
+ Output is JSON when piped.
414
+ ```
415
+
416
+ ### Example LLM tool call sequence
417
+
418
+ **User:** "Create a blog post about Iris Murdoch and publish it"
419
+
420
+ ```bash
421
+ # Step 1: Discover the content model
422
+ wagapi schema | cat
423
+
424
+ # Step 2: Get the BlogPage field schema
425
+ wagapi schema testapp.BlogPage | cat
426
+
427
+ # Step 3: Create and publish
428
+ wagapi pages create testapp.BlogPage \
429
+ --parent /blog/ \
430
+ --title "Iris Murdoch: The Sovereignty of Good" \
431
+ --body "## A Philosopher and Novelist
432
+
433
+ Iris Murdoch (1919–1999) was an Irish-British novelist and philosopher..." \
434
+ --field published_date:2026-04-06 \
435
+ --publish | cat
436
+ ```
437
+
438
+ ## Development
439
+
440
+ ```bash
441
+ # Install with dev dependencies
442
+ pip install -e ".[dev]"
443
+
444
+ # Run tests
445
+ pytest tests/ -v
446
+ ```
447
+
448
+ ## Command tree
449
+
450
+ ```
451
+ wagapi
452
+ ├── init Configure connection
453
+ ├── schema List all page types
454
+ │ └── <type> Show field schema for a type
455
+ ├── pages
456
+ │ ├── list List pages (with filters)
457
+ │ ├── get <id> Get page detail
458
+ │ ├── create <type> Create a page
459
+ │ ├── update <id> Update a page
460
+ │ ├── delete <id> Delete a page
461
+ │ ├── publish <id> Publish latest revision
462
+ │ └── unpublish <id> Revert to draft
463
+ └── images
464
+ ├── list List images
465
+ └── get <id> Get image detail
466
+ ```
467
+
468
+ ## License
469
+
470
+ MIT