Skip to content

Commit 29bf146

Browse files
committed
test message
1 parent 77db2dd commit 29bf146

4 files changed

Lines changed: 357 additions & 27 deletions

File tree

cmd/iterate/repl.go

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ func replHooks() iteragent.AgentHooks {
121121
fmt.Printf("%s[debug] ← %s (%s, %s, %d chars)%s\n",
122122
colorDim, toolName, status, elapsed, len(result), colorReset)
123123
}
124+
// Invalidate the @filename suggestion cache after file-mutating tools
125+
// so new files created by the agent are suggested within one cycle.
126+
switch toolName {
127+
case "write_file", "create_file", "edit_file", "delete_file", "move_file", "make_dir":
128+
invalidateAtFileCache()
129+
}
124130
},
125131
}
126132
}
@@ -537,14 +543,28 @@ func buildCommandContext(repoPath, line string, parts []string, p iteragent.Prov
537543
}
538544

539545
// atFileCache is a session-scoped cache of the repo's filename index.
546+
// It is rebuilt if the repo path changes or if the TTL expires (60s),
547+
// so new files created by the agent are picked up within one minute.
540548
var atFileCache struct {
541-
repoPath string
542-
index map[string]string // lowercase base → rel path
549+
repoPath string
550+
index map[string]string // lowercase base → rel path
551+
builtAt time.Time
552+
}
553+
554+
const atFileCacheTTL = 60 * time.Second
555+
556+
// invalidateAtFileCache forces the next call to buildAtFileIndex to rebuild.
557+
// Called after the agent writes or deletes files so suggestions stay fresh.
558+
func invalidateAtFileCache() {
559+
atFileCache.index = nil
560+
atFileCache.repoPath = ""
543561
}
544562

545-
// buildAtFileIndex walks repoPath once and caches the result.
563+
// buildAtFileIndex walks repoPath and caches the result for atFileCacheTTL.
546564
func buildAtFileIndex(repoPath string) map[string]string {
547-
if atFileCache.repoPath == repoPath && atFileCache.index != nil {
565+
if atFileCache.repoPath == repoPath &&
566+
atFileCache.index != nil &&
567+
time.Since(atFileCache.builtAt) < atFileCacheTTL {
548568
return atFileCache.index
549569
}
550570
const maxScan = 800
@@ -572,6 +592,7 @@ func buildAtFileIndex(repoPath string) map[string]string {
572592
})
573593
atFileCache.repoPath = repoPath
574594
atFileCache.index = idx
595+
atFileCache.builtAt = time.Now()
575596
return idx
576597
}
577598

internal/commands/autofix.go

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package commands
22

33
import (
4+
"bytes"
45
"context"
56
"fmt"
7+
"io"
8+
"os"
69
"os/exec"
710
"strconv"
811
)
@@ -56,32 +59,80 @@ func cmdAutofix(ctx Context) Result {
5659
testCmd, testArgs := detectTestCmd(ctx.RepoPath)
5760

5861
for attempt := 1; attempt <= maxAttempts; attempt++ {
59-
fmt.Printf("%s[attempt %d/%d]%s running %s %v\n",
60-
ColorDim, attempt, maxAttempts, ColorReset, testCmd, testArgs)
62+
fmt.Printf("%s[attempt %d/%d]%s %s %s\n",
63+
ColorDim, attempt, maxAttempts, ColorReset, testCmd, joinArgs(testArgs))
6164

65+
// Run with live output — capture into buf AND stream to terminal simultaneously.
66+
var buf bytes.Buffer
6267
cmd := exec.Command(testCmd, testArgs...)
6368
cmd.Dir = ctx.RepoPath
64-
output, err := cmd.CombinedOutput()
69+
cmd.Stdout = io.MultiWriter(os.Stdout, &buf)
70+
cmd.Stderr = io.MultiWriter(os.Stderr, &buf)
71+
72+
err := cmd.Run()
6573

6674
if err == nil {
67-
PrintSuccess("tests passed on attempt %d/%d", attempt, maxAttempts)
75+
PrintSuccess("all tests pass (%d/%d)", attempt, maxAttempts)
6876
return Result{Handled: true}
6977
}
7078

71-
// Tests failed — print failure output
72-
fmt.Printf("%s── Test failures ───────────────────%s\n", ColorDim, ColorReset)
73-
fmt.Println(string(output))
74-
fmt.Printf("%s──────────────────────────────────%s\n\n", ColorDim, ColorReset)
75-
79+
fmt.Println() // blank line after test output
7680
if attempt == maxAttempts {
7781
break
7882
}
7983

80-
// Ask agent to fix
81-
prompt := fmt.Sprintf("The following tests are failing. Fix them:\n\n%s", string(output))
84+
// Truncate failure output sent to agent (keep last 200 lines — most relevant).
85+
failureOutput := tailLines(buf.String(), 200)
86+
prompt := fmt.Sprintf(
87+
"Tests failed (attempt %d/%d). Fix the failures shown below, then ensure all tests pass.\n\n```\n%s\n```",
88+
attempt, maxAttempts, failureOutput)
8289
ctx.REPL.StreamAndPrint(context.Background(), ctx.Agent, prompt, ctx.RepoPath)
8390
}
8491

8592
fmt.Printf("%sautofix gave up after %d attempts%s\n\n", ColorRed, maxAttempts, ColorReset)
8693
return Result{Handled: true}
8794
}
95+
96+
func joinArgs(args []string) string {
97+
result := ""
98+
for i, a := range args {
99+
if i > 0 {
100+
result += " "
101+
}
102+
result += a
103+
}
104+
return result
105+
}
106+
107+
// tailLines returns the last n lines of s.
108+
func tailLines(s string, n int) string {
109+
lines := splitLines(s)
110+
if len(lines) <= n {
111+
return s
112+
}
113+
dropped := len(lines) - n
114+
return fmt.Sprintf("[... %d lines omitted ...]\n", dropped) + joinLines(lines[dropped:])
115+
}
116+
117+
func splitLines(s string) []string {
118+
var lines []string
119+
start := 0
120+
for i := 0; i < len(s); i++ {
121+
if s[i] == '\n' {
122+
lines = append(lines, s[start:i+1])
123+
start = i + 1
124+
}
125+
}
126+
if start < len(s) {
127+
lines = append(lines, s[start:])
128+
}
129+
return lines
130+
}
131+
132+
func joinLines(lines []string) string {
133+
var b bytes.Buffer
134+
for _, l := range lines {
135+
b.WriteString(l)
136+
}
137+
return b.String()
138+
}

0 commit comments

Comments
 (0)