diff --git a/CHANGELOG.md b/CHANGELOG.md
index c107f55..9b8d77c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,18 @@ All notable changes to CmdForge will be documented in this file.
- Automatically clears invalid/revoked tokens
- Shows status bar message when connection is cleared
- Prevents confusing errors when trying to publish with stale credentials
+- **Help menu and quick guides**: Added Help menu with documentation guides
+ - Getting Started guide with quick start steps and keyboard shortcuts
+ - How to Create a Tool guide with step-by-step instructions
+ - How to Install Tools guide for registry browsing
+ - How to Publish Tools guide for sharing tools
+ - Keyboard Shortcuts reference
+ - About CmdForge dialog
+ - F1 shortcut opens Getting Started guide
+- **Enhanced tooltips**: Added contextual tooltips throughout the GUI
+ - Section headings in Tool Builder (Arguments, Steps, Output Template)
+ - Registry page controls (search, filters, pagination, install)
+ - Sidebar navigation items with keyboard shortcuts
#### CLI Features
- `cmdforge config disconnect` - Clear registry token from local configuration
diff --git a/src/cmdforge/gui/dialogs/help_dialogs.py b/src/cmdforge/gui/dialogs/help_dialogs.py
new file mode 100644
index 0000000..b070052
--- /dev/null
+++ b/src/cmdforge/gui/dialogs/help_dialogs.py
@@ -0,0 +1,363 @@
+"""Help dialogs for CmdForge GUI."""
+
+from PySide6.QtWidgets import (
+ QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
+ QTextBrowser
+)
+from PySide6.QtCore import Qt
+
+
+# Help content for quick guides
+HELP_CONTENT = {
+ "getting_started": {
+ "title": "Getting Started with CmdForge",
+ "content": """
+
Welcome to CmdForge!
+CmdForge lets you create AI-powered command-line tools that work like any Unix command.
+
+Quick Start Steps:
+
+- Configure a Provider - Go to Providers in the sidebar and add an AI backend (like Claude or GPT). This is required before running AI tools.
+- Browse the Registry - Check out the Registry to discover tools shared by the community.
+- Install Tools - Click on any tool and hit Install to add it to your system.
+- Create Your Own - Use the Tool Builder to create custom AI-powered commands.
+- Use in Terminal - Run your tools like any command:
my-tool < input.txt
+
+
+Key Concepts:
+
+- Steps - Tools are made of steps: Prompt (AI call), Code (Python), or Tool (chain another tool)
+- Variables - Use
{variable} to pass data between steps
+- Arguments - Add flags like
--format that users can set when running the tool
+
+
+Keyboard Shortcuts:
+
+Ctrl+N | Create new tool |
+Ctrl+S | Save current tool |
+Ctrl+R | Refresh current page |
+Ctrl+1-4 | Navigate to pages |
+Ctrl+Q | Quit |
+Escape | Close tool builder |
+
+"""
+ },
+ "create_tool": {
+ "title": "How to Create a Tool",
+ "content": """
+Creating a New Tool
+
+Step 1: Start the Tool Builder
+Click Create Tool on the Welcome page, or press Ctrl+N from anywhere.
+
+Step 2: Fill in Basic Info
+
+- Name - Use lowercase letters, numbers, and hyphens (e.g.,
summarize-text)
+- Description - Brief explanation shown in listings
+- Category - Pick from Text, Developer, Data, or add your own
+
+
+Step 3: Add Arguments (Optional)
+Arguments let users customize tool behavior:
+
+- Flag - The command-line flag (e.g.,
--max)
+- Variable - Name to use in templates (e.g.,
max)
+- Default - Value when flag isn't provided
+
+
+Step 4: Add Steps
+Steps define what your tool does:
+
+- Prompt Step - Calls an AI provider with your template. Use
{input} for stdin content.
+- Code Step - Runs Python code. Variables are available directly (e.g.,
input, response).
+- Tool Step - Calls another CmdForge tool, enabling composition.
+
+
+Step 5: Set Output Template
+The output template defines what your tool prints. Use {variable} to include step outputs.
+Example: {response} prints the last AI response.
+
+Step 6: Save
+Press Ctrl+S or click Save. Your tool is now available as a command!
+
+Example: Simple Summarizer
+
+Name: summarize
+Steps:
+ 1. Prompt [claude] -> response
+ "Summarize this text: {input}"
+Output: {response}
+
+Run it: echo "Long text..." | summarize
+"""
+ },
+ "install_tools": {
+ "title": "How to Install Tools",
+ "content": """
+Installing Tools from the Registry
+
+Step 1: Open the Registry
+Click Registry in the sidebar to browse community tools.
+
+Step 2: Find a Tool
+
+- Search - Type keywords in the search box
+- Filter by Category - Use the dropdown to narrow results
+- Sort - Order by popularity, newest, or name
+- Tags - Click tags on tools to filter by tag
+
+
+Step 3: Review Tool Details
+Click on a tool to see:
+
+- Description and usage information
+- Rating and download count
+- Publisher reputation
+- Available versions
+
+
+Step 4: Install
+
+- Select a version (or use Latest)
+- Click Install
+- Wait for confirmation
+
+
+Step 5: Use the Tool
+Installed tools are available immediately in your terminal:
+
+echo "text" | tool-name
+cat file.txt | tool-name --flag value
+tool-name --help
+
+
+Updates
+Tools with available updates show a green arrow (↑) in the list. Click Update to get the latest version.
+
+Managing Installed Tools
+Go to Tools in the sidebar to see all your installed tools, edit them, or delete ones you no longer need.
+"""
+ },
+ "publish_tools": {
+ "title": "How to Publish Tools",
+ "content": """
+Publishing Your Tools
+
+Prerequisites
+
+- A tool you've created and tested
+- A registry account (sign up at cmdforge.brrd.tech)
+
+
+Step 1: Connect Your Account
+
+- Go to the Providers page
+- Click Connect to Registry
+- Log in or register
+
+
+Step 2: Prepare Your Tool
+
+- Give it a clear, descriptive name
+- Write a helpful description
+- Choose the right category
+- Test it thoroughly
+
+
+Step 3: Publish
+
+- Go to the Tools page
+- Select the tool you want to publish
+- Click Publish
+- Add tags to help users find it
+- Set the initial version number
+- Click Submit
+
+
+Versioning
+When you update a published tool:
+
+- Patch (1.0.0 → 1.0.1) - Bug fixes
+- Minor (1.0.0 → 1.1.0) - New features, backward compatible
+- Major (1.0.0 → 2.0.0) - Breaking changes
+
+
+Best Practices
+
+- Write clear descriptions and examples
+- Use semantic versioning
+- Test before publishing updates
+- Respond to user feedback
+
+"""
+ },
+ "keyboard_shortcuts": {
+ "title": "Keyboard Shortcuts",
+ "content": """
+Keyboard Shortcuts
+
+Global
+
+Ctrl+N | Create new tool |
+Ctrl+S | Save current tool |
+Ctrl+R | Refresh current page |
+Ctrl+Q | Quit application |
+F1 | Show help |
+
+
+Navigation
+
+Ctrl+1 | Go to Welcome page |
+Ctrl+2 | Go to Tools page |
+Ctrl+3 | Go to Registry page |
+Ctrl+4 | Go to Providers page |
+
+
+Tool Builder
+
+Escape | Close builder / cancel |
+Ctrl+S | Save tool |
+
+
+Lists and Tables
+
+Double-click | Edit selected item |
+Drag & Drop | Reorder steps |
+
+"""
+ }
+}
+
+
+class QuickGuideDialog(QDialog):
+ """Dialog showing a quick guide on a topic."""
+
+ def __init__(self, guide_key: str, parent=None):
+ super().__init__(parent)
+
+ guide = HELP_CONTENT.get(guide_key, {})
+ self.setWindowTitle(guide.get("title", "Help"))
+ self.setMinimumSize(550, 450)
+ self.resize(600, 500)
+ self.setModal(True)
+
+ self._setup_ui(guide)
+
+ def _setup_ui(self, guide: dict):
+ """Set up the dialog UI."""
+ layout = QVBoxLayout(self)
+ layout.setContentsMargins(24, 24, 24, 24)
+ layout.setSpacing(16)
+
+ # Content browser with styled HTML
+ browser = QTextBrowser()
+ browser.setOpenExternalLinks(True)
+ browser.setStyleSheet("""
+ QTextBrowser {
+ border: none;
+ background-color: transparent;
+ font-size: 13px;
+ }
+ """)
+
+ # Build styled content
+ content = f"""
+
+ {guide.get("content", "")}
+ """
+ browser.setHtml(content)
+ layout.addWidget(browser, 1)
+
+ # Close button
+ btn_layout = QHBoxLayout()
+ btn_layout.addStretch()
+
+ btn_close = QPushButton("Close")
+ btn_close.setMinimumWidth(100)
+ btn_close.clicked.connect(self.accept)
+ btn_layout.addWidget(btn_close)
+
+ layout.addLayout(btn_layout)
+
+
+class AboutDialog(QDialog):
+ """About CmdForge dialog."""
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.setWindowTitle("About CmdForge")
+ self.setFixedSize(400, 300)
+ self.setModal(True)
+ self._setup_ui()
+
+ def _setup_ui(self):
+ """Set up the dialog UI."""
+ layout = QVBoxLayout(self)
+ layout.setContentsMargins(32, 32, 32, 32)
+ layout.setSpacing(16)
+
+ # Logo/title
+ title = QLabel("CmdForge")
+ title.setStyleSheet("""
+ font-size: 32px;
+ font-weight: bold;
+ color: #667eea;
+ """)
+ title.setAlignment(Qt.AlignCenter)
+ layout.addWidget(title)
+
+ # Tagline
+ tagline = QLabel("AI-Powered CLI Tool Builder")
+ tagline.setStyleSheet("font-size: 14px; color: #718096;")
+ tagline.setAlignment(Qt.AlignCenter)
+ layout.addWidget(tagline)
+
+ layout.addSpacing(16)
+
+ # Version info
+ try:
+ from cmdforge import __version__
+ version = __version__
+ except ImportError:
+ version = "unknown"
+
+ version_label = QLabel(f"Version {version}")
+ version_label.setStyleSheet("color: #4a5568;")
+ version_label.setAlignment(Qt.AlignCenter)
+ layout.addWidget(version_label)
+
+ # Description
+ desc = QLabel(
+ "Create custom terminal commands that call AI providers,\n"
+ "chain prompts with Python code, and use them like\n"
+ "any Unix pipe command."
+ )
+ desc.setStyleSheet("color: #718096; font-size: 12px;")
+ desc.setAlignment(Qt.AlignCenter)
+ layout.addWidget(desc)
+
+ layout.addStretch()
+
+ # Links
+ links = QLabel(
+ 'Website'
+ )
+ links.setOpenExternalLinks(True)
+ links.setAlignment(Qt.AlignCenter)
+ layout.addWidget(links)
+
+ # Close button
+ btn_close = QPushButton("Close")
+ btn_close.clicked.connect(self.accept)
+ layout.addWidget(btn_close)
diff --git a/src/cmdforge/gui/main_window.py b/src/cmdforge/gui/main_window.py
index 2b1ed03..9840bd5 100644
--- a/src/cmdforge/gui/main_window.py
+++ b/src/cmdforge/gui/main_window.py
@@ -3,10 +3,10 @@
from PySide6.QtWidgets import (
QMainWindow, QWidget, QHBoxLayout, QVBoxLayout,
QListWidget, QListWidgetItem, QStackedWidget,
- QStatusBar, QLabel, QSplitter
+ QStatusBar, QLabel, QSplitter, QMenuBar, QMenu
)
from PySide6.QtCore import Qt, QSize, QSettings, QTimer
-from PySide6.QtGui import QIcon, QFont, QShortcut, QKeySequence
+from PySide6.QtGui import QIcon, QFont, QShortcut, QKeySequence, QAction
from .styles import STYLESHEET
@@ -65,6 +65,9 @@ class MainWindow(QMainWindow):
# Setup keyboard shortcuts
self._setup_shortcuts()
+ # Setup menu bar
+ self._setup_menu_bar()
+
# Restore window geometry
self._restore_geometry()
@@ -98,11 +101,11 @@ class MainWindow(QMainWindow):
def _setup_sidebar(self):
"""Set up sidebar navigation items."""
items = [
- ("CmdForge", "Welcome to CmdForge"),
- ("Tools", "Manage your tools"),
- ("Registry", "Browse and install tools"),
- ("Providers", "Configure AI providers"),
- ("Profiles", "AI persona profiles"),
+ ("CmdForge", "Welcome page - Quick actions and getting started (Ctrl+1)"),
+ ("Tools", "View, edit, delete, and test your local tools (Ctrl+2)"),
+ ("Registry", "Browse and install tools from the community (Ctrl+3)"),
+ ("Providers", "Configure AI backends like Claude, GPT, etc. (Ctrl+4)"),
+ ("Profiles", "AI persona profiles for customizing tool behavior"),
]
font = QFont()
@@ -228,6 +231,88 @@ class MainWindow(QMainWindow):
shortcut_quit = QShortcut(QKeySequence("Ctrl+Q"), self)
shortcut_quit.activated.connect(self.close)
+ # F1: Help
+ shortcut_help = QShortcut(QKeySequence("F1"), self)
+ shortcut_help.activated.connect(self._show_getting_started)
+
+ def _setup_menu_bar(self):
+ """Set up the menu bar with Help menu."""
+ menubar = self.menuBar()
+
+ # Help menu
+ help_menu = menubar.addMenu("&Help")
+
+ # Getting Started
+ action_getting_started = QAction("&Getting Started", self)
+ action_getting_started.setShortcut("F1")
+ action_getting_started.triggered.connect(self._show_getting_started)
+ help_menu.addAction(action_getting_started)
+
+ help_menu.addSeparator()
+
+ # Quick Guides
+ action_create_tool = QAction("How to &Create a Tool", self)
+ action_create_tool.triggered.connect(self._show_create_tool_guide)
+ help_menu.addAction(action_create_tool)
+
+ action_install_tools = QAction("How to &Install Tools", self)
+ action_install_tools.triggered.connect(self._show_install_tools_guide)
+ help_menu.addAction(action_install_tools)
+
+ action_publish = QAction("How to &Publish Tools", self)
+ action_publish.triggered.connect(self._show_publish_guide)
+ help_menu.addAction(action_publish)
+
+ help_menu.addSeparator()
+
+ # Keyboard Shortcuts
+ action_shortcuts = QAction("&Keyboard Shortcuts", self)
+ action_shortcuts.triggered.connect(self._show_keyboard_shortcuts)
+ help_menu.addAction(action_shortcuts)
+
+ help_menu.addSeparator()
+
+ # About
+ action_about = QAction("&About CmdForge", self)
+ action_about.triggered.connect(self._show_about)
+ help_menu.addAction(action_about)
+
+ def _show_getting_started(self):
+ """Show Getting Started guide."""
+ from .dialogs.help_dialogs import QuickGuideDialog
+ dialog = QuickGuideDialog("getting_started", self)
+ dialog.exec()
+
+ def _show_create_tool_guide(self):
+ """Show Create Tool guide."""
+ from .dialogs.help_dialogs import QuickGuideDialog
+ dialog = QuickGuideDialog("create_tool", self)
+ dialog.exec()
+
+ def _show_install_tools_guide(self):
+ """Show Install Tools guide."""
+ from .dialogs.help_dialogs import QuickGuideDialog
+ dialog = QuickGuideDialog("install_tools", self)
+ dialog.exec()
+
+ def _show_publish_guide(self):
+ """Show Publish Tools guide."""
+ from .dialogs.help_dialogs import QuickGuideDialog
+ dialog = QuickGuideDialog("publish_tools", self)
+ dialog.exec()
+
+ def _show_keyboard_shortcuts(self):
+ """Show Keyboard Shortcuts guide."""
+ from .dialogs.help_dialogs import QuickGuideDialog
+ dialog = QuickGuideDialog("keyboard_shortcuts", self)
+ dialog.exec()
+
+ def _show_about(self):
+ """Show About dialog."""
+ from .dialogs.help_dialogs import AboutDialog
+ dialog = AboutDialog(self)
+ dialog.exec()
+
def _shortcut_new_tool(self):
"""Handle Ctrl+N: Create new tool."""
self.open_tool_builder()
diff --git a/src/cmdforge/gui/pages/registry_page.py b/src/cmdforge/gui/pages/registry_page.py
index 1b4dd6f..8892ba7 100644
--- a/src/cmdforge/gui/pages/registry_page.py
+++ b/src/cmdforge/gui/pages/registry_page.py
@@ -129,6 +129,7 @@ class RegistryPage(QWidget):
# Browse all button
self.btn_browse = QPushButton("Browse All")
+ self.btn_browse.setToolTip("Clear filters and show all available tools")
self.btn_browse.clicked.connect(self._browse_all)
header.addWidget(self.btn_browse)
@@ -143,6 +144,7 @@ class RegistryPage(QWidget):
# Search input
self.search_input = QLineEdit()
self.search_input.setPlaceholderText("Search tools...")
+ self.search_input.setToolTip("Search by name, description, or keywords (press Enter)")
self.search_input.returnPressed.connect(self._do_search)
filters_layout.addWidget(self.search_input, 2)
@@ -152,6 +154,7 @@ class RegistryPage(QWidget):
self.category_combo = QComboBox()
self.category_combo.addItems(["All", "Text", "Developer", "Data", "Other"])
self.category_combo.setMinimumWidth(100)
+ self.category_combo.setToolTip("Filter by tool category")
self.category_combo.currentTextChanged.connect(self._on_filter_changed)
filters_layout.addWidget(self.category_combo)
@@ -163,11 +166,13 @@ class RegistryPage(QWidget):
self.sort_combo.addItem("Newest", "published_at")
self.sort_combo.addItem("Name (A-Z)", "name")
self.sort_combo.setMinimumWidth(120)
+ self.sort_combo.setToolTip("Change sort order of results")
self.sort_combo.currentIndexChanged.connect(self._on_filter_changed)
filters_layout.addWidget(self.sort_combo)
# Search button
self.btn_search = QPushButton("Search")
+ self.btn_search.setToolTip("Search with current filters")
self.btn_search.clicked.connect(self._do_search)
filters_layout.addWidget(self.btn_search)
@@ -196,6 +201,7 @@ class RegistryPage(QWidget):
self.results_table = QTableWidget()
self.results_table.setColumnCount(6)
self.results_table.setHorizontalHeaderLabels(["", "Name", "Owner", "Rating", "Downloads", "Version"])
+ self.results_table.setToolTip("Click a tool to see details. ✓ = installed, ↑ = update available")
self.results_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Fixed)
self.results_table.setColumnWidth(0, 30) # Installed indicator
self.results_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
@@ -215,6 +221,7 @@ class RegistryPage(QWidget):
pag_layout.setContentsMargins(0, 8, 0, 0)
self.btn_prev = QPushButton("← Previous")
+ self.btn_prev.setToolTip("Go to previous page of results")
self.btn_prev.clicked.connect(self._prev_page)
self.btn_prev.setEnabled(False)
pag_layout.addWidget(self.btn_prev)
@@ -228,6 +235,7 @@ class RegistryPage(QWidget):
pag_layout.addStretch()
self.btn_next = QPushButton("Next →")
+ self.btn_next.setToolTip("Go to next page of results")
self.btn_next.clicked.connect(self._next_page)
self.btn_next.setEnabled(False)
pag_layout.addWidget(self.btn_next)
@@ -263,6 +271,7 @@ class RegistryPage(QWidget):
self.version_combo.setMinimumWidth(120)
self.version_combo.addItem("Latest", None)
self.version_combo.setEnabled(False)
+ self.version_combo.setToolTip("Select which version to install")
version_row.addWidget(self.version_combo)
version_row.addStretch()
@@ -272,11 +281,13 @@ class RegistryPage(QWidget):
actions = QHBoxLayout()
self.btn_install = QPushButton("Install")
+ self.btn_install.setToolTip("Download and install this tool locally")
self.btn_install.clicked.connect(self._install_tool)
self.btn_install.setEnabled(False)
actions.addWidget(self.btn_install)
self.btn_update = QPushButton("Update Available")
+ self.btn_update.setToolTip("Update to the latest version from the registry")
self.btn_update.clicked.connect(self._install_tool)
self.btn_update.setEnabled(False)
self.btn_update.setStyleSheet("background-color: #48bb78; color: white;")
diff --git a/src/cmdforge/gui/pages/tool_builder_page.py b/src/cmdforge/gui/pages/tool_builder_page.py
index 3d4d95e..f8ee2e8 100644
--- a/src/cmdforge/gui/pages/tool_builder_page.py
+++ b/src/cmdforge/gui/pages/tool_builder_page.py
@@ -51,10 +51,12 @@ class ToolBuilderPage(QWidget):
self.btn_cancel = QPushButton("Cancel")
self.btn_cancel.setObjectName("secondary")
+ self.btn_cancel.setToolTip("Cancel and return to tools page (Escape)")
self.btn_cancel.clicked.connect(self._cancel)
header_layout.addWidget(self.btn_cancel)
self.btn_save = QPushButton("Save")
+ self.btn_save.setToolTip("Save this tool (Ctrl+S)")
self.btn_save.clicked.connect(self._save)
header_layout.addWidget(self.btn_save)
@@ -76,14 +78,17 @@ class ToolBuilderPage(QWidget):
self.name_input = QLineEdit()
self.name_input.setPlaceholderText("my-tool")
+ self.name_input.setToolTip("Unique tool name using lowercase letters, numbers, and hyphens")
info_layout.addRow("Name:", self.name_input)
self.desc_input = QLineEdit()
self.desc_input.setPlaceholderText("A brief description of what this tool does")
+ self.desc_input.setToolTip("Brief description shown in tool listings and help text")
info_layout.addRow("Description:", self.desc_input)
self.category_combo = QComboBox()
self.category_combo.setEditable(True)
+ self.category_combo.setToolTip("Category for organizing tools (select or type custom)")
for cat in DEFAULT_CATEGORIES:
self.category_combo.addItem(cat)
info_layout.addRow("Category:", self.category_combo)
@@ -91,9 +96,19 @@ class ToolBuilderPage(QWidget):
left_layout.addWidget(info_box)
# Arguments group
- args_box = QGroupBox("Arguments")
+ args_box = QGroupBox()
args_layout = QVBoxLayout(args_box)
+ args_label = QLabel("Arguments")
+ args_label.setObjectName("sectionHeading")
+ args_label.setToolTip(
+ "Arguments are command-line flags users can pass to your tool.
"
+ "Example: Adding --max with variable 'max' lets users run:
"
+ "my-tool --max 100 < input.txt
"
+ "Use {max} in your prompts to reference the value.
"
+ )
+ args_layout.addWidget(args_label)
+
self.args_list = QListWidget()
self.args_list.itemDoubleClicked.connect(self._edit_argument)
args_layout.addWidget(self.args_list)
@@ -127,21 +142,37 @@ class ToolBuilderPage(QWidget):
right_layout.setSpacing(16)
# Steps group with view toggle
- steps_box = QGroupBox("Steps")
+ steps_box = QGroupBox()
steps_layout = QVBoxLayout(steps_box)
- # View toggle header
- view_header = QHBoxLayout()
+ # Steps header with label and view toggle
+ steps_header = QHBoxLayout()
+ steps_label = QLabel("Steps")
+ steps_label.setObjectName("sectionHeading")
+ steps_label.setToolTip(
+ "Steps define what your tool does, executed in order.
"
+ "Step types:
"
+ "- Prompt: Call an AI provider with a template
"
+ "- Code: Run Python code to process data
"
+ "- Tool: Call another CmdForge tool
"
+ "Use {variable} syntax to pass data between steps.
"
+ "Built-in: {input} contains stdin content.
"
+ )
+ steps_header.addWidget(steps_label)
+ steps_header.addStretch()
+ # View toggle buttons
self.btn_list_view = QPushButton("List")
self.btn_list_view.setCheckable(True)
self.btn_list_view.setChecked(True)
self.btn_list_view.setObjectName("viewToggle")
+ self.btn_list_view.setToolTip("View steps as a list (drag to reorder)")
self.btn_list_view.clicked.connect(lambda: self._set_view_mode(0))
self.btn_flow_view = QPushButton("Flow")
self.btn_flow_view.setCheckable(True)
self.btn_flow_view.setObjectName("viewToggle")
+ self.btn_flow_view.setToolTip("View steps as a visual flow graph")
self.btn_flow_view.clicked.connect(lambda: self._set_view_mode(1))
# Button group for mutual exclusivity
@@ -149,10 +180,9 @@ class ToolBuilderPage(QWidget):
self.view_group.addButton(self.btn_list_view, 0)
self.view_group.addButton(self.btn_flow_view, 1)
- view_header.addWidget(self.btn_list_view)
- view_header.addWidget(self.btn_flow_view)
- view_header.addStretch()
- steps_layout.addLayout(view_header)
+ steps_header.addWidget(self.btn_list_view)
+ steps_header.addWidget(self.btn_flow_view)
+ steps_layout.addLayout(steps_header)
# Stacked widget for list/flow views
self.steps_stack = QStackedWidget()
@@ -183,24 +213,29 @@ class ToolBuilderPage(QWidget):
# Step action buttons
steps_btns = QHBoxLayout()
self.btn_add_prompt = QPushButton("Add Prompt")
+ self.btn_add_prompt.setToolTip("Add an AI prompt step - calls your provider with a template")
self.btn_add_prompt.clicked.connect(self._add_prompt_step)
steps_btns.addWidget(self.btn_add_prompt)
self.btn_add_code = QPushButton("Add Code")
+ self.btn_add_code.setToolTip("Add a Python code step - process or transform data")
self.btn_add_code.clicked.connect(self._add_code_step)
steps_btns.addWidget(self.btn_add_code)
self.btn_add_tool = QPushButton("Add Tool")
+ self.btn_add_tool.setToolTip("Add a tool step - chain another CmdForge tool")
self.btn_add_tool.clicked.connect(self._add_tool_step)
steps_btns.addWidget(self.btn_add_tool)
self.btn_edit_step = QPushButton("Edit")
self.btn_edit_step.setObjectName("secondary")
+ self.btn_edit_step.setToolTip("Edit the selected step (or double-click)")
self.btn_edit_step.clicked.connect(self._edit_step)
steps_btns.addWidget(self.btn_edit_step)
self.btn_del_step = QPushButton("Delete")
self.btn_del_step.setObjectName("danger")
+ self.btn_del_step.setToolTip("Delete the selected step")
self.btn_del_step.clicked.connect(self._delete_step)
steps_btns.addWidget(self.btn_del_step)
@@ -210,9 +245,21 @@ class ToolBuilderPage(QWidget):
right_layout.addWidget(steps_box, 1) # Steps box gets stretch priority
# Output group
- output_box = QGroupBox("Output Template")
+ output_box = QGroupBox()
output_layout = QVBoxLayout(output_box)
+ output_label = QLabel("Output Template")
+ output_label.setObjectName("sectionHeading")
+ output_label.setToolTip(
+ "The output template defines what your tool prints.
"
+ "Use {variable} to include step outputs:
"
+ "- {response} - typical AI response variable
"
+ "- {result} - common code step output
"
+ "- Any output_var you defined in steps
"
+ "Example: {response} prints just the AI response.
"
+ )
+ output_layout.addWidget(output_label)
+
self.output_input = QTextEdit()
self.output_input.setPlaceholderText("Use {variable} to reference step outputs, e.g. {response}")
self.output_input.setPlainText("{response}") # Default value
diff --git a/src/cmdforge/gui/styles.py b/src/cmdforge/gui/styles.py
index fdf57b5..0ed31a5 100644
--- a/src/cmdforge/gui/styles.py
+++ b/src/cmdforge/gui/styles.py
@@ -225,6 +225,12 @@ QLabel#label {
color: #4a5568;
}
+QLabel#sectionHeading {
+ font-weight: 600;
+ font-size: 13px;
+ color: #2d3748;
+}
+
/* Scrollbars */
QScrollBar:vertical {
background-color: #f7fafc;