Skip to main content

ritebook

Ritebook is a Python CLI for validating, installing, synchronizing, and contributing Agent Skills, and for publishing, registering, and browsing Git-backed skill indexes. Publishers generate reviewable ritebook-index.json files; consumers install skills from registered indexes.

Specifications

This README covers setup and common workflows. Specifications define current contracts and their design rationale. Repository instructions govern agent contributions.

Requirements

  • Python 3.13 or newer
  • uv 0.13.0 or newer for host development and CI dependency management and command execution
  • GNU Make for the shared local and CI task interface
  • Git for index, installation, and contribution workflows
  • Docker for the complete verification gate

Development setup

Install development dependencies:

make setup

Setup checks that uv.lock matches pyproject.toml; it does not update the lockfile implicitly.

Install the Git pre-commit hook:

make install-hooks

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

make pre-commit

Pre-commit checks file hygiene, Ruff formatting and linting, ty types, Import Linter architecture contracts, and tests. It complements the full quality gate.

The Ruff hooks check without rewriting Python files. Ruff, ty, and pytest run when Python files, pyproject.toml, uv.lock, Makefile, or .pre-commit-config.yaml change.

Run the standard local quality gate:

make quality

This checks formatting, Ruff linting, ty types, Import Linter architecture contracts, and the default non-E2E pytest suite. make help lists the corresponding leaf targets and formatting or lint-fix commands.

make architecture enforces inward dependencies, private slice boundaries, shared-kernel and composition-root isolation, and an explicit I/O-library denylist, including imports under TYPE_CHECKING. See repository instructions for boundary rules. Dynamic imports and side effects through permitted APIs still need code review.

Plain pytest 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.

For implementation changes, run the complete verification gate before handoff:

make verify

make verify runs the standard quality gate, checks lockfile freshness, builds distributions, and builds and runs Docker E2E. It fails if Docker is unavailable. make docker-e2e runs only the Docker build and test steps.

Dockerfile.e2e tests an installed wheel in a clean consumer environment, with separate locked test tooling and no project source or developer-local state. Tests run unprivileged against local Git fixtures. CI runs the same Docker gate in parallel with non-E2E quality checks. The Docker E2E specification defines isolation, networking, and the limits of this verification.

Build the package distributions without running the other gates:

make build

The source distribution excludes the externally sourced .agents/ tree.

Audit locked runtime and development dependencies for known vulnerabilities and adverse package status:

make deps-audit

This target uses uv's preview audit command and requires network access. It does not update uv.lock.

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

Validate skill headers under an explicit skills root:

uv run ritebook skills lint --root <path>

skills lint finds visible SKILL.md candidates and reports invalid nested declarations and symlinked candidates without following symlinks.

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. Every metadata value must be a string; nested metadata is invalid. An empty catalog is valid. Successful lint runs report Checked N skill(s) without writing an index. The Skill Linter specification defines the full header and diagnostic contract.

The Git-tracked .agents/ tree contains externally sourced skills. Their owning collections validate them; Ritebook's local, pre-commit, and CI quality gates exclude the tree, including file-hygiene hooks.

Generate an index from the validated catalog:

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

Run indexes publish from the repository directory that will contain ritebook-index.json. The required --skills-root may be relative or absolute, but must resolve to that directory or a descendant. Ritebook stores it as a portable repository-relative skills_root. The required --name must be a stable single-segment kebab-case identifier such as company-skills. It becomes index.name in the generated index and the default consumer local alias.

indexes publish uses the same validation flow as skills lint. It writes only when every discovered skill is valid, using that pass's immutable snapshot.

Review the generated ritebook-index.json before committing it with the related skill changes. The publisher specification defines generation and output safety; the shared catalog contract defines schema-v1 fields, paths, and input limits.

Consumer index registry

Register and refresh Git-backed Ritebook skill indexes, then browse and install their skills.

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 v2 records whether an alias came from the published name or an explicit --alias. Legacy schema-v1 registries require the force recovery described below.

Refresh a registered index from its remembered Git source:

uv run ritebook indexes update platform-skills

