Metadata-Version: 2.4
Name: vekna
Version: 1.0.0
Summary: Run coding agents as rituals: ordinary Python programs whose steps you control
License-Expression: BSD-3-Clause
License-File: LICENSE
Keywords: agents,ai-agents,automation,claude,claude-code,coding-agents,llm,orchestration,tui,workflow
Author: Radosław Ganczarek
Author-email: radoslaw@ganczarek.in
Requires-Python: >=3.11,<3.16
Classifier: Development Status :: 5 - Production/Stable
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: Programming Language :: Python :: 3.15
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: textual (>=8.2.8,<9.0.0)
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

[![PyPI](https://img.shields.io/pypi/v/vekna)](https://pypi.org/project/vekna/)
[![Python](https://img.shields.io/pypi/pyversions/vekna)](https://pypi.org/project/vekna/)
[![CI](https://github.com/fancysnake/vekna/actions/workflows/ci.yml/badge.svg)](https://github.com/fancysnake/vekna/actions/workflows/ci.yml)
[![Coverage](https://codecov.io/gh/fancysnake/vekna/graph/badge.svg)](https://codecov.io/gh/fancysnake/vekna)
[![License](https://img.shields.io/pypi/l/vekna)](https://github.com/fancysnake/vekna/blob/main/LICENSE)

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).

![The vekna window: casts by ritual on the left, a pr_fix cast waiting on a
push decision on the right](https://raw.githubusercontent.com/fancysnake/vekna/main/docs/assets/tui.svg)

## 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 Done, 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


# The return annotation is the step's exits: back to `fix`, or finished with a
# `Verdict`. mypy checks every return against it, so a mis-wire fails the type
# check rather than a cast.
@step
async def fix(state: Attempt) -> Attempt | Done[Verdict]:
    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 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) -> Attempt:
    return 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"}
```

## Claude Code plugin

The `ritual-scribe` skill teaches Claude Code to write rituals. Install it from
this repo's marketplace:

```bash
claude plugin marketplace add fancysnake/vekna
claude plugin install ritual-scribe@vekna
```

## 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 |

## Contributing

[`CONTRIBUTING.md`](https://github.com/fancysnake/vekna/blob/main/.github/CONTRIBUTING.md)
covers setup, the check loop, where code goes, and pull requests.
[`docs/README.md`](https://github.com/fancysnake/vekna/blob/main/docs/README.md)
is where the ideas live.

## Licence

BSD-3-Clause.

