Skip to main content

Autonomous, DSL-fluent coding agent built on the Claude Agent SDK — point it at any DSL directory and it plans, codes, debugs, verifies, and self-extends.

Project description

sceneprogagent

An autonomous, DSL-fluent coding agent built on the Claude Agent SDK.

Point it at a DSL directory — a folder of .py/.md/.html files describing your domain API. The agent plans, retrieves the right DSL symbols, writes code, runs it the way your DSL says to, debugs until the result is verified correct, and can autonomously extend the DSL when a task needs a capability it lacks.

It is domain-agnostic: 3D generation, Blender, SfM, scientific simulation, or any other domain is just a different DSL directory. No domain assumptions live in this package.


Prerequisites

Requirement Notes
Python ≥ 3.10
Claude Code CLI npm install -g @anthropic-ai/claude-code (or download from claude.ai/code)
Authentication — see below
git Only needed for autonomous DSL self-extension (extension_enabled=True)

Authentication

The agent drives the claude CLI as a subprocess — auth is handled by that process. Pick whichever method fits your setup:

Option A — Anthropic API key (pay-per-token, from console.anthropic.com)

# shell (recommended for scripts / CI)
export ANTHROPIC_API_KEY=sk-ant-...

Or pass it programmatically — no shell env var needed:

agent = SceneProgAgent(AgentConfig(dsl_dir="./dsl", api_key="sk-ant-..."))

Or via the CLI flag:

sceneprogagent run --dsl ./dsl --api-key sk-ant-... "My task"

Option B — Claude.ai / Claude Code login (Claude Pro/Max/Teams subscription)

claude auth login    # one-time; stores credentials in ~/.claude/.credentials.json

After that, no API key is needed — the subprocess reads the stored credentials automatically.

Running as root (Docker)

AgentConfig automatically sets IS_SANDBOX=1 for the CLI subprocess when permission_mode="bypassPermissions" (the default). You do not need to set it manually.

If the CLI is not on PATH, tell the agent where to find it:

export CLAUDE_CODE_EXECPATH=/path/to/claude   # or pass cli_path= to AgentConfig

Install

From PyPI (recommended)

pip install sceneprogagent

With optional embedding-based doc search:

pip install "sceneprogagent[embed]"

From source

git clone https://github.com/KunalMGupta/sceneprogagent
cd sceneprogagent
pip install -e .              # core
pip install -e ".[embed]"     # + chromadb / sentence-transformers
pip install -e ".[dev]"       # + pytest

Quick start (5 minutes)

1 — Point at your DSL directory

Your DSL directory needs at minimum one .py file with the API and a runbook — a markdown file telling the agent how to execute scripts with this DSL.

my_project/
├── dsl/
│   ├── api.py          ← your domain Python library
│   ├── RUNBOOK.md      ← execution recipe (see below)
│   └── docs.md         ← optional extra docs
└── solve.py            ← your script using sceneprogagent

dsl/RUNBOOK.md (minimal):

# My DSL

Import the API with `import api` and call its functions.

## How to run code
```run
python3 {file}
```

The ```run block is the execution recipe. {file} is replaced with the path to each generated script. For a Blender DSL it might be blender --background --python {file}; for SfM it might be python3 {file} with a special env.

2 — Run from the CLI

# one-shot task
sceneprogagent run --dsl ./dsl "Create a red cube and save it to output.blend"

# save the final verified program
sceneprogagent run --dsl ./dsl --save-code final.py "Create a red cube"

# iterative optimization
sceneprogagent run --dsl ./dsl --optimize "minimise polygon count" "Create a sphere"

# disable autonomous DSL self-extension
sceneprogagent run --dsl ./dsl --no-extend "Create a box"

3 — Use from Python

from sceneprogagent import AgentConfig, SceneProgAgent

with SceneProgAgent(AgentConfig(dsl_dir="./dsl")) as agent:
    summary = agent.run("Create a scene with a box named 'table' of size 2 and save it")
    print(agent.final_code)   # the final verified program

Deploying in a new project

Step 1 — Install

pip install sceneprogagent

Step 2 — Create a DSL directory

A DSL directory needs:

  • At least one .py file with your domain API (public functions, well-named, with docstrings).
  • A runbook named RUNBOOK.md (also discovered as AGENT.md, DSL.md, or README.md).

The runbook teaches the agent two things: (1) how to execute scripts, via the ```run block; (2) any domain conventions and gotchas, in plain markdown prose.

