Removes tools from grouping.

This commit is contained in:
2026-02-22 21:31:21 -08:00
parent d43c49d219
commit a5e9835d55
77 changed files with 260 additions and 2497 deletions

View File

@@ -79,13 +79,6 @@ class ToolSummary(BaseModel):
installed: bool = False
class ToolCategory(BaseModel):
"""Tools grouped by category."""
name: str
tools: list[ToolSummary]
class ToolDetail(ToolSummary):
"""Full detail for a single tool, including documentation."""

View File

@@ -11,7 +11,7 @@ from castle_cli.config import load_config
from castle_cli.manifest import ComponentManifest
from castle_api.config import settings
from castle_api.models import ToolCategory, ToolDetail, ToolSummary
from castle_api.models import ToolDetail, ToolSummary
router = APIRouter(tags=["tools"])
@@ -42,55 +42,16 @@ def _tool_summary(name: str, manifest: ComponentManifest, root: Path | None = No
)
def _find_md_for_tool(
root: Path,
source: str,
tool_name: str,
) -> Path | None:
"""Find the .md documentation file for a tool source path."""
source_path = root / source
if source_path.is_file():
md = source_path.with_suffix(".md")
if md.exists():
return md
elif source_path.is_dir():
py_name = tool_name.replace("-", "_")
pkg_name = source_path.name
md = source_path / "src" / pkg_name / f"{py_name}.md"
if md.exists():
return md
return None
def _strip_frontmatter(content: str) -> str:
"""Strip YAML frontmatter from markdown content."""
if content.startswith("---\n"):
end = content.find("\n---\n", 4)
if end != -1:
content = content[end + 5:]
return content.strip()
@router.get("/tools", response_model=list[ToolCategory])
def list_tools() -> list[ToolCategory]:
"""List tools grouped by source directory."""
@router.get("/tools", response_model=list[ToolSummary])
def list_tools() -> list[ToolSummary]:
"""List all registered tools."""
config = load_config(settings.castle_root)
tools = {k: v for k, v in config.components.items() if v.tool}
by_group: dict[str, list[ToolSummary]] = {}
for name, manifest in tools.items():
t = manifest.tool
assert t is not None
if t.source:
group = Path(t.source).name
else:
group = "standalone"
by_group.setdefault(group, []).append(_tool_summary(name, manifest, config.root))
return [
ToolCategory(name=group, tools=sorted(items, key=lambda t: t.id))
for group, items in sorted(by_group.items())
]
return sorted(
[_tool_summary(name, manifest, config.root) for name, manifest in tools.items()],
key=lambda t: t.id,
)
@router.get("/tools/{name}", response_model=ToolDetail)
@@ -112,16 +73,7 @@ def get_tool(name: str) -> ToolDetail:
)
summary = _tool_summary(name, manifest, config.root)
docs: str | None = None
t = manifest.tool
if t.source:
md_path = _find_md_for_tool(config.root, t.source, name)
if md_path and md_path.exists():
docs = _strip_frontmatter(md_path.read_text())
if not docs:
docs = None
return ToolDetail(**summary.model_dump(), docs=docs)
return ToolDetail(**summary.model_dump())
@router.post("/tools/{name}/install")