tools: pipe JSON args to stdin, sort tool/skill lists

- callPromotedTool now pipes raw JSON object to tool stdin instead of
  unpacking positional args. Tools parse their own input.
- Default schema changed from positional array to empty object.
- Registry.Discover() skips files without ollie:prompt marker.
- Registry.Summaries() and Loaded() return sorted results (both
  tools and skills registries).
This commit is contained in:
Levi Neely 2026-07-29 08:47:06 +02:00
parent d5c591ad26
commit 75b76b118e
4 changed files with 21 additions and 19 deletions

View File

@ -4,6 +4,7 @@ package skills
import (
"fmt"
"sort"
"os"
"path/filepath"
"strings"
@ -86,6 +87,7 @@ func (r *Registry) Summaries() []SkillInfo {
for _, info := range r.global {
out = append(out, info)
}
sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
return out
}
@ -133,6 +135,7 @@ func (r *Registry) Loaded(sessionID string) []SkillInfo {
out = append(out, info)
}
}
sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
return out
}

View File

@ -2,6 +2,8 @@ package execute
import (
"fmt"
"strings"
"sort"
"os"
"path/filepath"
"sync"
@ -45,6 +47,9 @@ func (r *Registry) Discover() error {
continue
}
script := string(data)
if !strings.Contains(script, "ollie:prompt") {
continue
}
global[e.Name()] = ParseToolInfo(e.Name(), script)
}
r.mu.Lock()
@ -63,6 +68,7 @@ func (r *Registry) Summaries() []tools.ToolInfo {
Description: info.Description,
})
}
sort.Slice(summaries, func(i, j int) bool { return summaries[i].Name < summaries[j].Name })
return summaries
}
@ -121,6 +127,7 @@ func (r *Registry) Loaded(sessionID string) []tools.ToolInfo {
for _, info := range sessionTools {
loaded = append(loaded, info)
}
sort.Slice(loaded, func(i, j int) bool { return loaded[i].Name < loaded[j].Name })
return loaded
}

View File

@ -79,7 +79,7 @@ func ParseToolInfo(name, script string) tools.ToolInfo {
desc := ExtractShortDescription(prompt)
argsSchema := ExtractArgsSchema(script)
if argsSchema == nil {
argsSchema = json.RawMessage(`{"type":"object","properties":{"tool":{"type":"string"},"args":{"type":"array","items":{"type":"string"}}},"required":["tool","args"]}`)
argsSchema = json.RawMessage(`{"type":"object","properties":{}}`)
}
meta := ExtractMetadata(script)
tier := ExtractTier(script)

View File

@ -274,7 +274,7 @@ func (e *Server) SetToolRegistry(r *Registry, sessionID string) {
}
// callPromotedTool executes a tool promoted via the registry by running the
// script file inside the sandbox, or elevated if the args request it.
// script file inside the sandbox, piping the JSON args to stdin.
func (e *Server) callPromotedTool(ctx context.Context, tool string, args json.RawMessage) (json.RawMessage, error) {
// Resolve script path.
if strings.Contains(tool, "/") || strings.Contains(tool, "..") {
@ -282,32 +282,23 @@ func (e *Server) callPromotedTool(ctx context.Context, tool string, args json.Ra
}
path := filepath.Join(ToolsPath(), tool)
// Parse args — extract positional args and optional elevated flag.
var positional []string
// Extract elevated flag (dispatch-level concern, not passed to tool).
elevated := false
var argMap map[string]interface{}
if err := json.Unmarshal(args, &argMap); err == nil {
if argsArr, ok := argMap["args"]; ok {
if arr, ok := argsArr.([]interface{}); ok {
for _, v := range arr {
positional = append(positional, fmt.Sprintf("%v", v))
}
}
}
if e, ok := argMap["elevated"]; ok {
if b, ok := e.(bool); ok {
elevated = b
}
// Remove elevated from the args passed to the tool.
delete(argMap, "elevated")
args, _ = json.Marshal(argMap)
}
}
// Build a shell command that runs the tool script with its args.
quoted := make([]string, 0, len(positional)+1)
quoted = append(quoted, path)
for _, a := range positional {
quoted = append(quoted, fmt.Sprintf("%q", a))
}
code := strings.Join(quoted, " ")
// The tool script receives JSON args on stdin.
code := path
stdinData := string(args)
var result string
var err error
@ -317,7 +308,7 @@ func (e *Server) callPromotedTool(ctx context.Context, tool string, args json.Ra
e.wdMu.RUnlock()
result, err = e.executeElevated(ctx, code, workDir, 30)
} else {
result, err = e.executeWithStdin(ctx, code, "bash", 30, "default", false, "")
result, err = e.executeWithStdin(ctx, code, "bash", 30, "default", false, stdinData)
}
if err != nil {
return json.Marshal(map[string]interface{}{
@ -332,6 +323,7 @@ func (e *Server) callPromotedTool(ctx context.Context, tool string, args json.Ra
// Close is called when the session ends. Calls OnClose hook if registered.
func (e *Server) Close() {
e.cleanupDetached()