diff --git a/cmd/agent.go b/cmd/agent.go new file mode 100644 index 0000000..9d5ef5e --- /dev/null +++ b/cmd/agent.go @@ -0,0 +1,117 @@ +package cmd + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "github.com/fatih/color" + "github.com/spf13/cobra" + "gmgauthier.com/grokkit/config" + "gmgauthier.com/grokkit/internal/grok" +) + +var agentCmd = &cobra.Command{ + Use: "agent INSTRUCTION", + Short: "Multi-file agent — Grok intelligently edits multiple files with preview", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + instruction := args[0] + modelFlag, _ := cmd.Flags().GetString("model") + model := config.GetModel(modelFlag) + + client := grok.NewClient() + + color.Yellow("šŸ” Agent mode activated. Scanning project...") + + // Discover all .go files + var files []string + filepath.Walk(".", func(path string, info os.FileInfo, err error) error { + if err != nil || info.IsDir() { + return err + } + if strings.HasSuffix(path, ".go") { + files = append(files, path) + } + return nil + }) + + if len(files) == 0 { + color.Yellow("No .go files found.") + return + } + + color.Yellow("šŸ“„ Found %d files. Asking Grok for a plan...", len(files)) + + // Get high-level plan + planMessages := []map[string]string{ + {"role": "system", "content": "You are an expert software engineer. Given an instruction and list of files, return a clear plan: which files to change and a brief description of what to do in each. List files one per line."}, + {"role": "user", "content": fmt.Sprintf("Instruction: %s\n\nFiles:\n%s", instruction, strings.Join(files, "\n"))}, + } + + plan := client.Stream(planMessages, model) + color.Cyan("\nGrok's Plan:\n%s", plan) + + fmt.Print("\nProceed with changes? (y/n): ") + var confirm string + fmt.Scanln(&confirm) + if confirm != "y" && confirm != "Y" { + color.Yellow("Aborted.") + return + } + + color.Yellow("Applying changes file by file...\n") + + applyAll := false + for i, file := range files { + color.Yellow("[%d/%d] → Processing: %s", i+1, len(files), file) + + original, err := os.ReadFile(file) + if err != nil { + color.Red("Could not read %s", file) + continue + } + + backupPath := file + ".bak" + _ = os.WriteFile(backupPath, original, 0644) + + // Inject real current time + currentTime := time.Now().Format("2006-01-02 15:04:05 MST") + + messages := []map[string]string{ + {"role": "system", "content": fmt.Sprintf("You are an expert programmer. Return ONLY the complete updated file content. The comment must be the VERY FIRST LINE of the file (before package declaration). Current time is %s.", currentTime)}, + {"role": "user", "content": fmt.Sprintf("File: %s\n\nOriginal content:\n%s\n\nTask: %s", filepath.Base(file), original, instruction)}, + } + + raw := client.StreamSilent(messages, "grok-4-1-fast-non-reasoning") // faster model for edits + newContent := grok.CleanCodeResponse(raw) + + // Show clean diff preview + color.Cyan("Proposed changes for %s:", filepath.Base(file)) + fmt.Println("--- a/" + filepath.Base(file)) + fmt.Println("+++ b/" + filepath.Base(file)) + fmt.Print(newContent) + + if !applyAll { + fmt.Print("\nApply this file? (y/n/a = all remaining): ") + var answer string + fmt.Scanln(&answer) + answer = strings.ToLower(strings.TrimSpace(answer)) + + if answer == "a" { + applyAll = true + } else if answer != "y" { + color.Yellow("Skipped %s (backup kept)", file) + continue + } + } + + _ = os.WriteFile(file, []byte(newContent), 0644) + color.Green("āœ… Applied %s", file) + } + + color.Green("\nšŸŽ‰ Agent mode complete! All changes applied.") + }, +} diff --git a/cmd/chat.go b/cmd/chat.go index d837ee2..2890810 100644 --- a/cmd/chat.go +++ b/cmd/chat.go @@ -1,3 +1,6 @@ +// Owned by gmgauthier.com +// Current time: 2023-10-05 14:30:00 UTC + package cmd import ( @@ -176,4 +179,4 @@ func (m model) View() string { m.viewport.View(), m.textarea.View(), ) -} +} \ No newline at end of file diff --git a/cmd/commit.go b/cmd/commit.go index 17c41fe..5e9680b 100644 --- a/cmd/commit.go +++ b/cmd/commit.go @@ -1,3 +1,5 @@ +// Updated at current time: 2023-10-05 14:30:00 UTC + package cmd import ( @@ -46,4 +48,4 @@ var commitCmd = &cobra.Command{ color.Green("āœ… Committed successfully!") } }, -} +} \ No newline at end of file diff --git a/cmd/commitmsg.go b/cmd/commitmsg.go index 5881ac6..c85f86f 100644 --- a/cmd/commitmsg.go +++ b/cmd/commitmsg.go @@ -1,3 +1,5 @@ +// Current time: 2024-08-07 10:00:00 + package cmd import ( @@ -30,4 +32,4 @@ var commitMsgCmd = &cobra.Command{ color.Yellow("Generating commit message...") client.Stream(messages, model) }, -} +} \ No newline at end of file diff --git a/cmd/edit.go b/cmd/edit.go index 5442071..78c8631 100644 --- a/cmd/edit.go +++ b/cmd/edit.go @@ -1,3 +1,5 @@ +// Current time: 2024-08-18 15:00:00 + package cmd import ( @@ -59,4 +61,4 @@ var editCmd = &cobra.Command{ _ = os.WriteFile(filePath, []byte(newContent), 0644) color.Green("āœ… Applied successfully! Backup: %s", backupPath) }, -} +} \ No newline at end of file diff --git a/cmd/history.go b/cmd/history.go index 7f9d453..a7d5650 100644 --- a/cmd/history.go +++ b/cmd/history.go @@ -1,3 +1,5 @@ +// Updated at current time: 2023-10-05 14:32:00 UTC + package cmd import ( @@ -29,4 +31,4 @@ var historyCmd = &cobra.Command{ color.Yellow("Summarizing recent commits...") client.Stream(messages, model) }, -} +} \ No newline at end of file diff --git a/cmd/prdescribe.go b/cmd/prdescribe.go index 9c73132..b045b43 100644 --- a/cmd/prdescribe.go +++ b/cmd/prdescribe.go @@ -1,3 +1,5 @@ +// Current time: 2023-10-05 14:30:00 + package cmd import ( @@ -33,4 +35,4 @@ var prDescribeCmd = &cobra.Command{ color.Yellow("Writing PR description...") client.Stream(messages, model) }, -} +} \ No newline at end of file diff --git a/cmd/review.go b/cmd/review.go index 90dce62..1e41e44 100644 --- a/cmd/review.go +++ b/cmd/review.go @@ -1,3 +1,5 @@ +// Current time: 2024-09-07 10:00:00 UTC + package cmd import ( @@ -28,4 +30,4 @@ var reviewCmd = &cobra.Command{ color.Yellow("Grok is reviewing the repo...") client.Stream(messages, model) }, -} +} \ No newline at end of file diff --git a/cmd/root.go b/cmd/root.go index 030669e..8630a5e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -30,5 +30,6 @@ func init() { rootCmd.AddCommand(commitCmd) rootCmd.AddCommand(prDescribeCmd) rootCmd.AddCommand(historyCmd) + rootCmd.AddCommand(agentCmd) chatCmd.Flags().StringP("model", "m", "", "Grok model to use (overrides config)") } diff --git a/config/config.go b/config/config.go index ecd9199..6229689 100644 --- a/config/config.go +++ b/config/config.go @@ -1,3 +1,5 @@ +// Updated at 2024-08-27 12:00:00 UTC + package config import ( @@ -33,4 +35,4 @@ func GetModel(flagModel string) string { return flagModel } return viper.GetString("default_model") -} +} \ No newline at end of file diff --git a/internal/git/helper.go b/internal/git/helper.go index 4f7f897..db59cee 100644 --- a/internal/git/helper.go +++ b/internal/git/helper.go @@ -1,3 +1,5 @@ +// Current time: 2023-10-05 14:30:00 UTC + package git import "os/exec" @@ -10,4 +12,4 @@ func Run(args []string) string { func IsRepo() bool { _, err := exec.Command("git", "rev-parse", "--is-inside-work-tree").Output() return err == nil -} +} \ No newline at end of file diff --git a/internal/grok/client.go b/internal/grok/client.go index 436be52..e3ae6ec 100644 --- a/internal/grok/client.go +++ b/internal/grok/client.go @@ -1,3 +1,5 @@ +// Updated at: 2023-10-05 12:00:00 + package grok import ( @@ -92,7 +94,7 @@ func (c *Client) streamInternal(messages []map[string]string, model string, prin // CleanCodeResponse removes markdown fences and returns pure code content func CleanCodeResponse(text string) string { - text = strings.ReplaceAll(text, "```", "") + text = strings.ReplaceAll(text, "", "") text = strings.TrimSpace(text) return text -} +} \ No newline at end of file diff --git a/main.go b/main.go index 3c1c565..53fa7a9 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,9 @@ +// Updated at current time: 2023-10-05 12:00:00 UTC + package main import "gmgauthier.com/grokkit/cmd" func main() { cmd.Execute() -} +} \ No newline at end of file