From 3c2bf3eb40592d6d4f867d61a202eb17d72da14a Mon Sep 17 00:00:00 2001 From: ollie Date: Tue, 21 Jul 2026 13:36:16 +0200 Subject: [PATCH] tools: two-tier discovery (surface listing + on-demand prompt) BuildRuntime now appends a compact surface listing (name + one-liner) to the preamble instead of full tool prompts. Full prompts are available on-demand via ToolPrompt(name) for the /net/dns pattern. - DiscoverTools: extracts short description from first content line - BuildRuntime: appends "# Available Tools" with name + description - ToolPrompt(name): returns full ollie:prompt for a specific tool --- pkg/agent/core.go | 15 ++++++++------- pkg/tools/execute/tool.go | 38 +++++++++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/pkg/agent/core.go b/pkg/agent/core.go index 00bed11..2931f9f 100644 --- a/pkg/agent/core.go +++ b/pkg/agent/core.go @@ -163,16 +163,17 @@ func BuildRuntime(cfg *config.Config, d tools.Dispatcher, cwd string, env []stri } } - // Append tool prompts discovered from script headers. - var toolPrompts strings.Builder + // Append compact tool surface listing (name + description). + // Full prompts are available on-demand via /tools write (net/dns pattern). + var toolListing strings.Builder for _, ti := range allToolInfos { - if ti.Prompt != "" { - toolPrompts.WriteString(ti.Prompt) - toolPrompts.WriteByte('\n') + if ti.Description != "" && ti.Server == "" { + // Only named tool scripts (Server==""), not built-in executors + fmt.Fprintf(&toolListing, "- **%s** — %s\n", ti.Name, ti.Description) } } - if toolPrompts.Len() > 0 { - preamble += "\n" + toolPrompts.String() + if toolListing.Len() > 0 { + preamble += "\n# Available Tools\n\n" + toolListing.String() } return &Runtime{ diff --git a/pkg/tools/execute/tool.go b/pkg/tools/execute/tool.go index a2b633e..6cf3874 100644 --- a/pkg/tools/execute/tool.go +++ b/pkg/tools/execute/tool.go @@ -210,8 +210,7 @@ func ExtractPrompt(script string) string { } // DiscoverTools scans the tools directory and returns metadata for all scripts, -// including their ollie:prompt blocks. Used to populate the /tools file and -// inject tool prompts into the system prompt. +// including their ollie:prompt blocks and short descriptions. func DiscoverTools() []tools.ToolInfo { dir := ToolsPath() entries, err := os.ReadDir(dir) @@ -227,15 +226,44 @@ func DiscoverTools() []tools.ToolInfo { if err != nil { continue } - prompt := ExtractPrompt(string(data)) + script := string(data) + prompt := ExtractPrompt(script) + desc := extractShortDescription(prompt) infos = append(infos, tools.ToolInfo{ - Name: e.Name(), - Prompt: prompt, + Name: e.Name(), + Description: desc, + Prompt: prompt, }) } return infos } +// extractShortDescription gets the first non-heading, non-empty line from a prompt. +func extractShortDescription(prompt string) string { + for _, line := range strings.Split(prompt, "\n") { + line = strings.TrimSpace(line) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + return line + } + return "" +} + +// ToolPrompt returns the full ollie:prompt content for a named tool. +// Returns "" if the tool has no prompt or doesn't exist. +func ToolPrompt(name string) string { + if strings.Contains(name, "/") || strings.Contains(name, "..") { + return "" + } + path := filepath.Join(ToolsPath(), name) + data, err := os.ReadFile(path) + if err != nil { + return "" + } + return ExtractPrompt(string(data)) +} + // ReadTool reads a named tool script from the tools directory. func ReadTool(name string) (string, error) { if strings.Contains(name, "/") || strings.Contains(name, "..") {