From 4426093c85f5ba979e7e45c24900745e0498c19d Mon Sep 17 00:00:00 2001 From: Kujtim Hoxha Date: Sat, 29 Nov 2025 15:44:16 +0100 Subject: [PATCH] chore: update agent tools dynamically --- go.mod | 6 ++++-- go.sum | 10 ++++------ internal/agent/agent.go | 3 +++ internal/agent/coordinator.go | 16 ++++++++++++++++ internal/tui/tui.go | 12 ++++++++++-- 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 09f1482ee4d5c305b54fb96c279d0920d50f2d6f..1f6023af0cd571b6d202a7538f369d439fb83350 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module github.com/charmbracelet/crush go 1.25.0 +replace charm.land/fantasy => ../../fantasy + require ( charm.land/bubbles/v2 v2.0.0-rc.1 charm.land/bubbletea/v2 v2.0.0-rc.2.0.20251126220703-2a0096c500a7 @@ -70,7 +72,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect github.com/RealAlexandreAI/json-repair v0.0.14 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect - github.com/aws/aws-sdk-go-v2 v1.39.6 // indirect + github.com/aws/aws-sdk-go-v2 v1.40.0 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect @@ -169,7 +171,7 @@ require ( golang.org/x/term v0.37.0 // indirect golang.org/x/time v0.12.0 // indirect google.golang.org/api v0.239.0 // indirect - google.golang.org/genai v1.34.0 // indirect + google.golang.org/genai v1.36.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/grpc v1.74.2 // indirect google.golang.org/protobuf v1.36.10 // indirect diff --git a/go.sum b/go.sum index c575d10b05d87532c234585577b8d0c2c1e0cc90..7b1885f8786815da537d11ca9fb19b56cc4f5893 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,6 @@ charm.land/bubbles/v2 v2.0.0-rc.1 h1:EiIFVAc3Zi/yY86td+79mPhHR7AqZ1OxF+6ztpOCRaM charm.land/bubbles/v2 v2.0.0-rc.1/go.mod h1:5AbN6cEd/47gkEf8TgiQ2O3RZ5QxMS14l9W+7F9fPC4= charm.land/bubbletea/v2 v2.0.0-rc.2.0.20251126220703-2a0096c500a7 h1:3qsObfEm0WuACFhe3MSTPX8QByjVcjWkZDO4o2VWFpc= charm.land/bubbletea/v2 v2.0.0-rc.2.0.20251126220703-2a0096c500a7/go.mod h1:IXFmnCnMLTWw/KQ9rEatSYqbAPAYi8kA3Yqwa1SFnLk= -charm.land/fantasy v0.3.2 h1:yHTsSZ25LcICMRw3xzdz3OkaZtDQch+B5ljJo17HxgU= -charm.land/fantasy v0.3.2/go.mod h1:sV8Ns/JTJHOaYOHPgVRDugMheAyxsW/nmdpVGrycYEk= charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251119143523-0334bb4562ca h1:6bVc8OFotCS4sS7HKqxTudP7yn8Y0ODR6df2pdlY/+s= charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251119143523-0334bb4562ca/go.mod h1:XSJjv7DaH4zd1Y27kZis295RkEj9OFR9zh2WffQQsKQ= charm.land/x/vcr v0.1.1 h1:PXCFMUG0rPtyk35rhfzYCJEduOzWXCIbrXTFq4OF/9Q= @@ -44,8 +42,8 @@ github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kk github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk= -github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= +github.com/aws/aws-sdk-go-v2 v1.40.0 h1:/WMUA0kjhZExjOQN2z3oLALDREea1A7TobfuiBrKlwc= +github.com/aws/aws-sdk-go-v2 v1.40.0/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM= github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= @@ -457,8 +455,8 @@ golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.239.0 h1:2hZKUnFZEy81eugPs4e2XzIJ5SOwQg0G82bpXD65Puo= google.golang.org/api v0.239.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50= -google.golang.org/genai v1.34.0 h1:lPRJRO+HqRX1SwFo1Xb/22nZ5MBEPUbXDl61OoDxlbY= -google.golang.org/genai v1.34.0/go.mod h1:7pAilaICJlQBonjKKJNhftDFv3SREhZcTe9F6nRcjbg= +google.golang.org/genai v1.36.0 h1:sJCIjqTAmwrtAIaemtTiKkg2TO1RxnYEusTmEQ3nGxM= +google.golang.org/genai v1.36.0/go.mod h1:A3kkl0nyBjyFlNjgxIwKq70julKbIxpSxqKO5gw/gmk= google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE= google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4= diff --git a/internal/agent/agent.go b/internal/agent/agent.go index 9b7266c7c358865bcfa58520b477ae7a2dcfb22b..4249acb23af2c08b6493105c5cc3bbc916cdc208 100644 --- a/internal/agent/agent.go +++ b/internal/agent/agent.go @@ -212,6 +212,9 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (*fantasy prepared.Messages[i].ProviderOptions = nil } + // Use latest tools (updated by SetTools when MCP tools change). + prepared.Tools = a.tools + queuedCalls, _ := a.messageQueue.Get(call.SessionID) a.messageQueue.Del(call.SessionID) for _, queued := range queuedCalls { diff --git a/internal/agent/coordinator.go b/internal/agent/coordinator.go index c01ae619726343e33992b1c0c98066697e0b5f7f..075a1eccb3c3c42bd9946e3d8089063b65fcaf76 100644 --- a/internal/agent/coordinator.go +++ b/internal/agent/coordinator.go @@ -52,6 +52,7 @@ type Coordinator interface { Summarize(context.Context, string) error Model() Model UpdateModels(ctx context.Context) error + RefreshTools(ctx context.Context) error } type coordinator struct { @@ -742,6 +743,21 @@ func (c *coordinator) UpdateModels(ctx context.Context) error { return nil } +func (c *coordinator) RefreshTools(ctx context.Context) error { + agentCfg, ok := c.cfg.Agents[config.AgentCoder] + if !ok { + return errors.New("coder agent not configured") + } + + tools, err := c.buildTools(ctx, agentCfg) + if err != nil { + return err + } + c.currentAgent.SetTools(tools) + slog.Debug("refreshed agent tools", "count", len(tools)) + return nil +} + func (c *coordinator) QueuedPrompts(sessionID string) int { return c.currentAgent.QueuedPrompts(sessionID) } diff --git a/internal/tui/tui.go b/internal/tui/tui.go index e5640a6ae5e19752aa7df900229dd72fc7ad7d05..080fc86dd23a2f463006ff3608e037bbade6456e 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -3,6 +3,7 @@ package tui import ( "context" "fmt" + "log/slog" "math/rand" "slices" "strings" @@ -11,6 +12,7 @@ import ( "charm.land/bubbles/v2/key" tea "charm.land/bubbletea/v2" "charm.land/lipgloss/v2" + "github.com/charmbracelet/crush/internal/agent" "github.com/charmbracelet/crush/internal/agent/tools/mcp" "github.com/charmbracelet/crush/internal/app" "github.com/charmbracelet/crush/internal/config" @@ -152,7 +154,7 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case mcp.EventPromptsListChanged: return a, handleMCPPromptsEvent(context.Background(), msg.Payload.Name) case mcp.EventToolsListChanged: - return a, handleMCPToolsEvent(context.Background(), msg.Payload.Name) + return a, handleMCPToolsEvent(context.Background(), msg.Payload.Name, a.app.AgentCoordinator) } // Completions messages @@ -706,9 +708,15 @@ func handleMCPPromptsEvent(ctx context.Context, name string) tea.Cmd { } } -func handleMCPToolsEvent(ctx context.Context, name string) tea.Cmd { +func handleMCPToolsEvent(ctx context.Context, name string, coordinator agent.Coordinator) tea.Cmd { return func() tea.Msg { mcp.RefreshTools(ctx, name) + // Refresh agent tools to pick up the new MCP tools. + if coordinator != nil { + if err := coordinator.RefreshTools(ctx); err != nil { + slog.Error("failed to refresh agent tools", "error", err) + } + } return nil } }