Example structure:

# My Domain DSL

Use `import myapi` to access all functions.

All functions work with absolute paths. Pass `save=True` to persist results.

## How to run code
```run
python3 {file}
```

## Tips
- Always call `myapi.init()` first.
- `myapi.save(path)` requires the parent directory to already exist.

Step 3 — Configure and run

from sceneprogagent import AgentConfig, SceneProgAgent

config = AgentConfig(
    dsl_dir="./dsl",             # path to your DSL directory
    work_dir="./agent_runs",     # scratch dir for code attempts + logs (auto-created)
    model="claude-opus-4-8",     # any Claude model id
    max_turns=80,                # agentic tool round-trips per run
    max_debug_iters=8,           # debug retries before giving up
    max_budget_usd=1.00,         # optional spend ceiling
    extension_enabled=True,      # let the agent extend the DSL when needed
    save_code="out/final.py",    # write the final program here
)

with SceneProgAgent(config) as agent:
    summary = agent.run("Your task description here")
    print(summary)
    print(agent.final_code)

Python API reference

AgentConfig

Parameter Default Description
dsl_dir (required) Path to the DSL directory
work_dir <dsl>/../.sceneprog_work Scratch dir for code attempts, logs
runbook_path auto-discovered Override runbook location
api_key None Anthropic API key (sk-ant-...). Falls back to ANTHROPIC_API_KEY env var or stored claude auth login credentials
model "claude-opus-4-8" Claude model ID
max_turns 80 Max tool round-trips per run()
max_debug_iters 8 Max debug retries before giving up
max_budget_usd None Optional spend ceiling per run
extension_enabled True Allow autonomous DSL self-extension
embed_enabled False Use chromadb embeddings for doc search
save_code None Default path to write the final program
cli_path auto-discovered Override path to the claude CLI binary
permission_mode "bypassPermissions" Claude SDK permission mode
extra_env {} Extra env vars for the subprocess

SceneProgAgent

agent = SceneProgAgent(config)

# Solve one task (synchronous)
summary: str = agent.run(task, save_code=None)

# Iterative optimization
summary: str = agent.optimize(task, objective, max_rounds=3, save_code=None)

# Async variant (use inside an existing event loop, e.g. FastAPI, Jupyter)
summary: str = await agent.run_async(task, save_code=None)

# Result of the last run
agent.final_code    # str | None — the final verified program
agent.retriever     # query the DSL index without a model call

# Lifecycle
agent.close()       # close the trajectory log when done
# or use as a context manager:
with SceneProgAgent(config) as agent:
    ...

Init once, run many

A single SceneProgAgent is reusable — initialize once and solve tasks sequentially. Each call to run() resets per-task result tracking; the trajectory log spans the whole session.

with SceneProgAgent(AgentConfig(dsl_dir="./dsl")) as agent:
    for i, task in enumerate(my_tasks):
        summary = agent.run(task, save_code=f"out/task_{i}.py")
        results[i] = agent.final_code

How it works

task ─▶ plan ─▶ retrieve DSL context ─▶ write code ─▶ run via runbook ─▶ verify
                      ▲                                     │              │
                      └──────────── debug loop ◀────────────┘   fail ◀─────┘
                                                                  │ (capability missing)
                                                         autonomous DSL extension
  • Hybrid retrieval grounds every call in the real API — no hallucinated functions:
    • dsl_overview — compact cheatsheet of all public symbols (always in context);
    • dsl_lookup — real signatures, docstrings, and source for any symbol;
    • dsl_search_docs + built-in Grep/Read for depth (optional embeddings via [embed]).
  • Verification oracle (verify_solution): clean exit + an assertion script the agent writes, so "success" means the answer is correct, not just non-crashing.
  • Autonomous self-extension: git snapshot → WebSearch/clone → translator subagent adapts external code into DSL-style files → reindex → smoke-test → use. restore_dsl rolls back a bad extension.
  • Trajectory log — every tool call, code submission, and verification result is written to <work_dir>/trajectory.jsonl; every generated script is kept in <work_dir>/code/attempt_NNN.py.

