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:
parent
7e9bd0d533
commit
65279bd8c8
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
68
README.md
68
README.md
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
Loading…
Reference in New Issue