CmdForge Architecture
Module Structure
src/cmdforge/
├── cli/ # CLI commands
│ ├── __init__.py
│ ├── tool_commands.py # list, create, edit, delete
│ ├── provider_commands.py # providers management
│ ├── registry_commands.py # publish, install
│ └── collections_commands.py # collections create, show, add, remove, delete, publish, status
├── registry/ # Registry API
│ ├── app.py # Flask API endpoints
│ ├── db.py # SQLite schema and queries
│ ├── embeddings.py # Semantic search (Ollama embeddings)
│ ├── settings.py # Admin-configurable settings
│ ├── sync.py # Git repo sync
│ └── rate_limit.py
├── web/ # Web UI (cmdforge.brrd.tech)
│ ├── app.py # Flask app factory
│ ├── routes.py # Page routes
│ ├── auth.py # User authentication
│ ├── forum/ # Forum feature
│ ├── templates/ # Jinja2 templates
│ └── static/ # CSS, JS
├── gui/ # Desktop GUI (PySide6)
│ ├── __init__.py # Entry point, run_gui()
│ ├── main_window.py # Main window with sidebar
│ ├── styles.py # QSS stylesheet
│ ├── pages/ # Application pages
│ │ ├── tools_page.py # Tool list and details
│ │ ├── tool_builder_page.py # Create/edit tools
│ │ ├── registry_page.py # Browse/install tools
│ │ ├── collections_page.py # Local and registry collections
│ │ └── providers_page.py # Provider management
│ └── dialogs/ # Modal dialogs
│ ├── step_dialog.py # Prompt/code step editors
│ ├── argument_dialog.py
│ ├── provider_dialog.py
│ ├── connect_dialog.py # Registry connect
│ └── publish_dialog.py
├── tool.py # Tool dataclass and loading
├── collection.py # Collection dataclass and tool resolution
├── runner.py # Tool execution engine
└── providers.py # AI provider abstraction
Data Flow
CLI Tool Execution
User Input (stdin)
│
▼
┌─────────────┐
│ runner.py │ ──── Loads tool from ~/.cmdforge/<name>/config.yaml
└─────────────┘
│
▼
┌─────────────┐
│ Steps │
│ (prompt/ │ ──── For prompt steps, calls providers.py
│ code) │ ──── For code steps, exec() Python
└─────────────┘
│
▼
Output (stdout)
Web UI Request Flow
Browser Request
│
▼
┌─────────────────┐
│ Cloudflare │ ──── HTTPS termination
└─────────────────┘
│
▼
┌─────────────────┐
│ Flask :5050 │ ──── web/app.py
└─────────────────┘
│
├──── /api/* → registry/app.py (API)
└──── /* → web/routes.py (Pages)
│
▼
┌─────────────┐
│ SQLite DB │
└─────────────┘
Key Classes
Tool (tool.py)
@dataclass
class Tool:
name: str
description: str
category: str
arguments: List[ToolArgument]
steps: List[Step] # PromptStep | CodeStep | ToolStep
output: str
dependencies: List[str]
source: Optional[ToolSource] # Attribution for imports
version: str
ToolSource (tool.py)
@dataclass
class ToolSource:
type: str # "original", "imported", "forked"
license: str
url: str
author: str
original_tool: str # e.g., "fabric/patterns/extract_wisdom"
Provider (providers.py)
@dataclass
class Provider:
name: str # e.g., "opencode-pickle"
command: str # e.g., "$HOME/.opencode/bin/opencode run --model ..."
description: str
Collection (collection.py)
@dataclass
class Collection:
name: str # Unique identifier (kebab-case)
display_name: str # Human-readable name
description: str
maintainer: str
tools: List[str] # Tool references (local or owner/name)
pinned: Dict[str, str] # Version constraints
tags: List[str]
published: bool # Whether published to registry
registry_name: str # Name in registry (if different)
pending_approval: bool # Awaiting moderation
pending_tools: List[str] # Tools awaiting approval
ToolResolutionResult (collection.py)
@dataclass
class ToolResolutionResult:
registry_refs: List[str] # Transformed owner/name refs
transformed_pinned: Dict[str, str] # Pinned with transformed keys
local_unpublished: List[str] # Local tools not in registry
local_published: List[tuple] # (name, status, has_approved) tuples
visibility_issues: List[tuple] # (name, visibility) for non-public
registry_tool_issues: List[tuple] # (ref, reason) for invalid refs
Error Handling
The runner provides detailed error messages for debugging:
Code Step Errors
When Python code fails in a code step, shows:
- Step number and error type
- Offending line with context (line before/after)
- Visual pointer (
>>>) to error line - List of available variables
Error in code step (step 2):
NameError: name 'undefined_var' is not defined
1: data = input.split('\n')
>>> 2: result = undefined_var + data
3: output = result
Available variables: ['input', 'max_size', 'step1_output']
YAML Syntax Errors
When a tool's config.yaml has syntax errors:
- Line and column number
- Visual pointer to exact position
- Context line above
Error loading tool 'my-tool': YAML syntax error
Line 15, column 8
14: steps:
> 15: - type prompt # missing colon
^
Problem: expected ',' or ']'
Nested Tool Errors
When a tool calls another tool that fails, shows the full call stack:
Error in tool chain:
my-wrapper (step 2)
-> summarize (step 1)
-> Tool 'missing-tool' not found
Registry Database
SQLite schema for published tools:
CREATE TABLE tools (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE NOT NULL,
description TEXT,
category TEXT DEFAULT 'Other',
config_yaml TEXT NOT NULL, -- Full tool YAML
source TEXT, -- Deprecated (type only)
source_url TEXT, -- Deprecated
source_json TEXT, -- Full ToolSource as JSON
published_at TIMESTAMP,
downloads INTEGER DEFAULT 0,
owner_id INTEGER REFERENCES users(id)
);
The source_json column stores the complete ToolSource object, preserving all attribution fields when tools are published.
Semantic Search Embeddings
CREATE TABLE tool_embeddings (
tool_id INTEGER PRIMARY KEY REFERENCES tools(id) ON DELETE CASCADE,
embedding BLOB NOT NULL, -- Packed float32 vector (768 dims × 4 bytes = 3KB)
dimensions INTEGER NOT NULL, -- Actual vector dimensions
model TEXT NOT NULL, -- Model used (e.g., "nomic-embed-text")
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Embeddings are generated via Ollama (AI-Server, 192.168.0.186:11434) using the nomic-embed-text model. Only public+approved tools are embedded. Vectors are stored as packed binary blobs and compared using pure Python cosine similarity at query time (~100 tools = sub-ms).
Configuration Files
| File | Location | Purpose |
|---|---|---|
| Tool config | ~/.cmdforge/<name>/config.yaml | Tool definition |
| Providers | ~/.cmdforge/providers.yaml | AI provider commands |
| Main config | ~/.cmdforge/config.yaml | Registry URL, client ID |
| Collections | ~/.cmdforge/collections/<name>.yaml | Local collection definitions |