Move docs to project-docs symlink structure

- Docs now point to project-docs/docs/projects/cmdforge
- Old docs preserved in olddocs/
- Updated .gitignore to ignore docs symlink

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

Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
rob
2026-01-06 22:26:39 -04:00
co-authored by Claude Opus 4.5
parent 1caa3454f0
commit 9fbe45244d
11 changed files with 3 additions and 0 deletions
+116
View File
@@ -0,0 +1,116 @@
# CmdForge Collections
Collections are curated groups of tools that can be installed together with a single command.
## Use Cases
1. **Thematic bundles**: "writing-toolkit" with grammar, tone, simplify tools
2. **Application stacks**: "data-science" with json-extract, csv-insights, etc.
3. **Source bundles**: "fabric-text" with all Fabric text processing patterns
4. **Workflow packages**: Tools that work well together for a specific task
## Collection Manifest Format
Collections are defined in the registry repo under `collections/`:
```yaml
# collections/writing-toolkit.yaml
name: writing-toolkit
display_name: Writing Toolkit
description: Essential tools for writers and editors
icon: pencil # Optional icon identifier
maintainer: official # Publisher who maintains this collection
tools:
- official/fix-grammar
- official/simplify
- official/tone-shift
- official/expand
- official/proofread
# Optional: version constraints
pinned:
official/fix-grammar: "^1.0.0"
# Optional: suggested order for documentation
order:
- official/fix-grammar
- official/proofread
- official/simplify
- official/tone-shift
- official/expand
tags:
- writing
- editing
- grammar
```
## CLI Usage
```bash
# List available collections
cmdforge collections list
# View collection details
cmdforge collections info writing-toolkit
# Install all tools in a collection
cmdforge collections install writing-toolkit
# Install with version constraints from collection
cmdforge collections install writing-toolkit --pinned
```
## API Endpoints
```
GET /api/v1/collections # List all collections
GET /api/v1/collections/:name # Get collection details with tool info
GET /api/v1/collections/:name/tools # Get just the tools list
```
## Database Schema
```sql
CREATE TABLE collections (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
display_name TEXT NOT NULL,
description TEXT,
icon TEXT,
maintainer TEXT NOT NULL,
tools TEXT NOT NULL, -- JSON array of tool refs
pinned TEXT, -- JSON object of version constraints
tags TEXT, -- JSON array
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_collections_name ON collections(name);
CREATE INDEX idx_collections_maintainer ON collections(maintainer);
```
## Web UI
- `/collections` - Browse all collections
- `/collections/:name` - Collection detail page with tool grid
- Install button that shows CLI command
- Filter by tag, maintainer
## Sync from Registry Repo
Collections sync from the registry repo just like tools:
```
CmdForge-Registry/
├── tools/
│ └── ...
└── collections/
├── writing-toolkit.yaml
├── data-science.yaml
├── fabric-text.yaml
└── fabric-code.yaml
```
The webhook sync process reads `collections/*.yaml` and upserts to database.
+264
View File
@@ -0,0 +1,264 @@
# CmdForge Deployment Guide
This guide covers deploying the CmdForge Registry web application.
## Quick Start (Development)
```bash
# Clone and install
git clone https://gitea.brrd.tech/rob/CmdForge.git
cd CmdForge
pip install -e ".[dev]"
# Initialize database
cd src/cmdforge/web
python -c "from app import create_app; create_app()"
# Run development server
flask run --port 5050 --host 0.0.0.0
```
Access at `http://localhost:5050`
## Production Deployment
### Prerequisites
- Python 3.8+
- pip/pipx
- nginx (recommended for reverse proxy)
- systemd (for process management)
### 1. Install Application
```bash
# Create application directory
sudo mkdir -p /opt/cmdforge
sudo chown $USER:$USER /opt/cmdforge
cd /opt/cmdforge
# Clone repository
git clone https://gitea.brrd.tech/rob/CmdForge.git .
# Create virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -e ".[prod]"
pip install gunicorn
```
### 2. Configure Environment
Create `/opt/cmdforge/.env`:
```bash
# Required
FLASK_ENV=production
SECRET_KEY=your-secret-key-here # Generate with: python -c "import secrets; print(secrets.token_hex(32))"
# Optional
SENTRY_DSN=https://[email protected]/xxx # Error monitoring
SITE_URL=https://cmdforge.brrd.tech # For sitemap/SEO
```
### 3. Initialize Database
```bash
cd /opt/cmdforge/src/cmdforge/web
python -c "from app import create_app; create_app()"
```
Database will be created at `data/cmdforge.db`.
### 4. systemd Service
Create `/etc/systemd/system/cmdforge-web.service`:
```ini
[Unit]
Description=CmdForge Registry Web Application
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/cmdforge
Environment="PATH=/opt/cmdforge/venv/bin"
EnvironmentFile=/opt/cmdforge/.env
ExecStart=/opt/cmdforge/venv/bin/gunicorn \
--workers 4 \
--bind 127.0.0.1:5050 \
--access-logfile /var/log/cmdforge/access.log \
--error-logfile /var/log/cmdforge/error.log \
'cmdforge.web.app:create_app()'
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
```
Enable and start:
```bash
# Create log directory
sudo mkdir -p /var/log/cmdforge
sudo chown www-data:www-data /var/log/cmdforge
# Enable service
sudo systemctl daemon-reload
sudo systemctl enable cmdforge-web
sudo systemctl start cmdforge-web
# Check status
sudo systemctl status cmdforge-web
```
### 5. nginx Configuration
Create `/etc/nginx/sites-available/cmdforge`:
```nginx
server {
listen 80;
server_name cmdforge.brrd.tech;
# Redirect HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name cmdforge.brrd.tech;
# SSL certificates (use certbot for Let's Encrypt)
ssl_certificate /etc/letsencrypt/live/cmdforge.brrd.tech/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/cmdforge.brrd.tech/privkey.pem;
# SSL settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
# Static files (optional - Flask can serve these)
location /static/ {
alias /opt/cmdforge/src/cmdforge/web/static/;
expires 1d;
add_header Cache-Control "public, immutable";
}
# Proxy to gunicorn
location / {
proxy_pass http://127.0.0.1:5050;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
Enable site:
```bash
sudo ln -s /etc/nginx/sites-available/cmdforge /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```
### 6. SSL with Let's Encrypt
```bash
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d cmdforge.brrd.tech
```
## Environment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| `FLASK_ENV` | Yes | `production` or `development` |
| `SECRET_KEY` | Yes | Secret key for sessions (32+ chars) |
| `SENTRY_DSN` | No | Sentry error monitoring DSN |
| `SITE_URL` | No | Public URL for sitemap generation |
| `DATABASE_PATH` | No | Override database path (default: `data/cmdforge.db`) |
## Database Backup
```bash
# Backup
cp /opt/cmdforge/data/cmdforge.db /backup/cmdforge-$(date +%Y%m%d).db
# Or use SQLite backup command for consistency
sqlite3 /opt/cmdforge/data/cmdforge.db ".backup '/backup/cmdforge.db'"
```
## Monitoring
### Logs
```bash
# Application logs
tail -f /var/log/cmdforge/error.log
tail -f /var/log/cmdforge/access.log
# systemd logs
journalctl -u cmdforge-web -f
```
### Sentry Integration
The application includes Sentry integration for error monitoring. Set `SENTRY_DSN` environment variable to enable.
### Health Check
```bash
curl http://localhost:5050/api/v1/tools
```
## Troubleshooting
### Service won't start
```bash
# Check logs
journalctl -u cmdforge-web -n 50
# Verify Python path
/opt/cmdforge/venv/bin/python -c "import cmdforge"
# Test gunicorn manually
cd /opt/cmdforge
source venv/bin/activate
gunicorn --bind 127.0.0.1:5050 'cmdforge.web.app:create_app()'
```
### Database errors
```bash
# Check database exists and is readable
ls -la /opt/cmdforge/data/cmdforge.db
# Verify permissions
chown www-data:www-data /opt/cmdforge/data/
chown www-data:www-data /opt/cmdforge/data/cmdforge.db
```
### Static files not loading
```bash
# Rebuild Tailwind CSS
cd /opt/cmdforge
npm install
npm run css:build
# Or use development CSS
# (static/css/output.css should exist)
```
## Architecture Diagram
See `docs/diagrams/deployment-architecture.svg` for visual representation.
+425
View File
@@ -0,0 +1,425 @@
# CmdForge - Design Document
> A lightweight personal tool builder for AI-powered CLI commands
## Overview
CmdForge lets you create custom AI-powered terminal commands. You define a tool once (name, steps, 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
```
~/.cmdforge/
sum/
config.yaml
processed.py # Optional external code file
reviewer/
config.yaml
translator/
config.yaml
```
### config.yaml Format
```yaml
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}"
# Optional: declare dependencies for meta-tools
dependencies:
- official/summarize
- official/translate@^1.0.0 # With version constraint
```
### Step Types
**Prompt Step** - Calls an AI provider:
```yaml
- type: prompt
prompt: "Your prompt template with {variables}"
provider: claude
output_var: response
```
**Code Step** - Runs Python code:
```yaml
- type: code
code: |
processed = input.upper()
count = len(processed.split())
output_var: processed, count
code_file: processed.py # Optional: external file storage
```
**Tool Step** - Calls another CmdForge tool (meta-tools):
```yaml
- type: tool
tool: official/summarize # Tool reference (owner/name or just name for local)
input: "{input}" # Input to pass (supports variable substitution)
args: # Optional arguments
max_words: "100"
output_var: summary # Variable to store the tool's output
provider: claude # Optional: override the called tool's provider
```
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:**
```yaml
output_var: processed
```
```python
processed = input.upper() # This gets captured
```
**Multiple variables (comma-separated):**
```yaml
output_var: processed, count, summary
```
```python
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
```bash
# 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 cmdforge run
cmdforge 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
```bash
# Launch the UI to manage tools
cmdforge
# Or use CLI directly:
cmdforge list # List all tools
cmdforge create sum # Create new tool (basic)
cmdforge edit sum # Edit tool config in $EDITOR
cmdforge delete sum # Delete tool
cmdforge test sum # Test with mock provider
cmdforge run sum # Run tool for real
cmdforge refresh # Refresh all wrapper scripts
cmdforge check sum # Check dependencies for meta-tools
cmdforge ui # Launch interactive UI
```
## Terminal UI
A BIOS-style terminal UI using `urwid` with full mouse support.
### Main Menu
```
┌──────────────────────────────────────┐
│ CmdForge 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 `~/.cmdforge/<toolname>/<filename>` on OK
- **Load button** - Load code from external file (with confirmation)
- **Multiple output vars** - Capture multiple variables (comma-separated)
## Implementation
### Directory Structure
```
cmdforge/
__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 `~/.cmdforge/providers.yaml`:
```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, CmdForge creates a wrapper script in `~/.local/bin/`:
```bash
#!/bin/bash
# CmdForge wrapper for 'sum'
# Auto-generated - do not edit
exec /path/to/python -m cmdforge.runner sum "$@"
```
The wrapper uses the full Python path to ensure the correct environment is used.
Use `cmdforge 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 (`/\|&;$`"'<>(){}[]!?*#~`)
## Tool Composition
CmdForge are designed to chain together like any Unix command.
### External Pipelines (Tool-to-Tool)
```bash
# Chain multiple CmdForge
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):
- 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
1. Run `cmdforge` to open UI
2. Select "Create" to create a new tool
3. Fill in: name, description, output template
4. Add arguments (e.g., `--max` with default `500`)
5. Add a prompt step with your prompt template and provider
6. Click "Save"
7. Exit UI
8. 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` |
+1058
View File
File diff suppressed because it is too large Load Diff
+249
View File
@@ -0,0 +1,249 @@
# Installation Guide
Complete installation instructions for CmdForge.
## Quick Install
```bash
git clone https://gitea.brrd.tech/rob/CmdForge.git
cd CmdForge
pip install -e .
export PATH="$HOME/.local/bin:$PATH"
cmdforge providers install
```
## Detailed Installation
### Step 1: Install Python 3.10+
**Ubuntu/Debian:**
```bash
sudo apt update
sudo apt install python3 python3-pip
```
**macOS:**
```bash
brew install [email protected]
```
**Verify:**
```bash
python3 --version # Should be 3.10 or higher
```
### Step 2: Install CmdForge
```bash
git clone https://gitea.brrd.tech/rob/CmdForge.git
cd CmdForge
pip install -e .
```
**With TUI and development dependencies:**
```bash
pip install -e ".[dev]"
```
### Step 3: Configure PATH
CmdForge creates executable wrappers in `~/.local/bin/`. Add this to your shell config:
**Bash (~/.bashrc):**
```bash
export PATH="$HOME/.local/bin:$PATH"
```
**Zsh (~/.zshrc):**
```bash
export PATH="$HOME/.local/bin:$PATH"
```
**Fish (~/.config/fish/config.fish):**
```fish
set -gx PATH $HOME/.local/bin $PATH
```
Then reload:
```bash
source ~/.bashrc # or restart terminal
```
### Step 4: Install an AI Provider
You need at least one AI CLI tool. The easiest way is the interactive installer:
```bash
cmdforge providers install
```
This guides you through installing and authenticating providers.
#### Manual Installation
**OpenCode (free tier available):**
```bash
curl -fsSL https://opencode.ai/install | bash
source ~/.bashrc # or start new shell
opencode # Opens browser for sign-in
```
**Claude CLI (pay-per-use):**
```bash
npm install -g @anthropic-ai/claude-code
claude # Opens browser for sign-in
```
**Codex (pay-per-use):**
```bash
npm install -g @openai/codex
codex # Opens browser for sign-in
```
**Gemini (free tier available):**
```bash
npm install -g @google/gemini-cli
gemini # Opens browser for Google sign-in
```
**Ollama (free, runs locally):**
```bash
curl -fsSL https://ollama.ai/install.sh | bash
ollama pull llama3
cmdforge providers add ollama "ollama run llama3" -d "Local Llama 3"
```
See [PROVIDERS.md](PROVIDERS.md) for full provider setup instructions.
### Step 5: Verify Installation
```bash
# Check CmdForge is installed
cmdforge --version
# List configured providers
cmdforge providers list
# Check which providers are available
cmdforge providers check
# Test with mock provider (no AI needed)
echo "hello world" | cmdforge run --provider mock
```
### Step 6: Create Wrapper Scripts
After installation or any Python environment change:
```bash
cmdforge refresh
```
This creates executable scripts in `~/.local/bin/` for all your tools.
## Installing Example Tools
### Quick Install (All 28 Tools)
```bash
python examples/install.py
cmdforge refresh
```
### Manual Install
Copy individual tool configs to `~/.cmdforge/<toolname>/config.yaml`.
See [EXAMPLES.md](EXAMPLES.md) for all tool configurations.
## Troubleshooting
### "cmdforge: command not found"
PATH is not configured. Add to your shell config:
```bash
export PATH="$HOME/.local/bin:$PATH"
```
### "Provider 'X' not found"
1. Check provider is installed: `which claude` or `which opencode`
2. Check provider is configured: `cmdforge providers`
3. Add missing provider: `cmdforge providers add`
### "Permission denied" on tool execution
Refresh wrapper scripts:
```bash
cmdforge refresh
```
### TUI doesn't start / looks broken
Install urwid:
```bash
pip install urwid
```
Ensure your terminal supports mouse (most modern terminals do).
### Tools work with `cmdforge run` but not directly
The wrapper scripts may be outdated. Refresh them:
```bash
cmdforge refresh
```
### Python version mismatch
If you have multiple Python versions, ensure CmdForge uses the right one:
```bash
python3.11 -m pip install cmdforge
```
## Uninstalling
```bash
# Remove package
pip uninstall cmdforge
# Remove config and tools (optional)
rm -rf ~/.cmdforge
# Remove wrapper scripts (optional)
rm ~/.local/bin/{summarize,translate,fix-grammar,...}
```
## Upgrading
```bash
pip install --upgrade cmdforge
cmdforge refresh
```
## Directory Structure
After installation:
```
~/.cmdforge/
├── providers.yaml # Provider configurations
├── summarize/
│ └── config.yaml # Tool config
├── translate/
│ ├── config.yaml
│ └── prompt.txt # External prompt file (optional)
└── ...
~/.local/bin/
├── cmdforge # Main command
├── summarize # Tool wrapper script
├── translate # Tool wrapper script
└── ...
```
## Next Steps
1. [Set up providers](PROVIDERS.md)
2. [Browse example tools](EXAMPLES.md)
3. Run `cmdforge ui` to create your first tool
+249
View File
@@ -0,0 +1,249 @@
# Meta-Tools: Tools That Call Other Tools
Meta-tools are CmdForge tools that can invoke other tools as steps in their workflow. This enables powerful composition and reuse of existing tools.
## Step Type: `tool`
A new step type `tool` allows calling another CmdForge tool from within a tool's workflow.
### Manifest Format
```yaml
name: summarize-and-translate
description: Summarize text then translate the summary
version: 1.0.0
category: Text
steps:
- type: tool
tool: official/summarize # Tool to call (owner/name or just name for local)
input: "{input}" # What to pass as input (supports variable substitution)
args: # Optional arguments to pass to the tool
max_words: "100"
output_var: summary # Variable to store the output
- type: tool
tool: official/translate
input: "{summary}" # Use output from previous step
args:
target_language: "{language}" # Can use tool's own arguments
output_var: translated
output: "{translated}"
arguments:
- flag: "--language"
variable: language
default: "Spanish"
description: "Target language for translation"
```
### Resolution Order
When resolving a tool reference in a `tool` step:
1. **Fully qualified**: `owner/name` - looks up in registry or installed tools
2. **Local tool**: Just `name` - checks local `~/.cmdforge/<name>/config.yaml` first
3. **Registry fallback**: If not found locally, checks installed registry tools
### Tool Step Properties
| Property | Required | Description |
|----------|----------|-------------|
| `type` | Yes | Must be `"tool"` |
| `tool` | Yes | Tool reference (owner/name or name) |
| `input` | No | Input to pass to the tool (default: current `{input}`) |
| `args` | No | Dictionary of arguments to pass |
| `output_var` | Yes | Variable name to store the tool's output |
| `provider` | No | Override provider for the called tool |
### Execution
When a `tool` step executes:
1. Resolve the tool reference to find the tool definition
2. Substitute variables in `input` and `args` values
3. Execute the tool with the resolved input and arguments
4. Capture the output in the specified `output_var`
### Error Handling
- If a called tool fails, the parent tool fails with an appropriate error
- Tool not found errors include helpful installation suggestions
- Circular dependencies are detected and prevented
## Dependency Resolution
### Declaring Dependencies
Tools can declare their dependencies in the config:
```yaml
name: my-meta-tool
dependencies:
- official/summarize
- official/translate@^1.0.0 # With version constraint
```
### CLI Integration
```bash
# Install a tool and its dependencies
cmdforge install official/summarize-and-translate
# Check if dependencies are satisfied
cmdforge check my-meta-tool
# Install missing dependencies
cmdforge install --deps my-meta-tool
```
### Dependency Checking
Before running a meta-tool, the runner verifies all dependencies are installed:
```python
def check_dependencies(tool: Tool) -> List[str]:
"""Returns list of missing dependencies."""
missing = []
for dep in tool.dependencies:
if not is_tool_installed(dep):
missing.append(dep)
return missing
```
## Security Considerations
1. **Sandboxing**: Called tools run in the same process context as the parent
2. **Input validation**: All inputs are treated as untrusted strings
3. **No shell execution**: Tool references cannot contain shell commands
4. **Depth limit**: Maximum nesting depth (default: 10) prevents runaway recursion
## Example: Multi-Step Analysis Pipeline
```yaml
name: code-review-pipeline
description: Comprehensive code review using multiple specialized tools
version: 1.0.0
category: Developer
dependencies:
- official/analyze-complexity
- official/find-bugs
- official/suggest-improvements
steps:
# Step 1: Analyze code complexity
- type: tool
tool: official/analyze-complexity
input: "{input}"
output_var: complexity_report
# Step 2: Find potential bugs
- type: tool
tool: official/find-bugs
input: "{input}"
output_var: bug_report
# Step 3: Generate improvement suggestions based on findings
- type: tool
tool: official/suggest-improvements
input: |
## Code:
{input}
## Complexity Analysis:
{complexity_report}
## Bug Report:
{bug_report}
output_var: suggestions
output: |
# Code Review Results
## Complexity Analysis
{complexity_report}
## Potential Issues
{bug_report}
## Improvement Suggestions
{suggestions}
```
## Implementation Notes
### ToolStep Dataclass
```python
@dataclass
class ToolStep:
"""A step that calls another tool."""
tool: str # Tool reference (owner/name or name)
output_var: str # Variable to store output
input_template: str = "{input}" # Input template
args: Dict[str, str] = field(default_factory=dict)
provider: Optional[str] = None # Provider override
def to_dict(self) -> dict:
d = {
"type": "tool",
"tool": self.tool,
"output_var": self.output_var,
}
if self.input_template != "{input}":
d["input"] = self.input_template
if self.args:
d["args"] = self.args
if self.provider:
d["provider"] = self.provider
return d
```
### Execution Function
```python
def execute_tool_step(
step: ToolStep,
variables: dict,
depth: int = 0,
max_depth: int = 10
) -> tuple[str, bool]:
"""Execute a tool step by calling another tool."""
if depth >= max_depth:
return "", False # Prevent infinite recursion
# Resolve the tool
try:
resolved = resolve_tool(step.tool)
except ToolNotFoundError:
print(f"Error: Tool '{step.tool}' not found", file=sys.stderr)
return "", False
# Prepare input
input_text = substitute_variables(step.input_template, variables)
# Prepare arguments
args = {}
for key, value in step.args.items():
args[key] = substitute_variables(value, variables)
# Run the tool
output, exit_code = run_tool(
tool=resolved.tool,
input_text=input_text,
custom_args=args,
provider_override=step.provider,
_depth=depth + 1 # Track nesting depth
)
return output, exit_code == 0
```
## Future Enhancements
- **Parallel tool execution**: Run independent tool steps concurrently
- **Conditional execution**: Skip steps based on conditions
- **Tool aliases**: Define shorthand names for frequently used tools
- **Caching**: Cache tool outputs for identical inputs
+180
View File
@@ -0,0 +1,180 @@
# CmdForge Project Overview
This document provides a comprehensive overview of the CmdForge project structure, components, and how everything fits together.
## What is CmdForge?
CmdForge is a personal tool builder for AI-powered CLI commands. It consists of three main components:
1. **CLI Tool** (`cmdforge`) - Create, manage, and run AI-powered command-line tools
2. **Registry API** - REST API for publishing and discovering tools
3. **Web UI** - Browser interface for the registry with docs, search, and user dashboard
## Quick Reference
| Component | Location | Purpose |
|-----------|----------|---------|
| CLI | `src/cmdforge/cli.py` | Main entry point for `cmdforge` command |
| Registry API | `src/cmdforge/registry/` | REST API for tool registry |
| Web UI | `src/cmdforge/web/` | Flask web application |
| Tool Storage | `~/.cmdforge/` | User's installed tools |
| Database | `data/cmdforge.db` | SQLite with FTS5 search |
## Architecture Diagrams
See `docs/diagrams/` for visual representations:
- **system-architecture.svg** - How components interact
- **deployment-architecture.svg** - Production deployment setup
- **source-organization.svg** - Code structure
## Source Code Structure
```
src/cmdforge/
├── cli.py # Entry point, subcommand routing
├── tool.py # Tool dataclasses, YAML loading
├── runner.py # Step execution engine
├── providers.py # AI provider abstraction
├── config.py # Global configuration
├── manifest.py # Tool manifest handling
│
├── registry/ # Registry API Server
│ ├── app.py # Flask API application
│ ├── db.py # Database operations, FTS5 search
│ ├── rate_limit.py # API rate limiting
│ ├── sync.py # Git sync for tool submissions
│ ├── categorize.py # Auto-categorization
│ └── similarity.py # Similar tool recommendations
│
├── web/ # Web UI Application
│ ├── app.py # Flask web app, Sentry integration
│ ├── routes.py # Page routes and handlers
│ ├── auth.py # Password hashing, login/register
│ ├── sessions.py # Session management
│ ├── docs_content.py # Documentation text content
│ ├── seo.py # Sitemap, robots.txt
│ ├── filters.py # Jinja template filters
│ ├── templates/ # Jinja HTML templates
│ └── static/ # CSS, JS, images
│
├── registry_client.py # CLI client for registry API
├── resolver.py # Dependency resolution
│
├── ui.py # TUI dispatcher
├── ui_urwid.py # urwid TUI implementation
└── ui_snack.py # newt/snack TUI fallback
```
## Database Schema
SQLite database at `data/cmdforge.db`:
| Table | Purpose |
|-------|---------|
| `publishers` | User accounts (username, email, password_hash) |
| `tools` | Published tools (name, owner, description, config) |
| `api_tokens` | Authentication tokens for API access |
| `download_stats` | Tool download tracking |
| `pageviews` | Analytics for tool detail pages |
| `tools_fts` | Full-text search index (FTS5) |
## API Endpoints
Base URL: `/api/v1/`
| Endpoint | Method | Purpose |
|----------|--------|---------|
| `/tools` | GET | List/search tools |
| `/tools/<owner>/<name>` | GET | Get tool details |
| `/tools/<owner>/<name>` | POST | Publish tool (auth required) |
| `/tools/<owner>/<name>/download` | POST | Record download |
| `/me` | GET | Current user info |
| `/tokens` | GET/POST | Manage API tokens |
## Web Routes
| Route | Purpose |
|-------|---------|
| `/` | Homepage with featured tools |
| `/tools` | Tool browser with search |
| `/tools/<owner>/<name>` | Tool detail page |
| `/docs/*` | Documentation pages |
| `/login`, `/register` | Authentication |
| `/dashboard/*` | User dashboard, token management |
## Key Files to Know
### Configuration
- `~/.cmdforge/providers.yaml` - AI provider configurations
- `~/.cmdforge/config.yaml` - Global settings
- `~/.cmdforge/<tool>/config.yaml` - Individual tool configs
### Development
- `pyproject.toml` - Package configuration
- `pytest.ini` - Test configuration
- `tailwind.config.js` - Tailwind CSS configuration
### Documentation
- `docs/REGISTRY.md` - Registry design document (comprehensive)
- `docs/WEB_UI.md` - Web UI design document (comprehensive)
- `wiki/Home.md` - User-facing wiki documentation
- `CLAUDE.md` - Development guidance for AI assistants
## Running the Project
### Development
```bash
# Install with dev dependencies
pip install -e ".[dev]"
# Run CLI
python -m cmdforge.cli
# Run web server (development)
cd src/cmdforge/web
flask run --port 5050
```
### Production
```bash
# Using gunicorn
gunicorn -w 4 -b 0.0.0.0:5050 'cmdforge.web.app:create_app()'
# Environment variables needed:
# FLASK_ENV=production
# SECRET_KEY=<your-secret-key>
# SENTRY_DSN=<optional-sentry-dsn>
# SITE_URL=https://your-domain.com
```
### systemd Service
See `docs/DEPLOYMENT.md` for systemd service configuration.
## Testing
```bash
# Run all tests
pytest
# Run specific test
pytest tests/test_name.py::test_function
# Run with coverage
pytest --cov=cmdforge
```
## Design Documents
For detailed design decisions and specifications:
- **docs/REGISTRY.md** - Complete registry API design (Phases 1-8)
- **docs/WEB_UI.md** - Complete web UI design (Phase 7)
- **wiki/Home.md** - User documentation for CLI usage
## Archive
Old development discussions and diagrams are preserved in `archive/discussions/` for historical reference.
+328
View File
@@ -0,0 +1,328 @@
# Provider Setup Guide
CmdForge works with any AI CLI tool that accepts input via stdin or arguments. This guide covers setup for the most popular providers.
## Provider Comparison
We profiled 12 providers with a 4-task benchmark (Math, Code, Reasoning, Data Extraction):
| Provider | Speed | Score | Cost | Best For |
|----------|-------|-------|------|----------|
| **opencode-deepseek** | 13s | 4/4 | ~$0.28/M tokens | **Best value** - daily driver |
| **opencode-pickle** | 13s | 4/4 | FREE | **Best free** - accurate |
| **claude-haiku** | 14s | 4/4 | ~$0.25/M tokens | Fast + high quality |
| **codex** | 14s | 4/4 | ~$1.25/M tokens | Reliable, auto-routes |
| **claude** | 18s | 4/4 | Varies | Auto-routes to best |
| **claude-opus** | 18s | 4/4 | ~$15/M tokens | Highest quality |
| **claude-sonnet** | 21s | 4/4 | ~$3/M tokens | Balanced |
| **opencode-nano** | 24s | 4/4 | Paid | GPT-5 Nano |
| **gemini-flash** | 28s | 4/4 | ~$0.075/M tokens | Google, faster |
| **opencode-reasoner** | 33s | 4/4 | ~$0.28/M tokens | Complex reasoning |
| **gemini** | 91s | 3/4 | ~$1.25/M tokens | 1M token context |
| **opencode-grok** | 11s | 2/4 | FREE | Fastest but unreliable |
### Recommendations
- **Daily use:** `opencode-deepseek` or `opencode-pickle` (free)
- **Quality work:** `claude-haiku` or `claude-opus`
- **Complex reasoning:** `opencode-reasoner`
- **Large documents:** `gemini` (1M token context window)
- **Budget:** `opencode-pickle` (free) or `opencode-deepseek` (cheap)
## Provider Setup
### OpenCode (Recommended)
OpenCode provides access to multiple models including free options.
**Install:**
```bash
curl -fsSL https://opencode.ai/install | bash
```
**Authenticate:**
```bash
~/.opencode/bin/opencode auth
```
**Available Models:**
| Provider Name | Model | Cost |
|---------------|-------|------|
| `opencode-deepseek` | deepseek-chat | Cheap |
| `opencode-pickle` | big-pickle | FREE |
| `opencode-grok` | grok-code | FREE |
| `opencode-nano` | gpt-5-nano | Paid |
| `opencode-reasoner` | deepseek-reasoner | Cheap |
**Test:**
```bash
echo "Hello" | ~/.opencode/bin/opencode run --model opencode/big-pickle
```
### Claude CLI
Anthropic's official CLI for Claude models.
**Install:**
```bash
npm install -g @anthropic-ai/claude-code
```
**Authenticate:**
```bash
claude # Opens browser for sign-in (auto-saves auth tokens)
```
**Available Models:**
| Provider Name | Model | Cost |
|---------------|-------|------|
| `claude` | Auto-routes | Varies |
| `claude-haiku` | Haiku 4.5 | Cheap |
| `claude-sonnet` | Sonnet 4.5 | Medium |
| `claude-opus` | Opus 4.5 | Expensive |
**Test:**
```bash
echo "Hello" | claude -p
```
### Codex (OpenAI)
OpenAI's Codex CLI with auto-routing.
**Install:**
```bash
npm install -g @openai/codex
```
**Authenticate:**
```bash
codex # Opens browser for sign-in (auto-saves auth tokens)
```
**Test:**
```bash
echo "Hello" | codex exec -
```
### Gemini
Google's Gemini models. Best for large context (1M tokens).
**Install:**
```bash
npm install -g @google/gemini-cli
```
**Authenticate:**
```bash
gemini # Opens browser for Google sign-in
```
**Available Models:**
| Provider Name | Model | Notes |
|---------------|-------|-------|
| `gemini` | gemini-2.5-pro | Quality, slow CLI |
| `gemini-flash` | gemini-2.5-flash | Faster |
**Note:** Gemini CLI has known performance issues. Use `gemini-flash` for interactive tasks, `gemini` for large documents.
**Test:**
```bash
echo "Hello" | gemini --model gemini-2.5-flash
```
## Managing Providers
### Interactive Installation (Recommended)
The easiest way to install providers:
```bash
cmdforge providers install
```
This interactive guide:
- Shows available AI providers with costs
- Runs the installation command
- Updates PATH automatically
- Shows next steps for authentication
### List Providers
```bash
cmdforge providers list
```
### Check Availability
```bash
cmdforge providers check
```
### Add Custom Provider
```bash
cmdforge providers add myname "my-command --args" -d "Description"
```
Or edit `~/.cmdforge/providers.yaml`:
```yaml
providers:
- name: my-custom
command: my-ai-tool --prompt
description: My custom AI tool
```
### Provider Command Format
The command should:
1. Accept input via stdin
2. Output response to stdout
3. Exit 0 on success
Example commands:
```bash
# Claude
claude -p
# OpenCode
$HOME/.opencode/bin/opencode run --model deepseek/deepseek-chat
# Gemini
gemini --model gemini-2.5-flash
# Codex
codex exec -
# Custom (any tool that reads stdin)
my-tool --input -
```
### Environment Variables
Provider commands can use environment variables:
```yaml
providers:
- name: opencode
command: $HOME/.opencode/bin/opencode run
```
`$HOME` and `~` are expanded automatically.
## Using Providers in Tools
### In Tool Config
```yaml
steps:
- type: prompt
prompt: "Summarize: {input}"
provider: opencode-pickle # Use this provider
output_var: response
```
### Override at Runtime
```bash
# Use a different provider for this run
cat file.txt | summarize --provider claude-opus
```
### Provider Selection Strategy
1. **Tool default** - Set in tool's config.yaml
2. **Runtime override** - `--provider` flag
3. **Cost optimization** - Use cheap providers for simple tasks
4. **Quality needs** - Use opus/sonnet for important work
## Troubleshooting
### "Provider 'X' not found"
1. Check it's in your providers list: `cmdforge providers`
2. Verify the command works: `echo "test" | <command>`
3. Add it: `cmdforge providers add`
### "Command 'X' not found"
The AI CLI tool isn't installed or not in PATH:
```bash
which claude # Should show path
which opencode # Might need full path
```
For OpenCode, use full path in provider:
```yaml
command: $HOME/.opencode/bin/opencode run
```
### Slow Provider
- Use `gemini-flash` instead of `gemini`
- Use `claude-haiku` instead of `claude-opus`
- Use `opencode-deepseek` for best speed/quality ratio
### Provider Errors
Check the provider works directly:
```bash
echo "Say hello" | claude -p
echo "Say hello" | ~/.opencode/bin/opencode run
```
If it works directly but not in CmdForge, check:
1. Provider command in `~/.cmdforge/providers.yaml`
2. Environment variables are expanded correctly
## Cost Optimization
### Free Providers
- `opencode-pickle` - Big Pickle model (FREE, accurate)
- `opencode-grok` - Grok Code (FREE, fast but less reliable)
### Cheap Providers
- `opencode-deepseek` - ~$0.28/M tokens
- `opencode-reasoner` - ~$0.28/M tokens
- `claude-haiku` - ~$0.25/M tokens
### Tips
1. Use `opencode-pickle` for simple tasks (free + accurate)
2. Use `claude-haiku` when you need reliability
3. Reserve `claude-opus` for important work
4. Use `gemini` only for large document analysis
## Adding New Providers
Any CLI tool that:
- Reads from stdin
- Writes to stdout
- Exits 0 on success
Can be a provider. Examples:
**Local LLM (Ollama):**
```yaml
- name: ollama-llama
command: ollama run llama3
description: Local Llama 3
```
**Custom API wrapper:**
```yaml
- name: my-api
command: curl -s -X POST https://my-api.com/chat -d @-
description: My custom API
```
**Python script:**
```yaml
- name: my-python
command: python3 ~/scripts/my_ai.py
description: Custom Python AI
```
+2023
View File
File diff suppressed because it is too large Load Diff
+1462
View File
File diff suppressed because it is too large Load Diff