Force a clean rebuild of Ritebook-owned consumer state when the managed clone or cached index may be damaged:

uv run ritebook indexes update platform-skills --force
uv run ritebook indexes update --all --force

For Git URL registrations, --force fetches through a fresh managed clone and switches the registry only after the committed root ritebook-index.json validates. For local repository registrations, it re-reads the clean committed repository without deleting or mutating it. The previous coherent registry/cache binding remains usable if rebuilding fails. A superseded managed clone is removed only after no other registry alias references it. This flag repairs consumer state; it does not generate or publish an upstream index.

The same forced update recovers schema-v1 indexes.json registrations from their saved aliases and source locations. It discovers each source's current branch and rebuilds validated provenance. Failed registrations remain saved for retry; rerun indexes update --all --force after fixing their sources. The registry becomes schema v2 once all registrations are rebuilt. Ordinary reads remain unavailable until recovery completes. Use --all to finish a registry with multiple legacy entries. Legacy cache paths are left untouched.

List registered indexes:

uv run ritebook indexes list

List skills from all 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

skills list works offline from the local registry and digest-verified cached indexes. It does not refresh sources or read skill files.

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

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

The tree shows each skill's catalog-relative path. Append it to the local alias for skills install. With --show-description, Ritebook appends the cached description:

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

Registry storage and overrides

Default registry and cache locations:

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

<registry-id> is the SHA-256 hex of the canonical registry path, separating caches even when registries share aliases and a cache root. 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. The Index Registry specification defines registration, refresh, cache safety, and recovery.

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 skill

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. platform-skills/runtime-verification does not select platform-skills/browser/runtime-verification.

Ritebook copies the whole skill directory and creates missing target parent directories. Without --force, an existing target is preserved and direct install exits nonzero. With --force, Ritebook transactionally replaces the exact existing directory even when it is unmanaged or locally edited. The previous tree is retained for rollback until ownership state commits. Symlinks, regular files, special files, dangerous paths, unsafe ancestry, and targets that change after inspection are still 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 writes user-level installation state to:

~/.config/ritebook/installations.json

Direct installation state uses strict schema version 3 and records ownership, verified source provenance, and the installed-tree digest. On POSIX, Ritebook writes indexes.json and installations.json with mode 0600. Persisted sources exclude standard-URL user-info; indexes list also strips it from displayed sources. Legacy or unsafe installation state requires inspection and regeneration.

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>

Sync declared skills

Declare repeatable repository 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 process the skills declared by the default ritebook.toml in the current working directory:

uv run ritebook skills sync

Use --file to read a different requirements file, --force to replace any existing safe declared target directory with verified index content, 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 specifies the exact target path for that skill entry. Each 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 uses the selected schema-v3 ritebook.lock as its only persistent installation record and creates no .ritebook/ bookkeeping. Commit the lockfile when a repository uses ritebook.toml. It records current owned declarations, installed-tree digests, provenance, status, and processing issues. Complete no-change syncs produce byte-identical lock state. Skipped targets produce state = "partial", diagnostics, and a nonzero exit while independent successful changes remain installed.

Locally modified skills are preserved with their recorded baseline. Diagnostics show how to prepare a local contribution or explicitly replace the skill with --force. If the selected lockfile is missing, sync installs missing targets but preserves existing directories, even when they match the current index. Review every current declaration before a force rerun: sync --force replaces all selected targets, discarding local edits and unmanaged contents.

Sync failures and legacy state

Run one mutating installation command at a time for the affected project and targets. Sync acquires no installation operation lock and provides no concurrent writer guarantee. Index-registry locking and direct-install recovery remain in place.

Sync stages complete trees, revalidates each target, and atomically replaces the lockfile document. It has no whole-sync rollback or automatic next-run recovery. A failed or interrupted run may leave completed replacements with an older or absent lockfile, an absent target during a directory swap, or temporary sibling staging/previous-tree artifacts. Inspect targets, the lockfile, and reported retained paths; rerun normally to identify preserved mismatches or unmanaged targets. After reviewing or backing up valuable content in every declaration, use --force to establish verified baselines. Inspect interrupted-run artifacts before removing them; later syncs neither replay nor delete them automatically.

