Add Docker support and documentation updates

- Add Dockerfile for containerized builds
- Add .dockerignore for cleaner builds
- Add CLAUDE.md for development guidance
- Update README and docs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rob 2025-12-23 23:40:13 -04:00
parent 7e9bd0d533
commit 65279bd8c8
9 changed files with 471 additions and 5 deletions

35
.dockerignore Normal file
View File

@ -0,0 +1,35 @@
# Git
.git
.gitignore
# Python
__pycache__
*.py[cod]
*$py.class
*.so
.Python
*.egg-info
.eggs
*.egg
dist
build
.venv
venv
ENV
# IDE
.idea
.vscode
*.swp
# Docker
Dockerfile*
docker-compose*
.docker
# Misc
*.md
!README.md
.coverage
.pytest_cache
htmlcov

80
CLAUDE.md Normal file
View File

@ -0,0 +1,80 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
SmartTools is a lightweight personal tool builder for AI-powered CLI commands. It lets users create custom terminal commands that call AI providers, chain prompts with Python code steps, and use them like any Unix pipe command.
## Development Commands
```bash
# Install for development
pip install -e ".[dev]"
# Run tests
pytest
# Run a single test
pytest tests/test.py::test_name
# Run the CLI
python -m smarttools.cli
# Launch the UI
smarttools ui
```
## Architecture
### Core Modules (`src/smarttools/`)
- **cli.py**: Entry point (`smarttools` command). Routes subcommands: list, create, edit, delete, test, run, ui, refresh
- **tool.py**: Tool definition dataclasses (`Tool`, `ToolArgument`, `PromptStep`, `CodeStep`), YAML config loading/saving, wrapper script generation
- **runner.py**: Execution engine. Runs tool steps sequentially, handles variable substitution (`{input}`, `{varname}`), executes Python code steps via `exec()`
- **providers.py**: Provider abstraction. Calls AI CLI tools via subprocess, reads provider configs from `~/.smarttools/providers.yaml`
- **ui.py**: UI dispatcher - selects between urwid and snack implementations
- **ui_urwid.py**: Full TUI implementation using urwid library
- **ui_snack.py**: Fallback TUI using python-newt/snack
### Key Paths
- **Tools storage**: `~/.smarttools/<toolname>/config.yaml`
- **Wrapper scripts**: `~/.local/bin/<toolname>` (auto-generated bash scripts)
- **Provider config**: `~/.smarttools/providers.yaml`
### Tool Structure
Tools are YAML configs with:
- `name`, `description`, `category`
- `arguments`: Custom flags with defaults (e.g., `--max``{max}`)
- `steps`: Ordered list of `prompt` or `code` steps
- `output`: Template for final output (e.g., `"{response}"`)
### Step Types
1. **Prompt Step**: Calls AI provider with template, stores result in `output_var`
2. **Code Step**: Executes Python code via `exec()`, captures specified variables
### Variable Flow
Variables are passed between steps:
- `{input}` - always available (stdin/file content)
- `{argname}` - from tool arguments
- `{step_output_var}` - from previous step's `output_var`
Variable substitution is simple string replacement in `runner.py:substitute_variables()`.
## Provider System
Providers are CLI tools that accept prompts via stdin and output to stdout. Defined in `~/.smarttools/providers.yaml`:
```yaml
providers:
- name: claude
command: "claude -p"
- name: mock
command: "echo '[MOCK]'"
```
The `mock` provider is built-in for testing without API calls.

45
Dockerfile Normal file
View File

