feat!: one paperspec command with subcommands, help and examples

The six console scripts (paperspec-check, -build, -validate, -load,
-stress, -site) become subcommands of a single `paperspec` command:
paperspec validate, load, check, build, stress and site. `python -m
paperspec COMMAND` runs the same thing.

- paperspec/cli/main.py builds the whole parser, apart from the commands,
  so `paperspec --help` and `paperspec COMMAND --help` work without a
  checkout or the tools extra; a command's module is imported only when
  it runs.
- `paperspec` with no command prints the overview: what each command does,
  examples, and which work anywhere (validate, load) and which need a
  checkout (check, build, stress, site). Each command's help has a
  description with advice, and worked examples; a misspelled command gets
  a suggestion (argparse suggest_on_error); `--version` prints the version
  and the pinned Unicode version.
- Each command module exposes run(args) and returns the exit status.
  Arguments that parse but do not fit together raise UsageError, which
  prints the command's usage and exits 2: load now also refuses
  --built-from or --store without --bundle.
- The failure messages say what to do: install paperspec[tools], clone the
  repository, rerun stress with the printed seed, or run paperspec build
  when the rebuild check differs. check and stress say how many failures
  they did not list.
- check runs the rebuild as `python -m paperspec build`.
- pyproject installs one script, `paperspec`; the pixi tasks and the docs
  use the subcommands.

pixi run check passes (run here as `python -m paperspec check` with src/ on
the path); the rebuild is byte-identical. pixi.lock still needs
`pixi lock`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
alexmandClaude committed 2026-10-15 09:28:30 -04:00
1 parent 3a5f28d010
commit 88379d152c
30 files changed
+385 -174

No files matched your search

+2 -2
View File
@@ -31,7 +31,7 @@ dolt sql < sql/doc.sql && dolt sql < sql/store.sql
The PostgreSQL database `lab` and the Dolt repository must already exist.
The `sqlite3` line runs as shown; the `psql` and `dolt` lines follow the portability rules of [chapter 13](../spec/13-database.md#131-portability) but have not been run on those engines in this repository.
To load packages into SQLite without pixi, install the package with its `tools` extra on Python 3.14, then run `paperspec-load --sqlite lab.db PACKAGE…`.
To load packages into SQLite without pixi, install the package with its `tools` extra on Python 3.14, then run `paperspec load --sqlite lab.db PACKAGE…`.
The file [docs/examples.md](../docs/examples.md#load-packages-into-sqlite) has queries to try.
The loader copies files that have no table (assets, dataset files, derived files) by SHA-256 into `lab.db.assets/`.
@@ -57,7 +57,7 @@ On `fixtures/packages/hale2026harmonic-damb` it returns one row, `hca6dc5`: the
- Names: no table, column or index name is reserved in SQLite, PostgreSQL or MySQL (Dolt is MySQL-compatible).
The formats' `key`, `index`, `at`, `by`, `before` and `after` become `doc_key`, `idx`, `made_at`, `signer`, `before_hash` and `after_hash`.
- Round trip: each row keeps every property of its record, so unloading restores the same bytes and the same manifest hash.
Unloading copies the files with no table from the asset store, which must travel with the database. `paperspec-check` tests this for every fixture package.
Unloading copies the files with no table from the asset store, which must travel with the database. `paperspec check` tests this for every fixture package.
- Derived tables: `citations`, `receipt_nodes` and `annotation_targets` are built on load for queries and are not part of the package.
## Status