Skip to main content

ritebook

Ritebook is a Python CLI for validating, publishing, registering, browsing, and installing Agent Skill indexes. It supports publisher workflows that generate reviewable ritebook-index.json files and consumer workflows that install skills from registered Git-backed indexes.

Specifications

docs/specs/ is the canonical source for Ritebook's product, shared-contract, and supported-workflow requirements. This README provides setup and usage guidance; when its examples need more detail, the owning specification defines the contract.

Requirements

  • Python 3.13 or newer
  • uv for dependency management and command execution

Development setup

Install development dependencies:

uv sync --group dev

Install the Git pre-commit hook:

uv run pre-commit install

Run the pre-commit hooks across the full repository when changing the hook configuration or before opening a PR:

uv run pre-commit run --all-files

Pre-commit provides fast local feedback for file hygiene, Ruff formatting and linting, ty type checking, Import Linter architecture contracts, and tests. It complements, but does not replace, the full local quality gate.

Run local quality checks:

uv run ruff format .
uv run ruff check .
uv run ty check src/ritebook
uv run lint-imports
uv run pytest

Plain pytest execution deselects Docker E2E tests by default. The Docker image explicitly opts into them with --run-e2e -m e2e; direct host opt-in is rejected because it cannot establish the clean installed-wheel boundary.

Run the mandatory Docker E2E gate before handoff:

docker build -f Dockerfile.e2e -t ritebook-e2e .
docker run --rm ritebook-e2e

Dockerfile.e2e is a clean installed-wheel test boundary, not production packaging. It builds a wheel from the current source, installs that wheel and its declared runtime dependencies into a consumer environment, and runs the E2E suite from a separate locked test-tool environment. The final image contains the E2E harness but not the project source, pyproject.toml, or uv.lock, so the tests invoke the installed ritebook console script directly. Tests run as an unprivileged user with a controlled writable home and use local Git repositories, explicit registry files, and explicit cache directories. Normal container networking is allowed, but the image receives no credentials or developer-local Ritebook state. Docker is the only supported E2E execution path. CI/CD uses the same build and run commands and runs Docker E2E as a mandatory quality gate in parallel with the non-E2E quality checks.

Build the package distributions:

uv build

Print the installed Ritebook version:

uv run ritebook --version

CLI command structure

Ritebook groups operations by resource:

ritebook skills <lint|list|install|sync|contribute> ...
ritebook indexes <publish|add|list|update> ...

Publisher skill index generation

Maintainers can validate skill headers and generate a reviewable skill catalog index from an explicit skills root:

uv run ritebook skills lint --root <path>

The skills lint command iteratively finds visible SKILL.md candidates so invalid nested declarations are reported, then enforces the schema-v1 catalog layout. It does not follow symlinked skill directories or files; visible symlinked candidates are reported as validation issues. A skill must be either <skill>/SKILL.md at the catalog root or <collection>/<skill>/SKILL.md one level below an implicit collection. Every catalog segment must be a canonical kebab-case identifier. A node cannot be both a root skill and a collection, and a SKILL.md directly at the skills root is invalid. The Agent Skills specification is the canonical baseline for portable skill headers. The minimal accepted header contains only name and description; Ritebook also supports the standard optional license, compatibility, metadata, and allowed-tools fields. Compatible collection profiles may require standard optional flat metadata, but nested metadata is invalid and every metadata value must be a string. YAML mapping keys must be unique. Frontmatter may span any number of lines but must be valid UTF-8 and at most 65,536 bytes including its delimiters and line endings. An empty catalog is valid. Successful lint runs report Checked N skill(s) using the regular-file candidate count without writing an index file. See the Skill Linter specification for the complete header and diagnostic contract.

The repository's .agents/skills directory contains skills sourced from other repositories. Their owning collections validate them, so Ritebook's local and CI quality gates do not lint that directory.

uv run ritebook indexes publish --skills-root <path> --name <published-name>

