From f2bc39b50038f007c11dde5ed2140cae6ae2181a Mon Sep 17 00:00:00 2001 From: Mathias Bergqvist Date: Wed, 22 Apr 2026 15:37:56 +0200 Subject: [PATCH] feat(skills): inject brain context into review, debug, spec, tdd before spawning workers --- cmd/supervisor/main.go | 38 +++++++++++++++++------------- internal/skills/debug/handlers.go | 6 +++++ internal/skills/debug/skill.go | 9 +++---- internal/skills/review/handlers.go | 6 +++++ internal/skills/review/skill.go | 9 +++---- internal/skills/spec/handlers.go | 6 +++++ internal/skills/spec/skill.go | 9 +++---- internal/skills/tdd/handlers.go | 6 +++++ internal/skills/tdd/skill.go | 11 +++++---- 9 files changed, 66 insertions(+), 34 deletions(-) diff --git a/cmd/supervisor/main.go b/cmd/supervisor/main.go index 46bbd58..c7f9c7f 100644 --- a/cmd/supervisor/main.go +++ b/cmd/supervisor/main.go @@ -113,11 +113,12 @@ func main() { reg := registry.New() reg.Register(tdd.New(tdd.Config{ - SystemPrompt: string(systemPrompt), - SkillPrompt: string(tddPrompt), - DefaultModel: models.ChainFor("tdd", "")[0], - ExecutorFn: buildOrch("tdd"), - SessionsDir: cfg.SessionsDir, + SystemPrompt: string(systemPrompt), + SkillPrompt: string(tddPrompt), + DefaultModel: models.ChainFor("tdd", "")[0], + ExecutorFn: buildOrch("tdd"), + SessionsDir: cfg.SessionsDir, + IngestBaseURL: cfg.IngestBaseURL, })) reg.Register(brain.New(brain.Config{ IngestBaseURL: cfg.IngestBaseURL, @@ -135,22 +136,25 @@ func main() { ExecutorFn: buildOrch("retrospective"), })) reg.Register(review.New(review.Config{ - SkillPrompt: string(reviewPrompt), - DefaultModel: models.ChainFor("review", "")[0], - ExecutorFn: buildOrch("review"), - SessionsDir: cfg.SessionsDir, + SkillPrompt: string(reviewPrompt), + DefaultModel: models.ChainFor("review", "")[0], + ExecutorFn: buildOrch("review"), + SessionsDir: cfg.SessionsDir, + IngestBaseURL: cfg.IngestBaseURL, })) reg.Register(skilldebug.New(skilldebug.Config{ - SkillPrompt: string(debugPrompt), - DefaultModel: models.ChainFor("debug", "")[0], - ExecutorFn: buildOrch("debug"), - SessionsDir: cfg.SessionsDir, + SkillPrompt: string(debugPrompt), + DefaultModel: models.ChainFor("debug", "")[0], + ExecutorFn: buildOrch("debug"), + SessionsDir: cfg.SessionsDir, + IngestBaseURL: cfg.IngestBaseURL, })) reg.Register(spec.New(spec.Config{ - SkillPrompt: string(specPrompt), - DefaultModel: models.ChainFor("spec", "")[0], - ExecutorFn: buildOrch("spec"), - SessionsDir: cfg.SessionsDir, + SkillPrompt: string(specPrompt), + DefaultModel: models.ChainFor("spec", "")[0], + ExecutorFn: buildOrch("spec"), + SessionsDir: cfg.SessionsDir, + IngestBaseURL: cfg.IngestBaseURL, })) reg.Register(trainer.New(trainer.Config{ ReaderPrompt: string(trainerReaderPrompt), diff --git a/internal/skills/debug/handlers.go b/internal/skills/debug/handlers.go index 250b32b..344ae20 100644 --- a/internal/skills/debug/handlers.go +++ b/internal/skills/debug/handlers.go @@ -7,6 +7,7 @@ import ( "fmt" "time" + "github.com/mathiasbq/supervisor/internal/brain" iexec "github.com/mathiasbq/supervisor/internal/exec" "github.com/mathiasbq/supervisor/internal/session" ) @@ -40,11 +41,16 @@ func (s *Skill) Handle(ctx context.Context, tool string, args json.RawMessage) ( model = s.cfg.DefaultModel } + brainCtx, _ := brain.Query(ctx, s.cfg.IngestBaseURL, a.Error+" "+a.Context, 3) + task := fmt.Sprintf( "phase: debug\nproject_root: %s\nerror: %s\ncontext: %s\nmodel: %s", a.ProjectRoot, a.Error, a.Context, model, ) task = session.PrependHistory(s.cfg.SessionsDir, a.SessionID, "debug", task) + if brainCtx != "" { + task = brainCtx + "\n---\n\n" + task + } if s.cfg.ExecutorFn == nil { return nil, fmt.Errorf("no executor configured") diff --git a/internal/skills/debug/skill.go b/internal/skills/debug/skill.go index 8dcc083..3f7df03 100644 --- a/internal/skills/debug/skill.go +++ b/internal/skills/debug/skill.go @@ -14,10 +14,11 @@ type ExecutorFn func(ctx context.Context, req iexec.Request) (iexec.Result, erro // Config holds dependencies for the debug skill. type Config struct { - SkillPrompt string - DefaultModel string - ExecutorFn ExecutorFn - SessionsDir string + SkillPrompt string + DefaultModel string + ExecutorFn ExecutorFn + SessionsDir string + IngestBaseURL string // optional: base URL of ingestion server for brain context } // Skill implements the debug MCP tool. diff --git a/internal/skills/review/handlers.go b/internal/skills/review/handlers.go index e2bf9a1..ecabbc7 100644 --- a/internal/skills/review/handlers.go +++ b/internal/skills/review/handlers.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/mathiasbq/supervisor/internal/brain" iexec "github.com/mathiasbq/supervisor/internal/exec" "github.com/mathiasbq/supervisor/internal/session" ) @@ -41,11 +42,16 @@ func (s *Skill) Handle(ctx context.Context, tool string, args json.RawMessage) ( 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.ExecutorFn == nil { return nil, fmt.Errorf("no executor configured") diff --git a/internal/skills/review/skill.go b/internal/skills/review/skill.go index 25a6936..8c309f0 100644 --- a/internal/skills/review/skill.go +++ b/internal/skills/review/skill.go @@ -14,10 +14,11 @@ type ExecutorFn func(ctx context.Context, req iexec.Request) (iexec.Result, erro // Config holds dependencies for the review skill. type Config struct { - SkillPrompt string - DefaultModel string - ExecutorFn ExecutorFn - SessionsDir string + SkillPrompt string + DefaultModel string + ExecutorFn ExecutorFn + SessionsDir string + IngestBaseURL string // optional: base URL of ingestion server for brain context } // Skill implements the review MCP tool. diff --git a/internal/skills/spec/handlers.go b/internal/skills/spec/handlers.go index 5e4378e..0514917 100644 --- a/internal/skills/spec/handlers.go +++ b/internal/skills/spec/handlers.go @@ -7,6 +7,7 @@ import ( "fmt" "time" + "github.com/mathiasbq/supervisor/internal/brain" iexec "github.com/mathiasbq/supervisor/internal/exec" "github.com/mathiasbq/supervisor/internal/session" ) @@ -45,11 +46,16 @@ func (s *Skill) Handle(ctx context.Context, tool string, args json.RawMessage) ( model = s.cfg.DefaultModel } + brainCtx, _ := brain.Query(ctx, s.cfg.IngestBaseURL, a.Requirements+" "+a.Context, 3) + 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 brainCtx != "" { + task = brainCtx + "\n---\n\n" + task + } if s.cfg.ExecutorFn == nil { return nil, fmt.Errorf("no executor configured") diff --git a/internal/skills/spec/skill.go b/internal/skills/spec/skill.go index fa52d20..75a749e 100644 --- a/internal/skills/spec/skill.go +++ b/internal/skills/spec/skill.go @@ -14,10 +14,11 @@ type ExecutorFn func(ctx context.Context, req iexec.Request) (iexec.Result, erro // Config holds dependencies for the spec skill. type Config struct { - SkillPrompt string - DefaultModel string - ExecutorFn ExecutorFn - SessionsDir string + SkillPrompt string + DefaultModel string + ExecutorFn ExecutorFn + SessionsDir string + IngestBaseURL string // optional: base URL of ingestion server for brain context } // Skill implements the spec MCP tool. diff --git a/internal/skills/tdd/handlers.go b/internal/skills/tdd/handlers.go index f74825c..3ed1437 100644 --- a/internal/skills/tdd/handlers.go +++ b/internal/skills/tdd/handlers.go @@ -6,6 +6,7 @@ import ( "fmt" "time" + "github.com/mathiasbq/supervisor/internal/brain" iexec "github.com/mathiasbq/supervisor/internal/exec" "github.com/mathiasbq/supervisor/internal/session" ) @@ -41,10 +42,15 @@ func (s *Skill) handleRed(ctx context.Context, raw json.RawMessage) (json.RawMes if args.Spec == "" { return nil, fmt.Errorf("spec is required") } + brainCtx, _ := brain.Query(ctx, s.cfg.IngestBaseURL, args.Spec, 3) + task := fmt.Sprintf( "phase: red\nproject_root: %s\nspec: %s\nmodel: %s\ntest_cmd: %s", args.ProjectRoot, args.Spec, s.resolveModel(args.Model), args.TestCmd, ) + if brainCtx != "" { + task = brainCtx + "\n---\n\n" + task + } return s.execute(ctx, task) } diff --git a/internal/skills/tdd/skill.go b/internal/skills/tdd/skill.go index 5d74417..a7f5f96 100644 --- a/internal/skills/tdd/skill.go +++ b/internal/skills/tdd/skill.go @@ -12,11 +12,12 @@ import ( type ExecutorFn func(ctx context.Context, req iexec.Request) (iexec.Result, error) type Config struct { - SystemPrompt string - SkillPrompt string - ExecutorFn ExecutorFn // nil = no executor (tests that don't reach execute()) - DefaultModel string - SessionsDir string // optional: path to brain/sessions/ for history injection + SystemPrompt string + SkillPrompt string + ExecutorFn ExecutorFn // nil = no executor (tests that don't reach execute()) + DefaultModel string + SessionsDir string // optional: path to brain/sessions/ for history injection + IngestBaseURL string // optional: base URL of ingestion server for brain context } type Skill struct {