Commit 75e6850

mo khan <mo@mokhan.ca>
2025-06-22 04:47:08
fix the things
1 parent e682b96
cmd/del/main.go
@@ -1,27 +1,288 @@
 package main
 
 import (
+	"bufio"
 	"context"
-	"log"
+	"encoding/json"
+	"fmt"
+	"os"
+	"os/exec"
+	"regexp"
+	"strings"
 
-	"github.com/spf13/cobra"
-	"github.com/xlgmokha/deltron/internal/del"
+	"github.com/creack/pty"
+	"github.com/ollama/ollama/api"
 )
 
-func main() {
-	var model string
-	rootCmd := &cobra.Command{
-		Use:   "del",
-		Short: "Del the Funky Robosapien - Claude Code replacement with local AI",
-		Run: func(cmd *cobra.Command, args []string) {
-			ai := del.NewOllamaProvider(model)
-			assistant := del.NewDel(ai)
-			assistant.StartREPL(context.Background())
+const SystemPrompt = `
+You are Del, an autonomous coding assistant. You have the ability to use tools by outputting a TOOL_USE block.
+
+---
+
+# INSTRUCTIONS (STRICT)
+
+1. When the user asks you to do anything related to code, files, shell, or system operations, always reply with exactly ONE TOOL_USE block, using the following format:
+
+TOOL_USE: tool_name {JSON args}
+
+Example:
+TOOL_USE: run_command {"command": "ls -alh"}
+
+- Never return explanations, code fences, Markdown, or JSON unless the tool returns it as a result.
+- Only ever reply with a TOOL_USE block if a tool is needed for the user's request.
+- If the request is general chat or does not map to a tool, answer as a friendly, concise assistant, but do not mention tool usage.
+- Never return an object with keys TOOL_USE or args as root JSON unless inside a TOOL_USE block.
+- Never apologize or repeat the user's command in natural language.
+
+---
+
+# TOOLS AVAILABLE
+
+- run_command: Runs a shell command. Args: {"command": string}
+- read_file: Reads the contents of a file. Args: {"path": string}
+- write_file: Writes content to a file. Args: {"path": string, "content": string}
+- analyze_code: Analyzes code. Args: {"content": string}
+- mcp_call: Calls an MCP endpoint (stub). Args: {"endpoint": string, "payload": string}
+
+---
+
+# EXAMPLES
+
+User: list all files in this directory
+TOOL_USE: run_command {"command": "ls"}
+
+User: show me main.go
+TOOL_USE: read_file {"path": "main.go"}
+
+User: analyze this code: ...
+TOOL_USE: analyze_code {"content": "..."}
+
+User: write this code to foo.go
+TOOL_USE: write_file {"path": "foo.go", "content": "..."}
+
+---
+
+Reply ONLY with a TOOL_USE block for any tool action. No Markdown, no code fences, no JSON root objects named TOOL_USE/args, no extra text. Do not explain or apologize.`
+
+type Tool struct {
+	Name        string
+	Description string
+	Handler     func(ctx context.Context, args map[string]interface{}, ch chan string) (interface{}, error)
+}
+
+type Del struct {
+	aiProvider  AIProvider
+	tools       map[string]*Tool
+	chatHistory []api.Message
+}
+
+type AIProvider interface {
+	Chat(ctx context.Context, history []api.Message) (string, error)
+	Name() string
+}
+
+type OllamaProvider struct {
+	model  string
+	client *api.Client
+}
+
+func NewOllamaProvider(model string) *OllamaProvider {
+	client, _ := api.ClientFromEnvironment()
+	return &OllamaProvider{model: model, client: client}
+}
+
+func (o *OllamaProvider) Chat(ctx context.Context, history []api.Message) (string, error) {
+	var full string
+	err := o.client.Chat(ctx, &api.ChatRequest{
+		Model:    o.model,
+		Messages: history,
+	}, func(resp api.ChatResponse) error {
+		full += resp.Message.Content
+		return nil
+	})
+	if err != nil {
+		return "", fmt.Errorf("ollama API error: %w", err)
+	}
+	return full, nil
+}
+
+func (o *OllamaProvider) Name() string {
+	return fmt.Sprintf("Ollama (%s)", o.model)
+}
+
+func NewDel(provider AIProvider) *Del {
+	d := &Del{
+		aiProvider: provider,
+		tools:      make(map[string]*Tool),
+		chatHistory: []api.Message{
+			{
+				Role:    "system",
+				Content: SystemPrompt,
+			},
+		},
+	}
+	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
+		},
+	}
+	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
+		},
+	}
+	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
+		},
+	}
+	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
+		},
+	}
+	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
 		},
 	}
 
