diff --git a/internal/skills/tdd/handlers.go b/internal/skills/tdd/handlers.go index 89ce09d..f74825c 100644 --- a/internal/skills/tdd/handlers.go +++ b/internal/skills/tdd/handlers.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "time" iexec "github.com/mathiasbq/supervisor/internal/exec" "github.com/mathiasbq/supervisor/internal/session" @@ -70,8 +71,15 @@ func (s *Skill) handleGreen(ctx context.Context, raw json.RawMessage) (json.RawM "phase: green\nproject_root: %s\ntest_path: %s\nmodel: %s\ntest_cmd: %s", args.ProjectRoot, args.TestPath, s.resolveModel(args.Model), args.TestCmd, ) - task = s.prependHistory(args.SessionID, "green", task) - return s.execute(ctx, task) + task = session.PrependHistory(s.cfg.SessionsDir, args.SessionID, "green", task) + + t0 := time.Now() + result, err := s.execute(ctx, task) + if err != nil { + return nil, err + } + s.logAttempt(args.SessionID, args.ProjectRoot, "tdd", "green", t0, result) + return result, nil } type refactorArgs struct { @@ -101,23 +109,15 @@ func (s *Skill) handleRefactor(ctx context.Context, raw json.RawMessage) (json.R "phase: refactor\nproject_root: %s\ntest_path: %s\nimpl_path: %s\nmodel: %s\ntest_cmd: %s", args.ProjectRoot, args.TestPath, args.ImplPath, s.resolveModel(args.Model), args.TestCmd, ) - task = s.prependHistory(args.SessionID, "refactor", task) - return s.execute(ctx, task) -} + task = session.PrependHistory(s.cfg.SessionsDir, args.SessionID, "refactor", task) -func (s *Skill) prependHistory(sessionID, currentPhase, task string) string { - if sessionID == "" || s.cfg.SessionsDir == "" { - return task + t0 := time.Now() + result, err := s.execute(ctx, task) + if err != nil { + return nil, err } - entries, err := session.Read(s.cfg.SessionsDir, sessionID) - if err != nil || len(entries) == 0 { - return task - } - history := session.FormatHistory(entries, currentPhase) - if history == "" { - return task - } - return history + "\n---\n\n" + task + s.logAttempt(args.SessionID, args.ProjectRoot, "tdd", "refactor", t0, result) + return result, nil } func (s *Skill) resolveModel(override string) string { @@ -127,6 +127,7 @@ func (s *Skill) resolveModel(override string) string { return s.cfg.DefaultModel } +// execute calls ExecutorFn and returns the marshaled result. func (s *Skill) execute(ctx context.Context, task string) (json.RawMessage, error) { if s.cfg.ExecutorFn == nil { return nil, fmt.Errorf("no executor configured") @@ -141,3 +142,28 @@ func (s *Skill) execute(ctx context.Context, task string) (json.RawMessage, erro } return json.Marshal(result) } + +// logAttempt writes a session.Entry for a completed phase if session_id is set. +// raw is the marshaled Result returned by execute; we unmarshal to extract fields. +func (s *Skill) logAttempt(sessionID, projectRoot, skill, phase string, t0 time.Time, raw json.RawMessage) { + if sessionID == "" || s.cfg.SessionsDir == "" { + return + } + var result iexec.Result + if err := json.Unmarshal(raw, &result); err != nil { + return + } + _ = session.Append(s.cfg.SessionsDir, sessionID, session.Entry{ + SessionID: sessionID, + Timestamp: time.Now(), + Skill: skill, + Phase: phase, + ProjectRoot: projectRoot, + Attempts: session.AttemptsFrom(result.Attempts), + FinalStatus: result.Status, + FilePath: result.FilePath, + ModelUsed: result.ModelUsed, + DurationMs: time.Since(t0).Milliseconds(), + Message: result.Message, + }) +}