clipboard.go
1 package tools 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 "os/exec" 9 10 "github.com/Kocoro-lab/ShanClaw/internal/agent" 11 ) 12 13 type ClipboardTool struct{} 14 15 type clipboardArgs struct { 16 Action string `json:"action"` 17 Content string `json:"content,omitempty"` 18 } 19 20 func (t *ClipboardTool) Info() agent.ToolInfo { 21 return agent.ToolInfo{ 22 Name: "clipboard", 23 Description: "Read or write the system clipboard (macOS only). Use action 'read' to get clipboard contents, 'write' to set them.", 24 Parameters: map[string]any{ 25 "type": "object", 26 "properties": map[string]any{ 27 "action": map[string]any{"type": "string", "description": "Action: 'read' or 'write'"}, 28 "content": map[string]any{"type": "string", "description": "Content to write (required for write action)"}, 29 }, 30 }, 31 Required: []string{"action"}, 32 } 33 } 34 35 func (t *ClipboardTool) Run(ctx context.Context, argsJSON string) (agent.ToolResult, error) { 36 var args clipboardArgs 37 if err := json.Unmarshal([]byte(argsJSON), &args); err != nil { 38 return agent.ToolResult{Content: fmt.Sprintf("invalid arguments: %v", err), IsError: true}, nil 39 } 40 41 switch args.Action { 42 case "read": 43 cmd := exec.CommandContext(ctx, "pbpaste") 44 output, err := cmd.CombinedOutput() 45 if err != nil { 46 return agent.ToolResult{Content: fmt.Sprintf("clipboard read error: %v", err), IsError: true}, nil 47 } 48 return agent.ToolResult{Content: string(output)}, nil 49 50 case "write": 51 if args.Content == "" { 52 return agent.ToolResult{Content: "content is required for write action", IsError: true}, nil 53 } 54 cmd := exec.CommandContext(ctx, "pbcopy") 55 cmd.Stdin = bytes.NewReader([]byte(args.Content)) 56 if err := cmd.Run(); err != nil { 57 return agent.ToolResult{Content: fmt.Sprintf("clipboard write error: %v", err), IsError: true}, nil 58 } 59 return agent.ToolResult{Content: fmt.Sprintf("wrote %d bytes to clipboard", len(args.Content))}, nil 60 61 default: 62 return agent.ToolResult{Content: fmt.Sprintf("unknown action: %q (use 'read' or 'write')", args.Action), IsError: true}, nil 63 } 64 } 65 66 func (t *ClipboardTool) RequiresApproval() bool { return true } 67 68 func (t *ClipboardTool) IsReadOnlyCall(argsJSON string) bool { 69 var args struct { 70 Action string `json:"action"` 71 } 72 if json.Unmarshal([]byte(argsJSON), &args) != nil { 73 return false 74 } 75 return args.Action == "read" 76 }