Commit 98185b2

mo khan <mo@mokhan.ca>
2025-06-22 01:16:27
refactor: reorganize files
1 parent a471172
Changed files (3)
cmd/del/main.go
@@ -1,529 +1,40 @@
 package main
 
 import (
-	"bufio"
-	"context"
-	"encoding/json"
-	"fmt"
 	"log"
-	"os"
-	"os/exec"
-	"strings"
 
 	"github.com/spf13/cobra"
-)
-
-// AI Provider interface - supports multiple AI backends
-type AIProvider interface {
-	Generate(ctx context.Context, prompt string) (string, error)
-	Name() string
-}
-
-// Tool represents an available capability
-type Tool struct {
-	Name        string
-	Description string
-	Handler     func(args map[string]interface{}) (interface{}, error)
-}
-
-// Del is our main AI assistant - Del the Funky Robosapien!
-type Del struct {
-	aiProvider AIProvider
-	tools      map[string]*Tool
-	mcpServers map[string]string // name -> command path
-}
-
-// Claude API provider
-type ClaudeProvider struct {
-	apiKey string
-}
-
-func (c *ClaudeProvider) Generate(ctx context.Context, prompt string) (string, error) {
-	// Use Claude CLI if available, fallback to API
-	cmd := exec.CommandContext(ctx, "claude", "--print", prompt)
-	output, err := cmd.Output()
-	if err != nil {
-		return "", fmt.Errorf("claude error: %w", err)
-	}
-	return string(output), nil
-}
-
-func (c *ClaudeProvider) Name() string {
-	return "Claude"
-}
-
-// Ollama provider
-type OllamaProvider struct {
-	model string
-}
-
-func (o *OllamaProvider) Generate(ctx context.Context, prompt string) (string, error) {
-	cmd := exec.CommandContext(ctx, "ollama", "run", o.model, prompt)
-	output, err := cmd.Output()
-	if err != nil {
-		return "", fmt.Errorf("ollama error: %w", err)
-	}
-	return string(output), nil
-}
-
-func (o *OllamaProvider) Name() string {
-	return fmt.Sprintf("Ollama (%s)", o.model)
-}
-
-// Initialize Del with tools and MCP servers
-func NewDel(provider AIProvider) *Del {
-	d := &Del{
-		aiProvider: provider,
-		tools:      make(map[string]*Tool),
-		mcpServers: make(map[string]string),
-	}
-
-	// Register built-in tools
-	d.registerBuiltinTools()
-	
-	// Auto-discover MCP servers
-	d.discoverMCPServers()
-	
-	return d
-}
-
-func (d *Del) registerBuiltinTools() {
-	// Security scanner tool
-	d.tools["security_scan"] = &Tool{
-		Name:        "security_scan",
-		Description: "Scan code for security vulnerabilities",
-		Handler:     d.handleSecurityScan,
-	}
-	
-	// Code analysis tool
-	d.tools["analyze_code"] = &Tool{
-		Name:        "analyze_code", 
-		Description: "Analyze code quality and suggest improvements",
-		Handler:     d.handleCodeAnalysis,
-	}
-	
-	// File operations
-	d.tools["read_file"] = &Tool{
-		Name:        "read_file",
-		Description: "Read contents of a file",
-		Handler:     d.handleReadFile,
-	}
-	
-	d.tools["write_file"] = &Tool{
-		Name:        "write_file",
-		Description: "Write content to a file",
-		Handler:     d.handleWriteFile,
-	}
-	
-	// Command execution
-	d.tools["run_command"] = &Tool{
-		Name:        "run_command",
-		Description: "Execute shell commands",
-		Handler:     d.handleRunCommand,
-	}
-	
-	// MCP integration tools (when backend Claude completes servers)
-	d.tools["mcp_git_list"] = &Tool{
-		Name:        "mcp_git_list",
-		Description: "List files in git repository via MCP",
-		Handler:     d.handleMCPGitList,
-	}
-	
-	d.tools["mcp_git_read"] = &Tool{
-		Name:        "mcp_git_read", 
-		Description: "Read file from git repository via MCP",
-		Handler:     d.handleMCPGitRead,
-	}
-}
 