Run indexes publish from the repository directory that will contain the generated index. Ritebook always writes ritebook-index.json in that current directory. The required --skills-root may be relative or absolute, but it must resolve to that directory or one of its descendants. Ritebook serializes it as a portable repository-relative skills_root; equivalent relative and absolute inputs produce the same index paths. The --name option is required and must be a stable single-segment kebab-case identifier such as company-skills; slashes are not allowed because skill references use <local-alias>/<skill-path>. This published name is written to ritebook-index.json as index.name and becomes the default consumer local alias. The indexes publish command reuses the same validation flow as skills lint and refuses to write or overwrite ritebook-index.json when any discovered skill is invalid. When validation succeeds, publication consumes the exact immutable snapshot produced by that validation pass; it does not rescan skill files or reparse frontmatter. The success message reports the canonical output path ritebook-index.json relative to the invocation directory.

Review the generated ritebook-index.json before committing it with the related skill changes. Schema v1 is a closed portable contract: consumers reject unknown or duplicate JSON members, non-standard JSON constants, unsafe or non-portable paths, invalid canonical UTC timestamps, non-normalized descriptions, excessive input above 16 MiB, nesting above depth 32, and catalogs above 10,000 skills.

Consumer index registry

Users can register, refresh, browse, and install skills from Git-backed Ritebook skill indexes.

Register a Git URL source:

uv run ritebook indexes add --source git@github.com:company/internal-skills.git

Use SSH configuration, a Git credential helper, or another Git-managed authentication mechanism. Do not embed usernames, passwords, or tokens in a standard URL: Ritebook rejects URL authority user-info before running Git or writing local state. scp-like SSH sources such as the example above remain supported.

Register an already-cloned local Git repository without Ritebook mutating it:

uv run ritebook indexes add --source ./internal-skills

Assign a local alias to resolve a published-name collision, or replace an existing registration with the same alias:

uv run ritebook indexes add \
  --source git@github.com:company/internal-skills.git \
  --alias platform-skills \
  --force

Each index has a publisher-owned name in ritebook-index.json and a local alias in the consumer registry. The alias defaults to the published name. --alias changes only the local namespace used for cache paths, updates, listing, and skill references; it does not rewrite publisher metadata. If a project shares alias-based references in ritebook.toml or ritebook.lock, collaborators and CI must register the source with the same alias. Registry schema v1 records whether an alias came from the published name or an explicit --alias. Existing entries are inferred conservatively and gain the explicit origin on their next successful registry write.

Refresh a registered index from its remembered Git source:

uv run ritebook indexes update platform-skills

List skills from all locally cached registered indexes:

uv run ritebook skills list

List skills from one local index alias:

uv run ritebook skills list --index platform-skills

Show cached skill descriptions when available:

uv run ritebook skills list --show-description

The skills list command is offline-first: it reads the local registry and each selected registry entry's cached ritebook-index.json file only. It does not clone, fetch, pull, scan publisher skill directories, or read raw SKILL.md files. Ritebook verifies each cached file's exact bytes against its registered SHA-256 digest before decoding or parsing it.

Non-empty output is grouped by local alias in a deterministic tree:

Indexes
├── platform-skills
│   ├── skill-a
│   └── browser/skill-b
└── data-skills
    └── query-helper

By default, the tree shows each skill's cached relative path, which can be copied after the local alias into skills install. With --show-description, Ritebook appends descriptions cached from publisher indexes when that metadata is present:

Indexes
└── platform-skills
    └── skill-a — Helps with platform workflows.

Relative paths identify skills within an index. Duplicate skill names are valid at different paths, such as backend/code-review and frontend/code-review; Ritebook does not fall back from a path to the final skill name.

When no registered cached skills are available, Ritebook prints:

No skills found

Consumer skill installation

Ritebook installs skills from registered Git-backed indexes. Direct skills install is offline against the currently registered commit-and-index binding. Repository skills sync refreshes every referenced local alias before resolving requirements and aborts without target or installation-state mutation if any refresh fails.

Install one fully qualified skill into an explicit target path:

uv run ritebook skills install platform-skills/code-review \
  --target .claude/skills/code-review

For skills published in a first-level collection, use the relative skill path shown by skills list after the local alias:

uv run ritebook skills install platform-skills/browser/runtime-verification \
  --target .claude/skills/runtime-verification

