scripy-cli 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.
- scripy_cli-0.1.0/.gitignore +26 -0
- scripy_cli-0.1.0/Claude.md +292 -0
- scripy_cli-0.1.0/LICENSE +21 -0
- scripy_cli-0.1.0/PKG-INFO +244 -0
- scripy_cli-0.1.0/README.md +198 -0
- scripy_cli-0.1.0/pyproject.toml +45 -0
- scripy_cli-0.1.0/scripy/__init__.py +1 -0
- scripy_cli-0.1.0/scripy/agent.py +592 -0
- scripy_cli-0.1.0/scripy/cli.py +91 -0
- scripy_cli-0.1.0/scripy/config.py +52 -0
- scripy_cli-0.1.0/scripy/executor.py +80 -0
- scripy_cli-0.1.0/scripy/gates.py +126 -0
- scripy_cli-0.1.0/scripy/prompts.py +33 -0
- scripy_cli-0.1.0/scripy/reporter.py +80 -0
- scripy_cli-0.1.0/scripy/theme.py +31 -0
- scripy_cli-0.1.0/scripy/tools.py +82 -0
- scripy_cli-0.1.0/scripy/tui/__init__.py +0 -0
- scripy_cli-0.1.0/scripy/tui/app.py +816 -0
- scripy_cli-0.1.0/tests/__init__.py +0 -0
- scripy_cli-0.1.0/tests/test_agent_unit.py +282 -0
- scripy_cli-0.1.0/tests/test_clean.py +276 -0
- scripy_cli-0.1.0/tests/test_config.py +104 -0
- scripy_cli-0.1.0/tests/test_executor.py +89 -0
- scripy_cli-0.1.0/tests/test_gates.py +57 -0
- scripy_cli-0.1.0/tests/test_prompts.py +60 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.pyo
|
|
5
|
+
.venv/
|
|
6
|
+
venv/
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
*.egg
|
|
12
|
+
|
|
13
|
+
# Claude local settings (not the checked-in CLAUDE.md)
|
|
14
|
+
.claude/
|
|
15
|
+
|
|
16
|
+
# Env
|
|
17
|
+
.env
|
|
18
|
+
|
|
19
|
+
# Editor
|
|
20
|
+
.DS_Store
|
|
21
|
+
.vscode/
|
|
22
|
+
.idea/
|
|
23
|
+
|
|
24
|
+
# Generated test scripts
|
|
25
|
+
*.py.out
|
|
26
|
+
*.sh.out
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# scripy — Claude Code Project Declaration
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
**scripy** is a local-first CLI agent for generating small, single-file scripts (Python, Bash, etc.) using locally hosted LLMs via Ollama or LM Studio. Invoked with a single command and prompt, it runs an agentic loop that generates, validates, self-corrects, and writes the final script to disk.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Goals
|
|
10
|
+
|
|
11
|
+
- Minimal hardware requirements — optimized for small coding models (e.g. qwen2.5-coder:7b, deepseek-coder:6.7b)
|
|
12
|
+
- Single-command UX: `scripy -p "build me a script to rename all my jpegs by date"`
|
|
13
|
+
- Self-correcting agentic loop (generate → run → observe → revise)
|
|
14
|
+
- Optional TUI via Textual for visual feedback
|
|
15
|
+
- No cloud dependencies — fully local inference
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Tech Stack
|
|
20
|
+
|
|
21
|
+
- **Language:** Python 3.11+
|
|
22
|
+
- **CLI:** `click`
|
|
23
|
+
- **Model client:** `openai` SDK (OpenAI-compatible API for both Ollama and LM Studio)
|
|
24
|
+
- **TUI:** `textual`
|
|
25
|
+
- **Output formatting:** `rich`
|
|
26
|
+
- **Packaging:** `pyproject.toml` with `pipx`-installable entry point
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Project Structure
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
scripy/
|
|
34
|
+
├── scripy/
|
|
35
|
+
│ ├── __init__.py
|
|
36
|
+
│ ├── cli.py # Entry point, click CLI definition
|
|
37
|
+
│ ├── agent.py # Agentic loop, multi-turn conversation logic
|
|
38
|
+
│ ├── tools.py # Tool definitions: read_file, run_script, write_file, list_directory
|
|
39
|
+
│ ├── prompts.py # System prompt and prompt templates
|
|
40
|
+
│ ├── executor.py # Sandboxed script runner (subprocess), syntax validation
|
|
41
|
+
│ ├── config.py # Config loading (model URL, defaults, etc.)
|
|
42
|
+
│ └── tui/
|
|
43
|
+
│ ├── __init__.py
|
|
44
|
+
│ └── app.py # Optional Textual TUI app
|
|
45
|
+
├── tests/
|
|
46
|
+
├── pyproject.toml
|
|
47
|
+
├── README.md
|
|
48
|
+
└── CLAUDE.md # This file
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## CLI Interface
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Basic usage
|
|
57
|
+
scripy -p "write a python script to find duplicate files in a directory"
|
|
58
|
+
|
|
59
|
+
# Specify output file
|
|
60
|
+
scripy -p "..." -o dedup.py
|
|
61
|
+
|
|
62
|
+
# Specify language explicitly
|
|
63
|
+
scripy -p "..." --lang bash
|
|
64
|
+
|
|
65
|
+
# Specify model
|
|
66
|
+
scripy -p "..." --model qwen2.5-coder:7b
|
|
67
|
+
|
|
68
|
+
# Launch TUI
|
|
69
|
+
scripy -p "..." --tui
|
|
70
|
+
|
|
71
|
+
# Modify an existing script
|
|
72
|
+
scripy -p "add error handling" --input myscript.py
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Agentic Loop
|
|
78
|
+
|
|
79
|
+
The agent runs a bounded multi-turn conversation with the model:
|
|
80
|
+
|
|
81
|
+
1. **Generate** — model writes the script based on the user prompt
|
|
82
|
+
2. **Validate** — syntax check (e.g. `py_compile`, `bash -n`) and optional sandboxed execution
|
|
83
|
+
3. **Self-correct** — if validation fails, feed stderr back to model (max 3 iterations)
|
|
84
|
+
4. **Finalize** — write output to disk or stdout
|
|
85
|
+
|
|
86
|
+
Hard cap: **3 correction iterations** max to prevent infinite loops on bad models.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Tool Definitions
|
|
91
|
+
|
|
92
|
+
Tools follow OpenAI function-calling schema. One tool call per turn (small model safe).
|
|
93
|
+
|
|
94
|
+
| Tool | Description |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `write_file(path, content)` | Write the final script to disk |
|
|
97
|
+
| `run_script(code, interpreter)` | Execute script in sandbox, return stdout/stderr |
|
|
98
|
+
| `read_file(path)` | Read an existing file (for modification tasks) |
|
|
99
|
+
| `list_directory(path)` | List directory contents |
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## System Prompt Contract
|
|
104
|
+
|
|
105
|
+
- Output **only** valid, runnable code — no markdown fences, no prose explanation
|
|
106
|
+
- Single-file scripts only; no multi-file output
|
|
107
|
+
- Always include a shebang and a brief comment header
|
|
108
|
+
- Infer language from the prompt; default to Python
|
|
109
|
+
- Scripts must be self-contained unless user explicitly requests external dependencies
|
|
110
|
+
- When using tools, call **one tool at a time**
|
|
111
|
+
- Only call `run_script` to validate; do not use it to simulate output
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Model Configuration
|
|
116
|
+
|
|
117
|
+
Default config (overridable via `~/.config/scripy/config.toml` or CLI flags):
|
|
118
|
+
|
|
119
|
+
```toml
|
|
120
|
+
[model]
|
|
121
|
+
base_url = "http://localhost:11434/v1" # Ollama default
|
|
122
|
+
model = "qwen2.5-coder:7b"
|
|
123
|
+
api_key = "ollama" # LM Studio uses "lm-studio"
|
|
124
|
+
temperature = 0.2
|
|
125
|
+
max_tokens = 2048
|
|
126
|
+
|
|
127
|
+
[agent]
|
|
128
|
+
max_iterations = 3
|
|
129
|
+
default_lang = "python"
|
|
130
|
+
sandbox_timeout = 10 # seconds
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Switching to LM Studio: change `base_url` to `http://localhost:1234/v1` and `api_key` to `"lm-studio"`.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Constraints & Non-Goals
|
|
138
|
+
|
|
139
|
+
- **No multi-file output** — scripy targets single-file scripts only
|
|
140
|
+
- **No persistent memory** — each invocation is stateless
|
|
141
|
+
- **No cloud LLM calls** — local inference only by design
|
|
142
|
+
- **No package installation** — the generated script runs with what's available; scripy itself does not install deps on behalf of the user
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Recommended Models (Ollama)
|
|
147
|
+
|
|
148
|
+
| Model | Size | Notes |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| `qwen2.5-coder:7b` | ~4GB | Best overall for the size |
|
|
151
|
+
| `deepseek-coder:6.7b` | ~4GB | Strong on Python |
|
|
152
|
+
| `codellama:7b` | ~4GB | Good Bash/shell support |
|
|
153
|
+
| `qwen2.5-coder:1.5b` | ~1GB | Ultra-low resource fallback |
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Design Spec
|
|
158
|
+
|
|
159
|
+
scripy should feel like a premium, intentional tool — not a weekend hack. Every detail matters.
|
|
160
|
+
|
|
161
|
+
### Visual Identity
|
|
162
|
+
|
|
163
|
+
- **Theme:** Dark, high-contrast terminal aesthetic — true black background, not softened
|
|
164
|
+
- **Accent color:** Amber (`#F59E0B`) — used sparingly for brand moments and key highlights only
|
|
165
|
+
- **Secondary:** Muted slate for borders, dividers, and secondary text
|
|
166
|
+
- **Status colors:** Desaturated green/red for success/error — never neon
|
|
167
|
+
- **Wordmark:** Shown once on launch, then gone:
|
|
168
|
+
```
|
|
169
|
+
▸ scripy v0.1.0
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Glyph Language
|
|
173
|
+
|
|
174
|
+
Use a consistent set of prefix glyphs everywhere — headless and TUI:
|
|
175
|
+
|
|
176
|
+
| Glyph | Meaning |
|
|
177
|
+
|---|---|
|
|
178
|
+
| `▸` | In progress / working |
|
|
179
|
+
| `✓` | Success |
|
|
180
|
+
| `✗` | Failed |
|
|
181
|
+
| `~` | Warning / note |
|
|
182
|
+
| `?` | Awaiting user input |
|
|
183
|
+
|
|
184
|
+
Never mix in emoji or other unicode symbols.
|
|
185
|
+
|
|
186
|
+
### Headless (`rich`) Presentation
|
|
187
|
+
|
|
188
|
+
- **Spinner:** Custom sequence during inference — not the `rich` default dots. Use `▸ ▸▸ ▸▸▸` or similar
|
|
189
|
+
- **Script display:** Rendered in a `rich` syntax-highlighted panel with a subtle `box.MINIMAL` border; language label pinned to top-right corner of panel
|
|
190
|
+
- **Status lines:** Left-aligned, glyph-prefixed, single line per event — no multi-line prose
|
|
191
|
+
- **Completion line:** Always show elapsed time and file size:
|
|
192
|
+
```
|
|
193
|
+
✓ wrote dedup.py 892B 1.4s
|
|
194
|
+
```
|
|
195
|
+
- **Avoid `rich` defaults** — override panel borders, colors, and spinner to match the scripy palette
|
|
196
|
+
|
|
197
|
+
### TUI (`textual`) Layout
|
|
198
|
+
|
|
199
|
+
Single-screen, high information density — no wasted chrome:
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
┌─ scripy ──────────────────────────────────────────────────────┐
|
|
203
|
+
│ Log / conversation │ Script preview (live) │
|
|
204
|
+
│ │ │
|
|
205
|
+
│ ▸ generating... │ #!/usr/bin/env python3 │
|
|
206
|
+
│ ✓ syntax valid │ # dedup.py — find duplicate │
|
|
207
|
+
│ ▸ running sandbox... │ ... │
|
|
208
|
+
│ │ │
|
|
209
|
+
├──────────────────────────────┴────────────────────────────────┤
|
|
210
|
+
│ iteration 2/3 [y] run [n] skip [e] edit [v] view [a] always │
|
|
211
|
+
└───────────────────────────────────────────────────────────────┘
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
- Left pane: agent log / conversation events
|
|
215
|
+
- Right pane: live syntax-highlighted script preview, updates on each iteration
|
|
216
|
+
- Footer: persistent keybind bar — shows only contextually relevant keys at each gate
|
|
217
|
+
- Iteration counter always visible when in correction loop
|
|
218
|
+
- Confirmation gates render as animated footer prompt, never a modal
|
|
219
|
+
|
|
220
|
+
### Diff View on Revision
|
|
221
|
+
|
|
222
|
+
When the model revises a script after a failed run, display a unified diff between iterations so the user can see exactly what changed — don't just silently replace the preview.
|
|
223
|
+
|
|
224
|
+
### `--version` Output
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
scripy 0.1.0
|
|
228
|
+
model qwen2.5-coder:7b @ localhost:11434
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Always show the currently configured model, not just the scripy version.
|
|
232
|
+
|
|
233
|
+
### Implementation Notes for Claude Code
|
|
234
|
+
|
|
235
|
+
- Use `rich.console`, `rich.panel`, `rich.syntax`, `rich.box` — customize everything, do not use defaults
|
|
236
|
+
- TUI built with `textual` — layout via `compose()`, footer via `Footer` widget with custom bindings
|
|
237
|
+
- All styling centralized in `scripy/theme.py` — single source of truth for colors, glyphs, box styles
|
|
238
|
+
- Headless and TUI share glyph/color constants from `theme.py`
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## User Confirmation Gates
|
|
243
|
+
|
|
244
|
+
scripy prompts the user before any side-effectful action. Two distinct gates:
|
|
245
|
+
|
|
246
|
+
### 1. Run Gate (before `run_script`)
|
|
247
|
+
|
|
248
|
+
Displayed before sandboxed execution during the self-correction loop:
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
Run script to validate? [y/n/e/v/a] ›
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
| Key | Action |
|
|
255
|
+
|---|---|
|
|
256
|
+
| `y` | Run it |
|
|
257
|
+
| `n` | Skip execution; model continues without stdout/stderr feedback |
|
|
258
|
+
| `e` | Open script in `$EDITOR` before running |
|
|
259
|
+
| `v` | Print script to terminal, then re-prompt |
|
|
260
|
+
| `a` | Always yes — skip run gate for all remaining iterations this session |
|
|
261
|
+
|
|
262
|
+
The `a` option is important UX — the self-correction loop may call `run_script` up to 3 times, so users shouldn't have to confirm every iteration.
|
|
263
|
+
|
|
264
|
+
### 2. Write Gate (before `write_file`)
|
|
265
|
+
|
|
266
|
+
Displayed once when the agent is ready to finalize output:
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
Write to disk as dedup.py? [y/n] ›
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
| Key | Action |
|
|
273
|
+
|---|---|
|
|
274
|
+
| `y` | Write file |
|
|
275
|
+
| `n` | Abort write; print script to stdout instead |
|
|
276
|
+
|
|
277
|
+
### TUI Mode
|
|
278
|
+
|
|
279
|
+
In `--tui` mode, both gates render as a footer action bar with the same key bindings rather than stdin prompts. Core logic in `executor.py` must be gate-agnostic — the TUI and headless modes both call the same confirmation interface, just with different renderers.
|
|
280
|
+
|
|
281
|
+
### Non-interactive Mode
|
|
282
|
+
|
|
283
|
+
A `--yes` / `-y` flag bypasses all gates (equivalent to `a` on run, `y` on write). Intended for scripting scripy itself.
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Development Notes
|
|
288
|
+
|
|
289
|
+
- Keep tool execution sandboxed — use `subprocess` with `timeout`, never `exec()`
|
|
290
|
+
- Validate interpreter exists before invoking (`shutil.which`)
|
|
291
|
+
- TUI is fully optional — all core logic must work headlessly
|
|
292
|
+
- Prefer `rich.console` for non-TUI output so it degrades gracefully
|
scripy_cli-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bradley Slayter
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: scripy-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local-first CLI agent for generating small scripts using local LLMs
|
|
5
|
+
License: MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2026 Bradley Slayter
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Keywords: LM Studio,agent,cli,codegen,llm,local-ai,ollama
|
|
28
|
+
Classifier: Development Status :: 3 - Alpha
|
|
29
|
+
Classifier: Environment :: Console
|
|
30
|
+
Classifier: Intended Audience :: Developers
|
|
31
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
32
|
+
Classifier: Programming Language :: Python :: 3
|
|
33
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
35
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
36
|
+
Classifier: Topic :: Utilities
|
|
37
|
+
Requires-Python: >=3.11
|
|
38
|
+
Requires-Dist: click>=8.1
|
|
39
|
+
Requires-Dist: openai>=1.0
|
|
40
|
+
Requires-Dist: rich>=13.0
|
|
41
|
+
Requires-Dist: textual>=0.50
|
|
42
|
+
Provides-Extra: dev
|
|
43
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
44
|
+
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
45
|
+
Description-Content-Type: text/markdown
|
|
46
|
+
|
|
47
|
+
# ▸ scripy
|
|
48
|
+
|
|
49
|
+
Generate small, single-file scripts using locally hosted LLMs. One command, no cloud.
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
scripy -p "rename all my jpegs by date taken"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
scripy runs an agentic loop — generate, validate, self-correct, write — entirely on your machine via [Ollama](https://ollama.com) or [LM Studio](https://lmstudio.ai).
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
Requires Python 3.11+ and a running Ollama or LM Studio instance.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pipx install .
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Or for development:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install -e .
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Quick start
|
|
76
|
+
|
|
77
|
+
> **Note:** This assumes you're running Ollama on the same machine with `qwen2.5-coder:7b` installed. See [Configuration](#configuration) below for more detialed configuration steps.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Generate a Python script
|
|
81
|
+
scripy -p "find duplicate files in a directory"
|
|
82
|
+
|
|
83
|
+
# Specify output file
|
|
84
|
+
scripy -p "find duplicate files in a directory" -o dedup.py
|
|
85
|
+
|
|
86
|
+
# Generate Bash
|
|
87
|
+
scripy -p "backup my home directory to /tmp" --lang bash
|
|
88
|
+
|
|
89
|
+
# Modify an existing script
|
|
90
|
+
scripy -p "add a --dry-run flag" --input dedup.py
|
|
91
|
+
|
|
92
|
+
# Skip all confirmation prompts (for scripting)
|
|
93
|
+
scripy -p "..." -y
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Confirmation gates
|
|
99
|
+
|
|
100
|
+
scripy prompts before any side-effectful action.
|
|
101
|
+
|
|
102
|
+
**Before sandboxed execution:**
|
|
103
|
+
```
|
|
104
|
+
? run script to validate? [y/n/e/v/a] ›
|
|
105
|
+
```
|
|
106
|
+
| Key | Action |
|
|
107
|
+
|-----|--------|
|
|
108
|
+
| `y` | Run it |
|
|
109
|
+
| `n` | Skip — model continues without sandbox feedback |
|
|
110
|
+
| `e` | Open in `$EDITOR` before running |
|
|
111
|
+
| `v` | Print script to terminal, then re-prompt |
|
|
112
|
+
| `a` | Always yes — skip gate for all remaining iterations |
|
|
113
|
+
|
|
114
|
+
**Before writing to disk:**
|
|
115
|
+
```
|
|
116
|
+
? write to disk as dedup.py? [y/n] ›
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Use `-y` / `--yes` to bypass all gates non-interactively.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Configuration
|
|
124
|
+
|
|
125
|
+
Default config works with a local Ollama instance. Override via `~/.config/scripy/config.toml`:
|
|
126
|
+
|
|
127
|
+
```toml
|
|
128
|
+
[model]
|
|
129
|
+
base_url = "http://192.168.1.10:11434/v1" # remote Ollama
|
|
130
|
+
model = "llama3.1:8b"
|
|
131
|
+
api_key = "ollama" # use "lm-studio" for LM Studio
|
|
132
|
+
temperature = 0.2
|
|
133
|
+
max_tokens = 4096
|
|
134
|
+
|
|
135
|
+
[agent]
|
|
136
|
+
force_tools = true
|
|
137
|
+
max_iterations = 3
|
|
138
|
+
default_lang = "python"
|
|
139
|
+
sandbox_timeout = 10
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
CLI flags override config for a single run:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
scripy --model qwen2.5-coder:7b -p "..."
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Model recommendations
|
|
151
|
+
|
|
152
|
+
scripy targets models that run on small consumer hardware (~4–8GB RAM) but will obviously
|
|
153
|
+
excell on more powerful machines. That being said here are some reccomendations for
|
|
154
|
+
small machines.
|
|
155
|
+
|
|
156
|
+
| Model | Size | Tool calling | Code quality | Notes |
|
|
157
|
+
|-------|------|-------------|-------------|-------|
|
|
158
|
+
| `llama3.1:8b` | ~4.7GB | Native | Good | **Recommended default** |
|
|
159
|
+
| `llama3.2:3b` | ~2.0GB | Native | Fair | Best ultra-low-resource option |
|
|
160
|
+
| `qwen2.5-coder:7b` | ~4.4GB | Inline only | Excellent | Best code quality; see note below |
|
|
161
|
+
| `deepseek-coder:6.7b` | ~4.0GB | Inline only | Excellent | Same trade-off as qwen-coder |
|
|
162
|
+
|
|
163
|
+
### Tool calling — what this means in practice
|
|
164
|
+
|
|
165
|
+
scripy uses the OpenAI function-calling API to let the model invoke tools
|
|
166
|
+
(`write_file`, `run_script`, `read_file`, `list_directory`). Models fall into
|
|
167
|
+
two categories:
|
|
168
|
+
|
|
169
|
+
**Native tool calling** (`llama3.1`, `llama3.2`) — the model returns structured
|
|
170
|
+
`tool_calls` in the API response. The agentic loop runs cleanly; `run_script`
|
|
171
|
+
validation and multi-turn self-correction work as intended.
|
|
172
|
+
|
|
173
|
+
**Inline tool calling** (`qwen2.5-coder`, `deepseek-coder`) — these models
|
|
174
|
+
ignore the tool-calling API and instead append a JSON blob to their text
|
|
175
|
+
response. scripy detects and handles this automatically, but the behaviour is
|
|
176
|
+
less reliable: the self-correction loop may not fire, and you will see:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
~ inline tool call detected — model is not using structured tool calling
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`--force-tools` (`tool_choice=required`) is **on by default**. This works well
|
|
183
|
+
with native tool-calling models and is generally the right choice. If you see
|
|
184
|
+
errors or garbled output — which can happen with certain qwen-coder or
|
|
185
|
+
deepseek-coder builds on older Ollama versions — disable it via config:
|
|
186
|
+
|
|
187
|
+
```toml
|
|
188
|
+
# ~/.config/scripy/config.toml
|
|
189
|
+
[agent]
|
|
190
|
+
force_tools = false
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
With `force_tools` off, scripy falls back to inline tool-call detection automatically.
|
|
194
|
+
I've personally had better results using LM Studio for smaller models on small hardware
|
|
195
|
+
(think M2 mac mini 8GB RAM). Sometimes using OLLama the model kept appending tool calls
|
|
196
|
+
to the script output.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## CLI reference
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
Usage: scripy [OPTIONS]
|
|
204
|
+
|
|
205
|
+
scripy — generate scripts with local LLMs.
|
|
206
|
+
|
|
207
|
+
Options:
|
|
208
|
+
-p, --prompt TEXT What script to generate.
|
|
209
|
+
-o, --output TEXT Output file path.
|
|
210
|
+
-l, --lang TEXT Language override (python, bash, etc.).
|
|
211
|
+
--model TEXT Model name override.
|
|
212
|
+
--input TEXT Existing script to modify.
|
|
213
|
+
--tui Launch Textual TUI.
|
|
214
|
+
-y, --yes Skip all confirmation gates.
|
|
215
|
+
--force-tools Override config and set tool_choice=required for this run.
|
|
216
|
+
--version Show version and exit.
|
|
217
|
+
--help Show this message and exit.
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Current state
|
|
223
|
+
|
|
224
|
+
**Phase 1 — skeleton & config** ✓
|
|
225
|
+
**Phase 2 — headless agent** ✓
|
|
226
|
+
|
|
227
|
+
- Generate → syntax validate → sandbox run → self-correct → write loop
|
|
228
|
+
- Confirmation gates (run / write) with keyboard shortcuts
|
|
229
|
+
- Inline tool call detection and fallback for models that don't use the tool-calling API
|
|
230
|
+
- `~/.config/scripy/config.toml` config with CLI overrides
|
|
231
|
+
|
|
232
|
+
**Phase 3 — TUI** ✓
|
|
233
|
+
|
|
234
|
+
- Textual TUI with live script preview, diff view on revision, and confirmation gates
|
|
235
|
+
- Language picker (Ctrl+L), inline prompt compose, and refinement loop
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Development
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
pip install -e ".[dev]"
|
|
243
|
+
pytest
|
|
244
|
+
```
|