Existing .ritebook/ files are ignored and left untouched. Before retiring old state, finish any pending recovery with the previous Ritebook version or inspect and reconcile it manually. A journal, transaction data, or sibling backup may be the only copy of prior content. Once no old operation or recovery is pending, remove only confirmed obsolete generated ledgers, lock files, and completed recovery artifacts; preserve unrelated files. Do not delete the whole directory without inspecting its contents. See the installation specification for the full sync and direct-install contracts.

Removing an entry from ritebook.toml makes the next sync remove it from generated lock state while leaving its directory untouched. Delete or archive an undeclared skill directory when it is no longer wanted. If declared again later, the path is unmanaged and requires --force to replace.

Older valid schema-v3 state may contain desired: false entries. Ritebook still parses those files, but such entries do not establish ownership and disappear from newly generated state unless the target is currently declared.

Schema-v1 and schema-v2 lockfiles and installation registries are rejected, including with --force; they lack current ownership or canonical source-branch provenance. Inspect and back up legacy state and targets, then move the rejected state file aside before reinstalling. A fresh skills sync installs missing targets and preserves existing directories until reviewed force replacement. Shared ritebook.lock files have no forced private mode; Ritebook rejects credential-bearing standard source URLs before writing.

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 machine-local paths are not commit-safe. To migrate, register the same published index from its Git URL, keeping the 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 a branch, or open a merge request or pull request. Inspect the checkout and commit before following it. The Skill Contribution specification defines provenance checks, checkout ownership, and failure behavior.

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 without merging or overwriting the upstream change. Refresh/reinstall the skill and reconcile the changes manually before retrying.

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 initial development, 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. atomically push that release commit to master with the matching v0.1.x tag,
  6. publish a GitHub release without maintaining a changelog, and
  7. publish the built distributions to PyPI in the same workflow run.

The release commit becomes the new master head, keeping repository metadata in sync with Git tags and published packages. Its commit body contains [skip ci], so the generated push does not start another CI/CD workflow after the parent commit has passed quality and Docker E2E. The release job also skips commits whose message starts with release: bump version to to guard against release loops. When the project is ready to move beyond patch-only 0.1.x releases, the same Semantic Release tooling can support 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; other actors and pull requests remain subject to the required checks. The release job uses a short-lived installation token from the repository-scoped release GitHub App, which has an explicit Master ruleset bypass. Before generating a release, the job verifies that origin/master still identifies the triggering commit. It then uses a non-force, atomic push for the generated release commit and tag, so a concurrent update fails without partially publishing Git refs.

The GitHub App client ID is stored in the repository variable RITEBOOK_RELEASE_APP_CLIENT_ID; its private key is stored as the pypi environment secret RITEBOOK_RELEASE_APP_PRIVATE_KEY. The app must remain installed only on this repository with Contents: read and write permission.

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

Metadata

Release files for ritebook 0.1.62

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.62
File Size Uploaded
ritebook-0.1.62.tar.gz 327.9 kB Details

Built distribution (wheel)

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

Total release size: 509.7 kB

Release files / ritebook-0.1.62.tar.gz

Download URL ritebook-0.1.62.tar.gz
Size 327.9 kB
Tags Source
SHA-256 checksum
How to use checksums
c998eb4cbd8173489a277b3017d130da3d32208ce1c669733009f96eafb859b8
BLAKE2b-256 checksum
How to use checksums
6b2eefe35afc38083c5652f34458177d281ef23a5e54b626449aef057a41f0b0
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 16, 2026.

Transparency log

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

Download URL ritebook-0.1.62-py3-none-any.whl
Size 181.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e1cb95973b147a8ab5c2b6ef93e4fc1c46e55797f291f77ac8d7882deffc27f5
BLAKE2b-256 checksum
How to use checksums
27c69055dbce3170115a5b294e9d97017cc17cba55d7ee2654ec8cc01f34f824
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 16, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.63

2 release files

This release

0.1.62 This release

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

0.1.51

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