-	rootCmd.Flags().StringVarP(&model, "model", "m", "deepseek-coder-v2:16b", "Ollama model to use")
-	if err := rootCmd.Execute(); err != nil {
-		log.Fatal(err)
+	return d
+}
+
+func (d *Del) handleToolCalls(ctx context.Context, response string) string {
+	re := regexp.MustCompile(`(?s)TOOL_USE:\s*(\w+)\s*(\{.*?\})`)
+	matches := re.FindAllStringSubmatch(response, -1)
+	if len(matches) == 0 {
+		return response
+	}
+	var finalOutput strings.Builder
+	for _, match := range matches {
+		toolName := match[1]
+		argsJSON := match[2]
+		tool, ok := d.tools[toolName]
+		if !ok {
+			finalOutput.WriteString(fmt.Sprintf("[error] unknown tool: %s\n", toolName))
+			continue
+		}
+		var args map[string]interface{}
+		if err := json.Unmarshal([]byte(argsJSON), &args); err != nil {
+			finalOutput.WriteString(fmt.Sprintf("[error] bad args: %v\n", err))
+			continue
+		}
+		ch := make(chan string, 10)
+		go func() {
+			for line := range ch {
+				fmt.Print(line)
+			}
+		}()
+		result, err := tool.Handler(ctx, args, ch)
+		close(ch)
+		if err != nil {
+			finalOutput.WriteString(fmt.Sprintf("[tool error] %v\n", err))
+			continue
+		}
+		switch val := result.(type) {
+		case string:
+			finalOutput.WriteString(val)
+			if !strings.HasSuffix(val, "\n") {
+				finalOutput.WriteString("\n")
+			}
+		default:
+			outBytes, _ := json.MarshalIndent(val, "", "  ")
+			finalOutput.WriteString(string(outBytes) + "\n")
+		}
 	}
+	return finalOutput.String()
+}
+
+func (d *Del) StartREPL(ctx context.Context) {
+	fmt.Printf("๐ŸŽค Del is ready with %s\n", d.aiProvider.Name())
+	scanner := bufio.NewScanner(os.Stdin)
+	for {
+		fmt.Print("๐ŸŽค You: ")
+		if !scanner.Scan() {
+			break
+		}
+		input := scanner.Text()
+		if strings.HasPrefix(input, "/quit") {
+			fmt.Println("๐Ÿ‘‹ Goodbye!")
+			return
+		}
+		d.chatHistory = append(d.chatHistory, api.Message{Role: "user", Content: input})
+		response, err := d.aiProvider.Chat(ctx, d.chatHistory)
+		if err != nil {
+			fmt.Println("[error]", err)
+			continue
+		}
+		d.chatHistory = append(d.chatHistory, api.Message{Role: "assistant", Content: response})
+		output := d.handleToolCalls(ctx, response)
+		fmt.Print(output)
+	}
+}
+
+func main() {
+	ctx := context.Background()
+	provider := NewOllamaProvider("deepseek-coder-v2:16b")
+	assistant := NewDel(provider)
+	assistant.StartREPL(ctx)
 }
