# scry-run

**Write class definitions, get working code.** A Python library where any method call automatically generates its implementation via LLM.

Define your application as a class with a docstring, then call any method you want—`scry-run` generates the code on-demand, caches it, and executes it. No boilerplate, no implementation required.

## Features

- **Zero-boilerplate coding**: Define intent via class docstrings, methods generate automatically
- **Intelligent caching**: Generated code persists across runs—pay once, use forever
- **Production-ready**: Bake apps into standalone packages with frozen, pre-generated code
- **CLI-first**: Manage apps, inspect cache, and export generated code via intuitive commands

## Installation

```bash
pip install scry-run
# or with uv
uv pip install scry-run
```

### Run without installing (uvx)

```bash
# From PyPI
uvx scry-run --help
uvx scry-run init --name=myapp --description="My app"

# From GitHub (latest)
uvx --from git+https://github.com/scry-run/scry-run scry-run --help
```

## Quick Start

### 1. Set up your backend

Requires Claude Code CLI to be installed.

```bash
# Verify claude CLI is available
claude --version
```

### 2. Initialize a project

```bash
# Non-interactive
scry-run init --name=todoist --description='minimal web todo app'

# Interactive
scry-run init
```

### 3. Run your app

```bash
scry-run run todoist
```

Or use it directly in Python:

```python
from scry_run import ScryClass

class Todoist(ScryClass):
    """A minimal web todo app with task management."""
    pass

app = Todoist()

# These methods will be generated automatically!
app.add_task("Buy groceries")
app.list_tasks()
app.complete_task(0)
```

## Demos

### Creating a simple Hello World app

![Hello Demo](demos/hello.gif)

### Building a maze game with PyGame

![Maze Demo](demos/maze.gif)


## How It Works

1. When you access an undefined attribute on an `ScryClass` subclass, the metaclass intercepts it
2. The library collects the full codebase context and builds a prompt
3. The configured backend generates code with structured JSON output (code, type, docstring, dependencies)
4. The code is validated with Python's `ast.parse()` to ensure it's syntactically correct
5. Valid code is cached and executed
6. On subsequent accesses, the cached code is used directly

## Backend

Uses Claude Code CLI with `--print` flag for non-interactive output.

```bash
# If claude CLI is installed, it's used automatically
scry-run run myapp

# Optionally specify a model
export SCRY_RUN_MODEL=opus
```

## Configuration

### Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `SCRY_RUN_BACKEND` | Backend to use: `claude`, `frozen`, or `auto` | `auto` |
| `SCRY_RUN_MODEL` | Model override (e.g., `opus`) | (backend default) |
| `SCRY_RUN_MAX_GENERATIONS` | Max code generations per process | `100` |

### Class-level options

```python
class MyApp(ScryClass):
    """My application."""

    # Disable LLM generation (raises AttributeError for missing attrs)
    _llm_enabled = False

    # Use minimal context instead of full codebase (faster)
    _llm_use_full_context = False

    # Suppress generation messages
    _llm_quiet = True

    # Override model for this class
    _llm_model = "opus"
```

## CLI Commands

### App Management

```bash
# Initialize a new app
scry-run init --name=NAME --description=DESC

# List all apps
scry-run list

# Get path to an app's main file
scry-run which myapp

# Run an app
scry-run run myapp [args...]

# Remove an app
scry-run rm myapp
scry-run rm myapp --force  # Skip confirmation

# Reset an app (clear code & cache, keep name/description)
scry-run reset myapp
scry-run reset myapp --force  # Skip confirmation
```

### Cache Management

```bash
# List cached entries for an app
scry-run cache list myapp

# Show a specific entry
scry-run cache show myapp MyClass.my_method

# Export cache
scry-run cache export myapp --output=generated.py --format=python
scry-run cache export myapp --output=cache.json --format=json

# Remove a specific entry
scry-run cache rm myapp MyClass.my_method

# Prune cache entries
scry-run cache prune myapp --class=MyClass --attr=my_method
scry-run cache prune myapp --class=MyClass  # All methods of a class
scry-run cache prune myapp --all            # Everything

# Reset cache (clear all entries)
scry-run cache reset myapp --force
```

## API

### ScryClass

Base class to inherit from for LLM-powered code generation.

```python
from scry_run import ScryClass

class MyApp(ScryClass):
    """Description of your app - the LLM uses this!"""

    def existing_method(self):
        """Existing methods are used as context."""
        return "hello"

# Class methods
MyApp.llm_export_cache("output.py")  # Export generated code
MyApp.llm_prune_cache("method_name") # Remove cached entry
MyApp.llm_disable()                   # Disable generation
MyApp.llm_enable()                    # Re-enable generation
```

### ScryCache

Manage the code cache programmatically.

```python
from scry_run import ScryCache

cache = ScryCache()

# Get cached code
entry = cache.get("MyClass", "my_method")
print(entry.code)

# List all entries
for entry in cache.list_entries():
    print(f"{entry.class_name}.{entry.attr_name}: {entry.docstring}")

# Export
cache.export_to_file("all_generated_code.py")

# Prune
cache.prune(class_name="MyClass", attr_name="my_method")
```

## Development

```bash
# Clone the repo
git clone https://github.com/scry-run/scry-run
cd scry-run

# Install with dev dependencies
uv sync --dev

# Run tests
uv run pytest tests/ -v
```

## License

MIT
