Rýni
An opinionated linter for agent harnesses, written in Python.
Getting started · Usage · Rules · Plugins · Contributing
Agent instructions and skills are part of your codebase. Rýni helps you keep them consistent as your project grows, enforcing conventions for how harnesses are structured, shared, and maintained, with actionable diagnostics when they drift.
- Validate skills. Check
SKILL.mdfrontmatter for required metadata. - Keep instructions in sync. Check that
CLAUDE.mdlinks to its siblingAGENTS.md. - Check skill organization. Find misplaced skill sources, tracked installations, and missing or inconsistent local installations.
- Choose your rules. Run the full catalog or select individual checks by ID.
- Use it in automation. Get structured JSON output and predictable exit codes.
- Add your own conventions. Distribute custom rules as Python packages.
Rýni's built-in checks run locally, without a model or API key, and do not modify files. The project is in early development; the CLI and plugin API may change.
Getting started
Rýni requires Python 3.14 or later. Git is required for the installed-skills
ignore check (SKILL002).
Install from PyPI with uv:
uv tool install ryni
Or with pip:
pip install ryni
Then run Rýni in the project you want to check:
ryni check .
Usage
Check files, directories, or multiple paths:
ryni check .
ryni check skills/ AGENTS.md CLAUDE.md
List available rules:
ryni rule
Produce JSON for scripts and CI:
ryni check . --output-format json
The JSON report contains checked_files, findings, and errors. Each finding
includes a path, line number, rule ID, and message. For repository-wide checks,
checked_files also includes repository roots.
| Exit code | Meaning |
|---|---|
0 |
Checks completed without findings. |
1 |
Checks completed with findings. |
2 |
Invalid invocation, execution error, or incomplete checks. |
Reports preserve findings when another check fails. Runs with no applicable targets explicitly report zero checked targets.
Configuration and discovery
All registered rules, including installed plugins, run by default. Use --select
to choose a comma-separated list of rule IDs. Configuration is currently through
CLI options; Rýni does not read a configuration file.
File rules search the supplied paths recursively and select files by name.
Discovery skips .git, .venv, venv, node_modules, __pycache__, .agents,
.claude, .codex, and .github. You can check those locations by passing them
explicitly. Directory symlinks are not traversed, and repeated paths are deduplicated
without resolving symlinks. File discovery does not apply .gitignore patterns.
Repository rules inspect the nearest enclosing Git checkout for each supplied path, once per repository. Outside a Git checkout, they use the supplied directory or the file's parent directory. Selecting a single file does not narrow the scope of repository rules. Select only file rules when you want checks limited to those paths.
Rules
Rýni includes five deterministic rules:
| ID | Name | Checks |
|---|---|---|
SKILL001 |
skill-frontmatter |
SKILL.md starts with valid YAML frontmatter containing non-empty string name and description fields. |
AGENT001 |
claude-agents-symlink |
Each existing CLAUDE.md is a symlink resolving to an existing sibling AGENTS.md. |
SKILL002 |
installed-skills-gitignored |
Installed skill directories for configured agents are covered by repository .gitignore rules and are not tracked. |
SKILL003 |
local-skills-source-location |
Locally owned skills live under a directory named skills/, with nested categories allowed. |
SKILL004 |
local-skills-installed |
Local skills have a matching local-source skills-lock.json entry and matching installed SKILL.md files for agents configured in their source scope. |
SKILL001 allows additional frontmatter fields and checks structure only.
AGENT001 allows AGENTS.md without a corresponding CLAUDE.md. SKILL004
accepts copies and symlinks and checks installation evidence, not command history.
These rules express Rýni’s opinions about maintaining agent harnesses. They do not assess instruction quality or agent behavior. Semantic and hybrid rule kinds are reserved for future use; agent execution is not implemented.
Plugins
Extend Rýni with rules for your team's conventions. A Python package can export a
ryni.models.Rule instance through the ryni.rules entry-point group.
In the plugin's pyproject.toml:
[project.entry-points."ryni.rules"]
example = "my_rules:EXAMPLE"
In my_rules.py:
from pathlib import Path
from ryni.models import Finding, Rule, RuleKind
def check_readme(path: Path) -> list[Finding]:
if not path.read_text(encoding="utf-8").strip():
return [Finding(str(path), 1, "CUSTOM001", "README must not be empty.")]
return []
EXAMPLE = Rule(
id="CUSTOM001",
name="nonempty-readme",
kind=RuleKind.DETERMINISTIC,
description="README.md must contain text.",
filename="README.md",
evaluate=check_readme,
)
Install the plugin in the same Python environment as Rýni. Both check and rule
load installed plugins; duplicate rule IDs are rejected. Plugins execute Python
code, so only install packages you trust.
Evaluators receive a path and return a list of findings. They must not print or
modify files. An evaluator exception produces an execution error and exit code 2,
while other checks continue.
Contributing
Bug reports, rule proposals, documentation improvements, and pull requests are welcome. For a bug report, include the command you ran, a minimal example, and the expected and actual output. For a new rule, describe the convention it checks and show examples that should pass and fail.
Rýni currently lives in tools/ryni in this repository. From the repository root:
uv run ryni --help
uv run pytest tools/ryni/tests
uv run ruff check tools/ryni
The CLI handles commands and output, the
engine discovers targets and runs checks, and the
rule catalog registers built-in rules and loads plugins.
Add built-in rules under src/ryni/rules/, register them in BUILTINS, and include
tests under tests/.
Metadata
Release files for ryni 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| ryni-0.1.0.tar.gz | 17.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ryni-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 33.3 kB
Release files / ryni-0.1.0.tar.gz
| Download URL | ryni-0.1.0.tar.gz |
|---|---|
| Size | 17.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
01ffe331c85bba90eeb4696560837c5366698faa94200e294573f076ffdc65ed
|
|
BLAKE2b-256 checksum How to use checksums |
a6af691f1284cddcf42ddf69553283d274edd84c5802b2632d4f2856befd8042
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / ryni-0.1.0-py3-none-any.whl
| Download URL | ryni-0.1.0-py3-none-any.whl |
|---|---|
| Size | 15.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d93e08cd567936ced986aa72a927a0310d75be259655088759e5799f25d73c37
|
|
BLAKE2b-256 checksum How to use checksums |
9064493f97319df3cacf8d8fd1d5e647b03411d70447bb511522dd1c609bfe83
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for ryni 0.1.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.