internal/del/assistant.go
@@ -1,235 +0,0 @@
-package del
-
-import (
-	"bufio"
-	"context"
-	"encoding/json"
-	"fmt"
-	"os"
-	"os/exec"
-	"regexp"
-	"strings"
-
-	"github.com/mattn/go-colorable"
-	"github.com/ollama/ollama/api"
-)
-
-// Tool represents a tool Del can run
-type Tool struct {
-	Name        string
-	Description string
-	Handler     func(args map[string]interface{}) (interface{}, error)
-}
-
-// Del represents the assistant instance
-type Del struct {
-	aiProvider  AIProvider
-	tools       map[string]*Tool
-	mcpServers  map[string]string
-	chatHistory []api.Message // for stateful interaction
-}
-
-// NewDel creates a new assistant
-func NewDel(provider AIProvider) *Del {
-	tools := map[string]*Tool{
-		"run_command": {
-			Name:        "run_command",
-			Description: "Execute a shell command and return the output",
-			Handler: func(args map[string]interface{}) (interface{}, error) {
-				cmdStr, ok := args["command"].(string)
-				if !ok {
-					return nil, fmt.Errorf("missing 'command' string argument")
-				}
-				cmd := exec.Command("bash", "-c", cmdStr)
-				cmd.Env = os.Environ()
-				cmd.Stderr = os.Stderr
-				stdout, err := cmd.StdoutPipe()
-				if err != nil {
-					return nil, err
-				}
-				if err := cmd.Start(); err != nil {
-					return nil, err
-				}
-				var output strings.Builder
-				scanner := bufio.NewScanner(stdout)
-				colorOut := colorable.NewColorableStdout()
-				for scanner.Scan() {
-					line := scanner.Text()
-					fmt.Fprintln(colorOut, line)
-					output.WriteString(line + "\n")
-				}
-				if err := scanner.Err(); err != nil {
-					return nil, err
-				}
-				if err := cmd.Wait(); err != nil {
-					return nil, err
-				}
-				return output.String(), nil
-			},
-		},
-		"read_file": {
-			Name:        "read_file",
-			Description: "Reads the contents of a file",
-			Handler: func(args map[string]interface{}) (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
-			},
-		},
-		"write_file": {
-			Name:        "write_file",
-			Description: "Writes content to a file",
-			Handler: func(args map[string]interface{}) (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
-			},
-		},
-		"list_dir": {
-			Name:        "list_dir",
-			Description: "Lists files in a directory",
-			Handler: func(args map[string]interface{}) (interface{}, error) {
-				dir, ok := args["path"].(string)
-				if !ok {
-					dir = "."
-				}
-				entries, err := os.ReadDir(dir)
-				if err != nil {
-					return nil, err
-				}
-				var names []string
-				for _, entry := range entries {
-					names = append(names, entry.Name())
-				}
-				return names, nil
-			},
-		},
-	}
-
-	return &Del{
-		aiProvider: provider,
-		tools:      tools,
-		mcpServers: make(map[string]string),
-		chatHistory: []api.Message{
-			{
-				Role: "system",
-				Content: `You are Del, an assistant that can run system tools using TOOL_USE blocks.
-
-To use a tool, respond exactly like this:
-TOOL_USE: tool_name {json_args}
-
-Supported tools:
-
-1. run_command
-  - Runs a shell command.
-  - Args:
-    - "command": string (e.g., "ls -alh")
-
-2. read_file
-  - Reads the contents of a file.
-  - Args:
-    - "path": string
-
-3. write_file
-  - Writes content to a file.
-  - Args:
-    - "path": string
-    - "content": string
-
-4. list_dir
-  - Lists files in a directory.
-  - Args:
-    - "path": string (optional; default is ".")
-
-Only emit a TOOL_USE block if needed. Do not explain it. Only one TOOL_USE per response.`,
-			},
-		},
-	}
-}
-
-// StartREPL launches an interactive session
-func (d *Del) StartREPL(ctx context.Context) {
-	fmt.Printf("๐ŸŽค Del is ready with %s\n", d.aiProvider.Name())
-
-	scanner := bufio.NewScanner(os.Stdin)
-	for {
-		fmt.Print("๐ŸŽค You: ")
-		if !scanner.Scan() {
-			break
-		}
-		input := scanner.Text()
-		if strings.HasPrefix(input, "/quit") {
-			fmt.Println("๐Ÿ‘‹ Goodbye!")
-			return
-		}
-
-		d.chatHistory = append(d.chatHistory, api.Message{Role: "user", Content: input})
-
-		response, err := d.aiProvider.Chat(ctx, d.chatHistory)
-		if err != nil {
-			fmt.Println("[error]", err)
-			continue
-		}
-
-		d.chatHistory = append(d.chatHistory, api.Message{Role: "assistant", Content: response})
-		output := d.handleToolCalls(ctx, response)
-		fmt.Print(output)
-	}
-}
-
-// handleToolCalls parses and runs embedded tool requests from the model output
-func (d *Del) handleToolCalls(ctx context.Context, response string) string {
-	re := regexp.MustCompile(`(?s)TOOL_USE:\s*(\w+)\s*(\{.*?\})`)
-	matches := re.FindAllStringSubmatch(response, -1)
-	if len(matches) == 0 {
-		return response
-	}
-
-	var finalOutput strings.Builder
-	for _, match := range matches {
-		toolName := match[1]
-		argsJSON := match[2]
-
-		tool, ok := d.tools[toolName]
-		if !ok {
-			finalOutput.WriteString(fmt.Sprintf("[error] unknown tool: %s\n", toolName))
-			continue
-		}
-
-		var args map[string]interface{}
-		if err := json.Unmarshal([]byte(argsJSON), &args); err != nil {
-			finalOutput.WriteString(fmt.Sprintf("[error] bad args: %v\n", err))
-			continue
-		}
-
-		result, err := tool.Handler(args)
-		if err != nil {
-			finalOutput.WriteString(fmt.Sprintf("[tool error] %v\n", err))
-			continue
-		}
-
-		switch val := result.(type) {
-		case string:
-			finalOutput.WriteString(val)
-			if !strings.HasSuffix(val, "\n") {
-				finalOutput.WriteString("\n")
-			}
-		default:
-			outBytes, _ := json.MarshalIndent(val, "", "  ")
-			finalOutput.WriteString(string(outBytes) + "\n")
-		}
-	}
-	return finalOutput.String()
-}
internal/del/prompt.go
@@ -1,44 +0,0 @@
-package del
-
-import (
-	"fmt"
-	"os"
-	"os/exec"
-	"strings"
-)
-
-func (d *Del) buildPrompt(input string) string {
-	tools := []string{}
-	for name, tool := range d.tools {
-		tools = append(tools, fmt.Sprintf("- %s: %s", name, tool.Description))
-	}
-
-	return fmt.Sprintf(`You are Del the Funky Robosapien ๐ŸŽค๐Ÿค–
-
-Available tools:
-%s
-
-Working dir: %s
-Git status:
-%s
-
-User input:
-%s
-
-If you need a tool, respond like:
-TOOL_USE: read_file {"path": "main.go"}
-`, strings.Join(tools, " "), getCWD(), getGitStatus(), input)
-}
-
-func getCWD() string {
-	cwd, _ := os.Getwd()
-	return cwd
-}
-
-func getGitStatus() string {
-	out, err := exec.Command("git", "status", "--short").Output()
-	if err != nil {
-		return "Not a git repo"
-	}
-	return string(out)
-}
internal/del/provider.go
@@ -1,64 +0,0 @@
-package del
-
-import (
-	"context"
-	"fmt"
-
-	"github.com/ollama/ollama/api"
-)
-
-type AIProvider interface {
-	Generate(ctx context.Context, prompt string) (string, error)
-	Name() string
-	Chat(ctx context.Context, history []api.Message) (string, error)
-}
-
-type OllamaProvider struct {
-	model  string
-	client *api.Client
-}
-
-func NewOllamaProvider(model string) *OllamaProvider {
-	client, _ := api.ClientFromEnvironment()
-	return &OllamaProvider{
-		model:  model,
-		client: client,
-	}
-}
-
-func (o *OllamaProvider) Generate(ctx context.Context, prompt string) (string, error) {
-	var full string
-	err := o.client.Generate(ctx, &api.GenerateRequest{
-		Model:  o.model,
-		Prompt: prompt,
-	}, api.GenerateResponseFunc(func(resp api.GenerateResponse) error {
-		full += resp.Response
-		return nil
-	}))
-
-	if err != nil {
-		return "", fmt.Errorf("ollama API error: %w", err)
-	}
-	return full, nil
-}
-
-func (o *OllamaProvider) Name() string {
-	return fmt.Sprintf("Ollama (%s)", o.model)
-}
-
-func (o *OllamaProvider) Chat(ctx context.Context, history []api.Message) (string, error) {
-	var response string
-	stream := false
-	err := o.client.Chat(ctx, &api.ChatRequest{
-		Model:    o.model,
-		Messages: history,
-		Stream:   &stream,
-	}, api.ChatResponseFunc(func(resp api.ChatResponse) error {
-		response += resp.Message.Content
-		return nil
-	}))
-	if err != nil {
-		return "", err
-	}
-	return response, nil
-}
internal/del/tools.go
@@ -1,60 +0,0 @@
-package del
-
-import (
-	"fmt"
-	"os"
-	"os/exec"
-)
-
-func (d *Del) registerTools() {
-	d.tools["read_file"] = &Tool{"read_file", "Read a file from disk", d.readFile}
-	d.tools["write_file"] = &Tool{"write_file", "Write content to a file", d.writeFile}
-	d.tools["run_command"] = &Tool{"run_command", "Execute a shell command", d.runCommand}
-	d.tools["analyze_code"] = &Tool{"analyze_code", "Analyze code quality", d.analyzeCode}
-	d.tools["mcp_git_list"] = &Tool{"mcp_git_list", "MCP: List repo files", d.stubMCP}
-	d.tools["mcp_git_read"] = &Tool{"mcp_git_read", "MCP: Read repo file", d.stubMCP}
-}
-
-func (d *Del) readFile(args map[string]interface{}) (interface{}, error) {
-	path, ok := args["path"].(string)
-	if !ok {
-		return nil, fmt.Errorf("missing 'path'")
-	}
-	data, err := os.ReadFile(path)
-	if err != nil {
-		return nil, err
-	}
-	return string(data), nil
-}
-
-func (d *Del) writeFile(args map[string]interface{}) (interface{}, error) {
-	path, ok := args["path"].(string)
-	content, ok2 := args["content"].(string)
-	if !ok || !ok2 {
-		return nil, fmt.Errorf("missing 'path' or 'content'")
-	}
-	return "written", os.WriteFile(path, []byte(content), 0644)
-}
-
-func (d *Del) runCommand(args map[string]interface{}) (interface{}, error) {
-	command, ok := args["command"].(string)
-	if !ok {
-		return nil, fmt.Errorf("missing 'command'")
-	}
-	output, err := exec.Command("sh", "-c", command).CombinedOutput()
-	return string(output), err
-}
-
-func (d *Del) analyzeCode(args map[string]interface{}) (interface{}, error) {
-	return map[string]interface{}{
-		"quality": "good",
-		"suggestions": []string{
-			"Consider breaking large functions into smaller ones.",
-			"Add more comments for readability.",
-		},
-	}, nil
-}
-
-func (d *Del) stubMCP(args map[string]interface{}) (interface{}, error) {
-	return map[string]string{"status": "MCP stub - not connected"}, nil
-}
go.mod
@@ -3,15 +3,12 @@ module github.com/xlgmokha/deltron
 go 1.24.0
 
 require (
-	github.com/mattn/go-colorable v0.1.14
+	github.com/creack/pty v1.1.24
 	github.com/ollama/ollama v0.9.2
-	github.com/spf13/cobra v1.9.1
 )
 
 require (
-	github.com/inconshreveable/mousetrap v1.1.0 // indirect
-	github.com/mattn/go-isatty v0.0.20 // indirect
-	github.com/spf13/pflag v1.0.6 // indirect
 	golang.org/x/crypto v0.36.0 // indirect
-	golang.org/x/sys v0.31.0 // indirect
+	golang.org/x/sys v0.32.0 // indirect
+	golang.org/x/term v0.31.0 // indirect
 )
go.sum
@@ -1,32 +1,20 @@
-github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
+github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
+github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
 github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
-github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
-github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
-github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
-github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
-github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/ollama/ollama v0.9.2 h1:NEzeLb0gwz1XRyQUCPb30zqDyO/bze+Hiq9NUuYEUy4=
 github.com/ollama/ollama v0.9.2/go.mod h1:+5wt6UPgPmzYhnpLJ/rObxJJyEXURZ/SKKCMQsff8bA=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
-github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
-github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
-github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
 github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
 golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
-golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
-golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
+golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
+golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=