Commit 4e32ac4
Changed files (1)
cmd
del
cmd/del/main.go
@@ -110,6 +110,211 @@ func (o *OllamaProvider) Name() string {
return fmt.Sprintf("Ollama (%s)", o.model)
}
+func (d *Del) runCommand(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error) {
+ cmdStr, ok := args["command"].(string)
+ if !ok {
+ return nil, fmt.Errorf("missing 'command' string argument")
+ }
+ cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr)
+ cmd.Env = os.Environ()
+ f, err := pty.Start(cmd)
+ if err != nil {
+ return nil, fmt.Errorf("failed to start PTY: %w", err)
+ }
+ defer func() { _ = f.Close() }()
+ scanner := bufio.NewScanner(f)
+ var output strings.Builder
+ var anyOutput bool
+ for scanner.Scan() {
+ line := scanner.Text()
+ output.WriteString(line + "\n")
+ ch <- line + "\n"
+ anyOutput = true
+ }
+ if scanErr := scanner.Err(); scanErr != nil && !anyOutput {
+ return output.String(), fmt.Errorf("scanner error: %w", scanErr)
+ }
+ if err := cmd.Wait(); err != nil {
+ return output.String(), fmt.Errorf("command failed: %w", err)
+ }
+ return output.String(), nil
+}
+
+// read_file
+func (d *Del) readFile(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error) {
+ path, ok := args["path"].(string)
+ if !ok {
+ return nil, fmt.Errorf("missing 'path' string argument")
+ }
+ data, err := os.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+ ch <- string(data)
+ return string(data), nil
+}
+
+// write_file
+func (d *Del) writeFile(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error) {
+ path, ok1 := args["path"].(string)
+ content, ok2 := args["content"].(string)
+ if !ok1 || !ok2 {
+ return nil, fmt.Errorf("missing 'path' or 'content' argument")
+ }
+ if err := os.WriteFile(path, []byte(content), 0644); err != nil {
+ return nil, err
+ }
+ msg := fmt.Sprintf("wrote %d bytes to %s", len(content), path)
+ ch <- msg
+ return msg, nil
+}
+
+// list_dir
+func (d *Del) listDir(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error) {
+ dir, _ := args["path"].(string)
+ if dir == "" {
+ dir = "."
+ }
+ entries, err := os.ReadDir(dir)
+ if err != nil {
+ return nil, err
+ }
+ var names []string
+ for _, entry := range entries {
+ names = append(names, entry.Name())
+ }
+ output := strings.Join(names, "\n")
+ ch <- output
+ return output, nil
+}
+
+// analyze_code (counts lines, functions, basic complexity for Go/Python/JS)
+func (d *Del) analyzeCode(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error) {
+ content, ok := args["content"].(string)
+ if !ok {
+ return nil, fmt.Errorf("missing 'content' string argument")
+ }
+ lines := strings.Count(content, "\n") + 1
+ funcs := regexp.MustCompile(`(?m)^[ \t]*(func |def |function )`).FindAllStringIndex(content, -1)
+ out := fmt.Sprintf("Lines: %d\nFunctions: %d", lines, len(funcs))
+ ch <- out
+ return out, nil
+}
+
+// extract_functions (Go, Python, JS regexes)
+func (d *Del) extractFunctions(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error) {
+ content, ok := args["content"].(string)
+ if !ok {
+ return nil, fmt.Errorf("missing 'content' string argument")
+ }
+ var re = regexp.MustCompile(`(?m)^[ \t]*(func|def|function)\s+([\w_]+)`) // captures func name
+ matches := re.FindAllStringSubmatch(content, -1)
+ var names []string
+ for _, m := range matches {
+ names = append(names, m[2])
+ }
+ output := strings.Join(names, "\n")
+ ch <- output
+ return output, nil
+}
+
+// find_references (grep)
+func (d *Del) findReferences(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error) {
+ symbol, ok := args["symbol"].(string)
+ if !ok {
+ return nil, fmt.Errorf("missing 'symbol' string argument")
+ }
+ path, _ := args["path"].(string)
+ if path == "" {
+ path = "."
+ }
+ cmd := exec.Command("grep", "-rnw", path, "-e", symbol)
+ out, err := cmd.CombinedOutput()
+ result := string(out)
+ ch <- result
+ return result, err
+}
+
+// search_code (pattern search)
+func (d *Del) searchCode(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error) {
+ pattern, ok := args["pattern"].(string)
+ if !ok {
+ return nil, fmt.Errorf("missing 'pattern' string argument")
+ }
+ path, _ := args["path"].(string)
+ if path == "" {
+ path = "."
+ }
+ cmd := exec.Command("grep", "-rnIH", pattern, path)
+ out, err := cmd.CombinedOutput()
+ result := string(out)
+ ch <- result
+ return result, err
+}
+
+// format_code (auto-formats via gofmt/black/prettier/etc based on extension)
+func (d *Del) formatCode(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error) {
+ path, ok := args["path"].(string)
+ if !ok {
+ return nil, fmt.Errorf("missing 'path' string argument")
+ }
+ ext := ""
+ if dot := strings.LastIndex(path, "."); dot != -1 {
+ ext = path[dot+1:]
+ }
+ var cmd *exec.Cmd
+ switch ext {
+ case "go":
+ cmd = exec.Command("gofmt", "-w", path)
+ case "py":
+ cmd = exec.Command("black", path)
+ case "js", "ts":
+ cmd = exec.Command("prettier", "--write", path)
+ default:
+ return nil, fmt.Errorf("unsupported format for extension: %s", ext)
+ }
+ out, err := cmd.CombinedOutput()
+ result := string(out)
+ ch <- result
+ return result, err
+}
+
+// lint_code
+func (d *Del) lintCode(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error) {
+ path, ok := args["path"].(string)
+ if !ok {
+ return nil, fmt.Errorf("missing 'path' string argument")
+ }
+ ext := ""
+ if dot := strings.LastIndex(path, "."); dot != -1 {
+ ext = path[dot+1:]
+ }
+ var cmd *exec.Cmd
+ switch ext {
+ case "go":
+ cmd = exec.Command("golint", path)
+ case "py":
+ cmd = exec.Command("pylint", path)
+ case "js", "ts":
+ cmd = exec.Command("eslint", path)
+ default:
+ return nil, fmt.Errorf("unsupported linter for extension: %s", ext)
+ }
+ out, err := cmd.CombinedOutput()
+ result := string(out)
+ ch <- result
+ return result, err
+}
+
+// mcp_call (stub for now)
+func (d *Del) stubMCP(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error) {
+ endpoint, _ := args["endpoint"].(string)
+ payload, _ := args["payload"].(string)
+ msg := fmt.Sprintf("[stub] Would POST to MCP '%s' with payload: %s", endpoint, payload)
+ ch <- msg
+ return msg, nil
+}
+
func NewDel(provider AIProvider) *Del {
d := &Del{
aiProvider: provider,
@@ -124,88 +329,58 @@ func NewDel(provider AIProvider) *Del {
d.tools["run_command"] = &Tool{
Name: "run_command",
Description: "Execute a shell command and return the output",
- Handler: func(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error) {
- cmdStr, ok := args["command"].(string)
- if !ok {
- return nil, fmt.Errorf("missing 'command' string argument")
- }
- cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr)
- cmd.Env = os.Environ()
- f, err := pty.Start(cmd)
- if err != nil {
- return nil, fmt.Errorf("failed to start PTY: %w", err)
- }
- defer func() { _ = f.Close() }()
- scanner := bufio.NewScanner(f)
- var output strings.Builder
- var anyOutput bool
- for scanner.Scan() {
- line := scanner.Text()
- output.WriteString(line + "\n")
- ch <- line + "\n"
- anyOutput = true
- }
- if scanErr := scanner.Err(); scanErr != nil && !anyOutput {
- return output.String(), fmt.Errorf("scanner error: %w", scanErr)
- }
- if err := cmd.Wait(); err != nil {
- return output.String(), fmt.Errorf("command failed: %w", err)
- }
- return output.String(), nil
- },
+ Handler: d.runCommand,
}
d.tools["read_file"] = &Tool{
Name: "read_file",
Description: "Reads the contents of a file",
- Handler: func(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error) {
- path, ok := args["path"].(string)
- if !ok {
- return nil, fmt.Errorf("missing 'path' string argument")
- }
- data, err := os.ReadFile(path)
- if err != nil {
- return nil, err
- }
- return string(data), nil
- },
+ Handler: d.readFile,
}
d.tools["write_file"] = &Tool{
Name: "write_file",
Description: "Writes content to a file",
- Handler: func(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error) {
- path, ok1 := args["path"].(string)
- content, ok2 := args["content"].(string)
- if !ok1 || !ok2 {
- return nil, fmt.Errorf("missing 'path' or 'content' argument")
- }
- if err := os.WriteFile(path, []byte(content), 0644); err != nil {
- return nil, err
- }
- return fmt.Sprintf("wrote %d bytes to %s", len(content), path), nil
- },
+ Handler: d.writeFile,
+ }
+ d.tools["list_dir"] = &Tool{
+ Name: "list_dir",
+ Description: "Lists files in a directory",
+ Handler: d.listDir,
}
d.tools["analyze_code"] = &Tool{
Name: "analyze_code",
Description: "Analyze code",
- Handler: func(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error) {
- content, ok := args["content"].(string)
- if !ok {
- return nil, fmt.Errorf("missing 'content' string argument")
- }
- lines := strings.Count(content, "\n")
- return fmt.Sprintf("Code has %d lines. Potential for refactoring if > 200 lines.", lines), nil
- },
+ Handler: d.analyzeCode,
+ }
+ d.tools["extract_functions"] = &Tool{
+ Name: "extract_functions",
+ Description: "Extracts all function names and signatures from a source file",
+ Handler: d.extractFunctions,
+ }
+ d.tools["find_references"] = &Tool{
+ Name: "find_references",
+ Description: "Finds references to a symbol (variable, function, etc) in a directory or project",
+ Handler: d.findReferences,
+ }
+ d.tools["search_code"] = &Tool{
+ Name: "search_code",
+ Description: "Searches for a pattern (string or regex) in the codebase",
+ Handler: d.searchCode,
+ }
+ d.tools["format_code"] = &Tool{
+ Name: "format_code",
+ Description: "Auto-formats source code using language-specific formatters (gofmt, prettier, black, etc)",
+ Handler: d.formatCode,
+ }
+ d.tools["lint_code"] = &Tool{
+ Name: "lint_code",
+ Description: "Runs a linter on the source code and returns warnings/errors",
+ Handler: d.lintCode,
}
d.tools["mcp_call"] = &Tool{
Name: "mcp_call",
Description: "Stub for MCP integration",
- Handler: func(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error) {
- endpoint, _ := args["endpoint"].(string)
- payload, _ := args["payload"].(string)
- return fmt.Sprintf("[stub] Would send POST to MCP '%s' with payload: %s", endpoint, payload), nil
- },
+ Handler: d.stubMCP,
}
-
return d
}