// internal/skills/review/handlers.go package review import ( "context" "encoding/json" "fmt" "strings" "time" "github.com/mathiasbq/supervisor/internal/brain" "github.com/mathiasbq/supervisor/internal/session" ) type reviewArgs struct { ProjectRoot string `json:"project_root"` Files []string `json:"files"` Context string `json:"context"` Model string `json:"model"` SessionID string `json:"session_id"` } // Handle dispatches the MCP tool call to the appropriate handler. func (s *Skill) Handle(ctx context.Context, tool string, args json.RawMessage) (json.RawMessage, error) { if tool != "review" { return nil, fmt.Errorf("unknown tool: %s", tool) } var a reviewArgs if err := json.Unmarshal(args, &a); err != nil { return nil, fmt.Errorf("parse args: %w", err) } if a.ProjectRoot == "" { return nil, fmt.Errorf("project_root is required") } if len(a.Files) == 0 { return nil, fmt.Errorf("files is required") } model := a.Model if model == "" { model = s.cfg.DefaultModel } brainCtx, _ := brain.Query(ctx, s.cfg.IngestBaseURL, strings.Join(a.Files, " ")+" "+a.Context, 3) task := fmt.Sprintf( "phase: review\nproject_root: %s\nfiles: %s\ncontext: %s\nmodel: %s", a.ProjectRoot, strings.Join(a.Files, ", "), a.Context, model, ) task = session.PrependHistory(s.cfg.SessionsDir, a.SessionID, "review", task) if brainCtx != "" { task = brainCtx + "\n---\n\n" + task } if s.cfg.CompleteFunc == nil { return nil, fmt.Errorf("no executor configured") } t0 := time.Now() text, dur, err := s.cfg.CompleteFunc(ctx, model, s.cfg.SkillPrompt, task) if err != nil { return nil, err } if a.SessionID != "" && s.cfg.SessionsDir != "" { msg := text if len(msg) > 200 { msg = msg[:200] } _ = session.Append(s.cfg.SessionsDir, a.SessionID, session.Entry{ SessionID: a.SessionID, Timestamp: time.Now(), Skill: "review", Phase: "review", ProjectRoot: a.ProjectRoot, FinalStatus: "ok", ModelUsed: model, DurationMs: time.Since(t0).Milliseconds(), Message: msg, }) } return json.Marshal(map[string]any{"text": text, "model": model, "duration_ms": dur}) }