refactor: update tools handling and improve CLI documentation

This commit is contained in:
2026-02-22 21:34:00 -08:00
parent a5e9835d55
commit eab6f8b535
7 changed files with 32 additions and 51 deletions

View File

@@ -33,9 +33,6 @@ castle gateway reload
# Standalone tool — CLI tool with argparse, stdin/stdout, Unix pipes # Standalone tool — CLI tool with argparse, stdin/stdout, Unix pipes
castle create my-tool --type tool --description "Does something" castle create my-tool --type tool --description "Does something"
# Category tool — adds to existing tools/<category>/ package
castle create my-tool --type tool --category document --description "Converts something"
``` ```
## CLI Reference ## CLI Reference
@@ -53,8 +50,8 @@ castle gateway start|stop|reload Manage Caddy reverse proxy
castle service enable|disable NAME Manage a systemd service castle service enable|disable NAME Manage a systemd service
castle service status Show all service statuses castle service status Show all service statuses
castle services start|stop Start/stop everything castle services start|stop Start/stop everything
castle tool list List tools grouped by category castle tool list List all tools
castle tool info NAME Show tool details + docs castle tool info NAME Show tool details
``` ```
## Registry ## Registry
@@ -96,7 +93,7 @@ central-context/ <- content storage API (git submodule)
notification-bridge/ <- desktop notification forwarder (git submodule) notification-bridge/ <- desktop notification forwarder (git submodule)
protonmail/ <- email sync tool/job protonmail/ <- email sync tool/job
devbox-connect/ <- SSH tunnel manager devbox-connect/ <- SSH tunnel manager
tools/ <- category tool packages (document/, search/, system/, etc.) pdf2md/ <- standalone tool (each tool is its own project)
ruff.toml <- shared lint config ruff.toml <- shared lint config
pyrightconfig.json <- shared type checking config pyrightconfig.json <- shared type checking config
``` ```
@@ -130,24 +127,23 @@ pyrightconfig.json <- shared type checking config
### Tools ### Tools
| Tool | Category | Description | | Tool | Description |
|------|----------|-------------| |------|-------------|
| docx2md | document | Convert Word .docx to Markdown | | android-backup | Backup Android devices via ADB |
| pdf2md | document | Convert PDF to Markdown | | browser | Browse the web via browser-use |
| html2text | document | Convert HTML to plain text | | devbox-connect | SSH tunnel manager |
| md2pdf | document | Convert Markdown to PDF | | docx-extractor | Extract content from Word files |
| mbox2eml | document | Convert MBOX mailboxes to .eml files | | docx2md | Convert Word .docx to Markdown |
| search | search | Manage searchable file collections | | gpt | OpenAI text generation |
| docx-extractor | search | Extract content from Word files | | html2text | Convert HTML to plain text |
| pdf-extractor | search | Extract content from PDF files | | mbox2eml | Convert MBOX mailboxes to .eml files |
| text-extractor | search | Extract content from text files | | md2pdf | Convert Markdown to PDF |
| schedule | system | Manage systemd user timers | | mdscraper | Combine text files into markdown |
| backup-collect | system | Collect files for backup | | pdf-extractor | Extract content from PDF files |
| android-backup | android | Backup Android devices via ADB | | pdf2md | Convert PDF to Markdown |
| browser | browser | Browse the web via browser-use | | schedule | Manage systemd user timers |
| gpt | gpt | OpenAI text generation | | search | Manage searchable file collections |
| mdscraper | mdscraper | Combine text files into markdown | | text-extractor | Extract content from text files |
| devbox-connect | standalone | SSH tunnel manager |
### Frontends ### Frontends

View File

