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.
@@ -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
@@ -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
+ ```