Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var BashTool = DefineTool("bash", "Execute a shell command in the current working directory. Returns combined stdout and stderr. Output is truncated to last max_lines lines (default: 500).", map[string]any{ "type": "object", "properties": map[string]any{ "command": map[string]any{"type": "string", "description": "The shell command to execute"}, "description": map[string]any{"type": "string", "description": "Brief description of what this command does"}, "max_lines": map[string]any{"type": "integer", "description": "Maximum lines to return from the end of output (default: 500)"}, }, "required": []string{"command"}, }, func(ctx context.Context, p BashParams) (string, error) { script := "trap 'pkill -TERM -P $$ 2>/dev/null' EXIT\n" + p.Command scriptF, err := os.CreateTemp("", "mate-bash-*.sh") if err != nil { return "", fmt.Errorf("creating temp script: %w", err) } defer func() { _ = os.Remove(scriptF.Name()) }() if _, err := scriptF.WriteString(script); err != nil { _ = scriptF.Close() return "", fmt.Errorf("writing temp script: %w", err) } _ = scriptF.Close() outF, err := os.CreateTemp("", "mate-bash-out-*") if err != nil { return "", fmt.Errorf("creating output file: %w", err) } defer func() { _ = os.Remove(outF.Name()) }() cmd := exec.CommandContext(ctx, "bash", scriptF.Name()) cmd.Stdout = outF cmd.Stderr = outF cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} cmd.Cancel = func() error { return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) } runErr := cmd.Run() if cmd.Process != nil { pgid := cmd.Process.Pid _ = syscall.Kill(-pgid, syscall.SIGTERM) time.Sleep(20 * time.Millisecond) _ = syscall.Kill(-pgid, syscall.SIGKILL) } _ = outF.Close() outBytes, _ := os.ReadFile(outF.Name()) result := string(outBytes) if runErr != nil { result = strings.TrimSpace(result) if result == "" { result = runErr.Error() } } result = strings.TrimSpace(result) if result == "" { return result, nil } maxLines := p.MaxLines if maxLines <= 0 { maxLines = 500 } lines := strings.Split(result, "\n") if len(lines) <= maxLines { return result, nil } truncated := lines[len(lines)-maxLines:] return strings.Join(truncated, "\n"), nil }, )
View Source
var EditFileTool = DefineTool("edit_file", "Edit a file using exact text replacement. Each oldText must be unique in the file. Multiple edits are applied in order.", map[string]any{ "type": "object", "properties": map[string]any{ "path": map[string]any{"type": "string", "description": "Path to the file to edit (relative or absolute)"}, "edits": map[string]any{ "type": "array", "items": map[string]any{ "type": "object", "properties": map[string]any{ "oldText": map[string]any{"type": "string", "description": "Exact text to replace"}, "newText": map[string]any{"type": "string", "description": "Replacement text"}, }, "required": []string{"oldText", "newText"}, }, }, }, "required": []string{"path", "edits"}, }, func(ctx context.Context, p EditParams) (string, error) { data, err := os.ReadFile(p.Path) if err != nil { return "", fmt.Errorf("read file %s: %w", p.Path, err) } content := string(data) applied := 0 for _, edit := range p.Edits { count := strings.Count(content, edit.OldText) if count == 0 { return "", fmt.Errorf("oldText not found in %s: %q", p.Path, edit.OldText) } if count > 1 { return "", fmt.Errorf("oldText found %d times in %s, must be unique: %q", count, p.Path, edit.OldText) } content = strings.Replace(content, edit.OldText, edit.NewText, 1) applied++ } if err := atomicWrite(p.Path, []byte(content)); err != nil { return "", fmt.Errorf("write file %s: %w", p.Path, err) } return fmt.Sprintf("Applied %d edit(s) to %s", applied, p.Path), nil }, )
View Source
var GlobTool = DefineTool("glob", "Find files matching a glob pattern. Supports ** for recursive matching. Skips common VCS/dependency directories and respects .gitignore.", map[string]any{ "type": "object", "properties": map[string]any{ "pattern": map[string]any{"type": "string", "description": "Glob pattern, e.g. \"**/*.go\", \"src/*_test.go\", \"*.md\""}, "path": map[string]any{"type": "string", "description": "Root directory to search from (default: current working directory)"}, "max_results": map[string]any{"type": "integer", "description": "Maximum results (default: 50)"}, }, "required": []string{"pattern"}, }, func(ctx context.Context, p GlobParams) (string, error) { if p.Path == "" { p.Path = "." } if p.MaxResults <= 0 { p.MaxResults = 50 } re, err := globToRegex(p.Pattern) if err != nil { return "", fmt.Errorf("glob: %w", err) } ig := ParseGitignore(p.Path) var results []string walkErr := filepath.WalkDir(p.Path, func(path string, d fs.DirEntry, err error) error { if err != nil { return nil } rel, _ := filepath.Rel(p.Path, path) if d.IsDir() { if shouldSkipDir(d.Name()) { return filepath.SkipDir } if ig.IsIgnored(rel, true) { return filepath.SkipDir } return nil } if ig.IsIgnored(rel, false) { return nil } if re.MatchString(rel) { results = append(results, rel) if len(results) >= p.MaxResults { return fs.SkipAll } } return nil }) if walkErr != nil { return "", fmt.Errorf("glob: %w", walkErr) } sort.Strings(results) return strings.Join(results, "\n"), nil }, )
View Source
var GrepTool = DefineTool("grep", "Search for a pattern in files. Returns matching lines with file path and line number. Skips binary files and common VCS/dependency directories.", map[string]any{ "type": "object", "properties": map[string]any{ "pattern": map[string]any{"type": "string", "description": "Text or regex pattern to search for"}, "path": map[string]any{"type": "string", "description": "File or directory to search in (default: current working directory)"}, "glob": map[string]any{"type": "string", "description": "Filter file names, e.g. \"*.go\", \"*_test.go\""}, "max_results": map[string]any{"type": "integer", "description": "Maximum matches to return (default: 30)"}, "regex": map[string]any{"type": "boolean", "description": "Treat pattern as regex (default: false, literal match)"}, }, "required": []string{"pattern"}, }, func(ctx context.Context, p GrepParams) (string, error) { if p.Path == "" { p.Path = "." } if p.MaxResults <= 0 { p.MaxResults = 30 } matcher, err := buildMatcher(p.Pattern, p.Regex) if err != nil { return "", fmt.Errorf("grep: %w", err) } info, err := os.Stat(p.Path) if err != nil { return "", fmt.Errorf("grep: %w", err) } if !info.IsDir() { if isBinaryFile(p.Path) { return "", nil } return grepFile(p.Path, matcher, p.MaxResults) } ig := ParseGitignore(p.Path) var results []string walkErr := filepath.WalkDir(p.Path, func(path string, d fs.DirEntry, err error) error { if err != nil { return nil } if d.IsDir() { if shouldSkipDir(d.Name()) { return filepath.SkipDir } rel, _ := filepath.Rel(p.Path, path) if ig.IsIgnored(rel, true) { return filepath.SkipDir } return nil } rel, _ := filepath.Rel(p.Path, path) if ig.IsIgnored(rel, false) { return nil } if p.Glob != "" { matched, _ := filepath.Match(p.Glob, d.Name()) if !matched { return nil } } if isBinaryFile(path) { return nil } matches, _ := grepFile(path, matcher, p.MaxResults-len(results)) if matches != "" { results = append(results, matches) } if len(results) >= p.MaxResults { return fs.SkipAll } return nil }) if walkErr != nil { return "", fmt.Errorf("grep: %w", walkErr) } return strings.Join(results, "\n"), nil }, )
View Source
var ReadFileTool = DefineTool("read_file", "Read contents of a file. Supports text files. When no offset/limit is specified, returns up to 500 lines. Use offset/limit for large files.", map[string]any{ "type": "object", "properties": map[string]any{ "path": map[string]any{"type": "string", "description": "Path to the file to read (relative or absolute)"}, "offset": map[string]any{"type": "integer", "description": "Line number to start reading from (1-indexed)"}, "limit": map[string]any{"type": "integer", "description": "Maximum number of lines to read"}, }, "required": []string{"path"}, }, func(ctx context.Context, p ReadFileParams) (string, error) { if p.Offset < 1 { p.Offset = 1 } if p.Limit <= 0 { p.Limit = defaultReadLimit } content, _, err := readFileLines(p) if err != nil { return "", err } return content, nil }, )
View Source
var WriteFileTool = DefineTool("write_file", "Create or overwrite a file with the given content. Creates parent directories automatically.", map[string]any{ "type": "object", "properties": map[string]any{ "path": map[string]any{"type": "string", "description": "Path to the file to write (relative or absolute)"}, "content": map[string]any{"type": "string", "description": "Content to write to the file"}, }, "required": []string{"path", "content"}, }, func(ctx context.Context, p WriteFileParams) (string, error) { dir := filepath.Dir(p.Path) if err := os.MkdirAll(dir, 0755); err != nil { return "", fmt.Errorf("create parent directories for %s: %w", p.Path, err) } if err := atomicWrite(p.Path, []byte(p.Content)); err != nil { return "", fmt.Errorf("write file %s: %w", p.Path, err) } return fmt.Sprintf("Wrote %d bytes to %s", len(p.Content), p.Path), nil }, )
Functions ¶
func CatalogNames ¶
func CatalogNames() []string
Types ¶
type BashParams ¶
type EditParams ¶
type GitignoreMatcher ¶
type GitignoreMatcher struct {
// contains filtered or unexported fields
}
func ParseGitignore ¶
func ParseGitignore(root string) *GitignoreMatcher
type GitignoreRule ¶
type GitignoreRule struct {
// contains filtered or unexported fields
}
type GlobParams ¶
type GrepParams ¶
type ReadFileParams ¶
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
func NewRegistry ¶
func NewRegistry() *Registry
type Tool ¶
type Tool struct {
Name string
Description string
Parameters map[string]any
Execute func(ctx context.Context, rawParams json.RawMessage) (string, error)
}
func DefineTool ¶
type WriteFileParams ¶
Source Files
¶
Click to show internal directories.
Click to hide internal directories.