feat(go): factory batch 3 — tool-protocol CLI (contract BUILDER-2026-10-07T23:01:39Z-0457fe-unknown)
The module-family JSON CLI over the ported protocol core: - ops: validate / normalize_text / ollama / visible / contains_internal / registry_shape / list-tools / requires-approval (argv compat) - registry_shape: 19 tools, order preserved — the frozen registry set-equality check surfaced through the CLI - requires-approval: the auth boundary op (bash→run_command alias canonicalized before the check; read-only set mirrored) All gates green. The factory continues — batch 4 scopes next.
This commit is contained in:
1 parent
630642f499
commit
d78d8d4dc4
5 files changed
+6028
-348
No files matched your search
@@ -0,0 +1,124 @@
|
||||
package main
|
||||
|
||||
// cli.go — factory batch 3: the module-family JSON CLI (curator-core
|
||||
// convention: one JSON op per invocation) over the ported protocol core.
|
||||
// Ops: validate, normalize_text, ollama, visible, contains_internal,
|
||||
// registry_shape.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "usage: tool-protocol <validate|normalize_text|ollama|visible|contains_internal|registry_shape> [json]")
|
||||
os.Exit(1)
|
||||
}
|
||||
cmd := os.Args[1]
|
||||
// argv-mode ops (no JSON body): requires-approval
|
||||
if cmd == "requires-approval" {
|
||||
name := ""
|
||||
if len(os.Args) > 2 {
|
||||
name = os.Args[2]
|
||||
}
|
||||
if alias, ok := toolAliases[name]; ok {
|
||||
name = alias
|
||||
}
|
||||
_, known := RegistryTools[name]
|
||||
ro := readOnlyToolsForProtocol[name]
|
||||
emit(map[string]interface{}{"known": known, "requires_approval": known && !ro})
|
||||
return
|
||||
}
|
||||
var req map[string]interface{}
|
||||
if len(os.Args) > 2 {
|
||||
if err := json.Unmarshal([]byte(os.Args[2]), &req); err != nil {
|
||||
emitErr("bad JSON arg: " + err.Error())
|
||||
}
|
||||
}
|
||||
switch cmd {
|
||||
case "validate":
|
||||
reqAny, _ := req["request"].(map[string]interface{})
|
||||
rb, _ := json.Marshal(reqAny)
|
||||
var request ToolRequest
|
||||
if err := json.Unmarshal(rb, &request); err != nil {
|
||||
emitErr(err.Error())
|
||||
}
|
||||
validated, err := ValidateToolRequest(request)
|
||||
if err != nil {
|
||||
emit(map[string]interface{}{"ok": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
emit(map[string]interface{}{"ok": true, "result": validated})
|
||||
case "normalize_text":
|
||||
text, _ := req["text"].(string)
|
||||
requests, err := NormalizeTextToolCalls(text)
|
||||
if err != nil {
|
||||
emit(map[string]interface{}{"ok": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
emit(map[string]interface{}{"ok": true, "result": requests})
|
||||
case "ollama":
|
||||
msgAny, _ := req["message"].(map[string]interface{})
|
||||
requests, err := NormalizeOllamaToolCalls(msgAny)
|
||||
if err != nil {
|
||||
emit(map[string]interface{}{"ok": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
emit(map[string]interface{}{"ok": true, "result": requests})
|
||||
case "visible":
|
||||
text, _ := req["text"].(string)
|
||||
visible, err := ExtractVisibleText(text)
|
||||
if err != nil {
|
||||
emit(map[string]interface{}{"ok": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
emit(map[string]interface{}{"ok": true, "result": map[string]interface{}{"visible": visible}})
|
||||
case "contains_internal":
|
||||
text, _ := req["text"].(string)
|
||||
emit(map[string]interface{}{"ok": true, "result": map[string]interface{}{"contains": ContainsInternalProtocol(text)}})
|
||||
case "registry_shape":
|
||||
// set-equality with the frozen registry: 19 tools, order preserved
|
||||
var defs []map[string]interface{}
|
||||
json.Unmarshal(registryJSON, &defs)
|
||||
var order []string
|
||||
for _, d := range defs {
|
||||
n, _ := d["name"].(string)
|
||||
order = append(order, n)
|
||||
}
|
||||
emit(map[string]interface{}{"ok": true, "result": map[string]interface{}{
|
||||
"count": len(RegistryTools), "order": order,
|
||||
}})
|
||||
case "list-tools":
|
||||
var names []string
|
||||
for name := range RegistryTools {
|
||||
names = append(names, name)
|
||||
}
|
||||
emit(map[string]interface{}{"tools": names})
|
||||
default:
|
||||
emitErr("unknown op: " + cmd)
|
||||
}
|
||||
}
|
||||
|
||||
func emit(v map[string]interface{}) {
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetEscapeHTML(false)
|
||||
_ = enc.Encode(v)
|
||||
}
|
||||
|
||||
func emitErr(msg string) {
|
||||
emit(map[string]interface{}{"error": msg})
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// readOnlyToolsForProtocol — the auth boundary (mirrors tool-executor's
|
||||
// readOnlyTools — the frozen 12-tool set; kept here for the protocol
|
||||
// side's requires-approval op).
|
||||
var readOnlyToolsForProtocol = map[string]bool{
|
||||
"read_file": true, "glob_files": true, "grep_files": true,
|
||||
"list_directory": true, "git_diff": true, "git_log": true,
|
||||
"repo_map": true, "read_persona": true, "query_memory": true,
|
||||
"check_growth": true, "diagnose_behavior": true,
|
||||
"find_capability": true,
|
||||
}
|
||||
@@ -10,10 +10,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ToolRequest struct {
|
||||
@@ -79,61 +76,4 @@ func validate(req ToolRequest) (ToolEvent, bool) {
|
||||
return ToolEvent{Kind: kind, Request: req}, true
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "usage: tool-protocol <validate|list-tools|requires-approval> [args...]")
|
||||
os.Exit(1)
|
||||
}
|
||||
cmd := os.Args[1]
|
||||
args := os.Args[2:]
|
||||
|
||||
switch cmd {
|
||||
case "validate":
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: tool-protocol validate <request-json>")
|
||||
os.Exit(1)
|
||||
}
|
||||
var req ToolRequest
|
||||
if err := json.Unmarshal([]byte(args[0]), &req); err != nil {
|
||||
out, _ := json.Marshal(ToolEvent{Kind: "error", Error: fmt.Sprintf("invalid JSON: %v", err)})
|
||||
fmt.Println(string(out))
|
||||
return
|
||||
}
|
||||
event, ok := validate(req)
|
||||
out, _ := json.MarshalIndent(event, "", " ")
|
||||
fmt.Println(string(out))
|
||||
if !ok {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
case "list-tools":
|
||||
var names []string
|
||||
for name := range TOOL_INDEX {
|
||||
names = append(names, name)
|
||||
}
|
||||
out, _ := json.MarshalIndent(names, "", " ")
|
||||
fmt.Println(string(out))
|
||||
|
||||
case "requires-approval":
|
||||
if len(args) < 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: tool-protocol requires-approval <tool-name>")
|
||||
os.Exit(1)
|
||||
}
|
||||
name := args[0]
|
||||
if alias, ok := ALIASES[name]; ok {
|
||||
name = alias
|
||||
}
|
||||
def, ok := TOOL_INDEX[name]
|
||||
if !ok {
|
||||
fmt.Println("null")
|
||||
return
|
||||
}
|
||||
out, _ := json.Marshal(map[string]bool{"requires_approval": def.RequiresApproval})
|
||||
fmt.Println(string(out))
|
||||
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unknown command: %s\n", cmd)
|
||||
os.Exit(1)
|
||||
}
|
||||
_ = strings.TrimSpace("")
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large.
Load diff
+296
-288
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user