skills install resolves that path exactly and never expands a collection. A shorthand such as platform-skills/runtime-verification does not select platform-skills/browser/runtime-verification.

Ritebook copies the whole skill directory and creates missing target parent directories. An existing target can be replaced only when Ritebook already owns that exact target, its current tree still matches the last committed digest, and --force is provided. Unmanaged targets, locally edited owned targets, symlinks, special files, and dangerous paths are preserved or rejected:

Before planning or mutating targets, Ritebook validates the selected committed SKILL.md and requires its name and normalized description to match the digest-bound index entry.

uv run ritebook skills install platform-skills/code-review \
  --target .claude/skills/code-review \
  --force

Direct skills install runs write generated user-level installation state to:

~/.config/ritebook/installations.json

Direct installation state uses strict schema version 3 and includes ownership, verified commit, canonical-branch, and index-digest provenance, and the canonical installed-tree digest. On POSIX platforms, Ritebook writes both indexes.json and installations.json with mode 0600. Persisted source values never include standard-URL user-info, and indexes list defensively removes such user-info from displayed sources. Legacy or unsafe generated installation state is rejected and must be inspected and regenerated.

Tests and automation can override both the index registry and direct-install state paths:

uv run ritebook skills install platform-skills/code-review \
  --target .claude/skills/code-review \
  --registry-path <path-to-indexes.json> \
  --installation-registry-path <path-to-installations.json>

Repositories can declare repeatable skill installations in ritebook.toml:

[targets]
claude = ".claude/skills"
agents = ".agents/skills"

[[skills]]
name = "platform-skills/code-review"
target = "claude"

[[skills]]
name = "platform-skills/test-driven-development"
target = "agents"

[[skills]]
name = "company-agents/security-review"
target_path = "shared-agent-skills/security-review"

Refresh referenced indexes and exactly reconcile the default ritebook.toml in the current working directory:

uv run ritebook skills sync

Use --file to read a different requirements file, --force to rematerialize unchanged Ritebook-owned desired targets, and --lockfile to choose where generated lock state is written:

uv run ritebook skills sync \
  --file path/to/ritebook.toml \
  --force \
  --registry-path <path-to-indexes.json> \
  --lockfile <path-to-ritebook.lock>

target = "nickname" resolves to <targets.nickname>/<final-skill-name>. target_path is used exactly as the target path for that skill entry. Each skill entry must use exactly one of target or target_path. Sync targets are portable paths relative to the requirements-file directory; absolute paths and parent traversal are rejected.

In ritebook.toml only, a one-segment selector may select an implicit first-level collection. For example, platform-skills/browser expands to the indexed immediate children browser/<skill> in deterministic catalog-path order. A collection requirement must use target, so each child is installed below the target base by its final skill name; it cannot use target_path. Expansion never matches deeper descendants or searches by skills[].name. Direct skills install and skills contribute commands remain exact-only and never expand collections.

Sync transactionally writes strict schema-v3 generated state to ritebook.lock and local ownership state to .ritebook/installations.json. Commit ritebook.lock when a repository uses ritebook.toml; do not commit .ritebook/. The lock records the actual retained Ritebook-owned targets, their installed-tree digests, whether each target is still desired, materialization status, and any reconciliation issues. Complete no-change syncs produce byte-identical lock state. Partial reconciliation preserves independent successful changes, writes truthful state = "partial" data, reports each issue, and exits nonzero.

Schema-v1 and schema-v2 lockfiles and installation registries are rejected without automatic migration because they lack current ownership or canonical source-branch provenance. Inspect and remove or relocate legacy targets, then rerun skills sync to generate schema-v3 state. Because ritebook.lock is shared, Ritebook does not force a private file mode; it rejects credential-bearing standard source URLs before writing instead.

Each lock entry's requirement stores the exact catalog-qualified selector, such as platform-skills/browser/runtime-verification. Its skill_path and skill_file are different: they are safe paths relative to the source repository and include the published skills_root, such as skills/browser/runtime-verification and skills/browser/runtime-verification/SKILL.md.

