scry-run 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scry_run-0.1.0/.claude/settings.local.json +12 -0
- scry_run-0.1.0/.gitignore +3 -0
- scry_run-0.1.0/PKG-INFO +282 -0
- scry_run-0.1.0/README.md +252 -0
- scry_run-0.1.0/pyproject.toml +59 -0
- scry_run-0.1.0/src/scry_run/__init__.py +102 -0
- scry_run-0.1.0/src/scry_run/backends/__init__.py +6 -0
- scry_run-0.1.0/src/scry_run/backends/base.py +65 -0
- scry_run-0.1.0/src/scry_run/backends/claude.py +404 -0
- scry_run-0.1.0/src/scry_run/backends/frozen.py +85 -0
- scry_run-0.1.0/src/scry_run/backends/registry.py +72 -0
- scry_run-0.1.0/src/scry_run/cache.py +441 -0
- scry_run-0.1.0/src/scry_run/cli/__init__.py +137 -0
- scry_run-0.1.0/src/scry_run/cli/apps.py +396 -0
- scry_run-0.1.0/src/scry_run/cli/cache.py +342 -0
- scry_run-0.1.0/src/scry_run/cli/config_cmd.py +84 -0
- scry_run-0.1.0/src/scry_run/cli/env.py +27 -0
- scry_run-0.1.0/src/scry_run/cli/init.py +375 -0
- scry_run-0.1.0/src/scry_run/cli/run.py +71 -0
- scry_run-0.1.0/src/scry_run/config.py +141 -0
- scry_run-0.1.0/src/scry_run/console.py +52 -0
- scry_run-0.1.0/src/scry_run/context.py +298 -0
- scry_run-0.1.0/src/scry_run/generator.py +698 -0
- scry_run-0.1.0/src/scry_run/home.py +60 -0
- scry_run-0.1.0/src/scry_run/logging.py +171 -0
- scry_run-0.1.0/src/scry_run/meta.py +1852 -0
- scry_run-0.1.0/src/scry_run/packages.py +175 -0
- scry_run-0.1.0/tests/conftest.py +75 -0
- scry_run-0.1.0/tests/test_cache.py +375 -0
- scry_run-0.1.0/tests/test_cli.py +557 -0
- scry_run-0.1.0/tests/test_cli_default.py +81 -0
- scry_run-0.1.0/tests/test_cli_env.py +56 -0
- scry_run-0.1.0/tests/test_cli_run.py +109 -0
- scry_run-0.1.0/tests/test_config.py +263 -0
- scry_run-0.1.0/tests/test_context.py +204 -0
- scry_run-0.1.0/tests/test_generator.py +346 -0
- scry_run-0.1.0/tests/test_home.py +84 -0
- scry_run-0.1.0/tests/test_integration.py +256 -0
- scry_run-0.1.0/tests/test_logging.py +140 -0
- scry_run-0.1.0/tests/test_meta.py +393 -0
- scry_run-0.1.0/tests/test_packages.py +226 -0
- scry_run-0.1.0/uv.lock +1176 -0
scry_run-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: scry-run
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: LLM-powered dynamic code generation via metaclasses. Define classes with docstrings, call any method—code generates automatically, caches persistently, and executes instantly.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Tener/scry-run
|
|
6
|
+
Project-URL: Repository, https://github.com/Tener/scry-run
|
|
7
|
+
Author: Krzysztof Skrzętnicki
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: ai,claude,code-generation,llm,metaclass
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Requires-Dist: claude-agent-sdk>=0.1.0
|
|
20
|
+
Requires-Dist: click>=8.0.0
|
|
21
|
+
Requires-Dist: jinja2>=3.0.0
|
|
22
|
+
Requires-Dist: rich>=13.0.0
|
|
23
|
+
Requires-Dist: tomli>=2.0.0; python_version < '3.11'
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-timeout>=2.0.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# scry-run
|
|
32
|
+
|
|
33
|
+
**Write class definitions, get working code.** A Python library where any method call automatically generates its implementation via LLM.
|
|
34
|
+
|
|
35
|
+
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.
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- **Zero-boilerplate coding**: Define intent via class docstrings, methods generate automatically
|
|
40
|
+
- **Intelligent caching**: Generated code persists across runs—pay once, use forever
|
|
41
|
+
- **Production-ready**: Bake apps into standalone packages with frozen, pre-generated code
|
|
42
|
+
- **CLI-first**: Manage apps, inspect cache, and export generated code via intuitive commands
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install scry-run
|
|
48
|
+
# or with uv
|
|
49
|
+
uv pip install scry-run
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Run without installing (uvx)
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# From PyPI
|
|
56
|
+
uvx scry-run --help
|
|
57
|
+
uvx scry-run init --name=myapp --description="My app"
|
|
58
|
+
|
|
59
|
+
# From GitHub (latest)
|
|
60
|
+
uvx --from git+https://github.com/scry-run/scry-run scry-run --help
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
### 1. Set up your backend
|
|
66
|
+
|
|
67
|
+
Requires Claude Code CLI to be installed.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Verify claude CLI is available
|
|
71
|
+
claude --version
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 2. Initialize a project
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# Non-interactive
|
|
78
|
+
scry-run init --name=todoist --description='minimal web todo app'
|
|
79
|
+
|
|
80
|
+
# Interactive
|
|
81
|
+
scry-run init
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3. Run your app
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
scry-run run todoist
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Or use it directly in Python:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from scry_run import ScryClass
|
|
94
|
+
|
|
95
|
+
class Todoist(ScryClass):
|
|
96
|
+
"""A minimal web todo app with task management."""
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
app = Todoist()
|
|
100
|
+
|
|
101
|
+
# These methods will be generated automatically!
|
|
102
|
+
app.add_task("Buy groceries")
|
|
103
|
+
app.list_tasks()
|
|
104
|
+
app.complete_task(0)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Demos
|
|
108
|
+
|
|
109
|
+
### Creating a simple Hello World app
|
|
110
|
+
|
|
111
|
+

