Skip to main content

Spec-driven code generation framework: write intent as decorated Python stubs, generate implementations and tests with LLMs.

Project description

Jaunt

William Blake, 'The Tyger' from Songs of Experience (1794). The Metropolitan Museum of Art, Open Access.
William Blake, The Tyger, plate 42 from Songs of Experience (1794). The Metropolitan Museum of Art, Open Access.

Tyger Tyger, burning bright, In the forests of the night; What immortal hand or eye, Could frame thy fearful symmetry?

-- William Blake, via Alfred Bester's The Stars My Destination

Jaunt is a small Python library + CLI for spec-driven code generation. You write the intent — a signature and a docstring — and Jaunt writes the implementation under __generated__/ using the OpenAI Codex CLI (codex exec).

Call jaunt.magic_module(__name__) once at the top of a file and every top-level stub below it becomes a spec, with no per-symbol decorators:

import re
import jaunt

jaunt.magic_module(__name__, prompt="All parsers are RFC 5322 strict.")

EMAIL_RE = re.compile(r"...")           # real body → handwritten, kept as-is

class Email:
    """Email with from_, to, subject, body. Validates on construction."""
    # docstring-only class → Jaunt designs and writes the whole class

def parse_email(raw: str) -> Email:
    """Parse an RFC 5322 payload into an Email. Raise ValueError on malformed
    input, naming the first offending header."""
    ...

def _debug(email: Email) -> str:        # real body → handwritten helper
    return f"<{email.from_} -> {email.to}>"

jaunt build fills in Email and parse_email and leaves EMAIL_RE and _debug alone. The scan governs only top-level stubs — a def or class whose body is ..., a bare docstring, pass, or raise NotImplementedError. A spec body never runs; an unbuilt one raises a clear error on first use. If your type checker flags ... under a concrete return annotation, either relax that rule on your spec roots or write raise NotImplementedError instead — the two forms digest identically, so switching between them never restales. Anything with a real body, or carrying a non-jaunt decorator like @property or @dataclass, is handwritten context the model reads but never regenerates.

The precision layer: @jaunt.magic

Reach for the decorator when you want per-symbol control. @jaunt.magic(deps=..., prompt=...) overrides the module defaults for one symbol — the module defaults still merge in key by key, and the per-symbol value wins. The decorator is also how you opt a symbol in against the scan: a stub carrying @property, or an intentionally-empty function marked @jaunt.preserve, stays handwritten until you add @jaunt.magic.

@jaunt.magic(deps=[parse_email], prompt="Reuse parse_email per line.")
def parse_mbox(raw: str) -> list[Email]:
    """Split an mbox payload on `From ` lines and parse each message."""
    ...

What you get

  • Module-level magicjaunt.magic_module(__name__) turns every top-level stub in a file into a spec. Mixed files (specs plus handwritten helpers) are first-class. Decorate individual symbols with @jaunt.magic / @jaunt.test when you want per-symbol overrides.
  • Whole-class specs — a class-level spec can be docstring-only (Jaunt designs the API), stub methods only, or a mix. Each method sits in one of three tiers: @jaunt.preserve keeps it verbatim, @jaunt.sig locks its signature while Jaunt writes the body, and an unmarked guidepost stub lets the model adapt the signature.
  • Parallel, DAG-scheduled builds — modules build over the dependency graph with a critical-path-first ready queue. A module starts generating the instant its dependencies finish, with no wave barriers, up to [build] jobs workers at once. A failed module skips only its dependents; the rest of the graph keeps building.
  • Smart change detection — freshness is a SHA-256 digest over the AST-normalized contract, so reformatting, comment edits, and quote-style churn never trigger a rebuild. Staleness is dependency-aware: changing a module's exported API restales its dependents, while a body-only rebuild does not. A behaviorally-equivalent docstring edit gets re-frozen by the semantic gate instead of paying for a full rebuild.
  • Async, tests, and contractsasync def specs build and test through build.async_runner, @jaunt.test specs generate pytest batteries, and @jaunt.contract pins hand-written code with a derived, committed battery.

Two Modes

Jaunt has two authoring modes that coexist in the same project:

  • Magic mode (jaunt.magic_module / @jaunt.magic / @jaunt.test): the docstring is canonical and Jaunt generates implementations under __generated__/.
  • Contract mode (@jaunt.contract): committed code is canonical and Jaunt derives a committed pytest battery under tests/contract/. Covers top-level functions (sync or async) and whole classes; derived cases may use pytest fixtures resolved from tests/contract/conftest.py.

See examples/contract_slugify/ for a Contract-mode walkthrough.

Installation

pip install jaunt

# The base install is batteries-included (rich, watchfiles, pytest,
# pytest-asyncio, anyio) — no optional extras.

# Jaunt drives the external OpenAI Codex CLI, which you install and
# authenticate separately:
#   1. Install the `codex` CLI (see the Codex docs).
#   2. Authenticate it: `codex login`.

Codex Engine

Codex is the sole code-generation engine: Jaunt drives codex exec for all build/test/skill workflows. It requires the external codex binary on your PATH, authenticated via codex login. Multi-provider routing is deferred.

Quickstart (This Repo)

Prereqs: uv installed.

uv sync
codex login                 # authenticate the Codex engine
uv run jaunt --version

For your own project, run tests with the source root importable, e.g. PYTHONPATH=src.

See docs-site/ for rendered docs, or DOCS.md for a plain-text walkthrough.

All examples live under examples/. See examples/README.md for the full list.

Your First Spec

jaunt init scaffolds a starter src/specs.py in module-magic style — one stub Jaunt implements:

import jaunt

jaunt.magic_module(__name__)


