Introduce new `agent` command that scans .go files in the project, generates an AI-driven plan for changes based on user instruction, and applies edits with previews and backups. Includes integration with Grok client for planning and content generation. Update existing files with timestamp comments as part of the agent's editing demonstration. Add agentCmd to root command.
118 lines
3.4 KiB
Go
118 lines
3.4 KiB
Go
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.")
|
|
},
|
|
}
|