@@ -4,7 +4,7 @@ import { useTools } from "@/services/api/hooks"
import { ToolCard } from "@/components/ToolCard" import { ToolCard } from "@/components/ToolCard"
export function ToolsPage() { export function ToolsPage() {
const { data: categories, isLoading } = useTools() const { data: tools, isLoading } = useTools()
return ( return (
<div className="max-w-6xl mx-auto px-6 py-8"> <div className="max-w-6xl mx-auto px-6 py-8">
@@ -14,24 +14,15 @@ export function ToolsPage() {
<h1 className="text-3xl font-bold mb-2">Tools</h1> <h1 className="text-3xl font-bold mb-2">Tools</h1>
<p className="text-sm text-[var(--muted)] mb-8"> <p className="text-sm text-[var(--muted)] mb-8">
CLI utilities grouped by category. Each tool is installed to PATH via castle and run with uv. CLI utilities installed to PATH via castle and run with uv.
</p> </p>
{isLoading ? ( {isLoading ? (
<p className="text-[var(--muted)]">Loading tools...</p> <p className="text-[var(--muted)]">Loading tools...</p>
) : categories?.length ? ( ) : tools?.length ? (
<div className="space-y-8"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{categories.map((cat) => ( {tools.map((tool) => (
<section key={cat.name}> <ToolCard key={tool.id} tool={tool} />
<h2 className="text-lg font-semibold mb-3 text-[var(--muted)]">
{cat.name}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{cat.tools.map((tool) => (
<ToolCard key={tool.id} tool={tool} />
))}
</div>
</section>
))} ))}
</div> </div>
) : ( ) : (

View File

@@ -8,7 +8,7 @@ import type {
GatewayInfo, GatewayInfo,
ServiceActionResponse, ServiceActionResponse,
SSEHealthEvent, SSEHealthEvent,
ToolCategory, ToolSummary,
ToolDetail, ToolDetail,
} from "@/types" } from "@/types"
@@ -110,7 +110,7 @@ export function useToolAction() {
export function useTools() { export function useTools() {
return useQuery({ return useQuery({
queryKey: ["tools"], queryKey: ["tools"],
queryFn: () => apiClient.get<ToolCategory[]>("/tools"), queryFn: () => apiClient.get<ToolSummary[]>("/tools"),
}) })
} }

View File

@@ -69,11 +69,6 @@ export interface ToolSummary {
installed: boolean installed: boolean
} }
export interface ToolCategory {
name: string
tools: ToolSummary[]
}
export interface ToolDetail extends ToolSummary { export interface ToolDetail extends ToolSummary {
docs: string | null docs: string | null
} }

View File

@@ -120,7 +120,7 @@ def build_parser() -> argparse.ArgumentParser:
# castle tool # castle tool
tool_parser = subparsers.add_parser("tool", help="Manage tools") tool_parser = subparsers.add_parser("tool", help="Manage tools")
tool_sub = tool_parser.add_subparsers(dest="tool_command") tool_sub = tool_parser.add_subparsers(dest="tool_command")
tool_sub.add_parser("list", help="List all tools by category") tool_sub.add_parser("list", help="List all tools")
tool_info_parser = tool_sub.add_parser("info", help="Show tool details") tool_info_parser = tool_sub.add_parser("info", help="Show tool details")
tool_info_parser.add_argument("name", help="Tool name") tool_info_parser.add_argument("name", help="Tool name")

View File

@@ -164,9 +164,8 @@ Each project has its own ruff rules (devbox-connect: `E,F,I,W`; mboxer: `ALL`).
Don't move toolkit in as a monolith. Instead: Don't move toolkit in as a monolith. Instead:
1. Add toolkit as a submodule 1. Add toolkit as a submodule
2. Graduate heavy tools (`search`, `protonmail`, `browser`) into independent castle projects 2. Graduate all tools into independent castle projects (each tool is its own standalone package)
3. Keep lightweight tools (`docx2md`, `html2text`, etc.) grouped in a single `tools` package 3. Promote toolkit's meta-tooling up into the castle CLI
4. Promote toolkit's meta-tooling up into the castle CLI
## 11. What to defer ## 11. What to defer