What the agent can and can't guarantee

Will reliably do:

  • Read and use your DSL's actual API (not hallucinate functions)
  • Run the generated code and show you the output
  • Retry with the real error/traceback until it passes its own assertions
  • Report final_code only when a program ran cleanly and passed verification

May still fail when:

  • The task requires DSL capabilities that don't exist (use extension_enabled=True to let the agent add them)
  • The agent misunderstands the task and asserts the wrong thing — phrasing matters; "verify it equals 17" beats "check it's roughly right"
  • max_debug_iters is reached without a solution — increase it or break the task into smaller pieces

Advanced: querying the DSL index directly

agent = SceneProgAgent(config)

# No model call — instant, free
print(agent.retriever.cheatsheet())         # all public symbols
print(agent.retriever.lookup("my_func"))    # signature + source
print(agent.retriever.search_docs("save"))  # heading / prose search

This is useful for building your own tooling on top of the DSL index — e.g. autocomplete, a REPL helper, or a CI check that the DSL API hasn't changed.


Module layout

Module Responsibility
config.py AgentConfig: paths, model/budget, toggles, CLI + sandbox env
runbook.py discover/parse the runbook and its execution recipe
index/ static (AST) API index + cheatsheet; optional embedding store
retrieval.py hybrid retriever (overview / lookup / docs)
execution.py run generated code via the runbook recipe
verify.py layered verification oracle (exit code + assertions + optional LLM judge)
extension.py git snapshot/restore + repo cloning primitives
tools.py the sceneprog MCP tool server (all agent-callable tools)
prompts.py system prompt + debugger/translator subagent definitions
agent.py main orchestrator — assembles options, drives the SDK loop
optimize.py iterative-optimization loop
telemetry.py JSONL trajectory logging
cli.py command-line entry point

Tests

pytest                              # offline: index, retrieval, runbook, execution, verify
SCENEPROG_RUN_E2E=1 pytest -s       # + live agent tests (use real Claude; cost money/time)

Building and publishing to PyPI

pip install build twine

# build the distribution
python -m build                     # produces dist/sceneprogagent-*.whl and .tar.gz

# upload to TestPyPI first
twine upload --repository testpypi dist/*
pip install --index-url https://test.pypi.org/simple/ sceneprogagent

# when satisfied, publish to PyPI
twine upload dist/*

To bump the version, edit version = "..." in pyproject.toml and __version__ in sceneprogagent/__init__.py, then rebuild.


License

MIT © Kunal Gupta

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

sceneprogagent-0.1.2.tar.gz (39.8 kB view details)

Uploaded Source

Built Distribution

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

sceneprogagent-0.1.2-py3-none-any.whl (39.7 kB view details)

Uploaded Python 3

File details

Details for the file sceneprogagent-0.1.2.tar.gz.

File metadata

  • Download URL: sceneprogagent-0.1.2.tar.gz
  • Upload date:
  • Size: 39.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for sceneprogagent-0.1.2.tar.gz
Algorithm Hash digest
SHA256 099fe3f490be02a261abcea6739cdeac47394a4502ce23d22ece03a8fe562bc9
MD5 4e23e2681a1a1b89a9888c0b68df18ce
BLAKE2b-256 b6277e0020b1577051672d843faef69264cebe3d3f120b41254b7ab3466e4f07

See more details on using hashes here.

File details

Details for the file sceneprogagent-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: sceneprogagent-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 39.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for sceneprogagent-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 8bcbe4880b0e87d55e9b9049d5893a0e587ae462f4e8360726880d3de7099c3f
MD5 102fd72ebfe9fa4ac0ec8e7cc39179f9
BLAKE2b-256 fea01190c89f1549cc4c284b679ffb2f9cb1d61f782bd0c599eaa2165669fd6b

See more details on using hashes here.

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