def greet(name: str) -> str:
    """Return a friendly greeting for `name`.

    Includes the name verbatim and ends with an exclamation mark.
    """
    ...
uv run jaunt build
PYTHONPATH=src uv run jaunt test

Hackathon Demo (JWT Auth)

Headline demo: JWT auth (the "wow gap" example: short spec, real generated glue + tests).

# Generate implementations for @jaunt.magic specs.
uv run jaunt build --root examples/jwt_auth

# Generate pytest tests for @jaunt.test specs and run them.
PYTHONPATH=examples/jwt_auth/src uv run jaunt test --root examples/jwt_auth

For Coding Agents

Point any coding agent (Claude Code, Codex, Cursor, a bare shell, CI) at Jaunt with one command:

jaunt instructions          # a tight, project-aware primer to load into context
jaunt instructions --json   # {command, ok, text, project} for tooling/MCP

It prints the framework rules (the two modes, the build/test loop, how to write a good spec, the command + exit-code reference) followed by a live snapshot of the current project (resolved paths, engine/model, and which modules are stale). It ships with the package, so the briefing always matches the installed version. Run it before you start working.

Background Daemon

jaunt daemon start runs background codegen with commit-triggered isolated jobs and auto-commit on green. jaunt daemon stop|status stops or inspects it. Use jaunt jobs for job records, would-rebuild previews, show <id> [--full], and retry <id>. jaunt log tails JAUNT_LOG (-n N, --module X), and jaunt guard warns when agents touch __generated__ via the PreToolUse hook.

Freshness Model

  • The full cleaned docstring is part of the spec contract, not just the first summary line.
  • For whole-class @jaunt.magic specs, Jaunt treats the class signature plus declared members and method signatures as exported API.
  • Jaunt's freshness model uses that dependency API too, so an upstream contract change can mark downstream modules stale even if their own source file did not change.

Eval Suite

jaunt eval is deferred under the Codex engine (rework pending).

Prompt snapshots:

uv run pytest tests/test_prompt_snapshots.py --snapshot-update

Auto-Generate PyPI Skills (Build)

jaunt build includes a best-effort pre-build step that auto-generates “skills” for external libraries your project imports and injects them into the build prompt.

What happens:

  • Scan paths.source_roots for import ... / from ... import ... (ignores stdlib, internal modules, and relative imports).
  • Resolve imports to installed PyPI distributions + versions from the current environment.
  • Ensure a skill exists per distribution at:
    • <project_root>/.agents/skills/<dist-normalized>/SKILL.md
  • If missing/outdated, fetch the exact PyPI README for <dist>==<version> and generate SKILL.md using the Codex engine.
  • Inject the concatenated skills text into the build LLM prompt.

Overwrite rules:

  • Jaunt only overwrites a skill if it was previously Jaunt-generated (it has a <!-- jaunt:skill=pypi ... --> header) and the installed version changed.
  • If the header is missing, the file is treated as user-managed and will never be overwritten.

Failure mode: warnings to stderr, and the build continues without missing skills.

Docs Site (Fumadocs)

The repository includes a Fumadocs (Next.js) documentation site under docs-site/.

cd docs-site
npm run dev

Publish to PyPI

If you keep your token in .env as UV_PUBLISH_TOKEN=..., load it into your shell first:

set -a
source .env
set +a

Build and validate artifacts:

uv build
uvx twine check dist/*

Upload to PyPI:

uv publish --check-url https://pypi.org/simple/

Dev

uv run ruff check --fix .
uv run ruff format .
uv run ty check
uv run pytest

Final verification before pushing:

uv run ruff check .
uv run ruff format --check .
uv run ty check
uv run pytest

Why "Jaunt"?

Named after jaunting -- teleportation by thought alone -- from Alfred Bester's 1956 novel The Stars My Destination (originally published as Tiger! Tiger!). You think about where you want to be, and you're there.

Jaunt works the same way: describe your intent, and arrive at working code.

The forge-and-furnace imagery you'll find scattered through the codebase comes from William Blake's poem "The Tyger," which Bester used as the novel's epigraph and alternate title. The poem's vision of creation -- hammer, chain, furnace, anvil -- mirrors the act of forging code from pure specification.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jaunt-1.5.0.tar.gz (248.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jaunt-1.5.0-py3-none-any.whl (301.4 kB view details)

Uploaded Python 3

File details

Details for the file jaunt-1.5.0.tar.gz.

File metadata

  • Download URL: jaunt-1.5.0.tar.gz
  • Upload date:
  • Size: 248.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for jaunt-1.5.0.tar.gz
Algorithm Hash digest
SHA256 e582a9f8d722262eaa48301136a8d8cde58d239a2105c166e4cd9b446fb0ed4a
MD5 41bf2aba644b95bfe6130f6d40ffbba6
BLAKE2b-256 71170985ed9cb5d7b0bac1c32115ab430a095d0746d2bbadacf991c1ea957c7d

See more details on using hashes here.

Provenance

The following attestation bundles were made for jaunt-1.5.0.tar.gz:

Publisher: release.yml on creatorrr/jaunt

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jaunt-1.5.0-py3-none-any.whl.

File metadata

  • Download URL: jaunt-1.5.0-py3-none-any.whl
  • Upload date:
  • Size: 301.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for jaunt-1.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5c89832c592daaa88d9b39ab6e5c3e8f5e7a4a88c4aa78c609203d267e57d183
MD5 d9a5850ef7fea98bd8b636e62ba0a392
BLAKE2b-256 b0dc2bccd0c26d8772c81bcf99b511da6fe325012f7124b6996c3f5b5a094283

See more details on using hashes here.

Provenance

The following attestation bundles were made for jaunt-1.5.0-py3-none-any.whl:

Publisher: release.yml on creatorrr/jaunt

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page