Metadata-Version: 2.5
Name: neva-boost
Version: 0.2.0
Summary: Composes the Neva framework's agent guidelines into your project.
Requires-Python: >=3.12
Requires-Dist: packaging>=24.0
Requires-Dist: python-neva>=4.1.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: testing
Requires-Dist: pytest>=9.0.2; extra == 'testing'
Description-Content-Type: text/markdown

# neva-boost

Composes the [Neva](https://pypi.org/project/python-neva/) framework's agent
guidelines into your project.

Neva's packages ship their own guidance inside their wheels. This tool finds it,
keeps only what applies to the versions you actually installed, and writes it
where your coding agent will read it. The point is that nobody hand-maintains a
pile of skills describing a framework that keeps moving: the guidance ships with
the code it describes, and you regenerate.

## Install

```bash
uv add --dev neva-boost
uv run neva-boost install
```

That writes one Claude Code skill per topic under `.claude/skills/`. For an
agent-agnostic project, or as a fallback alongside the skills:

```bash
uv run neva-boost install --target agents            # a marked region in AGENTS.md
uv run neva-boost install --target all               # every target
uv run neva-boost install --target agents --target claude   # or name them
```

Re-run it after upgrading a Neva package. `--dry-run` reports what would change
without touching anything.

## How it composes

### Contributing from a package

```bash
cd neva-fastapi
uv run neva-boost new neva-fastapi
```

That writes the manifest and a template fragment, and prints the two lines to add
to `pyproject.toml`. Don't hand-write the layout.

A contributing package declares a manifest through the `neva.boost` entry-point
group:

```toml
[project.entry-points."neva.boost"]
neva = "neva.guidelines:MANIFEST"          # the core
neva-fastapi = "neva.fastapi.guidelines:MANIFEST"
```

The manifest is read **structurally** -- a `name` string and a `fragments`
directory. Nothing here is imported by the packages that contribute, so the
framework-agnostic core does not depend on its own tooling.

**The entry-point name is the package's label**, and it is what gets rendered:
skills are named `<label>-<id>`, so `neva-events` and `neva-fastapi-routing`. It
is deliberately separate from the distribution name, which is only what you would
`uv add` -- the core is `neva` to people and `python-neva` on PyPI, where the
short name was already taken. Choosing it in your own `pyproject.toml` is the
point; it must be a kebab-case slug, because it becomes part of a directory name.

Each fragment is a Markdown file with frontmatter:

```markdown
---
id: database-transactions
title: Database and transactions
requires: python-neva>=4.1
triggers: [database query, opening a transaction, DB.begin, savepoint, rollback]
priority: 40
verified_by: [tests/database/test_transaction.py]
---

A transaction-first wrapper over SQLAlchemy async. Never build an engine...
```

`requires` is checked against the installed version, so a project on an older
release is given the guidance for the release it has rather than guidance for one
it does not. `triggers` becomes the skill description an agent routes on --
that is what makes one skill per topic cheaper than one document per framework.
`priority` orders the output. Nothing reserves a range -- the core's own
fragments sit at 10 to 60, so a plugin that wants to be read after them picks
something above that.

An `id` has to be unique among the fragments that *apply*, not among the files on
disk. A package can therefore ship one file per release line for the same topic
and let `requires` choose between them:

```
fragments/transactions-legacy.md   requires: python-neva>=3.0,<4.0
fragments/transactions.md          requires: python-neva>=4.0
```

Exactly one applies, so exactly one is rendered. Two fragments that share an id
*and* both apply is a real conflict, and it is reported.

## Keeping the guidance honest

`verified_by` names the tests that prove a fragment's claims. `check` holds them
to it, and belongs in the contributing package's CI:

```bash
uv run neva-boost check neva/guidelines/fragments
```

It fails on malformed frontmatter, an empty `verified_by`, any cited test path
that no longer resolves, and two files sharing an `id` with the same `requires`.
It deliberately allows the release-line variants above -- a conflict between two
overlapping-but-different specifiers surfaces at install time, in the environment
where they actually collide. A behaviour change that lands
without touching the fragment that describes it will usually take a cited test
with it -- which is the point. Documentation that cannot go stale silently is
worth more than documentation that is merely correct today.

Run it with no arguments to check what is installed instead; only fragments
living inside `--root` have their `verified_by` paths resolved, since a fragment
from a wheel names tests that wheel does not ship.

## Inspecting

```bash
uv run neva-boost list
```

Prints every applicable fragment with its package, priority and topic, then
anything skipped and why -- `requires 'python-neva>=4.2', but python-neva 4.1.0
is installed` is a normal answer, not a failure.

## What it writes

`--target claude` owns `.claude/skills/<label>-<id>/SKILL.md`. Files it
generates carry a marker comment, and only files carrying it are ever deleted --
a skill you wrote by hand is left alone even if it sits where ours would.

When a fragment goes away, the generated `SKILL.md` is deleted and its directory
follows **only if that was the last thing in it**. Anything you added alongside it
is yours, and a directory that keeps something of yours keeps it.

`--target agents` owns the region between `<!-- BEGIN neva-boost -->` and
`<!-- END neva-boost -->` in `AGENTS.md`, and preserves everything outside it. If
only one of the two markers survives an edit or a merge, it refuses rather than
guess.

## Develop

```bash
uv sync --all-extras
poe lint && poe fmt && poe tc && poe test
```

Commits follow Conventional Commits with gitmoji via `cz commit`; releases are
cut with `cz bump`.
