// internal/skills/spec/handlers.go package spec import ( "context" "encoding/json" "fmt" "time" iexec "github.com/mathiasbq/supervisor/internal/exec" "github.com/mathiasbq/supervisor/internal/session" ) type specArgs struct { ProjectRoot string `json:"project_root"` Requirements string `json:"requirements"` OutputPath string `json:"output_path"` 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 != "spec" { return nil, fmt.Errorf("unknown tool: %s", tool) } var a specArgs 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 a.Requirements == "" { return nil, fmt.Errorf("requirements is required") } outputPath := a.OutputPath if outputPath == "" { outputPath = "docs/spec.md" } model := a.Model if model == "" { model = s.cfg.DefaultModel } task := fmt.Sprintf( "phase: spec\nproject_root: %s\nrequirements: %s\noutput_path: %s\ncontext: %s\nmodel: %s", a.ProjectRoot, a.Requirements, outputPath, a.Context, model, ) task = session.PrependHistory(s.cfg.SessionsDir, a.SessionID, "spec", task) if s.cfg.ExecutorFn == nil { return nil, fmt.Errorf("no executor configured") } t0 := time.Now() result, err := s.cfg.ExecutorFn(ctx, iexec.Request{ SkillPrompt: s.cfg.SkillPrompt, TaskPrompt: task, Model: model, Tools: "Read,Write", }) if err != nil { return nil, err } if a.SessionID != "" && s.cfg.SessionsDir != "" { _ = session.Append(s.cfg.SessionsDir, a.SessionID, session.Entry{ SessionID: a.SessionID, Timestamp: time.Now(), Skill: "spec", Phase: "spec", ProjectRoot: a.ProjectRoot, Attempts: session.AttemptsFrom(result.Attempts), FinalStatus: result.Status, FilePath: result.FilePath, ModelUsed: result.ModelUsed, DurationMs: time.Since(t0).Milliseconds(), Message: result.Message, }) } b, err := json.Marshal(result) if err != nil { return nil, fmt.Errorf("marshal result: %w", err) } return b, nil }