diff --git a/src/cmdforge/gui/pages/tool_builder_page.py b/src/cmdforge/gui/pages/tool_builder_page.py index 145ec9c..02f1e04 100644 --- a/src/cmdforge/gui/pages/tool_builder_page.py +++ b/src/cmdforge/gui/pages/tool_builder_page.py @@ -133,6 +133,47 @@ class ToolBuilderPage(QWidget): left_layout.addWidget(args_box) + # Dependencies group + deps_box = QGroupBox() + deps_layout = QVBoxLayout(deps_box) + + deps_label = QLabel("Dependencies") + deps_label.setObjectName("sectionHeading") + deps_label.setToolTip( + "

Dependencies are other CmdForge tools this tool requires.

" + "

If your code steps call other tools via subprocess,
" + "declare them here so the dependency system knows about them.

" + "

Use this when you can't use a Tool step directly
" + "(e.g., calling tools in a loop or with complex logic).

" + ) + deps_layout.addWidget(deps_label) + + self.deps_list = QListWidget() + self.deps_list.setMaximumHeight(100) + deps_layout.addWidget(self.deps_list) + + # Dependency add row: combo + add button + deps_add_row = QHBoxLayout() + self.deps_combo = QComboBox() + self.deps_combo.setEditable(True) + self.deps_combo.setPlaceholderText("Select or type tool name...") + self.deps_combo.setToolTip("Select an installed tool or type a tool reference (e.g., official/summarize)") + self._populate_deps_combo() + deps_add_row.addWidget(self.deps_combo, 1) + + self.btn_add_dep = QPushButton("Add") + self.btn_add_dep.clicked.connect(self._add_dependency) + deps_add_row.addWidget(self.btn_add_dep) + + self.btn_del_dep = QPushButton("Remove") + self.btn_del_dep.setObjectName("danger") + self.btn_del_dep.clicked.connect(self._delete_dependency) + deps_add_row.addWidget(self.btn_del_dep) + + deps_layout.addLayout(deps_add_row) + + left_layout.addWidget(deps_box) + splitter.addWidget(left) # Right: Steps and output @@ -406,6 +447,9 @@ class ToolBuilderPage(QWidget): # Load arguments self._refresh_arguments() + # Load dependencies + self._refresh_dependencies() + # Load steps self._refresh_steps() @@ -579,6 +623,66 @@ class ToolBuilderPage(QWidget): del self._tool.arguments[idx] self._refresh_arguments() + def _populate_deps_combo(self): + """Populate dependencies combo with installed tools.""" + from ...tool import TOOLS_DIR + self.deps_combo.clear() + + # Get all installed tools + tools = [] + if TOOLS_DIR.exists(): + for item in TOOLS_DIR.iterdir(): + if item.is_dir(): + config = item / "config.yaml" + if config.exists(): + tools.append(item.name) + + # Sort and add to combo + for tool in sorted(tools): + self.deps_combo.addItem(tool) + + def _add_dependency(self): + """Add selected tool to dependencies.""" + tool_ref = self.deps_combo.currentText().strip() + if not tool_ref: + return + + if not self._tool: + # Create a minimal tool object if none exists + self._tool = Tool(name="", description="") + + # Initialize dependencies list if needed + if not hasattr(self._tool, 'dependencies') or self._tool.dependencies is None: + self._tool.dependencies = [] + + # Check if already in list + if tool_ref in self._tool.dependencies: + self.main_window.show_status(f"'{tool_ref}' is already a dependency") + return + + self._tool.dependencies.append(tool_ref) + self._refresh_dependencies() + self.deps_combo.setCurrentText("") + self.main_window.show_status(f"Added dependency: {tool_ref}") + + def _delete_dependency(self): + """Remove selected dependency.""" + items = self.deps_list.selectedItems() + if not items: + return + + dep = items[0].text() + if self._tool and self._tool.dependencies and dep in self._tool.dependencies: + self._tool.dependencies.remove(dep) + self._refresh_dependencies() + + def _refresh_dependencies(self): + """Refresh dependencies list widget.""" + self.deps_list.clear() + if self._tool and self._tool.dependencies: + for dep in self._tool.dependencies: + self.deps_list.addItem(dep) + def _add_tool_dependency(self, tool_ref: str): """Add a tool reference to the dependencies list if not already present. @@ -830,13 +934,18 @@ class ToolBuilderPage(QWidget): category=category, arguments=self._tool.arguments if self._tool else [], steps=self._tool.steps if self._tool else [], - output=output + output=output, + dependencies=self._tool.dependencies if self._tool else [] ) # Preserve source if editing if self._tool and self._tool.source: tool.source = self._tool.source + # Preserve version if editing + if self._tool and self._tool.version: + tool.version = self._tool.version + try: save_tool(tool) self.main_window.show_status(f"Saved tool '{name}'")