|
|
112
|
+
|
|
113
|
+
### Building a maze game with PyGame
|
|
114
|
+
|
|
115
|
+

|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
## How It Works
|
|
119
|
+
|
|
120
|
+
1. When you access an undefined attribute on an `ScryClass` subclass, the metaclass intercepts it
|
|
121
|
+
2. The library collects the full codebase context and builds a prompt
|
|
122
|
+
3. The configured backend generates code with structured JSON output (code, type, docstring, dependencies)
|
|
123
|
+
4. The code is validated with Python's `ast.parse()` to ensure it's syntactically correct
|
|
124
|
+
5. Valid code is cached and executed
|
|
125
|
+
6. On subsequent accesses, the cached code is used directly
|
|
126
|
+
|
|
127
|
+
## Backend
|
|
128
|
+
|
|
129
|
+
Uses Claude Code CLI with `--print` flag for non-interactive output.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# If claude CLI is installed, it's used automatically
|
|
133
|
+
scry-run run myapp
|
|
134
|
+
|
|
135
|
+
# Optionally specify a model
|
|
136
|
+
export SCRY_RUN_MODEL=opus
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Configuration
|
|
140
|
+
|
|
141
|
+
### Environment Variables
|
|
142
|
+
|
|
143
|
+
| Variable | Description | Default |
|
|
144
|
+
|----------|-------------|---------|
|
|
145
|
+
| `SCRY_RUN_BACKEND` | Backend to use: `claude`, `frozen`, or `auto` | `auto` |
|
|
146
|
+
| `SCRY_RUN_MODEL` | Model override (e.g., `opus`) | (backend default) |
|
|
147
|
+
| `SCRY_RUN_MAX_GENERATIONS` | Max code generations per process | `100` |
|
|
148
|
+
|
|
149
|
+
### Class-level options
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
class MyApp(ScryClass):
|
|
153
|
+
"""My application."""
|
|
154
|
+
|
|
155
|
+
# Disable LLM generation (raises AttributeError for missing attrs)
|
|
156
|
+
_llm_enabled = False
|
|
157
|
+
|
|
158
|
+
# Use minimal context instead of full codebase (faster)
|
|
159
|
+
_llm_use_full_context = False
|
|
160
|
+
|
|
161
|
+
# Suppress generation messages
|
|
162
|
+
_llm_quiet = True
|
|
163
|
+
|
|
164
|
+
# Override model for this class
|
|
165
|
+
_llm_model = "opus"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## CLI Commands
|
|
169
|
+
|
|
170
|
+
### App Management
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# Initialize a new app
|
|
174
|
+
scry-run init --name=NAME --description=DESC
|
|
175
|
+
|
|
176
|
+
# List all apps
|
|
177
|
+
scry-run list
|
|
178
|
+
|
|
179
|
+
# Get path to an app's main file
|
|
180
|
+
scry-run which myapp
|
|
181
|
+
|
|
182
|
+
# Run an app
|
|
183
|
+
scry-run run myapp [args...]
|
|
184
|
+
|
|
185
|
+
# Remove an app
|
|
186
|
+
scry-run rm myapp
|
|
187
|
+
scry-run rm myapp --force # Skip confirmation
|
|
188
|
+
|
|
189
|
+
# Reset an app (clear code & cache, keep name/description)
|
|
190
|
+
scry-run reset myapp
|
|
191
|
+
scry-run reset myapp --force # Skip confirmation
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Cache Management
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
# List cached entries for an app
|
|
198
|
+
scry-run cache list myapp
|
|
199
|
+
|
|
200
|
+
# Show a specific entry
|
|
201
|
+
scry-run cache show myapp MyClass.my_method
|
|
202
|
+
|
|
203
|
+
# Export cache
|
|
204
|
+
scry-run cache export myapp --output=generated.py --format=python
|
|
205
|
+
scry-run cache export myapp --output=cache.json --format=json
|
|
206
|
+
|
|
207
|
+
# Remove a specific entry
|
|
208
|
+
scry-run cache rm myapp MyClass.my_method
|
|
209
|
+
|
|
210
|
+
# Prune cache entries
|
|
211
|
+
scry-run cache prune myapp --class=MyClass --attr=my_method
|
|
212
|
+
scry-run cache prune myapp --class=MyClass # All methods of a class
|
|
213
|
+
scry-run cache prune myapp --all # Everything
|
|
214
|
+
|
|
215
|
+
# Reset cache (clear all entries)
|
|
216
|
+
scry-run cache reset myapp --force
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## API
|
|
220
|
+
|
|
221
|
+
### ScryClass
|
|
222
|
+
|
|
223
|
+
Base class to inherit from for LLM-powered code generation.
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
from scry_run import ScryClass
|
|
227
|
+
|
|
228
|
+
class MyApp(ScryClass):
|
|
229
|
+
"""Description of your app - the LLM uses this!"""
|
|
230
|
+
|
|
231
|
+
def existing_method(self):
|
|
232
|
+
"""Existing methods are used as context."""
|
|
233
|
+
return "hello"
|
|
234
|
+
|
|
235
|
+
# Class methods
|
|
236
|
+
MyApp.llm_export_cache("output.py") # Export generated code
|
|
237
|
+
MyApp.llm_prune_cache("method_name") # Remove cached entry
|
|
238
|
+
MyApp.llm_disable() # Disable generation
|
|
239
|
+
MyApp.llm_enable() # Re-enable generation
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### ScryCache
|
|
243
|
+
|
|
244
|
+
Manage the code cache programmatically.
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
from scry_run import ScryCache
|
|
248
|
+
|
|
249
|
+
cache = ScryCache()
|
|
250
|
+
|
|
251
|
+
# Get cached code
|
|
252
|
+
entry = cache.get("MyClass", "my_method")
|
|
253
|
+
print(entry.code)
|
|
254
|
+
|
|
255
|
+
# List all entries
|
|
256
|
+
for entry in cache.list_entries():
|
|
257
|
+
print(f"{entry.class_name}.{entry.attr_name}: {entry.docstring}")
|
|
258
|
+
|
|
259
|
+
# Export
|
|
260
|
+
cache.export_to_file("all_generated_code.py")
|
|
261
|
+
|
|
262
|
+
# Prune
|
|
263
|
+
cache.prune(class_name="MyClass", attr_name="my_method")
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Development
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# Clone the repo
|
|
270
|
+
git clone https://github.com/scry-run/scry-run
|
|
271
|
+
cd scry-run
|
|
272
|
+
|
|
273
|
+
# Install with dev dependencies
|
|
274
|
+
uv sync --dev
|
|
275
|
+
|
|
276
|
+
# Run tests
|
|
277
|
+
uv run pytest tests/ -v
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
MIT
|
scry_run-0.1.0/README.md
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# scry-run
|
|
2
|
+
|
|
3
|
+
**Write class definitions, get working code.** A Python library where any method call automatically generates its implementation via LLM.
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Zero-boilerplate coding**: Define intent via class docstrings, methods generate automatically
|
|
10
|
+
- **Intelligent caching**: Generated code persists across runs—pay once, use forever
|
|
11
|
+
- **Production-ready**: Bake apps into standalone packages with frozen, pre-generated code
|
|
12
|
+
- **CLI-first**: Manage apps, inspect cache, and export generated code via intuitive commands
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install scry-run
|
|
18
|
+
# or with uv
|
|
19
|
+
uv pip install scry-run
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Run without installing (uvx)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# From PyPI
|
|
26
|
+
uvx scry-run --help
|
|
27
|
+
uvx scry-run init --name=myapp --description="My app"
|
|
28
|
+
|
|
29
|
+
# From GitHub (latest)
|
|
30
|
+
uvx --from git+https://github.com/scry-run/scry-run scry-run --help
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
### 1. Set up your backend
|
|
36
|
+
|
|
37
|
+
Requires Claude Code CLI to be installed.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Verify claude CLI is available
|
|
41
|
+
claude --version
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 2. Initialize a project
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Non-interactive
|
|
48
|
+
scry-run init --name=todoist --description='minimal web todo app'
|
|
49
|
+
|
|
50
|
+
# Interactive
|
|
51
|
+
scry-run init
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 3. Run your app
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
scry-run run todoist
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Or use it directly in Python:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from scry_run import ScryClass
|
|
64
|
+
|
|
65
|
+
class Todoist(ScryClass):
|
|
66
|
+
"""A minimal web todo app with task management."""
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
app = Todoist()
|
|
70
|
+
|
|
71
|
+
# These methods will be generated automatically!
|
|
72
|
+
app.add_task("Buy groceries")
|
|
73
|
+
app.list_tasks()
|
|
74
|
+
app.complete_task(0)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Demos
|
|
78
|
+
|
|
79
|
+
### Creating a simple Hello World app
|
|
80
|
+
|
|
81
|
+