Shared ritebook.lock entries require indexes registered from portable Git URLs. An index registered from a local repository path remains available for browsing and direct skills install, but ritebook skills sync rejects it before copying because relative, absolute, missing, or moved machine-local paths are not commit-safe. To migrate, register the same published index from its Git URL (using the same local alias when applicable), then rerun ritebook skills sync to regenerate the lockfile.

Contributing installed skill changes upstream

After editing a repo-local skill installed from ritebook.toml, prepare one reviewable upstream contribution from its ritebook.lock provenance:

uv run ritebook skills contribute platform-skills/code-review

The reference must be <local-alias>/<skill-path> and resolves by exact indexed path without falling back to skill_name. Collection-only references are not expanded. By default, Ritebook reads ritebook.lock from the current working directory and creates or reuses an isolated, Ritebook-owned checkout under ~/.cache/ritebook/contributions. Tests and automation can override both paths:

uv run ritebook skills contribute platform-skills/code-review \
  --lockfile <path-to-ritebook.lock> \
  --contribution-root <checkout-root>

Ritebook compares the installed skill with the current upstream skill. If the skill is unchanged, the command succeeds without creating a branch or commit:

No local changes to publish for platform-skills/code-review

When local changes exist, Ritebook copies only that skill into the isolated checkout, validates it, regenerates ritebook-index.json, and creates a local branch and commit. Branches use ritebook/<skill-path-with-dashes>-<YYYYMMDDHHMMSS> in UTC. For a portable Git URL source with a usable origin, output resembles:

Prepared contribution for platform-skills/code-review
Branch: ritebook/code-review-20260718201534
Commit: 0123456789abcdef0123456789abcdef01234567
Checkout: /path/to/contributions/0123456789abcdef/platform-skills-code-review-01234567
Next: cd /path/to/contributions/0123456789abcdef/platform-skills-code-review-01234567 && git push origin ritebook/code-review-20260718201534

Ritebook does not run the suggested command, push any branch, or open a merge request or pull request. Inspect the checkout and commit before following the suggested next step.

Contribution publishing accepts only desired schema-v3 lock entries with materialized or local_changes status, an installed-tree digest, and a portable git_url source. Schema-v1, schema-v2, issue-only, retained, and hand-written local_git_repo entries fail before any contribution clone or Git operation, with guidance to re-register by Git URL and regenerate the lockfile.

If the selected upstream skill path changed after the lockfile's source_revision, Ritebook stops instead of attempting to merge or overwrite the upstream change. Refresh/reinstall the skill and reconcile the changes manually before retrying.

By default, Ritebook stores registry metadata and cached index contents under:

~/.config/ritebook/indexes.json
~/.cache/ritebook/indexes/<local-alias>/<sha256-hex>/ritebook-index.json
~/.cache/ritebook/git/<source-cache-id>/

Local aliases are single path-safe kebab-case segments. Each validated index is stored as an immutable generation under its alias, keyed by the lowercase SHA-256 hex recorded in registry metadata. The registry file atomically switches to the new generation only after the complete cache file has been synchronized.

Tests and automation can override these locations:

uv run ritebook indexes add \
  --source <git-url-or-local-git-repo> \
  --registry-path <path-to-indexes.json> \
  --cache-root <cache-directory>

uv run ritebook indexes update <local-alias> \
  --registry-path <path-to-indexes.json> \
  --cache-root <cache-directory>

uv run ritebook skills list \
  --registry-path <path-to-indexes.json>

uv run ritebook skills list \
  --index <local-alias> \
  --registry-path <path-to-indexes.json>

Consumer registration requires published schema version 1 indexes to include index.name metadata. Legacy ritebook-index.json files without that metadata are rejected instead of guessing a name.

Publishing

The GitHub Actions workflow in .github/workflows/ci-cd.yaml runs formatting, linting, type checking, non-E2E tests, package builds, Docker E2E, patch releases, and PyPI publishing. Docker E2E runs as a separate mandatory job in parallel with the main quality-check job, and releases require both jobs to pass.