-func (d *Del) discoverMCPServers() {
-	// Look for MCP servers in the other Claude's repo
-	mcpPath := "/home/mokhax/src/github.com/xlgmokha/mcp"
-	servers := map[string]string{
-		"git":        mcpPath + "/cmd/git/main.go",
-		"filesystem": mcpPath + "/cmd/fs/main.go", 
-		"bash":       mcpPath + "/cmd/bash/main.go",
-	}
-	
-	for name, path := range servers {
-		if _, err := os.Stat(path); err == nil {
-			d.mcpServers[name] = path
-		}
-	}
-}
+	"github.com/xlgmokha/deltron/internal/del"
+)
 
-func (d *Del) handleSecurityScan(args map[string]interface{}) (interface{}, error) {
-	path, ok := args["path"].(string)
-	if !ok {
-		return nil, fmt.Errorf("path required")
-	}
-	
-	// Run security tools like gosec, semgrep, etc.
-	results := map[string]interface{}{
-		"vulnerabilities": []string{},
-		"warnings":       []string{},
-		"suggestions":    []string{},
-	}
-	
-	// Check for common security issues
-	if strings.Contains(path, ".go") {
-		// Run gosec if available
-		cmd := exec.Command("gosec", "-fmt", "json", path)
-		if output, err := cmd.Output(); err == nil {
-			results["gosec_output"] = string(output)
-		}
-	}
-	
-	return results, nil
-}
+func main() {
+	var provider, model string
+
+	rootCmd := &cobra.Command{
+		Use:   "del",
+		Short: "Del the Funky Robosapien - Your AI-powered coding superhero",
+		Run: func(cmd *cobra.Command, args []string) {
+			var ai del.AIProvider
+
+			switch provider {
+			case "claude":
+				ai = del.NewClaudeProvider("")
+			case "ollama":
+				ai = del.NewOllamaProvider(model)
+			default:
+				ai = del.AutoProvider("", model)
+			}
 
-func (d *Del) handleCodeAnalysis(args map[string]interface{}) (interface{}, error) {
-	path, ok := args["path"].(string)
-	if !ok {
-		return nil, fmt.Errorf("path required")
-	}
-	
-	analysis := map[string]interface{}{
-		"path":          path,
-		"complexity":    "medium",
-		"maintainability": "good",
-		"suggestions": []string{
-			"Consider adding more comments",
-			"Extract complex functions",
+			assistant := del.NewDel(ai)
+			assistant.StartREPL()
 		},
 	}
-	
-	return analysis, nil
-}
 
-func (d *Del) handleReadFile(args map[string]interface{}) (interface{}, error) {
-	path, ok := args["path"].(string)
-	if !ok {
-		return nil, fmt.Errorf("path required")
-	}
-	
-	content, err := os.ReadFile(path)
-	if err != nil {
-		return nil, err
-	}
-	
-	return map[string]interface{}{
-		"path":    path,
-		"content": string(content),
-		"size":    len(content),
-	}, nil
-}
+	rootCmd.Flags().StringVarP(&provider, "provider", "p", "auto", "AI provider (claude, ollama, auto)")
+	rootCmd.Flags().StringVarP(&model, "model", "m", "deepseek-coder-v2:16b", "Model to use (for Ollama)")
 
-func (d *Del) handleWriteFile(args map[string]interface{}) (interface{}, error) {
-	path, ok := args["path"].(string)
-	if !ok {
-		return nil, fmt.Errorf("path required")
-	}
-	
-	content, ok := args["content"].(string)
-	if !ok {
-		return nil, fmt.Errorf("content required")
-	}
-	
-	// Ask for confirmation
-	fmt.Printf("๐ŸŽค Del asks: Write to %s? [y/N]: ", path)
-	reader := bufio.NewReader(os.Stdin)
-	response, _ := reader.ReadString('\n')
-	
-	if strings.ToLower(strings.TrimSpace(response)) != "y" {
-		return map[string]interface{}{"status": "cancelled"}, nil
-	}
-	
-	err := os.WriteFile(path, []byte(content), 0644)
-	if err != nil {
-		return nil, err
-	}
-	
-	return map[string]interface{}{
-		"status": "written",
-		"path":   path,
-		"size":   len(content),
-	}, nil
-}
-
-func (d *Del) handleRunCommand(args map[string]interface{}) (interface{}, error) {
-	command, ok := args["command"].(string)
-	if !ok {
-		return nil, fmt.Errorf("command required")
-	}
-	
-	// Ask for confirmation
-	fmt.Printf("๐ŸŽค Del asks: Execute '%s'? [y/N]: ", command)
-	reader := bufio.NewReader(os.Stdin)
-	response, _ := reader.ReadString('\n')
-	
-	if strings.ToLower(strings.TrimSpace(response)) != "y" {
-		return map[string]interface{}{"status": "cancelled"}, nil
-	}
-	
-	cmd := exec.Command("sh", "-c", command)
-	output, err := cmd.CombinedOutput()
-	
-	result := map[string]interface{}{
-		"command": command,
-		"output":  string(output),
-		"success": err == nil,
-	}
-	
-	if err != nil {
-		result["error"] = err.Error()
-	}
-	
-	return result, nil
-}
-
-// MCP Git List handler - will connect to MCP servers once available
-func (d *Del) handleMCPGitList(args map[string]interface{}) (interface{}, error) {
-	// Check if git MCP server is available
-	if _, exists := d.mcpServers["git"]; !exists {
-		return nil, fmt.Errorf("git MCP server not available yet")
-	}
-	
-	// TODO: Implement actual MCP client call once server is ready
-	return map[string]interface{}{
-		"status": "pending",
-		"message": "Git MCP server implementation in progress",
-	}, nil
-}
-
-// MCP Git Read handler - will connect to MCP servers once available
-func (d *Del) handleMCPGitRead(args map[string]interface{}) (interface{}, error) {
-	// Check if git MCP server is available
-	if _, exists := d.mcpServers["git"]; !exists {
-		return nil, fmt.Errorf("git MCP server not available yet")
-	}
-	
-	path, ok := args["path"].(string)
-	if !ok {
-		return nil, fmt.Errorf("path required")
-	}
-	
-	// TODO: Implement actual MCP client call once server is ready
-	return map[string]interface{}{
-		"status": "pending", 
-		"path": path,
-		"message": "Git MCP server implementation in progress",
-	}, nil
-}
-
-// Enhanced prompt with available tools
-func (d *Del) buildPrompt(userInput string) string {
-	toolsList := make([]string, 0, len(d.tools))
-	for name, tool := range d.tools {
-		toolsList = append(toolsList, fmt.Sprintf("- %s: %s", name, tool.Description))
-	}
-	
-	mcpList := make([]string, 0, len(d.mcpServers))
-	for name := range d.mcpServers {
-		mcpList = append(mcpList, name)
-	}
-	
-	return fmt.Sprintf(`You are Del the Funky Robosapien, an AI-powered coding superhero assistant.
-Channel the energy of Del the Funky Homosapien - you're creative, clever, and always ready to drop some funky code solutions!
-
-AVAILABLE TOOLS:
-%s
-
-AVAILABLE MCP SERVERS:
-%s
-
-CURRENT CONTEXT:
-- Working Directory: %s
-- Git Status: %s
-
-When you want to use a tool, respond with:
-TOOL_USE: tool_name {"arg1": "value1", "arg2": "value2"}
-
-For example:
-TOOL_USE: read_file {"path": "main.go"}
-TOOL_USE: security_scan {"path": "."}
-TOOL_USE: run_command {"command": "go build"}
-
-Keep it funky! ๐ŸŽค๐Ÿค–
-
-User Request: %s`,
-		strings.Join(toolsList, "\n"),
-		strings.Join(mcpList, ", "),
-		getCurrentDir(),
-		getGitStatus(),
-		userInput)
-}
-
-func getCurrentDir() string {
-	dir, _ := os.Getwd()
-	return dir
-}
-
-func getGitStatus() string {
-	cmd := exec.Command("git", "status", "--porcelain")
-	output, err := cmd.Output()
-	if err != nil {
-		return "Not a git repository"
-	}
-	return string(output)
-}
-
-// Process AI response and execute tools
-func (d *Del) processResponse(response string) {
-	lines := strings.Split(response, "\n")
-	
-	for _, line := range lines {
-		if strings.HasPrefix(line, "TOOL_USE:") {
-			d.executeTool(strings.TrimPrefix(line, "TOOL_USE:"))
-		}
-	}
-}
-
-func (d *Del) executeTool(toolCall string) {
-	parts := strings.SplitN(toolCall, " ", 2)
-	if len(parts) != 2 {
-		fmt.Printf("โŒ Invalid tool call: %s\n", toolCall)
-		return
-	}
-	
-	toolName := strings.TrimSpace(parts[0])
-	argsJSON := strings.TrimSpace(parts[1])
-	
-	tool, exists := d.tools[toolName]
-	if !exists {
-		fmt.Printf("โŒ Unknown tool: %s\n", toolName)
-		return
-	}
-	
-	var args map[string]interface{}
-	if err := json.Unmarshal([]byte(argsJSON), &args); err != nil {
-		fmt.Printf("โŒ Invalid tool args: %s\n", err)
-		return
-	}
-	
-	fmt.Printf("๐ŸŽค Del is dropping some tool magic: %s\n", toolName)
-	result, err := tool.Handler(args)
-	if err != nil {
-		fmt.Printf("โŒ Tool error: %s\n", err)
-		return
-	}
-	
-	fmt.Printf("โœ… Funky result: %+v\n", result)
-}
-
-func (d *Del) showHelp() {
-	fmt.Println("๐ŸŽค๐Ÿค– Del the Funky Robosapien - Help Menu:")
-	fmt.Println("  ๐Ÿ›ก๏ธ  Security Commands:")
-	fmt.Println("    'scan for vulnerabilities'")
-	fmt.Println("    'check this code for security issues'")
-	fmt.Println("  ๐Ÿš€ Development Commands:")
-	fmt.Println("    'read the main.go file'")
-	fmt.Println("    'build this project'")
-	fmt.Println("    'run the tests'")
-	fmt.Println("    'analyze code quality'")
-	fmt.Println("  ๐Ÿ”ง Available Tools:")
-	for name, tool := range d.tools {
-		fmt.Printf("    - %s: %s\n", name, tool.Description)
-	}
-	fmt.Println("  ๐ŸŽต Remember: Keep it funky!")
-	fmt.Println()
-}
-
-// Interactive REPL
-func (d *Del) startREPL() {
-	fmt.Printf("๐ŸŽค๐Ÿค– Del the Funky Robosapien is in the house! (Provider: %s)\n", d.aiProvider.Name())
-	
-	fmt.Printf("๐Ÿ”ง Available tools: %s\n", strings.Join(func() []string {
-		var names []string
-		for name := range d.tools {
-			names = append(names, name)
-		}
-		return names
-	}(), ", "))
-	fmt.Println("๐Ÿ’ฌ Type 'quit' to exit, 'help' for assistance")
-	fmt.Println("๐ŸŽต Let's make some funky code together!")
-	fmt.Println()
-	
-	scanner := bufio.NewScanner(os.Stdin)
-	
-	for {
-		fmt.Print("๐ŸŽค You: ")
-		if !scanner.Scan() {
-			break
-		}
-		
-		input := strings.TrimSpace(scanner.Text())
-		if input == "" {
-			continue
-		}
-		
-		if input == "quit" || input == "exit" {
-			fmt.Println("๐ŸŽค Del says: Catch you on the flip side! Stay funky! โœŒ๏ธ")
-			break
-		}
-		
-		if input == "help" {
-			d.showHelp()
-			continue
-		}
-		
-		// Generate AI response
-		prompt := d.buildPrompt(input)
-		response, err := d.aiProvider.Generate(context.Background(), prompt)
-		if err != nil {
-			fmt.Printf("โŒ AI Error: %s\n", err)
-			continue
-		}
-		
-		fmt.Printf("๐Ÿค– Del: %s\n", response)
-		
-		// Process any tool calls
-		d.processResponse(response)
-		fmt.Println()
-	}
-}
-
-var rootCmd = &cobra.Command{
-	Use:   "del",
-	Short: "Del the Funky Robosapien - Your AI-powered coding superhero",
-	Long: `๐ŸŽค๐Ÿค– Del the Funky Robosapien
-	
-Your AI-powered coding superhero with the soul of a funky homosapien!
-Equipped with security scanning, code analysis, file operations, and more.
-
-Channel the creative energy of Del the Funky Homosapien while coding!`,
-	Run: func(cmd *cobra.Command, args []string) {
-		provider := cmd.Flag("provider").Value.String()
-		model := cmd.Flag("model").Value.String()
-		
-		var aiProvider AIProvider
-		
-		switch provider {
-		case "claude":
-			aiProvider = &ClaudeProvider{}
-		case "ollama":
-			aiProvider = &OllamaProvider{model: model}
-		default:
-			// Try Claude first, fallback to Ollama
-			if exec.Command("claude", "--version").Run() == nil {
-				aiProvider = &ClaudeProvider{}
-			} else {
-				aiProvider = &OllamaProvider{model: "deepseek-coder-v2:16b"}
-			}
-		}
-		
-		del := NewDel(aiProvider)
-		del.startREPL()
-	},
-}
-
-func init() {
-	rootCmd.Flags().StringP("provider", "p", "auto", "AI provider (claude, ollama, auto)")
-	rootCmd.Flags().StringP("model", "m", "deepseek-coder-v2:16b", "Model to use (for Ollama)")
-}
-
-func main() {
 	if err := rootCmd.Execute(); err != nil {
 		log.Fatal(err)
 	}
-}
\ No newline at end of file
+}
internal/del/assistant.go
@@ -0,0 +1,34 @@
+package del
+
+import (
+	"fmt"
+)
+
+// Tool represents a tool Del can run (stub for now)
+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
+}
+
+// NewDel creates a new assistant
+func NewDel(provider AIProvider) *Del {
+	return &Del{
+		aiProvider: provider,
+		tools:      make(map[string]*Tool),
+		mcpServers: make(map[string]string),
+	}
+}
+
+// StartREPL is a stub for now
+func (d *Del) StartREPL() {
+	fmt.Printf("๐ŸŽค Del (%s) is ready!\n", d.aiProvider.Name())
+	// Stub โ€” eventually add interactive loop here
+}
internal/del/provider.go
@@ -0,0 +1,65 @@
+package del
+
+import (
+	"context"
+	"fmt"
+	"os/exec"
+)
+
+// AIProvider abstracts Claude/Ollama/etc
+type AIProvider interface {
+	Generate(ctx context.Context, prompt string) (string, error)
+	Name() string
+}
+
+// ClaudeProvider uses the Claude CLI
+type ClaudeProvider struct {
+	apiKey string
+}
+
+func NewClaudeProvider(apiKey string) AIProvider {
+	return &ClaudeProvider{apiKey: apiKey}
+}
+
+func (c *ClaudeProvider) Generate(ctx context.Context, prompt string) (string, error) {
+	cmd := exec.CommandContext(ctx, "claude", "--print", prompt)
+	output, err := cmd.Output()
+	if err != nil {
+		return "", fmt.Errorf("claude error: %w", err)
+	}
+	return string(output), nil
+}
+
+func (c *ClaudeProvider) Name() string {
+	return "Claude"
+}
+
+// OllamaProvider uses the local Ollama CLI
+type OllamaProvider struct {
+	model string
+}
+
+func NewOllamaProvider(model string) AIProvider {
+	return &OllamaProvider{model: model}
+}
+
+func (o *OllamaProvider) Generate(ctx context.Context, prompt string) (string, error) {
+	cmd := exec.CommandContext(ctx, "ollama", "run", o.model, prompt)
+	output, err := cmd.Output()
+	if err != nil {
+		return "", fmt.Errorf("ollama error: %w", err)
+	}
+	return string(output), nil
+}
+
+func (o *OllamaProvider) Name() string {
+	return fmt.Sprintf("Ollama (%s)", o.model)
+}
+
+// AutoProvider picks Claude if available, fallback to Ollama
+func AutoProvider(apiKey, model string) AIProvider {
+	if exec.Command("claude", "--version").Run() == nil {
+		return NewClaudeProvider(apiKey)
+	}
+	return NewOllamaProvider(model)
+}