diff --git a/src/cmdforge/tool.py b/src/cmdforge/tool.py index 5a426db..dd1a676 100644 --- a/src/cmdforge/tool.py +++ b/src/cmdforge/tool.py @@ -290,22 +290,49 @@ def get_bin_dir() -> Path: def list_tools() -> list[str]: - """List all available tools.""" + """List all available tools. + + Returns tools from: + - Direct children: ~/.cmdforge//config.yaml + - Owner subdirectories: ~/.cmdforge///config.yaml + """ tools_dir = get_tools_dir() tools = [] + if not tools_dir.exists(): + return tools + for item in tools_dir.iterdir(): if item.is_dir(): config = item / "config.yaml" if config.exists(): + # Direct tool (e.g., ~/.cmdforge/my-tool/) tools.append(item.name) + else: + # Check if this is an owner directory with nested tools + # (e.g., ~/.cmdforge/official/summarize/) + for subitem in item.iterdir(): + if subitem.is_dir(): + subconfig = subitem / "config.yaml" + if subconfig.exists(): + # Qualified name: owner/name + tools.append(f"{item.name}/{subitem.name}") return sorted(tools) def load_tool(name: str) -> Optional[Tool]: - """Load a tool by name.""" - config_path = get_tools_dir() / name / "config.yaml" + """Load a tool by name. + + Args: + name: Tool name, can be: + - Simple: "my-tool" (looks in ~/.cmdforge/my-tool/) + - Qualified: "official/summarize" (looks in ~/.cmdforge/official/summarize/) + """ + tools_dir = get_tools_dir() + + # Try direct path first (handles both simple and qualified names) + config_path = tools_dir / name / "config.yaml" if not config_path.exists(): return None