During the early project lifecycle, releases stay on the 0.1.x line and every accepted push to master increments the patch version. The CI/CD workflow uses Python Semantic Release to:

  1. run the quality gate,
  2. bump pyproject.toml from 0.1.x to the next patch version,
  3. update uv.lock to the same package version,
  4. commit both version files together in a release commit,
  5. retain that release commit under the matching v0.1.x tag without pushing the generated commit to protected master,
  6. publish a GitHub release without maintaining a changelog, and
  7. publish the built distributions to PyPI in the same workflow run.

The version tag identifies the exact generated source commit used for the GitHub and PyPI release, while master remains on the already-tested change commit. When the project is ready to move beyond patch-only 0.1.x releases, the same Semantic Release tooling can be used for normal commit-derived SemVer releases.

The default branch requires the stable Quality checks and Docker E2E status checks. The repository owner retains an explicit ruleset bypass for the current solo-maintainer direct-push workflow; CI verifies each accepted push, and the release job still waits for both checks to pass. Other actors and pull requests remain subject to the required checks. The release workflow pushes only the generated version tag after both prerequisite jobs succeed, so it does not need to update the protected branch.

Publishing uses PyPI Trusted Publishing through GitHub Actions OIDC. Before the first release, configure a trusted publisher for this repository in the PyPI project settings:

  • Repository owner: ondrej-winter
  • Repository name: ritebook
  • Workflow filename: ci-cd.yaml
  • Environment name: pypi

Architecture direction

Future business capabilities should be implemented as vertical feature slices under src/ritebook/features/, keeping domain, application ports/use cases, and adapters separated according to hexagonal architecture principles.

Metadata

Release files for ritebook 0.1.51

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ritebook 0.1.51
File Size Uploaded
ritebook-0.1.51.tar.gz 461.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ritebook 0.1.51
File Interpreter ABI Platform
ritebook-0.1.51-py3-none-any.whl Python 3 none any Details

Total release size: 635.1 kB

Release files / ritebook-0.1.51.tar.gz

Download URL ritebook-0.1.51.tar.gz
Size 461.8 kB
Tags Source
SHA-256 checksum
How to use checksums
47bad57cbda0a978f61b0d3b12ae8b7de24bf1ef4ec90dcfb9f750301472283b
BLAKE2b-256 checksum
How to use checksums
b00873dd547eaf98b07257955ac1e90ba5e074802cada6d4127efcc0c63d0049
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release files / ritebook-0.1.51-py3-none-any.whl

Download URL ritebook-0.1.51-py3-none-any.whl
Size 173.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d8be1f4ff087235dd48fac7907d6bdeb27fff8fe71c2a2d70d95ae7fe9dabc41
BLAKE2b-256 checksum
How to use checksums
fd9313d66dcbada48321ef65f2c0ca9516d1f669c0b460b1f778099541b46a3e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.63

2 release files

0.1.62

2 release files

0.1.61

2 release files

0.1.60

2 release files

0.1.59

2 release files

0.1.58

2 release files

0.1.57

2 release files

0.1.56

2 release files

0.1.55

2 release files

0.1.54

2 release files

0.1.53

2 release files

0.1.52

2 release files

This release

0.1.51 This release

2 release files

0.1.48

2 release files

0.1.47

2 release files

0.1.46

2 release files

0.1.45

2 release files

0.1.44

2 release files

0.1.43

2 release files

0.1.42

2 release files

0.1.41

2 release files

0.1.40

2 release files

0.1.39

2 release files

0.1.38

2 release files

0.1.37

2 release files

0.1.36

2 release files

0.1.35

2 release files

0.1.34

2 release files

0.1.33

2 release files

0.1.32

2 release files

0.1.31

2 release files

0.1.30

2 release files

0.1.29

2 release files

0.1.28

2 release files

0.1.27

2 release files

0.1.26

2 release files

0.1.25

2 release files

0.1.24

2 release files

0.1.23

2 release files

0.1.22

2 release files

0.1.21

2 release files

0.1.20

2 release files

0.1.19

2 release files

0.1.18

2 release files

0.1.17

2 release files

0.1.16

2 release files

0.1.15

2 release files

0.1.14

2 release files

0.1.13

2 release files

0.1.12

2 release files

0.1.11

2 release files

0.1.10

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page