Initial commit: SmartTools design and README
A lightweight personal tool builder for AI-powered CLI commands. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
commit
ba18c0feaa
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Project specific
|
||||||
|
*.log
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
# SmartTools
|
||||||
|
|
||||||
|
A lightweight personal tool builder for AI-powered CLI commands.
|
||||||
|
|
||||||
|
## What is this?
|
||||||
|
|
||||||
|
SmartTools lets you create custom AI-powered terminal commands. You define a tool once (name, prompt, provider), then use it like any Linux command.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create a summarizer, then use it like any command:
|
||||||
|
sum -i document.txt -o summary.txt --max 512
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Example
|
||||||
|
|
||||||
|
A tool is just a YAML config:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# ~/.smarttools/sum/config.yaml
|
||||||
|
name: sum
|
||||||
|
description: "Summarize documents"
|
||||||
|
prompt: |
|
||||||
|
Summarize the following text in {max} words or less:
|
||||||
|
{input}
|
||||||
|
provider: codex
|
||||||
|
provider_args: "-p"
|
||||||
|
inputs:
|
||||||
|
- name: max
|
||||||
|
flag: --max
|
||||||
|
default: 500
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sum -i myfile.txt -o summary.txt
|
||||||
|
sum -i myfile.txt --dry-run # preview prompt
|
||||||
|
sum -i myfile.txt --provider mock # test without API
|
||||||
|
```
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Simple UI** - Create/edit/delete tools with `dialog`-based interface
|
||||||
|
- **CLI-first** - Tools work like regular Linux commands
|
||||||
|
- **Provider-agnostic** - Use Codex, Claude, Gemini, or any CLI AI tool
|
||||||
|
- **Testing built-in** - `--dry-run`, `--show-prompt`, `--provider mock`
|
||||||
|
- **Minimal** - ~430 lines of Python, minimal dependencies
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install smarttools
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Launch UI to manage tools
|
||||||
|
smarttools
|
||||||
|
|
||||||
|
# Or use CLI:
|
||||||
|
smarttools list
|
||||||
|
smarttools create mytool
|
||||||
|
smarttools edit mytool
|
||||||
|
smarttools delete mytool
|
||||||
|
smarttools test mytool
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
See [docs/DESIGN.md](docs/DESIGN.md) for the full design document.
|
||||||
|
|
||||||
|
## Philosophy
|
||||||
|
|
||||||
|
This is a **personal power tool**. You write the tools, you run the tools, you accept the responsibility. No trust tiers, no sandboxing, no signing - just like any bash script you write.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|
@ -0,0 +1,304 @@
|
||||||
|
# SmartTools - Simple Design
|
||||||
|
|
||||||
|
> A lightweight personal tool builder for AI-powered CLI commands
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
SmartTools lets you create custom AI-powered terminal commands. You define a tool once (name, prompt, provider), then use it like any Linux command.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```bash
|
||||||
|
# Create a summarizer tool, then use it:
|
||||||
|
sum -i text.txt -o summary.txt --max 512
|
||||||
|
```
|
||||||
|
|
||||||
|
## Core Concepts
|
||||||
|
|
||||||
|
### Tool = Directory + Config
|
||||||
|
|
||||||
|
```
|
||||||
|
~/.smarttools/
|
||||||
|
sum/
|
||||||
|
config.yaml
|
||||||
|
post.py # optional post-processing script
|
||||||
|
reviewer/
|
||||||
|
config.yaml
|
||||||
|
translator/
|
||||||
|
config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Minimal config.yaml
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: sum
|
||||||
|
description: "Summarize documents"
|
||||||
|
prompt: |
|
||||||
|
Summarize the following text in {max} words or less:
|
||||||
|
|
||||||
|
{input}
|
||||||
|
|
||||||
|
provider: codex
|
||||||
|
provider_args: "-p"
|
||||||
|
|
||||||
|
# Optional
|
||||||
|
inputs:
|
||||||
|
- name: max
|
||||||
|
flag: --max
|
||||||
|
default: 500
|
||||||
|
|
||||||
|
# Optional post-processing script
|
||||||
|
post_process: post.py
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it. No trust tiers, no signing, no containers.
|
||||||
|
|
||||||
|
## CLI Interface
|
||||||
|
|
||||||
|
### Running Tools
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Basic usage
|
||||||
|
sum -i document.txt -o summary.txt
|
||||||
|
|
||||||
|
# With custom args
|
||||||
|
sum -i document.txt --max 200
|
||||||
|
|
||||||
|
# Preview prompt without calling AI
|
||||||
|
sum -i document.txt --dry-run
|
||||||
|
|
||||||
|
# See what prompt gets sent
|
||||||
|
sum -i document.txt --show-prompt
|
||||||
|
|
||||||
|
# Test with mock (no API call)
|
||||||
|
sum -i document.txt --provider mock
|
||||||
|
|
||||||
|
# Read from stdin, write to stdout
|
||||||
|
cat doc.txt | sum | less
|
||||||
|
```
|
||||||
|
|
||||||
|
### Universal Flags (all tools)
|
||||||
|
|
||||||
|
| Flag | Short | Description |
|
||||||
|
|------|-------|-------------|
|
||||||
|
| `--input` | `-i` | Input file (or stdin if omitted) |
|
||||||
|
| `--output` | `-o` | Output file (or stdout if omitted) |
|
||||||
|
| `--dry-run` | | Show prompt, don't call AI |
|
||||||
|
| `--show-prompt` | | Call AI but also print prompt to stderr |
|
||||||
|
| `--provider` | `-p` | Override provider (e.g., `--provider mock`) |
|
||||||
|
| `--verbose` | `-v` | Show debug info |
|
||||||
|
| `--help` | `-h` | Show help |
|
||||||
|
|
||||||
|
### Managing Tools
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Launch the UI to manage tools
|
||||||
|
smarttools
|
||||||
|
|
||||||
|
# Or use CLI directly:
|
||||||
|
smarttools list # List all tools
|
||||||
|
smarttools create sum # Create new tool
|
||||||
|
smarttools edit sum # Edit existing tool
|
||||||
|
smarttools delete sum # Delete tool
|
||||||
|
smarttools test sum # Test with mock provider
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lightweight UI
|
||||||
|
|
||||||
|
A simple terminal UI using `dialog` or `whiptail` (available on most Linux systems).
|
||||||
|
|
||||||
|
### Main Menu
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────┐
|
||||||
|
│ SmartTools Manager │
|
||||||
|
├──────────────────────────────────────┤
|
||||||
|
│ > List Tools │
|
||||||
|
│ Create New Tool │
|
||||||
|
│ Edit Tool │
|
||||||
|
│ Delete Tool │
|
||||||
|
│ Test Tool │
|
||||||
|
│ Exit │
|
||||||
|
└──────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create/Edit Tool Form
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────┐
|
||||||
|
│ Create New Tool │
|
||||||
|
├──────────────────────────────────────┤
|
||||||
|
│ Name: [sum________________] │
|
||||||
|
│ Description: [Summarize documents_] │
|
||||||
|
│ Provider: [codex_____________] │
|
||||||
|
│ Provider Args: [-p________________] │
|
||||||
|
│ │
|
||||||
|
│ Prompt: │
|
||||||
|
│ ┌──────────────────────────────────┐ │
|
||||||
|
│ │ Summarize the following text │ │
|
||||||
|
│ │ in {max} words or less: │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ {input} │ │
|
||||||
|
│ └──────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Custom Arguments: │
|
||||||
|
│ --max (default: 500) │
|
||||||
|
│ [Add Argument] │
|
||||||
|
│ │
|
||||||
|
│ Post-process Script: [none______▼] │
|
||||||
|
│ │
|
||||||
|
│ [Save] [Test] [Cancel] │
|
||||||
|
└──────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Tool
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────┐
|
||||||
|
│ Test Tool: sum │
|
||||||
|
├──────────────────────────────────────┤
|
||||||
|
│ Input: [Select file...] or [Paste] │
|
||||||
|
│ │
|
||||||
|
│ ┌─ Final Prompt ───────────────────┐ │
|
||||||
|
│ │ Summarize the following text │ │
|
||||||
|
│ │ in 500 words or less: │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Lorem ipsum dolor sit amet... │ │
|
||||||
|
│ └──────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Provider: [mock_____▼] │
|
||||||
|
│ │
|
||||||
|
│ [Run Test] [Copy Command] │
|
||||||
|
└──────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
"Copy Command" shows: `sum -i test.txt --dry-run`
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
### Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
smarttools/
|
||||||
|
__init__.py
|
||||||
|
cli.py # Entry point, argument parsing
|
||||||
|
ui.py # Dialog-based UI
|
||||||
|
tool.py # Tool loading/saving
|
||||||
|
runner.py # Execute tools
|
||||||
|
providers.py # Provider abstraction (minimal)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Provider Abstraction (Simple)
|
||||||
|
|
||||||
|
```python
|
||||||
|
# providers.py
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
def call_provider(provider: str, args: str, prompt: str) -> str:
|
||||||
|
"""Call an AI CLI tool with the given prompt."""
|
||||||
|
cmd = f"{provider} {args}"
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
shell=True,
|
||||||
|
input=prompt,
|
||||||
|
capture_output=True,
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise RuntimeError(f"Provider failed: {result.stderr}")
|
||||||
|
return result.stdout
|
||||||
|
|
||||||
|
def mock_provider(prompt: str) -> str:
|
||||||
|
"""Return a mock response for testing."""
|
||||||
|
return f"[MOCK RESPONSE]\nReceived prompt of {len(prompt)} chars"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tool Runner (Simple)
|
||||||
|
|
||||||
|
```python
|
||||||
|
# runner.py
|
||||||
|
import yaml
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
TOOLS_DIR = Path.home() / ".smarttools"
|
||||||
|
|
||||||
|
def load_tool(name: str) -> dict:
|
||||||
|
config_path = TOOLS_DIR / name / "config.yaml"
|
||||||
|
return yaml.safe_load(config_path.read_text())
|
||||||
|
|
||||||
|
def build_prompt(tool: dict, input_text: str, args: dict) -> str:
|
||||||
|
prompt = tool["prompt"]
|
||||||
|
prompt = prompt.replace("{input}", input_text)
|
||||||
|
for key, value in args.items():
|
||||||
|
prompt = prompt.replace(f"{{{key}}}", str(value))
|
||||||
|
return prompt
|
||||||
|
|
||||||
|
def run_tool(name: str, input_text: str, args: dict, provider_override: str = None) -> str:
|
||||||
|
tool = load_tool(name)
|
||||||
|
prompt = build_prompt(tool, input_text, args)
|
||||||
|
|
||||||
|
provider = provider_override or tool["provider"]
|
||||||
|
if provider == "mock":
|
||||||
|
return mock_provider(prompt)
|
||||||
|
|
||||||
|
return call_provider(provider, tool.get("provider_args", ""), prompt)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generated Wrapper Script
|
||||||
|
|
||||||
|
When you create a tool, SmartTools generates a wrapper script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# ~/.smarttools/sum/sum (auto-generated)
|
||||||
|
exec python3 -m smarttools.run sum "$@"
|
||||||
|
```
|
||||||
|
|
||||||
|
And symlinks it to `~/.local/bin/sum` so you can call `sum` directly.
|
||||||
|
|
||||||
|
## What This Design Doesn't Include
|
||||||
|
|
||||||
|
Intentionally omitted (not needed for personal use):
|
||||||
|
|
||||||
|
- Trust tiers / security levels
|
||||||
|
- Cryptographic signing
|
||||||
|
- Container isolation / sandboxing
|
||||||
|
- Certification testing
|
||||||
|
- Distribution packaging
|
||||||
|
- PII redaction
|
||||||
|
- Audit logging
|
||||||
|
- Provider capability negotiation
|
||||||
|
|
||||||
|
**Why?** This is a personal tool builder. You write the tools, you run the tools, you accept the responsibility. Just like any bash script you write.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
Minimal:
|
||||||
|
- Python 3.10+
|
||||||
|
- PyYAML
|
||||||
|
- `dialog` or `whiptail` (pre-installed on most Linux)
|
||||||
|
|
||||||
|
Optional:
|
||||||
|
- `textual` (if you want a fancier TUI later)
|
||||||
|
|
||||||
|
## File Sizes
|
||||||
|
|
||||||
|
Estimated implementation:
|
||||||
|
- `cli.py`: ~100 lines
|
||||||
|
- `ui.py`: ~150 lines
|
||||||
|
- `tool.py`: ~80 lines
|
||||||
|
- `runner.py`: ~60 lines
|
||||||
|
- `providers.py`: ~40 lines
|
||||||
|
|
||||||
|
**Total: ~430 lines of Python**
|
||||||
|
|
||||||
|
## Example Workflow
|
||||||
|
|
||||||
|
1. Run `smarttools` to open UI
|
||||||
|
2. Select "Create New Tool"
|
||||||
|
3. Fill in: name=`sum`, prompt, provider=`codex`
|
||||||
|
4. Click "Test" to verify with mock provider
|
||||||
|
5. Click "Save"
|
||||||
|
6. Exit UI
|
||||||
|
7. Run `sum -i myfile.txt -o summary.txt`
|
||||||
|
|
||||||
|
Done. No containers, no signing, no certification. Just a tool that works.
|
||||||
Loading…
Reference in New Issue