12 KiB
SmartTools - Design Document
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, steps, provider), then use it like any Linux command.
Example:
# 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
processed.py # Optional external code file
reviewer/
config.yaml
translator/
config.yaml
config.yaml Format
name: sum
description: "Summarize documents"
arguments:
- flag: --max
variable: max
default: "500"
description: "Maximum words in summary"
steps:
- type: prompt
prompt: |
Summarize the following text in {max} words or less:
{input}
provider: claude
output_var: response
output: "{response}"
Step Types
Prompt Step - Calls an AI provider:
- type: prompt
prompt: "Your prompt template with {variables}"
provider: claude
output_var: response
Code Step - Runs Python code:
- type: code
code: |
processed = input.upper()
count = len(processed.split())
output_var: processed, count
code_file: processed.py # Optional: external file storage
Steps execute in order. Each step's output_var becomes available to subsequent steps.
Variables
{input}- Always available, contains stdin or input file content (empty string if no input){variable_name}- From arguments (e.g.,{max}){output_var}- From previous steps (e.g.,{response},{processed})
Output Variables
The output_var field specifies which Python variable(s) to capture from your code:
Single variable:
output_var: processed
processed = input.upper() # This gets captured
Multiple variables (comma-separated):
output_var: processed, count, summary
processed = input.upper()
count = len(processed.split())
summary = f"Processed {count} words"
# All three are captured and available as {processed}, {count}, {summary}
CLI Interface
Running Tools
# Basic usage (wrapper script in ~/.local/bin)
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
# Interactive stdin input
sum --stdin
# No input (empty string) - useful for tools using only arguments
sum --max 100
# Or via smarttools run
smarttools run sum -i document.txt
Input Handling
| Scenario | Behavior |
|---|---|
| Piped stdin | Automatically read (cat file.txt | mytool) |
-i file.txt |
Read from file |
--stdin |
Interactive input (type then Ctrl+D) |
| No input | Empty string (useful for argument-only tools) |
Universal Flags (all tools)
| Flag | Short | Description |
|---|---|---|
--input |
-i |
Input file |
--output |
-o |
Output file (or stdout if omitted) |
--stdin |
Read input interactively (type then Ctrl+D) | |
--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
# Launch the UI to manage tools
smarttools
# Or use CLI directly:
smarttools list # List all tools
smarttools create sum # Create new tool (basic)
smarttools edit sum # Edit tool config in $EDITOR
smarttools delete sum # Delete tool
smarttools test sum # Test with mock provider
smarttools run sum # Run tool for real
smarttools refresh # Refresh all wrapper scripts
smarttools ui # Launch interactive UI
Terminal UI
A BIOS-style terminal UI using urwid with full mouse support.
Main Menu
┌──────────────────────────────────────┐
│ SmartTools Manager │
├──────────────────────────────────────┤
│ Tools: │
│ ┌────────────────────────────────┐ │
│ │ sum │ │
│ │ reviewer │ │
│ │ translator │ │
│ └────────────────────────────────┘ │
│ │
│ [Create] [Edit] [Delete] [Test] │
│ │
│ Tool Info: │
│ Name: sum │
│ Description: Summarize documents │
│ Arguments: --max │
│ Steps: PROMPT[claude] -> {response} │
│ │
│ [EXIT] │
└──────────────────────────────────────┘
Tool Builder
┌──────────────────────────────────────────────────────────────┐
│ New Tool │
├──────────────────────────────────────────────────────────────┤
│ ┌─[ Tool Info ]──────────┐ ┌─ Arguments ─────────────────┐ │
│ │ Name: [sum__________] │ │ --max -> {max} │ │
│ │ │ │ │ │
│ │ Desc: [Summarize____] │ └─────────────────────────────┘ │
│ │ │ [Add] [Edit] [Delete] │
│ │ Output: [{response}_] │ │
│ └────────────────────────┘ ┌─ Execution Steps ───────────┐ │
│ │ P:claude -> {response} │ │
│ │ │ │
│ └─────────────────────────────┘ │
│ [Add] [Edit] [Delete] │
│ │
│ [Save] [Cancel] │
└──────────────────────────────────────────────────────────────┘
Navigation:
- Tab - Cycle between sections (Tool Info -> Arguments -> Steps -> Buttons)
- Arrow keys - Navigate within lists
- Enter/Click - Select/activate
- Escape - Cancel/back
- Shift + mouse - Terminal-native text selection (bypasses UI mouse handling)
- Ctrl+Shift+V - Paste
The current section's title is highlighted with brackets: [ Tool Info ]
Copy/Paste tip: Hold Shift while dragging with mouse to select text, then use your terminal's copy function (usually right-click or Ctrl+Shift+C).
Code Step Dialog
┌──────────────── Add Code Step ─────────────────┐
│ Variables: input, max, response │
│ │
│ File: [processed.py______________] < Load > │
│ │
│ ┌─ Code ─────────────────────────────────────┐ │
│ │processed = input.upper() │ │
│ │count = len(processed.split()) │ │
│ │ │ │
│ └────────────────────────────────────────────┘ │
│ │
│ Output var: [processed, count____] │
├────────────────────────────────────────────────┤
│ < OK > < Cancel > │
└────────────────────────────────────────────────┘
Code Step Features:
- Multiline editor - Write multi-line Python code
- External file storage - Code is auto-saved to
~/.smarttools/<toolname>/<filename>on OK - Load button - Load code from external file (with confirmation)
- Multiple output vars - Capture multiple variables (comma-separated)
Implementation
Directory Structure
smarttools/
__init__.py
cli.py # Entry point, argument parsing
ui.py # UI dispatcher (chooses urwid/snack/dialog)
ui_urwid.py # Urwid-based BIOS-style UI
ui_snack.py # Snack/newt fallback UI
tool.py # Tool loading/saving/wrapper scripts
runner.py # Execute tools
providers.py # Provider abstraction
Provider System
Providers are defined in ~/.smarttools/providers.yaml:
providers:
- name: claude
command: "claude -p"
description: "Anthropic Claude"
- name: codex
command: "codex -p"
description: "OpenAI Codex"
- name: gemini
command: "gemini"
description: "Google Gemini"
The provider command receives the prompt via stdin and outputs to stdout.
Wrapper Scripts
When you save a tool, SmartTools creates a wrapper script in ~/.local/bin/:
#!/bin/bash
# SmartTools wrapper for 'sum'
# Auto-generated - do not edit
exec /path/to/python -m smarttools.runner sum "$@"
The wrapper uses the full Python path to ensure the correct environment is used.
Use smarttools refresh to regenerate all wrapper scripts (e.g., after changing Python environments).
Tool Name Validation
Tool names must:
- Start with a letter or underscore
- Contain only letters, numbers, underscores, and dashes
- Not contain spaces or shell-problematic characters (
/\|&;$"'<>(){}[]!?*#~`)
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
Required:
- Python 3.10+
- PyYAML
- urwid (for TUI)
Optional fallbacks:
- python3-newt/snack (simpler TUI)
- dialog/whiptail (basic TUI)
Example Workflow
- Run
smarttoolsto open UI - Select "Create" to create a new tool
- Fill in: name, description, output template
- Add arguments (e.g.,
--maxwith default500) - Add a prompt step with your prompt template and provider
- Click "Save"
- Exit UI
- Run
sum -i myfile.txt -o summary.txt
Done. No containers, no signing, no certification. Just a tool that works.
Default Values
| Step Type | Default Output Var | Default File |
|---|---|---|
| Prompt | response |
n/a |
| Code | processed |
processed.py |