@ -0,0 +1,45 @@
# SmartTools - AI-powered CLI command builder
# Build: docker build -t smarttools .
# Run: docker run -it --rm -v ~/.smarttools:/root/.smarttools smarttools
FROM python:3.12-slim
LABEL maintainer="rob"
LABEL description="SmartTools - Personal AI-powered CLI command builder"
# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
curl \
&& rm -rf /var/lib/apt/lists/*
# Set working directory
WORKDIR /app
# Copy project files
COPY pyproject.toml README.md ./
COPY src/ ./src/
COPY examples/ ./examples/
COPY docs/ ./docs/
# Install SmartTools and dependencies
RUN pip install --no-cache-dir -e ".[dev]"
# Create smarttools directory
RUN mkdir -p /root/.smarttools /root/.local/bin
# Install example tools
RUN python examples/install.py
# Generate CLI wrappers
RUN smarttools refresh
# Add local bin to PATH
ENV PATH="/root/.local/bin:${PATH}"
# Default command - show help
CMD ["smarttools", "--help"]
# For interactive use:
# docker run -it --rm smarttools smarttools ui
# docker run -it --rm smarttools bash

View File

@ -123,6 +123,59 @@ cat file.txt | mytool --dry-run
cat file.txt | mytool --provider mock
```
### Composing Tools
SmartTools follows Unix philosophy: each tool does one thing well, and tools chain together for complex workflows.
```bash
# Chain SmartTools together
cat error.log | log-errors | summarize | translate --lang Spanish
# Code review pipeline: focus on changes, review, then summarize
git diff | diff-focus | review-code | tldr
# Extract data, convert format, analyze
cat report.pdf | json-extract --fields "revenue, costs, date" | json2csv | csv-insights
# Process text through multiple transformations
cat technical_doc.txt | simplify | fix-grammar | tone-shift --tone friendly
# Generate code, validate it, then explain what it does
echo "parse CSV and calculate averages" | code-validate | explain-code
```
**Mix with standard Unix tools:**
```bash
# Extract emails, deduplicate, count
cat inbox.txt | extract-emails | sort -u | wc -l
# Find errors in multiple logs, explain them
cat *.log | grep -i error | explain-error
# Generate changelog for specific author
git log --author="rob" --oneline | changelog > CHANGELOG.md
# Review only large files in a commit
git diff --stat | awk '$3 > 100 {print $1}' | xargs cat | review-code
```
**Build shell functions for common pipelines:**
```bash
# ~/.bashrc
quick-review() {
git diff "$@" | diff-focus | review-code --focus "bugs and security" | tldr
}
translate-doc() {
cat "$1" | summarize | translate --lang "$2"
}
# Usage: quick-review HEAD~3
# Usage: translate-doc manual.txt French
```
## Example Tools
SmartTools comes with 28 pre-built examples you can install:
@ -347,12 +400,17 @@ This lets you generate or modify Python code using AI directly within the tool b
## Philosophy
SmartTools is a **personal power tool**:
SmartTools is built on **Unix philosophy**:
- **You own your tools** - YAML files you can read, edit, share
- **You choose the AI** - Any provider, swap anytime
- **You accept responsibility** - No sandboxing, like any script you write
- **Unix philosophy** - Small tools that compose
1. **Do one thing well** - Each tool solves one problem. `summarize` summarizes. `translate` translates. No bloated mega-tools.
2. **Compose freely** - Tools read stdin, write stdout. Chain them: `cat doc.txt | summarize | translate --lang French`
3. **Text is the interface** - No proprietary formats. Pipe text in, get text out. Works with `grep`, `awk`, `sed`, and every other Unix tool.
4. **You own everything** - Tools are YAML files. Prompts are plain text. No vendor lock-in, no cloud dependency for your tool definitions.
5. **Swap parts freely** - Change AI providers per-tool or per-run. Today's best model might not be tomorrow's.
## Contributing

View File

@ -317,6 +317,50 @@ Tool names must:
- Contain only letters, numbers, underscores, and dashes
- Not contain spaces or shell-problematic characters (`/\|&;$`"'<>(){}[]!?*#~`)
## Tool Composition
SmartTools are designed to chain together like any Unix command.
### External Pipelines (Tool-to-Tool)
```bash
# Chain multiple SmartTools
cat logs.txt | log-errors | summarize | translate --lang Japanese
# Mix with standard Unix tools
git log --oneline | head -20 | changelog | tee CHANGELOG.md
# Build complex workflows
cat *.py | review-code --focus security | json-extract --fields "issue, severity, file" | json2csv
```
Each tool reads stdin and writes stdout. No special integration needed.
### Internal Pipelines (Multi-Step)
Within a single tool, chain steps for preprocessing/validation:
```yaml
steps:
- type: code # Preprocess
code: |
filtered = '\n'.join(l for l in input.split('\n') if 'ERROR' in l)
output_var: filtered
- type: prompt # AI processes filtered input
prompt: "Explain these errors: {filtered}"
provider: claude-haiku
output_var: explanation
- type: code # Post-process
code: |
result = f"Found {len(filtered.split(chr(10)))} errors:\n\n{explanation}"
output_var: result
output: "{result}"
```
**When to use which:**
- **External pipelines**: Reusable tools, different providers per stage, standard Unix interop
- **Internal pipelines**: Tightly coupled steps, shared context, validation of AI output
## What This Design Doesn't Include
Intentionally omitted (not needed for personal use):

View File

@ -958,3 +958,101 @@ output: "{result}"
2. **Use code steps** to validate AI output
3. **Preprocess large inputs** to reduce tokens
4. **Test with mock** before using real AI: `--provider mock`
---
## Pipeline Recipes
SmartTools chain together like Unix commands. Here are practical examples:
### Development Workflows
```bash
# Quick PR review: extract changes, review, summarize
git diff main | diff-focus | review-code --focus "bugs and security" | tldr
# Explain and fix an error in one pipeline
python script.py 2>&1 | explain-error | tee error_analysis.txt
# Generate tests for changed files only
git diff --name-only | grep '\.py$' | xargs cat | gen-tests > new_tests.py
# Create release notes from commits
git log v1.0..v1.1 --oneline | changelog | translate --lang French > RELEASE_FR.md
```
### Data Processing
```bash
# Extract, transform, analyze
curl -s api.example.com/data | json-extract --fields "users, revenue" | json2csv | csv-insights
# Process multiple files
for f in reports/*.txt; do
cat "$f" | json-extract --fields "total, date"
done | json2csv > summary.csv
# Clean and deduplicate contacts
cat *.vcf | extract-contacts | sort -u -t',' -k2 > contacts.csv
```
### Text Processing Pipelines
```bash
# Translate technical docs for international team
cat API.md | simplify --level "non-technical" | translate --lang Spanish > API_ES.md
# Process customer feedback
cat feedback.txt | summarize --length "10 points" | tone-shift --tone analytical | json-extract --fields "themes, sentiment"
# Prepare content for different audiences
cat whitepaper.txt | eli5 > simple_version.txt
cat whitepaper.txt | summarize > executive_summary.txt
```
### Shell Functions
Add these to `~/.bashrc` for common workflows:
```bash
# Review recent changes
review-recent() {
git diff HEAD~"${1:-1}" | diff-focus | review-code | tldr
}
# Quick translate with summary
translate-summary() {
cat "$1" | summarize | translate --lang "${2:-Spanish}"
}
# Analyze any log file
analyze-log() {
cat "$1" | log-errors | summarize --length "5 key issues"
}
# Generate commit message and commit
auto-commit() {
msg=$(git diff --staged | commit-msg)
echo "Commit message: $msg"
read -p "Commit? [y/N] " confirm
[[ $confirm == [yY] ]] && git commit -m "$msg"
}
```
### Combining with Standard Unix Tools
```bash
# Filter before AI processing (saves tokens)
cat huge.log | grep -i error | tail -100 | explain-error
# Post-process AI output
cat doc.txt | summarize | fmt -w 80 > formatted_summary.txt
# Parallel processing
find . -name "*.py" | parallel -j4 'cat {} | review-code > {}.review'
# Watch and process
tail -f app.log | grep --line-buffered ERROR | while read line; do
echo "$line" | explain-error
done
```

View File

@ -263,6 +263,79 @@ def cmd_refresh(args):
return 0
def cmd_docs(args):
"""View or edit tool documentation."""
import os
import subprocess
from .tool import get_tools_dir
tool = load_tool(args.name)
if not tool:
print(f"Error: Tool '{args.name}' not found.")
return 1
readme_path = get_tools_dir() / args.name / "README.md"
if args.edit:
# Edit/create README
editor = os.environ.get("EDITOR", "nano")
# Create a template if README doesn't exist
if not readme_path.exists():
template = f"""# {args.name}
{tool.description or 'No description provided.'}
## Usage
```bash
echo "input" | {args.name}
```
## Arguments
| Flag | Default | Description |
|------|---------|-------------|
"""
for arg in tool.arguments:
template += f"| `{arg.flag}` | {arg.default or ''} | {arg.description or ''} |\n"
template += """
## Examples
```bash
# Example 1
```
## Requirements
- List any dependencies here
"""
readme_path.write_text(template)
print(f"Created template: {readme_path}")
try:
subprocess.run([editor, str(readme_path)], check=True)
print(f"Documentation updated: {readme_path}")
return 0
except subprocess.CalledProcessError:
print("Error: Editor failed.")
return 1
except FileNotFoundError:
print(f"Error: Editor '{editor}' not found. Set $EDITOR environment variable.")
return 1
else:
# View README
if not readme_path.exists():
print(f"No documentation found for '{args.name}'.")
print(f"Create it with: smarttools docs {args.name} --edit")
return 1
print(readme_path.read_text())
return 0
def main():
"""Main CLI entry point."""
parser = argparse.ArgumentParser(
@ -326,6 +399,12 @@ def main():
p_refresh = subparsers.add_parser("refresh", help="Refresh all wrapper scripts")
p_refresh.set_defaults(func=cmd_refresh)
# 'docs' command
p_docs = subparsers.add_parser("docs", help="View or edit tool documentation")
p_docs.add_argument("name", help="Tool name")
p_docs.add_argument("-e", "--edit", action="store_true", help="Edit/create README in $EDITOR")
p_docs.set_defaults(func=cmd_docs)
args = parser.parse_args()
# If no command, launch UI

10
tests/diagram.puml Normal file
View File

@ -0,0 +1,10 @@
@startuml
start
:Set a = 5;
:Set b = 6;
:Set c = 7;
:Calculate semi-perimeter s = (a + b + c) / 2;
:Calculate area = sqrt(s*(s-a)*(s-b)*(s-c));
:Print "The area of the triangle is [area]";
stop
@enduml

17
tests/test.py Normal file
View File

@ -0,0 +1,17 @@
# Python Program to find the area of triangle
a = 5
b = 6
c = 7
# Uncomment below to take inputs from the user
# a = float(input('Enter first side: '))
# b = float(input('Enter second side: '))
# c = float(input('Enter third side: '))
# calculate the semi-perimeter
s = (a + b + c) / 2
# calculate the area
area = (s*(s-a)*(s-b)*(s-c)) ** 0.5
print('The area of the triangle is %0.2f' %area)