Metadata-Version: 2.4
Name: vekna
Version: 0.7.0
Summary: Run coding agents as rituals: ordinary Python programs whose steps you control
License-Expression: BSD-3-Clause
License-File: LICENSE
Keywords: agents,claude,llm,orchestration,workflow
Author: Radosław Ganczarek
Author-email: radoslaw@ganczarek.in
Requires-Python: >=3.11,<4
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Build Tools
Classifier: Typing :: Typed
Provides-Extra: trial
Requires-Dist: claude-agent-sdk (>=0.2,<1.0)
Requires-Dist: click (>=8.3.2,<9.0.0)
Requires-Dist: pydantic (>=2.12.5,<3.0.0)
Requires-Dist: pytest (>=8.0,<10.0) ; extra == "trial"
Requires-Dist: typing-extensions (>=4.16,<5.0)
Project-URL: Changelog, https://github.com/fancysnake/vekna/blob/main/CHANGELOG.md
Project-URL: Documentation, https://vekna.fancysnake.dev
Project-URL: Homepage, https://vekna.fancysnake.dev
Project-URL: Issues, https://github.com/fancysnake/vekna/issues
Project-URL: Repository, https://github.com/fancysnake/vekna
Description-Content-Type: text/markdown

# vekna

Run coding agents as **rituals**: ordinary Python programs whose steps you
control and whose agent calls happen inside those steps. Agents run
permissively *within* a step; determinism lives at the step boundaries.

Documentation is at [vekna.fancysnake.dev](https://vekna.fancysnake.dev).

## Install

```bash
pip install vekna
```

Python 3.11+. Testing your rituals needs the `trial` extra:
`pip install 'vekna[trial]'` — quoted, because zsh globs the brackets.

## A ritual

Put a `rituals.py` in your project — or a `rituals/` package, split however you
like, once one file stops being enough:

```python
from typing import Annotated

from pydantic import BaseModel, Field

from vekna.folio.coding import coding
from vekna.folio.shell import shell
from vekna.lexicon import Transition, done, goto, ritual, step


class FixTests(BaseModel):
    # A retry budget counts down to zero, so the CLI rejects a negative one
    # rather than letting `--bound -1` run until the step backstop.
    bound: Annotated[int, Field(ge=0)] = 3


class Attempt(BaseModel):
    left: int


class Verdict(BaseModel):
    outcome: str


@step
async def fix(state: Attempt) -> Transition:
    result = await shell("mise run test:py")
    if result.exit_code == 0:
        return done(Verdict(outcome="green"))
    if state.left <= 0:
        return done(Verdict(outcome="gave up"))
    await coding(f"The test suite fails:\n{result.stdout}\nFix it.")
    return goto(fix, Attempt(left=state.left - 1))


# `def`, not `async def`: naming the first step has nothing to await. A step or
# entrypoint is written whichever way its body needs.
@ritual("fix_tests")
def fix_tests(components: FixTests) -> Transition:
    return goto(fix, Attempt(left=components.bound))
```

Then cast it:

```bash
vekna cast fix_tests --bound 5
```

Output streams live as a tree of rites — one node per step, one nested under
it per medium call, with the agent's own output indented beneath. The last
line is the cast's result, as JSON:

```text
result: {"outcome":"green"}
```

## Commands

| Command | What it does |
| --- | --- |
| `vekna cast <ritual> [--<component> value …]` | Run a ritual from `rituals.py` |
| `vekna cast --prompt "<text>"` | One-shot cast on the coding medium, no `rituals.py` needed |
| `vekna rituals list` | Every ritual and the options it takes |
| `vekna rituals show <ritual>` | A ritual's components and its step graph |

## Architecture

[GLIMPSE](https://glimpse.fancysnake.dev/) layering, enforced by
[`import-linter`](https://import-linter.readthedocs.io/). See
[`docs/architecture.md`](docs/architecture.md) and
[`docs/README.md`](docs/README.md) for where the ideas live.

## Development

```bash
mise run test:py     # all tests
mise run check:py    # the loop while you work: format, lint, tests
mise run fullcheck   # the gate before you push: adds diff-coverage and tingle
```

## Licence

BSD-3-Clause.

