Fix list_tools and load_tool to handle owner subdirectories

Tools from the registry are installed to ~/.cmdforge/owner/name/ (e.g.,
~/.cmdforge/official/summarize/), but list_tools() only searched direct
children of ~/.cmdforge/.

Changes:
- list_tools() now searches both direct children and owner subdirectories
- Returns qualified names for nested tools (e.g., "official/summarize")
- load_tool() already handled qualified paths, added docstring clarification

Fixes tools installed via `cmdforge install` not appearing in `cmdforge list`.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rob 2026-01-27 01:10:57 -04:00
parent be712ddc2e
commit ce94ba89dc
1 changed files with 30 additions and 3 deletions

View File

@ -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/<name>/config.yaml
- Owner subdirectories: ~/.cmdforge/<owner>/<name>/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