refactor(recipe): implement one-file-at-a-time refactoring handler
Add refactorFiles to process discovered files individually, generating small JSON responses per file to avoid truncation. Update handleApplyStep to collect and parse multiple single-file JSONs into a unified patch. Switch discoverFiles comment to reflect real scanning. Add fallback default in Run for other steps.
This commit is contained in:
parent
0b5aa5eb57
commit
a8208da4c1
@ -37,11 +37,16 @@ func (r *Runner) Run() error {
|
|||||||
previousResults = append(previousResults, "Discovered files:\n"+result)
|
previousResults = append(previousResults, "Discovered files:\n"+result)
|
||||||
fmt.Println(result)
|
fmt.Println(result)
|
||||||
|
|
||||||
|
case strings.Contains(titleLower, "refactor"):
|
||||||
|
r.refactorFiles(previousResults) // <-- new one-file-at-a-time handler
|
||||||
|
continue
|
||||||
|
|
||||||
case strings.Contains(titleLower, "apply") || strings.Contains(titleLower, "patch"):
|
case strings.Contains(titleLower, "apply") || strings.Contains(titleLower, "patch"):
|
||||||
r.handleApplyStep(previousResults)
|
r.handleApplyStep(previousResults)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
// fallback for any other step
|
||||||
prompt := fmt.Sprintf(`Recipe Overview:
|
prompt := fmt.Sprintf(`Recipe Overview:
|
||||||
%s
|
%s
|
||||||
|
|
||||||
@ -76,7 +81,7 @@ Execute this step now. Respond ONLY with the expected output format — no expla
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// discoverFiles — temporary .go hard-code (we'll generalize with extensions next)
|
// discoverFiles — real filesystem scan (we'll generalize with extensions next)
|
||||||
func (r *Runner) discoverFiles() []string {
|
func (r *Runner) discoverFiles() []string {
|
||||||
var files []string
|
var files []string
|
||||||
root := "internal"
|
root := "internal"
|
||||||
@ -98,6 +103,50 @@ func (r *Runner) discoverFiles() []string {
|
|||||||
return files
|
return files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// refactorFiles — one file at a time (small JSON, no truncation)
|
||||||
|
func (r *Runner) refactorFiles(previousResults []string) {
|
||||||
|
discoveredLine := previousResults[len(previousResults)-1]
|
||||||
|
lines := strings.Split(discoveredLine, "\n")
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
filePath := strings.TrimSpace(line)
|
||||||
|
if filePath == "" || strings.HasPrefix(filePath, "Discovered") || filePath == "No files found matching the criteria." {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(" Refactoring %s...\n", filePath)
|
||||||
|
|
||||||
|
content, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" ❌ Could not read %s\n", filePath)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt := fmt.Sprintf(`Refactor the following file to use Result[T] instead of naked errors.
|
||||||
|
Follow existing style and preserve all comments.
|
||||||
|
Return ONLY this exact JSON (no extra text, no markdown):
|
||||||
|
|
||||||
|
{
|
||||||
|
"file": "%s",
|
||||||
|
"content": "the complete refactored file here"
|
||||||
|
}
|
||||||
|
|
||||||
|
Original file:
|
||||||
|
%s`, filePath, string(content))
|
||||||
|
|
||||||
|
messages := []map[string]string{
|
||||||
|
{"role": "system", "content": "You are Grok, built by xAI. Precise expert programmer and refactoring assistant."},
|
||||||
|
{"role": "user", "content": prompt},
|
||||||
|
}
|
||||||
|
|
||||||
|
response := r.Client.Stream(messages, r.Model)
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
// store the JSON response for the apply step
|
||||||
|
previousResults = append(previousResults, response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type FileChange struct {
|
type FileChange struct {
|
||||||
File string `json:"file"`
|
File string `json:"file"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
@ -109,32 +158,30 @@ func (r *Runner) handleApplyStep(previousResults []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
lastResult := previousResults[len(previousResults)-1]
|
// collect all JSON objects from the refactor step(s)
|
||||||
|
var allChanges []FileChange
|
||||||
|
for _, res := range previousResults {
|
||||||
|
start := strings.Index(res, "{")
|
||||||
|
end := strings.LastIndex(res, "}") + 1
|
||||||
|
if start == -1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
jsonStr := res[start:end]
|
||||||
|
|
||||||
// Find the JSON array in the response
|
var ch FileChange
|
||||||
start := strings.Index(lastResult, "[")
|
if err := json.Unmarshal([]byte(jsonStr), &ch); err == nil {
|
||||||
end := strings.LastIndex(lastResult, "]") + 1
|
allChanges = append(allChanges, ch)
|
||||||
if start == -1 || end == 0 {
|
}
|
||||||
fmt.Println(" ⚠️ No JSON found in previous step — skipping.")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonStr := lastResult[start:end]
|
if len(allChanges) == 0 {
|
||||||
|
fmt.Println(" ⚠️ No valid file changes found — skipping.")
|
||||||
var changes []FileChange
|
|
||||||
if err := json.Unmarshal([]byte(jsonStr), &changes); err != nil {
|
|
||||||
fmt.Printf(" ⚠️ Could not parse JSON: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(changes) == 0 {
|
|
||||||
fmt.Println(" ⚠️ No files to apply — skipping.")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(" 📄 Dry-run mode: creating patch file...")
|
fmt.Println(" 📄 Dry-run mode: creating patch file...")
|
||||||
patchPath := filepath.Join(".", "recipe-refactor.patch")
|
patchPath := filepath.Join(".", "recipe-refactor.patch")
|
||||||
if err := createUnifiedPatch(changes, patchPath); err != nil {
|
if err := createUnifiedPatch(allChanges, patchPath); err != nil {
|
||||||
fmt.Printf(" ❌ Failed to create patch: %v\n", err)
|
fmt.Printf(" ❌ Failed to create patch: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user