|
|
82
|
+
|
|
83
|
+
### Building a maze game with PyGame
|
|
84
|
+
|
|
85
|
+

|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
## How It Works
|
|
89
|
+
|
|
90
|
+
1. When you access an undefined attribute on an `ScryClass` subclass, the metaclass intercepts it
|
|
91
|
+
2. The library collects the full codebase context and builds a prompt
|
|
92
|
+
3. The configured backend generates code with structured JSON output (code, type, docstring, dependencies)
|
|
93
|
+
4. The code is validated with Python's `ast.parse()` to ensure it's syntactically correct
|
|
94
|
+
5. Valid code is cached and executed
|
|
95
|
+
6. On subsequent accesses, the cached code is used directly
|
|
96
|
+
|
|
97
|
+
## Backend
|
|
98
|
+
|
|
99
|
+
Uses Claude Code CLI with `--print` flag for non-interactive output.
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# If claude CLI is installed, it's used automatically
|
|
103
|
+
scry-run run myapp
|
|
104
|
+
|
|
105
|
+
# Optionally specify a model
|
|
106
|
+
export SCRY_RUN_MODEL=opus
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Configuration
|
|
110
|
+
|
|
111
|
+
### Environment Variables
|
|
112
|
+
|
|
113
|
+
| Variable | Description | Default |
|
|
114
|
+
|----------|-------------|---------|
|
|
115
|
+
| `SCRY_RUN_BACKEND` | Backend to use: `claude`, `frozen`, or `auto` | `auto` |
|
|
116
|
+
| `SCRY_RUN_MODEL` | Model override (e.g., `opus`) | (backend default) |
|
|
117
|
+
| `SCRY_RUN_MAX_GENERATIONS` | Max code generations per process | `100` |
|
|
118
|
+
|
|
119
|
+
### Class-level options
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
class MyApp(ScryClass):
|
|
123
|
+
"""My application."""
|
|
124
|
+
|
|
125
|
+
# Disable LLM generation (raises AttributeError for missing attrs)
|
|
126
|
+
_llm_enabled = False
|
|
127
|
+
|
|
128
|
+
# Use minimal context instead of full codebase (faster)
|
|
129
|
+
_llm_use_full_context = False
|
|
130
|
+
|
|
131
|
+
# Suppress generation messages
|
|
132
|
+
_llm_quiet = True
|
|
133
|
+
|
|
134
|
+
# Override model for this class
|
|
135
|
+
_llm_model = "opus"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## CLI Commands
|
|
139
|
+
|
|
140
|
+
### App Management
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Initialize a new app
|
|
144
|
+
scry-run init --name=NAME --description=DESC
|
|
145
|
+
|
|
146
|
+
# List all apps
|
|
147
|
+
scry-run list
|
|
148
|
+
|
|
149
|
+
# Get path to an app's main file
|
|
150
|
+
scry-run which myapp
|
|
151
|
+
|
|
152
|
+
# Run an app
|
|
153
|
+
scry-run run myapp [args...]
|
|
154
|
+
|
|
155
|
+
# Remove an app
|
|
156
|
+
scry-run rm myapp
|
|
157
|
+
scry-run rm myapp --force # Skip confirmation
|
|
158
|
+
|
|
159
|
+
# Reset an app (clear code & cache, keep name/description)
|
|
160
|
+
scry-run reset myapp
|
|
161
|
+
scry-run reset myapp --force # Skip confirmation
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Cache Management
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# List cached entries for an app
|
|
168
|
+
scry-run cache list myapp
|
|
169
|
+
|
|
170
|
+
# Show a specific entry
|
|
171
|
+
scry-run cache show myapp MyClass.my_method
|
|
172
|
+
|
|
173
|
+
# Export cache
|
|
174
|
+
scry-run cache export myapp --output=generated.py --format=python
|
|
175
|
+
scry-run cache export myapp --output=cache.json --format=json
|
|
176
|
+
|
|
177
|
+
# Remove a specific entry
|
|
178
|
+
scry-run cache rm myapp MyClass.my_method
|
|
179
|
+
|
|
180
|
+
# Prune cache entries
|
|
181
|
+
scry-run cache prune myapp --class=MyClass --attr=my_method
|
|
182
|
+
scry-run cache prune myapp --class=MyClass # All methods of a class
|
|
183
|
+
scry-run cache prune myapp --all # Everything
|
|
184
|
+
|
|
185
|
+
# Reset cache (clear all entries)
|
|
186
|
+
scry-run cache reset myapp --force
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## API
|
|
190
|
+
|
|
191
|
+
### ScryClass
|
|
192
|
+
|
|
193
|
+
Base class to inherit from for LLM-powered code generation.
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
from scry_run import ScryClass
|
|
197
|
+
|
|
198
|
+
class MyApp(ScryClass):
|
|
199
|
+
"""Description of your app - the LLM uses this!"""
|
|
200
|
+
|
|
201
|
+
def existing_method(self):
|
|
202
|
+
"""Existing methods are used as context."""
|
|
203
|
+
return "hello"
|
|
204
|
+
|
|
205
|
+
# Class methods
|
|
206
|
+
MyApp.llm_export_cache("output.py") # Export generated code
|
|
207
|
+
MyApp.llm_prune_cache("method_name") # Remove cached entry
|
|
208
|
+
MyApp.llm_disable() # Disable generation
|
|
209
|
+
MyApp.llm_enable() # Re-enable generation
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### ScryCache
|
|
213
|
+
|
|
214
|
+
Manage the code cache programmatically.
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
from scry_run import ScryCache
|
|
218
|
+
|
|
219
|
+
cache = ScryCache()
|
|
220
|
+
|
|
221
|
+
# Get cached code
|
|
222
|
+
entry = cache.get("MyClass", "my_method")
|
|
223
|
+
print(entry.code)
|
|
224
|
+
|
|
225
|
+
# List all entries
|
|
226
|
+
for entry in cache.list_entries():
|
|
227
|
+
print(f"{entry.class_name}.{entry.attr_name}: {entry.docstring}")
|
|
228
|
+
|
|
229
|
+
# Export
|
|
230
|
+
cache.export_to_file("all_generated_code.py")
|
|
231
|
+
|
|
232
|
+
# Prune
|
|
233
|
+
cache.prune(class_name="MyClass", attr_name="my_method")
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Development
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Clone the repo
|
|
240
|
+
git clone https://github.com/scry-run/scry-run
|
|
241
|
+
cd scry-run
|
|
242
|
+
|
|
243
|
+
# Install with dev dependencies
|
|
244
|
+
uv sync --dev
|
|
245
|
+
|
|
246
|
+
# Run tests
|
|
247
|
+
uv run pytest tests/ -v
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## License
|
|
251
|
+
|
|
252
|
+
MIT
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "scry-run"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "LLM-powered dynamic code generation via metaclasses. Define classes with docstrings, call any method—code generates automatically, caches persistently, and executes instantly."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { text = "MIT" }
|
|
7
|
+
requires-python = ">=3.10"
|
|
8
|
+
authors = [{ name = "Krzysztof Skrzętnicki" }]
|
|
9
|
+
keywords = ["llm", "code-generation", "metaclass", "claude", "ai"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 3 - Alpha",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.10",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Topic :: Software Development :: Code Generators",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
dependencies = [
|
|
22
|
+
"click>=8.0.0",
|
|
23
|
+
"rich>=13.0.0",
|
|
24
|
+
"jinja2>=3.0.0",
|
|
25
|
+
"tomli>=2.0.0;python_version<'3.11'",
|
|
26
|
+
"claude-agent-sdk>=0.1.0",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
dev = [
|
|
31
|
+
"pytest>=7.0.0",
|
|
32
|
+
"pytest-cov>=4.0.0",
|
|
33
|
+
"pytest-asyncio>=0.21.0",
|
|
34
|
+
"pytest-timeout>=2.0.0",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
scry-run = "scry_run.cli:main"
|
|
39
|
+
|
|
40
|
+
[project.urls]
|
|
41
|
+
Homepage = "https://github.com/Tener/scry-run"
|
|
42
|
+
Repository = "https://github.com/Tener/scry-run"
|
|
43
|
+
|
|
44
|
+
[build-system]
|
|
45
|
+
requires = ["hatchling"]
|
|
46
|
+
build-backend = "hatchling.build"
|
|
47
|
+
|
|
48
|
+
[tool.hatch.build.targets.wheel]
|
|
49
|
+
packages = ["src/scry_run"]
|
|
50
|
+
|
|
51
|
+
[tool.hatch.build.targets.sdist]
|
|
52
|
+
exclude = ["demos/"]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
[tool.pytest.ini_options]
|
|
56
|
+
testpaths = ["tests"]
|
|
57
|
+
pythonpath = ["src"]
|
|
58
|
+
timeout = 30
|
|
59
|
+
timeout_method = "thread"
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""scry-run: LLM-powered dynamic code generation via metaclasses."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from scry_run.meta import ScryClass, ScryMeta
|
|
6
|
+
from scry_run.cache import ScryCache
|
|
7
|
+
from scry_run.home import get_home, get_app_dir, ensure_home_exists
|
|
8
|
+
from scry_run.config import Config, load_config
|
|
9
|
+
from scry_run.generator import (
|
|
10
|
+
CodeGenerator,
|
|
11
|
+
ScryRunError,
|
|
12
|
+
APIKeyError,
|
|
13
|
+
RateLimitError,
|
|
14
|
+
QuotaExceededError,
|
|
15
|
+
ModelNotFoundError,
|
|
16
|
+
ContentBlockedError,
|
|
17
|
+
NetworkError,
|
|
18
|
+
CodeGenerationError,
|
|
19
|
+
CodeValidationError,
|
|
20
|
+
)
|
|
21
|
+
from scry_run.context import ContextBuilder
|
|
22
|
+
from scry_run.backends.frozen import FrozenAppError
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def scry_create(name: str, description: str = "") -> type:
|
|
26
|
+
"""Create a new ScryClass subclass dynamically.
|
|
27
|
+
|
|
28
|
+
Use this to create multiple classes without manually defining them.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
name: Class name (e.g., "TodoItem", "Database")
|
|
32
|
+
description: Docstring describing what this class does
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
A new class inheriting from ScryClass
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
>>> TodoItem = scry_create("TodoItem", "A single todo item with title and status")
|
|
39
|
+
>>> item = TodoItem("Buy groceries")
|
|
40
|
+
>>> item.mark_done() # auto-generated!
|
|
41
|
+
"""
|
|
42
|
+
doc = description or f"Auto-generated {name} class"
|
|
43
|
+
return type(name, (ScryClass,), {"__doc__": doc})
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def is_generated(obj: Any) -> bool:
|
|
47
|
+
"""Check if an object (function, method, property, class) was generated by scry-run.
|
|
48
|
+
|
|
49
|
+
This function reliably checks for internal metadata attached to generated code.
|
|
50
|
+
It handles unwrapping of properties, classmethods, and bound methods.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
obj: The object to check
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
True if the object was generated by LLM, False otherwise.
|
|
57
|
+
"""
|
|
58
|
+
marker = "_llm_is_generated"
|
|
59
|
+
|
|
60
|
+
# Handle descriptors (property, classmethod, staticmethod)
|
|
61
|
+
if isinstance(obj, property) and obj.fget:
|
|
62
|
+
return getattr(obj.fget, marker, False)
|
|
63
|
+
|
|
64
|
+
if isinstance(obj, (classmethod, staticmethod)):
|
|
65
|
+
return getattr(obj.__func__, marker, False)
|
|
66
|
+
|
|
67
|
+
# Handle bound methods (e.g. instance.method)
|
|
68
|
+
if hasattr(obj, "__func__"):
|
|
69
|
+
return getattr(obj.__func__, marker, False)
|
|
70
|
+
|
|
71
|
+
# Handle plain functions/classes
|
|
72
|
+
return getattr(obj, marker, False)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
__version__ = "0.1.0"
|
|
76
|
+
__all__ = [
|
|
77
|
+
"ScryClass",
|
|
78
|
+
"ScryMeta",
|
|
79
|
+
"ScryCache",
|
|
80
|
+
"CodeGenerator",
|
|
81
|
+
"ContextBuilder",
|
|
82
|
+
"scry_create",
|
|
83
|
+
"is_generated",
|
|
84
|
+
# Home directory
|
|
85
|
+
"get_home",
|
|
86
|
+
"get_app_dir",
|
|
87
|
+
"ensure_home_exists",
|
|
88
|
+
# Config
|
|
89
|
+
"Config",
|
|
90
|
+
"load_config",
|
|
91
|
+
# Exceptions
|
|
92
|
+
"ScryRunError",
|
|
93
|
+
"APIKeyError",
|
|
94
|
+
"RateLimitError",
|
|
95
|
+
"QuotaExceededError",
|
|
96
|
+
"ModelNotFoundError",
|
|
97
|
+
"ContentBlockedError",
|
|
98
|
+
"NetworkError",
|
|
99
|
+
"CodeGenerationError",
|
|
100
|
+
"CodeValidationError",
|
|
101
|
+
"FrozenAppError",
|
|
102
|
+
]
|