Commit 75e6850
Changed files (7)
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=