From 5a1a3866d4a681d771162d35680d6c7219eac3b0 Mon Sep 17 00:00:00 2001 From: Johanan Date: Wed, 30 Sep 2026 23:01:50 +0200 Subject: [PATCH 01/13] Preserve versioned STDIO API user agents across MCP protocols (#3323) * Identify STDIO API clients across MCP protocols Preserve release identities, resolve actual source revisions, and attach upstream client metadata per request without mutating shared transports. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6dde41da-ec25-4370-b26c-b036d0a53b3c * Escape MCP client metadata and preserve revision provenance Encode upstream client comments as valid HTTP header data, keep explicit build revisions independent of embedded dirty state, and reject null source hashes. Cover protocol wire behaviour and document the version-source contract. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6dde41da-ec25-4370-b26c-b036d0a53b3c * Preserve STDIO startup without usable version metadata Skip invalid build metadata while retaining release and revision precedence. Fall back to dev with a stderr warning rather than preventing startup, and cover the compatibility contract in tests and documentation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6dde41da-ec25-4370-b26c-b036d0a53b3c --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6dde41da-ec25-4370-b26c-b036d0a53b3c --- README.md | 8 +- cmd/github-mcp-server/main.go | 11 +- cmd/github-mcp-server/version.go | 75 ++++++++ cmd/github-mcp-server/version_test.go | 122 +++++++++++++ docs/installation-guides/README.md | 2 +- internal/ghmcp/server.go | 78 ++++---- internal/ghmcp/server_test.go | 250 ++++++++++++++++++++++++++ pkg/http/transport/user_agent.go | 14 +- pkg/http/transport/user_agent_test.go | 48 +++++ 9 files changed, 560 insertions(+), 48 deletions(-) create mode 100644 cmd/github-mcp-server/version.go create mode 100644 cmd/github-mcp-server/version_test.go create mode 100644 pkg/http/transport/user_agent_test.go diff --git a/README.md b/README.md index d013303d81..221211dc58 100644 --- a/README.md +++ b/README.md @@ -403,7 +403,13 @@ For a complete overview of all installation options, see our **[Installation Gui ### Build from source If you don't have Docker, you can use `go build` to build the binary in the -`cmd/github-mcp-server` directory, and use the `github-mcp-server stdio` command with the `GITHUB_PERSONAL_ACCESS_TOKEN` environment variable set to your token. To specify the output location of the build, use the `-o` flag. You should configure your server to use the built executable as its `command`. For example: +`cmd/github-mcp-server` directory, and use the `github-mcp-server stdio` command with the `GITHUB_PERSONAL_ACCESS_TOKEN` environment variable set to your token. To specify the output location of the build, use the `-o` flag. You should configure your server to use the built executable as its `command`. + +STDIO API requests identify the server as `github-mcp-server/` and retain the upstream MCP client's name/version in parentheses when available. Control characters, quotes, backslashes and parentheses in client metadata are escaped so the HTTP header remains valid; ordinary names, versions, spaces and printable Unicode are preserved. Release builds keep their release version. Source and default Docker builds with revision metadata use `vcs-`. The `-dirty` marker applies only when both the revision and modified state come from embedded VCS metadata, never to a valid, explicitly supplied `main.commit`. This is a VCS build identifier, not a release number. + +Build the complete package with `go build -o github-mcp-server ./cmd/github-mcp-server` from a Git checkout to embed its VCS revision. For builds without VCS metadata, supply the actual release with `-ldflags '-X main.version='` or the full source revision with `-ldflags '-X main.commit='`. Valid metadata is selected in this order: explicit release, explicit source revision, embedded VCS revision, then installed main-module version. Valid explicit revisions remain authoritative even if build-context filtering changes embedded VCS metadata. Missing or malformed candidates fall through to the next usable source. If none is available, STDIO still starts with the development label `dev` and emits a warning on stderr, leaving stdout available for the MCP protocol. Supply real release or revision metadata when version-specific attribution is needed. + +For example: ```JSON { diff --git a/cmd/github-mcp-server/main.go b/cmd/github-mcp-server/main.go index c0cadbbc63..91abef6a08 100644 --- a/cmd/github-mcp-server/main.go +++ b/cmd/github-mcp-server/main.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "os" + "runtime/debug" "strings" "time" @@ -38,7 +39,13 @@ var ( Use: "stdio", Short: "Start stdio server", Long: `Start a server that communicates via standard input/output streams using JSON-RPC messages.`, - RunE: func(_ *cobra.Command, _ []string) error { + RunE: func(cmd *cobra.Command, _ []string) error { + info, _ := debug.ReadBuildInfo() + serverVersion := resolveServerVersion(version, commit, info) + if serverVersion == developmentServerVersion { + cmd.PrintErrln("Warning: no usable server build version metadata; using development version dev") + } + token := viper.GetString("personal_access_token") appID := viper.GetString("app-id") appInstallationID := viper.GetString("app-installation-id") @@ -110,7 +117,7 @@ var ( ttl := viper.GetDuration("repo-access-cache-ttl") stdioServerConfig := ghmcp.StdioServerConfig{ - Version: version, + Version: serverVersion, Host: viper.GetString("host"), Token: token, EnabledToolsets: enabledToolsets, diff --git a/cmd/github-mcp-server/version.go b/cmd/github-mcp-server/version.go new file mode 100644 index 0000000000..51b9e5880c --- /dev/null +++ b/cmd/github-mcp-server/version.go @@ -0,0 +1,75 @@ +package main + +import ( + "encoding/hex" + "runtime/debug" + "strings" + "unicode" +) + +const developmentServerVersion = "dev" + +func resolveServerVersion(release, revision string, info *debug.BuildInfo) string { + isPlaceholder := func(value string) bool { + switch value { + case "", "version", "dev", "unknown", "(devel)": + return true + default: + return false + } + } + validRelease := func(value string) bool { + if isPlaceholder(value) { + return false + } + for _, r := range value { + if r > unicode.MaxASCII || r <= ' ' || r == 127 || strings.ContainsRune("()<>@,;:\\\"/[]?={}", r) { + return false + } + } + return true + } + if validRelease(release) { + return release + } + + var vcsRevision string + var dirty bool + if info != nil { + for _, setting := range info.Settings { + switch setting.Key { + case "vcs.revision": + vcsRevision = setting.Value + case "vcs.modified": + dirty = setting.Value == "true" + } + } + } + revisions := []struct { + value string + dirty bool + }{ + {value: revision}, + {value: vcsRevision, dirty: dirty}, + } + for _, candidate := range revisions { + if len(candidate.value) != 40 && len(candidate.value) != 64 { + continue + } + if _, err := hex.DecodeString(candidate.value); err != nil { + continue + } + if strings.Trim(candidate.value, "0") == "" { + continue + } + resolved := "vcs-" + strings.ToLower(candidate.value) + if candidate.dirty { + resolved += "-dirty" + } + return resolved + } + if info != nil && validRelease(info.Main.Version) { + return info.Main.Version + } + return developmentServerVersion +} diff --git a/cmd/github-mcp-server/version_test.go b/cmd/github-mcp-server/version_test.go new file mode 100644 index 0000000000..4258b817c1 --- /dev/null +++ b/cmd/github-mcp-server/version_test.go @@ -0,0 +1,122 @@ +package main + +import ( + "runtime/debug" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestResolveServerVersion(t *testing.T) { + t.Parallel() + sha := strings.Repeat("a1", 20) + otherSHA := strings.Repeat("b2", 20) + source := &debug.BuildInfo{ + Main: debug.Module{Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "vcs.revision", Value: sha}, {Key: "vcs.modified", Value: "false"}, + }, + } + dirty := &debug.BuildInfo{ + Main: debug.Module{Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "vcs.revision", Value: sha}, {Key: "vcs.modified", Value: "true"}, + }, + } + tests := []struct { + name string + release string + revision string + info *debug.BuildInfo + want string + }{ + {name: "release unchanged", release: "v1.2.3", revision: sha, info: dirty, want: "v1.2.3"}, + {name: "release suffix unchanged", release: "v1.2.3-rc.1+build.4", want: "v1.2.3-rc.1+build.4"}, + {name: "release before invalid revision", release: "v1.2.3", revision: "short", info: dirty, want: "v1.2.3"}, + {name: "source build", release: "version", revision: "commit", info: source, want: "vcs-" + sha}, + { + name: "source revision before inferred module version", release: "version", revision: "commit", + info: &debug.BuildInfo{ + Main: debug.Module{Version: "v1.2.4-0.20260916095829-a1a1a1a1a1a1+dirty"}, + Settings: dirty.Settings, + }, + want: "vcs-" + sha + "-dirty", + }, + {name: "dirty source", release: "version", revision: "commit", info: dirty, want: "vcs-" + sha + "-dirty"}, + {name: "Docker revision", release: "dev", revision: sha, want: "vcs-" + sha}, + {name: "Docker revision ignores context dirty state", release: "dev", revision: sha, info: dirty, want: "vcs-" + sha}, + {name: "linked revision ignores unrelated dirty state", release: "dev", revision: otherSHA, info: dirty, want: "vcs-" + otherSHA}, + {name: "linked revision precedence", release: "dev", revision: otherSHA, info: source, want: "vcs-" + otherSHA}, + {name: "SHA256", release: "dev", revision: strings.Repeat("ab", 32), want: "vcs-" + strings.Repeat("ab", 32)}, + {name: "canonical hex", release: "dev", revision: strings.ToUpper(sha), want: "vcs-" + sha}, + {name: "installed module", info: &debug.BuildInfo{Main: debug.Module{Version: "v1.2.3"}}, want: "v1.2.3"}, + {name: "absent build info", release: "version", revision: "commit", want: "dev"}, + {name: "missing VCS metadata", release: "dev", info: &debug.BuildInfo{}, want: "dev"}, + {name: "unknown placeholder", release: "unknown", want: "dev"}, + {name: "short revision", release: "dev", revision: "abcdef", want: "dev"}, + {name: "short revision falls through to VCS", release: "dev", revision: "abcdef", info: source, want: "vcs-" + sha}, + {name: "malformed linked revision falls through to VCS", release: "dev", revision: strings.Repeat("x", 40), info: source, want: "vcs-" + sha}, + {name: "malformed linked revision without VCS", release: "dev", revision: strings.Repeat("x", 40), want: "dev"}, + { + name: "malformed VCS falls through to installed module", + info: &debug.BuildInfo{ + Main: debug.Module{Version: "v1.2.3"}, + Settings: []debug.BuildSetting{{Key: "vcs.revision", Value: "short"}}, + }, + want: "v1.2.3", + }, + { + name: "malformed VCS without installed module", + info: &debug.BuildInfo{ + Main: debug.Module{Version: "(devel)"}, + Settings: []debug.BuildSetting{{Key: "vcs.revision", Value: "short"}}, + }, + want: "dev", + }, + {name: "invalid installed module", info: &debug.BuildInfo{Main: debug.Module{Version: "v1.2.3\n"}}, want: "dev"}, + {name: "placeholder installed module", info: &debug.BuildInfo{Main: debug.Module{Version: "(devel)"}}, want: "dev"}, + {name: "release whitespace", release: "v1.2.3 extra", want: "dev"}, + {name: "release newline", release: "v1.2.3\n", want: "dev"}, + {name: "release slash", release: "release/v1.2.3", want: "dev"}, + {name: "release non ASCII", release: "v1.2.3-\u00e9", want: "dev"}, + {name: "invalid release falls through to linked revision", release: "v1.2.3 extra", revision: otherSHA, info: dirty, want: "vcs-" + otherSHA}, + {name: "invalid release falls through to dirty VCS", release: "release/v1.2.3", info: dirty, want: "vcs-" + sha + "-dirty"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, resolveServerVersion(tt.release, tt.revision, tt.info)) + }) + } +} + +func TestResolveServerVersionSkipsNullRevisions(t *testing.T) { + t.Parallel() + for _, revision := range []string{strings.Repeat("0", 40), strings.Repeat("0", 64)} { + for _, modified := range []string{"false", "true"} { + for _, linked := range []bool{false, true} { + name := revision + "/modified=" + modified + if linked { + name += "/linked" + } + t.Run(name, func(t *testing.T) { + info := &debug.BuildInfo{Settings: []debug.BuildSetting{ + {Key: "vcs.revision", Value: revision}, + {Key: "vcs.modified", Value: modified}, + }} + commit := "commit" + want := "dev" + if linked { + commit = revision + info.Settings[0].Value = strings.Repeat("ab", 20) + want = "vcs-" + strings.Repeat("ab", 20) + if modified == "true" { + want += "-dirty" + } + } + assert.Equal(t, want, resolveServerVersion("dev", commit, info)) + }) + } + } + } +} diff --git a/docs/installation-guides/README.md b/docs/installation-guides/README.md index 46581aa77e..888caf64a4 100644 --- a/docs/installation-guides/README.md +++ b/docs/installation-guides/README.md @@ -66,7 +66,7 @@ The GitHub MCP Server can be installed using several methods. **Docker is the mo - **Pros**: Latest features, full customization, no external dependencies - **Cons**: Requires Go development environment, more complex setup - **Prerequisites**: [Go 1.24+](https://go.dev/doc/install) -- **Build command**: `go build -o github-mcp-server cmd/github-mcp-server/main.go` +- **Build command**: `go build -o github-mcp-server ./cmd/github-mcp-server` (build the complete package from a Git checkout to retain its source revision). - **Best for**: Developers who want the latest features or need custom modifications ### Important Notes on the GitHub MCP Server diff --git a/internal/ghmcp/server.go b/internal/ghmcp/server.go index f713a44026..5da57527e8 100644 --- a/internal/ghmcp/server.go +++ b/internal/ghmcp/server.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "os/signal" + "strconv" "strings" "syscall" "time" @@ -33,12 +34,10 @@ import ( // githubClients holds all the GitHub API clients created for a server instance. type githubClients struct { - rest *gogithub.Client - restUATransp *transport.UserAgentTransport - gql *githubv4.Client - gqlHTTP *http.Client // retained for middleware to modify transport - raw *raw.Client - repoAccess *lockdown.RepoAccessCache + rest *gogithub.Client + gql *githubv4.Client + raw *raw.Client + repoAccess *lockdown.RepoAccessCache } // createGitHubClients creates all the GitHub API clients needed by the server. @@ -90,7 +89,7 @@ func createGitHubClients(cfg github.MCPServerConfig, apiHost utils.APIHostResolv // client per request (see pkg/github RequestDeps) and does not use this path. restUATransport := &transport.UserAgentTransport{ Transport: &transport.ETagTransport{Transport: http.DefaultTransport}, - Agent: fmt.Sprintf("github-mcp-server/%s", cfg.Version), + Agent: stdioUserAgent(cfg, nil), } restClient, err := newRESTClient(cfg, restUATransport, restURL.String(), uploadURL.String(), allowedHosts) if err != nil { @@ -102,7 +101,10 @@ func createGitHubClients(cfg github.MCPServerConfig, apiHost utils.APIHostResolv gqlHTTPClient := &http.Client{ Transport: &transport.BearerAuthTransport{ Transport: &transport.GraphQLFeaturesTransport{ - Transport: http.DefaultTransport, + Transport: &transport.UserAgentTransport{ + Transport: http.DefaultTransport, + Agent: stdioUserAgent(cfg, nil), + }, }, Token: cfg.Token, TokenProvider: cfg.TokenProvider, @@ -117,7 +119,7 @@ func createGitHubClients(cfg github.MCPServerConfig, apiHost utils.APIHostResolv // be large and are streamed rather than retained in memory. rawUATransport := &transport.UserAgentTransport{ Transport: http.DefaultTransport, - Agent: fmt.Sprintf("github-mcp-server/%s", cfg.Version), + Agent: stdioUserAgent(cfg, nil), } rawRESTClient, err := newRESTClient(cfg, rawUATransport, restURL.String(), uploadURL.String(), allowedHosts) if err != nil { @@ -141,12 +143,10 @@ func createGitHubClients(cfg github.MCPServerConfig, apiHost utils.APIHostResolv } return &githubClients{ - rest: restClient, - restUATransp: restUATransport, - gql: gqlClient, - gqlHTTP: gqlHTTPClient, - raw: rawClient, - repoAccess: repoAccessCache, + rest: restClient, + gql: gqlClient, + raw: rawClient, + repoAccess: repoAccessCache, }, nil } @@ -232,7 +232,7 @@ func NewStdioMCPServer(ctx context.Context, cfg github.MCPServerConfig) (*mcp.Se return nil, fmt.Errorf("failed to create GitHub MCP server: %w", err) } - ghServer.AddReceivingMiddleware(addUserAgentsMiddleware(cfg, clients.restUATransp, clients.gqlHTTP)) + ghServer.AddReceivingMiddleware(addUserAgentsMiddleware(cfg)) return ghServer, nil } @@ -441,36 +441,28 @@ func createFeatureChecker(enabledFeatures []string, insidersMode bool) inventory } } -func addUserAgentsMiddleware(cfg github.MCPServerConfig, restUATransp *transport.UserAgentTransport, gqlHTTPClient *http.Client) func(next mcp.MethodHandler) mcp.MethodHandler { +func stdioUserAgent(cfg github.MCPServerConfig, client *mcp.Implementation) string { + agent := fmt.Sprintf("github-mcp-server/%s", cfg.Version) + if client != nil { + comment := strconv.Quote(client.Name + "/" + client.Version) + comment = comment[1 : len(comment)-1] + comment = strings.NewReplacer("(", `\(`, ")", `\)`).Replace(comment) + agent += " (" + comment + ")" + } + if cfg.InsidersMode { + agent += " (insiders)" + } + return agent +} + +func addUserAgentsMiddleware(cfg github.MCPServerConfig) func(next mcp.MethodHandler) mcp.MethodHandler { return func(next mcp.MethodHandler) mcp.MethodHandler { return func(ctx context.Context, method string, request mcp.Request) (result mcp.Result, err error) { - if method != "initialize" { - return next(ctx, method, request) - } - - initializeRequest, ok := request.(*mcp.InitializeRequest) - if !ok { - return next(ctx, method, request) + var client *mcp.Implementation + if info, ok := request.(interface{ ClientInfo() *mcp.Implementation }); ok { + client = info.ClientInfo() } - - message := initializeRequest - userAgent := fmt.Sprintf( - "github-mcp-server/%s (%s/%s)", - cfg.Version, - message.Params.ClientInfo.Name, - message.Params.ClientInfo.Version, - ) - if cfg.InsidersMode { - userAgent += " (insiders)" - } - - restUATransp.Agent = userAgent - - gqlHTTPClient.Transport = &transport.UserAgentTransport{ - Transport: gqlHTTPClient.Transport, - Agent: userAgent, - } - + ctx = transport.WithUserAgent(ctx, stdioUserAgent(cfg, client)) return next(ctx, method, request) } } diff --git a/internal/ghmcp/server_test.go b/internal/ghmcp/server_test.go index 6f0e3ac3f3..b0fa1f7544 100644 --- a/internal/ghmcp/server_test.go +++ b/internal/ghmcp/server_test.go @@ -1 +1,251 @@ package ghmcp + +import ( + "context" + "encoding/json" + "net" + "net/http" + "net/http/httptest" + "sync/atomic" + "testing" + "time" + + "github.com/github/github-mcp-server/pkg/github" + "github.com/github/github-mcp-server/pkg/observability" + "github.com/github/github-mcp-server/pkg/observability/metrics" + "github.com/github/github-mcp-server/pkg/translations" + "github.com/modelcontextprotocol/go-sdk/mcp" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCreateGitHubClientsGraphQLUserAgent(t *testing.T) { + t.Parallel() + for _, insiders := range []bool{false, true} { + t.Run(map[bool]string{false: "release", true: "insiders"}[insiders], func(t *testing.T) { + headers := make(chan http.Header, 1) + api := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + headers <- r.Header.Clone() + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"data":{"viewer":{"login":"octocat"}}}`)) + })) + defer api.Close() + cfg := github.MCPServerConfig{Version: "v1.2.3", Token: "fixture-token", InsidersMode: insiders} + clients, err := createGitHubClients(cfg, newStaticAPIHostResolver(t, api.URL)) + require.NoError(t, err) + var query struct { + Viewer struct{ Login string } + } + require.NoError(t, clients.gql.Query(t.Context(), &query, nil)) + want := "github-mcp-server/v1.2.3" + if insiders { + want += " (insiders)" + } + header := <-headers + assert.Equal(t, want, header.Get("User-Agent")) + assert.Equal(t, "Bearer fixture-token", header.Get("Authorization")) + }) + } +} + +func TestStdioGraphQLUserAgent(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + handshake string + clientInfo *mcp.Implementation + version string + insiders bool + want string + }{ + { + name: "SDK discovery", handshake: "sdk", version: "v1.2.3", + clientInfo: &mcp.Implementation{Name: "test-client", Version: "4.5.6"}, + want: "github-mcp-server/v1.2.3 (test-client/4.5.6)", + }, + { + name: "direct modern call", version: "v1.2.3", + clientInfo: &mcp.Implementation{Name: "test-client", Version: "4.5.6"}, + want: "github-mcp-server/v1.2.3 (test-client/4.5.6)", + }, + { + name: "modern call without optional client info", version: "v1.2.3", + want: "github-mcp-server/v1.2.3", + }, + { + name: "legacy initialize", handshake: "legacy", version: "v1.2.3", + clientInfo: &mcp.Implementation{Name: "test-client", Version: "4.5.6"}, + want: "github-mcp-server/v1.2.3 (test-client/4.5.6)", + }, + { + name: "insiders", version: "v1.2.3", insiders: true, + clientInfo: &mcp.Implementation{Name: "test-client", Version: "4.5.6"}, + want: "github-mcp-server/v1.2.3 (test-client/4.5.6) (insiders)", + }, + { + name: "different server build", version: "v1.2.4", + clientInfo: &mcp.Implementation{Name: "test-client", Version: "4.5.6"}, + want: "github-mcp-server/v1.2.4 (test-client/4.5.6)", + }, + { + name: "HTTP comment delimiters", version: "v1.2.3", + clientInfo: &mcp.Implementation{Name: `Editor (Preview) \client`, Version: `v(1)\build`}, + want: `github-mcp-server/v1.2.3 (Editor \(Preview\) \\client/v\(1\)\\build)`, + }, + { + name: "modern control characters", version: "v1.2.3", + clientInfo: &mcp.Implementation{Name: "editor(\x00)\\client\r\nX-Fake:1", Version: "1.0\tbeta\x7f"}, + want: `github-mcp-server/v1.2.3 (editor\(\x00\)\\client\r\nX-Fake:1/1.0\tbeta\x7f)`, + }, + { + name: "SDK control characters", handshake: "sdk", version: "v1.2.3", insiders: true, + clientInfo: &mcp.Implementation{Name: "editor(\x00)\\client\r\nX-Fake:1", Version: "1.0\tbeta\x7f"}, + want: `github-mcp-server/v1.2.3 (editor\(\x00\)\\client\r\nX-Fake:1/1.0\tbeta\x7f) (insiders)`, + }, + { + name: "legacy control characters", handshake: "legacy", version: "v1.2.3", + clientInfo: &mcp.Implementation{Name: "editor(\x00)\\client\r\nX-Fake:1", Version: "1.0\tbeta\x7f"}, + want: `github-mcp-server/v1.2.3 (editor\(\x00\)\\client\r\nX-Fake:1/1.0\tbeta\x7f)`, + }, + { + name: "spaces and Unicode preserved", version: "v1.2.3", + clientInfo: &mcp.Implementation{Name: "VS Code \u03b2", Version: "\u03b1 1.0"}, + want: "github-mcp-server/v1.2.3 (VS Code \u03b2/\u03b1 1.0)", + }, + { + name: "empty client metadata", version: "v1.2.3", + clientInfo: &mcp.Implementation{}, + want: "github-mcp-server/v1.2.3 (/)", + }, + { + name: "quoted client metadata", version: "v1.2.3", + clientInfo: &mcp.Implementation{Name: `"Editor"`, Version: `v"1`}, + want: `github-mcp-server/v1.2.3 (\"Editor\"/v\"1)`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second) + defer cancel() + + headers := make(chan http.Header, 2) + api := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + headers <- r.Header.Clone() + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"data":{"repository":{"isPrivate":false,"issues":{"nodes":[],"totalCount":0,"pageInfo":{"hasNextPage":false,"endCursor":""}}}}}`)) + })) + defer api.Close() + + cfg := github.MCPServerConfig{ + Version: tt.version, Token: "fixture-token", InsidersMode: tt.insiders, + Translator: translations.NullTranslationHelper, Logger: discardLogger(), + } + clients, err := createGitHubClients(cfg, newStaticAPIHostResolver(t, api.URL)) + require.NoError(t, err) + obs, err := observability.NewExporters(cfg.Logger, metrics.NewNoopMetrics()) + require.NoError(t, err) + deps := github.NewBaseDeps( + clients.rest, clients.gql, clients.raw, nil, cfg.Translator, + github.FeatureFlags{}, 5000, createFeatureChecker(nil, tt.insiders), obs, + ) + inv, err := github.NewInventory(cfg.Translator). + WithToolsets([]string{}).WithTools([]string{"list_issues"}).Build() + require.NoError(t, err) + server, err := github.NewMCPServer(ctx, &cfg, deps, inv) + require.NoError(t, err) + server.AddReceivingMiddleware(addUserAgentsMiddleware(cfg)) + var initialized atomic.Bool + server.AddReceivingMiddleware(func(next mcp.MethodHandler) mcp.MethodHandler { + return func(ctx context.Context, method string, req mcp.Request) (mcp.Result, error) { + if method == "initialize" { + initialized.Store(true) + } + return next(ctx, method, req) + } + }) + + serverConn, clientConn := net.Pipe() + defer clientConn.Close() + require.NoError(t, clientConn.SetDeadline(time.Now().Add(10*time.Second))) + session, err := server.Connect(ctx, &mcp.IOTransport{Reader: serverConn, Writer: serverConn}, nil) + require.NoError(t, err) + defer session.Close() + params := &mcp.CallToolParams{ + Name: "list_issues", Arguments: map[string]any{"owner": "owner", "repo": "repo"}, + } + var changedClient bool + if tt.handshake == "sdk" { + client := mcp.NewClient(tt.clientInfo, nil) + cs, err := client.Connect(ctx, &mcp.IOTransport{Reader: clientConn, Writer: clientConn}, nil) + require.NoError(t, err) + defer cs.Close() + assert.Equal(t, "2026-07-28", cs.InitializeResult().ProtocolVersion) + result, err := cs.CallTool(ctx, params) + require.NoError(t, err) + require.False(t, result.IsError, "%+v", result.Content) + } else { + encoder, decoder := json.NewEncoder(clientConn), json.NewDecoder(clientConn) + id := 0 + call := func(method string, params any) json.RawMessage { + t.Helper() + id++ + require.NoError(t, encoder.Encode(map[string]any{ + "jsonrpc": "2.0", "id": id, "method": method, "params": params, + })) + var response struct { + Result json.RawMessage `json:"result"` + Error json.RawMessage `json:"error"` + } + require.NoError(t, decoder.Decode(&response)) + require.Empty(t, response.Error) + return response.Result + } + if tt.handshake == "legacy" { + call("initialize", &mcp.InitializeParams{ + ProtocolVersion: "2025-11-25", Capabilities: &mcp.ClientCapabilities{}, + ClientInfo: tt.clientInfo, + }) + require.NoError(t, encoder.Encode(map[string]any{ + "jsonrpc": "2.0", "method": "notifications/initialized", + })) + } else { + params.Meta = mcp.Meta{ + mcp.MetaKeyProtocolVersion: "2026-07-28", mcp.MetaKeyClientCapabilities: map[string]any{}, + } + if tt.clientInfo != nil { + params.Meta[mcp.MetaKeyClientInfo] = tt.clientInfo + } + } + var result mcp.CallToolResult + require.NoError(t, json.Unmarshal(call("tools/call", params), &result)) + require.False(t, result.IsError, "%+v", result.Content) + if tt.handshake != "legacy" { + params.Meta[mcp.MetaKeyClientInfo] = &mcp.Implementation{Name: "other-client", Version: "7.8.9"} + require.NoError(t, json.Unmarshal(call("tools/call", params), &result)) + require.False(t, result.IsError, "%+v", result.Content) + changedClient = true + } + } + + assert.Equal(t, tt.handshake == "legacy", initialized.Load()) + select { + case header := <-headers: + assert.Equal(t, tt.want, header.Get("User-Agent")) + assert.Equal(t, "Bearer fixture-token", header.Get("Authorization")) + assert.Equal(t, "issue_fields, repo_issue_fields", header.Get("GraphQL-Features")) + case <-ctx.Done(): + t.Fatal("GraphQL request did not reach the local HTTP server") + } + if changedClient { + want := "github-mcp-server/" + tt.version + " (other-client/7.8.9)" + if tt.insiders { + want += " (insiders)" + } + assert.Equal(t, want, (<-headers).Get("User-Agent")) + } + }) + } +} diff --git a/pkg/http/transport/user_agent.go b/pkg/http/transport/user_agent.go index a489941cce..a40b4cc45b 100644 --- a/pkg/http/transport/user_agent.go +++ b/pkg/http/transport/user_agent.go @@ -1,11 +1,19 @@ package transport import ( + "context" "net/http" "github.com/github/github-mcp-server/pkg/http/headers" ) +type userAgentKey struct{} + +// WithUserAgent supplies a request-scoped identity without mutating a shared transport. +func WithUserAgent(ctx context.Context, agent string) context.Context { + return context.WithValue(ctx, userAgentKey{}, agent) +} + type UserAgentTransport struct { Transport http.RoundTripper Agent string @@ -13,6 +21,10 @@ type UserAgentTransport struct { func (t *UserAgentTransport) RoundTrip(req *http.Request) (*http.Response, error) { req = req.Clone(req.Context()) - req.Header.Set(headers.UserAgentHeader, t.Agent) + agent := t.Agent + if scoped, ok := req.Context().Value(userAgentKey{}).(string); ok { + agent = scoped + } + req.Header.Set(headers.UserAgentHeader, agent) return t.Transport.RoundTrip(req) } diff --git a/pkg/http/transport/user_agent_test.go b/pkg/http/transport/user_agent_test.go new file mode 100644 index 0000000000..58eee9b699 --- /dev/null +++ b/pkg/http/transport/user_agent_test.go @@ -0,0 +1,48 @@ +package transport + +import ( + "context" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestUserAgentTransportRequestIsolation(t *testing.T) { + t.Parallel() + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + _, _ = io.WriteString(w, req.UserAgent()) + })) + t.Cleanup(server.Close) + transport := &UserAgentTransport{ + Transport: http.DefaultTransport, + Agent: "github-mcp-server/remote-abcdef", + } + client := &http.Client{Transport: transport} + + for _, agent := range []string{"", "github-mcp-server/v1.2.3 (first/1)", "github-mcp-server/v1.2.3 (second/2)"} { + t.Run(agent, func(t *testing.T) { + t.Parallel() + ctx := context.Background() + want := transport.Agent + if agent != "" { + ctx = WithUserAgent(ctx, agent) + want = agent + } + req, err := http.NewRequestWithContext(ctx, http.MethodGet, server.URL, nil) + require.NoError(t, err) + req.Header.Set("User-Agent", "original") + resp, err := client.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + assert.Equal(t, want, string(body)) + assert.Equal(t, "original", req.UserAgent()) + assert.Equal(t, "github-mcp-server/remote-abcdef", transport.Agent) + }) + } +} From 63f559545320fa72eaf184c4a42e05904c09e817 Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Thu, 1 Oct 2026 15:54:18 +0200 Subject: [PATCH 02/13] Always enable MCP Apps UI; remove remote_mcp_ui_apps flag gate (#3348) * Always enable MCP Apps UI; remove remote_mcp_ui_apps flag gate The remote_mcp_ui_apps flag is fully rolled out on the hosted server, but the OSS server still defaulted it off. Make MCP Apps unconditional: - _meta.ui is always emitted, stripped only when the client explicitly does not advertise the io.modelcontextprotocol/ui capability - ui_get is always registered - write tools defer to MCP App forms by default (still opt-out via mcp_apps_disable_form_deferral) - remove MCPAppsFeatureFlag and its AllowedFeatureFlags/InsidersFeatureFlags entries - update tests and regenerate docs Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Remove unused Inventory.checkFeatureFlag Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Omit app-only tools for clients without MCP Apps support When a client does not advertise io.modelcontextprotocol/ui, stripping _meta.ui from ui_get turned it into an ordinary model-visible tool, violating its app-only contract. Now that ui_get is no longer feature gated, omit tools whose ui.visibility excludes "model" instead. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- README.md | 10 +++ cmd/github-mcp-server/generate_docs.go | 4 +- docs/feature-flags.md | 64 ---------------- docs/insiders-features.md | 89 ---------------------- docs/server-configuration.md | 53 ++----------- pkg/github/feature_flags.go | 5 -- pkg/github/feature_flags_benchmark_test.go | 2 +- pkg/github/feature_flags_test.go | 12 +-- pkg/github/issues_test.go | 2 +- pkg/github/pullrequests_test.go | 4 +- pkg/github/server.go | 7 +- pkg/github/tools_validation_test.go | 6 -- pkg/github/ui_capability.go | 7 +- pkg/github/ui_capability_test.go | 19 +---- pkg/github/ui_tools.go | 1 - pkg/github/ui_tools_test.go | 23 +++++- pkg/http/handler_test.go | 75 ++++++------------ pkg/http/server_test.go | 10 +-- pkg/inventory/builder.go | 38 +++++++-- pkg/inventory/filters.go | 6 -- pkg/inventory/registry.go | 82 ++++---------------- pkg/inventory/registry_test.go | 88 +++++++++++---------- 22 files changed, 181 insertions(+), 426 deletions(-) diff --git a/README.md b/README.md index 221211dc58..01cfd47ee1 100644 --- a/README.md +++ b/README.md @@ -710,6 +710,7 @@ The following sets of tools are available: person Context - **get_me** - Get my user profile + - **MCP App UI**: `ui://github-mcp-server/get-me` - No parameters required - **get_team_members** - Get team members @@ -721,6 +722,12 @@ The following sets of tools are available: - **OAuth Challenge Scopes**: `read:org` - `user`: Username to get teams for. If not provided, uses the authenticated user. (string, optional) +- **ui_get** - Get UI data + - **OAuth Challenge Scopes**: `repo`, `read:org` + - `method`: The type of data to fetch (string, required) + - `owner`: Repository owner (required for all methods) (string, required) + - `repo`: Repository name (required for labels, assignees, milestones, branches, issue fields, reviewers) (string, optional) +
@@ -989,6 +996,7 @@ The following sets of tools are available: - **issue_write** - Create or update issue/pull request - **OAuth Challenge Scopes**: `repo` + - **MCP App UI**: `ui://github-mcp-server/issue-write` - `assignees`: Usernames to assign to this issue (string[], optional) - `body`: Issue body content (string, optional) - `duplicate_of`: Issue number that this issue is a duplicate of. Required when state_reason is 'duplicate'. (number, optional) @@ -1244,6 +1252,7 @@ The following sets of tools are available: - **create_pull_request** - Open new pull request - **OAuth Challenge Scopes**: `repo` + - **MCP App UI**: `ui://github-mcp-server/pr-write` - `base`: Branch to merge into (string, required) - `body`: PR description (string, optional) - `draft`: Create as draft PR (boolean, optional) @@ -1322,6 +1331,7 @@ The following sets of tools are available: - **update_pull_request** - Edit pull request - **OAuth Challenge Scopes**: `repo` + - **MCP App UI**: `ui://github-mcp-server/pr-edit` - `base`: New base branch name (string, optional) - `body`: New description (string, optional) - `draft`: Mark pull request as draft (true) or ready for review (false) (boolean, optional) diff --git a/cmd/github-mcp-server/generate_docs.go b/cmd/github-mcp-server/generate_docs.go index a9a6c20f83..736513f6f1 100644 --- a/cmd/github-mcp-server/generate_docs.go +++ b/cmd/github-mcp-server/generate_docs.go @@ -222,9 +222,7 @@ func writeToolDoc(buf *strings.Builder, tool inventory.ServerTool) { fmt.Fprintf(buf, " - **OAuth Challenge Scopes**: `%s`\n", strings.Join(scopes, "`, `")) } - // MCP App UI metadata (only rendered when the remote_mcp_ui_apps flag - // applied to the inventory; for the no-flags README this section is - // stripped by inventory.ToolsForRegistration before rendering). + // MCP App UI metadata. if ui, ok := tool.Tool.Meta["ui"].(map[string]any); ok { if uri, ok := ui["resourceUri"].(string); ok && uri != "" { fmt.Fprintf(buf, " - **MCP App UI**: `%s`\n", uri) diff --git a/docs/feature-flags.md b/docs/feature-flags.md index ec5282bd54..e48c2488b5 100644 --- a/docs/feature-flags.md +++ b/docs/feature-flags.md @@ -86,70 +86,6 @@ as output formatting) won't appear here. -### `remote_mcp_ui_apps` - -- **create_pull_request** - Open new pull request - - **OAuth Challenge Scopes**: `repo` - - **MCP App UI**: `ui://github-mcp-server/pr-write` - - `base`: Branch to merge into (string, required) - - `body`: PR description (string, optional) - - `draft`: Create as draft PR (boolean, optional) - - `head`: Branch containing changes (string, required) - - `maintainer_can_modify`: Allow maintainer edits (boolean, optional) - - `owner`: Repository owner (string, required) - - `repo`: Repository name (string, required) - - `reviewers`: GitHub usernames or ORG/team-slug team reviewers to request reviews from (string[], optional) - - `title`: PR title (string, required) - -- **get_me** - Get my user profile - - **MCP App UI**: `ui://github-mcp-server/get-me` - - No parameters required - -- **issue_write** - Create or update issue/pull request - - **OAuth Challenge Scopes**: `repo` - - **MCP App UI**: `ui://github-mcp-server/issue-write` - - `assignees`: Usernames to assign to this issue (string[], optional) - - `body`: Issue body content (string, optional) - - `duplicate_of`: Issue number that this issue is a duplicate of. Required when state_reason is 'duplicate'. (number, optional) - - `issue_fields`: Issue field values to set or clear. Each item requires 'field_name' and exactly one of 'value', 'field_option_name', or 'delete: true'. (object[], optional) - - `issue_number`: Issue number to update (number, optional) - - `labels`: Labels to apply to this issue (string[], optional) - - `method`: Write operation to perform on a single issue. - Options are: - - 'create' - creates a new issue. - - 'update' - updates an existing issue. - (string, required) - - `milestone`: Milestone number (number, optional) - - `owner`: Repository owner (string, required) - - `parent_issue_number`: Issue number of the parent issue. Only used when method is 'create' and cannot be combined with issue_fields. The new issue is created and attached to this parent in the same operation. (number, optional) - - `parent_owner`: Repository owner of the parent issue. Must be provided with parent_repo. Omit both to use owner and repo. Only used when method is 'create' and parent_issue_number is provided. (string, optional) - - `parent_repo`: Repository name of the parent issue. Must be provided with parent_owner. Omit both to use owner and repo. Only used when method is 'create' and parent_issue_number is provided. (string, optional) - - `repo`: Repository name (string, required) - - `state`: New state (string, optional) - - `state_reason`: Reason for the state change. Ignored unless state is changed. (string, optional) - - `title`: Issue title (string, optional) - - `type`: Type of this issue. For updates, pass null to remove the current type. Only use if issue types are enabled for this repository. Use list_issue_types to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter. (string | null, optional) - -- **ui_get** - Get UI data - - **OAuth Challenge Scopes**: `repo`, `read:org` - - `method`: The type of data to fetch (string, required) - - `owner`: Repository owner (required for all methods) (string, required) - - `repo`: Repository name (required for labels, assignees, milestones, branches, issue fields, reviewers) (string, optional) - -- **update_pull_request** - Edit pull request - - **OAuth Challenge Scopes**: `repo` - - **MCP App UI**: `ui://github-mcp-server/pr-edit` - - `base`: New base branch name (string, optional) - - `body`: New description (string, optional) - - `draft`: Mark pull request as draft (true) or ready for review (false) (boolean, optional) - - `maintainer_can_modify`: Allow maintainer edits (boolean, optional) - - `owner`: Repository owner (string, required) - - `pullNumber`: Pull request number to update (number, required) - - `repo`: Repository name (string, required) - - `reviewers`: GitHub usernames or ORG/team-slug team reviewers to request reviews from (string[], optional) - - `state`: New state (string, optional) - - `title`: New title (string, optional) - ### `issues_granular` - **add_issue_comment_reaction** - Add Reaction to Issue or Pull Request Comment diff --git a/docs/insiders-features.md b/docs/insiders-features.md index 4255e5bdd9..5738252c7c 100644 --- a/docs/insiders-features.md +++ b/docs/insiders-features.md @@ -26,70 +26,6 @@ The list below is generated from the Go source. It covers tool **inventory and s -### `remote_mcp_ui_apps` - -- **create_pull_request** - Open new pull request - - **OAuth Challenge Scopes**: `repo` - - **MCP App UI**: `ui://github-mcp-server/pr-write` - - `base`: Branch to merge into (string, required) - - `body`: PR description (string, optional) - - `draft`: Create as draft PR (boolean, optional) - - `head`: Branch containing changes (string, required) - - `maintainer_can_modify`: Allow maintainer edits (boolean, optional) - - `owner`: Repository owner (string, required) - - `repo`: Repository name (string, required) - - `reviewers`: GitHub usernames or ORG/team-slug team reviewers to request reviews from (string[], optional) - - `title`: PR title (string, required) - -- **get_me** - Get my user profile - - **MCP App UI**: `ui://github-mcp-server/get-me` - - No parameters required - -- **issue_write** - Create or update issue/pull request - - **OAuth Challenge Scopes**: `repo` - - **MCP App UI**: `ui://github-mcp-server/issue-write` - - `assignees`: Usernames to assign to this issue (string[], optional) - - `body`: Issue body content (string, optional) - - `duplicate_of`: Issue number that this issue is a duplicate of. Required when state_reason is 'duplicate'. (number, optional) - - `issue_fields`: Issue field values to set or clear. Each item requires 'field_name' and exactly one of 'value', 'field_option_name', or 'delete: true'. (object[], optional) - - `issue_number`: Issue number to update (number, optional) - - `labels`: Labels to apply to this issue (string[], optional) - - `method`: Write operation to perform on a single issue. - Options are: - - 'create' - creates a new issue. - - 'update' - updates an existing issue. - (string, required) - - `milestone`: Milestone number (number, optional) - - `owner`: Repository owner (string, required) - - `parent_issue_number`: Issue number of the parent issue. Only used when method is 'create' and cannot be combined with issue_fields. The new issue is created and attached to this parent in the same operation. (number, optional) - - `parent_owner`: Repository owner of the parent issue. Must be provided with parent_repo. Omit both to use owner and repo. Only used when method is 'create' and parent_issue_number is provided. (string, optional) - - `parent_repo`: Repository name of the parent issue. Must be provided with parent_owner. Omit both to use owner and repo. Only used when method is 'create' and parent_issue_number is provided. (string, optional) - - `repo`: Repository name (string, required) - - `state`: New state (string, optional) - - `state_reason`: Reason for the state change. Ignored unless state is changed. (string, optional) - - `title`: Issue title (string, optional) - - `type`: Type of this issue. For updates, pass null to remove the current type. Only use if issue types are enabled for this repository. Use list_issue_types to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter. (string | null, optional) - -- **ui_get** - Get UI data - - **OAuth Challenge Scopes**: `repo`, `read:org` - - `method`: The type of data to fetch (string, required) - - `owner`: Repository owner (required for all methods) (string, required) - - `repo`: Repository name (required for labels, assignees, milestones, branches, issue fields, reviewers) (string, optional) - -- **update_pull_request** - Edit pull request - - **OAuth Challenge Scopes**: `repo` - - **MCP App UI**: `ui://github-mcp-server/pr-edit` - - `base`: New base branch name (string, optional) - - `body`: New description (string, optional) - - `draft`: Mark pull request as draft (true) or ready for review (false) (boolean, optional) - - `maintainer_can_modify`: Allow maintainer edits (boolean, optional) - - `owner`: Repository owner (string, required) - - `pullNumber`: Pull request number to update (number, required) - - `repo`: Repository name (string, required) - - `reviewers`: GitHub usernames or ORG/team-slug team reviewers to request reviews from (string[], optional) - - `state`: New state (string, optional) - - `title`: New title (string, optional) - ### `file_blame` - **get_file_blame** - Get file blame information @@ -139,31 +75,6 @@ The list below is generated from the Go source. It covers tool **inventory and s --- -## MCP Apps - -[MCP Apps](https://modelcontextprotocol.io/docs/extensions/apps) is an extension to the Model Context Protocol that enables servers to deliver interactive user interfaces to end users. Instead of returning plain text that the LLM must interpret and relay, tools can render forms, profiles, and dashboards right in the chat using MCP Apps. - -This means you can interact with GitHub visually: fill out forms to create issues, see user profiles with avatars, open pull requests — all without leaving your agent chat. - -### Supported tools - -The following tools have MCP Apps UIs: - -| Tool | Description | -|------|-------------| -| `get_me` | Displays your GitHub user profile with avatar, bio, and stats in a rich card | -| `issue_write` | Opens an interactive form to create or update issues | -| `create_pull_request` | Provides a full PR creation form to create a pull request (or a draft pull request) | - -### Client requirements - -MCP Apps requires a host that supports the [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps). Currently tested and working with: - -- **VS Code Insiders** — enable via the `chat.mcp.apps.enabled` setting -- **Visual Studio Code** — enable via the `chat.mcp.apps.enabled` setting - ---- - ## CSV output for list tools CSV output mode returns supported list tool responses as CSV instead of JSON. This is intended to reduce response context for agents when scanning or summarising lists of GitHub data. diff --git a/docs/server-configuration.md b/docs/server-configuration.md index 42584362d9..25c641153e 100644 --- a/docs/server-configuration.md +++ b/docs/server-configuration.md @@ -345,7 +345,7 @@ As an intentional exception, content authored by trusted bot accounts (currently **Best for:** Users who want early access to experimental features and new tools before they reach general availability. -Insiders Mode unlocks experimental features, such as [MCP Apps](#mcp-apps) support. We created this mode to have a way to roll out experimental features and collect feedback. So if you are using Insiders, please don't hesitate to share your feedback with us! Features in Insiders Mode may change, evolve, or be removed based on user feedback. +Insiders Mode unlocks experimental features, such as [CSV output for list tools](./insiders-features.md#csv-output-for-list-tools). We created this mode to have a way to roll out experimental features and collect feedback. So if you are using Insiders, please don't hesitate to share your feedback with us! Features in Insiders Mode may change, evolve, or be removed based on user feedback. @@ -402,18 +402,18 @@ See [Insiders Features](./insiders-features.md) for a full list of what's availa [MCP Apps](https://modelcontextprotocol.io/docs/extensions/apps) is an extension to the Model Context Protocol that enables servers to deliver interactive user interfaces to end users. Instead of returning plain text that the LLM must interpret and relay, tools can render forms, profiles, and dashboards right in the chat. -MCP Apps is enabled by [Insiders Mode](#insiders-mode), or independently via the `remote_mcp_ui_apps` feature flag. +MCP Apps is enabled by default. Tools with MCP Apps UIs advertise them via `_meta.ui` to clients that support the [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps); the metadata is omitted for clients that do not advertise the `io.modelcontextprotocol/ui` capability. To keep MCP App result views enabled while making write tools execute directly -instead of first opening an interactive form, also enable the -`mcp_apps_disable_form_deferral` feature flag. For the remote server, send both -flags in the request header: +instead of first opening an interactive form, enable the +`mcp_apps_disable_form_deferral` feature flag. For the remote server, send the +flag in the request header: ```http -X-MCP-Features: remote_mcp_ui_apps,mcp_apps_disable_form_deferral +X-MCP-Features: mcp_apps_disable_form_deferral ``` -For the local server, pass both flags to `--features`. +For the local server, pass it to `--features`. **Supported tools:** @@ -422,47 +422,10 @@ For the local server, pass both flags to `--features`. | `get_me` | Displays your GitHub user profile with avatar, bio, and stats in a rich card | | `issue_write` | Opens an interactive form to create or update issues | | `create_pull_request` | Provides a full PR creation form to create a pull request (or a draft pull request) | +| `update_pull_request` | Opens an interactive form to edit a pull request | **Client requirements:** MCP Apps requires a host that supports the [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps). Currently tested with VS Code (`chat.mcp.apps.enabled` setting). -
Remote ServerLocal Server
- - - - - -
Remote ServerLocal Server
- -```json -{ - "type": "http", - "url": "https://api.githubcopilot.com/mcp/", - "headers": { - "X-MCP-Features": "remote_mcp_ui_apps" - } -} -``` - - - -```json -{ - "type": "stdio", - "command": "go", - "args": [ - "run", - "./cmd/github-mcp-server", - "stdio", - "--features=remote_mcp_ui_apps" - ], - "env": { - "GITHUB_PERSONAL_ACCESS_TOKEN": "${input:github_token}" - } -} -``` - -
- --- ### Scope Filtering diff --git a/pkg/github/feature_flags.go b/pkg/github/feature_flags.go index 94f335ebe5..321d8fa3bb 100644 --- a/pkg/github/feature_flags.go +++ b/pkg/github/feature_flags.go @@ -6,9 +6,6 @@ import ( "github.com/github/github-mcp-server/pkg/inventory" ) -// MCPAppsFeatureFlag is the feature flag name for MCP Apps (interactive UI forms). -const MCPAppsFeatureFlag = "remote_mcp_ui_apps" - // MCPAppsDisableFormDeferralFeatureFlag disables handing write-tool calls off // to MCP App forms while preserving MCP Apps UI metadata and result views. const MCPAppsDisableFormDeferralFeatureFlag = "mcp_apps_disable_form_deferral" @@ -47,7 +44,6 @@ const FeatureFlagThreadResolutionReason = "thread_resolution_reason" // Only flags in this list are accepted; unknown flags are silently ignored. // This is the single source of truth for which flags are user-controllable. var AllowedFeatureFlags = []string{ - MCPAppsFeatureFlag, MCPAppsDisableFormDeferralFeatureFlag, FeatureFlagCSVOutput, FeatureFlagIFCLabels, @@ -64,7 +60,6 @@ var AllowedFeatureFlags = []string{ // This is the single source of truth for what "insiders" means in terms of // feature flag expansion. var InsidersFeatureFlags = []string{ - MCPAppsFeatureFlag, FeatureFlagCSVOutput, FeatureFlagFileBlame, FeatureFlagIssueDependencies, diff --git a/pkg/github/feature_flags_benchmark_test.go b/pkg/github/feature_flags_benchmark_test.go index 79aa17d55d..97d4951ca6 100644 --- a/pkg/github/feature_flags_benchmark_test.go +++ b/pkg/github/feature_flags_benchmark_test.go @@ -146,7 +146,7 @@ func featureBenchmarkDistributions() []featureBenchmarkDistribution { { name: "mixed", enabled: map[string]bool{ - MCPAppsFeatureFlag: true, + FeatureFlagCSVOutput: true, FeatureFlagFileBlame: true, FeatureFlagIssuesGranular: true, FeatureFlagIssueDependencies: true, diff --git a/pkg/github/feature_flags_test.go b/pkg/github/feature_flags_test.go index cc3fbf0837..241d02c5dd 100644 --- a/pkg/github/feature_flags_test.go +++ b/pkg/github/feature_flags_test.go @@ -149,12 +149,12 @@ func TestResolveFeatureFlags(t *testing.T) { name: "no features, no insiders", enabledFeatures: nil, expectedFlags: nil, - unexpectedFlags: []string{MCPAppsFeatureFlag}, + unexpectedFlags: []string{FeatureFlagCSVOutput}, }, { name: "explicit feature enabled", - enabledFeatures: []string{MCPAppsFeatureFlag}, - expectedFlags: []string{MCPAppsFeatureFlag}, + enabledFeatures: []string{FeatureFlagCSVOutput}, + expectedFlags: []string{FeatureFlagCSVOutput}, }, { name: "MCP Apps form deferral can be disabled directly", @@ -191,8 +191,8 @@ func TestResolveFeatureFlags(t *testing.T) { }, { name: "mix of known and unknown flags", - enabledFeatures: []string{MCPAppsFeatureFlag, "unknown_flag"}, - expectedFlags: []string{MCPAppsFeatureFlag}, + enabledFeatures: []string{FeatureFlagCSVOutput, "unknown_flag"}, + expectedFlags: []string{FeatureFlagCSVOutput}, unexpectedFlags: []string{"unknown_flag"}, }, { @@ -214,7 +214,7 @@ func TestResolveFeatureFlags(t *testing.T) { }, { name: "explicit plus insiders deduplicates", - enabledFeatures: []string{MCPAppsFeatureFlag}, + enabledFeatures: []string{FeatureFlagCSVOutput}, insidersMode: true, expectedFlags: InsidersFeatureFlags, }, diff --git a/pkg/github/issues_test.go b/pkg/github/issues_test.go index 7138148c21..e2abb2e8de 100644 --- a/pkg/github/issues_test.go +++ b/pkg/github/issues_test.go @@ -2216,7 +2216,7 @@ func Test_IssueWrite_MCPAppsFeature_UIGate(t *testing.T) { deps := BaseDeps{ Client: client, GQLClient: githubv4.NewClient(nil), - featureChecker: featureCheckerFor(MCPAppsFeatureFlag), + featureChecker: featureCheckerFor(), } handler := serverTool.Handler(deps) diff --git a/pkg/github/pullrequests_test.go b/pkg/github/pullrequests_test.go index c0e392aea6..88facf8c51 100644 --- a/pkg/github/pullrequests_test.go +++ b/pkg/github/pullrequests_test.go @@ -2966,7 +2966,7 @@ func Test_CreatePullRequest_MCPAppsFeature_UIGate(t *testing.T) { deps := BaseDeps{ Client: client, GQLClient: githubv4.NewClient(nil), - featureChecker: featureCheckerFor(MCPAppsFeatureFlag), + featureChecker: featureCheckerFor(), } handler := serverTool.Handler(deps) @@ -3068,7 +3068,7 @@ func Test_UpdatePullRequest_MCPAppsFeature_UIGate(t *testing.T) { deps := BaseDeps{ Client: client, GQLClient: githubv4.NewClient(nil), - featureChecker: featureCheckerFor(MCPAppsFeatureFlag), + featureChecker: featureCheckerFor(), } handler := serverTool.Handler(deps) diff --git a/pkg/github/server.go b/pkg/github/server.go index 6e9b5b7566..4ce1f965b1 100644 --- a/pkg/github/server.go +++ b/pkg/github/server.go @@ -125,10 +125,9 @@ func NewMCPServer(ctx context.Context, cfg *MCPServerConfig, deps ToolDependenci inv.RegisterAll(ctx, ghServer, deps, cfg.ToolHandlerMiddleware...) // Register MCP App UI resources whenever the embedded UI assets are - // available. The resources are static HTML and are only referenced by - // tools when the remote_mcp_ui_apps feature flag is enabled for the - // request (the inventory strips the _meta.ui block otherwise via - // stripMCPAppsMetadata), so registering them unconditionally is safe. + // available. The resources are static HTML referenced by tools' _meta.ui + // block (the inventory strips that block for clients that do not support + // MCP Apps), so registering them unconditionally is safe. // Registering here — rather than in the stdio bootstrap — ensures the // remote/HTTP server also serves them, fixing the "-32002 Resource not // found" error clients hit after the tool returns a ui:// URI. diff --git a/pkg/github/tools_validation_test.go b/pkg/github/tools_validation_test.go index 3eacc8e1c2..dc3e758dd3 100644 --- a/pkg/github/tools_validation_test.go +++ b/pkg/github/tools_validation_test.go @@ -234,12 +234,6 @@ func TestFeatureRulesOverlap(t *testing.T) { assert.False(t, featureDeclarationsOverlap([]inventory.ServerTool{enabled, disabled})) } -func TestMCPAppsFeatureFlagMatchesInventory(t *testing.T) { - inv, err := NewInventory(stubTranslation).Build() - require.NoError(t, err) - assert.Contains(t, inv.RequiredFeatures(), inventory.FeatureFlag(MCPAppsFeatureFlag)) -} - // TestNoDuplicateResourceNames ensures all resources have unique names func TestNoDuplicateResourceNames(t *testing.T) { resources := AllResources(stubTranslation) diff --git a/pkg/github/ui_capability.go b/pkg/github/ui_capability.go index 3de6a39b74..b4f5ddf281 100644 --- a/pkg/github/ui_capability.go +++ b/pkg/github/ui_capability.go @@ -62,16 +62,15 @@ func hasNonFormParams(args map[string]any, formParams map[string]struct{}) bool // shouldDeferToForm is the single source of truth for the show/defer decision // shared by the form-backed write tools (create_pull_request, // update_pull_request, issue_write). It reports whether a call should be handed -// off to its MCP App form instead of executing now: defer only when MCP Apps -// are enabled, form deferral has not been disabled, the client can render UI, +// off to its MCP App form instead of executing now: defer only when form +// deferral has not been disabled, the client can render UI, // the call is not itself a form submission, and every supplied parameter can // be represented by the form (formParams is the tool's form-parameter // allowlist). When it returns false the handler executes directly; the host may // still render the tool's view, which renders the result rather than an input // form. func shouldDeferToForm(ctx context.Context, deps ToolDependencies, req *mcp.CallToolRequest, args map[string]any, formParams map[string]struct{}) bool { - return deps.IsFeatureEnabled(ctx, MCPAppsFeatureFlag) && - !deps.IsFeatureEnabled(ctx, MCPAppsDisableFormDeferralFeatureFlag) && + return !deps.IsFeatureEnabled(ctx, MCPAppsDisableFormDeferralFeatureFlag) && clientSupportsUI(ctx, req) && !uiSubmitted(args) && !hasNonFormParams(args, formParams) diff --git a/pkg/github/ui_capability_test.go b/pkg/github/ui_capability_test.go index 812d6fde89..7d3ee34c4a 100644 --- a/pkg/github/ui_capability_test.go +++ b/pkg/github/ui_capability_test.go @@ -100,27 +100,14 @@ func Test_shouldDeferToForm_featureFlags(t *testing.T) { want bool }{ { - name: "MCP Apps enabled defers to form", - enabledFlags: []inventory.FeatureFlag{MCPAppsFeatureFlag}, - want: true, + name: "defers to form by default", + want: true, }, { - name: "form deferral disabled executes directly", - enabledFlags: []inventory.FeatureFlag{ - MCPAppsFeatureFlag, - MCPAppsDisableFormDeferralFeatureFlag, - }, - want: false, - }, - { - name: "form deferral opt-out does not enable MCP Apps", + name: "form deferral disabled executes directly", enabledFlags: []inventory.FeatureFlag{MCPAppsDisableFormDeferralFeatureFlag}, want: false, }, - { - name: "MCP Apps disabled executes directly", - want: false, - }, } for _, tc := range tests { diff --git a/pkg/github/ui_tools.go b/pkg/github/ui_tools.go index b8d6cd31a5..60ff65a742 100644 --- a/pkg/github/ui_tools.go +++ b/pkg/github/ui_tools.go @@ -98,7 +98,6 @@ func UIGet(t translations.TranslationHelperFunc) inventory.ServerTool { return utils.NewToolResultError(fmt.Sprintf("unknown method: %s", method)), nil, nil } }) - st.FeatureRule = featureEnabledRule(MCPAppsFeatureFlag) return st } diff --git a/pkg/github/ui_tools_test.go b/pkg/github/ui_tools_test.go index d400752bdf..8bf28c3674 100644 --- a/pkg/github/ui_tools_test.go +++ b/pkg/github/ui_tools_test.go @@ -12,7 +12,7 @@ import ( "github.com/github/github-mcp-server/internal/githubv4mock" "github.com/github/github-mcp-server/internal/toolsnaps" - "github.com/github/github-mcp-server/pkg/inventory" + ghcontext "github.com/github/github-mcp-server/pkg/context" "github.com/github/github-mcp-server/pkg/translations" "github.com/google/go-github/v89/github" "github.com/google/jsonschema-go/jsonschema" @@ -106,7 +106,7 @@ func Test_UIGet(t *testing.T) { assert.Contains(t, tool.InputSchema.(*jsonschema.Schema).Properties, "repo") assert.ElementsMatch(t, tool.InputSchema.(*jsonschema.Schema).Required, []string{"method", "owner"}) assert.True(t, tool.Annotations.ReadOnlyHint, "ui_get should be read-only") - assert.Equal(t, []inventory.FeatureFlag{MCPAppsFeatureFlag}, serverTool.FeatureRule.Features()) + assert.Empty(t, serverTool.FeatureRule.Features(), "ui_get should not be feature-gated") // ui_get must be app-only so the host hides it from the agent's tool list // while keeping it callable by the views (MCP Apps 2026-01-26 spec). @@ -583,3 +583,22 @@ func Test_marshalUIGetIssueFields_TrimsForUI(t *testing.T) { textField := fields[1].(map[string]any) assert.NotContains(t, textField, "options") } + +func Test_UIGet_OmittedForClientsWithoutUISupport(t *testing.T) { + inv, err := NewInventory(translations.NullTranslationHelper).WithToolsets([]string{"all"}).Build() + require.NoError(t, err) + + hasUIGet := func(ctx context.Context) bool { + for _, tool := range inv.ToolsForRegistration(ctx) { + if tool.Tool.Name == "ui_get" { + return true + } + } + return false + } + + assert.False(t, hasUIGet(ghcontext.WithUISupport(context.Background(), false)), + "app-only ui_get must not be exposed as a model-visible tool to non-UI clients") + assert.True(t, hasUIGet(ghcontext.WithUISupport(context.Background(), true))) + assert.True(t, hasUIGet(context.Background()), "ui_get is kept when UI support is unknown") +} diff --git a/pkg/http/handler_test.go b/pkg/http/handler_test.go index ea37ec2a6a..c9fa1de095 100644 --- a/pkg/http/handler_test.go +++ b/pkg/http/handler_test.go @@ -1299,62 +1299,36 @@ func TestSubscriptionsListenIsRejected(t *testing.T) { } } -// TestInsidersRoutePreservesUIMeta is a regression test for the bug where -// _meta.ui was stripped from tools/list responses on the HTTP /insiders route. -// -// Before the fix: -// - buildStaticInventory called Build() on a builder configured with the -// HTTP feature checker (which reads insiders mode from the request ctx). -// - Build() invoked checkFeatureFlag(context.Background()) — bg ctx has no -// insiders mode, so the FF reported MCP Apps off, and stripMCPAppsMetadata -// ran eagerly against the static tool slice at server startup. -// - Per-request inventory factories then served pre-stripped tools regardless -// of whether the request actually came in via /insiders. -// -// After the fix: -// - Build() no longer touches MCP Apps metadata. -// - RegisterTools applies the strip per-request, using the request context -// where the HTTP feature checker correctly observes insiders mode. -func TestInsidersRoutePreservesUIMeta(t *testing.T) { +// TestUIMetaPreservedByDefault verifies that _meta.ui is preserved in the +// registered tool surface on both the /insiders route and the default route: +// MCP Apps UI metadata is no longer gated behind a feature flag. +func TestUIMetaPreservedByDefault(t *testing.T) { const uiURI = "ui://test/widget" uiTool := mockTool("with_ui", "repos", true) uiTool.Tool.Meta = mcp.Meta{"ui": map[string]any{"resourceUri": uiURI}} - checker := createHTTPFeatureChecker(nil, false) - build := func() *inventory.Inventory { - inv, err := inventory.NewBuilder(). - SetTools([]inventory.ServerTool{uiTool}). - WithFeatureChecker(checker). - WithToolsets([]string{"all"}). - Build() - require.NoError(t, err) - return inv - } - - // Simulate a /insiders request: ctx has insiders mode set. - insidersCtx := ghcontext.WithInsidersMode(context.Background(), true) + inv, err := inventory.NewBuilder(). + SetTools([]inventory.ServerTool{uiTool}). + WithFeatureChecker(createHTTPFeatureChecker(nil, false)). + WithToolsets([]string{"all"}). + Build() + require.NoError(t, err) - // AvailableTools no longer strips _meta.ui (post-fix), regardless of ctx. - // The strip lives in RegisterTools, gated on the per-request FF check. - insidersTools := build().AvailableTools(insidersCtx) - plainTools := build().AvailableTools(context.Background()) - - // On the /insiders path, the FF check returns true → no strip → _meta preserved. - enabled, _ := checker(insidersCtx, "remote_mcp_ui_apps") - require.True(t, enabled, "FF should be on for /insiders ctx") - require.Len(t, insidersTools, 1) - require.NotNil(t, insidersTools[0].Tool.Meta, "_meta should be present on /insiders") - require.Equal(t, uiURI, insidersTools[0].Tool.Meta["ui"].(map[string]any)["resourceUri"]) - - // On the non-insiders path, RegisterTools strips _meta.ui. - plainEnabled, _ := checker(context.Background(), "remote_mcp_ui_apps") - require.False(t, plainEnabled, "FF should be off for non-insiders ctx") - require.Len(t, plainTools, 1) + for name, ctx := range map[string]context.Context{ + "insiders": ghcontext.WithInsidersMode(context.Background(), true), + "default": context.Background(), + } { + t.Run(name, func(t *testing.T) { + tools := inv.ToolsForRegistration(ctx) + require.Len(t, tools, 1) + require.NotNil(t, tools[0].Tool.Meta, "_meta should be present") + require.Equal(t, uiURI, tools[0].Tool.Meta["ui"].(map[string]any)["resourceUri"]) + }) + } } -// TestUIMetaStrippedWhenClientLacksCapability verifies that even on the -// /insiders path (where the feature flag is on), UI metadata is stripped from -// tools/list responses when the client did NOT advertise the +// TestUIMetaStrippedWhenClientLacksCapability verifies that UI metadata is +// stripped from tools/list responses when the client did NOT advertise the // io.modelcontextprotocol/ui extension capability. Per the 2026-01-26 MCP // Apps spec, servers SHOULD check client capabilities before exposing // UI-enabled tools. @@ -1387,10 +1361,9 @@ func TestUIMetaStrippedWhenClientLacksCapability(t *testing.T) { require.NotNil(t, preserved[0].Tool.Meta["ui"], "_meta.ui should be preserved when client advertises UI capability") require.Equal(t, uiURI, preserved[0].Tool.Meta["ui"].(map[string]any)["resourceUri"]) - // Unknown capability falls through to the FF gate (insiders ctx → kept). unknown := build().ToolsForRegistration(insidersCtx) require.Len(t, unknown, 1) - require.NotNil(t, unknown[0].Tool.Meta["ui"], "_meta.ui should be preserved when capability is unknown and FF is on") + require.NotNil(t, unknown[0].Tool.Meta["ui"], "_meta.ui should be preserved when capability is unknown") } // TestMaxRequestBodyBytes checks the effective limit and, critically, that it diff --git a/pkg/http/server_test.go b/pkg/http/server_test.go index 500bb40611..903194082e 100644 --- a/pkg/http/server_test.go +++ b/pkg/http/server_test.go @@ -374,12 +374,6 @@ func TestCreateHTTPFeatureChecker(t *testing.T) { headerFeatures: []string{github.FeatureFlagPullRequestsGranular}, wantEnabled: true, }, - { - name: "MCP Apps flag accepted from header", - flagName: github.MCPAppsFeatureFlag, - headerFeatures: []string{github.MCPAppsFeatureFlag}, - wantEnabled: true, - }, { name: "MCP Apps form deferral opt-out accepted from header", flagName: github.MCPAppsDisableFormDeferralFeatureFlag, @@ -417,8 +411,8 @@ func TestCreateHTTPFeatureChecker(t *testing.T) { wantEnabled: false, }, { - name: "insiders mode enables MCP Apps without header", - flagName: github.MCPAppsFeatureFlag, + name: "insiders mode enables CSV output without header", + flagName: github.FeatureFlagCSVOutput, insidersMode: true, wantEnabled: true, }, diff --git a/pkg/inventory/builder.go b/pkg/inventory/builder.go index 60bda764b6..e70df0eed3 100644 --- a/pkg/inventory/builder.go +++ b/pkg/inventory/builder.go @@ -14,11 +14,6 @@ var ( ErrUnknownTools = errors.New("unknown tools specified in WithTools") ) -// mcpAppsFeatureFlag is the feature flag name that controls MCP Apps UI metadata. -// This is defined here to avoid importing pkg/github (which imports pkg/inventory). -// The value must match github.MCPAppsFeatureFlag. -const mcpAppsFeatureFlag FeatureFlag = "remote_mcp_ui_apps" - // ToolFilter is a function that determines if a tool should be included. // Returns true if the tool should be included, false to exclude it. type ToolFilter func(ctx context.Context, tool *ServerTool) (bool, error) @@ -374,16 +369,21 @@ func (b *Builder) processToolsets() (map[ToolsetID]bool, []string, []ToolsetID, return enabledToolsets, unrecognized, allToolsetIDs, validIDs, defaultToolsetIDList, descriptions } -// mcpAppsMetaKeys lists the Meta keys controlled by the remote_mcp_ui_apps feature flag. +// mcpAppsMetaKeys lists the Meta keys that carry MCP Apps UI metadata. var mcpAppsMetaKeys = []string{ "ui", // MCP Apps UI metadata } // stripMCPAppsMetadata removes MCP Apps UI metadata from tools when the -// remote_mcp_ui_apps feature flag is not enabled. +// client does not support MCP Apps UI. App-only tools (ui.visibility without +// "model") are omitted entirely rather than being exposed as ordinary +// model-visible tools. func stripMCPAppsMetadata(tools []ServerTool) []ServerTool { result := make([]ServerTool, 0, len(tools)) for _, tool := range tools { + if isAppOnlyTool(&tool) { + continue + } if stripped := stripMetaKeys(tool, mcpAppsMetaKeys); stripped != nil { result = append(result, *stripped) } else { @@ -393,6 +393,30 @@ func stripMCPAppsMetadata(tools []ServerTool) []ServerTool { return result } +// isAppOnlyTool reports whether a tool declares MCP Apps visibility that +// excludes the model (e.g. `_meta.ui.visibility: ["app"]`). Such tools are +// only meant to be called by MCP App views, never by the model directly. +func isAppOnlyTool(tool *ServerTool) bool { + ui, ok := tool.Tool.Meta["ui"].(map[string]any) + if !ok { + return false + } + var visibility []string + switch v := ui["visibility"].(type) { + case []string: + visibility = v + case []any: + for _, item := range v { + if s, ok := item.(string); ok { + visibility = append(visibility, s) + } + } + default: + return false + } + return len(visibility) > 0 && !slices.Contains(visibility, "model") +} + // stripMetaKeys removes the specified Meta keys from a single tool. // Returns a modified copy if changes were made, nil otherwise. func stripMetaKeys(tool ServerTool, keys []string) *ServerTool { diff --git a/pkg/inventory/filters.go b/pkg/inventory/filters.go index b3dfbeb912..bcc0d58c5f 100644 --- a/pkg/inventory/filters.go +++ b/pkg/inventory/filters.go @@ -16,12 +16,6 @@ func (r *Inventory) isToolsetEnabled(toolsetID ToolsetID) bool { return true } -// checkFeatureFlag checks a feature flag using the feature checker. -// Returns false if checker is nil or returns an error (errors are logged). -func (r *Inventory) checkFeatureFlag(ctx context.Context, flagName FeatureFlag) bool { - return ResolveFeature(ctx, r.featureChecker, flagName) -} - // isToolEnabled checks if a specific tool is enabled based on current filters. // Filter evaluation order: // 1. Tool.Enabled (tool self-filtering) diff --git a/pkg/inventory/registry.go b/pkg/inventory/registry.go index 9439e6f201..02dc5b0fd7 100644 --- a/pkg/inventory/registry.go +++ b/pkg/inventory/registry.go @@ -179,25 +179,18 @@ func (r *Inventory) ToolsetDescriptions() map[ToolsetID]string { // diagnostics that need the same view of the tool surface the server would // register. // -// The strip applies when EITHER of the following is true: -// -// - The remote_mcp_ui_apps feature flag is not enabled in ctx (server-side gate). -// - The client explicitly did not advertise the io.modelcontextprotocol/ui -// extension capability (per the 2026-01-26 MCP Apps spec, servers SHOULD -// check client capabilities before exposing UI-enabled tools). When the -// capability is unknown (e.g. stdio paths that do not populate the -// context flag) the feature-flag gate is the sole source of truth. +// MCP Apps UI metadata is stripped only when the client explicitly did not +// advertise the io.modelcontextprotocol/ui extension capability (per the +// 2026-01-26 MCP Apps spec, servers SHOULD check client capabilities before +// exposing UI-enabled tools). In that case app-only tools (whose +// _meta.ui.visibility excludes "model") are omitted entirely. When the +// capability is unknown (e.g. stdio paths that do not populate the context +// flag) the metadata is kept. func (r *Inventory) ToolsForRegistration(ctx context.Context) []ServerTool { ctx = WithFeatureState(ctx, r.featureChecker) tools := r.availableTools(ctx) - if present, checkFeature := mcpAppsMetadataStatus(ctx, tools); present { - featureEnabled := false - if checkFeature { - featureEnabled = r.checkFeatureFlag(ctx, mcpAppsFeatureFlag) - } - if shouldStripMCPAppsMetadata(ctx, featureEnabled) { - tools = stripMCPAppsMetadata(tools) - } + if shouldStripMCPAppsMetadata(ctx) { + tools = stripMCPAppsMetadata(tools) } return tools } @@ -215,9 +208,6 @@ func (r *Inventory) RequiredFeatures() []FeatureFlag { for i := range r.prompts { features = appendFeatureDeclaration(features, r.prompts[i].FeatureRule) } - if r.usesMCPAppsMetadata() { - features = appendUniqueFeature(features, mcpAppsFeatureFlag) - } slices.Sort(features) return features } @@ -228,37 +218,6 @@ func (r *Inventory) WithFeatureState(ctx context.Context) context.Context { return WithFeatureState(ctx, r.featureChecker) } -func (r *Inventory) usesMCPAppsMetadata() bool { - for i := range r.tools { - if toolUsesMCPAppsMetadata(&r.tools[i]) { - return true - } - } - return false -} - -func mcpAppsMetadataStatus(ctx context.Context, tools []ServerTool) (present, checkFeature bool) { - for i := range tools { - if !toolUsesMCPAppsMetadata(&tools[i]) { - continue - } - present = true - if featureDecisionForToolAvailability(ctx, tools[i].availability()) != includeToolWithoutFeatureRule { - return true, true - } - } - return present, false -} - -func toolUsesMCPAppsMetadata(tool *ServerTool) bool { - for _, key := range mcpAppsMetaKeys { - if _, ok := tool.Tool.Meta[key]; ok { - return true - } - } - return false -} - func appendFeatureDeclaration(features []FeatureFlag, rule FeatureRule) []FeatureFlag { for _, feature := range rule.features { features = appendUniqueFeature(features, feature) @@ -267,29 +226,20 @@ func appendFeatureDeclaration(features []FeatureFlag, rule FeatureRule) []Featur } // shouldStripMCPAppsMetadata centralises the strip decision so the same logic -// is exercised by tests and by RegisterTools. -func shouldStripMCPAppsMetadata(ctx context.Context, featureFlagEnabled bool) bool { - if !featureFlagEnabled { - return true - } - // Feature flag is on. Respect the client capability if it is known. - if supported, ok := ghcontext.HasUISupport(ctx); ok && !supported { - return true - } - return false +// is exercised by tests and by RegisterTools. Metadata is stripped only when +// the client is known not to support MCP Apps UI. +func shouldStripMCPAppsMetadata(ctx context.Context) bool { + supported, ok := ghcontext.HasUISupport(ctx) + return ok && !supported } // RegisterTools registers all available tools with the server using the provided dependencies. // The context is used for feature flag evaluation and client capability checks. // // MCP Apps UI metadata (`_meta.ui`) is stripped from the registered tools when -// either the MCP Apps feature flag is not enabled for this request, or the -// client did not advertise the io.modelcontextprotocol/ui extension. The +// the client did not advertise the io.modelcontextprotocol/ui extension. The // strip happens here (rather than at Build() time) so the per-request -// context is in scope — HTTP feature checkers that read insiders mode or -// user identity from ctx would otherwise see context.Background() and -// falsely report the flag off, even when the actual request arrived on the -// /insiders route. +// context, which carries the client capability, is in scope. func (r *Inventory) RegisterTools(ctx context.Context, s *mcp.Server, deps any, middleware ...ToolHandlerMiddleware) { tools := r.ToolsForRegistration(ctx) addToolAvailabilityMiddleware(s, tools) diff --git a/pkg/inventory/registry_test.go b/pkg/inventory/registry_test.go index 2e21fc632c..4e827936c6 100644 --- a/pkg/inventory/registry_test.go +++ b/pkg/inventory/registry_test.go @@ -1334,7 +1334,7 @@ func TestMetadataBehaviorRemainsLive(t *testing.T) { meta["typed_map"].(map[string]string)["value"] = "changed" meta["bytes"].([]byte)[0] = 'X' - require.Contains(t, inv.RequiredFeatures(), mcpAppsFeatureFlag) + require.Empty(t, inv.RequiredFeatures()) available := inv.AllTools() require.Equal(t, "changed", available[0].Tool.Meta["typed_map"].(map[string]string)["value"]) require.Equal(t, byte('X'), available[0].Tool.Meta["bytes"].([]byte)[0]) @@ -2088,15 +2088,14 @@ func mockToolWithMeta(name string, toolsetID string, meta map[string]any) Server ) } -func TestWithMCPApps_DisabledStripsUIMetadata(t *testing.T) { +func TestWithMCPApps_UnsupportedClientStripsUIMetadata(t *testing.T) { toolWithUI := mockToolWithMeta("tool_with_ui", "toolset1", map[string]any{ "ui": map[string]any{"html": "
hello
"}, "description": "kept", }) - // Default: MCP Apps is disabled - UI meta should be stripped on registration. reg := mustBuild(t, NewBuilder().SetTools([]ServerTool{toolWithUI}).WithToolsets([]string{"all"})) - registered := captureRegisteredTools(context.Background(), t, reg) + registered := captureRegisteredTools(ghcontext.WithUISupport(context.Background(), false), t, reg) require.Len(t, registered, 1) if registered[0].Meta["ui"] != nil { @@ -2107,27 +2106,55 @@ func TestWithMCPApps_DisabledStripsUIMetadata(t *testing.T) { } } -func TestWithMCPApps_EnabledPreservesUIMetadata(t *testing.T) { +func TestWithMCPApps_UnsupportedClientOmitsAppOnlyTools(t *testing.T) { + appOnly := mockToolWithMeta("app_only", "toolset1", map[string]any{ + "ui": map[string]any{"visibility": []string{"app"}}, + }) + appOnlyAny := mockToolWithMeta("app_only_any", "toolset1", map[string]any{ + "ui": map[string]any{"visibility": []any{"app"}}, + }) + modelAndApp := mockToolWithMeta("model_and_app", "toolset1", map[string]any{ + "ui": map[string]any{"resourceUri": "ui://x", "visibility": []string{"model", "app"}}, + }) + noVisibility := mockToolWithMeta("no_visibility", "toolset1", map[string]any{ + "ui": map[string]any{"resourceUri": "ui://y"}, + }) + reg := mustBuild(t, NewBuilder(). + SetTools([]ServerTool{appOnly, appOnlyAny, modelAndApp, noVisibility}). + WithToolsets([]string{"all"})) + + stripped := reg.ToolsForRegistration(ghcontext.WithUISupport(context.Background(), false)) + names := make([]string, 0, len(stripped)) + for _, tool := range stripped { + names = append(names, tool.Tool.Name) + require.Nil(t, tool.Tool.Meta["ui"], "ui meta should be stripped from %s", tool.Tool.Name) + } + require.ElementsMatch(t, []string{"model_and_app", "no_visibility"}, names) + + for _, ctx := range []context.Context{ + context.Background(), + ghcontext.WithUISupport(context.Background(), true), + } { + require.Len(t, reg.ToolsForRegistration(ctx), 4, "app-only tools should be kept when UI is supported or unknown") + } +} + +func TestWithMCPApps_PreservesUIMetadataByDefault(t *testing.T) { uiData := map[string]any{"html": "
hello
"} toolWithUI := mockToolWithMeta("tool_with_ui", "toolset1", map[string]any{ "ui": uiData, "description": "kept", }) - // Feature checker enables MCP Apps - UI meta should be preserved - mcpAppsChecker := func(_ context.Context, flag string) (bool, error) { - return flag == string(mcpAppsFeatureFlag), nil - } reg := mustBuild(t, NewBuilder(). SetTools([]ServerTool{toolWithUI}). - WithToolsets([]string{"all"}). - WithFeatureChecker(mcpAppsChecker)) - available := reg.AvailableTools(context.Background()) + WithToolsets([]string{"all"})) + available := reg.ToolsForRegistration(context.Background()) require.Len(t, available, 1) // UI metadata should be preserved if available[0].Tool.Meta["ui"] == nil { - t.Errorf("Expected 'ui' meta to be preserved with MCP Apps enabled") + t.Errorf("Expected 'ui' meta to be preserved") } // Other metadata should also be preserved if available[0].Tool.Meta["description"] != "kept" { @@ -2142,7 +2169,6 @@ func TestWithMCPApps_ToolsWithoutUIMetaUnaffected(t *testing.T) { }) toolNilMeta := mockTool("tool_nil_meta", "toolset1", true) - // Test with MCP Apps disabled (default) - non-UI meta should be unaffected reg := mustBuild(t, NewBuilder(). SetTools([]ServerTool{toolNoUI, toolNilMeta}). WithToolsets([]string{"all"})) @@ -2183,7 +2209,7 @@ func TestWithMCPApps_UIOnlyMetaBecomesNil(t *testing.T) { reg := mustBuild(t, NewBuilder(). SetTools([]ServerTool{toolUIOnly}). WithToolsets([]string{"all"})) - registered := captureRegisteredTools(context.Background(), t, reg) + registered := captureRegisteredTools(ghcontext.WithUISupport(context.Background(), false), t, reg) require.Len(t, registered, 1) if registered[0].Meta != nil { @@ -2311,8 +2337,8 @@ func TestWithMCPApps_DoesNotMutateOriginalTools(t *testing.T) { tool := mockToolWithMeta("test", "toolset1", originalMeta) tools := []ServerTool{tool} - // Build with MCP Apps disabled (default) - should strip ui - _ = mustBuild(t, NewBuilder().SetTools(tools).WithToolsets([]string{"all"})) + reg := mustBuild(t, NewBuilder().SetTools(tools).WithToolsets([]string{"all"})) + _ = reg.ToolsForRegistration(ghcontext.WithUISupport(context.Background(), false)) // Original tool should be unchanged require.Equal(t, "data", tools[0].Tool.Meta["ui"], "original tool should not be mutated") @@ -2484,52 +2510,36 @@ func captureRegisteredTools(ctx context.Context, t *testing.T, reg *Inventory) [ } // TestShouldStripMCPAppsMetadata verifies the spec-conformant strip decision: -// strip when the feature flag is off, OR when the client explicitly does not -// advertise the io.modelcontextprotocol/ui extension. +// strip only when the client explicitly does not advertise the +// io.modelcontextprotocol/ui extension. func TestShouldStripMCPAppsMetadata(t *testing.T) { t.Parallel() tests := []struct { name string setupCtx func() context.Context - ffOn bool want bool }{ { - name: "FF off, capability unknown -> strip", - setupCtx: context.Background, - ffOn: false, - want: true, - }, - { - name: "FF off, capability present -> strip (FF wins)", - setupCtx: func() context.Context { return ghcontext.WithUISupport(context.Background(), true) }, - ffOn: false, - want: true, - }, - { - name: "FF on, capability unknown -> keep", + name: "capability unknown -> keep", setupCtx: context.Background, - ffOn: true, want: false, }, { - name: "FF on, capability present -> keep", + name: "capability present -> keep", setupCtx: func() context.Context { return ghcontext.WithUISupport(context.Background(), true) }, - ffOn: true, want: false, }, { - name: "FF on, capability explicitly absent -> strip", + name: "capability explicitly absent -> strip", setupCtx: func() context.Context { return ghcontext.WithUISupport(context.Background(), false) }, - ffOn: true, want: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - got := shouldStripMCPAppsMetadata(tc.setupCtx(), tc.ffOn) + got := shouldStripMCPAppsMetadata(tc.setupCtx()) require.Equal(t, tc.want, got) }) } From a1f6500450302da817537cbad58e440ec4bb5a24 Mon Sep 17 00:00:00 2001 From: Johanan Date: Thu, 1 Oct 2026 17:31:36 +0200 Subject: [PATCH 03/13] Use source revisions for non-release Docker builds (#3357) Only tag builds should supply a release version. Other Docker builds use the existing linked-commit fallback instead of treating a branch or pull-request ref name as a release. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6dde41da-ec25-4370-b26c-b036d0a53b3c --- .github/workflows/docker-publish.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index b06914cfb9..6d756188a2 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -116,7 +116,7 @@ jobs: cache-to: type=gha,mode=max platforms: linux/amd64,linux/arm64 build-args: | - VERSION=${{ github.ref_name }} + VERSION=${{ github.ref_type == 'tag' && github.ref_name || 'dev' }} secrets: | oauth_client_id=${{ secrets.OAUTH_CLIENT_ID }} oauth_client_secret=${{ secrets.OAUTH_CLIENT_SECRET }} diff --git a/README.md b/README.md index 01cfd47ee1..c1f9857d1a 100644 --- a/README.md +++ b/README.md @@ -405,7 +405,7 @@ For a complete overview of all installation options, see our **[Installation Gui If you don't have Docker, you can use `go build` to build the binary in the `cmd/github-mcp-server` directory, and use the `github-mcp-server stdio` command with the `GITHUB_PERSONAL_ACCESS_TOKEN` environment variable set to your token. To specify the output location of the build, use the `-o` flag. You should configure your server to use the built executable as its `command`. -STDIO API requests identify the server as `github-mcp-server/` and retain the upstream MCP client's name/version in parentheses when available. Control characters, quotes, backslashes and parentheses in client metadata are escaped so the HTTP header remains valid; ordinary names, versions, spaces and printable Unicode are preserved. Release builds keep their release version. Source and default Docker builds with revision metadata use `vcs-`. The `-dirty` marker applies only when both the revision and modified state come from embedded VCS metadata, never to a valid, explicitly supplied `main.commit`. This is a VCS build identifier, not a release number. +STDIO API requests identify the server as `github-mcp-server/` and retain the upstream MCP client's name/version in parentheses when available. Control characters, quotes, backslashes and parentheses in client metadata are escaped so the HTTP header remains valid; ordinary names, versions, spaces and printable Unicode are preserved. Release builds keep their release version. Source and default Docker builds with revision metadata use `vcs-`. The Docker publishing workflow preserves tag versions, including prereleases, and lets branch, pull-request and nightly builds use the linked source revision. The `-dirty` marker applies only when both the revision and modified state come from embedded VCS metadata, never to a valid, explicitly supplied `main.commit`. This is a VCS build identifier, not a release number. Build the complete package with `go build -o github-mcp-server ./cmd/github-mcp-server` from a Git checkout to embed its VCS revision. For builds without VCS metadata, supply the actual release with `-ldflags '-X main.version='` or the full source revision with `-ldflags '-X main.commit='`. Valid metadata is selected in this order: explicit release, explicit source revision, embedded VCS revision, then installed main-module version. Valid explicit revisions remain authoritative even if build-context filtering changes embedded VCS metadata. Missing or malformed candidates fall through to the next usable source. If none is available, STDIO still starts with the development label `dev` and emits a warning on stderr, leaving stdout available for the MCP protocol. Supply real release or revision metadata when version-specific attribution is needed. From 85b0399bf214f24f5028d9cedecdd020f646c822 Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Thu, 1 Oct 2026 12:01:48 -0700 Subject: [PATCH 04/13] Add granular tools to hide and unhide issue comments, PR review comments and PR reviews (#3350) * Add hide_comment and unhide_comment granular issue tools Add two tools behind the issues_granular feature flag that hide (minimize) and unhide (unminimize) comments via the GraphQL minimizeComment/unminimizeComment mutations. They support issue and pull request conversation comments, pull request review comments, and pull request review bodies. Callers pass the numeric REST ID plus a comment_type; the tool resolves the GraphQL node ID via the matching REST endpoint before mutating. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Rename comment_type enum values to pull_request_review(_comment) Use `pull_request_review` and `pull_request_review_comment` instead of `review` and `review_comment` so the comment_type values are explicit about which object they refer to. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use realistic minimizedReason values in hide_comment test mocks GitHub returns minimizedReason in lowercase, hyphenated form (e.g. "off-topic") rather than echoing the classifier enum, as confirmed by an end-to-end run against the live API. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Split hide/unhide comment tools per object type Replace hide_comment/unhide_comment, which took a comment_type argument, with a granular pair per object type: - hide_issue_comment / unhide_issue_comment (issues_granular): issue comments and pull request conversation comments - hide_pull_request_review_comment / unhide_pull_request_review_comment (pull_requests_granular): inline pull request review comments - hide_pull_request_review / unhide_pull_request_review (pull_requests_granular): pull request review bodies Every argument is now unconditionally required, so the schemas no longer need prose explaining which arguments apply to which type. The six tools are built from one shared helper, parameterised by a commentVisibilityTarget that describes each object type and how to resolve its GraphQL node ID. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Validate classifier and IDs before calling the GitHub API JSON schema constraints (enum, minimum) aren't enforced when tool arguments are unmarshalled, so an invalid classifier or a negative ID previously reached GitHub and surfaced as a confusing 404 or GraphQL error. Reject them up front with a clear argument error instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/feature-flags.md | 41 ++ .../__toolsnaps__/hide_issue_comment.snap | 48 +++ .../hide_pull_request_review.snap | 54 +++ .../hide_pull_request_review_comment.snap | 48 +++ .../__toolsnaps__/unhide_issue_comment.snap | 34 ++ .../unhide_pull_request_review.snap | 40 ++ .../unhide_pull_request_review_comment.snap | 34 ++ pkg/github/comment_minimize.go | 323 +++++++++++++++ pkg/github/comment_minimize_test.go | 383 ++++++++++++++++++ pkg/github/granular_tools_test.go | 12 + pkg/github/issues_granular.go | 10 + pkg/github/pullrequests_granular.go | 20 + pkg/github/tools.go | 6 + 13 files changed, 1053 insertions(+) create mode 100644 pkg/github/__toolsnaps__/hide_issue_comment.snap create mode 100644 pkg/github/__toolsnaps__/hide_pull_request_review.snap create mode 100644 pkg/github/__toolsnaps__/hide_pull_request_review_comment.snap create mode 100644 pkg/github/__toolsnaps__/unhide_issue_comment.snap create mode 100644 pkg/github/__toolsnaps__/unhide_pull_request_review.snap create mode 100644 pkg/github/__toolsnaps__/unhide_pull_request_review_comment.snap create mode 100644 pkg/github/comment_minimize.go create mode 100644 pkg/github/comment_minimize_test.go diff --git a/docs/feature-flags.md b/docs/feature-flags.md index e48c2488b5..0d32b3c33a 100644 --- a/docs/feature-flags.md +++ b/docs/feature-flags.md @@ -120,6 +120,13 @@ as output formatting) won't appear here. - `repo`: Repository name (string, required) - `title`: Issue title (string, required) +- **hide_issue_comment** - Hide Issue Comment + - **OAuth Challenge Scopes**: `repo` + - `classifier`: The reason for hiding the comment (string, required) + - `comment_id`: The numeric ID of the issue or pull request conversation comment (number, required) + - `owner`: Repository owner (username or organization) (string, required) + - `repo`: Repository name (string, required) + - **remove_issue_comment_reaction** - Remove Reaction from Issue or Pull Request Comment - **OAuth Challenge Scopes**: `repo` - `comment_id`: The issue or pull request comment ID (number, required) @@ -157,6 +164,12 @@ as output formatting) won't appear here. - `owner`: Repository owner (username or organization) (string, required) - `repo`: Repository name (string, required) +- **unhide_issue_comment** - Unhide Issue Comment + - **OAuth Challenge Scopes**: `repo` + - `comment_id`: The numeric ID of the issue or pull request conversation comment (number, required) + - `owner`: Repository owner (username or organization) (string, required) + - `repo`: Repository name (string, required) + - **update_issue_assignees** - Update Issue Assignees - **OAuth Challenge Scopes**: `repo` - `assignees`: GitHub usernames to assign to this issue. ([], required) @@ -251,6 +264,21 @@ as output formatting) won't appear here. - `pullNumber`: The pull request number (number, required) - `repo`: Repository name (string, required) +- **hide_pull_request_review** - Hide Pull Request Review + - **OAuth Challenge Scopes**: `repo` + - `classifier`: The reason for hiding the comment (string, required) + - `owner`: Repository owner (username or organization) (string, required) + - `pullNumber`: The pull request number (number, required) + - `repo`: Repository name (string, required) + - `review_id`: The numeric ID of the pull request review (number, required) + +- **hide_pull_request_review_comment** - Hide Pull Request Review Comment + - **OAuth Challenge Scopes**: `repo` + - `classifier`: The reason for hiding the comment (string, required) + - `comment_id`: The numeric pull request review comment ID. Use the number from a #discussion_r... anchor, not the GraphQL thread node ID (PRRT_...). (number, required) + - `owner`: Repository owner (username or organization) (string, required) + - `repo`: Repository name (string, required) + - **remove_pull_request_review_comment_reaction** - Remove Pull Request Review Comment Reaction - **OAuth Challenge Scopes**: `repo` - `comment_id`: The numeric pull request review comment ID. Use the number from a #discussion_r... anchor, not the GraphQL thread node ID (PRRT_...). (number, required) @@ -277,6 +305,19 @@ as output formatting) won't appear here. - `pullNumber`: The pull request number (number, required) - `repo`: Repository name (string, required) +- **unhide_pull_request_review** - Unhide Pull Request Review + - **OAuth Challenge Scopes**: `repo` + - `owner`: Repository owner (username or organization) (string, required) + - `pullNumber`: The pull request number (number, required) + - `repo`: Repository name (string, required) + - `review_id`: The numeric ID of the pull request review (number, required) + +- **unhide_pull_request_review_comment** - Unhide Pull Request Review Comment + - **OAuth Challenge Scopes**: `repo` + - `comment_id`: The numeric pull request review comment ID. Use the number from a #discussion_r... anchor, not the GraphQL thread node ID (PRRT_...). (number, required) + - `owner`: Repository owner (username or organization) (string, required) + - `repo`: Repository name (string, required) + - **unresolve_review_thread** - Unresolve Review Thread - **OAuth Challenge Scopes**: `repo` - `threadID`: The node ID of the review thread to unresolve (e.g., PRRT_kwDOxxx) (string, required) diff --git a/pkg/github/__toolsnaps__/hide_issue_comment.snap b/pkg/github/__toolsnaps__/hide_issue_comment.snap new file mode 100644 index 0000000000..bb66c3fc22 --- /dev/null +++ b/pkg/github/__toolsnaps__/hide_issue_comment.snap @@ -0,0 +1,48 @@ +{ + "annotations": { + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true, + "readOnlyHint": false, + "title": "Hide Issue Comment" + }, + "description": "Hide (minimize) a comment on an issue, or a conversation comment on a pull request. Requires triage or write access to the repository, or being its author.", + "inputSchema": { + "properties": { + "classifier": { + "description": "The reason for hiding the comment", + "enum": [ + "SPAM", + "ABUSE", + "OFF_TOPIC", + "OUTDATED", + "DUPLICATE", + "RESOLVED", + "LOW_QUALITY" + ], + "type": "string" + }, + "comment_id": { + "description": "The numeric ID of the issue or pull request conversation comment", + "minimum": 1, + "type": "number" + }, + "owner": { + "description": "Repository owner (username or organization)", + "type": "string" + }, + "repo": { + "description": "Repository name", + "type": "string" + } + }, + "required": [ + "owner", + "repo", + "comment_id", + "classifier" + ], + "type": "object" + }, + "name": "hide_issue_comment" +} \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/hide_pull_request_review.snap b/pkg/github/__toolsnaps__/hide_pull_request_review.snap new file mode 100644 index 0000000000..09ecfdf6cd --- /dev/null +++ b/pkg/github/__toolsnaps__/hide_pull_request_review.snap @@ -0,0 +1,54 @@ +{ + "annotations": { + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true, + "readOnlyHint": false, + "title": "Hide Pull Request Review" + }, + "description": "Hide (minimize) the body of a submitted pull request review. Requires triage or write access to the repository, or being its author.", + "inputSchema": { + "properties": { + "classifier": { + "description": "The reason for hiding the comment", + "enum": [ + "SPAM", + "ABUSE", + "OFF_TOPIC", + "OUTDATED", + "DUPLICATE", + "RESOLVED", + "LOW_QUALITY" + ], + "type": "string" + }, + "owner": { + "description": "Repository owner (username or organization)", + "type": "string" + }, + "pullNumber": { + "description": "The pull request number", + "minimum": 1, + "type": "number" + }, + "repo": { + "description": "Repository name", + "type": "string" + }, + "review_id": { + "description": "The numeric ID of the pull request review", + "minimum": 1, + "type": "number" + } + }, + "required": [ + "owner", + "repo", + "pullNumber", + "review_id", + "classifier" + ], + "type": "object" + }, + "name": "hide_pull_request_review" +} \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/hide_pull_request_review_comment.snap b/pkg/github/__toolsnaps__/hide_pull_request_review_comment.snap new file mode 100644 index 0000000000..46c39c9885 --- /dev/null +++ b/pkg/github/__toolsnaps__/hide_pull_request_review_comment.snap @@ -0,0 +1,48 @@ +{ + "annotations": { + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true, + "readOnlyHint": false, + "title": "Hide Pull Request Review Comment" + }, + "description": "Hide (minimize) an inline review comment on a pull request diff. Requires triage or write access to the repository, or being its author.", + "inputSchema": { + "properties": { + "classifier": { + "description": "The reason for hiding the comment", + "enum": [ + "SPAM", + "ABUSE", + "OFF_TOPIC", + "OUTDATED", + "DUPLICATE", + "RESOLVED", + "LOW_QUALITY" + ], + "type": "string" + }, + "comment_id": { + "description": "The numeric pull request review comment ID. Use the number from a #discussion_r... anchor, not the GraphQL thread node ID (PRRT_...).", + "minimum": 1, + "type": "number" + }, + "owner": { + "description": "Repository owner (username or organization)", + "type": "string" + }, + "repo": { + "description": "Repository name", + "type": "string" + } + }, + "required": [ + "owner", + "repo", + "comment_id", + "classifier" + ], + "type": "object" + }, + "name": "hide_pull_request_review_comment" +} \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/unhide_issue_comment.snap b/pkg/github/__toolsnaps__/unhide_issue_comment.snap new file mode 100644 index 0000000000..ea258d12f3 --- /dev/null +++ b/pkg/github/__toolsnaps__/unhide_issue_comment.snap @@ -0,0 +1,34 @@ +{ + "annotations": { + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true, + "readOnlyHint": false, + "title": "Unhide Issue Comment" + }, + "description": "Unhide (unminimize) a previously hidden comment on an issue, or conversation comment on a pull request. Requires triage or write access to the repository, or being its author.", + "inputSchema": { + "properties": { + "comment_id": { + "description": "The numeric ID of the issue or pull request conversation comment", + "minimum": 1, + "type": "number" + }, + "owner": { + "description": "Repository owner (username or organization)", + "type": "string" + }, + "repo": { + "description": "Repository name", + "type": "string" + } + }, + "required": [ + "owner", + "repo", + "comment_id" + ], + "type": "object" + }, + "name": "unhide_issue_comment" +} \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/unhide_pull_request_review.snap b/pkg/github/__toolsnaps__/unhide_pull_request_review.snap new file mode 100644 index 0000000000..2e627d519f --- /dev/null +++ b/pkg/github/__toolsnaps__/unhide_pull_request_review.snap @@ -0,0 +1,40 @@ +{ + "annotations": { + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true, + "readOnlyHint": false, + "title": "Unhide Pull Request Review" + }, + "description": "Unhide (unminimize) the previously hidden body of a submitted pull request review. Requires triage or write access to the repository, or being its author.", + "inputSchema": { + "properties": { + "owner": { + "description": "Repository owner (username or organization)", + "type": "string" + }, + "pullNumber": { + "description": "The pull request number", + "minimum": 1, + "type": "number" + }, + "repo": { + "description": "Repository name", + "type": "string" + }, + "review_id": { + "description": "The numeric ID of the pull request review", + "minimum": 1, + "type": "number" + } + }, + "required": [ + "owner", + "repo", + "pullNumber", + "review_id" + ], + "type": "object" + }, + "name": "unhide_pull_request_review" +} \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/unhide_pull_request_review_comment.snap b/pkg/github/__toolsnaps__/unhide_pull_request_review_comment.snap new file mode 100644 index 0000000000..38ff864f15 --- /dev/null +++ b/pkg/github/__toolsnaps__/unhide_pull_request_review_comment.snap @@ -0,0 +1,34 @@ +{ + "annotations": { + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": true, + "readOnlyHint": false, + "title": "Unhide Pull Request Review Comment" + }, + "description": "Unhide (unminimize) a previously hidden inline review comment on a pull request diff. Requires triage or write access to the repository, or being its author.", + "inputSchema": { + "properties": { + "comment_id": { + "description": "The numeric pull request review comment ID. Use the number from a #discussion_r... anchor, not the GraphQL thread node ID (PRRT_...).", + "minimum": 1, + "type": "number" + }, + "owner": { + "description": "Repository owner (username or organization)", + "type": "string" + }, + "repo": { + "description": "Repository name", + "type": "string" + } + }, + "required": [ + "owner", + "repo", + "comment_id" + ], + "type": "object" + }, + "name": "unhide_pull_request_review_comment" +} \ No newline at end of file diff --git a/pkg/github/comment_minimize.go b/pkg/github/comment_minimize.go new file mode 100644 index 0000000000..0bdabae44f --- /dev/null +++ b/pkg/github/comment_minimize.go @@ -0,0 +1,323 @@ +package github + +import ( + "context" + "encoding/json" + "fmt" + "maps" + "slices" + "strings" + + ghErrors "github.com/github/github-mcp-server/pkg/errors" + "github.com/github/github-mcp-server/pkg/inventory" + "github.com/github/github-mcp-server/pkg/scopes" + "github.com/github/github-mcp-server/pkg/translations" + "github.com/github/github-mcp-server/pkg/utils" + "github.com/google/go-github/v89/github" + "github.com/google/jsonschema-go/jsonschema" + "github.com/modelcontextprotocol/go-sdk/mcp" + "github.com/shurcooL/githubv4" +) + +// MinimizeCommentResult is the response returned by the hide and unhide comment tools. +type MinimizeCommentResult struct { + NodeID string `json:"node_id"` + IsMinimized bool `json:"is_minimized"` + MinimizedReason string `json:"minimized_reason,omitempty"` +} + +var commentClassifiers = []any{"SPAM", "ABUSE", "OFF_TOPIC", "OUTDATED", "DUPLICATE", "RESOLVED", "LOW_QUALITY"} + +const commentVisibilityPermissionNote = " Requires triage or write access to the repository, or being its author." + +// commentVisibilityTarget describes one kind of hideable object: how the tools that +// hide and unhide it are named and described, and how to find its GraphQL node ID. +type commentVisibilityTarget struct { + toolset inventory.ToolsetMetadata + featureRule inventory.FeatureRule + // name is appended to "hide_" and "unhide_" to form the tool names. + name string + title string + hideDescription string + unhideDescription string + properties func() map[string]*jsonschema.Schema + required []string + resolveNodeID func(ctx context.Context, client *github.Client, owner, repo string, args map[string]any) (string, *mcp.CallToolResult) +} + +var issueCommentVisibilityTarget = commentVisibilityTarget{ + toolset: ToolsetMetadataIssues, + featureRule: issuesGranularFeatureRule, + name: "issue_comment", + title: "Issue Comment", + hideDescription: "Hide (minimize) a comment on an issue, or a conversation comment on a pull request.", + unhideDescription: "Unhide (unminimize) a previously hidden comment on an issue, or conversation comment on a pull request.", + properties: func() map[string]*jsonschema.Schema { + return map[string]*jsonschema.Schema{ + "comment_id": { + Type: "number", + Description: "The numeric ID of the issue or pull request conversation comment", + Minimum: jsonschema.Ptr(1.0), + }, + } + }, + required: []string{"comment_id"}, + resolveNodeID: func(ctx context.Context, client *github.Client, owner, repo string, args map[string]any) (string, *mcp.CallToolResult) { + commentID, err := requiredPositiveBigInt(args, "comment_id") + if err != nil { + return "", utils.NewToolResultError(err.Error()) + } + comment, resp, err := client.Issues.GetComment(ctx, owner, repo, commentID) + return nodeIDFromResponse(ctx, "failed to get issue comment", comment.GetNodeID(), resp, err) + }, +} + +var pullRequestReviewCommentVisibilityTarget = commentVisibilityTarget{ + toolset: ToolsetMetadataPullRequests, + featureRule: pullRequestsGranularFeatureRule, + name: "pull_request_review_comment", + title: "Pull Request Review Comment", + hideDescription: "Hide (minimize) an inline review comment on a pull request diff.", + unhideDescription: "Unhide (unminimize) a previously hidden inline review comment on a pull request diff.", + properties: func() map[string]*jsonschema.Schema { + return map[string]*jsonschema.Schema{ + "comment_id": { + Type: "number", + Description: "The numeric pull request review comment ID. Use the number from a #discussion_r... anchor, not the GraphQL thread node ID (PRRT_...).", + Minimum: jsonschema.Ptr(1.0), + }, + } + }, + required: []string{"comment_id"}, + resolveNodeID: func(ctx context.Context, client *github.Client, owner, repo string, args map[string]any) (string, *mcp.CallToolResult) { + commentID, err := requiredPositiveBigInt(args, "comment_id") + if err != nil { + return "", utils.NewToolResultError(err.Error()) + } + comment, resp, err := client.PullRequests.GetComment(ctx, owner, repo, commentID) + return nodeIDFromResponse(ctx, "failed to get pull request review comment", comment.GetNodeID(), resp, err) + }, +} + +var pullRequestReviewVisibilityTarget = commentVisibilityTarget{ + toolset: ToolsetMetadataPullRequests, + featureRule: pullRequestsGranularFeatureRule, + name: "pull_request_review", + title: "Pull Request Review", + hideDescription: "Hide (minimize) the body of a submitted pull request review.", + unhideDescription: "Unhide (unminimize) the previously hidden body of a submitted pull request review.", + properties: func() map[string]*jsonschema.Schema { + return map[string]*jsonschema.Schema{ + "pullNumber": { + Type: "number", + Description: "The pull request number", + Minimum: jsonschema.Ptr(1.0), + }, + "review_id": { + Type: "number", + Description: "The numeric ID of the pull request review", + Minimum: jsonschema.Ptr(1.0), + }, + } + }, + required: []string{"pullNumber", "review_id"}, + resolveNodeID: func(ctx context.Context, client *github.Client, owner, repo string, args map[string]any) (string, *mcp.CallToolResult) { + pullNumber, err := RequiredInt(args, "pullNumber") + if err != nil { + return "", utils.NewToolResultError(err.Error()) + } + if pullNumber < 1 { + return "", utils.NewToolResultError("pullNumber must be greater than 0") + } + reviewID, err := requiredPositiveBigInt(args, "review_id") + if err != nil { + return "", utils.NewToolResultError(err.Error()) + } + review, resp, err := client.PullRequests.GetReview(ctx, owner, repo, pullNumber, reviewID) + return nodeIDFromResponse(ctx, "failed to get pull request review", review.GetNodeID(), resp, err) + }, +} + +// commentVisibilityTool builds the hide_ tool when hide is true, and the unhide_ tool otherwise. +func commentVisibilityTool(t translations.TranslationHelperFunc, target commentVisibilityTarget, hide bool) inventory.ServerTool { + action, titleAction, description := "unhide", "Unhide", target.unhideDescription + if hide { + action, titleAction, description = "hide", "Hide", target.hideDescription + } + name := action + "_" + target.name + + properties := map[string]*jsonschema.Schema{ + "owner": { + Type: "string", + Description: "Repository owner (username or organization)", + }, + "repo": { + Type: "string", + Description: "Repository name", + }, + } + maps.Copy(properties, target.properties()) + required := append([]string{"owner", "repo"}, target.required...) + if hide { + properties["classifier"] = &jsonschema.Schema{ + Type: "string", + Description: "The reason for hiding the comment", + Enum: commentClassifiers, + } + required = append(required, "classifier") + } + + st := NewTool( + target.toolset, + mcp.Tool{ + Name: name, + Description: t("TOOL_"+strings.ToUpper(name)+"_DESCRIPTION", description+commentVisibilityPermissionNote), + Annotations: &mcp.ToolAnnotations{ + Title: t("TOOL_"+strings.ToUpper(name)+"_USER_TITLE", titleAction+" "+target.title), + ReadOnlyHint: false, + DestructiveHint: jsonschema.Ptr(false), + OpenWorldHint: jsonschema.Ptr(true), + }, + InputSchema: &jsonschema.Schema{ + Type: "object", + Properties: properties, + Required: required, + }, + }, + scopes.RequireAll(scopes.Repo), + func(ctx context.Context, deps ToolDependencies, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { + return setCommentVisibility(ctx, deps, target, args, hide), nil, nil + }, + ) + st.FeatureRule = target.featureRule + return st +} + +// setCommentVisibility resolves the object identified by args and hides or unhides it. +func setCommentVisibility(ctx context.Context, deps ToolDependencies, target commentVisibilityTarget, args map[string]any, hide bool) *mcp.CallToolResult { + owner, err := RequiredParam[string](args, "owner") + if err != nil { + return utils.NewToolResultError(err.Error()) + } + repo, err := RequiredParam[string](args, "repo") + if err != nil { + return utils.NewToolResultError(err.Error()) + } + var classifier string + if hide { + classifier, err = RequiredParam[string](args, "classifier") + if err != nil { + return utils.NewToolResultError(err.Error()) + } + classifier = strings.ToUpper(classifier) + if !slices.Contains(commentClassifiers, any(classifier)) { + return utils.NewToolResultError(fmt.Sprintf("invalid classifier %q: must be one of %v", classifier, commentClassifiers)) + } + } + + client, err := deps.GetClient(ctx) + if err != nil { + return utils.NewToolResultErrorFromErr("failed to get GitHub client", err) + } + + nodeID, errResult := target.resolveNodeID(ctx, client, owner, repo, args) + if errResult != nil { + return errResult + } + + gqlClient, err := deps.GetGQLClient(ctx) + if err != nil { + return utils.NewToolResultErrorFromErr("failed to get GitHub GraphQL client", err) + } + + var result MinimizeCommentResult + if hide { + result, errResult = minimizeComment(ctx, gqlClient, nodeID, classifier) + } else { + result, errResult = unminimizeComment(ctx, gqlClient, nodeID) + } + if errResult != nil { + return errResult + } + + r, err := json.Marshal(result) + if err != nil { + return utils.NewToolResultErrorFromErr("failed to marshal response", err) + } + return utils.NewToolResultText(string(r)) +} + +// requiredPositiveBigInt reads a required ID argument. The schema's minimum is not enforced +// when arguments are unmarshalled, so negative values are rejected here before any API call. +func requiredPositiveBigInt(args map[string]any, p string) (int64, error) { + v, err := RequiredBigInt(args, p) + if err != nil { + return 0, err + } + if v < 1 { + return 0, fmt.Errorf("%s must be greater than 0", p) + } + return v, nil +} + +// nodeIDFromResponse turns the result of a REST lookup into a GraphQL node ID, since the +// minimize mutations only accept node IDs. +func nodeIDFromResponse(ctx context.Context, errMessage, nodeID string, resp *github.Response, err error) (string, *mcp.CallToolResult) { + if resp != nil && resp.Body != nil { + defer func() { _ = resp.Body.Close() }() + } + if err != nil { + return "", ghErrors.NewGitHubAPIErrorResponse(ctx, errMessage, resp, err) + } + if nodeID == "" { + return "", utils.NewToolResultError(errMessage + ": response has no node ID") + } + return nodeID, nil +} + +func minimizeComment(ctx context.Context, client *githubv4.Client, nodeID, classifier string) (MinimizeCommentResult, *mcp.CallToolResult) { + var mutation struct { + MinimizeComment struct { + MinimizedComment struct { + IsMinimized githubv4.Boolean + MinimizedReason githubv4.String + } + } `graphql:"minimizeComment(input: $input)"` + } + + input := githubv4.MinimizeCommentInput{ + SubjectID: githubv4.ID(nodeID), + Classifier: githubv4.ReportedContentClassifiers(classifier), + } + if err := client.Mutate(ctx, &mutation, input, nil); err != nil { + return MinimizeCommentResult{}, ghErrors.NewGitHubGraphQLErrorResponse(ctx, "failed to minimize comment", err) + } + + return MinimizeCommentResult{ + NodeID: nodeID, + IsMinimized: bool(mutation.MinimizeComment.MinimizedComment.IsMinimized), + MinimizedReason: string(mutation.MinimizeComment.MinimizedComment.MinimizedReason), + }, nil +} + +func unminimizeComment(ctx context.Context, client *githubv4.Client, nodeID string) (MinimizeCommentResult, *mcp.CallToolResult) { + var mutation struct { + UnminimizeComment struct { + UnminimizedComment struct { + IsMinimized githubv4.Boolean + } + } `graphql:"unminimizeComment(input: $input)"` + } + + input := githubv4.UnminimizeCommentInput{ + SubjectID: githubv4.ID(nodeID), + } + if err := client.Mutate(ctx, &mutation, input, nil); err != nil { + return MinimizeCommentResult{}, ghErrors.NewGitHubGraphQLErrorResponse(ctx, "failed to unminimize comment", err) + } + + return MinimizeCommentResult{ + NodeID: nodeID, + IsMinimized: bool(mutation.UnminimizeComment.UnminimizedComment.IsMinimized), + }, nil +} diff --git a/pkg/github/comment_minimize_test.go b/pkg/github/comment_minimize_test.go new file mode 100644 index 0000000000..75e97e0fbb --- /dev/null +++ b/pkg/github/comment_minimize_test.go @@ -0,0 +1,383 @@ +package github + +import ( + "context" + "encoding/json" + "net/http" + "testing" + + "github.com/github/github-mcp-server/internal/githubv4mock" + "github.com/github/github-mcp-server/internal/toolsnaps" + "github.com/github/github-mcp-server/pkg/inventory" + "github.com/github/github-mcp-server/pkg/translations" + "github.com/google/go-github/v89/github" + "github.com/google/jsonschema-go/jsonschema" + "github.com/shurcooL/githubv4" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + getIssueCommentRoute = "GET /repos/{owner}/{repo}/issues/comments/{comment_id}" + getReviewCommentRoute = "GET /repos/{owner}/{repo}/pulls/comments/{comment_id}" + getReviewRoute = "GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}" +) + +// minimizedReason is the lowercase, hyphenated form GitHub returns (e.g. "off-topic"), not the classifier enum. +func minimizeCommentMatcher(nodeID, classifier, minimizedReason string) githubv4mock.Matcher { + return githubv4mock.NewMutationMatcher( + struct { + MinimizeComment struct { + MinimizedComment struct { + IsMinimized githubv4.Boolean + MinimizedReason githubv4.String + } + } `graphql:"minimizeComment(input: $input)"` + }{}, + githubv4.MinimizeCommentInput{ + SubjectID: githubv4.ID(nodeID), + Classifier: githubv4.ReportedContentClassifiers(classifier), + }, + nil, + githubv4mock.DataResponse(map[string]any{ + "minimizeComment": map[string]any{ + "minimizedComment": map[string]any{ + "isMinimized": true, + "minimizedReason": minimizedReason, + }, + }, + }), + ) +} + +func unminimizeCommentMatcher(nodeID string) githubv4mock.Matcher { + return githubv4mock.NewMutationMatcher( + struct { + UnminimizeComment struct { + UnminimizedComment struct { + IsMinimized githubv4.Boolean + } + } `graphql:"unminimizeComment(input: $input)"` + }{}, + githubv4.UnminimizeCommentInput{SubjectID: githubv4.ID(nodeID)}, + nil, + githubv4mock.DataResponse(map[string]any{ + "unminimizeComment": map[string]any{ + "unminimizedComment": map[string]any{"isMinimized": false}, + }, + }), + ) +} + +func minimizeCommentErrorMatcher(nodeID, classifier string) githubv4mock.Matcher { + return githubv4mock.NewMutationMatcher( + struct { + MinimizeComment struct { + MinimizedComment struct { + IsMinimized githubv4.Boolean + MinimizedReason githubv4.String + } + } `graphql:"minimizeComment(input: $input)"` + }{}, + githubv4.MinimizeCommentInput{ + SubjectID: githubv4.ID(nodeID), + Classifier: githubv4.ReportedContentClassifiers(classifier), + }, + nil, + githubv4mock.ErrorResponse("Resource not accessible by integration"), + ) +} + +func unminimizeCommentErrorMatcher(nodeID string) githubv4mock.Matcher { + return githubv4mock.NewMutationMatcher( + struct { + UnminimizeComment struct { + UnminimizedComment struct { + IsMinimized githubv4.Boolean + } + } `graphql:"unminimizeComment(input: $input)"` + }{}, + githubv4.UnminimizeCommentInput{SubjectID: githubv4.ID(nodeID)}, + nil, + githubv4mock.ErrorResponse("Resource not accessible by integration"), + ) +} + +func Test_CommentVisibilityToolSchemas(t *testing.T) { + tests := []struct { + tool inventory.ServerTool + name string + toolset inventory.ToolsetID + featureFlag string + expectedRequire []string + }{ + { + tool: GranularHideIssueComment(translations.NullTranslationHelper), + name: "hide_issue_comment", + toolset: ToolsetMetadataIssues.ID, + featureFlag: FeatureFlagIssuesGranular, + expectedRequire: []string{"owner", "repo", "comment_id", "classifier"}, + }, + { + tool: GranularUnhideIssueComment(translations.NullTranslationHelper), + name: "unhide_issue_comment", + toolset: ToolsetMetadataIssues.ID, + featureFlag: FeatureFlagIssuesGranular, + expectedRequire: []string{"owner", "repo", "comment_id"}, + }, + { + tool: GranularHidePullRequestReviewComment(translations.NullTranslationHelper), + name: "hide_pull_request_review_comment", + toolset: ToolsetMetadataPullRequests.ID, + featureFlag: FeatureFlagPullRequestsGranular, + expectedRequire: []string{"owner", "repo", "comment_id", "classifier"}, + }, + { + tool: GranularUnhidePullRequestReviewComment(translations.NullTranslationHelper), + name: "unhide_pull_request_review_comment", + toolset: ToolsetMetadataPullRequests.ID, + featureFlag: FeatureFlagPullRequestsGranular, + expectedRequire: []string{"owner", "repo", "comment_id"}, + }, + { + tool: GranularHidePullRequestReview(translations.NullTranslationHelper), + name: "hide_pull_request_review", + toolset: ToolsetMetadataPullRequests.ID, + featureFlag: FeatureFlagPullRequestsGranular, + expectedRequire: []string{"owner", "repo", "pullNumber", "review_id", "classifier"}, + }, + { + tool: GranularUnhidePullRequestReview(translations.NullTranslationHelper), + name: "unhide_pull_request_review", + toolset: ToolsetMetadataPullRequests.ID, + featureFlag: FeatureFlagPullRequestsGranular, + expectedRequire: []string{"owner", "repo", "pullNumber", "review_id"}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + tool := tc.tool.Tool + require.NoError(t, toolsnaps.Test(tool.Name, tool)) + + assert.Equal(t, tc.name, tool.Name) + assert.NotEmpty(t, tool.Description) + assert.False(t, tool.Annotations.ReadOnlyHint) + assert.Equal(t, tc.toolset, tc.tool.Toolset.ID) + assert.Equal(t, []inventory.FeatureFlag{inventory.FeatureFlag(tc.featureFlag)}, tc.tool.FeatureRule.Features()) + + schema := tool.InputSchema.(*jsonschema.Schema) + assert.ElementsMatch(t, tc.expectedRequire, schema.Required) + assert.Len(t, schema.Properties, len(tc.expectedRequire), "every property should be required") + }) + } +} + +func Test_HideAndUnhideComments(t *testing.T) { + issueComment := mockResponse(t, http.StatusOK, &github.IssueComment{ID: github.Ptr(int64(1)), NodeID: github.Ptr("IC_1")}) + reviewComment := mockResponse(t, http.StatusOK, &github.PullRequestComment{ID: github.Ptr(int64(2)), NodeID: github.Ptr("PRRC_2")}) + review := mockResponse(t, http.StatusOK, &github.PullRequestReview{ID: github.Ptr(int64(3)), NodeID: github.Ptr("PRR_3")}) + notFound := mockResponse(t, http.StatusNotFound, `{"message": "Not Found"}`) + + tests := []struct { + name string + tool inventory.ServerTool + restHandlers map[string]http.HandlerFunc + gqlMatchers []githubv4mock.Matcher + requestArgs map[string]any + expectedResult MinimizeCommentResult + expectedErrMsg string + }{ + { + name: "hide issue comment", + tool: GranularHideIssueComment(translations.NullTranslationHelper), + restHandlers: map[string]http.HandlerFunc{getIssueCommentRoute: issueComment}, + gqlMatchers: []githubv4mock.Matcher{minimizeCommentMatcher("IC_1", "SPAM", "spam")}, + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "comment_id": float64(1), "classifier": "spam"}, + expectedResult: MinimizeCommentResult{NodeID: "IC_1", IsMinimized: true, MinimizedReason: "spam"}, + }, + { + name: "unhide issue comment", + tool: GranularUnhideIssueComment(translations.NullTranslationHelper), + restHandlers: map[string]http.HandlerFunc{getIssueCommentRoute: issueComment}, + gqlMatchers: []githubv4mock.Matcher{unminimizeCommentMatcher("IC_1")}, + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "comment_id": float64(1)}, + expectedResult: MinimizeCommentResult{NodeID: "IC_1", IsMinimized: false}, + }, + { + name: "hide pull request review comment", + tool: GranularHidePullRequestReviewComment(translations.NullTranslationHelper), + restHandlers: map[string]http.HandlerFunc{getReviewCommentRoute: reviewComment}, + gqlMatchers: []githubv4mock.Matcher{minimizeCommentMatcher("PRRC_2", "OUTDATED", "outdated")}, + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "comment_id": float64(2), "classifier": "OUTDATED"}, + expectedResult: MinimizeCommentResult{NodeID: "PRRC_2", IsMinimized: true, MinimizedReason: "outdated"}, + }, + { + name: "unhide pull request review comment", + tool: GranularUnhidePullRequestReviewComment(translations.NullTranslationHelper), + restHandlers: map[string]http.HandlerFunc{getReviewCommentRoute: reviewComment}, + gqlMatchers: []githubv4mock.Matcher{unminimizeCommentMatcher("PRRC_2")}, + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "comment_id": float64(2)}, + expectedResult: MinimizeCommentResult{NodeID: "PRRC_2", IsMinimized: false}, + }, + { + name: "hide pull request review", + tool: GranularHidePullRequestReview(translations.NullTranslationHelper), + restHandlers: map[string]http.HandlerFunc{getReviewRoute: review}, + gqlMatchers: []githubv4mock.Matcher{minimizeCommentMatcher("PRR_3", "RESOLVED", "resolved")}, + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "pullNumber": float64(42), "review_id": float64(3), "classifier": "RESOLVED"}, + expectedResult: MinimizeCommentResult{NodeID: "PRR_3", IsMinimized: true, MinimizedReason: "resolved"}, + }, + { + name: "unhide pull request review", + tool: GranularUnhidePullRequestReview(translations.NullTranslationHelper), + restHandlers: map[string]http.HandlerFunc{getReviewRoute: review}, + gqlMatchers: []githubv4mock.Matcher{unminimizeCommentMatcher("PRR_3")}, + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "pullNumber": float64(42), "review_id": float64(3)}, + expectedResult: MinimizeCommentResult{NodeID: "PRR_3", IsMinimized: false}, + }, + { + name: "issue comment not found", + tool: GranularUnhideIssueComment(translations.NullTranslationHelper), + restHandlers: map[string]http.HandlerFunc{getIssueCommentRoute: notFound}, + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "comment_id": float64(1)}, + expectedErrMsg: "failed to get issue comment", + }, + { + name: "pull request review comment not found", + tool: GranularUnhidePullRequestReviewComment(translations.NullTranslationHelper), + restHandlers: map[string]http.HandlerFunc{getReviewCommentRoute: notFound}, + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "comment_id": float64(2)}, + expectedErrMsg: "failed to get pull request review comment", + }, + { + name: "pull request review not found", + tool: GranularUnhidePullRequestReview(translations.NullTranslationHelper), + restHandlers: map[string]http.HandlerFunc{getReviewRoute: notFound}, + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "pullNumber": float64(42), "review_id": float64(3)}, + expectedErrMsg: "failed to get pull request review", + }, + { + name: "response without node ID", + tool: GranularUnhideIssueComment(translations.NullTranslationHelper), + restHandlers: map[string]http.HandlerFunc{ + getIssueCommentRoute: mockResponse(t, http.StatusOK, &github.IssueComment{ID: github.Ptr(int64(1))}), + }, + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "comment_id": float64(1)}, + expectedErrMsg: "response has no node ID", + }, + { + name: "hide mutation fails", + tool: GranularHideIssueComment(translations.NullTranslationHelper), + restHandlers: map[string]http.HandlerFunc{getIssueCommentRoute: issueComment}, + gqlMatchers: []githubv4mock.Matcher{minimizeCommentErrorMatcher("IC_1", "SPAM")}, + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "comment_id": float64(1), "classifier": "SPAM"}, + expectedErrMsg: "failed to minimize comment", + }, + { + name: "unhide mutation fails", + tool: GranularUnhideIssueComment(translations.NullTranslationHelper), + restHandlers: map[string]http.HandlerFunc{getIssueCommentRoute: issueComment}, + gqlMatchers: []githubv4mock.Matcher{unminimizeCommentErrorMatcher("IC_1")}, + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "comment_id": float64(1)}, + expectedErrMsg: "failed to unminimize comment", + }, + { + name: "invalid classifier is rejected before any API call", + tool: GranularHideIssueComment(translations.NullTranslationHelper), + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "comment_id": float64(1), "classifier": "BOGUS"}, + expectedErrMsg: `invalid classifier "BOGUS"`, + }, + { + name: "negative issue comment_id", + tool: GranularHideIssueComment(translations.NullTranslationHelper), + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "comment_id": float64(-1), "classifier": "SPAM"}, + expectedErrMsg: "comment_id must be greater than 0", + }, + { + name: "negative pull request review comment_id", + tool: GranularUnhidePullRequestReviewComment(translations.NullTranslationHelper), + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "comment_id": float64(-2)}, + expectedErrMsg: "comment_id must be greater than 0", + }, + { + name: "negative review_id", + tool: GranularUnhidePullRequestReview(translations.NullTranslationHelper), + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "pullNumber": float64(42), "review_id": float64(-3)}, + expectedErrMsg: "review_id must be greater than 0", + }, + { + name: "negative pullNumber", + tool: GranularUnhidePullRequestReview(translations.NullTranslationHelper), + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "pullNumber": float64(-42), "review_id": float64(3)}, + expectedErrMsg: "pullNumber must be greater than 0", + }, + { + name: "missing owner", + tool: GranularUnhideIssueComment(translations.NullTranslationHelper), + requestArgs: map[string]any{"repo": "repo", "comment_id": float64(1)}, + expectedErrMsg: "owner", + }, + { + name: "missing repo", + tool: GranularUnhideIssueComment(translations.NullTranslationHelper), + requestArgs: map[string]any{"owner": "owner", "comment_id": float64(1)}, + expectedErrMsg: "repo", + }, + { + name: "missing classifier", + tool: GranularHideIssueComment(translations.NullTranslationHelper), + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "comment_id": float64(1)}, + expectedErrMsg: "classifier", + }, + { + name: "missing issue comment_id", + tool: GranularUnhideIssueComment(translations.NullTranslationHelper), + requestArgs: map[string]any{"owner": "owner", "repo": "repo"}, + expectedErrMsg: "comment_id", + }, + { + name: "missing pull request review comment_id", + tool: GranularUnhidePullRequestReviewComment(translations.NullTranslationHelper), + requestArgs: map[string]any{"owner": "owner", "repo": "repo"}, + expectedErrMsg: "comment_id", + }, + { + name: "missing pullNumber", + tool: GranularUnhidePullRequestReview(translations.NullTranslationHelper), + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "review_id": float64(3)}, + expectedErrMsg: "pullNumber", + }, + { + name: "missing review_id", + tool: GranularUnhidePullRequestReview(translations.NullTranslationHelper), + requestArgs: map[string]any{"owner": "owner", "repo": "repo", "pullNumber": float64(42)}, + expectedErrMsg: "review_id", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + deps := BaseDeps{ + Client: mustNewGHClient(t, MockHTTPClientWithHandlers(tc.restHandlers)), + GQLClient: githubv4.NewClient(githubv4mock.NewMockedHTTPClient(tc.gqlMatchers...)), + } + handler := tc.tool.Handler(deps) + + request := createMCPRequest(tc.requestArgs) + result, err := handler(ContextWithDeps(context.Background(), deps), &request) + require.NoError(t, err) + + if tc.expectedErrMsg != "" { + require.True(t, result.IsError) + assert.Contains(t, getErrorResult(t, result).Text, tc.expectedErrMsg) + return + } + + require.False(t, result.IsError, getTextResult(t, result).Text) + var got MinimizeCommentResult + require.NoError(t, json.Unmarshal([]byte(getTextResult(t, result).Text), &got)) + assert.Equal(t, tc.expectedResult, got) + }) + } +} diff --git a/pkg/github/granular_tools_test.go b/pkg/github/granular_tools_test.go index 425f954ef9..0d36ab97a5 100644 --- a/pkg/github/granular_tools_test.go +++ b/pkg/github/granular_tools_test.go @@ -56,6 +56,8 @@ func TestGranularToolSnaps(t *testing.T) { GranularRemoveIssueReaction, GranularAddIssueCommentReaction, GranularRemoveIssueCommentReaction, + GranularHideIssueComment, + GranularUnhideIssueComment, GranularUpdatePullRequestTitle, GranularUpdatePullRequestBody, GranularUpdatePullRequestState, @@ -69,6 +71,10 @@ func TestGranularToolSnaps(t *testing.T) { GranularUnresolveReviewThread, GranularAddPullRequestReviewCommentReaction, GranularRemovePullRequestReviewCommentReaction, + GranularHidePullRequestReviewComment, + GranularUnhidePullRequestReviewComment, + GranularHidePullRequestReview, + GranularUnhidePullRequestReview, } for _, constructor := range toolConstructors { @@ -105,6 +111,8 @@ func TestIssuesGranularToolset(t *testing.T) { "remove_issue_reaction", "add_issue_comment_reaction", "remove_issue_comment_reaction", + "hide_issue_comment", + "unhide_issue_comment", } for _, name := range expected { assert.Contains(t, toolNames, name) @@ -142,6 +150,10 @@ func TestPullRequestsGranularToolset(t *testing.T) { "unresolve_review_thread", "add_pull_request_review_comment_reaction", "remove_pull_request_review_comment_reaction", + "hide_pull_request_review_comment", + "unhide_pull_request_review_comment", + "hide_pull_request_review", + "unhide_pull_request_review", } for _, name := range expected { assert.Contains(t, toolNames, name) diff --git a/pkg/github/issues_granular.go b/pkg/github/issues_granular.go index f22a8a1536..bed003bef3 100644 --- a/pkg/github/issues_granular.go +++ b/pkg/github/issues_granular.go @@ -1901,3 +1901,13 @@ func GranularRemoveIssueCommentReaction(t translations.TranslationHelperFunc) in st.FeatureRule = issuesGranularFeatureRule return st } + +// GranularHideIssueComment hides (minimizes) an issue or pull request conversation comment. +func GranularHideIssueComment(t translations.TranslationHelperFunc) inventory.ServerTool { + return commentVisibilityTool(t, issueCommentVisibilityTarget, true) +} + +// GranularUnhideIssueComment unhides (unminimizes) an issue or pull request conversation comment. +func GranularUnhideIssueComment(t translations.TranslationHelperFunc) inventory.ServerTool { + return commentVisibilityTool(t, issueCommentVisibilityTarget, false) +} diff --git a/pkg/github/pullrequests_granular.go b/pkg/github/pullrequests_granular.go index e670da9a34..92e7fb6f5d 100644 --- a/pkg/github/pullrequests_granular.go +++ b/pkg/github/pullrequests_granular.go @@ -973,3 +973,23 @@ func GranularRemovePullRequestReviewCommentReaction(t translations.TranslationHe st.FeatureRule = pullRequestsGranularFeatureRule return st } + +// GranularHidePullRequestReviewComment hides (minimizes) an inline pull request review comment. +func GranularHidePullRequestReviewComment(t translations.TranslationHelperFunc) inventory.ServerTool { + return commentVisibilityTool(t, pullRequestReviewCommentVisibilityTarget, true) +} + +// GranularUnhidePullRequestReviewComment unhides (unminimizes) an inline pull request review comment. +func GranularUnhidePullRequestReviewComment(t translations.TranslationHelperFunc) inventory.ServerTool { + return commentVisibilityTool(t, pullRequestReviewCommentVisibilityTarget, false) +} + +// GranularHidePullRequestReview hides (minimizes) the body of a pull request review. +func GranularHidePullRequestReview(t translations.TranslationHelperFunc) inventory.ServerTool { + return commentVisibilityTool(t, pullRequestReviewVisibilityTarget, true) +} + +// GranularUnhidePullRequestReview unhides (unminimizes) the body of a pull request review. +func GranularUnhidePullRequestReview(t translations.TranslationHelperFunc) inventory.ServerTool { + return commentVisibilityTool(t, pullRequestReviewVisibilityTarget, false) +} diff --git a/pkg/github/tools.go b/pkg/github/tools.go index b91664a67f..eb1113754a 100644 --- a/pkg/github/tools.go +++ b/pkg/github/tools.go @@ -376,6 +376,8 @@ func AllTools(t translations.TranslationHelperFunc, opts ...ToolOption) []invent GranularRemoveIssueReaction(t), GranularAddIssueCommentReaction(t), GranularRemoveIssueCommentReaction(t), + GranularHideIssueComment(t), + GranularUnhideIssueComment(t), // Granular pull request tools (feature-flagged, replace consolidated update_pull_request/pull_request_review_write) GranularUpdatePullRequestTitle(t), @@ -392,6 +394,10 @@ func AllTools(t translations.TranslationHelperFunc, opts ...ToolOption) []invent GranularUnresolveReviewThread(t), GranularAddPullRequestReviewCommentReaction(t), GranularRemovePullRequestReviewCommentReaction(t), + GranularHidePullRequestReviewComment(t), + GranularUnhidePullRequestReviewComment(t), + GranularHidePullRequestReview(t), + GranularUnhidePullRequestReview(t), }) } From 38cb0c445dd90d5151001aca07bd33f68017e484 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Oct 2026 09:44:02 +0200 Subject: [PATCH 05/13] fix(pull_request_reviews): Update pending pull request review lookup to work when the authenticated actor is a Copilot bot (#3355) --- pkg/github/granular_tools_test.go | 50 +----- pkg/github/pullrequests.go | 262 ++++++++++++------------------ pkg/github/pullrequests_test.go | 255 ++++++++++++++++++----------- 3 files changed, 278 insertions(+), 289 deletions(-) diff --git a/pkg/github/granular_tools_test.go b/pkg/github/granular_tools_test.go index 0d36ab97a5..3db96a3f2a 100644 --- a/pkg/github/granular_tools_test.go +++ b/pkg/github/granular_tools_test.go @@ -1665,49 +1665,15 @@ func TestGranularUpdatePullRequestDraftState(t *testing.T) { func TestGranularAddPullRequestReviewComment(t *testing.T) { mockedClient := githubv4mock.NewMockedHTTPClient( - githubv4mock.NewQueryMatcher( - struct { - Viewer struct { - Login githubv4.String - } - }{}, - nil, - githubv4mock.DataResponse(map[string]any{ - "viewer": map[string]any{"login": "testuser"}, - }), - ), - githubv4mock.NewQueryMatcher( - struct { - Repository struct { - PullRequest struct { - Reviews struct { - Nodes []struct { - ID githubv4.ID - State githubv4.PullRequestReviewState - URL githubv4.URI - } - } `graphql:"reviews(first: 1, author: $author)"` - } `graphql:"pullRequest(number: $prNum)"` - } `graphql:"repository(owner: $owner, name: $name)"` - }{}, - map[string]any{ - "author": githubv4.String("testuser"), - "owner": githubv4.String("owner"), - "name": githubv4.String("repo"), - "prNum": githubv4.Int(1), + viewerIDQuery("U_testuser"), + getPendingReviewsQuery(getPendingReviewsQueryParams{ + owner: "owner", + repo: "repo", + prNum: 1, + reviews: []pendingReviewQueryReview{ + {id: "PRR_123", authorID: "U_testuser"}, }, - githubv4mock.DataResponse(map[string]any{ - "repository": map[string]any{ - "pullRequest": map[string]any{ - "reviews": map[string]any{ - "nodes": []map[string]any{ - {"id": "PRR_123", "state": "PENDING", "url": "https://github.com/owner/repo/pull/1#pullrequestreview-123"}, - }, - }, - }, - }, - }), - ), + }), githubv4mock.NewMutationMatcher( struct { AddPullRequestReviewThread struct { diff --git a/pkg/github/pullrequests.go b/pkg/github/pullrequests.go index 5cee8b3231..19885caa56 100644 --- a/pkg/github/pullrequests.go +++ b/pkg/github/pullrequests.go @@ -1989,57 +1989,9 @@ func CreatePullRequestReview(ctx context.Context, client *githubv4.Client, param } func SubmitPendingPullRequestReview(ctx context.Context, client *githubv4.Client, params PullRequestReviewWriteParams) (*mcp.CallToolResult, error) { - // First we'll get the current user - var getViewerQuery struct { - Viewer struct { - Login githubv4.String - } - } - - if err := client.Query(ctx, &getViewerQuery, nil); err != nil { - return ghErrors.NewGitHubGraphQLErrorResponse(ctx, - "failed to get current user", - err, - ), nil - } - - var getLatestReviewForViewerQuery struct { - Repository struct { - PullRequest struct { - Reviews struct { - Nodes []struct { - ID githubv4.ID - State githubv4.PullRequestReviewState - URL githubv4.URI - } - } `graphql:"reviews(first: 1, author: $author)"` - } `graphql:"pullRequest(number: $prNum)"` - } `graphql:"repository(owner: $owner, name: $name)"` - } - - vars := map[string]any{ - "author": githubv4.String(getViewerQuery.Viewer.Login), - "owner": githubv4.String(params.Owner), - "name": githubv4.String(params.Repo), - "prNum": githubv4.Int(params.PullNumber), - } - - if err := client.Query(ctx, &getLatestReviewForViewerQuery, vars); err != nil { - return ghErrors.NewGitHubGraphQLErrorResponse(ctx, - "failed to get latest review for current user", - err, - ), nil - } - - // Validate there is one review and the state is pending - if len(getLatestReviewForViewerQuery.Repository.PullRequest.Reviews.Nodes) == 0 { - return utils.NewToolResultError("No pending review found for the viewer"), nil - } - - review := getLatestReviewForViewerQuery.Repository.PullRequest.Reviews.Nodes[0] - if review.State != githubv4.PullRequestReviewStatePending { - errText := fmt.Sprintf("The latest review, found at %s is not pending", review.URL) - return utils.NewToolResultError(errText), nil + review, result := getPendingPullRequestReviewForViewer(ctx, client, params.Owner, params.Repo, params.PullNumber) + if result != nil { + return result, nil } // Prepare the mutation @@ -2055,7 +2007,7 @@ func SubmitPendingPullRequestReview(ctx context.Context, client *githubv4.Client ctx, &submitPullRequestReviewMutation, githubv4.SubmitPullRequestReviewInput{ - PullRequestReviewID: &review.ID, + PullRequestReviewID: review, Event: githubv4.PullRequestReviewEvent(params.Event), Body: newGQLStringlikePtr[githubv4.String](¶ms.Body), }, @@ -2074,57 +2026,9 @@ func SubmitPendingPullRequestReview(ctx context.Context, client *githubv4.Client } func DeletePendingPullRequestReview(ctx context.Context, client *githubv4.Client, params PullRequestReviewWriteParams) (*mcp.CallToolResult, error) { - // First we'll get the current user - var getViewerQuery struct { - Viewer struct { - Login githubv4.String - } - } - - if err := client.Query(ctx, &getViewerQuery, nil); err != nil { - return ghErrors.NewGitHubGraphQLErrorResponse(ctx, - "failed to get current user", - err, - ), nil - } - - var getLatestReviewForViewerQuery struct { - Repository struct { - PullRequest struct { - Reviews struct { - Nodes []struct { - ID githubv4.ID - State githubv4.PullRequestReviewState - URL githubv4.URI - } - } `graphql:"reviews(first: 1, author: $author)"` - } `graphql:"pullRequest(number: $prNum)"` - } `graphql:"repository(owner: $owner, name: $name)"` - } - - vars := map[string]any{ - "author": githubv4.String(getViewerQuery.Viewer.Login), - "owner": githubv4.String(params.Owner), - "name": githubv4.String(params.Repo), - "prNum": githubv4.Int(params.PullNumber), - } - - if err := client.Query(ctx, &getLatestReviewForViewerQuery, vars); err != nil { - return ghErrors.NewGitHubGraphQLErrorResponse(ctx, - "failed to get latest review for current user", - err, - ), nil - } - - // Validate there is one review and the state is pending - if len(getLatestReviewForViewerQuery.Repository.PullRequest.Reviews.Nodes) == 0 { - return utils.NewToolResultError("No pending review found for the viewer"), nil - } - - review := getLatestReviewForViewerQuery.Repository.PullRequest.Reviews.Nodes[0] - if review.State != githubv4.PullRequestReviewStatePending { - errText := fmt.Sprintf("The latest review, found at %s is not pending", review.URL) - return utils.NewToolResultError(errText), nil + review, result := getPendingPullRequestReviewForViewer(ctx, client, params.Owner, params.Repo, params.PullNumber) + if result != nil { + return result, nil } // Prepare the mutation @@ -2140,7 +2044,7 @@ func DeletePendingPullRequestReview(ctx context.Context, client *githubv4.Client ctx, &deletePullRequestReviewMutation, githubv4.DeletePullRequestReviewInput{ - PullRequestReviewID: &review.ID, + PullRequestReviewID: review, }, nil, ); err != nil { @@ -2235,57 +2139,9 @@ type AddCommentToPendingReviewParams struct { // AddCommentToPendingReviewCall adds a review comment to the viewer's pending pull request review. func AddCommentToPendingReviewCall(ctx context.Context, client *githubv4.Client, params AddCommentToPendingReviewParams) (*mcp.CallToolResult, error) { - // Get the current user - var getViewerQuery struct { - Viewer struct { - Login githubv4.String - } - } - - if err := client.Query(ctx, &getViewerQuery, nil); err != nil { - return ghErrors.NewGitHubGraphQLErrorResponse(ctx, - "failed to get current user", - err, - ), nil - } - - var getLatestReviewForViewerQuery struct { - Repository struct { - PullRequest struct { - Reviews struct { - Nodes []struct { - ID githubv4.ID - State githubv4.PullRequestReviewState - URL githubv4.URI - } - } `graphql:"reviews(first: 1, author: $author)"` - } `graphql:"pullRequest(number: $prNum)"` - } `graphql:"repository(owner: $owner, name: $name)"` - } - - vars := map[string]any{ - "author": githubv4.String(getViewerQuery.Viewer.Login), - "owner": githubv4.String(params.Owner), - "name": githubv4.String(params.Repo), - "prNum": githubv4.Int(params.PullNumber), - } - - if err := client.Query(ctx, &getLatestReviewForViewerQuery, vars); err != nil { - return ghErrors.NewGitHubGraphQLErrorResponse(ctx, - "failed to get latest review for current user", - err, - ), nil - } - - // Validate there is one review and the state is pending - if len(getLatestReviewForViewerQuery.Repository.PullRequest.Reviews.Nodes) == 0 { - return utils.NewToolResultError("No pending review found for the viewer"), nil - } - - review := getLatestReviewForViewerQuery.Repository.PullRequest.Reviews.Nodes[0] - if review.State != githubv4.PullRequestReviewStatePending { - errText := fmt.Sprintf("The latest review, found at %s is not pending", review.URL) - return utils.NewToolResultError(errText), nil + review, result := getPendingPullRequestReviewForViewer(ctx, client, params.Owner, params.Repo, params.PullNumber) + if result != nil { + return result, nil } // Create a new review thread comment on the review. @@ -2308,7 +2164,7 @@ func AddCommentToPendingReviewCall(ctx context.Context, client *githubv4.Client, Side: newGQLStringlikePtr[githubv4.DiffSide](params.Side), StartLine: newGQLIntPtr(params.StartLine), StartSide: newGQLStringlikePtr[githubv4.DiffSide](params.StartSide), - PullRequestReviewID: &review.ID, + PullRequestReviewID: review, }, nil, ); err != nil { @@ -2326,6 +2182,100 @@ func AddCommentToPendingReviewCall(ctx context.Context, client *githubv4.Client, return utils.NewToolResultText("pull request review comment successfully added to pending review"), nil } +type pendingReviewAuthor struct { + Bot struct { + ID githubv4.ID `graphql:"botId: id"` + } `graphql:"... on Bot"` + EnterpriseUserAccount struct { + ID githubv4.ID `graphql:"enterpriseUserAccountId: id"` + } `graphql:"... on EnterpriseUserAccount"` + Mannequin struct { + ID githubv4.ID `graphql:"mannequinId: id"` + } `graphql:"... on Mannequin"` + Organization struct { + ID githubv4.ID `graphql:"organizationId: id"` + } `graphql:"... on Organization"` + User struct { + ID githubv4.ID `graphql:"userId: id"` + } `graphql:"... on User"` +} + +func (a pendingReviewAuthor) id() githubv4.ID { + for _, id := range []githubv4.ID{ + a.Bot.ID, + a.EnterpriseUserAccount.ID, + a.Mannequin.ID, + a.Organization.ID, + a.User.ID, + } { + if id != nil { + return id + } + } + return nil +} + +func getPendingPullRequestReviewForViewer(ctx context.Context, client *githubv4.Client, owner, repo string, pullNumber int32) (*githubv4.ID, *mcp.CallToolResult) { + var getViewerQuery struct { + Viewer struct { + ID githubv4.ID + } + } + + if err := client.Query(ctx, &getViewerQuery, nil); err != nil { + return nil, ghErrors.NewGitHubGraphQLErrorResponse(ctx, + "failed to get current user", + err, + ) + } + + vars := map[string]any{ + "after": (*githubv4.String)(nil), + "owner": githubv4.String(owner), + "name": githubv4.String(repo), + "prNum": githubv4.Int(pullNumber), + "states": []githubv4.PullRequestReviewState{githubv4.PullRequestReviewStatePending}, + } + + for { + var getPendingReviewsQuery struct { + Repository struct { + PullRequest struct { + Reviews struct { + Nodes []struct { + ID githubv4.ID + Author pendingReviewAuthor + } + PageInfo struct { + HasNextPage githubv4.Boolean + EndCursor githubv4.String + } + } `graphql:"reviews(first: 100, after: $after, states: $states)"` + } `graphql:"pullRequest(number: $prNum)"` + } `graphql:"repository(owner: $owner, name: $name)"` + } + + if err := client.Query(ctx, &getPendingReviewsQuery, vars); err != nil { + return nil, ghErrors.NewGitHubGraphQLErrorResponse(ctx, + "failed to get pending pull request reviews", + err, + ) + } + + for _, review := range getPendingReviewsQuery.Repository.PullRequest.Reviews.Nodes { + if review.Author.id() == getViewerQuery.Viewer.ID { + reviewID := review.ID + return &reviewID, nil + } + } + + if !getPendingReviewsQuery.Repository.PullRequest.Reviews.PageInfo.HasNextPage { + return nil, utils.NewToolResultError("No pending review found for the viewer") + } + vars["after"] = githubv4.NewString(getPendingReviewsQuery.Repository.PullRequest.Reviews.PageInfo.EndCursor) + } +} + // AddCommentToPendingReview creates a tool to add a comment to a pull request review. func AddCommentToPendingReview(t translations.TranslationHelperFunc) inventory.ServerTool { schema := &jsonschema.Schema{ diff --git a/pkg/github/pullrequests_test.go b/pkg/github/pullrequests_test.go index 88facf8c51..8bca934c30 100644 --- a/pkg/github/pullrequests_test.go +++ b/pkg/github/pullrequests_test.go @@ -3667,7 +3667,7 @@ func TestAddPullRequestReviewCommentToPendingReview(t *testing.T) { expectedToolErrMsg string }{ { - name: "successful line comment addition", + name: "selects viewer pending review by ID", requestArgs: map[string]any{ "owner": "owner", "repo": "repo", @@ -3681,18 +3681,21 @@ func TestAddPullRequestReviewCommentToPendingReview(t *testing.T) { "startSide": "RIGHT", }, mockedClient: githubv4mock.NewMockedHTTPClient( - viewerQuery("williammartin"), - getLatestPendingReviewQuery(getLatestPendingReviewQueryParams{ - author: "williammartin", - owner: "owner", - repo: "repo", - prNum: 42, - - reviews: []getLatestPendingReviewQueryReview{ + viewerIDQuery("U_viewer"), + getPendingReviewsQuery(getPendingReviewsQueryParams{ + owner: "owner", + repo: "repo", + prNum: 42, + + reviews: []pendingReviewQueryReview{ + { + id: "PR_other", + authorID: "U_other", + }, { - id: "PR_kwDODKw3uc6WYN1T", - state: "PENDING", - url: "https://github.com/owner/repo/pull/42", + id: "PR_kwDODKw3uc6WYN1T", + authorID: "U_viewer", + authorField: "botId", }, }, }), @@ -3740,18 +3743,16 @@ func TestAddPullRequestReviewCommentToPendingReview(t *testing.T) { "startSide": "RIGHT", }, mockedClient: githubv4mock.NewMockedHTTPClient( - viewerQuery("williammartin"), - getLatestPendingReviewQuery(getLatestPendingReviewQueryParams{ - author: "williammartin", - owner: "owner", - repo: "repo", - prNum: 42, - - reviews: []getLatestPendingReviewQueryReview{ + viewerIDQuery("U_viewer"), + getPendingReviewsQuery(getPendingReviewsQueryParams{ + owner: "owner", + repo: "repo", + prNum: 42, + + reviews: []pendingReviewQueryReview{ { - id: "PR_kwDODKw3uc6WYN1T", - state: "PENDING", - url: "https://github.com/owner/repo/pull/42", + id: "PR_kwDODKw3uc6WYN1T", + authorID: "U_viewer", }, }, }), @@ -3821,18 +3822,16 @@ func TestAddPullRequestReviewCommentToPendingReview(t *testing.T) { "side": "RIGHT", }, mockedClient: githubv4mock.NewMockedHTTPClient( - viewerQuery("williammartin"), - getLatestPendingReviewQuery(getLatestPendingReviewQueryParams{ - author: "williammartin", - owner: "owner", - repo: "repo", - prNum: 42, - - reviews: []getLatestPendingReviewQueryReview{ + viewerIDQuery("U_viewer"), + getPendingReviewsQuery(getPendingReviewsQueryParams{ + owner: "owner", + repo: "repo", + prNum: 42, + + reviews: []pendingReviewQueryReview{ { - id: "PR_kwDODKw3uc6WYN1T", - state: "PENDING", - url: "https://github.com/owner/repo/pull/42", + id: "PR_kwDODKw3uc6WYN1T", + authorID: "U_viewer", }, }, }), @@ -3939,18 +3938,16 @@ func TestSubmitPendingPullRequestReview(t *testing.T) { "body": "This is a test review", }, mockedClient: githubv4mock.NewMockedHTTPClient( - viewerQuery("williammartin"), - getLatestPendingReviewQuery(getLatestPendingReviewQueryParams{ - author: "williammartin", - owner: "owner", - repo: "repo", - prNum: 42, - - reviews: []getLatestPendingReviewQueryReview{ + viewerIDQuery("U_viewer"), + getPendingReviewsQuery(getPendingReviewsQueryParams{ + owner: "owner", + repo: "repo", + prNum: 42, + + reviews: []pendingReviewQueryReview{ { - id: "PR_kwDODKw3uc6WYN1T", - state: "PENDING", - url: "https://github.com/owner/repo/pull/42", + id: "PR_kwDODKw3uc6WYN1T", + authorID: "U_viewer", }, }, }), @@ -4040,18 +4037,16 @@ func TestDeletePendingPullRequestReview(t *testing.T) { "pullNumber": float64(42), }, mockedClient: githubv4mock.NewMockedHTTPClient( - viewerQuery("williammartin"), - getLatestPendingReviewQuery(getLatestPendingReviewQueryParams{ - author: "williammartin", - owner: "owner", - repo: "repo", - prNum: 42, - - reviews: []getLatestPendingReviewQueryReview{ + viewerIDQuery("U_viewer"), + getPendingReviewsQuery(getPendingReviewsQueryParams{ + owner: "owner", + repo: "repo", + prNum: 42, + + reviews: []pendingReviewQueryReview{ { - id: "PR_kwDODKw3uc6WYN1T", - state: "PENDING", - url: "https://github.com/owner/repo/pull/42", + id: "PR_kwDODKw3uc6WYN1T", + authorID: "U_viewer", }, }, }), @@ -4106,6 +4101,57 @@ func TestDeletePendingPullRequestReview(t *testing.T) { } } +func TestGetPendingPullRequestReviewForViewerPaginates(t *testing.T) { + t.Parallel() + + var reviewQueries atomic.Int32 + transport := NewMockRoundTripper().OnRequest(http.MethodPost, "/graphql", func(w http.ResponseWriter, r *http.Request) { + var request struct { + Query string `json:"query"` + Variables map[string]any `json:"variables"` + } + require.NoError(t, json.NewDecoder(r.Body).Decode(&request)) + + w.Header().Set("Content-Type", "application/json") + switch { + case strings.Contains(request.Query, "viewer"): + require.NoError(t, json.NewEncoder(w).Encode(map[string]any{ + "data": map[string]any{ + "viewer": map[string]any{"id": "U_viewer"}, + }, + })) + case strings.Contains(request.Query, "reviews"): + queryNumber := reviewQueries.Add(1) + if queryNumber == 1 { + assert.Nil(t, request.Variables["after"]) + require.NoError(t, json.NewEncoder(w).Encode(pendingReviewsResponse( + []pendingReviewQueryReview{{id: "PR_other", authorID: "U_other"}}, + true, + "cursor-1", + ))) + return + } + + assert.Equal(t, "cursor-1", request.Variables["after"]) + require.NoError(t, json.NewEncoder(w).Encode(pendingReviewsResponse( + []pendingReviewQueryReview{{id: "PR_viewer", authorID: "U_viewer"}}, + false, + "", + ))) + default: + t.Fatalf("unexpected GraphQL query: %s", request.Query) + } + }) + + client := githubv4.NewClient(&http.Client{Transport: transport}) + reviewID, result := getPendingPullRequestReviewForViewer(context.Background(), client, "owner", "repo", 42) + + require.Nil(t, result) + require.NotNil(t, reviewID) + assert.Equal(t, githubv4.ID("PR_viewer"), *reviewID) + assert.Equal(t, int32(2), reviewQueries.Load()) +} + func TestGetPullRequestDiff(t *testing.T) { t.Parallel() @@ -4252,76 +4298,103 @@ index 5d6e7b2..8a4f5c3 100644 } } -func viewerQuery(login string) githubv4mock.Matcher { +func viewerIDQuery(id string) githubv4mock.Matcher { return githubv4mock.NewQueryMatcher( struct { Viewer struct { - Login githubv4.String + ID githubv4.ID } `graphql:"viewer"` }{}, map[string]any{}, githubv4mock.DataResponse(map[string]any{ "viewer": map[string]any{ - "login": login, + "id": id, }, }), ) } -type getLatestPendingReviewQueryReview struct { - id string - state string - url string +type pendingReviewQueryReview struct { + id string + authorID string + authorField string } -type getLatestPendingReviewQueryParams struct { - author string - owner string - repo string - prNum int32 +type getPendingReviewsQueryParams struct { + owner string + repo string + prNum int32 - reviews []getLatestPendingReviewQueryReview + reviews []pendingReviewQueryReview } -func getLatestPendingReviewQuery(p getLatestPendingReviewQueryParams) githubv4mock.Matcher { - return githubv4mock.NewQueryMatcher( +func getPendingReviewsQuery(p getPendingReviewsQueryParams) githubv4mock.Matcher { + matcher := githubv4mock.NewQueryMatcher( struct { Repository struct { PullRequest struct { Reviews struct { Nodes []struct { - ID githubv4.ID - State githubv4.PullRequestReviewState - URL githubv4.URI + ID githubv4.ID + Author pendingReviewAuthor } - } `graphql:"reviews(first: 1, author: $author)"` + PageInfo struct { + HasNextPage githubv4.Boolean + EndCursor githubv4.String + } + } `graphql:"reviews(first: 100, after: $after, states: $states)"` } `graphql:"pullRequest(number: $prNum)"` } `graphql:"repository(owner: $owner, name: $name)"` }{}, map[string]any{ - "author": githubv4.String(p.author), + "after": (*githubv4.String)(nil), "owner": githubv4.String(p.owner), "name": githubv4.String(p.repo), "prNum": githubv4.Int(p.prNum), + "states": []githubv4.PullRequestReviewState{githubv4.PullRequestReviewStatePending}, }, - githubv4mock.DataResponse( - map[string]any{ - "repository": map[string]any{ - "pullRequest": map[string]any{ - "reviews": map[string]any{ - "nodes": []any{ - map[string]any{ - "id": p.reviews[0].id, - "state": p.reviews[0].state, - "url": p.reviews[0].url, - }, - }, - }, + githubv4mock.DataResponse(map[string]any{ + "repository": pendingReviewsData(p.reviews, false, "")["repository"], + }), + ) + matcher.Variables["states"] = []any{"PENDING"} + return matcher +} + +func pendingReviewsResponse(reviews []pendingReviewQueryReview, hasNextPage bool, endCursor string) map[string]any { + return map[string]any{ + "data": pendingReviewsData(reviews, hasNextPage, endCursor), + } +} + +func pendingReviewsData(reviews []pendingReviewQueryReview, hasNextPage bool, endCursor string) map[string]any { + nodes := make([]any, 0, len(reviews)) + for _, review := range reviews { + authorField := review.authorField + if authorField == "" { + authorField = "userId" + } + nodes = append(nodes, map[string]any{ + "id": review.id, + "author": map[string]any{ + authorField: review.authorID, + }, + }) + } + + return map[string]any{ + "repository": map[string]any{ + "pullRequest": map[string]any{ + "reviews": map[string]any{ + "nodes": nodes, + "pageInfo": map[string]any{ + "hasNextPage": hasNextPage, + "endCursor": endCursor, }, }, }, - ), - ) + }, + } } func TestAddReplyToPullRequestComment(t *testing.T) { From cd5023333609cb54c13f7f96e84ddc67028540e5 Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Fri, 2 Oct 2026 11:09:32 +0200 Subject: [PATCH 06/13] build(deps): remediate npm alerts and refresh safe dependencies (#3370) * ci(deps): refresh workflow dependencies and align lint Go version Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * build(deps): refresh safe dependencies and Dependabot coverage Remediate all nine open npm alerts, update compatible Go and UI dependencies, refresh pinned Actions and the Go image, and backport the Cosign verification fix. Add weekly npm updates and regenerate third-party notices. Raise the minimum supported Go version to 1.26.8 for the latest x/net dependency and align CI linting with that toolchain. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * ci: preserve Docker tag ordering and remove trailing whitespace Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * chore: regenerate license files Auto-generated by license-check workflow * chore: regenerate license files Auto-generated by license-check workflow --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: github-actions[bot] --- .github/actions/build-ui/action.yml | 4 +- .github/dependabot.yml | 4 + .github/workflows/code-scanning.yml | 10 +- .github/workflows/docker-publish.yml | 7 +- .github/workflows/lint.yml | 2 +- .golangci.yml | 3 + CONTRIBUTING.md | 4 +- Dockerfile | 2 +- go.mod | 18 +- go.sum | 36 +- pkg/github/granular_tools_test.go | 2 +- pkg/github/pullrequests.go | 2 +- pkg/github/pullrequests_test.go | 6 +- pkg/http/oauth/oauth_test.go | 2 +- third-party-licenses.darwin.md | 18 +- third-party-licenses.linux.md | 18 +- third-party-licenses.windows.md | 18 +- ui/package-lock.json | 944 +++++++++++++-------------- ui/package.json | 22 +- 19 files changed, 547 insertions(+), 575 deletions(-) diff --git a/.github/actions/build-ui/action.yml b/.github/actions/build-ui/action.yml index 1ab6dc4e73..7dfe93f518 100644 --- a/.github/actions/build-ui/action.yml +++ b/.github/actions/build-ui/action.yml @@ -6,7 +6,7 @@ runs: steps: - name: Cache UI artifacts id: cache-ui - uses: actions/cache@v5 + uses: actions/cache@v6 with: path: | pkg/github/ui_dist/get-me.html @@ -18,7 +18,7 @@ runs: - name: Set up Node.js if: steps.cache-ui.outputs.cache-hit != 'true' - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version: "22" cache: npm diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d43dcee742..dbb35ef4d5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,6 +9,10 @@ updates: directory: "/" schedule: interval: "weekly" + - package-ecosystem: "npm" + directory: "/ui" + schedule: + interval: "weekly" - package-ecosystem: "docker" directory: "/" schedule: diff --git a/.github/workflows/code-scanning.yml b/.github/workflows/code-scanning.yml index ca08306237..18ec6d7cc3 100644 --- a/.github/workflows/code-scanning.yml +++ b/.github/workflows/code-scanning.yml @@ -44,7 +44,7 @@ jobs: uses: actions/checkout@v7 - name: Initialize CodeQL - uses: github/codeql-action/init@v4.37.9 + uses: github/codeql-action/init@v4.38.2 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -61,13 +61,13 @@ jobs: threat-models: [ ] - name: Setup proxy for registries id: proxy - uses: github/codeql-action/start-proxy@v4.37.9 + uses: github/codeql-action/start-proxy@v4.38.2 with: registries_credentials: ${{ secrets.GITHUB_REGISTRIES_PROXY }} language: ${{ matrix.language }} - name: Configure - uses: github/codeql-action/resolve-environment@v4.37.9 + uses: github/codeql-action/resolve-environment@v4.38.2 id: resolve-environment with: language: ${{ matrix.language }} @@ -91,10 +91,10 @@ jobs: uses: ./.github/actions/build-ui - name: Autobuild - uses: github/codeql-action/autobuild@v4.37.9 + uses: github/codeql-action/autobuild@v4.38.2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4.37.9 + uses: github/codeql-action/analyze@v4.38.2 env: CODEQL_PROXY_HOST: ${{ steps.proxy.outputs.proxy_host }} CODEQL_PROXY_PORT: ${{ steps.proxy.outputs.proxy_port }} diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 6d756188a2..db5e847a5a 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -48,13 +48,13 @@ jobs: if: github.event_name != 'pull_request' uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 #v4.1.2 with: - cosign-release: "v2.2.4" + cosign-release: "v2.6.5" # Set up BuildKit Docker container builder to be able to build # multi-platform images and export cache # https://github.com/docker/setup-buildx-action - name: Set up Docker Buildx - uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 + uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 # Login against a Docker registry except on PR # https://github.com/docker/login-action @@ -106,7 +106,7 @@ jobs: # https://github.com/docker/build-push-action - name: Build and push Docker image id: build-and-push - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 with: context: . push: ${{ github.event_name != 'pull_request' }} @@ -135,4 +135,3 @@ jobs: # This step uses the identity token to provision an ephemeral certificate # against the sigstore community Fulcio instance. run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} - \ No newline at end of file diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6119ee9f0f..e4acc354b7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -18,7 +18,7 @@ jobs: uses: ./.github/actions/build-ui - uses: actions/setup-go@v7 with: - go-version: '1.25' + go-version-file: 'go.mod' - name: golangci-lint uses: golangci/golangci-lint-action@v9 with: diff --git a/.golangci.yml b/.golangci.yml index a32fc897e8..a17770127a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -35,6 +35,9 @@ linters: - revive text: "var-naming: avoid package names that conflict with Go standard library package names" settings: + modernize: + disable: + - newexpr staticcheck: checks: - "all" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c16cd27dd..76af5d31f5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,9 +26,11 @@ Thanks for contributing and for helping us build toolsets that are truly valuabl These are one time installations required to be able to test your changes locally as part of the pull request (PR) submission process. -1. Install Go [through download](https://go.dev/doc/install) | [through Homebrew](https://formulae.brew.sh/formula/go) +1. Install Go 1.26.8 or later [through download](https://go.dev/doc/install) | [through Homebrew](https://formulae.brew.sh/formula/go) 2. [Install golangci-lint v2](https://golangci-lint.run/welcome/install/#local-installation) +The repository-pinned golangci-lint v2.9.0 supports Go 1.26, but cannot read Go 1.27 export data. When using a newer Go installation, run lint with `GOTOOLCHAIN=go1.26.8 script/lint`. + ## Submitting a pull request 1. [Fork][fork] and clone the repository diff --git a/Dockerfile b/Dockerfile index 19094d114d..04e39a09f2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ COPY ui/ ./ui/ RUN mkdir -p ./pkg/github/ui_dist && \ cd ui && npm run build -FROM golang:1.27.1-alpine@sha256:cf6fca6641884b8433441b2b0652976f975e1d0fdd26d177eaaf8596087f3125 AS build +FROM golang:1.27.1-alpine@sha256:8a5910f31396cd4d89662f56c68b3ae31d374308270a1c3bd96672ee5ed43414 AS build ARG VERSION="dev" # Set the working directory diff --git a/go.mod b/go.mod index f0237a1625..709dba1a32 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/github/github-mcp-server -go 1.25.12 +go 1.26.8 require ( github.com/go-chi/chi/v5 v5.3.2 @@ -10,17 +10,17 @@ require ( github.com/josephburnett/jd/v2 v2.5.0 github.com/lithammer/fuzzysearch v1.1.8 github.com/microcosm-cc/bluemonday v1.0.27 - github.com/modelcontextprotocol/go-sdk v1.7.0 + github.com/modelcontextprotocol/go-sdk v1.8.0 github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021 - github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 - github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 + github.com/shurcooL/githubv4 v0.0.0-20260209031235-2402fdf4a9ed + github.com/shurcooL/graphql v0.0.0-20240915155400-7ee5256398cf github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.12.1 github.com/yosida95/uritemplate/v3 v3.0.2 - golang.org/x/net v0.55.0 - golang.org/x/oauth2 v0.36.0 + golang.org/x/net v0.59.0 + golang.org/x/oauth2 v0.37.0 ) require ( @@ -39,8 +39,8 @@ require ( github.com/stretchr/objx v0.5.3 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.45.0 // indirect - golang.org/x/text v0.37.0 // indirect + golang.org/x/sync v0.23.0 // indirect + golang.org/x/sys v0.48.0 // indirect + golang.org/x/text v0.42.0 // indirect golang.org/x/time v0.15.0 // indirect ) diff --git a/go.sum b/go.sum index 4974f0c247..d64a3d05c0 100644 --- a/go.sum +++ b/go.sum @@ -34,8 +34,8 @@ github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8 github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= -github.com/modelcontextprotocol/go-sdk v1.7.0 h1:yqjY2dsbKAC0LSuWZVBMrHgiG8ukXv6NRo0JiALay44= -github.com/modelcontextprotocol/go-sdk v1.7.0/go.mod h1:dL7u98E/zjJTGzEq+j30jQ8K2k1mb6LeAH4inEcSGts= +github.com/modelcontextprotocol/go-sdk v1.8.0 h1:KIvahhYqwtbeniWVPs3TcXEA7b8jEtwfBpOTAI+Urx4= +github.com/modelcontextprotocol/go-sdk v1.8.0/go.mod h1:dL7u98E/zjJTGzEq+j30jQ8K2k1mb6LeAH4inEcSGts= github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021 h1:31Y+Yu373ymebRdJN1cWLLooHH8xAr0MhKTEJGV/87g= github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021/go.mod h1:WERUkUryfUWlrHnFSO/BEUZ+7Ns8aZy7iVOGewxKzcc= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= @@ -49,10 +49,10 @@ github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc= github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= github.com/segmentio/encoding v0.5.4 h1:OW1VRern8Nw6ITAtwSZ7Idrl3MXCFwXHPgqESYfvNt0= github.com/segmentio/encoding v0.5.4/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0= -github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 h1:cYCy18SHPKRkvclm+pWm1Lk4YrREb4IOIb/YdFO0p2M= -github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8= -github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0= -github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE= +github.com/shurcooL/githubv4 v0.0.0-20260209031235-2402fdf4a9ed h1:KT7hI8vYXgU0s2qaMkrfq9tCA1w/iEPgfredVP+4Tzw= +github.com/shurcooL/githubv4 v0.0.0-20260209031235-2402fdf4a9ed/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8= +github.com/shurcooL/graphql v0.0.0-20240915155400-7ee5256398cf h1:o1uxfymjZ7jZ4MsgCErcwWGtVKSiNAXtS59Lhs6uI/g= +github.com/shurcooL/graphql v0.0.0-20240915155400-7ee5256398cf/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= @@ -86,23 +86,23 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= -golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= -golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= -golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= +golang.org/x/net v0.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues= +golang.org/x/net v0.59.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg= +golang.org/x/oauth2 v0.37.0 h1:JUlcxA8oAtauLfiH8FX2/FkAWHAdi0QtGCGc+hofE98= +golang.org/x/oauth2 v0.37.0/go.mod h1:IxwZNxUULJmpBFf9K/9NTMSIfZZuvuTy1gGxhigP/58= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk= +golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= -golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= +golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -111,15 +111,15 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI= +golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= -golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= +golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= +golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/github/granular_tools_test.go b/pkg/github/granular_tools_test.go index 3db96a3f2a..90fe6d041a 100644 --- a/pkg/github/granular_tools_test.go +++ b/pkg/github/granular_tools_test.go @@ -1683,7 +1683,7 @@ func TestGranularAddPullRequestReviewComment(t *testing.T) { } `graphql:"addPullRequestReviewThread(input: $input)"` }{}, githubv4.AddPullRequestReviewThreadInput{ - Path: githubv4.String("src/main.go"), + Path: githubv4.NewString("src/main.go"), Body: githubv4.String("This needs a fix"), SubjectType: githubv4mock.Ptr(githubv4.PullRequestReviewThreadSubjectTypeLine), Line: githubv4mock.Ptr(githubv4.Int(42)), diff --git a/pkg/github/pullrequests.go b/pkg/github/pullrequests.go index 19885caa56..e30995d66e 100644 --- a/pkg/github/pullrequests.go +++ b/pkg/github/pullrequests.go @@ -2157,7 +2157,7 @@ func AddCommentToPendingReviewCall(ctx context.Context, client *githubv4.Client, ctx, &addPullRequestReviewThreadMutation, githubv4.AddPullRequestReviewThreadInput{ - Path: githubv4.String(params.Path), + Path: newGQLStringlikePtr[githubv4.String](¶ms.Path), Body: githubv4.String(params.Body), SubjectType: newGQLStringlikePtr[githubv4.PullRequestReviewThreadSubjectType](¶ms.SubjectType), Line: newGQLIntPtr(params.Line), diff --git a/pkg/github/pullrequests_test.go b/pkg/github/pullrequests_test.go index 8bca934c30..e2d8d88140 100644 --- a/pkg/github/pullrequests_test.go +++ b/pkg/github/pullrequests_test.go @@ -3708,7 +3708,7 @@ func TestAddPullRequestReviewCommentToPendingReview(t *testing.T) { } `graphql:"addPullRequestReviewThread(input: $input)"` }{}, githubv4.AddPullRequestReviewThreadInput{ - Path: githubv4.String("file.go"), + Path: githubv4.NewString("file.go"), Body: githubv4.String("This is a test comment"), SubjectType: githubv4mock.Ptr(githubv4.PullRequestReviewThreadSubjectTypeLine), Line: githubv4.NewInt(10), @@ -3765,7 +3765,7 @@ func TestAddPullRequestReviewCommentToPendingReview(t *testing.T) { } `graphql:"addPullRequestReviewThread(input: $input)"` }{}, githubv4.AddPullRequestReviewThreadInput{ - Path: githubv4.String("file.go"), + Path: githubv4.NewString("file.go"), Body: githubv4.String("This is a test comment"), SubjectType: githubv4mock.Ptr(githubv4.PullRequestReviewThreadSubjectTypeLine), Line: githubv4.NewInt(10), @@ -3844,7 +3844,7 @@ func TestAddPullRequestReviewCommentToPendingReview(t *testing.T) { } `graphql:"addPullRequestReviewThread(input: $input)"` }{}, githubv4.AddPullRequestReviewThreadInput{ - Path: githubv4.String("file.go"), + Path: githubv4.NewString("file.go"), Body: githubv4.String("Comment on non-existent line"), SubjectType: githubv4mock.Ptr(githubv4.PullRequestReviewThreadSubjectTypeLine), Line: githubv4.NewInt(999), diff --git a/pkg/http/oauth/oauth_test.go b/pkg/http/oauth/oauth_test.go index c2ef660104..515a0a471f 100644 --- a/pkg/http/oauth/oauth_test.go +++ b/pkg/http/oauth/oauth_test.go @@ -436,7 +436,7 @@ func TestHandleProtectedResource(t *testing.T) { host: "api.example.com", method: http.MethodGet, expectedStatusCode: http.StatusOK, -expectedScopes: []string{ + expectedScopes: []string{ "repo", "read:org", "read:user", diff --git a/third-party-licenses.darwin.md b/third-party-licenses.darwin.md index 9e8cd0b794..495abb0250 100644 --- a/third-party-licenses.darwin.md +++ b/third-party-licenses.darwin.md @@ -24,15 +24,15 @@ The following packages are included for the amd64, arm64 architectures. - [github.com/josephburnett/jd/v2](https://pkg.go.dev/github.com/josephburnett/jd/v2) ([MIT](https://github.com/josephburnett/jd/blob/v2.5.0/v2/LICENSE)) - [github.com/lithammer/fuzzysearch/fuzzy](https://pkg.go.dev/github.com/lithammer/fuzzysearch/fuzzy) ([MIT](https://github.com/lithammer/fuzzysearch/blob/v1.1.8/LICENSE)) - [github.com/microcosm-cc/bluemonday](https://pkg.go.dev/github.com/microcosm-cc/bluemonday) ([BSD-3-Clause](https://github.com/microcosm-cc/bluemonday/blob/v1.0.27/LICENSE.md)) - - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([Apache-2.0](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0/LICENSE)) - - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([MIT](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0/LICENSE)) + - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([Apache-2.0](https://github.com/modelcontextprotocol/go-sdk/blob/v1.8.0/LICENSE)) + - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([MIT](https://github.com/modelcontextprotocol/go-sdk/blob/v1.8.0/LICENSE)) - [github.com/muesli/cache2go](https://pkg.go.dev/github.com/muesli/cache2go) ([BSD-3-Clause](https://github.com/muesli/cache2go/blob/518229cd8021/LICENSE.txt)) - [github.com/pelletier/go-toml/v2](https://pkg.go.dev/github.com/pelletier/go-toml/v2) ([MIT](https://github.com/pelletier/go-toml/blob/v2.2.4/LICENSE)) - [github.com/sagikazarmark/locafero](https://pkg.go.dev/github.com/sagikazarmark/locafero) ([MIT](https://github.com/sagikazarmark/locafero/blob/v0.11.0/LICENSE)) - [github.com/segmentio/asm](https://pkg.go.dev/github.com/segmentio/asm) ([MIT](https://github.com/segmentio/asm/blob/v1.1.3/LICENSE)) - [github.com/segmentio/encoding](https://pkg.go.dev/github.com/segmentio/encoding) ([MIT](https://github.com/segmentio/encoding/blob/v0.5.4/LICENSE)) - - [github.com/shurcooL/githubv4](https://pkg.go.dev/github.com/shurcooL/githubv4) ([MIT](https://github.com/shurcooL/githubv4/blob/48295856cce7/LICENSE)) - - [github.com/shurcooL/graphql](https://pkg.go.dev/github.com/shurcooL/graphql) ([MIT](https://github.com/shurcooL/graphql/blob/ed46e5a46466/LICENSE)) + - [github.com/shurcooL/githubv4](https://pkg.go.dev/github.com/shurcooL/githubv4) ([MIT](https://github.com/shurcooL/githubv4/blob/2402fdf4a9ed/LICENSE)) + - [github.com/shurcooL/graphql](https://pkg.go.dev/github.com/shurcooL/graphql) ([MIT](https://github.com/shurcooL/graphql/blob/7ee5256398cf/LICENSE)) - [github.com/sourcegraph/conc](https://pkg.go.dev/github.com/sourcegraph/conc) ([MIT](https://github.com/sourcegraph/conc/blob/5f936abd7ae8/LICENSE)) - [github.com/spf13/afero](https://pkg.go.dev/github.com/spf13/afero) ([Apache-2.0](https://github.com/spf13/afero/blob/v1.15.0/LICENSE.txt)) - [github.com/spf13/cast](https://pkg.go.dev/github.com/spf13/cast) ([MIT](https://github.com/spf13/cast/blob/v1.10.0/LICENSE)) @@ -42,11 +42,11 @@ The following packages are included for the amd64, arm64 architectures. - [github.com/subosito/gotenv](https://pkg.go.dev/github.com/subosito/gotenv) ([MIT](https://github.com/subosito/gotenv/blob/v1.6.0/LICENSE)) - [github.com/yosida95/uritemplate/v3](https://pkg.go.dev/github.com/yosida95/uritemplate/v3) ([BSD-3-Clause](https://github.com/yosida95/uritemplate/blob/v3.0.2/LICENSE)) - [go.yaml.in/yaml/v3](https://pkg.go.dev/go.yaml.in/yaml/v3) ([MIT](https://github.com/yaml/go-yaml/blob/v3.0.5/LICENSE)) - - [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.55.0:LICENSE)) - - [golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2) ([BSD-3-Clause](https://cs.opensource.google/go/x/oauth2/+/v0.36.0:LICENSE)) - - [golang.org/x/sync/errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) ([BSD-3-Clause](https://cs.opensource.google/go/x/sync/+/v0.20.0:LICENSE)) - - [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.45.0:LICENSE)) - - [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.37.0:LICENSE)) + - [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.59.0:LICENSE)) + - [golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2) ([BSD-3-Clause](https://cs.opensource.google/go/x/oauth2/+/v0.37.0:LICENSE)) + - [golang.org/x/sync/errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) ([BSD-3-Clause](https://cs.opensource.google/go/x/sync/+/v0.23.0:LICENSE)) + - [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.48.0:LICENSE)) + - [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.42.0:LICENSE)) - [golang.org/x/time/rate](https://pkg.go.dev/golang.org/x/time/rate) ([BSD-3-Clause](https://cs.opensource.google/go/x/time/+/v0.15.0:LICENSE)) [github/github-mcp-server]: https://github.com/github/github-mcp-server diff --git a/third-party-licenses.linux.md b/third-party-licenses.linux.md index f5b267f28d..d46c953766 100644 --- a/third-party-licenses.linux.md +++ b/third-party-licenses.linux.md @@ -24,15 +24,15 @@ The following packages are included for the 386, amd64, arm64 architectures. - [github.com/josephburnett/jd/v2](https://pkg.go.dev/github.com/josephburnett/jd/v2) ([MIT](https://github.com/josephburnett/jd/blob/v2.5.0/v2/LICENSE)) - [github.com/lithammer/fuzzysearch/fuzzy](https://pkg.go.dev/github.com/lithammer/fuzzysearch/fuzzy) ([MIT](https://github.com/lithammer/fuzzysearch/blob/v1.1.8/LICENSE)) - [github.com/microcosm-cc/bluemonday](https://pkg.go.dev/github.com/microcosm-cc/bluemonday) ([BSD-3-Clause](https://github.com/microcosm-cc/bluemonday/blob/v1.0.27/LICENSE.md)) - - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([Apache-2.0](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0/LICENSE)) - - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([MIT](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0/LICENSE)) + - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([Apache-2.0](https://github.com/modelcontextprotocol/go-sdk/blob/v1.8.0/LICENSE)) + - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([MIT](https://github.com/modelcontextprotocol/go-sdk/blob/v1.8.0/LICENSE)) - [github.com/muesli/cache2go](https://pkg.go.dev/github.com/muesli/cache2go) ([BSD-3-Clause](https://github.com/muesli/cache2go/blob/518229cd8021/LICENSE.txt)) - [github.com/pelletier/go-toml/v2](https://pkg.go.dev/github.com/pelletier/go-toml/v2) ([MIT](https://github.com/pelletier/go-toml/blob/v2.2.4/LICENSE)) - [github.com/sagikazarmark/locafero](https://pkg.go.dev/github.com/sagikazarmark/locafero) ([MIT](https://github.com/sagikazarmark/locafero/blob/v0.11.0/LICENSE)) - [github.com/segmentio/asm](https://pkg.go.dev/github.com/segmentio/asm) ([MIT](https://github.com/segmentio/asm/blob/v1.1.3/LICENSE)) - [github.com/segmentio/encoding](https://pkg.go.dev/github.com/segmentio/encoding) ([MIT](https://github.com/segmentio/encoding/blob/v0.5.4/LICENSE)) - - [github.com/shurcooL/githubv4](https://pkg.go.dev/github.com/shurcooL/githubv4) ([MIT](https://github.com/shurcooL/githubv4/blob/48295856cce7/LICENSE)) - - [github.com/shurcooL/graphql](https://pkg.go.dev/github.com/shurcooL/graphql) ([MIT](https://github.com/shurcooL/graphql/blob/ed46e5a46466/LICENSE)) + - [github.com/shurcooL/githubv4](https://pkg.go.dev/github.com/shurcooL/githubv4) ([MIT](https://github.com/shurcooL/githubv4/blob/2402fdf4a9ed/LICENSE)) + - [github.com/shurcooL/graphql](https://pkg.go.dev/github.com/shurcooL/graphql) ([MIT](https://github.com/shurcooL/graphql/blob/7ee5256398cf/LICENSE)) - [github.com/sourcegraph/conc](https://pkg.go.dev/github.com/sourcegraph/conc) ([MIT](https://github.com/sourcegraph/conc/blob/5f936abd7ae8/LICENSE)) - [github.com/spf13/afero](https://pkg.go.dev/github.com/spf13/afero) ([Apache-2.0](https://github.com/spf13/afero/blob/v1.15.0/LICENSE.txt)) - [github.com/spf13/cast](https://pkg.go.dev/github.com/spf13/cast) ([MIT](https://github.com/spf13/cast/blob/v1.10.0/LICENSE)) @@ -42,11 +42,11 @@ The following packages are included for the 386, amd64, arm64 architectures. - [github.com/subosito/gotenv](https://pkg.go.dev/github.com/subosito/gotenv) ([MIT](https://github.com/subosito/gotenv/blob/v1.6.0/LICENSE)) - [github.com/yosida95/uritemplate/v3](https://pkg.go.dev/github.com/yosida95/uritemplate/v3) ([BSD-3-Clause](https://github.com/yosida95/uritemplate/blob/v3.0.2/LICENSE)) - [go.yaml.in/yaml/v3](https://pkg.go.dev/go.yaml.in/yaml/v3) ([MIT](https://github.com/yaml/go-yaml/blob/v3.0.5/LICENSE)) - - [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.55.0:LICENSE)) - - [golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2) ([BSD-3-Clause](https://cs.opensource.google/go/x/oauth2/+/v0.36.0:LICENSE)) - - [golang.org/x/sync/errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) ([BSD-3-Clause](https://cs.opensource.google/go/x/sync/+/v0.20.0:LICENSE)) - - [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.45.0:LICENSE)) - - [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.37.0:LICENSE)) + - [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.59.0:LICENSE)) + - [golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2) ([BSD-3-Clause](https://cs.opensource.google/go/x/oauth2/+/v0.37.0:LICENSE)) + - [golang.org/x/sync/errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) ([BSD-3-Clause](https://cs.opensource.google/go/x/sync/+/v0.23.0:LICENSE)) + - [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.48.0:LICENSE)) + - [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.42.0:LICENSE)) - [golang.org/x/time/rate](https://pkg.go.dev/golang.org/x/time/rate) ([BSD-3-Clause](https://cs.opensource.google/go/x/time/+/v0.15.0:LICENSE)) [github/github-mcp-server]: https://github.com/github/github-mcp-server diff --git a/third-party-licenses.windows.md b/third-party-licenses.windows.md index 7e834bc36f..856d78cc68 100644 --- a/third-party-licenses.windows.md +++ b/third-party-licenses.windows.md @@ -25,15 +25,15 @@ The following packages are included for the 386, amd64, arm64 architectures. - [github.com/josephburnett/jd/v2](https://pkg.go.dev/github.com/josephburnett/jd/v2) ([MIT](https://github.com/josephburnett/jd/blob/v2.5.0/v2/LICENSE)) - [github.com/lithammer/fuzzysearch/fuzzy](https://pkg.go.dev/github.com/lithammer/fuzzysearch/fuzzy) ([MIT](https://github.com/lithammer/fuzzysearch/blob/v1.1.8/LICENSE)) - [github.com/microcosm-cc/bluemonday](https://pkg.go.dev/github.com/microcosm-cc/bluemonday) ([BSD-3-Clause](https://github.com/microcosm-cc/bluemonday/blob/v1.0.27/LICENSE.md)) - - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([Apache-2.0](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0/LICENSE)) - - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([MIT](https://github.com/modelcontextprotocol/go-sdk/blob/v1.7.0/LICENSE)) + - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([Apache-2.0](https://github.com/modelcontextprotocol/go-sdk/blob/v1.8.0/LICENSE)) + - [github.com/modelcontextprotocol/go-sdk](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk) ([MIT](https://github.com/modelcontextprotocol/go-sdk/blob/v1.8.0/LICENSE)) - [github.com/muesli/cache2go](https://pkg.go.dev/github.com/muesli/cache2go) ([BSD-3-Clause](https://github.com/muesli/cache2go/blob/518229cd8021/LICENSE.txt)) - [github.com/pelletier/go-toml/v2](https://pkg.go.dev/github.com/pelletier/go-toml/v2) ([MIT](https://github.com/pelletier/go-toml/blob/v2.2.4/LICENSE)) - [github.com/sagikazarmark/locafero](https://pkg.go.dev/github.com/sagikazarmark/locafero) ([MIT](https://github.com/sagikazarmark/locafero/blob/v0.11.0/LICENSE)) - [github.com/segmentio/asm](https://pkg.go.dev/github.com/segmentio/asm) ([MIT](https://github.com/segmentio/asm/blob/v1.1.3/LICENSE)) - [github.com/segmentio/encoding](https://pkg.go.dev/github.com/segmentio/encoding) ([MIT](https://github.com/segmentio/encoding/blob/v0.5.4/LICENSE)) - - [github.com/shurcooL/githubv4](https://pkg.go.dev/github.com/shurcooL/githubv4) ([MIT](https://github.com/shurcooL/githubv4/blob/48295856cce7/LICENSE)) - - [github.com/shurcooL/graphql](https://pkg.go.dev/github.com/shurcooL/graphql) ([MIT](https://github.com/shurcooL/graphql/blob/ed46e5a46466/LICENSE)) + - [github.com/shurcooL/githubv4](https://pkg.go.dev/github.com/shurcooL/githubv4) ([MIT](https://github.com/shurcooL/githubv4/blob/2402fdf4a9ed/LICENSE)) + - [github.com/shurcooL/graphql](https://pkg.go.dev/github.com/shurcooL/graphql) ([MIT](https://github.com/shurcooL/graphql/blob/7ee5256398cf/LICENSE)) - [github.com/sourcegraph/conc](https://pkg.go.dev/github.com/sourcegraph/conc) ([MIT](https://github.com/sourcegraph/conc/blob/5f936abd7ae8/LICENSE)) - [github.com/spf13/afero](https://pkg.go.dev/github.com/spf13/afero) ([Apache-2.0](https://github.com/spf13/afero/blob/v1.15.0/LICENSE.txt)) - [github.com/spf13/cast](https://pkg.go.dev/github.com/spf13/cast) ([MIT](https://github.com/spf13/cast/blob/v1.10.0/LICENSE)) @@ -43,11 +43,11 @@ The following packages are included for the 386, amd64, arm64 architectures. - [github.com/subosito/gotenv](https://pkg.go.dev/github.com/subosito/gotenv) ([MIT](https://github.com/subosito/gotenv/blob/v1.6.0/LICENSE)) - [github.com/yosida95/uritemplate/v3](https://pkg.go.dev/github.com/yosida95/uritemplate/v3) ([BSD-3-Clause](https://github.com/yosida95/uritemplate/blob/v3.0.2/LICENSE)) - [go.yaml.in/yaml/v3](https://pkg.go.dev/go.yaml.in/yaml/v3) ([MIT](https://github.com/yaml/go-yaml/blob/v3.0.5/LICENSE)) - - [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.55.0:LICENSE)) - - [golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2) ([BSD-3-Clause](https://cs.opensource.google/go/x/oauth2/+/v0.36.0:LICENSE)) - - [golang.org/x/sync/errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) ([BSD-3-Clause](https://cs.opensource.google/go/x/sync/+/v0.20.0:LICENSE)) - - [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.45.0:LICENSE)) - - [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.37.0:LICENSE)) + - [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.59.0:LICENSE)) + - [golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2) ([BSD-3-Clause](https://cs.opensource.google/go/x/oauth2/+/v0.37.0:LICENSE)) + - [golang.org/x/sync/errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) ([BSD-3-Clause](https://cs.opensource.google/go/x/sync/+/v0.23.0:LICENSE)) + - [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.48.0:LICENSE)) + - [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.42.0:LICENSE)) - [golang.org/x/time/rate](https://pkg.go.dev/golang.org/x/time/rate) ([BSD-3-Clause](https://cs.opensource.google/go/x/time/+/v0.15.0:LICENSE)) [github/github-mcp-server]: https://github.com/github/github-mcp-server diff --git a/ui/package-lock.json b/ui/package-lock.json index c19577b0d6..22ca32aa96 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -9,21 +9,21 @@ "version": "1.0.0", "dependencies": { "@github/markdown-toolbar-element": "^2.2.3", - "@modelcontextprotocol/ext-apps": "^1.7.2", - "@primer/octicons-react": "^19.0.0", - "@primer/react": "^36.0.0", - "react": "^18.0.0", - "react-dom": "^18.0.0", + "@modelcontextprotocol/ext-apps": "^1.7.5", + "@primer/octicons-react": "^19.38.0", + "@primer/react": "^36.27.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-markdown": "^10.1.0", "remark-gfm": "^4.0.1" }, "devDependencies": { - "@types/node": "^25.2.0", - "@types/react": "^18.0.0", - "@types/react-dom": "^18.0.0", - "@vitejs/plugin-react": "^6.0.2", - "typescript": "^5.7.0", - "vite": "^8.0.16", + "@types/node": "^25.9.9", + "@types/react": "^18.3.31", + "@types/react-dom": "^18.3.7", + "@vitejs/plugin-react": "^6.1.1", + "typescript": "^5.9.3", + "vite": "^8.3.2", "vite-plugin-singlefile": "^2.3.3" }, "engines": { @@ -87,14 +87,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", - "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", "license": "MIT", "peer": true, "dependencies": { - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -104,13 +104,13 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", "license": "MIT", "peer": true, "dependencies": { - "@babel/types": "^7.27.3" + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -176,9 +176,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "license": "MIT", "peer": true, "engines": { @@ -230,13 +230,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", - "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", "license": "MIT", "peer": true, "dependencies": { - "@babel/types": "^7.29.7" + "@babel/types": "^7.29.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -246,13 +246,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", "license": "MIT", "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -262,9 +262,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", - "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -286,18 +286,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", - "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", "license": "MIT", "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", + "@babel/generator": "^7.29.8", "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.7", + "@babel/parser": "^7.29.8", "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7", + "@babel/types": "^7.29.8", "debug": "^4.3.1" }, "engines": { @@ -305,9 +305,9 @@ } }, "node_modules/@babel/types": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", - "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", "license": "MIT", "peer": true, "dependencies": { @@ -318,40 +318,6 @@ "node": ">=6.9.0" } }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@emotion/is-prop-valid": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", @@ -408,15 +374,15 @@ "license": "MIT" }, "node_modules/@github/tab-container-element": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/@github/tab-container-element/-/tab-container-element-4.8.2.tgz", - "integrity": "sha512-WkaM4mfs8x7dXRWEaDb5deC0OhH6sGQ5cw8i/sVw25gikl4f8C7mHj0kihL5k3eKIIqmGT1Fdswdoi+9ZLDpRA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@github/tab-container-element/-/tab-container-element-4.9.0.tgz", + "integrity": "sha512-wUk72T/pA3OG+YCdVaDcwHwpT2uyVmHlu6CjZyeRfoECWNQovFZAMqp8JVHjweeLxp2lM2ASqa8lLrkCskrB2g==", "license": "MIT" }, "node_modules/@hono/node-server": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.1.tgz", - "integrity": "sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.3.tgz", + "integrity": "sha512-TA//nWMqPhbfdfneACk6t5a9eqbS9lABEPyKn0/xZTah3H3U2XaVg85rJFl0/Fyit0I552YDHgXGVSf3GwqbUw==", "license": "MIT", "peer": true, "engines": { @@ -459,9 +425,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "license": "MIT", "peer": true }, @@ -483,15 +449,15 @@ "license": "BSD-3-Clause" }, "node_modules/@lit-labs/ssr-dom-shim": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.5.1.tgz", - "integrity": "sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.6.0.tgz", + "integrity": "sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==", "license": "BSD-3-Clause" }, "node_modules/@modelcontextprotocol/ext-apps": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/ext-apps/-/ext-apps-1.7.2.tgz", - "integrity": "sha512-OOWKDxdAjYDcgHkmzVzccyyag3FK+jBWPaWu4WvTxFsU4R/cgOX4eep66zPRA5n4v6WfxUNibPyvX4iJ7egYTg==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/ext-apps/-/ext-apps-1.7.5.tgz", + "integrity": "sha512-TjPH2S2y5UEGKhmI6+XGFuqfqOV4ppe1x6DA3txnUaEWkgtA4G5vo14jGKFZmegdkZ1H4QMLyujLvoU1BEdnAg==", "license": "MIT", "workspaces": [ "examples/*" @@ -518,9 +484,9 @@ } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz", - "integrity": "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==", + "version": "1.31.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.31.0.tgz", + "integrity": "sha512-UvTMgnNlnIBO/22ob2RcVGDlcvOslQs8T59+FTGdA0L27a39fdGF/EDETNtDVK4DZGpwomlsYpRdA8UXcVL/pw==", "license": "MIT", "peer": true, "dependencies": { @@ -558,25 +524,6 @@ } } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", - "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, "node_modules/@oddbird/popover-polyfill": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/@oddbird/popover-polyfill/-/popover-polyfill-0.3.8.tgz", @@ -584,19 +531,19 @@ "license": "BSD-3-Clause" }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.152.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.152.0.tgz", + "integrity": "sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==", "dev": true, "license": "MIT", "funding": { - "url": "https://github.com/sponsors/Boshen" + "url": "https://github.com/sponsors/oxc-project" } }, "node_modules/@primer/behaviors": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@primer/behaviors/-/behaviors-1.10.1.tgz", - "integrity": "sha512-9iNr3ulh2W4zmp1e2COu3XBNjq/eqXbHkCvg2SMD/g8zSe7oBXa/FFg8gdaXmyykElfWRytvZkaJh14FrY22Gw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@primer/behaviors/-/behaviors-1.11.0.tgz", + "integrity": "sha512-kDHtaw9kfCjPVT61eM731ftckhGyWiPX+OKmUccLN9sMFt9E6FXmsPAbny7O0RBxeW1Xj/NHNdbMEjYEg2dBYg==", "license": "MIT" }, "node_modules/@primer/live-region-element": { @@ -609,13 +556,10 @@ } }, "node_modules/@primer/octicons-react": { - "version": "19.21.2", - "resolved": "https://registry.npmjs.org/@primer/octicons-react/-/octicons-react-19.21.2.tgz", - "integrity": "sha512-Bk+S08EpeeWLFscUxwEY8t5z14KxByhIbPG6OiYXSNrkbzN4fmRetnB/C+K1srn4BWuRSwwFxUwvDI2ytgNrFw==", + "version": "19.38.0", + "resolved": "https://registry.npmjs.org/@primer/octicons-react/-/octicons-react-19.38.0.tgz", + "integrity": "sha512-ZzWVrX6kluBaCepsDj/jAxagBOMaRX18LEf723IKpr4E/jdkTdCOtlsN+60Oj7IV9YEVrlUpo9Oky0Tn1heSCA==", "license": "MIT", - "engines": { - "node": ">=8" - }, "peerDependencies": { "react": ">=16.3" } @@ -1430,10 +1374,27 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.12.tgz", + "integrity": "sha512-dB/a1214qKfHMXCpgqR4OZT+jS4kTyEXbQGJPqzobt5EwH5rX080pxE37alt3RzvR1bf1Yz/yGqRfrYAxuPw0A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", - "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.12.tgz", + "integrity": "sha512-7KHFgQ5VJxIHcLlrwrc3Xbds7oTNQT7Pgi9gQCJKrd2VGab/UksIOYp6VD8MzCstGxOKMgNamPwUCfxPdP1OHg==", "cpu": [ "arm64" ], @@ -1448,9 +1409,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", - "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.12.tgz", + "integrity": "sha512-3YIhqHD96nA5SaYNRBR16HnGv4oavZvXfD/ayHM+oYZ0WD/8lBAtf6zQua4kEyAvpqrluKXl0lnOBoiNby7x9w==", "cpu": [ "arm64" ], @@ -1465,9 +1426,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", - "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.12.tgz", + "integrity": "sha512-UuuJ35MFw4gmFOrE9pEqIV+K3syIKveph+Qc1/ljHZVdoDW4pz/JHR/eMVom+TZGl/5OOvGJOWaOCVt3ZfqhxA==", "cpu": [ "x64" ], @@ -1482,9 +1443,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", - "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.12.tgz", + "integrity": "sha512-uMvssit0a4W+/7D8CbHUvG719mH3R2jwXAlh/XcPvuHTE0g++LymF88DCGNX0HM2rBOn0xrzgXktIB6fLSJBTQ==", "cpu": [ "x64" ], @@ -1499,9 +1460,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", - "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.12.tgz", + "integrity": "sha512-XcFu0R0xWnwzSf4IQgFH1rJIckPN1pLy2R+4r9IDB7Yfu/ys9cVqfa4pBrMHj7a3gl8mIR4nRNPg0e5IvEVs6g==", "cpu": [ "arm" ], @@ -1516,9 +1477,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", - "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.12.tgz", + "integrity": "sha512-260UrKgn8tz39ak+SMDOirKzr7V04M9dWPw5llW00SwBivCZoWcRBKV1d8cXnRkUmSZA3BdiUmBHWk7734Ulpw==", "cpu": [ "arm64" ], @@ -1533,9 +1494,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", - "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.12.tgz", + "integrity": "sha512-5YK1I9SqDkbPgc1IA8BgDl34suqUS2q0KWnBrirm0E51YjOs6eo6dV6jbQfNE/argHRSvd0QUGgtpIoYx+WWpw==", "cpu": [ "arm64" ], @@ -1550,9 +1511,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", - "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.12.tgz", + "integrity": "sha512-Rkcrmp7eFRg74yL5fXEU91JEWbdEPLevWwGtXpmhbjlD1StScbWTmO94Bhly+Mo+ketKYkdmM1vNUKeWSlx8cQ==", "cpu": [ "ppc64" ], @@ -1567,9 +1528,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", - "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.12.tgz", + "integrity": "sha512-qvK4DuAsQc2BSjlx+Xr+IzOIvvxbGZqxFwdWfG6F518Erj0GGISyQbJ6pIappnOxlNPzNHvo/L0BwB30GZ+zVw==", "cpu": [ "s390x" ], @@ -1584,9 +1545,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", - "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.12.tgz", + "integrity": "sha512-Q9uLBO53Xd4QIq1WOycVQyPP1O4HhraEV2qqb3uTrnVw6QZih9duY4vNXOivL1xoUS1/z+W8eF4NMfl2a8Sdjw==", "cpu": [ "x64" ], @@ -1601,9 +1562,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", - "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.12.tgz", + "integrity": "sha512-3IBxWFMjbOZskDPKv8Lf9BCnahlKuHthWkYnyIxOH/QcJrFcS4EmcenthApkwr/5+nEqZlLzeYbxeMaX7A5u4g==", "cpu": [ "x64" ], @@ -1618,9 +1579,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", - "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.12.tgz", + "integrity": "sha512-xtX61xg4LKPkPWilZU1ynKClz5Gj4bf74LML4r3eVLWumKnGjoEr1OSHQhMdbBDoYTi+yjrujvpZe2pUnqCrrA==", "cpu": [ "arm64" ], @@ -1634,29 +1595,10 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", - "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", - "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.12.tgz", + "integrity": "sha512-At7fPB6PCaIjzgIhEZFxuT+BBFqiQibJDT4d3PhiR3f4E7bbMZF4aKblbFfEM3sETRDd1YiQx/+U/g/B/ou5Ew==", "cpu": [ "arm64" ], @@ -1671,9 +1613,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", - "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.12.tgz", + "integrity": "sha512-WIw2haVKwjuYdXkHaoC0mF8Le71TuCBxjrdKqLbJGctbBABj+ClfmNvtbOnzpq3RokNo5+V1qhtSzJyXorsklQ==", "cpu": [ "x64" ], @@ -1833,30 +1775,19 @@ "@styled-system/css": "^5.1.5" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", "license": "MIT", "dependencies": { "@types/ms": "*" } }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "license": "MIT" }, "node_modules/@types/estree-jsx": { @@ -1869,9 +1800,9 @@ } }, "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", "license": "MIT", "dependencies": { "@types/unist": "*" @@ -1893,13 +1824,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.2.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.0.tgz", - "integrity": "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==", + "version": "25.9.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.9.tgz", + "integrity": "sha512-b4e2xxj/yMeT2hNlD6x7zNFiQK2Dlmkb9CQ6v3/1G2RezKN4YS2lcEv8lzGm1mhs4f2lM4yNAMI/zUDBZBBt7w==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "node_modules/@types/prop-types": { @@ -1909,9 +1840,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.27", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", - "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", + "version": "18.3.31", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz", + "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -1968,19 +1899,19 @@ "license": "MIT" }, "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz", + "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==", "license": "ISC" }, "node_modules/@vitejs/plugin-react": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz", - "integrity": "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.1.tgz", + "integrity": "sha512-yxLaQV9gkhS8ezJqCM6+ndU7mDY6gqAg75NQ+0IjwEI8IYOmQCgkRwHKVSfWXW076DsqMo0Dk+0FK1U+M5RgFw==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "^1.0.0" + "@rolldown/pluginutils": "^1.0.1" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -1988,6 +1919,7 @@ "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", + "oxc-transform-react": "^0.145.0", "vite": "^8.0.0" }, "peerDependenciesMeta": { @@ -1996,6 +1928,9 @@ }, "babel-plugin-react-compiler": { "optional": true + }, + "oxc-transform-react": { + "optional": true } } }, @@ -2048,23 +1983,6 @@ } } }, - "node_modules/babel-plugin-styled-components": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.4.tgz", - "integrity": "sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "lodash": "^4.17.21", - "picomatch": "^2.3.1" - }, - "peerDependencies": { - "styled-components": ">= 2" - } - }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", @@ -2076,9 +1994,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.11.20", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.20.tgz", - "integrity": "sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw==", + "version": "2.11.27", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.27.tgz", + "integrity": "sha512-ElY12DaROGuan+lMmZ8Cvo/ZUbXPe7Enc/9VU/b1T3Kp4dwytRcNdR8DoSJN5SNJT/CuvcCA0DHDVmMOCePdRQ==", "license": "Apache-2.0", "peer": true, "bin": { @@ -2114,9 +2032,9 @@ } }, "node_modules/body-parser/node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", "license": "MIT", "peer": true, "engines": { @@ -2141,9 +2059,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", - "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.3.tgz", + "integrity": "sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA==", "funding": [ { "type": "opencollective", @@ -2161,11 +2079,11 @@ "license": "MIT", "peer": true, "dependencies": { - "baseline-browser-mapping": "^2.11.12", - "caniuse-lite": "^1.0.30001809", - "electron-to-chromium": "^1.5.402", - "node-releases": "^2.0.53", - "update-browserslist-db": "^1.3.0" + "baseline-browser-mapping": "^2.11.26", + "caniuse-lite": "^1.0.30001813", + "electron-to-chromium": "^1.5.439", + "node-releases": "^2.0.57", + "update-browserslist-db": "^1.3.3" }, "bin": { "browserslist": "cli.js" @@ -2226,9 +2144,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001810", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", - "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "version": "1.0.30001814", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001814.tgz", + "integrity": "sha512-/Uaf1lAzr59XcMpW0o96WoEfr+VXK2OX4U9AgFoiSHsVJ4HppnIFUjtYzsyDH2+tgANaQb2/oxYGwCPapN1FpA==", "funding": [ { "type": "opencollective", @@ -2306,9 +2224,9 @@ } }, "node_modules/color2k": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz", - "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.4.tgz", + "integrity": "sha512-OXAPGFRNeLFnUfqDtloYdxkwsJoIdXe28+bjbpJiPqyei2HPa3VHmMCWa0Qe62+U4Ftf9Hj7hRssOkxz7WiWbg==", "license": "MIT" }, "node_modules/comma-separated-tokens": { @@ -2546,9 +2464,9 @@ "peer": true }, "node_modules/electron-to-chromium": { - "version": "1.5.420", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.420.tgz", - "integrity": "sha512-2yD6XreGusOfNV+dUcvipJEXc3n/n7fgr7996aszTG+YY5E4mqM4tOq/3uhP129cazL9YHbVWSpc79ePotWtPA==", + "version": "1.5.444", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.444.tgz", + "integrity": "sha512-5ss/uJfoDYDHT0lfJzT6FbcskIzROIOPf0BbbFkGcvDzoJU7i//9GDrwwIHQVmIsrAGiF3ihpADBRIsrEFt1rQ==", "license": "ISC", "peer": true }, @@ -2658,9 +2576,9 @@ } }, "node_modules/eventsource-parser": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.8.tgz", - "integrity": "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz", + "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", "license": "MIT", "peer": true, "engines": { @@ -2712,12 +2630,13 @@ } }, "node_modules/express-rate-limit": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", - "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.0.tgz", + "integrity": "sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==", "license": "MIT", "peer": true, "dependencies": { + "debug": "^4.4.3", "ip-address": "^10.2.0" }, "engines": { @@ -2744,9 +2663,9 @@ "peer": true }, "node_modules/fast-uri": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", - "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "funding": [ { "type": "github", @@ -2760,6 +2679,24 @@ "license": "BSD-3-Clause", "peer": true }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -3017,9 +2954,9 @@ "peer": true }, "node_modules/hono": { - "version": "4.13.3", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.3.tgz", - "integrity": "sha512-r8AO2mYHoLxSHkgafNeC/BXyb2vWRxD3jem4Ts+ptav8oTG5FIRifAjuJEmZI4bSvvc2ns0GxmIYiZnHqN3mMw==", + "version": "4.13.12", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.12.tgz", + "integrity": "sha512-6E2QDAc9Ick9Sq77ZrGS/dk2WUYni91aufTw6LJKpV7w8kW5/GxVUc650FOADOmlwg3K+f7Pun6XlV+pYmW6gw==", "license": "MIT", "peer": true, "engines": { @@ -3058,9 +2995,9 @@ } }, "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", "license": "MIT", "peer": true, "dependencies": { @@ -3088,9 +3025,9 @@ "license": "MIT" }, "node_modules/ip-address": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.5.0.tgz", - "integrity": "sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g==", + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.3.tgz", + "integrity": "sha512-A1kdq/tSb5QjvKvAMgIoEvDBIgL7qaqVP/jkvSwYYRZ9iEzvPpopxp2wQfu3SuZRHtpHNxMn8Fs0bS+gf5Xmwg==", "license": "MIT", "peer": true, "engines": { @@ -3211,9 +3148,9 @@ "peer": true }, "node_modules/jose": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", - "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", + "version": "6.2.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.12.tgz", + "integrity": "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==", "license": "MIT", "peer": true, "funding": { @@ -3276,9 +3213,9 @@ } }, "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -3292,23 +3229,23 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", "cpu": [ "arm64" ], @@ -3327,9 +3264,9 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", "cpu": [ "arm64" ], @@ -3348,9 +3285,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", "cpu": [ "x64" ], @@ -3369,9 +3306,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", "cpu": [ "x64" ], @@ -3390,9 +3327,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", "cpu": [ "arm" ], @@ -3411,9 +3348,9 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", "cpu": [ "arm64" ], @@ -3432,9 +3369,9 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", "cpu": [ "arm64" ], @@ -3453,9 +3390,9 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", "cpu": [ "x64" ], @@ -3474,9 +3411,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", "cpu": [ "x64" ], @@ -3495,9 +3432,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", "cpu": [ "arm64" ], @@ -3516,9 +3453,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", "cpu": [ "x64" ], @@ -3536,13 +3473,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "license": "MIT", - "peer": true - }, "node_modules/lodash.isempty": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", @@ -3696,9 +3626,9 @@ } }, "node_modules/mdast-util-from-markdown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", - "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3773,9 +3703,9 @@ } }, "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.1.tgz", + "integrity": "sha512-OuJHqvr455pwu2OaOrir7dbzqs4jlWKOtlu9L6GjcIjjQwNyw02VntQsr/Ek6/A13vgnXfUhBekWRUP1OkNivw==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3916,9 +3846,9 @@ } }, "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.3.tgz", + "integrity": "sha512-wgyJtgUkUcdU7zci7uuwc/tzoAhm0TswWhaXuvnWolqug+8jY7bgNkBBL0dYBnHgc/tHL7lCzRdPsdUxUKKBqw==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3926,8 +3856,10 @@ "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", + "micromark-util-character": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" }, @@ -3950,13 +3882,17 @@ } }, "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/merge-descriptors": { @@ -3973,9 +3909,9 @@ } }, "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.3.tgz", + "integrity": "sha512-oGYfQzHSG5dOMovQcJ3fyTmZlWAWpi0XA0sJwJs+i6OT88o1+Jtw/8z0CmdowSLxGhhFK89rQ/oXph/wN02PNw==", "funding": [ { "type": "GitHub Sponsors", @@ -3998,6 +3934,7 @@ "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-edit-map": "^1.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", @@ -4008,9 +3945,9 @@ } }, "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.4.tgz", + "integrity": "sha512-wxEeE8v8XVvOrxn1TZj74qYhAtTQsSqufHVS3uNVyT1MupXxhyaHk8/9xDNPh9BmL77QNDgWTkZ3RsYHB2ry7w==", "funding": [ { "type": "GitHub Sponsors", @@ -4027,18 +3964,19 @@ "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", + "micromark-factory-space": "^2.1.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", + "micromark-util-edit-map": "^1.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "micromark-util-types": "^2.0.3" } }, "node_modules/micromark-extension-gfm": { @@ -4116,9 +4054,9 @@ } }, "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.2.tgz", + "integrity": "sha512-pRzm4kDTu0MjlmBkxmS9yYhw60nncfcEwu9NNdPFSQEFXS95ZKyIIyTSHu/o3ReBUrLKYEq+7YaXCRn/bPB4MA==", "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -4206,9 +4144,9 @@ } }, "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.1.0.tgz", + "integrity": "sha512-fS8hnLIjnjvdQIj39Geug8wWsR0HrYZ43KShKxNfwT7t2LOHo/LbWZEzEaSOjwtjdCsjoE6syHhonEzW0zv0+Q==", "funding": [ { "type": "GitHub Sponsors", @@ -4390,6 +4328,25 @@ "micromark-util-symbol": "^2.0.0" } }, + "node_modules/micromark-util-edit-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-edit-map/-/micromark-util-edit-map-1.0.0.tgz", + "integrity": "sha512-Pa2ljlsEL6sVwFaYeyrOLSYbQt73JvGbPOYFq+9AElXiSnfI5Q4465RRZ1sHv7lDjDOnGRDDaqPXqClqWK/q1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, "node_modules/micromark-util-encode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", @@ -4520,9 +4477,9 @@ "license": "MIT" }, "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.3.tgz", + "integrity": "sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg==", "funding": [ { "type": "GitHub Sponsors", @@ -4549,6 +4506,19 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", @@ -4592,9 +4562,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", "dev": true, "funding": [ { @@ -4611,19 +4581,40 @@ } }, "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", "license": "MIT", "peer": true, + "dependencies": { + "content-type": "^2.1.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/negotiator/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/node-releases": { - "version": "2.0.54", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", - "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", + "version": "2.0.57", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.57.tgz", + "integrity": "sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw==", "license": "MIT", "peer": true, "engines": { @@ -4738,12 +4729,12 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -4760,9 +4751,9 @@ } }, "node_modules/postcss": { - "version": "8.5.26", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", - "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", "dev": true, "funding": [ { @@ -4780,7 +4771,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.17", + "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -4813,9 +4804,9 @@ "license": "MIT" }, "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", "license": "MIT", "funding": { "type": "github", @@ -4823,9 +4814,9 @@ } }, "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", "license": "MIT", "peer": true, "dependencies": { @@ -4834,6 +4825,10 @@ }, "engines": { "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/qs": { @@ -4854,13 +4849,17 @@ } }, "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/raw-body": { @@ -4920,9 +4919,9 @@ } }, "node_modules/react-is": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.4.tgz", - "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.3.0.tgz", + "integrity": "sha512-UpMYezM4v5/18F28aC66AEsjXIgE02kyEMH6yLdgLXu/UTfa1Ntwck/nNLrbqJsEXW7gPb0coNO9FQse9WTovA==", "license": "MIT", "peer": true }, @@ -5030,13 +5029,13 @@ } }, "node_modules/rolldown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", - "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.12.tgz", + "integrity": "sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.133.0", + "@oxc-project/types": "=0.152.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -5046,21 +5045,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" + "@rolldown/binding-android-arm-eabi": "1.2.12", + "@rolldown/binding-android-arm64": "1.2.12", + "@rolldown/binding-darwin-arm64": "1.2.12", + "@rolldown/binding-darwin-x64": "1.2.12", + "@rolldown/binding-freebsd-x64": "1.2.12", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.12", + "@rolldown/binding-linux-arm64-gnu": "1.2.12", + "@rolldown/binding-linux-arm64-musl": "1.2.12", + "@rolldown/binding-linux-ppc64-gnu": "1.2.12", + "@rolldown/binding-linux-s390x-gnu": "1.2.12", + "@rolldown/binding-linux-x64-gnu": "1.2.12", + "@rolldown/binding-linux-x64-musl": "1.2.12", + "@rolldown/binding-openharmony-arm64": "1.2.12", + "@rolldown/binding-win32-arm64-msvc": "1.2.12", + "@rolldown/binding-win32-x64-msvc": "1.2.12" } }, "node_modules/router": { @@ -5279,9 +5278,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -5371,6 +5370,23 @@ "react-is": ">= 16.8.0" } }, + "node_modules/styled-components/node_modules/babel-plugin-styled-components": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.3.0.tgz", + "integrity": "sha512-nP/y6PbBqS/qtKROnJCgpGo8hYUzlBAVXN1QAjSBANL6vZiQXPQN7FYW/nUwoxY7nZhBEGm9T5tjL9gbzwulDw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-module-imports": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "styled-components": ">= 2" + } + }, "node_modules/styled-system": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/styled-system/-/styled-system-5.1.5.tgz", @@ -5422,37 +5438,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5496,14 +5481,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true - }, "node_modules/type-is": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", @@ -5524,9 +5501,9 @@ } }, "node_modules/type-is/node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", "license": "MIT", "peer": true, "engines": { @@ -5552,9 +5529,9 @@ } }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "dev": true, "license": "MIT" }, @@ -5666,9 +5643,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", - "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz", + "integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==", "funding": [ { "type": "opencollective", @@ -5753,16 +5730,16 @@ } }, "node_modules/vite": { - "version": "8.0.16", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", - "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.2.tgz", + "integrity": "sha512-SQr1x6W5vVSbROg7vsyXIaxK9b0G7zsT68acdWWRmnBUsgDieLCRG+Rep9WdZgcposvv/GSnr4GUUBqB3vXq6w==", "dev": true, "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "1.0.3", + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.28", + "rolldown": "~1.2.11", "tinyglobby": "^0.2.17" }, "bin": { @@ -5779,7 +5756,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", + "@vitejs/devtools": "^0.7.1", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -5852,19 +5829,6 @@ } } }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5896,9 +5860,9 @@ "peer": true }, "node_modules/zod": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", - "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz", + "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==", "license": "MIT", "peer": true, "funding": { diff --git a/ui/package.json b/ui/package.json index 9644b72d9e..6534328641 100644 --- a/ui/package.json +++ b/ui/package.json @@ -15,21 +15,21 @@ }, "dependencies": { "@github/markdown-toolbar-element": "^2.2.3", - "@modelcontextprotocol/ext-apps": "^1.7.2", - "@primer/octicons-react": "^19.0.0", - "@primer/react": "^36.0.0", - "react": "^18.0.0", - "react-dom": "^18.0.0", + "@modelcontextprotocol/ext-apps": "^1.7.5", + "@primer/octicons-react": "^19.38.0", + "@primer/react": "^36.27.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-markdown": "^10.1.0", "remark-gfm": "^4.0.1" }, "devDependencies": { - "@types/node": "^25.2.0", - "@types/react": "^18.0.0", - "@types/react-dom": "^18.0.0", - "@vitejs/plugin-react": "^6.0.2", - "typescript": "^5.7.0", - "vite": "^8.0.16", + "@types/node": "^25.9.9", + "@types/react": "^18.3.31", + "@types/react-dom": "^18.3.7", + "@vitejs/plugin-react": "^6.1.1", + "typescript": "^5.9.3", + "vite": "^8.3.2", "vite-plugin-singlefile": "^2.3.3" } } From fbeba3c994bea051bb5d0b46f2ad2b6495a7bd59 Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Fri, 2 Oct 2026 12:18:50 +0200 Subject: [PATCH 07/13] build: upgrade golangci-lint to v2.14.0 (#3380) Resolve new modernization findings and document narrow false-positive suppressions. Verify Go 1.26.8 and 1.27.1 support and remove obsolete export-data compatibility guidance. Refresh cached linter binaries when the repository pin changes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- CONTRIBUTING.md | 4 +--- internal/oauth/testutil_test.go | 2 +- pkg/buffer/buffer_test.go | 4 ++-- pkg/errors/error.go | 6 ++---- pkg/github/actions.go | 3 +-- pkg/github/copilot.go | 4 ++-- pkg/github/projects.go | 21 +++++++-------------- pkg/github/projects_batch.go | 6 ++---- pkg/github/projects_batch_mutation.go | 2 +- pkg/github/projects_batch_test.go | 12 ++++++------ pkg/github/projects_resolver.go | 2 +- pkg/github/secret_scanning.go | 4 ++-- pkg/http/middleware/pat_scope_test.go | 2 +- pkg/http/middleware/scope_challenge_test.go | 2 +- pkg/http/middleware/token_test.go | 10 +++++----- pkg/http/transport/etag_test.go | 12 ++++++------ pkg/http/transport/user_agent_test.go | 2 +- pkg/inventory/server_tool.go | 5 +++-- pkg/toolvalidation/readonlyhint.go | 2 +- script/lint | 4 ++-- 21 files changed, 49 insertions(+), 62 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e4acc354b7..568ea1ca18 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -23,4 +23,4 @@ jobs: uses: golangci/golangci-lint-action@v9 with: # sync with script/lint - version: v2.9 + version: v2.14.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 76af5d31f5..8c8f294b36 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,9 +27,7 @@ Thanks for contributing and for helping us build toolsets that are truly valuabl These are one time installations required to be able to test your changes locally as part of the pull request (PR) submission process. 1. Install Go 1.26.8 or later [through download](https://go.dev/doc/install) | [through Homebrew](https://formulae.brew.sh/formula/go) -2. [Install golangci-lint v2](https://golangci-lint.run/welcome/install/#local-installation) - -The repository-pinned golangci-lint v2.9.0 supports Go 1.26, but cannot read Go 1.27 export data. When using a newer Go installation, run lint with `GOTOOLCHAIN=go1.26.8 script/lint`. +2. [Install golangci-lint v2.14.0](https://golangci-lint.run/welcome/install/#local-installation), or let `script/lint` install the repository-pinned version. The pinned version supports both Go 1.26 and Go 1.27. ## Submitting a pull request diff --git a/internal/oauth/testutil_test.go b/internal/oauth/testutil_test.go index dc34c3841c..5c0ac501f7 100644 --- a/internal/oauth/testutil_test.go +++ b/internal/oauth/testutil_test.go @@ -73,7 +73,7 @@ func (f *fakeGitHub) handleAuthorize(w http.ResponseWriter, r *http.Request) { f.mu.Unlock() redirect := q.Get("redirect_uri") + "?code=authcode&state=" + url.QueryEscape(q.Get("state")) - http.Redirect(w, r, redirect, http.StatusFound) + http.Redirect(w, r, redirect, http.StatusFound) //nolint:gosec // G710: Fake OAuth server redirects to the test client's callback, not an external user's input. } func (f *fakeGitHub) handleToken(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/buffer/buffer_test.go b/pkg/buffer/buffer_test.go index 86308ec5e3..b66c3bc20b 100644 --- a/pkg/buffer/buffer_test.go +++ b/pkg/buffer/buffer_test.go @@ -83,14 +83,14 @@ func TestProcessResponseAsRingBufferToEnd(t *testing.T) { // Ring buffer size is 5, so we should only keep the last 5 lines var sb strings.Builder for i := 1; i <= 10; i++ { - sb.WriteString(fmt.Sprintf("line%d\n", i)) + fmt.Fprintf(&sb, "line%d\n", i) } // Insert an 11MB line (exceeds maxLineSize of 10MB) longLine := strings.Repeat("x", 11*1024*1024) sb.WriteString(longLine) sb.WriteString("\n") for i := 11; i <= 20; i++ { - sb.WriteString(fmt.Sprintf("line%d\n", i)) + fmt.Fprintf(&sb, "line%d\n", i) } resp := &http.Response{ diff --git a/pkg/errors/error.go b/pkg/errors/error.go index cc3e542cc9..3e45e53081 100644 --- a/pkg/errors/error.go +++ b/pkg/errors/error.go @@ -165,8 +165,7 @@ func NewGitHubAPIErrorResponse(ctx context.Context, message string, resp *github _, _ = addGitHubAPIErrorToContext(ctx, apiErr) // Explicitly ignore error for graceful handling } - var rateLimitErr *github.RateLimitError - if stderrors.As(err, &rateLimitErr) { + if rateLimitErr, ok := stderrors.AsType[*github.RateLimitError](err); ok { resetTime := rateLimitErr.Rate.Reset.Time if !resetTime.IsZero() { retryIn := time.Until(resetTime).Round(time.Second) @@ -179,8 +178,7 @@ func NewGitHubAPIErrorResponse(ctx context.Context, message string, resp *github "%s: GitHub API rate limit exceeded. Wait before retrying.", message)) } - var abuseErr *github.AbuseRateLimitError - if stderrors.As(err, &abuseErr) { + if abuseErr, ok := stderrors.AsType[*github.AbuseRateLimitError](err); ok { if abuseErr.RetryAfter != nil { retryAfter := abuseErr.RetryAfter.Round(time.Second) if retryAfter > 0 { diff --git a/pkg/github/actions.go b/pkg/github/actions.go index 3f5654db10..874677d848 100644 --- a/pkg/github/actions.go +++ b/pkg/github/actions.go @@ -1103,8 +1103,7 @@ func rerunFailedJobs(ctx context.Context, client *github.Client, owner, repo str func cancelWorkflowRun(ctx context.Context, client *github.Client, owner, repo string, runID int64) (*mcp.CallToolResult, any, error) { resp, err := client.Actions.CancelWorkflowRunByID(ctx, owner, repo, runID) if err != nil { - var acceptedErr *github.AcceptedError - if !errors.As(err, &acceptedErr) { + if _, ok := errors.AsType[*github.AcceptedError](err); !ok { return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to cancel workflow run", resp, err), nil, nil } } diff --git a/pkg/github/copilot.go b/pkg/github/copilot.go index e5db071499..747355491a 100644 --- a/pkg/github/copilot.go +++ b/pkg/github/copilot.go @@ -39,7 +39,7 @@ func (d *mvpDescription) String() string { sb.WriteString("\n\n") sb.WriteString("This tool can help with the following outcomes:\n") for _, outcome := range d.outcomes { - sb.WriteString(fmt.Sprintf("- %s\n", outcome)) + fmt.Fprintf(&sb, "- %s\n", outcome) } } @@ -47,7 +47,7 @@ func (d *mvpDescription) String() string { sb.WriteString("\n\n") sb.WriteString("More information can be found at:\n") for _, link := range d.referenceLinks { - sb.WriteString(fmt.Sprintf("- %s\n", link)) + fmt.Fprintf(&sb, "- %s\n", link) } } diff --git a/pkg/github/projects.go b/pkg/github/projects.go index 28e15d3bd1..71c29d5720 100644 --- a/pkg/github/projects.go +++ b/pkg/github/projects.go @@ -625,8 +625,7 @@ Use this tool to get details about individual projects, project fields, project } resolvedIDs, resolveErr := resolveFieldNamesToIDs(ctx, gqlClient, owner, ownerType, projectNumber, fieldNames, "fields") if resolveErr != nil { - var structured *ghErrors.StructuredResolutionError - if errors.As(resolveErr, &structured) { + if structured, ok := errors.AsType[*ghErrors.StructuredResolutionError](resolveErr); ok { return ghErrors.NewStructuredResolutionErrorResponse(structured), nil, nil } return utils.NewToolResultError(resolveErr.Error()), nil, nil @@ -984,8 +983,7 @@ func ProjectsWrite(t translations.TranslationHelperFunc) inventory.ServerTool { // Resolve the item by (item_owner, item_repo, issue_number). resolvedItemID, resolveErr := resolveItemIDFromIssueArgs(ctx, gqlClient, owner, ownerType, projectNumber, args) if resolveErr != nil { - var structured *ghErrors.StructuredResolutionError - if errors.As(resolveErr, &structured) { + if structured, ok := errors.AsType[*ghErrors.StructuredResolutionError](resolveErr); ok { return ghErrors.NewStructuredResolutionErrorResponse(structured), nil, nil } return utils.NewToolResultError(resolveErr.Error()), nil, nil @@ -1252,8 +1250,7 @@ func listProjectItems(ctx context.Context, client *github.Client, gqlClient *git if len(fieldNames) > 0 { resolvedIDs, resolveErr := resolveFieldNamesToIDs(ctx, gqlClient, owner, ownerType, projectNumber, fieldNames, "fields") if resolveErr != nil { - var structured *ghErrors.StructuredResolutionError - if errors.As(resolveErr, &structured) { + if structured, ok := errors.AsType[*ghErrors.StructuredResolutionError](resolveErr); ok { return ghErrors.NewStructuredResolutionErrorResponse(structured), nil, nil } return utils.NewToolResultError(resolveErr.Error()), nil, nil @@ -1446,8 +1443,7 @@ func fetchProjectItem(ctx context.Context, client *github.Client, owner, ownerTy func updateProjectItem(ctx context.Context, client *github.Client, gqlClient *githubv4.Client, owner, ownerType string, projectNumber int, itemID int64, fieldValue map[string]any) (*mcp.CallToolResult, any, error) { updatePayload, issueField, err := buildUpdateProjectItem(ctx, gqlClient, owner, ownerType, projectNumber, fieldValue) if err != nil { - var structured *ghErrors.StructuredResolutionError - if errors.As(err, &structured) { + if structured, ok := errors.AsType[*ghErrors.StructuredResolutionError](err); ok { return ghErrors.NewStructuredResolutionErrorResponse(structured), nil, nil } return utils.NewToolResultError(err.Error()), nil, nil @@ -1469,8 +1465,7 @@ func updateProjectItem(ctx context.Context, client *github.Client, gqlClient *gi issueID, resolveErr := projectItemIssueID(projectItem) if resolveErr != nil { - var structured *ghErrors.StructuredResolutionError - if errors.As(resolveErr, &structured) { + if structured, ok := errors.AsType[*ghErrors.StructuredResolutionError](resolveErr); ok { return ghErrors.NewStructuredResolutionErrorResponse(structured), nil, nil } return utils.NewToolResultError(resolveErr.Error()), nil, nil @@ -2136,8 +2131,7 @@ func createProjectView(ctx context.Context, gqlClient *githubv4.Client, args map } configuration, err := projectViewVisibleFieldsInput(ctx, gqlClient, args, owner, ownerType, projectNumber) if err != nil { - var structured *ghErrors.StructuredResolutionError - if errors.As(err, &structured) { + if structured, ok := errors.AsType[*ghErrors.StructuredResolutionError](err); ok { return ghErrors.NewStructuredResolutionErrorResponse(structured), nil, nil } return utils.NewToolResultError(err.Error()), nil, nil @@ -2267,8 +2261,7 @@ func updateProjectView(ctx context.Context, gqlClient *githubv4.Client, args map configuration, err := projectViewVisibleFieldsInput(ctx, gqlClient, args, owner, ownerType, projectNumber) if err != nil { - var structured *ghErrors.StructuredResolutionError - if errors.As(err, &structured) { + if structured, ok := errors.AsType[*ghErrors.StructuredResolutionError](err); ok { return ghErrors.NewStructuredResolutionErrorResponse(structured), nil, nil } return utils.NewToolResultError(err.Error()), nil, nil diff --git a/pkg/github/projects_batch.go b/pkg/github/projects_batch.go index 28493a4bf2..70b590ea6f 100644 --- a/pkg/github/projects_batch.go +++ b/pkg/github/projects_batch.go @@ -182,8 +182,7 @@ func updateProjectItemsBatch(ctx context.Context, client *github.Client, gqlClie } func batchTopLevelError(err error) *mcp.CallToolResult { - var structured *ghErrors.StructuredResolutionError - if errors.As(err, &structured) { + if structured, ok := errors.AsType[*ghErrors.StructuredResolutionError](err); ok { return ghErrors.NewStructuredResolutionErrorResponse(structured) } return utils.NewToolResultError(err.Error()) @@ -796,8 +795,7 @@ func resolveIssueRefs(ctx context.Context, gqlClient *githubv4.Client, projectID } func batchErrorFromResolution(err error) *batchItemError { - var structured *ghErrors.StructuredResolutionError - if errors.As(err, &structured) { + if structured, ok := errors.AsType[*ghErrors.StructuredResolutionError](err); ok { return &batchItemError{ Code: structured.Kind, Message: fmt.Sprintf("%s: %s", structured.Kind, structured.Name), diff --git a/pkg/github/projects_batch_mutation.go b/pkg/github/projects_batch_mutation.go index 0c478aabf9..13913e53bf 100644 --- a/pkg/github/projects_batch_mutation.go +++ b/pkg/github/projects_batch_mutation.go @@ -105,7 +105,7 @@ func executeAliasedMutation(ctx context.Context, gqlClient *githubv4.Client, kin outcomes := make([]mutationAliasOutcome, len(inputs)) elem := mutationPtr.Elem() for i := range inputs { - result, ok := elem.Field(i).Interface().(projectV2ItemMutationResult) + result, ok := reflect.TypeAssert[projectV2ItemMutationResult](elem.Field(i)) if !ok || result.ProjectV2Item.ID == "" { continue } diff --git a/pkg/github/projects_batch_test.go b/pkg/github/projects_batch_test.go index 985cd5bfc7..7e17849e4e 100644 --- a/pkg/github/projects_batch_test.go +++ b/pkg/github/projects_batch_test.go @@ -396,10 +396,10 @@ func Test_ProjectsWrite_UpdateProjectItems_NumericItemIDDeduplicatesRESTLookup(t } gqlClient := newTestGQLClient(transport) - var restCalls int32 + var restCalls atomic.Int32 restClient := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetOrgsProjectsV2ItemsByProjectByItemID: func(w http.ResponseWriter, _ *http.Request) { - atomic.AddInt32(&restCalls, 1) + restCalls.Add(1) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(`{"id":1001,"node_id":"PVTI_item1001"}`)) @@ -427,7 +427,7 @@ func Test_ProjectsWrite_UpdateProjectItems_NumericItemIDDeduplicatesRESTLookup(t require.NoError(t, json.Unmarshal([]byte(getTextResult(t, result).Text), &response)) assert.Equal(t, float64(1), response["succeeded"]) assert.Equal(t, float64(1), response["failed"]) - assert.Equal(t, int32(1), atomic.LoadInt32(&restCalls), "the same numeric item_id must only be resolved once") + assert.Equal(t, int32(1), restCalls.Load(), "the same numeric item_id must only be resolved once") results := response["results"].([]any) assert.Equal(t, "duplicate_target", results[1].(map[string]any)["error"].(map[string]any)["code"]) } @@ -581,10 +581,10 @@ func Test_ProjectsWrite_UpdateProjectItems_DuplicateTargetRejected(t *testing.T) }, } gqlClient := newTestGQLClient(transport) - var restCalls int32 + var restCalls atomic.Int32 restClient := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetOrgsProjectsV2ItemsByProjectByItemID: func(w http.ResponseWriter, _ *http.Request) { - atomic.AddInt32(&restCalls, 1) + restCalls.Add(1) w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"id":1001,"node_id":"PVTI_item1"}`)) }, @@ -616,7 +616,7 @@ func Test_ProjectsWrite_UpdateProjectItems_DuplicateTargetRejected(t *testing.T) second := results[1].(map[string]any) assert.Equal(t, "failed", second["status"]) assert.Equal(t, "duplicate_target", second["error"].(map[string]any)["code"]) - assert.Equal(t, int32(1), atomic.LoadInt32(&restCalls)) + assert.Equal(t, int32(1), restCalls.Load()) assert.Len(t, transport.mutationCalls, 1) } diff --git a/pkg/github/projects_resolver.go b/pkg/github/projects_resolver.go index 33e82d1de5..b4fba6c33e 100644 --- a/pkg/github/projects_resolver.go +++ b/pkg/github/projects_resolver.go @@ -606,7 +606,7 @@ func resolveFieldNamesToIDsFromFields(all []ResolvedField, names []string, owner for i, field := range resolved { id, parseErr := parseInt64(field.ID) if parseErr != nil { - return nil, fmt.Errorf("resolved field %q has non-numeric ID %q; pass it via '%s' instead", names[i], field.ID, idParameter) + return nil, fmt.Errorf("resolved field %q has non-numeric ID %q; pass it via '%s' instead", names[i], field.ID, idParameter) //nolint:gosec // G602: resolveFieldsByName returns exactly one field per name in order, or an error. } out = append(out, id) } diff --git a/pkg/github/secret_scanning.go b/pkg/github/secret_scanning.go index 1991d5d2be..f73fede25b 100644 --- a/pkg/github/secret_scanning.go +++ b/pkg/github/secret_scanning.go @@ -85,7 +85,7 @@ func GetSecretScanningAlert(t translations.TranslationHelperFunc) inventory.Serv return ghErrors.NewGitHubAPIStatusErrorResponse(ctx, "failed to get alert", resp, body), nil, nil } - r, err := json.Marshal(alert) + r, err := json.Marshal(alert) //nolint:gosec // G117: This security_events-scoped tool intentionally returns the alert's secret; the result is labeled private-untrusted. if err != nil { return nil, nil, fmt.Errorf("failed to marshal alert: %w", err) } @@ -200,7 +200,7 @@ func ListSecretScanningAlerts(t translations.TranslationHelperFunc) inventory.Se return ghErrors.NewGitHubAPIStatusErrorResponse(ctx, "failed to list alerts", resp, body), nil, nil } - r, err := json.Marshal(alerts) + r, err := json.Marshal(alerts) //nolint:gosec // G117: This security_events-scoped tool intentionally returns alert secrets; the result is labeled private-untrusted. if err != nil { return nil, nil, fmt.Errorf("failed to marshal alerts: %w", err) } diff --git a/pkg/http/middleware/pat_scope_test.go b/pkg/http/middleware/pat_scope_test.go index 0607b8cf2b..5182a636fe 100644 --- a/pkg/http/middleware/pat_scope_test.go +++ b/pkg/http/middleware/pat_scope_test.go @@ -55,7 +55,7 @@ func TestWithPATScopes(t *testing.T) { }, { name: "fine-grained PAT skips scope fetching", - tokenInfo: &ghcontext.TokenInfo{ + tokenInfo: &ghcontext.TokenInfo{ //nolint:gosec // G101: Synthetic fine-grained PAT fixture verifies scope fetching is skipped; it cannot authenticate. Token: "github_pat_xxxxxxxxxxxxxxxxxxxxxxx", TokenType: utils.TokenTypeFineGrainedPersonalAccessToken, }, diff --git a/pkg/http/middleware/scope_challenge_test.go b/pkg/http/middleware/scope_challenge_test.go index 1c83ba600c..2beeecf49d 100644 --- a/pkg/http/middleware/scope_challenge_test.go +++ b/pkg/http/middleware/scope_challenge_test.go @@ -359,7 +359,7 @@ func scopeChallengeContext(ctx context.Context) context.Context { } func scopeChallengeContextWithScopes(ctx context.Context, activeScopes []string) context.Context { - ctx = ghcontext.WithTokenInfo(ctx, &ghcontext.TokenInfo{ + ctx = ghcontext.WithTokenInfo(ctx, &ghcontext.TokenInfo{ //nolint:gosec // G101: "oauth-token" is a synthetic context fixture, not an authentication credential. Token: "oauth-token", TokenType: utils.TokenTypeOAuthAccessToken, }) diff --git a/pkg/http/middleware/token_test.go b/pkg/http/middleware/token_test.go index fa8f0ee98e..a8373a1565 100644 --- a/pkg/http/middleware/token_test.go +++ b/pkg/http/middleware/token_test.go @@ -37,7 +37,7 @@ func TestExtractUserToken(t *testing.T) { expectWWWAuth: true, }, // Personal Access Token (classic) - ghp_ prefix - { + { //nolint:gosec // G101: Synthetic token fixture exercises classic PAT prefix detection; it cannot authenticate. name: "personal access token (classic) with Bearer prefix", authHeader: "Bearer ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", expectedStatusCode: http.StatusOK, @@ -45,7 +45,7 @@ func TestExtractUserToken(t *testing.T) { expectedToken: "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", expectTokenInfo: true, }, - { + { //nolint:gosec // G101: Synthetic token fixture exercises lowercase bearer parsing; it cannot authenticate. name: "personal access token (classic) with bearer lowercase", authHeader: "bearer ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", expectedStatusCode: http.StatusOK, @@ -62,7 +62,7 @@ func TestExtractUserToken(t *testing.T) { expectTokenInfo: true, }, // Fine-grained Personal Access Token - github_pat_ prefix - { + { //nolint:gosec // G101: Synthetic token fixture exercises fine-grained PAT prefix detection; it cannot authenticate. name: "fine-grained personal access token with Bearer prefix", authHeader: "Bearer github_pat_xxxxxxxxxxxxxxxxxxxxxxx", expectedStatusCode: http.StatusOK, @@ -70,7 +70,7 @@ func TestExtractUserToken(t *testing.T) { expectedToken: "github_pat_xxxxxxxxxxxxxxxxxxxxxxx", expectTokenInfo: true, }, - { + { //nolint:gosec // G101: Synthetic token fixture exercises fine-grained PAT parsing without a bearer prefix; it cannot authenticate. name: "fine-grained personal access token without Bearer prefix", authHeader: "github_pat_xxxxxxxxxxxxxxxxxxxxxxx", expectedStatusCode: http.StatusOK, @@ -113,7 +113,7 @@ func TestExtractUserToken(t *testing.T) { expectTokenInfo: true, }, // Server-to-Server GitHub App Token (installation token) - ghs_ prefix - { + { //nolint:gosec // G101: Synthetic token fixture exercises installation token prefix detection; it cannot authenticate. name: "server-to-server GitHub App token with Bearer prefix", authHeader: "Bearer ghs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", expectedStatusCode: http.StatusOK, diff --git a/pkg/http/transport/etag_test.go b/pkg/http/transport/etag_test.go index a3905d01ee..dcac5566eb 100644 --- a/pkg/http/transport/etag_test.go +++ b/pkg/http/transport/etag_test.go @@ -46,10 +46,10 @@ func TestETagTransport_ServesCachedBodyOn304(t *testing.T) { const etag = `"abc123"` const body = `{"number":1}` - var requests int32 + var requests atomic.Int32 var lastIfNoneMatch string server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - n := atomic.AddInt32(&requests, 1) + n := requests.Add(1) lastIfNoneMatch = r.Header.Get(headers.IfNoneMatchHeader) w.Header().Set(headers.ETagHeader, etag) if n == 1 { @@ -84,7 +84,7 @@ func TestETagTransport_ServesCachedBodyOn304(t *testing.T) { assert.Equal(t, http.StatusOK, status2, "304 is translated to the cached 200") assert.Equal(t, body, body2, "cached body is served on 304") assert.Equal(t, etag, lastIfNoneMatch, "second request sends the cached ETag") - assert.Equal(t, int32(2), atomic.LoadInt32(&requests), "every request still reaches the server") + assert.Equal(t, int32(2), requests.Load(), "every request still reaches the server") } // TestETagTransport_UpdatesRateLimitHeadersFrom304 verifies that a cache-served @@ -95,9 +95,9 @@ func TestETagTransport_UpdatesRateLimitHeadersFrom304(t *testing.T) { const etag = `"v1"` - var requests int32 + var requests atomic.Int32 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - n := atomic.AddInt32(&requests, 1) + n := requests.Add(1) w.Header().Set(headers.ETagHeader, etag) if n == 1 { w.Header().Set("X-RateLimit-Remaining", "100") @@ -143,7 +143,7 @@ func TestETagTransport_ScopesCacheByAuthorization(t *testing.T) { return } w.WriteHeader(http.StatusOK) - _, _ = io.WriteString(w, r.Header.Get(headers.AuthorizationHeader)) + _, _ = io.WriteString(w, r.Header.Get(headers.AuthorizationHeader)) //nolint:gosec // G705: Test server echoes synthetic tokens to verify cache isolation; no browser consumes the response. })) defer server.Close() diff --git a/pkg/http/transport/user_agent_test.go b/pkg/http/transport/user_agent_test.go index 58eee9b699..9757b4937f 100644 --- a/pkg/http/transport/user_agent_test.go +++ b/pkg/http/transport/user_agent_test.go @@ -14,7 +14,7 @@ import ( func TestUserAgentTransportRequestIsolation(t *testing.T) { t.Parallel() server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - _, _ = io.WriteString(w, req.UserAgent()) + _, _ = io.WriteString(w, req.UserAgent()) //nolint:gosec // G705: Test server echoes the test client's user agent to verify transport isolation; no browser consumes the response. })) t.Cleanup(server.Close) transport := &UserAgentTransport{ diff --git a/pkg/inventory/server_tool.go b/pkg/inventory/server_tool.go index 2bc2769593..9dd708685f 100644 --- a/pkg/inventory/server_tool.go +++ b/pkg/inventory/server_tool.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "maps" + "slices" "github.com/github/github-mcp-server/pkg/octicons" "github.com/google/jsonschema-go/jsonschema" @@ -142,8 +143,8 @@ func (st *ServerTool) Handler(deps any) mcp.ToolHandler { // Panics if the tool has no handler - all tools should have handlers. func (st *ServerTool) RegisterFunc(s *mcp.Server, deps any, middleware ...ToolHandlerMiddleware) { handler := st.Handler(deps) // This will panic if HandlerFunc is nil - for i := len(middleware) - 1; i >= 0; i-- { - handler = middleware[i](handler) + for _, m := range slices.Backward(middleware) { + handler = m(handler) } handler = st.wrapAvailabilityCheck(handler) // Make a shallow copy of the tool to avoid mutating the original diff --git a/pkg/toolvalidation/readonlyhint.go b/pkg/toolvalidation/readonlyhint.go index bcde92a5ec..b9877bf847 100644 --- a/pkg/toolvalidation/readonlyhint.go +++ b/pkg/toolvalidation/readonlyhint.go @@ -58,7 +58,7 @@ func (v ReadOnlyHintViolation) String() string { // violations, err := toolvalidation.ScanReadOnlyHint(dir) func ScanReadOnlyHint(dir string) ([]ReadOnlyHintViolation, error) { fset := token.NewFileSet() - pkgs, err := parser.ParseDir(fset, dir, func(info os.FileInfo) bool { + pkgs, err := parser.ParseDir(fset, dir, func(info os.FileInfo) bool { //nolint:staticcheck // SA1019: This source-level validator intentionally scans all files regardless of build tags, without loading packages. // Skip test files: they are allowed to construct mcp.Tool literals // for fixtures or mocks where ReadOnlyHint is not meaningful. return !strings.HasSuffix(info.Name(), "_test.go") diff --git a/script/lint b/script/lint index 5b69cbe2ff..00cfeb1aa0 100755 --- a/script/lint +++ b/script/lint @@ -6,9 +6,9 @@ gofmt -s -w . BINDIR="$(git rev-parse --show-toplevel)"/bin BINARY=$BINDIR/golangci-lint # sync with .github/workflows/lint.yml -GOLANGCI_LINT_VERSION=v2.9.0 +GOLANGCI_LINT_VERSION=v2.14.0 -if [ ! -f "$BINARY" ]; then +if [ ! -f "$BINARY" ] || ! "$BINARY" version --short | grep -qx "${GOLANGCI_LINT_VERSION#v}"; then curl -sSfL https://golangci-lint.run/install.sh | sh -s -- -b "$BINDIR" "$GOLANGCI_LINT_VERSION" fi From 5e39ec8bbb4b24f8a420599b1036cf8d753373ea Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Fri, 2 Oct 2026 12:19:43 +0200 Subject: [PATCH 08/13] build(deps): upgrade go-github to v92.0.0 (#3381) Migrate all go-github imports and issue comment requests while preserving MCP schemas and payloads. Let v92 escape ruleset branch names exactly once. Refresh dependency license reports and narrowly retain the supported Ptr helper for compatibility with the pinned linter. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .golangci.yml | 4 ++++ e2e/e2e_test.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- internal/ghmcp/server.go | 2 +- pkg/errors/error.go | 2 +- pkg/errors/error_test.go | 2 +- pkg/github/actions.go | 2 +- pkg/github/actions_minimal_test.go | 2 +- pkg/github/actions_test.go | 2 +- pkg/github/code_quality_test.go | 2 +- pkg/github/code_scanning.go | 2 +- pkg/github/code_scanning_test.go | 2 +- pkg/github/comment_minimize.go | 2 +- pkg/github/comment_minimize_test.go | 2 +- pkg/github/context_tools_test.go | 2 +- pkg/github/copilot.go | 2 +- pkg/github/copilot_test.go | 2 +- pkg/github/custom_properties.go | 2 +- pkg/github/custom_properties_test.go | 2 +- pkg/github/dependabot.go | 2 +- pkg/github/dependabot_test.go | 2 +- pkg/github/dependencies.go | 2 +- pkg/github/discussions.go | 2 +- pkg/github/discussions_test.go | 2 +- pkg/github/fields_filtering_test.go | 2 +- pkg/github/gists.go | 2 +- pkg/github/gists_test.go | 2 +- pkg/github/git.go | 2 +- pkg/github/git_test.go | 2 +- pkg/github/granular_tools_test.go | 2 +- pkg/github/helper_test.go | 2 +- pkg/github/ifc_labels.go | 2 +- pkg/github/issue_dependencies.go | 2 +- pkg/github/issues.go | 10 +++++----- pkg/github/issues_delete_test.go | 2 +- pkg/github/issues_granular.go | 2 +- pkg/github/issues_test.go | 6 ++++-- pkg/github/minimal_types.go | 2 +- pkg/github/notifications.go | 2 +- pkg/github/notifications_test.go | 2 +- pkg/github/params.go | 2 +- pkg/github/params_test.go | 2 +- pkg/github/projects.go | 2 +- pkg/github/projects_batch.go | 2 +- pkg/github/projects_test.go | 2 +- pkg/github/pullrequests.go | 2 +- pkg/github/pullrequests_granular.go | 2 +- pkg/github/pullrequests_sha_pinning_test.go | 2 +- pkg/github/pullrequests_test.go | 2 +- pkg/github/repositories.go | 2 +- pkg/github/repositories_helper.go | 2 +- pkg/github/repositories_test.go | 2 +- pkg/github/repository_resource.go | 2 +- pkg/github/repository_resource_completions.go | 2 +- pkg/github/repository_resource_completions_test.go | 2 +- pkg/github/rulesets.go | 4 ++-- pkg/github/rulesets_test.go | 2 +- pkg/github/sanitize_coverage_test.go | 2 +- pkg/github/search.go | 2 +- pkg/github/search_test.go | 2 +- pkg/github/search_utils.go | 2 +- pkg/github/secret_scanning.go | 2 +- pkg/github/secret_scanning_test.go | 2 +- pkg/github/security_advisories.go | 2 +- pkg/github/security_advisories_test.go | 2 +- pkg/github/server_test.go | 2 +- pkg/github/tools.go | 2 +- pkg/github/ui_tools.go | 2 +- pkg/github/ui_tools_test.go | 2 +- pkg/lockdown/lockdown.go | 2 +- pkg/lockdown/lockdown_test.go | 2 +- pkg/raw/raw.go | 2 +- pkg/raw/raw_test.go | 2 +- third-party-licenses.darwin.md | 2 +- third-party-licenses.linux.md | 2 +- third-party-licenses.windows.md | 2 +- .../google/go-github/{v89 => v92}/github/LICENSE | 0 78 files changed, 89 insertions(+), 83 deletions(-) rename third-party/github.com/google/go-github/{v89 => v92}/github/LICENSE (100%) diff --git a/.golangci.yml b/.golangci.yml index a17770127a..bde46b8e80 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -31,6 +31,10 @@ linters: - examples$ - internal/githubv4mock rules: + # v2.9.0 misreports SA4006 for Go 1.26 new(expr); retain go-github's helper until the linter is upgraded. + - linters: + - staticcheck + text: '(go)?github\.Ptr is deprecated: use the new builtin instead\.' - linters: - revive text: "var-naming: avoid package names that conflict with Go standard library package names" diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index 112f653a37..3df717087a 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -20,7 +20,7 @@ import ( "github.com/github/github-mcp-server/pkg/github" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - gogithub "github.com/google/go-github/v89/github" + gogithub "github.com/google/go-github/v92/github" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/stretchr/testify/require" ) diff --git a/go.mod b/go.mod index 709dba1a32..dade1dbe16 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.26.8 require ( github.com/go-chi/chi/v5 v5.3.2 github.com/go-viper/mapstructure/v2 v2.5.0 - github.com/google/go-github/v89 v89.0.1-0.20260728185857-34349a88bac3 + github.com/google/go-github/v92 v92.0.0 github.com/google/jsonschema-go v0.4.3 github.com/josephburnett/jd/v2 v2.5.0 github.com/lithammer/fuzzysearch v1.1.8 diff --git a/go.sum b/go.sum index d64a3d05c0..bfa3c4dbae 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArs github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-github/v89 v89.0.1-0.20260728185857-34349a88bac3 h1:0a/p9KtPso8UBauBD/p9Go1oaZrrEydBNHjaaKkSHJo= -github.com/google/go-github/v89 v89.0.1-0.20260728185857-34349a88bac3/go.mod h1:QLcbU0ipeAqQuR5KSg8c2lql4Qk1EwJ2dWz/0rP4Nho= +github.com/google/go-github/v92 v92.0.0 h1:4vW4RVffwIvoEfIA4RX09mRSB47qQX557/2+HgeWRRg= +github.com/google/go-github/v92 v92.0.0/go.mod h1:w3CH62ZcmRfvW1cdXpyTztSOVMtdjxtKpgo0GouLmjY= github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= github.com/google/jsonschema-go v0.4.3 h1:/DBOLZTfDow7pe2GmaJNhltueGTtDKICi8V8p+DQPd0= diff --git a/internal/ghmcp/server.go b/internal/ghmcp/server.go index 5da57527e8..886f90c5f8 100644 --- a/internal/ghmcp/server.go +++ b/internal/ghmcp/server.go @@ -27,7 +27,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - gogithub "github.com/google/go-github/v89/github" + gogithub "github.com/google/go-github/v92/github" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" ) diff --git a/pkg/errors/error.go b/pkg/errors/error.go index 3e45e53081..c0e45ad1ad 100644 --- a/pkg/errors/error.go +++ b/pkg/errors/error.go @@ -11,7 +11,7 @@ import ( "github.com/github/github-mcp-server/pkg/sanitize" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/errors/error_test.go b/pkg/errors/error_test.go index bdf7cb16bf..dc47b9d58b 100644 --- a/pkg/errors/error_test.go +++ b/pkg/errors/error_test.go @@ -3,7 +3,7 @@ package errors import ( "context" "fmt" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/actions.go b/pkg/github/actions.go index 874677d848..f40d8a31d3 100644 --- a/pkg/github/actions.go +++ b/pkg/github/actions.go @@ -17,7 +17,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/github/actions_minimal_test.go b/pkg/github/actions_minimal_test.go index 4f0f8bd977..ef3504d38b 100644 --- a/pkg/github/actions_minimal_test.go +++ b/pkg/github/actions_minimal_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/github/actions_test.go b/pkg/github/actions_test.go index f390c31771..d2b59b9c7f 100644 --- a/pkg/github/actions_test.go +++ b/pkg/github/actions_test.go @@ -9,7 +9,7 @@ import ( "github.com/github/github-mcp-server/internal/toolsnaps" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/code_quality_test.go b/pkg/github/code_quality_test.go index e84e1c5e33..83a5a9d83b 100644 --- a/pkg/github/code_quality_test.go +++ b/pkg/github/code_quality_test.go @@ -6,7 +6,7 @@ import ( "net/http" "testing" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/code_scanning.go b/pkg/github/code_scanning.go index 12b0b3f1e2..f2c4834e65 100644 --- a/pkg/github/code_scanning.go +++ b/pkg/github/code_scanning.go @@ -12,7 +12,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/github/code_scanning_test.go b/pkg/github/code_scanning_test.go index c9978d8ac5..976161213c 100644 --- a/pkg/github/code_scanning_test.go +++ b/pkg/github/code_scanning_test.go @@ -8,7 +8,7 @@ import ( "github.com/github/github-mcp-server/internal/toolsnaps" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/comment_minimize.go b/pkg/github/comment_minimize.go index 0bdabae44f..43eb0134d7 100644 --- a/pkg/github/comment_minimize.go +++ b/pkg/github/comment_minimize.go @@ -13,7 +13,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" diff --git a/pkg/github/comment_minimize_test.go b/pkg/github/comment_minimize_test.go index 75e97e0fbb..a3337a7c4b 100644 --- a/pkg/github/comment_minimize_test.go +++ b/pkg/github/comment_minimize_test.go @@ -10,7 +10,7 @@ import ( "github.com/github/github-mcp-server/internal/toolsnaps" "github.com/github/github-mcp-server/pkg/inventory" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/shurcooL/githubv4" "github.com/stretchr/testify/assert" diff --git a/pkg/github/context_tools_test.go b/pkg/github/context_tools_test.go index 0825158abb..5e8b94f6cb 100644 --- a/pkg/github/context_tools_test.go +++ b/pkg/github/context_tools_test.go @@ -10,7 +10,7 @@ import ( "github.com/github/github-mcp-server/internal/githubv4mock" "github.com/github/github-mcp-server/internal/toolsnaps" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" "github.com/stretchr/testify/assert" diff --git a/pkg/github/copilot.go b/pkg/github/copilot.go index 747355491a..f507a16537 100644 --- a/pkg/github/copilot.go +++ b/pkg/github/copilot.go @@ -18,7 +18,7 @@ import ( "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" "github.com/go-viper/mapstructure/v2" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" diff --git a/pkg/github/copilot_test.go b/pkg/github/copilot_test.go index 3abaaf00c6..fbac06d884 100644 --- a/pkg/github/copilot_test.go +++ b/pkg/github/copilot_test.go @@ -13,7 +13,7 @@ import ( "github.com/github/github-mcp-server/internal/githubv4mock" "github.com/github/github-mcp-server/internal/toolsnaps" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/shurcooL/githubv4" "github.com/stretchr/testify/assert" diff --git a/pkg/github/custom_properties.go b/pkg/github/custom_properties.go index 0e71aa8ecc..734fe176ce 100644 --- a/pkg/github/custom_properties.go +++ b/pkg/github/custom_properties.go @@ -11,7 +11,7 @@ import ( "github.com/github/github-mcp-server/pkg/inventory" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/github/custom_properties_test.go b/pkg/github/custom_properties_test.go index be04872038..18f165ca72 100644 --- a/pkg/github/custom_properties_test.go +++ b/pkg/github/custom_properties_test.go @@ -7,7 +7,7 @@ import ( "net/http" "testing" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/dependabot.go b/pkg/github/dependabot.go index 9cec4cb814..4ba7d6c4c3 100644 --- a/pkg/github/dependabot.go +++ b/pkg/github/dependabot.go @@ -13,7 +13,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/github/dependabot_test.go b/pkg/github/dependabot_test.go index dba0a5603f..8aecc4e19f 100644 --- a/pkg/github/dependabot_test.go +++ b/pkg/github/dependabot_test.go @@ -8,7 +8,7 @@ import ( "github.com/github/github-mcp-server/internal/toolsnaps" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/github/dependencies.go b/pkg/github/dependencies.go index 333b1d9c07..21bbd99553 100644 --- a/pkg/github/dependencies.go +++ b/pkg/github/dependencies.go @@ -16,7 +16,7 @@ import ( "github.com/github/github-mcp-server/pkg/raw" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - gogithub "github.com/google/go-github/v89/github" + gogithub "github.com/google/go-github/v92/github" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" ) diff --git a/pkg/github/discussions.go b/pkg/github/discussions.go index 4b40569c39..890878e42d 100644 --- a/pkg/github/discussions.go +++ b/pkg/github/discussions.go @@ -13,7 +13,7 @@ import ( "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" "github.com/go-viper/mapstructure/v2" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" diff --git a/pkg/github/discussions_test.go b/pkg/github/discussions_test.go index 111372a9ef..c502f2aa47 100644 --- a/pkg/github/discussions_test.go +++ b/pkg/github/discussions_test.go @@ -9,7 +9,7 @@ import ( "github.com/github/github-mcp-server/internal/githubv4mock" "github.com/github/github-mcp-server/internal/toolsnaps" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/shurcooL/githubv4" "github.com/stretchr/testify/assert" diff --git a/pkg/github/fields_filtering_test.go b/pkg/github/fields_filtering_test.go index cd08dcef68..bb82d6abb8 100644 --- a/pkg/github/fields_filtering_test.go +++ b/pkg/github/fields_filtering_test.go @@ -9,7 +9,7 @@ import ( "github.com/github/github-mcp-server/internal/githubv4mock" "github.com/github/github-mcp-server/pkg/inventory" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/shurcooL/githubv4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/gists.go b/pkg/github/gists.go index 441e59ea7c..8ad6fda296 100644 --- a/pkg/github/gists.go +++ b/pkg/github/gists.go @@ -13,7 +13,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/github/gists_test.go b/pkg/github/gists_test.go index a0e153b033..2e5214a556 100644 --- a/pkg/github/gists_test.go +++ b/pkg/github/gists_test.go @@ -9,7 +9,7 @@ import ( "github.com/github/github-mcp-server/internal/toolsnaps" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/git.go b/pkg/github/git.go index 0f65b9324d..b066dfe757 100644 --- a/pkg/github/git.go +++ b/pkg/github/git.go @@ -12,7 +12,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/github/git_test.go b/pkg/github/git_test.go index 67921d6045..09c82a8703 100644 --- a/pkg/github/git_test.go +++ b/pkg/github/git_test.go @@ -9,7 +9,7 @@ import ( "github.com/github/github-mcp-server/internal/toolsnaps" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/granular_tools_test.go b/pkg/github/granular_tools_test.go index 90fe6d041a..07e5871f8a 100644 --- a/pkg/github/granular_tools_test.go +++ b/pkg/github/granular_tools_test.go @@ -14,7 +14,7 @@ import ( transportpkg "github.com/github/github-mcp-server/pkg/http/transport" "github.com/github/github-mcp-server/pkg/inventory" "github.com/github/github-mcp-server/pkg/translations" - gogithub "github.com/google/go-github/v89/github" + gogithub "github.com/google/go-github/v92/github" "github.com/shurcooL/githubv4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/helper_test.go b/pkg/github/helper_test.go index 0d089c8430..80a2e523f2 100644 --- a/pkg/github/helper_test.go +++ b/pkg/github/helper_test.go @@ -10,7 +10,7 @@ import ( "strings" "testing" - gogithub "github.com/google/go-github/v89/github" + gogithub "github.com/google/go-github/v92/github" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/stretchr/testify/assert" testifymock "github.com/stretchr/testify/mock" diff --git a/pkg/github/ifc_labels.go b/pkg/github/ifc_labels.go index d91a9b16e2..423362a0dc 100644 --- a/pkg/github/ifc_labels.go +++ b/pkg/github/ifc_labels.go @@ -4,7 +4,7 @@ import ( "context" "github.com/github/github-mcp-server/pkg/ifc" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/github/issue_dependencies.go b/pkg/github/issue_dependencies.go index dc78c9eccc..e553bab365 100644 --- a/pkg/github/issue_dependencies.go +++ b/pkg/github/issue_dependencies.go @@ -12,7 +12,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/github/issues.go b/pkg/github/issues.go index f1de84eb27..2ee131733d 100644 --- a/pkg/github/issues.go +++ b/pkg/github/issues.go @@ -20,7 +20,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" @@ -1507,8 +1507,8 @@ func AddIssueComment(t translations.TranslationHelperFunc) inventory.ServerTool var commentResponse *MinimalResponse if hasBody { - comment := &github.IssueComment{ - Body: github.Ptr(body), + comment := github.IssueCommentRequest{ + Body: body, } createdComment, resp, err := client.Issues.CreateComment(ctx, owner, repo, issueNumber, comment) if err != nil { @@ -1621,8 +1621,8 @@ func UpdateIssueComment(t translations.TranslationHelperFunc) inventory.ServerTo return utils.NewToolResultErrorFromErr("failed to get GitHub client", err), nil, nil } - updatedComment, resp, err := client.Issues.EditComment(ctx, owner, repo, commentID, &github.IssueComment{ - Body: github.Ptr(body), + updatedComment, resp, err := client.Issues.UpdateComment(ctx, owner, repo, commentID, github.IssueCommentRequest{ + Body: body, }) if resp != nil && resp.Body != nil { defer func() { _ = resp.Body.Close() }() diff --git a/pkg/github/issues_delete_test.go b/pkg/github/issues_delete_test.go index 11239e3a99..d370beb92d 100644 --- a/pkg/github/issues_delete_test.go +++ b/pkg/github/issues_delete_test.go @@ -10,7 +10,7 @@ import ( "testing" "github.com/github/github-mcp-server/internal/githubv4mock" - gogithub "github.com/google/go-github/v89/github" + gogithub "github.com/google/go-github/v92/github" "github.com/shurcooL/githubv4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/issues_granular.go b/pkg/github/issues_granular.go index bed003bef3..cd32b9c59f 100644 --- a/pkg/github/issues_granular.go +++ b/pkg/github/issues_granular.go @@ -13,7 +13,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" diff --git a/pkg/github/issues_test.go b/pkg/github/issues_test.go index e2abb2e8de..bea82c0c22 100644 --- a/pkg/github/issues_test.go +++ b/pkg/github/issues_test.go @@ -20,7 +20,7 @@ import ( transportpkg "github.com/github/github-mcp-server/pkg/http/transport" "github.com/github/github-mcp-server/pkg/inventory" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" @@ -6346,7 +6346,9 @@ func TestAddIssueCommentHandler(t *testing.T) { { name: "successful comment on issue", mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ - PostReposIssuesCommentsByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusCreated, mockComment), + PostReposIssuesCommentsByOwnerByRepoByIssueNumber: expectRequestBody(t, map[string]any{ + "body": "This is a comment", + }).andThen(mockResponse(t, http.StatusCreated, mockComment)), }), requestArgs: map[string]any{ "owner": "owner", diff --git a/pkg/github/minimal_types.go b/pkg/github/minimal_types.go index 2eba9a1628..2b06b279d5 100644 --- a/pkg/github/minimal_types.go +++ b/pkg/github/minimal_types.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/github/github-mcp-server/pkg/sanitize" diff --git a/pkg/github/notifications.go b/pkg/github/notifications.go index a637951f0a..117e96c806 100644 --- a/pkg/github/notifications.go +++ b/pkg/github/notifications.go @@ -14,7 +14,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/github/notifications_test.go b/pkg/github/notifications_test.go index f2cbaff9f4..c7fc296cb0 100644 --- a/pkg/github/notifications_test.go +++ b/pkg/github/notifications_test.go @@ -8,7 +8,7 @@ import ( "github.com/github/github-mcp-server/internal/toolsnaps" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/params.go b/pkg/github/params.go index a03a6a1581..f4da2d6814 100644 --- a/pkg/github/params.go +++ b/pkg/github/params.go @@ -6,7 +6,7 @@ import ( "math" "strconv" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" ) diff --git a/pkg/github/params_test.go b/pkg/github/params_test.go index 55a5526f77..3c0d7f43d1 100644 --- a/pkg/github/params_test.go +++ b/pkg/github/params_test.go @@ -5,7 +5,7 @@ import ( "math" "testing" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/github/projects.go b/pkg/github/projects.go index 71c29d5720..4cb6a875cd 100644 --- a/pkg/github/projects.go +++ b/pkg/github/projects.go @@ -19,7 +19,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" diff --git a/pkg/github/projects_batch.go b/pkg/github/projects_batch.go index 70b590ea6f..3c4e16c979 100644 --- a/pkg/github/projects_batch.go +++ b/pkg/github/projects_batch.go @@ -11,7 +11,7 @@ import ( ghErrors "github.com/github/github-mcp-server/pkg/errors" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" ) diff --git a/pkg/github/projects_test.go b/pkg/github/projects_test.go index 8b40a569e0..256f5af9d8 100644 --- a/pkg/github/projects_test.go +++ b/pkg/github/projects_test.go @@ -13,7 +13,7 @@ import ( transportpkg "github.com/github/github-mcp-server/pkg/http/transport" "github.com/github/github-mcp-server/pkg/inventory" "github.com/github/github-mcp-server/pkg/translations" - gogithub "github.com/google/go-github/v89/github" + gogithub "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/shurcooL/githubv4" "github.com/stretchr/testify/assert" diff --git a/pkg/github/pullrequests.go b/pkg/github/pullrequests.go index e30995d66e..42d99ab9bb 100644 --- a/pkg/github/pullrequests.go +++ b/pkg/github/pullrequests.go @@ -8,7 +8,7 @@ import ( "net/http" "github.com/go-viper/mapstructure/v2" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" diff --git a/pkg/github/pullrequests_granular.go b/pkg/github/pullrequests_granular.go index 92e7fb6f5d..cf7fa93c1d 100644 --- a/pkg/github/pullrequests_granular.go +++ b/pkg/github/pullrequests_granular.go @@ -12,7 +12,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - gogithub "github.com/google/go-github/v89/github" + gogithub "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" diff --git a/pkg/github/pullrequests_sha_pinning_test.go b/pkg/github/pullrequests_sha_pinning_test.go index 77e2d5152a..b1a6df3025 100644 --- a/pkg/github/pullrequests_sha_pinning_test.go +++ b/pkg/github/pullrequests_sha_pinning_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/github/pullrequests_test.go b/pkg/github/pullrequests_test.go index e2d8d88140..d06fe72806 100644 --- a/pkg/github/pullrequests_test.go +++ b/pkg/github/pullrequests_test.go @@ -12,7 +12,7 @@ import ( "github.com/github/github-mcp-server/internal/githubv4mock" "github.com/github/github-mcp-server/internal/toolsnaps" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/shurcooL/githubv4" "github.com/stretchr/testify/assert" diff --git a/pkg/github/repositories.go b/pkg/github/repositories.go index 8e2dd25172..21647059b7 100644 --- a/pkg/github/repositories.go +++ b/pkg/github/repositories.go @@ -20,7 +20,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" diff --git a/pkg/github/repositories_helper.go b/pkg/github/repositories_helper.go index 8859b4a2ba..0b46b839f1 100644 --- a/pkg/github/repositories_helper.go +++ b/pkg/github/repositories_helper.go @@ -15,7 +15,7 @@ import ( ghErrors "github.com/github/github-mcp-server/pkg/errors" "github.com/github/github-mcp-server/pkg/raw" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/github/repositories_test.go b/pkg/github/repositories_test.go index 8194895afa..4719b23164 100644 --- a/pkg/github/repositories_test.go +++ b/pkg/github/repositories_test.go @@ -17,7 +17,7 @@ import ( "github.com/github/github-mcp-server/pkg/raw" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" diff --git a/pkg/github/repository_resource.go b/pkg/github/repository_resource.go index 323cdf45d2..d6f82294f8 100644 --- a/pkg/github/repository_resource.go +++ b/pkg/github/repository_resource.go @@ -15,7 +15,7 @@ import ( "github.com/github/github-mcp-server/pkg/octicons" "github.com/github/github-mcp-server/pkg/raw" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/yosida95/uritemplate/v3" ) diff --git a/pkg/github/repository_resource_completions.go b/pkg/github/repository_resource_completions.go index 2c05d0531e..deb4b738dc 100644 --- a/pkg/github/repository_resource_completions.go +++ b/pkg/github/repository_resource_completions.go @@ -6,7 +6,7 @@ import ( "fmt" "strings" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/github/repository_resource_completions_test.go b/pkg/github/repository_resource_completions_test.go index 22c083b466..7d20d1e1db 100644 --- a/pkg/github/repository_resource_completions_test.go +++ b/pkg/github/repository_resource_completions_test.go @@ -6,7 +6,7 @@ import ( "fmt" "testing" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/rulesets.go b/pkg/github/rulesets.go index ac05282aaa..2e856b5dd7 100644 --- a/pkg/github/rulesets.go +++ b/pkg/github/rulesets.go @@ -14,7 +14,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" ) @@ -403,7 +403,7 @@ func GetRepositoryRulesForBranch(ctx context.Context, client *github.Client, own PerPage: pagination.PerPage, } - branchRules, resp, err := client.Repositories.ListRulesForBranch(ctx, owner, repo, url.PathEscape(branch), opts) + branchRules, resp, err := client.Repositories.ListRulesForBranch(ctx, owner, repo, branch, opts) if resp != nil { defer func() { _ = resp.Body.Close() }() } diff --git a/pkg/github/rulesets_test.go b/pkg/github/rulesets_test.go index 37de10f112..659efc671f 100644 --- a/pkg/github/rulesets_test.go +++ b/pkg/github/rulesets_test.go @@ -9,7 +9,7 @@ import ( "net/url" "testing" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/sanitize_coverage_test.go b/pkg/github/sanitize_coverage_test.go index 20a175b07a..b35081719b 100644 --- a/pkg/github/sanitize_coverage_test.go +++ b/pkg/github/sanitize_coverage_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/shurcooL/githubv4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/search.go b/pkg/github/search.go index 715ca83aed..18c4d466db 100644 --- a/pkg/github/search.go +++ b/pkg/github/search.go @@ -13,7 +13,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/github/search_test.go b/pkg/github/search_test.go index 52e70b639c..8139ce76aa 100644 --- a/pkg/github/search_test.go +++ b/pkg/github/search_test.go @@ -9,7 +9,7 @@ import ( "github.com/github/github-mcp-server/internal/toolsnaps" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/search_utils.go b/pkg/github/search_utils.go index 52d735ca0b..49ec7bbff2 100644 --- a/pkg/github/search_utils.go +++ b/pkg/github/search_utils.go @@ -11,7 +11,7 @@ import ( ghErrors "github.com/github/github-mcp-server/pkg/errors" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/github/secret_scanning.go b/pkg/github/secret_scanning.go index f73fede25b..8f6659f047 100644 --- a/pkg/github/secret_scanning.go +++ b/pkg/github/secret_scanning.go @@ -13,7 +13,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/github/secret_scanning_test.go b/pkg/github/secret_scanning_test.go index 603f1e9eb2..58b59296b8 100644 --- a/pkg/github/secret_scanning_test.go +++ b/pkg/github/secret_scanning_test.go @@ -8,7 +8,7 @@ import ( "github.com/github/github-mcp-server/internal/toolsnaps" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/security_advisories.go b/pkg/github/security_advisories.go index 63a9a43b0e..633cd2a8a4 100644 --- a/pkg/github/security_advisories.go +++ b/pkg/github/security_advisories.go @@ -13,7 +13,7 @@ import ( "github.com/github/github-mcp-server/pkg/scopes" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/pkg/github/security_advisories_test.go b/pkg/github/security_advisories_test.go index 8c212d5843..fc8e660a6f 100644 --- a/pkg/github/security_advisories_test.go +++ b/pkg/github/security_advisories_test.go @@ -8,7 +8,7 @@ import ( "github.com/github/github-mcp-server/internal/toolsnaps" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/stretchr/testify/assert" diff --git a/pkg/github/server_test.go b/pkg/github/server_test.go index bd8b9abd6b..54d8bc890f 100644 --- a/pkg/github/server_test.go +++ b/pkg/github/server_test.go @@ -17,7 +17,7 @@ import ( "github.com/github/github-mcp-server/pkg/observability/metrics" "github.com/github/github-mcp-server/pkg/raw" "github.com/github/github-mcp-server/pkg/translations" - gogithub "github.com/google/go-github/v89/github" + gogithub "github.com/google/go-github/v92/github" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" "github.com/stretchr/testify/assert" diff --git a/pkg/github/tools.go b/pkg/github/tools.go index eb1113754a..e3177f668f 100644 --- a/pkg/github/tools.go +++ b/pkg/github/tools.go @@ -5,7 +5,7 @@ import ( "slices" "strings" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/shurcooL/githubv4" "github.com/github/github-mcp-server/pkg/inventory" diff --git a/pkg/github/ui_tools.go b/pkg/github/ui_tools.go index 60ff65a742..a1a4179858 100644 --- a/pkg/github/ui_tools.go +++ b/pkg/github/ui_tools.go @@ -13,7 +13,7 @@ import ( "github.com/github/github-mcp-server/pkg/inventory" "github.com/github/github-mcp-server/pkg/translations" "github.com/github/github-mcp-server/pkg/utils" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" "github.com/shurcooL/githubv4" diff --git a/pkg/github/ui_tools_test.go b/pkg/github/ui_tools_test.go index 8bf28c3674..5f6f1a61fd 100644 --- a/pkg/github/ui_tools_test.go +++ b/pkg/github/ui_tools_test.go @@ -14,7 +14,7 @@ import ( "github.com/github/github-mcp-server/internal/toolsnaps" ghcontext "github.com/github/github-mcp-server/pkg/context" "github.com/github/github-mcp-server/pkg/translations" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/google/jsonschema-go/jsonschema" "github.com/shurcooL/githubv4" "github.com/stretchr/testify/assert" diff --git a/pkg/lockdown/lockdown.go b/pkg/lockdown/lockdown.go index 8625073116..1e47890852 100644 --- a/pkg/lockdown/lockdown.go +++ b/pkg/lockdown/lockdown.go @@ -11,7 +11,7 @@ import ( "sync" "time" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/muesli/cache2go" "github.com/shurcooL/githubv4" ) diff --git a/pkg/lockdown/lockdown_test.go b/pkg/lockdown/lockdown_test.go index 6254533682..22fab53b03 100644 --- a/pkg/lockdown/lockdown_test.go +++ b/pkg/lockdown/lockdown_test.go @@ -11,7 +11,7 @@ import ( "time" "github.com/github/github-mcp-server/internal/githubv4mock" - gogithub "github.com/google/go-github/v89/github" + gogithub "github.com/google/go-github/v92/github" "github.com/muesli/cache2go" "github.com/shurcooL/githubv4" "github.com/stretchr/testify/require" diff --git a/pkg/raw/raw.go b/pkg/raw/raw.go index fcaa5c6af2..3bf77b29e7 100644 --- a/pkg/raw/raw.go +++ b/pkg/raw/raw.go @@ -8,7 +8,7 @@ import ( "net/url" "strings" - gogithub "github.com/google/go-github/v89/github" + gogithub "github.com/google/go-github/v92/github" ) // errPathTraversal is returned when an owner, repo, ref/sha, or path diff --git a/pkg/raw/raw_test.go b/pkg/raw/raw_test.go index 8f6f54f4c7..4269ade1ea 100644 --- a/pkg/raw/raw_test.go +++ b/pkg/raw/raw_test.go @@ -9,7 +9,7 @@ import ( "strings" "testing" - "github.com/google/go-github/v89/github" + "github.com/google/go-github/v92/github" "github.com/stretchr/testify/require" ) diff --git a/third-party-licenses.darwin.md b/third-party-licenses.darwin.md index 495abb0250..d67b374b56 100644 --- a/third-party-licenses.darwin.md +++ b/third-party-licenses.darwin.md @@ -17,7 +17,7 @@ The following packages are included for the amd64, arm64 architectures. - [github.com/github/github-mcp-server](https://pkg.go.dev/github.com/github/github-mcp-server) ([MIT](https://github.com/github/github-mcp-server/blob/HEAD/LICENSE)) - [github.com/go-chi/chi/v5](https://pkg.go.dev/github.com/go-chi/chi/v5) ([MIT](https://github.com/go-chi/chi/blob/v5.3.2/LICENSE)) - [github.com/go-viper/mapstructure/v2](https://pkg.go.dev/github.com/go-viper/mapstructure/v2) ([MIT](https://github.com/go-viper/mapstructure/blob/v2.5.0/LICENSE)) - - [github.com/google/go-github/v89/github](https://pkg.go.dev/github.com/google/go-github/v89/github) ([BSD-3-Clause](https://github.com/google/go-github/blob/34349a88bac3/LICENSE)) + - [github.com/google/go-github/v92/github](https://pkg.go.dev/github.com/google/go-github/v92/github) ([BSD-3-Clause](https://github.com/google/go-github/blob/v92.0.0/LICENSE)) - [github.com/google/go-querystring/query](https://pkg.go.dev/github.com/google/go-querystring/query) ([BSD-3-Clause](https://github.com/google/go-querystring/blob/v1.2.0/LICENSE)) - [github.com/google/jsonschema-go/jsonschema](https://pkg.go.dev/github.com/google/jsonschema-go/jsonschema) ([MIT](https://github.com/google/jsonschema-go/blob/v0.4.3/LICENSE)) - [github.com/gorilla/css/scanner](https://pkg.go.dev/github.com/gorilla/css/scanner) ([BSD-3-Clause](https://github.com/gorilla/css/blob/v1.0.1/LICENSE)) diff --git a/third-party-licenses.linux.md b/third-party-licenses.linux.md index d46c953766..a1abb6821b 100644 --- a/third-party-licenses.linux.md +++ b/third-party-licenses.linux.md @@ -17,7 +17,7 @@ The following packages are included for the 386, amd64, arm64 architectures. - [github.com/github/github-mcp-server](https://pkg.go.dev/github.com/github/github-mcp-server) ([MIT](https://github.com/github/github-mcp-server/blob/HEAD/LICENSE)) - [github.com/go-chi/chi/v5](https://pkg.go.dev/github.com/go-chi/chi/v5) ([MIT](https://github.com/go-chi/chi/blob/v5.3.2/LICENSE)) - [github.com/go-viper/mapstructure/v2](https://pkg.go.dev/github.com/go-viper/mapstructure/v2) ([MIT](https://github.com/go-viper/mapstructure/blob/v2.5.0/LICENSE)) - - [github.com/google/go-github/v89/github](https://pkg.go.dev/github.com/google/go-github/v89/github) ([BSD-3-Clause](https://github.com/google/go-github/blob/34349a88bac3/LICENSE)) + - [github.com/google/go-github/v92/github](https://pkg.go.dev/github.com/google/go-github/v92/github) ([BSD-3-Clause](https://github.com/google/go-github/blob/v92.0.0/LICENSE)) - [github.com/google/go-querystring/query](https://pkg.go.dev/github.com/google/go-querystring/query) ([BSD-3-Clause](https://github.com/google/go-querystring/blob/v1.2.0/LICENSE)) - [github.com/google/jsonschema-go/jsonschema](https://pkg.go.dev/github.com/google/jsonschema-go/jsonschema) ([MIT](https://github.com/google/jsonschema-go/blob/v0.4.3/LICENSE)) - [github.com/gorilla/css/scanner](https://pkg.go.dev/github.com/gorilla/css/scanner) ([BSD-3-Clause](https://github.com/gorilla/css/blob/v1.0.1/LICENSE)) diff --git a/third-party-licenses.windows.md b/third-party-licenses.windows.md index 856d78cc68..7e6f57aeb7 100644 --- a/third-party-licenses.windows.md +++ b/third-party-licenses.windows.md @@ -17,7 +17,7 @@ The following packages are included for the 386, amd64, arm64 architectures. - [github.com/github/github-mcp-server](https://pkg.go.dev/github.com/github/github-mcp-server) ([MIT](https://github.com/github/github-mcp-server/blob/HEAD/LICENSE)) - [github.com/go-chi/chi/v5](https://pkg.go.dev/github.com/go-chi/chi/v5) ([MIT](https://github.com/go-chi/chi/blob/v5.3.2/LICENSE)) - [github.com/go-viper/mapstructure/v2](https://pkg.go.dev/github.com/go-viper/mapstructure/v2) ([MIT](https://github.com/go-viper/mapstructure/blob/v2.5.0/LICENSE)) - - [github.com/google/go-github/v89/github](https://pkg.go.dev/github.com/google/go-github/v89/github) ([BSD-3-Clause](https://github.com/google/go-github/blob/34349a88bac3/LICENSE)) + - [github.com/google/go-github/v92/github](https://pkg.go.dev/github.com/google/go-github/v92/github) ([BSD-3-Clause](https://github.com/google/go-github/blob/v92.0.0/LICENSE)) - [github.com/google/go-querystring/query](https://pkg.go.dev/github.com/google/go-querystring/query) ([BSD-3-Clause](https://github.com/google/go-querystring/blob/v1.2.0/LICENSE)) - [github.com/google/jsonschema-go/jsonschema](https://pkg.go.dev/github.com/google/jsonschema-go/jsonschema) ([MIT](https://github.com/google/jsonschema-go/blob/v0.4.3/LICENSE)) - [github.com/gorilla/css/scanner](https://pkg.go.dev/github.com/gorilla/css/scanner) ([BSD-3-Clause](https://github.com/gorilla/css/blob/v1.0.1/LICENSE)) diff --git a/third-party/github.com/google/go-github/v89/github/LICENSE b/third-party/github.com/google/go-github/v92/github/LICENSE similarity index 100% rename from third-party/github.com/google/go-github/v89/github/LICENSE rename to third-party/github.com/google/go-github/v92/github/LICENSE From f89f88451dc8b40a19a1e9b5f3f077c7471d9226 Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Fri, 2 Oct 2026 12:20:48 +0200 Subject: [PATCH 09/13] build(ui): upgrade TypeScript 7 and align Node 26 toolchain (#3379) Read the CI Node version from UI engines and match Docker and Node types.\nDocument the native compiler workflow without changing UI dependencies. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/actions/build-ui/action.yml | 2 +- .github/workflows/code-scanning.yml | 2 +- CONTRIBUTING.md | 11 + ui/package-lock.json | 393 ++++++++++++++++++++++++++-- ui/package.json | 6 +- 5 files changed, 393 insertions(+), 21 deletions(-) diff --git a/.github/actions/build-ui/action.yml b/.github/actions/build-ui/action.yml index 7dfe93f518..a7c73aea10 100644 --- a/.github/actions/build-ui/action.yml +++ b/.github/actions/build-ui/action.yml @@ -20,7 +20,7 @@ runs: if: steps.cache-ui.outputs.cache-hit != 'true' uses: actions/setup-node@v7 with: - node-version: "22" + node-version-file: ui/package.json cache: npm cache-dependency-path: ui/package-lock.json diff --git a/.github/workflows/code-scanning.yml b/.github/workflows/code-scanning.yml index 18ec6d7cc3..18d8d69ac8 100644 --- a/.github/workflows/code-scanning.yml +++ b/.github/workflows/code-scanning.yml @@ -82,7 +82,7 @@ jobs: if: matrix.language == 'javascript' uses: actions/setup-node@v7 with: - node-version: "20" + node-version-file: ui/package.json cache: "npm" cache-dependency-path: ui/package-lock.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8c8f294b36..7ec4f136a8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,6 +29,17 @@ These are one time installations required to be able to test your changes locall 1. Install Go 1.26.8 or later [through download](https://go.dev/doc/install) | [through Homebrew](https://formulae.brew.sh/formula/go) 2. [Install golangci-lint v2.14.0](https://golangci-lint.run/welcome/install/#local-installation), or let `script/lint` install the repository-pinned version. The pinned version supports both Go 1.26 and Go 1.27. +### UI development + +Use Node.js 26.x for the UI in `ui/`. This matches the Docker UI build stage +and `@types/node`; GitHub Actions reads the Node version from `ui/package.json`. +Node 26 is a supported Current release, with LTS scheduled for October 2026. + +From `ui/`, run `npm ci`, `npm run typecheck`, and `npm run build`. +Type checking uses the native TypeScript 7 compiler through `tsc`; Vite handles +transpilation and bundling separately. No TypeScript compiler API integration, +typescript-eslint, or ts-node is required. + ## Submitting a pull request 1. [Fork][fork] and clone the repository diff --git a/ui/package-lock.json b/ui/package-lock.json index 22ca32aa96..72de424ec9 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -18,16 +18,16 @@ "remark-gfm": "^4.0.1" }, "devDependencies": { - "@types/node": "^25.9.9", + "@types/node": "^26.6.4", "@types/react": "^18.3.31", "@types/react-dom": "^18.3.7", "@vitejs/plugin-react": "^6.1.1", - "typescript": "^5.9.3", + "typescript": "^7.0.2", "vite": "^8.3.2", "vite-plugin-singlefile": "^2.3.3" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^26.0.0" } }, "node_modules/@babel/code-frame": { @@ -1824,13 +1824,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.9.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.9.tgz", - "integrity": "sha512-b4e2xxj/yMeT2hNlD6x7zNFiQK2Dlmkb9CQ6v3/1G2RezKN4YS2lcEv8lzGm1mhs4f2lM4yNAMI/zUDBZBBt7w==", + "version": "26.6.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.4.tgz", + "integrity": "sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" + "undici-types": "~8.9.0" } }, "node_modules/@types/prop-types": { @@ -1898,6 +1898,346 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz", @@ -5515,23 +5855,44 @@ } }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", "dev": true, "license": "Apache-2.0", "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "tsc": "bin/tsc" }, "engines": { - "node": ">=14.17" + "node": ">=16.20.0" + }, + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" } }, "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", "dev": true, "license": "MIT" }, diff --git a/ui/package.json b/ui/package.json index 6534328641..bd8097d0e5 100644 --- a/ui/package.json +++ b/ui/package.json @@ -5,7 +5,7 @@ "type": "module", "description": "MCP App UIs for github-mcp-server using Primer React", "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^26.0.0" }, "scripts": { "build": "node scripts/build.mjs", @@ -24,11 +24,11 @@ "remark-gfm": "^4.0.1" }, "devDependencies": { - "@types/node": "^25.9.9", + "@types/node": "^26.6.4", "@types/react": "^18.3.31", "@types/react-dom": "^18.3.7", "@vitejs/plugin-react": "^6.1.1", - "typescript": "^5.9.3", + "typescript": "^7.0.2", "vite": "^8.3.2", "vite-plugin-singlefile": "^2.3.3" } From 876195b5b943e11a321f25190b139b9e922f7dbf Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Fri, 2 Oct 2026 13:07:00 +0200 Subject: [PATCH 10/13] feat(ui): migrate MCP Apps SDK to v2 (#3378) * feat(ui): migrate MCP Apps SDK to v2 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix: migrate go-github Ptr calls for Go 1.26 lint Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- pkg/github/actions_minimal_test.go | 180 +-- pkg/github/actions_test.go | 88 +- pkg/github/code_quality_test.go | 8 +- pkg/github/code_scanning_test.go | 24 +- pkg/github/comment_minimize_test.go | 8 +- pkg/github/context_tools_test.go | 52 +- pkg/github/copilot_test.go | 36 +- pkg/github/custom_properties_test.go | 2 +- pkg/github/dependabot_test.go | 22 +- pkg/github/discussions.go | 10 +- pkg/github/fields_filtering_test.go | 44 +- pkg/github/gists.go | 10 +- pkg/github/gists_test.go | 74 +- pkg/github/git_test.go | 30 +- pkg/github/granular_tools_test.go | 76 +- pkg/github/issues.go | 20 +- pkg/github/issues_delete_test.go | 34 +- pkg/github/issues_test.go | 482 ++++---- pkg/github/minimal_types.go | 22 +- pkg/github/notifications_test.go | 24 +- pkg/github/projects_test.go | 2 +- pkg/github/pullrequests.go | 20 +- pkg/github/pullrequests_sha_pinning_test.go | 6 +- pkg/github/pullrequests_test.go | 418 +++---- pkg/github/repositories.go | 44 +- pkg/github/repositories_helper.go | 4 +- pkg/github/repositories_test.go | 790 ++++++------ pkg/github/sanitize_coverage_test.go | 48 +- pkg/github/search_test.go | 192 +-- pkg/github/search_utils.go | 2 +- pkg/github/secret_scanning_test.go | 26 +- pkg/github/security_advisories_test.go | 60 +- pkg/github/server_test.go | 2 +- pkg/github/ui_tools_test.go | 28 +- pkg/lockdown/lockdown_test.go | 2 +- ui/package-lock.json | 1186 +------------------ ui/package.json | 6 +- ui/src/hooks/useMcpApp.ts | 2 +- ui/src/lib/toolResult.ts | 2 +- 39 files changed, 1496 insertions(+), 2590 deletions(-) diff --git a/pkg/github/actions_minimal_test.go b/pkg/github/actions_minimal_test.go index ef3504d38b..a2d547b33f 100644 --- a/pkg/github/actions_minimal_test.go +++ b/pkg/github/actions_minimal_test.go @@ -79,7 +79,7 @@ func TestConvertToMinimalWorkflowJob(t *testing.T) { func TestConvertToMinimalActionsLists(t *testing.T) { t.Run("workflow runs", func(t *testing.T) { result := convertToMinimalWorkflowRuns(&github.WorkflowRuns{ - TotalCount: github.Ptr(2), + TotalCount: new(2), WorkflowRuns: []*github.WorkflowRun{actionsTestWorkflowRun(), nil}, }) assert.Equal(t, 2, result.TotalCount) @@ -88,7 +88,7 @@ func TestConvertToMinimalActionsLists(t *testing.T) { t.Run("workflow jobs", func(t *testing.T) { result := convertToMinimalWorkflowJobs(&github.Jobs{ - TotalCount: github.Ptr(2), + TotalCount: new(2), Jobs: []*github.WorkflowJob{actionsTestWorkflowJob(), nil}, }) assert.Equal(t, 2, result.TotalCount) @@ -110,94 +110,94 @@ func TestConvertToMinimalActionsLists(t *testing.T) { func actionsTestWorkflowRun() *github.WorkflowRun { repository := &github.Repository{ - ID: github.Ptr(int64(1296269)), - NodeID: github.Ptr("MDEwOlJlcG9zaXRvcnkxMjk2MjY5"), - Name: github.Ptr("octo-repo"), - FullName: github.Ptr("octo-org/octo-repo"), - Description: github.Ptr("A representative repository description included in the full API response."), - HTMLURL: github.Ptr("https://github.com/octo-org/octo-repo"), - URL: github.Ptr("https://api.github.com/repos/octo-org/octo-repo"), - CloneURL: github.Ptr("https://github.com/octo-org/octo-repo.git"), - Language: github.Ptr("Go"), + ID: new(int64(1296269)), + NodeID: new("MDEwOlJlcG9zaXRvcnkxMjk2MjY5"), + Name: new("octo-repo"), + FullName: new("octo-org/octo-repo"), + Description: new("A representative repository description included in the full API response."), + HTMLURL: new("https://github.com/octo-org/octo-repo"), + URL: new("https://api.github.com/repos/octo-org/octo-repo"), + CloneURL: new("https://github.com/octo-org/octo-repo.git"), + Language: new("Go"), Topics: []string{"actions", "mcp", "automation"}, } return &github.WorkflowRun{ - ID: github.Ptr(int64(30433642)), - Name: github.Ptr("CI"), - NodeID: github.Ptr("MDEyOldvcmtmbG93IFJ1bjI2OTI4OQ=="), - HeadBranch: github.Ptr("feature/minimal-actions"), - HeadSHA: github.Ptr("acb5820ced9479c074f688cc328bf03f341a511d"), - Path: github.Ptr(".github/workflows/ci.yml"), - RunNumber: github.Ptr(562), - RunAttempt: github.Ptr(2), - Event: github.Ptr("pull_request"), - DisplayTitle: github.Ptr("Reduce GitHub Actions response payloads"), - Status: github.Ptr("completed"), - Conclusion: github.Ptr("failure"), - WorkflowID: github.Ptr(int64(161335)), - CheckSuiteID: github.Ptr(int64(42)), - CheckSuiteNodeID: github.Ptr("MDEwOkNoZWNrU3VpdGU0Mg=="), - URL: github.Ptr("https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642"), - HTMLURL: github.Ptr("https://github.com/octo-org/octo-repo/actions/runs/30433642"), - JobsURL: github.Ptr("https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/jobs"), - LogsURL: github.Ptr("https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/logs"), - CheckSuiteURL: github.Ptr("https://api.github.com/repos/octo-org/octo-repo/check-suites/42"), - ArtifactsURL: github.Ptr("https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/artifacts"), - CancelURL: github.Ptr("https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/cancel"), - RerunURL: github.Ptr("https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/rerun"), - PreviousAttemptURL: github.Ptr("https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/attempts/1"), - WorkflowURL: github.Ptr("https://api.github.com/repos/octo-org/octo-repo/actions/workflows/161335"), + ID: new(int64(30433642)), + Name: new("CI"), + NodeID: new("MDEyOldvcmtmbG93IFJ1bjI2OTI4OQ=="), + HeadBranch: new("feature/minimal-actions"), + HeadSHA: new("acb5820ced9479c074f688cc328bf03f341a511d"), + Path: new(".github/workflows/ci.yml"), + RunNumber: new(562), + RunAttempt: new(2), + Event: new("pull_request"), + DisplayTitle: new("Reduce GitHub Actions response payloads"), + Status: new("completed"), + Conclusion: new("failure"), + WorkflowID: new(int64(161335)), + CheckSuiteID: new(int64(42)), + CheckSuiteNodeID: new("MDEwOkNoZWNrU3VpdGU0Mg=="), + URL: new("https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642"), + HTMLURL: new("https://github.com/octo-org/octo-repo/actions/runs/30433642"), + JobsURL: new("https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/jobs"), + LogsURL: new("https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/logs"), + CheckSuiteURL: new("https://api.github.com/repos/octo-org/octo-repo/check-suites/42"), + ArtifactsURL: new("https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/artifacts"), + CancelURL: new("https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/cancel"), + RerunURL: new("https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/rerun"), + PreviousAttemptURL: new("https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/attempts/1"), + WorkflowURL: new("https://api.github.com/repos/octo-org/octo-repo/actions/workflows/161335"), Repository: repository, HeadRepository: repository, Actor: &github.User{ - Login: github.Ptr("octocat"), - ID: github.Ptr(int64(1)), - NodeID: github.Ptr("MDQ6VXNlcjE="), - AvatarURL: github.Ptr("https://github.com/images/error/octocat_happy.gif"), - HTMLURL: github.Ptr("https://github.com/octocat"), - URL: github.Ptr("https://api.github.com/users/octocat"), - Name: github.Ptr("The Octocat"), - Bio: github.Ptr("A long biography that is not needed to identify the workflow run actor."), + Login: new("octocat"), + ID: new(int64(1)), + NodeID: new("MDQ6VXNlcjE="), + AvatarURL: new("https://github.com/images/error/octocat_happy.gif"), + HTMLURL: new("https://github.com/octocat"), + URL: new("https://api.github.com/users/octocat"), + Name: new("The Octocat"), + Bio: new("A long biography that is not needed to identify the workflow run actor."), }, TriggeringActor: &github.User{ - Login: github.Ptr("hubot"), - ID: github.Ptr(int64(2)), - HTMLURL: github.Ptr("https://github.com/hubot"), - URL: github.Ptr("https://api.github.com/users/hubot"), + Login: new("hubot"), + ID: new(int64(2)), + HTMLURL: new("https://github.com/hubot"), + URL: new("https://api.github.com/users/hubot"), }, PullRequests: []*github.PullRequest{ { - ID: github.Ptr(int64(1001)), - Number: github.Ptr(42), - Title: github.Ptr("Reduce GitHub Actions response payloads"), - Body: github.Ptr("A pull request body that is unnecessary in a workflow run response."), - HTMLURL: github.Ptr("https://github.com/octo-org/octo-repo/pull/42"), + ID: new(int64(1001)), + Number: new(42), + Title: new("Reduce GitHub Actions response payloads"), + Body: new("A pull request body that is unnecessary in a workflow run response."), + HTMLURL: new("https://github.com/octo-org/octo-repo/pull/42"), Head: &github.PullRequestBranch{ - Ref: github.Ptr("feature/minimal-actions"), - SHA: github.Ptr("acb5820ced9479c074f688cc328bf03f341a511d"), + Ref: new("feature/minimal-actions"), + SHA: new("acb5820ced9479c074f688cc328bf03f341a511d"), Repo: repository, }, Base: &github.PullRequestBranch{ - Ref: github.Ptr("main"), - SHA: github.Ptr("9a2f3ec"), + Ref: new("main"), + SHA: new("9a2f3ec"), Repo: repository, }, }, }, HeadCommit: &github.HeadCommit{ - Message: github.Ptr("Reduce GitHub Actions response payloads"), - URL: github.Ptr("https://api.github.com/repos/octo-org/octo-repo/commits/acb5820"), + Message: new("Reduce GitHub Actions response payloads"), + URL: new("https://api.github.com/repos/octo-org/octo-repo/commits/acb5820"), Author: &github.CommitAuthor{ - Name: github.Ptr("The Octocat"), - Email: github.Ptr("octocat@example.com"), + Name: new("The Octocat"), + Email: new("octocat@example.com"), }, }, ReferencedWorkflows: []*github.ReferencedWorkflow{ { - Path: github.Ptr(".github/workflows/reusable-tests.yml"), - SHA: github.Ptr("9f4f87d9790ab0f5c2c5ad2b74b886cab515a886"), - Ref: github.Ptr("refs/tags/v3"), + Path: new(".github/workflows/reusable-tests.yml"), + SHA: new("9f4f87d9790ab0f5c2c5ad2b74b886cab515a886"), + Ref: new("refs/tags/v3"), }, nil, }, @@ -209,42 +209,42 @@ func actionsTestWorkflowRun() *github.WorkflowRun { func actionsTestWorkflowJob() *github.WorkflowJob { return &github.WorkflowJob{ - ID: github.Ptr(int64(399444496)), - RunID: github.Ptr(int64(30433642)), - RunURL: github.Ptr("https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642"), - NodeID: github.Ptr("MDEyOldvcmtmbG93IEpvYjM5OTQ0NDQ5Ng=="), - HeadBranch: github.Ptr("feature/minimal-actions"), - HeadSHA: github.Ptr("acb5820ced9479c074f688cc328bf03f341a511d"), - URL: github.Ptr("https://api.github.com/repos/octo-org/octo-repo/actions/jobs/399444496"), - HTMLURL: github.Ptr("https://github.com/octo-org/octo-repo/runs/399444496"), - Status: github.Ptr("completed"), - Conclusion: github.Ptr("failure"), + ID: new(int64(399444496)), + RunID: new(int64(30433642)), + RunURL: new("https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642"), + NodeID: new("MDEyOldvcmtmbG93IEpvYjM5OTQ0NDQ5Ng=="), + HeadBranch: new("feature/minimal-actions"), + HeadSHA: new("acb5820ced9479c074f688cc328bf03f341a511d"), + URL: new("https://api.github.com/repos/octo-org/octo-repo/actions/jobs/399444496"), + HTMLURL: new("https://github.com/octo-org/octo-repo/runs/399444496"), + Status: new("completed"), + Conclusion: new("failure"), CreatedAt: actionsTestTimestamp(), StartedAt: actionsTestTimestamp(), CompletedAt: actionsTestTimestamp(), - Name: github.Ptr("test (ubuntu-latest, Go 1.24)"), - CheckRunURL: github.Ptr("https://api.github.com/repos/octo-org/octo-repo/check-runs/399444496"), + Name: new("test (ubuntu-latest, Go 1.24)"), + CheckRunURL: new("https://api.github.com/repos/octo-org/octo-repo/check-runs/399444496"), Labels: []string{"ubuntu-latest", "x64"}, - RunnerID: github.Ptr(int64(1)), - RunnerName: github.Ptr("GitHub Actions 1"), - RunnerGroupID: github.Ptr(int64(2)), - RunnerGroupName: github.Ptr("GitHub Actions"), - RunAttempt: github.Ptr(int64(2)), - WorkflowName: github.Ptr("CI"), + RunnerID: new(int64(1)), + RunnerName: new("GitHub Actions 1"), + RunnerGroupID: new(int64(2)), + RunnerGroupName: new("GitHub Actions"), + RunAttempt: new(int64(2)), + WorkflowName: new("CI"), Steps: []*github.TaskStep{ { - Name: github.Ptr("Set up job"), - Status: github.Ptr("completed"), - Conclusion: github.Ptr("success"), - Number: github.Ptr(int64(1)), + Name: new("Set up job"), + Status: new("completed"), + Conclusion: new("success"), + Number: new(int64(1)), StartedAt: actionsTestTimestamp(), CompletedAt: actionsTestTimestamp(), }, { - Name: github.Ptr("Run tests"), - Status: github.Ptr("completed"), - Conclusion: github.Ptr("failure"), - Number: github.Ptr(int64(2)), + Name: new("Run tests"), + Status: new("completed"), + Conclusion: new("failure"), + Number: new(int64(2)), StartedAt: actionsTestTimestamp(), CompletedAt: actionsTestTimestamp(), }, diff --git a/pkg/github/actions_test.go b/pkg/github/actions_test.go index d2b59b9c7f..1490389a58 100644 --- a/pkg/github/actions_test.go +++ b/pkg/github/actions_test.go @@ -46,19 +46,19 @@ func Test_ActionsList_ListWorkflows(t *testing.T) { mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposActionsWorkflowsByOwnerByRepo: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { workflows := &github.Workflows{ - TotalCount: github.Ptr(2), + TotalCount: new(2), Workflows: []*github.Workflow{ { - ID: github.Ptr(int64(1)), - Name: github.Ptr("CI"), - Path: github.Ptr(".github/workflows/ci.yml"), - State: github.Ptr("active"), + ID: new(int64(1)), + Name: new("CI"), + Path: new(".github/workflows/ci.yml"), + State: new("active"), }, { - ID: github.Ptr(int64(2)), - Name: github.Ptr("Deploy"), - Path: github.Ptr(".github/workflows/deploy.yml"), - State: github.Ptr("active"), + ID: new(int64(2)), + Name: new("Deploy"), + Path: new(".github/workflows/deploy.yml"), + State: new("active"), }, }, } @@ -122,13 +122,13 @@ func Test_ActionsList_ListWorkflowRuns(t *testing.T) { mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposActionsWorkflowsRunsByOwnerByRepoByWorkflowID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { runs := &github.WorkflowRuns{ - TotalCount: github.Ptr(1), + TotalCount: new(1), WorkflowRuns: []*github.WorkflowRun{ { - ID: github.Ptr(int64(123)), - Name: github.Ptr("CI"), - Status: github.Ptr("completed"), - Conclusion: github.Ptr("success"), + ID: new(int64(123)), + Name: new("CI"), + Status: new("completed"), + Conclusion: new("success"), }, }, } @@ -167,18 +167,18 @@ func Test_ActionsList_ListWorkflowRuns(t *testing.T) { mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposActionsRunsByOwnerByRepo: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { runs := &github.WorkflowRuns{ - TotalCount: github.Ptr(2), + TotalCount: new(2), WorkflowRuns: []*github.WorkflowRun{ { - ID: github.Ptr(int64(123)), - Name: github.Ptr("CI"), - Status: github.Ptr("completed"), - Conclusion: github.Ptr("success"), + ID: new(int64(123)), + Name: new("CI"), + Status: new("completed"), + Conclusion: new("success"), }, { - ID: github.Ptr(int64(456)), - Name: github.Ptr("Deploy"), - Status: github.Ptr("in_progress"), + ID: new(int64(456)), + Name: new("Deploy"), + Status: new("in_progress"), Conclusion: nil, }, }, @@ -217,7 +217,7 @@ func Test_ActionsList_ListWorkflowJobs(t *testing.T) { toolDef := ActionsList(translations.NullTranslationHelper) mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposActionsRunsJobsByOwnerByRepoByRunID: mockResponse(t, http.StatusOK, &github.Jobs{ - TotalCount: github.Ptr(1), + TotalCount: new(1), Jobs: []*github.WorkflowJob{actionsTestWorkflowJob()}, }), }) @@ -268,10 +268,10 @@ func Test_ActionsGet_GetWorkflow(t *testing.T) { mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposActionsWorkflowsByOwnerByRepoByWorkflowID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { workflow := &github.Workflow{ - ID: github.Ptr(int64(1)), - Name: github.Ptr("CI"), - Path: github.Ptr(".github/workflows/ci.yml"), - State: github.Ptr("active"), + ID: new(int64(1)), + Name: new("CI"), + Path: new(".github/workflows/ci.yml"), + State: new("active"), } w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(workflow) @@ -651,22 +651,22 @@ func Test_ActionsGetJobLogs_FailedJobs(t *testing.T) { mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposActionsRunsJobsByOwnerByRepoByRunID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { jobs := &github.Jobs{ - TotalCount: github.Ptr(3), + TotalCount: new(3), Jobs: []*github.WorkflowJob{ { - ID: github.Ptr(int64(1)), - Name: github.Ptr("test-job-1"), - Conclusion: github.Ptr("success"), + ID: new(int64(1)), + Name: new("test-job-1"), + Conclusion: new("success"), }, { - ID: github.Ptr(int64(2)), - Name: github.Ptr("test-job-2"), - Conclusion: github.Ptr("failure"), + ID: new(int64(2)), + Name: new("test-job-2"), + Conclusion: new("failure"), }, { - ID: github.Ptr(int64(3)), - Name: github.Ptr("test-job-3"), - Conclusion: github.Ptr("failure"), + ID: new(int64(3)), + Name: new("test-job-3"), + Conclusion: new("failure"), }, }, } @@ -710,17 +710,17 @@ func Test_ActionsGetJobLogs_FailedJobs(t *testing.T) { mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposActionsRunsJobsByOwnerByRepoByRunID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { jobs := &github.Jobs{ - TotalCount: github.Ptr(2), + TotalCount: new(2), Jobs: []*github.WorkflowJob{ { - ID: github.Ptr(int64(1)), - Name: github.Ptr("test-job-1"), - Conclusion: github.Ptr("success"), + ID: new(int64(1)), + Name: new("test-job-1"), + Conclusion: new("success"), }, { - ID: github.Ptr(int64(2)), - Name: github.Ptr("test-job-2"), - Conclusion: github.Ptr("success"), + ID: new(int64(2)), + Name: new("test-job-2"), + Conclusion: new("success"), }, }, } diff --git a/pkg/github/code_quality_test.go b/pkg/github/code_quality_test.go index 83a5a9d83b..edd2ce65f3 100644 --- a/pkg/github/code_quality_test.go +++ b/pkg/github/code_quality_test.go @@ -65,11 +65,11 @@ func Test_GetCodeQualityFinding(t *testing.T) { // Setup mock finding for success case mockFinding := &codeQualityFinding{ - Number: github.Ptr(42), - State: github.Ptr("open"), + Number: new(42), + State: new("open"), Rule: &codeQualityRule{ - ID: github.Ptr("test-rule"), - Description: github.Ptr("Test Rule Description"), + ID: new("test-rule"), + Description: new("Test Rule Description"), }, } diff --git a/pkg/github/code_scanning_test.go b/pkg/github/code_scanning_test.go index 976161213c..4811b66f5a 100644 --- a/pkg/github/code_scanning_test.go +++ b/pkg/github/code_scanning_test.go @@ -32,10 +32,10 @@ func Test_GetCodeScanningAlert(t *testing.T) { // Setup mock alert for success case mockAlert := &github.Alert{ - Number: github.Ptr(42), - State: github.Ptr("open"), - Rule: &github.Rule{ID: github.Ptr("test-rule"), Description: github.Ptr("Test Rule Description")}, - HTMLURL: github.Ptr("https://github.com/owner/repo/security/code-scanning/42"), + Number: new(42), + State: new("open"), + Rule: &github.Rule{ID: new("test-rule"), Description: new("Test Rule Description")}, + HTMLURL: new("https://github.com/owner/repo/security/code-scanning/42"), } tests := []struct { @@ -144,16 +144,16 @@ func Test_ListCodeScanningAlerts(t *testing.T) { // Setup mock alerts for success case mockAlerts := []*github.Alert{ { - Number: github.Ptr(42), - State: github.Ptr("open"), - Rule: &github.Rule{ID: github.Ptr("test-rule-1"), Description: github.Ptr("Test Rule 1")}, - HTMLURL: github.Ptr("https://github.com/owner/repo/security/code-scanning/42"), + Number: new(42), + State: new("open"), + Rule: &github.Rule{ID: new("test-rule-1"), Description: new("Test Rule 1")}, + HTMLURL: new("https://github.com/owner/repo/security/code-scanning/42"), }, { - Number: github.Ptr(43), - State: github.Ptr("fixed"), - Rule: &github.Rule{ID: github.Ptr("test-rule-2"), Description: github.Ptr("Test Rule 2")}, - HTMLURL: github.Ptr("https://github.com/owner/repo/security/code-scanning/43"), + Number: new(43), + State: new("fixed"), + Rule: &github.Rule{ID: new("test-rule-2"), Description: new("Test Rule 2")}, + HTMLURL: new("https://github.com/owner/repo/security/code-scanning/43"), }, } diff --git a/pkg/github/comment_minimize_test.go b/pkg/github/comment_minimize_test.go index a3337a7c4b..9c07c17879 100644 --- a/pkg/github/comment_minimize_test.go +++ b/pkg/github/comment_minimize_test.go @@ -174,9 +174,9 @@ func Test_CommentVisibilityToolSchemas(t *testing.T) { } func Test_HideAndUnhideComments(t *testing.T) { - issueComment := mockResponse(t, http.StatusOK, &github.IssueComment{ID: github.Ptr(int64(1)), NodeID: github.Ptr("IC_1")}) - reviewComment := mockResponse(t, http.StatusOK, &github.PullRequestComment{ID: github.Ptr(int64(2)), NodeID: github.Ptr("PRRC_2")}) - review := mockResponse(t, http.StatusOK, &github.PullRequestReview{ID: github.Ptr(int64(3)), NodeID: github.Ptr("PRR_3")}) + issueComment := mockResponse(t, http.StatusOK, &github.IssueComment{ID: new(int64(1)), NodeID: new("IC_1")}) + reviewComment := mockResponse(t, http.StatusOK, &github.PullRequestComment{ID: new(int64(2)), NodeID: new("PRRC_2")}) + review := mockResponse(t, http.StatusOK, &github.PullRequestReview{ID: new(int64(3)), NodeID: new("PRR_3")}) notFound := mockResponse(t, http.StatusNotFound, `{"message": "Not Found"}`) tests := []struct { @@ -261,7 +261,7 @@ func Test_HideAndUnhideComments(t *testing.T) { name: "response without node ID", tool: GranularUnhideIssueComment(translations.NullTranslationHelper), restHandlers: map[string]http.HandlerFunc{ - getIssueCommentRoute: mockResponse(t, http.StatusOK, &github.IssueComment{ID: github.Ptr(int64(1))}), + getIssueCommentRoute: mockResponse(t, http.StatusOK, &github.IssueComment{ID: new(int64(1))}), }, requestArgs: map[string]any{"owner": "owner", "repo": "repo", "comment_id": float64(1)}, expectedErrMsg: "response has no node ID", diff --git a/pkg/github/context_tools_test.go b/pkg/github/context_tools_test.go index 5e8b94f6cb..a3d09ed652 100644 --- a/pkg/github/context_tools_test.go +++ b/pkg/github/context_tools_test.go @@ -30,19 +30,19 @@ func Test_GetMe(t *testing.T) { // Setup mock user response mockUser := &github.User{ - Login: github.Ptr("testuser"), - Name: github.Ptr("Test User"), - Email: github.Ptr("test@example.com"), - Bio: github.Ptr("GitHub user for testing"), - Company: github.Ptr("Test Company"), - Location: github.Ptr("Test Location"), - HTMLURL: github.Ptr("https://github.com/testuser"), + Login: new("testuser"), + Name: new("Test User"), + Email: new("test@example.com"), + Bio: new("GitHub user for testing"), + Company: new("Test Company"), + Location: new("Test Location"), + HTMLURL: new("https://github.com/testuser"), CreatedAt: &github.Timestamp{Time: time.Now().Add(-365 * 24 * time.Hour)}, - Type: github.Ptr("User"), - Hireable: github.Ptr(true), - TwitterUsername: github.Ptr("testuser_twitter"), + Type: new("User"), + Hireable: new(true), + TwitterUsername: new("testuser_twitter"), Plan: &github.Plan{ - Name: github.Ptr("pro"), + Name: new("pro"), }, } @@ -144,8 +144,8 @@ func Test_GetMe_OmittedArguments(t *testing.T) { t.Parallel() mockUser := &github.User{ - Login: github.Ptr("testuser"), - HTMLURL: github.Ptr("https://github.com/testuser"), + Login: new("testuser"), + HTMLURL: new("https://github.com/testuser"), CreatedAt: &github.Timestamp{Time: time.Now()}, } mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ @@ -175,8 +175,8 @@ func Test_GetMe_IFC_FeatureFlag(t *testing.T) { serverTool := GetMe(translations.NullTranslationHelper) mockUser := &github.User{ - Login: github.Ptr("testuser"), - HTMLURL: github.Ptr("https://github.com/testuser"), + Login: new("testuser"), + HTMLURL: new("https://github.com/testuser"), CreatedAt: &github.Timestamp{Time: time.Now()}, } mockedHTTPClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ @@ -246,19 +246,19 @@ func Test_GetTeams(t *testing.T) { assert.True(t, tool.Annotations.ReadOnlyHint, "get_teams tool should be read-only") mockUser := &github.User{ - Login: github.Ptr("testuser"), - Name: github.Ptr("Test User"), - Email: github.Ptr("test@example.com"), - Bio: github.Ptr("GitHub user for testing"), - Company: github.Ptr("Test Company"), - Location: github.Ptr("Test Location"), - HTMLURL: github.Ptr("https://github.com/testuser"), + Login: new("testuser"), + Name: new("Test User"), + Email: new("test@example.com"), + Bio: new("GitHub user for testing"), + Company: new("Test Company"), + Location: new("Test Location"), + HTMLURL: new("https://github.com/testuser"), CreatedAt: &github.Timestamp{Time: time.Now().Add(-365 * 24 * time.Hour)}, - Type: github.Ptr("User"), - Hireable: github.Ptr(true), - TwitterUsername: github.Ptr("testuser_twitter"), + Type: new("User"), + Hireable: new(true), + TwitterUsername: new("testuser_twitter"), Plan: &github.Plan{ - Name: github.Ptr("pro"), + Name: new("pro"), }, } diff --git a/pkg/github/copilot_test.go b/pkg/github/copilot_test.go index fbac06d884..8bb0722141 100644 --- a/pkg/github/copilot_test.go +++ b/pkg/github/copilot_test.go @@ -900,20 +900,20 @@ func Test_RequestCopilotReview(t *testing.T) { // Setup mock PR for success case mockPR := &github.PullRequest{ - Number: github.Ptr(42), - Title: github.Ptr("Test PR"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42"), + Number: new(42), + Title: new("Test PR"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/pull/42"), Head: &github.PullRequestBranch{ - SHA: github.Ptr("abcd1234"), - Ref: github.Ptr("feature-branch"), + SHA: new("abcd1234"), + Ref: new("feature-branch"), }, Base: &github.PullRequestBranch{ - Ref: github.Ptr("main"), + Ref: new("main"), }, - Body: github.Ptr("This is a test PR"), + Body: new("This is a test PR"), User: &github.User{ - Login: github.Ptr("testuser"), + Login: new("testuser"), }, } @@ -965,10 +965,10 @@ func Test_RequestCopilotReview(t *testing.T) { mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ PostReposPullsRequestedReviewersByOwnerByRepoByPullNumber: mockResponse(t, http.StatusNotFound, map[string]any{"message": "Not Found"}), GetReposByOwnerByRepo: mockResponse(t, http.StatusOK, &github.Repository{ - Name: github.Ptr("repo"), + Name: new("repo"), Permissions: &github.RepositoryPermissions{ - Pull: github.Ptr(true), - Push: github.Ptr(false), + Pull: new(true), + Push: new(false), }, }), }), @@ -985,10 +985,10 @@ func Test_RequestCopilotReview(t *testing.T) { mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ PostReposPullsRequestedReviewersByOwnerByRepoByPullNumber: mockResponse(t, http.StatusForbidden, map[string]any{"message": "Forbidden"}), GetReposByOwnerByRepo: mockResponse(t, http.StatusOK, &github.Repository{ - Name: github.Ptr("repo"), + Name: new("repo"), Permissions: &github.RepositoryPermissions{ - Pull: github.Ptr(true), - Push: github.Ptr(false), + Pull: new(true), + Push: new(false), }, }), }), @@ -1005,10 +1005,10 @@ func Test_RequestCopilotReview(t *testing.T) { mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ PostReposPullsRequestedReviewersByOwnerByRepoByPullNumber: mockResponse(t, http.StatusNotFound, map[string]any{"message": "Not Found"}), GetReposByOwnerByRepo: mockResponse(t, http.StatusOK, &github.Repository{ - Name: github.Ptr("repo"), + Name: new("repo"), Permissions: &github.RepositoryPermissions{ - Pull: github.Ptr(true), - Push: github.Ptr(true), + Pull: new(true), + Push: new(true), }, }), }), diff --git a/pkg/github/custom_properties_test.go b/pkg/github/custom_properties_test.go index 18f165ca72..394e183d9b 100644 --- a/pkg/github/custom_properties_test.go +++ b/pkg/github/custom_properties_test.go @@ -96,7 +96,7 @@ func Test_CustomPropertiesRead(t *testing.T) { }) t.Run("enterprise level: returns property definitions", func(t *testing.T) { - mockProps := []*github.CustomProperty{{PropertyName: github.Ptr("compliance"), ValueType: "true_false"}} + mockProps := []*github.CustomProperty{{PropertyName: new("compliance"), ValueType: "true_false"}} client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ "GET /enterprises/{enterprise}/properties/schema": mockResponse(t, http.StatusOK, mockProps), })) diff --git a/pkg/github/dependabot_test.go b/pkg/github/dependabot_test.go index 8aecc4e19f..a699980778 100644 --- a/pkg/github/dependabot_test.go +++ b/pkg/github/dependabot_test.go @@ -26,9 +26,9 @@ func Test_GetDependabotAlert(t *testing.T) { // Setup mock alert for success case mockAlert := &github.DependabotAlert{ - Number: github.Ptr(42), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/security/dependabot/42"), + Number: new(42), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/security/dependabot/42"), } tests := []struct { @@ -137,19 +137,19 @@ func Test_ListDependabotAlerts(t *testing.T) { // Setup mock alerts for success case criticalAlert := github.DependabotAlert{ - Number: github.Ptr(1), - HTMLURL: github.Ptr("https://github.com/owner/repo/security/dependabot/1"), - State: github.Ptr("open"), + Number: new(1), + HTMLURL: new("https://github.com/owner/repo/security/dependabot/1"), + State: new("open"), SecurityAdvisory: &github.DependabotSecurityAdvisory{ - Severity: github.Ptr("critical"), + Severity: new("critical"), }, } highSeverityAlert := github.DependabotAlert{ - Number: github.Ptr(2), - HTMLURL: github.Ptr("https://github.com/owner/repo/security/dependabot/2"), - State: github.Ptr("fixed"), + Number: new(2), + HTMLURL: new("https://github.com/owner/repo/security/dependabot/2"), + State: new("fixed"), SecurityAdvisory: &github.DependabotSecurityAdvisory{ - Severity: github.Ptr("high"), + Severity: new("high"), }, } diff --git a/pkg/github/discussions.go b/pkg/github/discussions.go index 890878e42d..a650a76d14 100644 --- a/pkg/github/discussions.go +++ b/pkg/github/discussions.go @@ -99,16 +99,16 @@ type WithCategoryNoOrder struct { func fragmentToDiscussion(fragment NodeFragment) *github.Discussion { return &github.Discussion{ - Number: github.Ptr(int(fragment.Number)), - Title: github.Ptr(sanitize.PlainText(string(fragment.Title))), - HTMLURL: github.Ptr(string(fragment.URL)), + Number: new(int(fragment.Number)), + Title: new(sanitize.PlainText(string(fragment.Title))), + HTMLURL: new(string(fragment.URL)), CreatedAt: &github.Timestamp{Time: fragment.CreatedAt.Time}, UpdatedAt: &github.Timestamp{Time: fragment.UpdatedAt.Time}, User: &github.User{ - Login: github.Ptr(string(fragment.Author.Login)), + Login: new(string(fragment.Author.Login)), }, DiscussionCategory: &github.DiscussionCategory{ - Name: github.Ptr(string(fragment.Category.Name)), + Name: new(string(fragment.Category.Name)), }, } } diff --git a/pkg/github/fields_filtering_test.go b/pkg/github/fields_filtering_test.go index bb82d6abb8..cc3e684013 100644 --- a/pkg/github/fields_filtering_test.go +++ b/pkg/github/fields_filtering_test.go @@ -20,16 +20,16 @@ import ( func mockListCommits() []*github.RepositoryCommit { return []*github.RepositoryCommit{ { - SHA: github.Ptr("abc123def456"), - HTMLURL: github.Ptr("https://github.com/owner/repo/commit/abc123def456"), + SHA: new("abc123def456"), + HTMLURL: new("https://github.com/owner/repo/commit/abc123def456"), Commit: &github.Commit{ - Message: github.Ptr("First commit with a reasonably long message to add bytes"), + Message: new("First commit with a reasonably long message to add bytes"), Author: &github.CommitAuthor{ - Name: github.Ptr("Test User"), - Email: github.Ptr("test@example.com"), + Name: new("Test User"), + Email: new("test@example.com"), }, }, - Author: &github.User{Login: github.Ptr("testuser")}, + Author: &github.User{Login: new("testuser")}, }, } } @@ -79,8 +79,8 @@ func mockListReleases() []*github.RepositoryRelease { { ID: 1, TagName: "v1.0.0", - Name: github.Ptr("First Release"), - Body: github.Ptr("Release notes with a reasonably long body to add bytes"), + Name: new("First Release"), + Body: new("Release notes with a reasonably long body to add bytes"), HTMLURL: "https://github.com/owner/repo/releases/tag/v1.0.0", }, } @@ -130,12 +130,12 @@ func Test_ListReleases_FieldsTelemetry(t *testing.T) { func mockListPullRequests() []*github.PullRequest { return []*github.PullRequest{ { - Number: github.Ptr(42), - Title: github.Ptr("First PR"), - Body: github.Ptr("PR body with a reasonably long description to add bytes"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42"), - User: &github.User{Login: github.Ptr("user1")}, + Number: new(42), + Title: new("First PR"), + Body: new("PR body with a reasonably long description to add bytes"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/pull/42"), + User: &github.User{Login: new("user1")}, }, } } @@ -187,16 +187,16 @@ func Test_ListPullRequests_FieldsTelemetry(t *testing.T) { // does not attempt the follow-up GraphQL field-values enrichment. func mockIssueSearchResult() *github.IssuesSearchResult { return &github.IssuesSearchResult{ - Total: github.Ptr(1), - IncompleteResults: github.Ptr(false), + Total: new(1), + IncompleteResults: new(false), Issues: []*github.Issue{ { - Number: github.Ptr(42), - Title: github.Ptr("A result"), - Body: github.Ptr("Body with a reasonably long description to add bytes"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42"), - User: &github.User{Login: github.Ptr("user1")}, + Number: new(42), + Title: new("A result"), + Body: new("Body with a reasonably long description to add bytes"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/pull/42"), + User: &github.User{Login: new("user1")}, }, }, } diff --git a/pkg/github/gists.go b/pkg/github/gists.go index 8ad6fda296..44baba5c6d 100644 --- a/pkg/github/gists.go +++ b/pkg/github/gists.go @@ -231,8 +231,8 @@ func CreateGist(t translations.TranslationHelperFunc) inventory.ServerTool { gist := github.CreateGistRequest{ Files: files, - Public: github.Ptr(public), - Description: github.Ptr(description), + Public: new(public), + Description: new(description), } client, err := deps.GetClient(ctx) @@ -327,8 +327,8 @@ func UpdateGist(t translations.TranslationHelperFunc) inventory.ServerTool { files := make(map[github.GistFilename]*github.UpdateGistFile) files[github.GistFilename(filename)] = &github.UpdateGistFile{ - Filename: github.Ptr(filename), - Content: github.Ptr(content), + Filename: new(filename), + Content: new(content), } // Only set Description when the caller actually provided it, so @@ -336,7 +336,7 @@ func UpdateGist(t translations.TranslationHelperFunc) inventory.ServerTool { // explicit empty string still clears it. var descriptionPtr *string if _, ok := args["description"]; ok { - descriptionPtr = github.Ptr(description) + descriptionPtr = new(description) } gist := github.UpdateGistRequest{ diff --git a/pkg/github/gists_test.go b/pkg/github/gists_test.go index 2e5214a556..9b3f72ba5a 100644 --- a/pkg/github/gists_test.go +++ b/pkg/github/gists_test.go @@ -37,30 +37,30 @@ func Test_ListGists(t *testing.T) { // Setup mock gists for success case mockGists := []*github.Gist{ { - ID: github.Ptr("gist1"), - Description: github.Ptr("First Gist"), - HTMLURL: github.Ptr("https://gist.github.com/user/gist1"), - Public: github.Ptr(true), + ID: new("gist1"), + Description: new("First Gist"), + HTMLURL: new("https://gist.github.com/user/gist1"), + Public: new(true), CreatedAt: &github.Timestamp{Time: time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)}, - Owner: &github.User{Login: github.Ptr("user")}, + Owner: &github.User{Login: new("user")}, Files: map[github.GistFilename]github.GistFile{ "file1.txt": { - Filename: github.Ptr("file1.txt"), - Content: github.Ptr("content of file 1"), + Filename: new("file1.txt"), + Content: new("content of file 1"), }, }, }, { - ID: github.Ptr("gist2"), - Description: github.Ptr("Second Gist"), - HTMLURL: github.Ptr("https://gist.github.com/testuser/gist2"), - Public: github.Ptr(false), + ID: new("gist2"), + Description: new("Second Gist"), + HTMLURL: new("https://gist.github.com/testuser/gist2"), + Public: new(false), CreatedAt: &github.Timestamp{Time: time.Date(2023, 2, 1, 0, 0, 0, 0, time.UTC)}, - Owner: &github.User{Login: github.Ptr("testuser")}, + Owner: &github.User{Login: new("testuser")}, Files: map[github.GistFilename]github.GistFile{ "file2.js": { - Filename: github.Ptr("file2.js"), - Content: github.Ptr("console.log('hello');"), + Filename: new("file2.js"), + Content: new("console.log('hello');"), }, }, }, @@ -202,16 +202,16 @@ func Test_GetGist(t *testing.T) { // Setup mock gist for success case mockGist := github.Gist{ - ID: github.Ptr("gist1"), - Description: github.Ptr("First Gist"), - HTMLURL: github.Ptr("https://gist.github.com/user/gist1"), - Public: github.Ptr(true), + ID: new("gist1"), + Description: new("First Gist"), + HTMLURL: new("https://gist.github.com/user/gist1"), + Public: new(true), CreatedAt: &github.Timestamp{Time: time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)}, - Owner: &github.User{Login: github.Ptr("user")}, + Owner: &github.User{Login: new("user")}, Files: map[github.GistFilename]github.GistFile{ github.GistFilename("file1.txt"): { - Filename: github.Ptr("file1.txt"), - Content: github.Ptr("content of file 1"), + Filename: new("file1.txt"), + Content: new("content of file 1"), }, }, } @@ -315,16 +315,16 @@ func Test_CreateGist(t *testing.T) { // Setup mock data for test cases createdGist := &github.Gist{ - ID: github.Ptr("new-gist-id"), - Description: github.Ptr("Test Gist"), - HTMLURL: github.Ptr("https://gist.github.com/user/new-gist-id"), - Public: github.Ptr(false), + ID: new("new-gist-id"), + Description: new("Test Gist"), + HTMLURL: new("https://gist.github.com/user/new-gist-id"), + Public: new(false), CreatedAt: &github.Timestamp{Time: time.Now()}, - Owner: &github.User{Login: github.Ptr("user")}, + Owner: &github.User{Login: new("user")}, Files: map[github.GistFilename]github.GistFile{ "test.go": { - Filename: github.Ptr("test.go"), - Content: github.Ptr("package main\n\nfunc main() {\n\tfmt.Println(\"Hello, Gist!\")\n}"), + Filename: new("test.go"), + Content: new("package main\n\nfunc main() {\n\tfmt.Println(\"Hello, Gist!\")\n}"), }, }, } @@ -454,16 +454,16 @@ func Test_UpdateGist(t *testing.T) { // Setup mock data for test cases updatedGist := &github.Gist{ - ID: github.Ptr("existing-gist-id"), - Description: github.Ptr("Updated Test Gist"), - HTMLURL: github.Ptr("https://gist.github.com/user/existing-gist-id"), - Public: github.Ptr(true), + ID: new("existing-gist-id"), + Description: new("Updated Test Gist"), + HTMLURL: new("https://gist.github.com/user/existing-gist-id"), + Public: new(true), UpdatedAt: &github.Timestamp{Time: time.Now()}, - Owner: &github.User{Login: github.Ptr("user")}, + Owner: &github.User{Login: new("user")}, Files: map[github.GistFilename]github.GistFile{ "updated.go": { - Filename: github.Ptr("updated.go"), - Content: github.Ptr("package main\n\nfunc main() {\n\tfmt.Println(\"Updated Gist!\")\n}"), + Filename: new("updated.go"), + Content: new("package main\n\nfunc main() {\n\tfmt.Println(\"Updated Gist!\")\n}"), }, }, } @@ -586,8 +586,8 @@ func Test_UpdateGist_DescriptionOnlySentWhenProvided(t *testing.T) { serverTool := UpdateGist(translations.NullTranslationHelper) updatedGist := &github.Gist{ - ID: github.Ptr("existing-gist-id"), - HTMLURL: github.Ptr("https://gist.github.com/user/existing-gist-id"), + ID: new("existing-gist-id"), + HTMLURL: new("https://gist.github.com/user/existing-gist-id"), } cases := []struct { diff --git a/pkg/github/git_test.go b/pkg/github/git_test.go index 09c82a8703..e914617642 100644 --- a/pkg/github/git_test.go +++ b/pkg/github/git_test.go @@ -35,27 +35,27 @@ func Test_GetRepositoryTree(t *testing.T) { // Setup mock data mockRepo := &github.Repository{ - DefaultBranch: github.Ptr("main"), + DefaultBranch: new("main"), } mockTree := &github.Tree{ - SHA: github.Ptr("abc123"), - Truncated: github.Ptr(false), + SHA: new("abc123"), + Truncated: new(false), Entries: []*github.TreeEntry{ { - Path: github.Ptr("README.md"), - Mode: github.Ptr("100644"), - Type: github.Ptr("blob"), - SHA: github.Ptr("file1sha"), - Size: github.Ptr(123), - URL: github.Ptr("https://api.github.com/repos/owner/repo/git/blobs/file1sha"), + Path: new("README.md"), + Mode: new("100644"), + Type: new("blob"), + SHA: new("file1sha"), + Size: new(123), + URL: new("https://api.github.com/repos/owner/repo/git/blobs/file1sha"), }, { - Path: github.Ptr("src/main.go"), - Mode: github.Ptr("100644"), - Type: github.Ptr("blob"), - SHA: github.Ptr("file2sha"), - Size: github.Ptr(456), - URL: github.Ptr("https://api.github.com/repos/owner/repo/git/blobs/file2sha"), + Path: new("src/main.go"), + Mode: new("100644"), + Type: new("blob"), + SHA: new("file2sha"), + Size: new(456), + URL: new("https://api.github.com/repos/owner/repo/git/blobs/file2sha"), }, }, } diff --git a/pkg/github/granular_tools_test.go b/pkg/github/granular_tools_test.go index 07e5871f8a..ebe7249cd4 100644 --- a/pkg/github/granular_tools_test.go +++ b/pkg/github/granular_tools_test.go @@ -172,9 +172,9 @@ func TestPullRequestsGranularToolset(t *testing.T) { func TestGranularCreateIssue(t *testing.T) { mockIssue := &gogithub.Issue{ - Number: gogithub.Ptr(1), - Title: gogithub.Ptr("Test Issue"), - Body: gogithub.Ptr("Test body"), + Number: new(1), + Title: new("Test Issue"), + Body: new("Test body"), } tests := []struct { @@ -233,8 +233,8 @@ func TestGranularCreateIssue(t *testing.T) { func TestGranularUpdateIssueTitle(t *testing.T) { client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ PatchReposIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, &gogithub.Issue{ - Number: gogithub.Ptr(42), - Title: gogithub.Ptr("New Title"), + Number: new(42), + Title: new("New Title"), }), })) deps := BaseDeps{Client: client} @@ -257,8 +257,8 @@ func TestGranularUpdateIssueBody(t *testing.T) { PatchReposIssuesByOwnerByRepoByIssueNumber: expectRequestBody(t, map[string]any{ "body": "Updated body", }).andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{ - Number: gogithub.Ptr(1), - Body: gogithub.Ptr("Updated body"), + Number: new(1), + Body: new("Updated body"), })), })) deps := BaseDeps{Client: client} @@ -280,7 +280,7 @@ func TestGranularUpdateIssueAssignees(t *testing.T) { client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ PatchReposIssuesByOwnerByRepoByIssueNumber: expectRequestBody(t, map[string]any{ "assignees": []any{"user1", "user2"}, - }).andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: gogithub.Ptr(1)})), + }).andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: new(1)})), })) deps := BaseDeps{Client: client} serverTool := GranularUpdateIssueAssignees(translations.NullTranslationHelper) @@ -374,7 +374,7 @@ func TestGranularUpdateIssueAssigneesObjectForm(t *testing.T) { t.Run(tc.name, func(t *testing.T) { client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ PatchReposIssuesByOwnerByRepoByIssueNumber: expectRequestBody(t, tc.expectedReq). - andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: gogithub.Ptr(1)})), + andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: new(1)})), })) deps := BaseDeps{Client: client} serverTool := GranularUpdateIssueAssignees(translations.NullTranslationHelper) @@ -517,7 +517,7 @@ func TestGranularUpdateIssueLabels(t *testing.T) { t.Run(tc.name, func(t *testing.T) { client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ PatchReposIssuesByOwnerByRepoByIssueNumber: expectRequestBody(t, tc.expectedReq). - andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: gogithub.Ptr(1)})), + andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: new(1)})), })) deps := BaseDeps{Client: client} serverTool := GranularUpdateIssueLabels(translations.NullTranslationHelper) @@ -595,7 +595,7 @@ func TestGranularUpdateIssueLabelsSuggest(t *testing.T) { t.Run(tc.name, func(t *testing.T) { client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ PatchReposIssuesByOwnerByRepoByIssueNumber: expectRequestBody(t, tc.expectedReq). - andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: gogithub.Ptr(1)})), + andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: new(1)})), })) deps := BaseDeps{Client: client} serverTool := GranularUpdateIssueLabels(translations.NullTranslationHelper) @@ -744,7 +744,7 @@ func TestGranularUpdateIssueLabelsConfidence(t *testing.T) { client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ PatchReposIssuesByOwnerByRepoByIssueNumber: expectRequestBody(t, tc.expectedReq). - andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: gogithub.Ptr(1)})), + andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: new(1)})), })) deps := BaseDeps{Client: client} serverTool := GranularUpdateIssueLabels(translations.NullTranslationHelper) @@ -762,7 +762,7 @@ func TestGranularUpdateIssueMilestone(t *testing.T) { client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ PatchReposIssuesByOwnerByRepoByIssueNumber: expectRequestBody(t, map[string]any{ "milestone": float64(5), - }).andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: gogithub.Ptr(1)})), + }).andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: new(1)})), })) deps := BaseDeps{Client: client} serverTool := GranularUpdateIssueMilestone(translations.NullTranslationHelper) @@ -831,7 +831,7 @@ func TestGranularUpdateIssueType(t *testing.T) { t.Run(tc.name, func(t *testing.T) { client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ PatchReposIssuesByOwnerByRepoByIssueNumber: expectRequestBody(t, tc.expectedReq). - andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: gogithub.Ptr(1)})), + andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: new(1)})), })) deps := BaseDeps{Client: client} serverTool := GranularUpdateIssueType(translations.NullTranslationHelper) @@ -930,7 +930,7 @@ func TestGranularUpdateIssueTypeSuggest(t *testing.T) { t.Run(tc.name, func(t *testing.T) { client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ PatchReposIssuesByOwnerByRepoByIssueNumber: expectRequestBody(t, tc.expectedReq). - andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: gogithub.Ptr(1)})), + andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: new(1)})), })) deps := BaseDeps{Client: client} serverTool := GranularUpdateIssueType(translations.NullTranslationHelper) @@ -1068,7 +1068,7 @@ func TestGranularUpdateIssueTypeConfidence(t *testing.T) { t.Run(tc.name, func(t *testing.T) { client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ PatchReposIssuesByOwnerByRepoByIssueNumber: expectRequestBody(t, tc.expectedReq). - andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: gogithub.Ptr(1)})), + andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: new(1)})), })) deps := BaseDeps{Client: client} serverTool := GranularUpdateIssueType(translations.NullTranslationHelper) @@ -1167,8 +1167,8 @@ func TestGranularUpdateIssueState(t *testing.T) { client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ PatchReposIssuesByOwnerByRepoByIssueNumber: expectRequestBody(t, tc.expectedReq). andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{ - Number: gogithub.Ptr(1), - State: gogithub.Ptr(tc.requestArgs["state"].(string)), + Number: new(1), + State: new(tc.requestArgs["state"].(string)), })), })) deps := BaseDeps{Client: client} @@ -1249,7 +1249,7 @@ func TestGranularUpdateIssueStateSuggest(t *testing.T) { t.Run(tc.name, func(t *testing.T) { client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ PatchReposIssuesByOwnerByRepoByIssueNumber: expectRequestBody(t, tc.expectedReq). - andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: gogithub.Ptr(1)})), + andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: new(1)})), })) deps := BaseDeps{Client: client} serverTool := GranularUpdateIssueState(translations.NullTranslationHelper) @@ -1313,11 +1313,11 @@ func TestGranularUpdateIssueStateDuplicate(t *testing.T) { t.Run(tc.name, func(t *testing.T) { client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, &gogithub.Issue{ - ID: gogithub.Ptr(duplicateIssueID), - Number: gogithub.Ptr(42), + ID: new(duplicateIssueID), + Number: new(42), }), PatchReposIssuesByOwnerByRepoByIssueNumber: expectRequestBody(t, tc.expectedReq). - andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: gogithub.Ptr(1)})), + andThen(mockResponse(t, http.StatusOK, &gogithub.Issue{Number: new(1)})), })) deps := BaseDeps{Client: client} serverTool := GranularUpdateIssueState(translations.NullTranslationHelper) @@ -1428,8 +1428,8 @@ func TestGranularUpdatePullRequestTitle(t *testing.T) { PatchReposPullsByOwnerByRepoByPullNumber: expectRequestBody(t, map[string]any{ "title": "New PR Title", }).andThen(mockResponse(t, http.StatusOK, &gogithub.PullRequest{ - Number: gogithub.Ptr(1), - Title: gogithub.Ptr("New PR Title"), + Number: new(1), + Title: new("New PR Title"), })), })) deps := BaseDeps{Client: client} @@ -1452,8 +1452,8 @@ func TestGranularUpdatePullRequestBody(t *testing.T) { PatchReposPullsByOwnerByRepoByPullNumber: expectRequestBody(t, map[string]any{ "body": "Updated description", }).andThen(mockResponse(t, http.StatusOK, &gogithub.PullRequest{ - Number: gogithub.Ptr(1), - Body: gogithub.Ptr("Updated description"), + Number: new(1), + Body: new("Updated description"), })), })) deps := BaseDeps{Client: client} @@ -1476,8 +1476,8 @@ func TestGranularUpdatePullRequestState(t *testing.T) { PatchReposPullsByOwnerByRepoByPullNumber: expectRequestBody(t, map[string]any{ "state": "closed", }).andThen(mockResponse(t, http.StatusOK, &gogithub.PullRequest{ - Number: gogithub.Ptr(1), - State: gogithub.Ptr("closed"), + Number: new(1), + State: new("closed"), })), })) deps := BaseDeps{Client: client} @@ -1500,7 +1500,7 @@ func TestGranularRequestPullRequestReviewers(t *testing.T) { PostReposPullsRequestedReviewersByOwnerByRepoByPullNumber: expectRequestBody(t, map[string]any{ "reviewers": []any{"user1"}, "team_reviewers": []any{"team1"}, - }).andThen(mockResponse(t, http.StatusOK, &gogithub.PullRequest{Number: gogithub.Ptr(1)})), + }).andThen(mockResponse(t, http.StatusOK, &gogithub.PullRequest{Number: new(1)})), })) deps := BaseDeps{Client: client} serverTool := GranularRequestPullRequestReviewers(translations.NullTranslationHelper) @@ -1728,10 +1728,10 @@ func TestGranularResolveReviewThread(t *testing.T) { { name: "enabled variant forwards resolution reason", withResolutionReason: true, - resolutionReason: gogithub.Ptr("addressed"), - expectedReason: gogithub.Ptr("addressed"), + resolutionReason: new("addressed"), + expectedReason: new("addressed"), }, - {name: "default variant omits resolution reason", resolutionReason: gogithub.Ptr("addressed")}, + {name: "default variant omits resolution reason", resolutionReason: new("addressed")}, {name: "without resolution reason"}, } @@ -2388,8 +2388,8 @@ func TestGranularSetIssueFields(t *testing.T) { func TestGranularAddIssueReaction(t *testing.T) { mockReaction := &gogithub.Reaction{ - ID: gogithub.Ptr(int64(12345)), - Content: gogithub.Ptr("+1"), + ID: new(int64(12345)), + Content: new("+1"), } tests := []struct { @@ -2524,8 +2524,8 @@ func TestGranularRemoveIssueReaction(t *testing.T) { func TestGranularAddIssueCommentReaction(t *testing.T) { mockReaction := &gogithub.Reaction{ - ID: gogithub.Ptr(int64(67890)), - Content: gogithub.Ptr("heart"), + ID: new(int64(67890)), + Content: new("heart"), } tests := []struct { @@ -2650,8 +2650,8 @@ func TestGranularRemoveIssueCommentReaction(t *testing.T) { func TestGranularAddPullRequestReviewCommentReaction(t *testing.T) { mockReaction := &gogithub.Reaction{ - ID: gogithub.Ptr(int64(54321)), - Content: gogithub.Ptr("rocket"), + ID: new(int64(54321)), + Content: new("rocket"), } tests := []struct { diff --git a/pkg/github/issues.go b/pkg/github/issues.go index 2ee131733d..80483d7905 100644 --- a/pkg/github/issues.go +++ b/pkg/github/issues.go @@ -1780,7 +1780,7 @@ func SubIssueWrite(t translations.TranslationHelperFunc) inventory.ServerTool { func AddSubIssue(ctx context.Context, client *github.Client, owner string, repo string, issueNumber int, subIssueID int, replaceParent bool) (*mcp.CallToolResult, error) { subIssueRequest := github.SubIssueRequest{ SubIssueID: int64(subIssueID), - ReplaceParent: github.Ptr(replaceParent), + ReplaceParent: new(replaceParent), } subIssue, resp, err := client.SubIssue.Add(ctx, owner, repo, int64(issueNumber), subIssueRequest) @@ -2092,10 +2092,10 @@ func sanitizeIssueTitleAndBody(issue *github.Issue) { return } if issue.Title != nil { - issue.Title = github.Ptr(sanitize.PlainText(*issue.Title)) + issue.Title = new(sanitize.PlainText(*issue.Title)) } if issue.Body != nil { - issue.Body = github.Ptr(sanitize.Content(*issue.Body)) + issue.Body = new(sanitize.Content(*issue.Body)) } } @@ -2104,10 +2104,10 @@ func sanitizeSubIssueTitleAndBody(issue *github.SubIssue) { return } if issue.Title != nil { - issue.Title = github.Ptr(sanitize.PlainText(*issue.Title)) + issue.Title = new(sanitize.PlainText(*issue.Title)) } if issue.Body != nil { - issue.Body = github.Ptr(sanitize.Content(*issue.Body)) + issue.Body = new(sanitize.Content(*issue.Body)) } } @@ -3102,7 +3102,7 @@ func CreateIssue(ctx context.Context, client *github.Client, owner string, repo // Create the issue request issueRequest := github.CreateIssueRequest{ Title: title, - Body: github.Ptr(body), + Body: new(body), Assignees: assignees, Labels: labels, IssueFieldValues: issueFieldValues, @@ -3113,7 +3113,7 @@ func CreateIssue(ctx context.Context, client *github.Client, owner string, repo } if issueType != "" { - issueRequest.Type = github.Ptr(issueType) + issueRequest.Type = new(issueType) } issue, resp, err := client.Issues.Create(ctx, owner, repo, issueRequest) @@ -3185,11 +3185,11 @@ func UpdateIssue(ctx context.Context, client *github.Client, gqlClient *githubv4 // Set optional parameters if provided if title != "" { - issueRequest.Title = github.Ptr(title) + issueRequest.Title = new(title) } if body != "" { - issueRequest.Body = github.Ptr(body) + issueRequest.Body = new(body) } if updateOptions.LabelsProvided { @@ -3205,7 +3205,7 @@ func UpdateIssue(ctx context.Context, client *github.Client, gqlClient *githubv4 } if issueType != "" { - issueRequest.Type = github.Ptr(issueType) + issueRequest.Type = new(issueType) } // Field IDs to clear via DELETE after the PATCH. See the post-PATCH loop diff --git a/pkg/github/issues_delete_test.go b/pkg/github/issues_delete_test.go index d370beb92d..d514d31084 100644 --- a/pkg/github/issues_delete_test.go +++ b/pkg/github/issues_delete_test.go @@ -24,7 +24,7 @@ func Test_IssueRequest_EmptyFieldValues_OmittedByJSON(t *testing.T) { t.Parallel() req := &gogithub.UpdateIssueRequest{ - Title: gogithub.Ptr("still here"), + Title: new("still here"), IssueFieldValues: []*gogithub.IssueRequestFieldValue{}, } body, err := json.Marshal(req) @@ -44,10 +44,10 @@ func Test_UpdateIssue_DeleteLastFieldValueCallsDeleteEndpoint(t *testing.T) { t.Parallel() mockIssue := &gogithub.Issue{ - Number: gogithub.Ptr(42), - Title: gogithub.Ptr("Test issue"), - State: gogithub.Ptr("open"), - HTMLURL: gogithub.Ptr("https://github.com/owner/repo/issues/42"), + Number: new(42), + Title: new("Test issue"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/42"), } var ( @@ -145,10 +145,10 @@ func Test_UpdateIssue_DeleteOneOfManyUsesSetSemantics(t *testing.T) { t.Parallel() mockIssue := &gogithub.Issue{ - Number: gogithub.Ptr(42), - Title: gogithub.Ptr("Test issue"), - State: gogithub.Ptr("open"), - HTMLURL: gogithub.Ptr("https://github.com/owner/repo/issues/42"), + Number: new(42), + Title: new("Test issue"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/42"), } var ( @@ -242,10 +242,10 @@ func Test_UpdateIssue_DeleteAbsentFieldIsNoOp(t *testing.T) { t.Parallel() mockIssue := &gogithub.Issue{ - Number: gogithub.Ptr(42), - Title: gogithub.Ptr("Test issue"), - State: gogithub.Ptr("open"), - HTMLURL: gogithub.Ptr("https://github.com/owner/repo/issues/42"), + Number: new(42), + Title: new("Test issue"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/42"), } var ( @@ -334,10 +334,10 @@ func Test_UpdateIssue_DeleteFallbackContinuesOnPartialFailure(t *testing.T) { t.Parallel() mockIssue := &gogithub.Issue{ - Number: gogithub.Ptr(42), - Title: gogithub.Ptr("Test issue"), - State: gogithub.Ptr("open"), - HTMLURL: gogithub.Ptr("https://github.com/owner/repo/issues/42"), + Number: new(42), + Title: new("Test issue"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/42"), } var ( diff --git a/pkg/github/issues_test.go b/pkg/github/issues_test.go index bea82c0c22..f279b41243 100644 --- a/pkg/github/issues_test.go +++ b/pkg/github/issues_test.go @@ -140,38 +140,38 @@ func Test_GetIssue(t *testing.T) { // Setup mock issue for success case mockIssue := &github.Issue{ - Number: github.Ptr(42), - Title: github.Ptr("Test Issue"), - Body: github.Ptr("This is a test issue"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/42"), + Number: new(42), + Title: new("Test Issue"), + Body: new("This is a test issue"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/42"), User: &github.User{ - Login: github.Ptr("testuser"), + Login: new("testuser"), }, Assignees: []*github.User{ - {Login: github.Ptr("octocat")}, - {Login: github.Ptr("mona")}, + {Login: new("octocat")}, + {Login: new("mona")}, }, Repository: &github.Repository{ - Name: github.Ptr("repo"), + Name: new("repo"), Owner: &github.User{ - Login: github.Ptr("owner"), + Login: new("owner"), }, }, } mockIssue2 := &github.Issue{ - Number: github.Ptr(422), - Title: github.Ptr("Test Issue 2"), - Body: github.Ptr("This is a test issue 2"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/42"), + Number: new(422), + Title: new("Test Issue 2"), + Body: new("This is a test issue 2"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/42"), User: &github.User{ - Login: github.Ptr("testuser2"), + Login: new("testuser2"), }, Repository: &github.Repository{ - Name: github.Ptr("repo2"), + Name: new("repo2"), Owner: &github.User{ - Login: github.Ptr("owner2"), + Login: new("owner2"), }, }, } @@ -318,16 +318,16 @@ func Test_IssueRead_IFC_InsidersMode(t *testing.T) { serverTool := IssueRead(translations.NullTranslationHelper) mockIssue := &github.Issue{ - Number: github.Ptr(1), - Title: github.Ptr("Test"), - Body: github.Ptr("body"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/octocat/repo/issues/1"), - User: &github.User{Login: github.Ptr("u")}, + Number: new(1), + Title: new("Test"), + Body: new("body"), + State: new("open"), + HTMLURL: new("https://github.com/octocat/repo/issues/1"), + User: &github.User{Login: new("u")}, } mockComments := []*github.IssueComment{ - {Body: github.Ptr("hello"), User: &github.User{Login: github.Ptr("u")}}, + {Body: new("hello"), User: &github.User{Login: new("u")}}, } makeMockClient := func(isPrivate bool, repoStatus int) *http.Client { @@ -435,13 +435,13 @@ func Test_GetIssue_FieldValues(t *testing.T) { serverTool := IssueRead(translations.NullTranslationHelper) mockIssueWithFields := &github.Issue{ - Number: github.Ptr(99), - Title: github.Ptr("Issue with field values"), - Body: github.Ptr("body"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/99"), + Number: new(99), + Title: new("Issue with field values"), + Body: new("body"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/99"), User: &github.User{ - Login: github.Ptr("testuser"), + Login: new("testuser"), }, IssueFieldValues: []*github.IssueFieldValue{ { @@ -506,14 +506,14 @@ func Test_GetIssue_FieldValues_Enriched(t *testing.T) { serverTool := IssueRead(translations.NullTranslationHelper) mockIssueWithFields := &github.Issue{ - Number: github.Ptr(99), - NodeID: github.Ptr("I_node_99"), - Title: github.Ptr("Issue with field values"), - Body: github.Ptr("body"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/99"), + Number: new(99), + NodeID: new("I_node_99"), + Title: new("Issue with field values"), + Body: new("body"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/99"), User: &github.User{ - Login: github.Ptr("testuser"), + Login: new("testuser"), }, IssueFieldValues: []*github.IssueFieldValue{ { @@ -593,20 +593,20 @@ func Test_GetIssue_FieldValues_Enriched(t *testing.T) { // With no parent and no sub-issues, the routing booleans are explicit false and the // optional relationship payloads are omitted. - assert.Equal(t, github.Ptr(false), returnedIssue.HasParent, "has_parent should be false without a parent") - assert.Equal(t, github.Ptr(false), returnedIssue.HasChildren, "has_children should be false without sub-issues") + assert.Equal(t, new(false), returnedIssue.HasParent, "has_parent should be false without a parent") + assert.Equal(t, new(false), returnedIssue.HasChildren, "has_children should be false without sub-issues") assert.Nil(t, returnedIssue.Parent, "parent should be omitted when there is no parent") assert.Nil(t, returnedIssue.SubIssuesSummary, "sub_issues_summary should be omitted with no sub-issues") } func Test_GetIssue_HierarchyEnrichment(t *testing.T) { mockIssue := &github.Issue{ - Number: github.Ptr(2990), - NodeID: github.Ptr("I_node_2990"), - Title: github.Ptr("Child issue"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/2990"), - User: &github.User{Login: github.Ptr("author")}, + Number: new(2990), + NodeID: new("I_node_2990"), + Title: new("Child issue"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/2990"), + User: &github.User{Login: new("author")}, } parentNode := map[string]any{ @@ -632,8 +632,8 @@ func Test_GetIssue_HierarchyEnrichment(t *testing.T) { parent: parentNode, summary: map[string]any{"total": 4, "completed": 1, "percentCompleted": 25}, assertResponse: func(t *testing.T, issue MinimalIssue) { - assert.Equal(t, github.Ptr(true), issue.HasParent) - assert.Equal(t, github.Ptr(true), issue.HasChildren) + assert.Equal(t, new(true), issue.HasParent) + assert.Equal(t, new(true), issue.HasChildren) require.NotNil(t, issue.Parent) assert.Equal(t, 2820, issue.Parent.Number) assert.Equal(t, "Parent issue", issue.Parent.Title) @@ -650,7 +650,7 @@ func Test_GetIssue_HierarchyEnrichment(t *testing.T) { parent: nil, summary: map[string]any{"total": 0, "completed": 0, "percentCompleted": 0}, assertResponse: func(t *testing.T, issue MinimalIssue) { - assert.Equal(t, github.Ptr(false), issue.HasParent) + assert.Equal(t, new(false), issue.HasParent) assert.Nil(t, issue.Parent) }, }, @@ -659,7 +659,7 @@ func Test_GetIssue_HierarchyEnrichment(t *testing.T) { parent: nil, summary: map[string]any{"total": 0, "completed": 1, "percentCompleted": 0}, assertResponse: func(t *testing.T, issue MinimalIssue) { - assert.Equal(t, github.Ptr(false), issue.HasChildren) + assert.Equal(t, new(false), issue.HasChildren) assert.Nil(t, issue.SubIssuesSummary) }, }, @@ -713,12 +713,12 @@ func Test_GetIssue_HierarchyEnrichment(t *testing.T) { func Test_GetIssue_HierarchyEnrichment_Lockdown(t *testing.T) { mockIssue := &github.Issue{ - Number: github.Ptr(2990), - NodeID: github.Ptr("I_node_2990"), - Title: github.Ptr("Child issue"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/2990"), - User: &github.User{Login: github.Ptr("author")}, + Number: new(2990), + NodeID: new("I_node_2990"), + Title: new("Child issue"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/2990"), + User: &github.User{Login: new("author")}, } parentNode := map[string]any{ @@ -780,17 +780,17 @@ func Test_GetIssue_HierarchyEnrichment_Lockdown(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(getTextResult(t, result).Text), &returnedIssue)) require.Nil(t, returnedIssue.Parent, "parent reference should be omitted under lockdown when it cannot be verified safe") - assert.Equal(t, github.Ptr(true), returnedIssue.HasParent, "has_parent should still be true so agents can route to get_parent") + assert.Equal(t, new(true), returnedIssue.HasParent, "has_parent should still be true so agents can route to get_parent") } func Test_GetIssue_HierarchyEnrichment_QueryFailureReturnsBaseIssue(t *testing.T) { mockIssue := &github.Issue{ - Number: github.Ptr(2990), - NodeID: github.Ptr("I_node_2990"), - Title: github.Ptr("Child issue"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/2990"), - User: &github.User{Login: github.Ptr("author")}, + Number: new(2990), + NodeID: new("I_node_2990"), + Title: new("Child issue"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/2990"), + User: &github.User{Login: new("author")}, } restClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ @@ -833,12 +833,12 @@ func Test_GetIssue_HierarchyEnrichment_QueryFailureReturnsBaseIssue(t *testing.T func Test_GetIssue_ClosedByPullRequests(t *testing.T) { mockIssue := &github.Issue{ - Number: github.Ptr(2990), - NodeID: github.Ptr("I_node_2990"), - Title: github.Ptr("Broken thing"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/2990"), - User: &github.User{Login: github.Ptr("author")}, + Number: new(2990), + NodeID: new("I_node_2990"), + Title: new("Broken thing"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/2990"), + User: &github.User{Login: new("author")}, } tests := []struct { @@ -994,12 +994,12 @@ func closingPullRequestFixtures(n int) []map[string]any { func Test_GetIssue_ClosedByPullRequests_Lockdown(t *testing.T) { mockIssue := &github.Issue{ - Number: github.Ptr(2990), - NodeID: github.Ptr("I_node_2990"), - Title: github.Ptr("Broken thing"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/2990"), - User: &github.User{Login: github.Ptr("author")}, + Number: new(2990), + NodeID: new("I_node_2990"), + Title: new("Broken thing"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/2990"), + User: &github.User{Login: new("author")}, } restClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ @@ -1093,29 +1093,29 @@ func Test_SearchIssues(t *testing.T) { // Setup mock search results mockSearchResult := &github.IssuesSearchResult{ - Total: github.Ptr(2), - IncompleteResults: github.Ptr(false), + Total: new(2), + IncompleteResults: new(false), Issues: []*github.Issue{ { - Number: github.Ptr(42), - Title: github.Ptr("Bug: Something is broken"), - Body: github.Ptr("This is a bug report"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/42"), - Comments: github.Ptr(5), + Number: new(42), + Title: new("Bug: Something is broken"), + Body: new("This is a bug report"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/42"), + Comments: new(5), User: &github.User{ - Login: github.Ptr("user1"), + Login: new("user1"), }, }, { - Number: github.Ptr(43), - Title: github.Ptr("Feature: Add new functionality"), - Body: github.Ptr("This is a feature request"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/43"), - Comments: github.Ptr(3), + Number: new(43), + Title: new("Feature: Add new functionality"), + Body: new("This is a feature request"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/43"), + Comments: new(3), User: &github.User{ - Login: github.Ptr("user2"), + Login: new("user2"), }, }, }, @@ -1439,11 +1439,11 @@ func Test_SearchIssues_IFC_InsidersMode(t *testing.T) { makeIssue := func(owner, repo string, number int) *github.Issue { return &github.Issue{ - Number: github.Ptr(number), - Title: github.Ptr("issue"), - State: github.Ptr("open"), - RepositoryURL: github.Ptr("https://api.github.com/repos/" + owner + "/" + repo), - User: &github.User{Login: github.Ptr("u")}, + Number: new(number), + Title: new("issue"), + State: new("open"), + RepositoryURL: new("https://api.github.com/repos/" + owner + "/" + repo), + User: &github.User{Login: new("u")}, } } @@ -1720,17 +1720,17 @@ func Test_SearchIssues_FieldValuesEnrichment(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockSearchResult := &github.IssuesSearchResult{ - Total: github.Ptr(1), - IncompleteResults: github.Ptr(false), + Total: new(1), + IncompleteResults: new(false), Issues: []*github.Issue{ { - Number: github.Ptr(42), - Title: github.Ptr("Bug: Something is broken"), - Body: github.Ptr("Details"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/42"), - NodeID: github.Ptr("I_node_42"), - User: &github.User{Login: github.Ptr("user1")}, + Number: new(42), + Title: new("Bug: Something is broken"), + Body: new("Details"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/42"), + NodeID: new("I_node_42"), + User: &github.User{Login: new("user1")}, IssueFieldValues: []*github.IssueFieldValue{ {IssueFieldID: 99, DataType: "text", Value: "raw REST value"}, }, @@ -1851,15 +1851,15 @@ func Test_CreateIssue(t *testing.T) { // Setup mock issue for success case mockIssue := &github.Issue{ - Number: github.Ptr(123), - Title: github.Ptr("Test Issue"), - Body: github.Ptr("This is a test issue"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/123"), - Assignees: []*github.User{{Login: github.Ptr("user1")}, {Login: github.Ptr("user2")}}, + Number: new(123), + Title: new("Test Issue"), + Body: new("This is a test issue"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/123"), + Assignees: []*github.User{{Login: new("user1")}, {Login: new("user2")}}, Labels: []*github.Label{{Name: "bug"}, {Name: "help wanted"}}, - Milestone: &github.Milestone{Number: github.Ptr(5)}, - Type: &github.IssueType{Name: github.Ptr("Bug")}, + Milestone: &github.Milestone{Number: new(5)}, + Type: &github.IssueType{Name: new("Bug")}, } tests := []struct { @@ -1903,10 +1903,10 @@ func Test_CreateIssue(t *testing.T) { name: "successful issue creation with minimal fields", mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ PostReposIssuesByOwnerByRepo: mockResponse(t, http.StatusCreated, &github.Issue{ - Number: github.Ptr(124), - Title: github.Ptr("Minimal Issue"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/124"), - State: github.Ptr("open"), + Number: new(124), + Title: new("Minimal Issue"), + HTMLURL: new("https://github.com/owner/repo/issues/124"), + State: new("open"), }), }), requestArgs: map[string]any{ @@ -1918,10 +1918,10 @@ func Test_CreateIssue(t *testing.T) { }, expectError: false, expectedIssue: &github.Issue{ - Number: github.Ptr(124), - Title: github.Ptr("Minimal Issue"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/124"), - State: github.Ptr("open"), + Number: new(124), + Title: new("Minimal Issue"), + HTMLURL: new("https://github.com/owner/repo/issues/124"), + State: new("open"), }, }, { @@ -1936,10 +1936,10 @@ func Test_CreateIssue(t *testing.T) { }, }).andThen( mockResponse(t, http.StatusCreated, &github.Issue{ - Number: github.Ptr(125), - Title: github.Ptr("Issue with fields"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/125"), - State: github.Ptr("open"), + Number: new(125), + Title: new("Issue with fields"), + HTMLURL: new("https://github.com/owner/repo/issues/125"), + State: new("open"), }), ), }), @@ -1987,10 +1987,10 @@ func Test_CreateIssue(t *testing.T) { }, expectError: false, expectedIssue: &github.Issue{ - Number: github.Ptr(125), - Title: github.Ptr("Issue with fields"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/125"), - State: github.Ptr("open"), + Number: new(125), + Title: new("Issue with fields"), + HTMLURL: new("https://github.com/owner/repo/issues/125"), + State: new("open"), }, }, { @@ -2132,9 +2132,9 @@ func TestIssueWriteReportsUnappliedLabels(t *testing.T) { responseLabels = append(responseLabels, &github.Label{Name: label}) } responseIssue := &github.Issue{ - ID: github.Ptr(int64(123)), - Number: github.Ptr(123), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/123"), + ID: new(int64(123)), + Number: new(123), + HTMLURL: new("https://github.com/owner/repo/issues/123"), Labels: responseLabels, } @@ -2202,9 +2202,9 @@ func Test_IssueWrite_MCPAppsFeature_UIGate(t *testing.T) { t.Parallel() mockIssue := &github.Issue{ - Number: github.Ptr(1), - Title: github.Ptr("Test"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/1"), + Number: new(1), + Title: new("Test"), + HTMLURL: new("https://github.com/owner/repo/issues/1"), } serverTool := IssueWrite(translations.NullTranslationHelper) @@ -2568,7 +2568,7 @@ func TestIssueWriteCreateWithParentPreservesSupportedFields(t *testing.T) { ) restHTTPClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ "GET /repos/{owner}/{repo}/issue-types": mockResponse(t, http.StatusOK, []*github.IssueType{ - {Name: github.Ptr("Bug"), NodeID: github.Ptr("ISSUE_TYPE_bug")}, + {Name: new("Bug"), NodeID: new("ISSUE_TYPE_bug")}, }), }) restCounter := &countingRoundTripper{next: restHTTPClient.Transport} @@ -4629,36 +4629,36 @@ func Test_UpdateIssue(t *testing.T) { // Mock issues for reuse across test cases mockBaseIssue := &github.Issue{ - Number: github.Ptr(123), - Title: github.Ptr("Title"), - Body: github.Ptr("Description"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/123"), - Assignees: []*github.User{{Login: github.Ptr("assignee1")}, {Login: github.Ptr("assignee2")}}, + Number: new(123), + Title: new("Title"), + Body: new("Description"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/123"), + Assignees: []*github.User{{Login: new("assignee1")}, {Login: new("assignee2")}}, Labels: []*github.Label{{Name: "bug"}, {Name: "priority"}}, - Milestone: &github.Milestone{Number: github.Ptr(5)}, - Type: &github.IssueType{Name: github.Ptr("Bug")}, + Milestone: &github.Milestone{Number: new(5)}, + Type: &github.IssueType{Name: new("Bug")}, } mockUpdatedIssue := &github.Issue{ - Number: github.Ptr(123), - Title: github.Ptr("Updated Title"), - Body: github.Ptr("Updated Description"), - State: github.Ptr("closed"), - StateReason: github.Ptr("duplicate"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/123"), - Assignees: []*github.User{{Login: github.Ptr("assignee1")}, {Login: github.Ptr("assignee2")}}, + Number: new(123), + Title: new("Updated Title"), + Body: new("Updated Description"), + State: new("closed"), + StateReason: new("duplicate"), + HTMLURL: new("https://github.com/owner/repo/issues/123"), + Assignees: []*github.User{{Login: new("assignee1")}, {Login: new("assignee2")}}, Labels: []*github.Label{{Name: "bug"}, {Name: "priority"}}, - Milestone: &github.Milestone{Number: github.Ptr(5)}, - Type: &github.IssueType{Name: github.Ptr("Bug")}, + Milestone: &github.Milestone{Number: new(5)}, + Type: &github.IssueType{Name: new("Bug")}, } mockReopenedIssue := &github.Issue{ - Number: github.Ptr(123), - Title: github.Ptr("Title"), - State: github.Ptr("open"), - StateReason: github.Ptr("reopened"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/123"), + Number: new(123), + Title: new("Title"), + State: new("open"), + StateReason: new("reopened"), + HTMLURL: new("https://github.com/owner/repo/issues/123"), } // Mock GraphQL responses for reuse across test cases @@ -4745,8 +4745,8 @@ func Test_UpdateIssue(t *testing.T) { "assignees": []any{}, }).andThen( mockResponse(t, http.StatusOK, &github.Issue{ - Number: github.Ptr(123), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/123"), + Number: new(123), + HTMLURL: new("https://github.com/owner/repo/issues/123"), }), ), }), @@ -4761,7 +4761,7 @@ func Test_UpdateIssue(t *testing.T) { }, expectError: false, expectedIssue: &github.Issue{ - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/123"), + HTMLURL: new("https://github.com/owner/repo/issues/123"), }, }, { @@ -5047,15 +5047,15 @@ func Test_UpdateIssue(t *testing.T) { "type": "Bug", }).andThen( mockResponse(t, http.StatusOK, &github.Issue{ - Number: github.Ptr(123), - Title: github.Ptr("Updated Title"), - Body: github.Ptr("Updated Description"), + Number: new(123), + Title: new("Updated Title"), + Body: new("Updated Description"), Labels: []*github.Label{{Name: "bug"}, {Name: "priority"}}, - Assignees: []*github.User{{Login: github.Ptr("assignee1")}, {Login: github.Ptr("assignee2")}}, - Milestone: &github.Milestone{Number: github.Ptr(5)}, - Type: &github.IssueType{Name: github.Ptr("Bug")}, - State: github.Ptr("open"), // Still open after REST update - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/123"), + Assignees: []*github.User{{Login: new("assignee1")}, {Login: new("assignee2")}}, + Milestone: &github.Milestone{Number: new(5)}, + Type: &github.IssueType{Name: new("Bug")}, + State: new("open"), // Still open after REST update + HTMLURL: new("https://github.com/owner/repo/issues/123"), }), ), }), @@ -5214,8 +5214,8 @@ func Test_UpdateIssue(t *testing.T) { func Test_UpdateIssueClearsLabelsAndAssignees(t *testing.T) { serverTool := IssueWrite(translations.NullTranslationHelper) updatedIssue := &github.Issue{ - Number: github.Ptr(8), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/8"), + Number: new(8), + HTMLURL: new("https://github.com/owner/repo/issues/8"), } client := mustNewGHClient(t, MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ @@ -5321,18 +5321,18 @@ func Test_GetIssueComments(t *testing.T) { // Setup mock comments for success case mockComments := []*github.IssueComment{ { - ID: github.Ptr(int64(123)), - Body: github.Ptr("This is the first comment"), + ID: new(int64(123)), + Body: new("This is the first comment"), User: &github.User{ - Login: github.Ptr("user1"), + Login: new("user1"), }, CreatedAt: &github.Timestamp{Time: time.Now().Add(-time.Hour * 24)}, }, { - ID: github.Ptr(int64(456)), - Body: github.Ptr("This is the second comment"), + ID: new(int64(456)), + Body: new("This is the second comment"), User: &github.User{ - Login: github.Ptr("user2"), + Login: new("user2"), }, CreatedAt: &github.Timestamp{Time: time.Now().Add(-time.Hour)}, }, @@ -5401,14 +5401,14 @@ func Test_GetIssueComments(t *testing.T) { mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposIssuesCommentsByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, []*github.IssueComment{ { - ID: github.Ptr(int64(789)), - Body: github.Ptr("Maintainer comment"), - User: &github.User{Login: github.Ptr("maintainer")}, + ID: new(int64(789)), + Body: new("Maintainer comment"), + User: &github.User{Login: new("maintainer")}, }, { - ID: github.Ptr(int64(790)), - Body: github.Ptr("External user comment"), - User: &github.User{Login: github.Ptr("testuser")}, + ID: new(int64(790)), + Body: new("External user comment"), + User: &github.User{Login: new("testuser")}, }, }), }), @@ -5421,9 +5421,9 @@ func Test_GetIssueComments(t *testing.T) { expectError: false, expectedComments: []*github.IssueComment{ { - ID: github.Ptr(int64(789)), - Body: github.Ptr("Maintainer comment"), - User: &github.User{Login: github.Ptr("maintainer")}, + ID: new(int64(789)), + Body: new("Maintainer comment"), + User: &github.User{Login: new("maintainer")}, }, }, lockdownEnabled: true, @@ -5773,19 +5773,19 @@ func Test_AddSubIssue(t *testing.T) { // Setup mock issue for success case (matches GitHub API response format) mockIssue := &github.Issue{ - Number: github.Ptr(42), - Title: github.Ptr("can't \"quote\" AT&T\u200B"), - Body: github.Ptr("This is **Markdown**\u200B"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/42"), + Number: new(42), + Title: new("can't \"quote\" AT&T\u200B"), + Body: new("This is **Markdown**\u200B"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/42"), User: &github.User{ - Login: github.Ptr("testuser"), + Login: new("testuser"), }, Labels: []*github.Label{ { Name: "enhancement", Color: "84b6eb", - Description: github.Ptr("New feature or request"), + Description: new("New feature or request"), }, }, } @@ -5998,33 +5998,33 @@ func Test_GetSubIssues(t *testing.T) { // Setup mock sub-issues for success case mockSubIssues := []*github.Issue{ { - Number: github.Ptr(123), - Title: github.Ptr("can't \"quote\" AT&T\u200B"), - Body: github.Ptr("This is **Markdown**\u200B"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/123"), + Number: new(123), + Title: new("can't \"quote\" AT&T\u200B"), + Body: new("This is **Markdown**\u200B"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/123"), User: &github.User{ - Login: github.Ptr("user1"), + Login: new("user1"), }, Labels: []*github.Label{ { Name: "bug", Color: "d73a4a", - Description: github.Ptr("Something isn't working"), + Description: new("Something isn't working"), }, }, }, { - Number: github.Ptr(124), - Title: github.Ptr("Sub-issue 2"), - Body: github.Ptr("This is the second sub-issue"), - State: github.Ptr("closed"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/124"), + Number: new(124), + Title: new("Sub-issue 2"), + Body: new("This is the second sub-issue"), + State: new("closed"), + HTMLURL: new("https://github.com/owner/repo/issues/124"), User: &github.User{ - Login: github.Ptr("user2"), + Login: new("user2"), }, Assignees: []*github.User{ - {Login: github.Ptr("assignee1")}, + {Login: new("assignee1")}, }, }, } @@ -6321,17 +6321,17 @@ func TestAddIssueCommentHandler(t *testing.T) { serverTool := AddIssueComment(translations.NullTranslationHelper) mockComment := &github.IssueComment{ - ID: github.Ptr(int64(456)), - Body: github.Ptr("This is a comment"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/42#issuecomment-456"), + ID: new(int64(456)), + Body: new("This is a comment"), + HTMLURL: new("https://github.com/owner/repo/issues/42#issuecomment-456"), } mockReaction := &github.Reaction{ - ID: github.Ptr(int64(789)), - Content: github.Ptr("heart"), + ID: new(int64(789)), + Content: new("heart"), } mockIssueComment := &github.IssueComment{ - ID: github.Ptr(int64(999)), - IssueURL: github.Ptr("https://api.github.com/repos/owner/repo/issues/42"), + ID: new(int64(999)), + IssueURL: new("https://api.github.com/repos/owner/repo/issues/42"), } commentCreatedAfterReactionFailure := &atomic.Bool{} @@ -6387,8 +6387,8 @@ func TestAddIssueCommentHandler(t *testing.T) { name: "issue comment reaction requires matching issue_number", mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposIssuesCommentByOwnerByRepoByCommentID: mockResponse(t, http.StatusOK, &github.IssueComment{ - ID: github.Ptr(int64(999)), - IssueURL: github.Ptr("https://api.github.com/repos/owner/repo/issues/43"), + ID: new(int64(999)), + IssueURL: new("https://api.github.com/repos/owner/repo/issues/43"), }), }), requestArgs: map[string]any{ @@ -6717,9 +6717,9 @@ func TestUpdateIssueCommentHandler(t *testing.T) { t.Parallel() updatedComment := &github.IssueComment{ - ID: github.Ptr(int64(456)), - Body: github.Ptr("Updated comment"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/42#issuecomment-456"), + ID: new(int64(456)), + Body: new("Updated comment"), + HTMLURL: new("https://github.com/owner/repo/issues/42#issuecomment-456"), } tests := []struct { @@ -6847,19 +6847,19 @@ func Test_RemoveSubIssue(t *testing.T) { // Setup mock issue for success case (matches GitHub API response format - the updated parent issue) mockIssue := &github.Issue{ - Number: github.Ptr(42), - Title: github.Ptr("can't \"quote\" AT&T\u200B"), - Body: github.Ptr("This is **Markdown**\u200B"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/42"), + Number: new(42), + Title: new("can't \"quote\" AT&T\u200B"), + Body: new("This is **Markdown**\u200B"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/42"), User: &github.User{ - Login: github.Ptr("testuser"), + Login: new("testuser"), }, Labels: []*github.Label{ { Name: "enhancement", Color: "84b6eb", - Description: github.Ptr("New feature or request"), + Description: new("New feature or request"), }, }, } @@ -7055,19 +7055,19 @@ func Test_ReprioritizeSubIssue(t *testing.T) { // Setup mock issue for success case (matches GitHub API response format - the updated parent issue) mockIssue := &github.Issue{ - Number: github.Ptr(42), - Title: github.Ptr("can't \"quote\" AT&T\u200B"), - Body: github.Ptr("This is **Markdown**\u200B"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/42"), + Number: new(42), + Title: new("can't \"quote\" AT&T\u200B"), + Body: new("This is **Markdown**\u200B"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/issues/42"), User: &github.User{ - Login: github.Ptr("testuser"), + Login: new("testuser"), }, Labels: []*github.Label{ { Name: "enhancement", Color: "84b6eb", - Description: github.Ptr("New feature or request"), + Description: new("New feature or request"), }, }, } @@ -7310,16 +7310,16 @@ func Test_ListIssueTypes(t *testing.T) { // Setup mock issue types for success case mockIssueTypes := []*github.IssueType{ { - ID: github.Ptr(int64(1)), - Name: github.Ptr("bug"), - Description: github.Ptr("Something isn't working"), - Color: github.Ptr("d73a4a"), + ID: new(int64(1)), + Name: new("bug"), + Description: new("Something isn't working"), + Color: new("d73a4a"), }, { - ID: github.Ptr(int64(2)), - Name: github.Ptr("feature"), - Description: github.Ptr("New feature or enhancement"), - Color: github.Ptr("a2eeef"), + ID: new(int64(2)), + Name: new("feature"), + Description: new("New feature or enhancement"), + Color: new("a2eeef"), }, } diff --git a/pkg/github/minimal_types.go b/pkg/github/minimal_types.go index 2b06b279d5..2bb9efba35 100644 --- a/pkg/github/minimal_types.go +++ b/pkg/github/minimal_types.go @@ -1211,19 +1211,19 @@ func convertToMinimalProject(fullProject *github.ProjectV2) *MinimalProject { } return &MinimalProject{ - ID: github.Ptr(fullProject.GetID()), - NodeID: github.Ptr(fullProject.GetNodeID()), + ID: new(fullProject.GetID()), + NodeID: new(fullProject.GetNodeID()), Owner: convertToMinimalUser(fullProject.GetOwner()), Creator: convertToMinimalUser(fullProject.GetCreator()), - Title: github.Ptr(fullProject.GetTitle()), - Description: github.Ptr(fullProject.GetDescription()), - Public: github.Ptr(fullProject.GetPublic()), - ClosedAt: github.Ptr(fullProject.GetClosedAt()), - CreatedAt: github.Ptr(fullProject.GetCreatedAt()), - UpdatedAt: github.Ptr(fullProject.GetUpdatedAt()), - DeletedAt: github.Ptr(fullProject.GetDeletedAt()), - Number: github.Ptr(fullProject.GetNumber()), - ShortDescription: github.Ptr(fullProject.GetShortDescription()), + Title: new(fullProject.GetTitle()), + Description: new(fullProject.GetDescription()), + Public: new(fullProject.GetPublic()), + ClosedAt: new(fullProject.GetClosedAt()), + CreatedAt: new(fullProject.GetCreatedAt()), + UpdatedAt: new(fullProject.GetUpdatedAt()), + DeletedAt: new(fullProject.GetDeletedAt()), + Number: new(fullProject.GetNumber()), + ShortDescription: new(fullProject.GetShortDescription()), DeletedBy: convertToMinimalUser(fullProject.GetDeletedBy()), } } diff --git a/pkg/github/notifications_test.go b/pkg/github/notifications_test.go index c7fc296cb0..09ee191914 100644 --- a/pkg/github/notifications_test.go +++ b/pkg/github/notifications_test.go @@ -35,8 +35,8 @@ func Test_ListNotifications(t *testing.T) { // All fields are optional, so Required should be empty assert.Empty(t, schema.Required) mockNotification := &github.Notification{ - ID: github.Ptr("123"), - Reason: github.Ptr("mention"), + ID: new("123"), + Reason: new("mention"), } tests := []struct { @@ -156,8 +156,8 @@ func Test_ManageNotificationSubscription(t *testing.T) { assert.Contains(t, schema.Properties, "action") assert.Equal(t, []string{"notificationID", "action"}, schema.Required) - mockSub := &github.Subscription{Ignored: github.Ptr(true)} - mockSubWatch := &github.Subscription{Ignored: github.Ptr(false), Subscribed: github.Ptr(true)} + mockSub := &github.Subscription{Ignored: new(true)} + mockSubWatch := &github.Subscription{Ignored: new(false), Subscribed: new(true)} tests := []struct { name string @@ -179,7 +179,7 @@ func Test_ManageNotificationSubscription(t *testing.T) { "action": "ignore", }, expectError: false, - expectIgnored: github.Ptr(true), + expectIgnored: new(true), }, { name: "watch subscription", @@ -191,7 +191,7 @@ func Test_ManageNotificationSubscription(t *testing.T) { "action": "watch", }, expectError: false, - expectIgnored: github.Ptr(false), + expectIgnored: new(false), }, { name: "delete subscription", @@ -296,8 +296,8 @@ func Test_ManageRepositoryNotificationSubscription(t *testing.T) { assert.Contains(t, schema.Properties, "action") assert.Equal(t, []string{"owner", "repo", "action"}, schema.Required) - mockSub := &github.Subscription{Ignored: github.Ptr(true)} - mockWatchSub := &github.Subscription{Ignored: github.Ptr(false), Subscribed: github.Ptr(true)} + mockSub := &github.Subscription{Ignored: new(true)} + mockWatchSub := &github.Subscription{Ignored: new(false), Subscribed: new(true)} tests := []struct { name string @@ -321,7 +321,7 @@ func Test_ManageRepositoryNotificationSubscription(t *testing.T) { "action": "ignore", }, expectError: false, - expectIgnored: github.Ptr(true), + expectIgnored: new(true), }, { name: "watch subscription", @@ -334,8 +334,8 @@ func Test_ManageRepositoryNotificationSubscription(t *testing.T) { "action": "watch", }, expectError: false, - expectIgnored: github.Ptr(false), - expectSubscribed: github.Ptr(true), + expectIgnored: new(false), + expectSubscribed: new(true), }, { name: "delete subscription", @@ -679,7 +679,7 @@ func Test_GetNotificationDetails(t *testing.T) { assert.Contains(t, schema.Properties, "notificationID") assert.Equal(t, []string{"notificationID"}, schema.Required) - mockThread := &github.Notification{ID: github.Ptr("123"), Reason: github.Ptr("mention")} + mockThread := &github.Notification{ID: new("123"), Reason: new("mention")} tests := []struct { name string diff --git a/pkg/github/projects_test.go b/pkg/github/projects_test.go index 256f5af9d8..6305569c3a 100644 --- a/pkg/github/projects_test.go +++ b/pkg/github/projects_test.go @@ -1488,7 +1488,7 @@ func Test_BuildIssueFieldUpdate(t *testing.T) { func Test_ProjectItemIssueID_RejectsNonIssueItems(t *testing.T) { for _, contentType := range []string{"PullRequest", "DraftIssue"} { t.Run(contentType, func(t *testing.T) { - item := &gogithub.ProjectV2Item{ContentType: gogithub.Ptr(gogithub.ProjectV2ItemContentType(contentType))} + item := &gogithub.ProjectV2Item{ContentType: new(gogithub.ProjectV2ItemContentType(contentType))} _, err := projectItemIssueID(item) var structured *ghErrors.StructuredResolutionError require.ErrorAs(t, err, &structured) diff --git a/pkg/github/pullrequests.go b/pkg/github/pullrequests.go index 42d99ab9bb..98354ac70b 100644 --- a/pkg/github/pullrequests.go +++ b/pkg/github/pullrequests.go @@ -777,17 +777,17 @@ func CreatePullRequest(t translations.TranslationHelperFunc) inventory.ServerToo } newPR := &github.CreatePullRequest{ - Title: github.Ptr(title), + Title: new(title), Head: head, Base: base, } if body != "" { - newPR.Body = github.Ptr(body) + newPR.Body = new(body) } - newPR.Draft = github.Ptr(draft) - newPR.MaintainerCanModify = github.Ptr(maintainerCanModify) + newPR.Draft = new(draft) + newPR.MaintainerCanModify = new(maintainerCanModify) client, err := deps.GetClient(ctx) if err != nil { @@ -969,35 +969,35 @@ func UpdatePullRequest(t translations.TranslationHelperFunc) inventory.ServerToo if title, ok, err := OptionalParamOK[string](args, "title"); err != nil { return utils.NewToolResultError(err.Error()), nil, nil } else if ok { - update.Title = github.Ptr(title) + update.Title = new(title) restUpdateNeeded = true } if body, ok, err := OptionalParamOK[string](args, "body"); err != nil { return utils.NewToolResultError(err.Error()), nil, nil } else if ok { - update.Body = github.Ptr(body) + update.Body = new(body) restUpdateNeeded = true } if state, ok, err := OptionalParamOK[string](args, "state"); err != nil { return utils.NewToolResultError(err.Error()), nil, nil } else if ok { - update.State = github.Ptr(state) + update.State = new(state) restUpdateNeeded = true } if base, ok, err := OptionalParamOK[string](args, "base"); err != nil { return utils.NewToolResultError(err.Error()), nil, nil } else if ok { - update.Base = &github.PullRequestBranch{Ref: github.Ptr(base)} + update.Base = &github.PullRequestBranch{Ref: new(base)} restUpdateNeeded = true } if maintainerCanModify, ok, err := OptionalParamOK[bool](args, "maintainer_can_modify"); err != nil { return utils.NewToolResultError(err.Error()), nil, nil } else if ok { - update.MaintainerCanModify = github.Ptr(maintainerCanModify) + update.MaintainerCanModify = new(maintainerCanModify) restUpdateNeeded = true } @@ -1732,7 +1732,7 @@ func UpdatePullRequestBranch(t translations.TranslationHelperFunc) inventory.Ser } opts := &github.PullRequestBranchUpdateOptions{} if expectedHeadSHA != "" { - opts.ExpectedHeadSHA = github.Ptr(expectedHeadSHA) + opts.ExpectedHeadSHA = new(expectedHeadSHA) } client, err := deps.GetClient(ctx) diff --git a/pkg/github/pullrequests_sha_pinning_test.go b/pkg/github/pullrequests_sha_pinning_test.go index b1a6df3025..02f8b758a3 100644 --- a/pkg/github/pullrequests_sha_pinning_test.go +++ b/pkg/github/pullrequests_sha_pinning_test.go @@ -17,9 +17,9 @@ func Test_MergePullRequestSHAPinning(t *testing.T) { const pinnedSHA = "0123456789abcdef0123456789abcdef01234567" mockMergeResult := &github.PullRequestMergeResult{ - SHA: github.Ptr("merge-result-sha"), - Merged: github.Ptr(true), - Message: github.Ptr("Pull Request successfully merged"), + SHA: new("merge-result-sha"), + Merged: new(true), + Message: new("Pull Request successfully merged"), } t.Run("expected head SHA is sent as REST sha", func(t *testing.T) { diff --git a/pkg/github/pullrequests_test.go b/pkg/github/pullrequests_test.go index d06fe72806..a6bb796839 100644 --- a/pkg/github/pullrequests_test.go +++ b/pkg/github/pullrequests_test.go @@ -36,20 +36,20 @@ func Test_GetPullRequest(t *testing.T) { // Setup mock PR for success case mockPR := &github.PullRequest{ - Number: github.Ptr(42), - Title: github.Ptr("Test PR"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42"), + Number: new(42), + Title: new("Test PR"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/pull/42"), Head: &github.PullRequestBranch{ - SHA: github.Ptr("abcd1234"), - Ref: github.Ptr("feature-branch"), + SHA: new("abcd1234"), + Ref: new("feature-branch"), }, Base: &github.PullRequestBranch{ - Ref: github.Ptr("main"), + Ref: new("main"), }, - Body: github.Ptr("This is a test PR"), + Body: new("This is a test PR"), User: &github.User{ - Login: github.Ptr("testuser"), + Login: new("testuser"), }, } @@ -203,32 +203,32 @@ func Test_UpdatePullRequest(t *testing.T) { // Setup mock PR for success case mockUpdatedPR := &github.PullRequest{ - Number: github.Ptr(42), - Title: github.Ptr("Updated Test PR Title"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42"), - Body: github.Ptr("Updated test PR body."), - MaintainerCanModify: github.Ptr(false), - Draft: github.Ptr(false), + Number: new(42), + Title: new("Updated Test PR Title"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/pull/42"), + Body: new("Updated test PR body."), + MaintainerCanModify: new(false), + Draft: new(false), Base: &github.PullRequestBranch{ - Ref: github.Ptr("develop"), + Ref: new("develop"), }, } mockClosedPR := &github.PullRequest{ - Number: github.Ptr(42), - Title: github.Ptr("Test PR"), - State: github.Ptr("closed"), // State updated + Number: new(42), + Title: new("Test PR"), + State: new("closed"), // State updated } // Mock PR for when there are no updates but we still need a response mockPRWithReviewers := &github.PullRequest{ - Number: github.Ptr(42), - Title: github.Ptr("Test PR"), - State: github.Ptr("open"), + Number: new(42), + Title: new("Test PR"), + State: new("open"), RequestedReviewers: []*github.User{ - {Login: github.Ptr("reviewer1")}, - {Login: github.Ptr("reviewer2")}, + {Login: new("reviewer1")}, + {Login: new("reviewer2")}, }, } @@ -430,15 +430,15 @@ func Test_UpdatePullRequest(t *testing.T) { func Test_UpdatePullRequest_Draft(t *testing.T) { // Setup mock PR for success case mockUpdatedPR := &github.PullRequest{ - Number: github.Ptr(42), - Title: github.Ptr("Test PR Title"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42"), - Body: github.Ptr("Test PR body."), - MaintainerCanModify: github.Ptr(false), - Draft: github.Ptr(false), // Updated to ready for review + Number: new(42), + Title: new("Test PR Title"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/pull/42"), + Body: new("Test PR body."), + MaintainerCanModify: new(false), + Draft: new(false), // Updated to ready for review Base: &github.PullRequestBranch{ - Ref: github.Ptr("main"), + Ref: new("main"), }, } @@ -635,16 +635,16 @@ func Test_ListPullRequests(t *testing.T) { // Setup mock PRs for success case mockPRs := []*github.PullRequest{ { - Number: github.Ptr(42), - Title: github.Ptr("First PR"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42"), + Number: new(42), + Title: new("First PR"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/pull/42"), }, { - Number: github.Ptr(43), - Title: github.Ptr("Second PR"), - State: github.Ptr("closed"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/43"), + Number: new(43), + Title: new("Second PR"), + State: new("closed"), + HTMLURL: new("https://github.com/owner/repo/pull/43"), }, } @@ -765,9 +765,9 @@ func Test_MergePullRequest(t *testing.T) { // Setup mock merge result for success case mockMergeResult := &github.PullRequestMergeResult{ - Merged: github.Ptr(true), - Message: github.Ptr("Pull Request successfully merged"), - SHA: github.Ptr("abcd1234efgh5678"), + Merged: new(true), + Message: new("Pull Request successfully merged"), + SHA: new("abcd1234efgh5678"), } tests := []struct { @@ -879,29 +879,29 @@ func Test_SearchPullRequests(t *testing.T) { assert.ElementsMatch(t, schema.Required, []string{"query"}) mockSearchResult := &github.IssuesSearchResult{ - Total: github.Ptr(2), - IncompleteResults: github.Ptr(false), + Total: new(2), + IncompleteResults: new(false), Issues: []*github.Issue{ { - Number: github.Ptr(42), - Title: github.Ptr("Test PR 1"), - Body: github.Ptr("Updated tests."), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/1"), - Comments: github.Ptr(5), + Number: new(42), + Title: new("Test PR 1"), + Body: new("Updated tests."), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/pull/1"), + Comments: new(5), User: &github.User{ - Login: github.Ptr("user1"), + Login: new("user1"), }, }, { - Number: github.Ptr(43), - Title: github.Ptr("Test PR 2"), - Body: github.Ptr("Updated build scripts."), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/2"), - Comments: github.Ptr(3), + Number: new(43), + Title: new("Test PR 2"), + Body: new("Updated build scripts."), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/pull/2"), + Comments: new(3), User: &github.User{ - Login: github.Ptr("user2"), + Login: new("user2"), }, }, }, @@ -1168,20 +1168,20 @@ func Test_GetPullRequestFiles(t *testing.T) { // Setup mock PR files for success case mockFiles := []*github.CommitFile{ { - Filename: github.Ptr("file1.go"), - Status: github.Ptr("modified"), - Additions: github.Ptr(10), - Deletions: github.Ptr(5), - Changes: github.Ptr(15), - Patch: github.Ptr("@@ -1,5 +1,10 @@"), + Filename: new("file1.go"), + Status: new("modified"), + Additions: new(10), + Deletions: new(5), + Changes: new(15), + Patch: new("@@ -1,5 +1,10 @@"), }, { - Filename: github.Ptr("file2.go"), - Status: github.Ptr("added"), - Additions: github.Ptr(20), - Deletions: github.Ptr(0), - Changes: github.Ptr(20), - Patch: github.Ptr("@@ -0,0 +1,20 @@"), + Filename: new("file2.go"), + Status: new("added"), + Additions: new(20), + Deletions: new(0), + Changes: new(20), + Patch: new("@@ -0,0 +1,20 @@"), }, } @@ -1261,8 +1261,8 @@ func Test_GetPullRequestFiles(t *testing.T) { name: "lockdown enabled - author lacks push access", mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposPullsByOwnerByRepoByPullNumber: mockResponse(t, http.StatusOK, &github.PullRequest{ - Number: github.Ptr(42), - User: &github.User{Login: github.Ptr("reader")}, + Number: new(42), + User: &github.User{Login: new("reader")}, }), }), requestArgs: map[string]any{ @@ -1280,8 +1280,8 @@ func Test_GetPullRequestFiles(t *testing.T) { name: "lockdown enabled - author has push access", mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposPullsByOwnerByRepoByPullNumber: mockResponse(t, http.StatusOK, &github.PullRequest{ - Number: github.Ptr(42), - User: &github.User{Login: github.Ptr("writer")}, + Number: new(42), + User: &github.User{Login: new("writer")}, }), GetReposPullsFilesByOwnerByRepoByPullNumber: mockResponse(t, http.StatusOK, mockFiles), }), @@ -1391,39 +1391,39 @@ func Test_GetPullRequestCommits(t *testing.T) { authorDate := time.Date(2026, 4, 1, 12, 0, 0, 0, time.UTC) mockCommits := []*github.RepositoryCommit{ { - SHA: github.Ptr("abc123def456"), - HTMLURL: github.Ptr("https://github.com/owner/repo/commit/abc123def456"), + SHA: new("abc123def456"), + HTMLURL: new("https://github.com/owner/repo/commit/abc123def456"), Commit: &github.Commit{ - Message: github.Ptr("feat: add commit listing"), + Message: new("feat: add commit listing"), Author: &github.CommitAuthor{ - Name: github.Ptr("Test User"), - Email: github.Ptr("test@example.com"), + Name: new("Test User"), + Email: new("test@example.com"), Date: &github.Timestamp{Time: authorDate}, }, Committer: &github.CommitAuthor{ - Name: github.Ptr("Merge Bot"), - Email: github.Ptr("merge@example.com"), + Name: new("Merge Bot"), + Email: new("merge@example.com"), Date: &github.Timestamp{Time: authorDate.Add(30 * time.Minute)}, }, }, Author: &github.User{ - Login: github.Ptr("test-user"), - ID: github.Ptr(int64(12345)), - HTMLURL: github.Ptr("https://github.com/test-user"), - AvatarURL: github.Ptr("https://github.com/test-user.png"), + Login: new("test-user"), + ID: new(int64(12345)), + HTMLURL: new("https://github.com/test-user"), + AvatarURL: new("https://github.com/test-user.png"), }, Committer: &github.User{ - Login: github.Ptr("merge-bot"), - ID: github.Ptr(int64(67890)), - HTMLURL: github.Ptr("https://github.com/merge-bot"), - AvatarURL: github.Ptr("https://github.com/merge-bot.png"), + Login: new("merge-bot"), + ID: new(int64(67890)), + HTMLURL: new("https://github.com/merge-bot"), + AvatarURL: new("https://github.com/merge-bot.png"), }, }, { - SHA: github.Ptr("def456abc789"), - HTMLURL: github.Ptr("https://github.com/owner/repo/commit/def456abc789"), + SHA: new("def456abc789"), + HTMLURL: new("https://github.com/owner/repo/commit/def456abc789"), Commit: &github.Commit{ - Message: github.Ptr("fix: handle pagination"), + Message: new("fix: handle pagination"), }, }, } @@ -1504,8 +1504,8 @@ func Test_GetPullRequestCommits(t *testing.T) { name: "lockdown enabled - author lacks push access", mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposPullsByOwnerByRepoByPullNumber: mockResponse(t, http.StatusOK, &github.PullRequest{ - Number: github.Ptr(42), - User: &github.User{Login: github.Ptr("reader")}, + Number: new(42), + User: &github.User{Login: new("reader")}, }), }), requestArgs: map[string]any{ @@ -1523,8 +1523,8 @@ func Test_GetPullRequestCommits(t *testing.T) { name: "lockdown enabled - author has push access", mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposPullsByOwnerByRepoByPullNumber: mockResponse(t, http.StatusOK, &github.PullRequest{ - Number: github.Ptr(42), - User: &github.User{Login: github.Ptr("writer")}, + Number: new(42), + User: &github.User{Login: new("writer")}, }), GetReposPullsCommitsByOwnerByRepoByPullNumber: mockResponse(t, http.StatusOK, mockCommits), }), @@ -1543,8 +1543,8 @@ func Test_GetPullRequestCommits(t *testing.T) { name: "lockdown enabled - trusted bot author lacks push access", mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposPullsByOwnerByRepoByPullNumber: mockResponse(t, http.StatusOK, &github.PullRequest{ - Number: github.Ptr(42), - User: &github.User{Login: github.Ptr("github-actions[bot]")}, + Number: new(42), + User: &github.User{Login: new("github-actions[bot]")}, }), GetReposPullsCommitsByOwnerByRepoByPullNumber: mockResponse(t, http.StatusOK, mockCommits), }), @@ -1653,54 +1653,54 @@ func Test_GetPullRequestStatus(t *testing.T) { // Setup mock PR for successful PR fetch mockPR := &github.PullRequest{ - Number: github.Ptr(42), - Title: github.Ptr("Test PR"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42"), + Number: new(42), + Title: new("Test PR"), + HTMLURL: new("https://github.com/owner/repo/pull/42"), Head: &github.PullRequestBranch{ - SHA: github.Ptr("abcd1234"), - Ref: github.Ptr("feature-branch"), + SHA: new("abcd1234"), + Ref: new("feature-branch"), }, } statusCreatedAt := &github.Timestamp{Time: time.Date(2026, time.August, 11, 9, 30, 0, 0, time.UTC)} statusUpdatedAt := &github.Timestamp{Time: time.Date(2026, time.August, 11, 9, 35, 0, 0, time.UTC)} mockStatus := &github.CombinedStatus{ - Name: github.Ptr("abcd1234"), - State: github.Ptr("success"), - SHA: github.Ptr("abcd1234"), - TotalCount: github.Ptr(2), - CommitURL: github.Ptr("https://api.github.com/repos/owner/repo/commits/abcd1234"), - RepositoryURL: github.Ptr( + Name: new("abcd1234"), + State: new("success"), + SHA: new("abcd1234"), + TotalCount: new(2), + CommitURL: new("https://api.github.com/repos/owner/repo/commits/abcd1234"), + RepositoryURL: new( "https://api.github.com/repos/owner/repo", ), Statuses: []*github.RepoStatus{ { - ID: github.Ptr(int64(101)), - NodeID: github.Ptr("SC_kwDOStatus101"), - URL: github.Ptr("https://api.github.com/repos/owner/repo/statuses/abcd1234"), - State: github.Ptr("success"), - Context: github.Ptr("continuous-integration/travis-ci"), - Description: github.Ptr("Build succeeded"), - TargetURL: github.Ptr("https://travis-ci.org/owner/repo/builds/123"), - AvatarURL: github.Ptr("https://avatars.githubusercontent.com/in/123"), + ID: new(int64(101)), + NodeID: new("SC_kwDOStatus101"), + URL: new("https://api.github.com/repos/owner/repo/statuses/abcd1234"), + State: new("success"), + Context: new("continuous-integration/travis-ci"), + Description: new("Build succeeded"), + TargetURL: new("https://travis-ci.org/owner/repo/builds/123"), + AvatarURL: new("https://avatars.githubusercontent.com/in/123"), Creator: &github.User{ - Login: github.Ptr("ci-bot"), + Login: new("ci-bot"), }, CreatedAt: statusCreatedAt, UpdatedAt: statusUpdatedAt, }, { - State: github.Ptr("success"), - Context: github.Ptr("codecov/patch"), - Description: github.Ptr("Coverage increased"), - TargetURL: github.Ptr("https://codecov.io/gh/owner/repo/pull/42"), + State: new("success"), + Context: new("codecov/patch"), + Description: new("Coverage increased"), + TargetURL: new("https://codecov.io/gh/owner/repo/pull/42"), }, }, } emptyStatus := &github.CombinedStatus{ - State: github.Ptr("pending"), - SHA: github.Ptr("abcd1234"), - TotalCount: github.Ptr(0), + State: new("pending"), + SHA: new("abcd1234"), + TotalCount: new(0), Statuses: []*github.RepoStatus{nil}, } @@ -1881,32 +1881,32 @@ func Test_GetPullRequestCheckRuns(t *testing.T) { // Setup mock PR for successful PR fetch mockPR := &github.PullRequest{ - Number: github.Ptr(42), - Title: github.Ptr("Test PR"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42"), + Number: new(42), + Title: new("Test PR"), + HTMLURL: new("https://github.com/owner/repo/pull/42"), Head: &github.PullRequestBranch{ - SHA: github.Ptr("abcd1234"), - Ref: github.Ptr("feature-branch"), + SHA: new("abcd1234"), + Ref: new("feature-branch"), }, } // Setup mock check runs for success case mockCheckRuns := &github.ListCheckRunsResults{ - Total: github.Ptr(2), + Total: new(2), CheckRuns: []*github.CheckRun{ { - ID: github.Ptr(int64(1)), - Name: github.Ptr("build"), - Status: github.Ptr("completed"), - Conclusion: github.Ptr("success"), - HTMLURL: github.Ptr("https://github.com/owner/repo/runs/1"), + ID: new(int64(1)), + Name: new("build"), + Status: new("completed"), + Conclusion: new("success"), + HTMLURL: new("https://github.com/owner/repo/runs/1"), }, { - ID: github.Ptr(int64(2)), - Name: github.Ptr("test"), - Status: github.Ptr("completed"), - Conclusion: github.Ptr("success"), - HTMLURL: github.Ptr("https://github.com/owner/repo/runs/2"), + ID: new(int64(2)), + Name: new("test"), + Status: new("completed"), + Conclusion: new("success"), + HTMLURL: new("https://github.com/owner/repo/runs/2"), }, }, } @@ -2036,8 +2036,8 @@ func Test_UpdatePullRequestBranch(t *testing.T) { // Setup mock update result for success case mockUpdateResult := &github.PullRequestBranchUpdateResponse{ - Message: github.Ptr("Branch was updated successfully"), - URL: github.Ptr("https://api.github.com/repos/owner/repo/pulls/42"), + Message: new("Branch was updated successfully"), + URL: new("https://api.github.com/repos/owner/repo/pulls/42"), } tests := []struct { @@ -2579,25 +2579,25 @@ func Test_GetPullRequestReviews(t *testing.T) { // Setup mock PR reviews for success case mockReviews := []*github.PullRequestReview{ { - ID: github.Ptr(int64(201)), - State: github.Ptr("APPROVED"), - Body: github.Ptr("LGTM"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42#pullrequestreview-201"), + ID: new(int64(201)), + State: new("APPROVED"), + Body: new("LGTM"), + HTMLURL: new("https://github.com/owner/repo/pull/42#pullrequestreview-201"), User: &github.User{ - Login: github.Ptr("approver"), + Login: new("approver"), }, - CommitID: github.Ptr("abcdef123456"), + CommitID: new("abcdef123456"), SubmittedAt: &github.Timestamp{Time: time.Now().Add(-24 * time.Hour)}, }, { - ID: github.Ptr(int64(202)), - State: github.Ptr("CHANGES_REQUESTED"), - Body: github.Ptr("Please address the following issues"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42#pullrequestreview-202"), + ID: new(int64(202)), + State: new("CHANGES_REQUESTED"), + Body: new("Please address the following issues"), + HTMLURL: new("https://github.com/owner/repo/pull/42#pullrequestreview-202"), User: &github.User{ - Login: github.Ptr("reviewer"), + Login: new("reviewer"), }, - CommitID: github.Ptr("abcdef123456"), + CommitID: new("abcdef123456"), SubmittedAt: &github.Timestamp{Time: time.Now().Add(-12 * time.Hour)}, }, } @@ -2674,16 +2674,16 @@ func Test_GetPullRequestReviews(t *testing.T) { mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposPullsReviewsByOwnerByRepoByPullNumber: mockResponse(t, http.StatusOK, []*github.PullRequestReview{ { - ID: github.Ptr(int64(2030)), - State: github.Ptr("APPROVED"), - Body: github.Ptr("Maintainer review"), - User: &github.User{Login: github.Ptr("maintainer")}, + ID: new(int64(2030)), + State: new("APPROVED"), + Body: new("Maintainer review"), + User: &github.User{Login: new("maintainer")}, }, { - ID: github.Ptr(int64(2031)), - State: github.Ptr("COMMENTED"), - Body: github.Ptr("External reviewer"), - User: &github.User{Login: github.Ptr("testuser")}, + ID: new(int64(2031)), + State: new("COMMENTED"), + Body: new("External reviewer"), + User: &github.User{Login: new("testuser")}, }, }), }), @@ -2696,10 +2696,10 @@ func Test_GetPullRequestReviews(t *testing.T) { expectError: false, expectedReviews: []*github.PullRequestReview{ { - ID: github.Ptr(int64(2030)), - State: github.Ptr("APPROVED"), - Body: github.Ptr("Maintainer review"), - User: &github.User{Login: github.Ptr("maintainer")}, + ID: new(int64(2030)), + State: new("APPROVED"), + Body: new("Maintainer review"), + User: &github.User{Login: new("maintainer")}, }, }, lockdownEnabled: true, @@ -2709,15 +2709,15 @@ func Test_GetPullRequestReviews(t *testing.T) { mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposPullsReviewsByOwnerByRepoByPullNumber: mockResponse(t, http.StatusOK, []*github.PullRequestReview{ { - ID: github.Ptr(int64(2040)), - State: github.Ptr("APPROVED"), - Body: github.Ptr("Ghost review"), - User: &github.User{Login: github.Ptr("")}, + ID: new(int64(2040)), + State: new("APPROVED"), + Body: new("Ghost review"), + User: &github.User{Login: new("")}, }, { - ID: github.Ptr(int64(2041)), - State: github.Ptr("COMMENTED"), - Body: github.Ptr("Another ghost review"), + ID: new(int64(2041)), + State: new("COMMENTED"), + Body: new("Another ghost review"), }, }), }), @@ -2814,23 +2814,23 @@ func Test_CreatePullRequest(t *testing.T) { // Setup mock PR for success case mockPR := &github.PullRequest{ - Number: github.Ptr(42), - Title: github.Ptr("Test PR"), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42"), + Number: new(42), + Title: new("Test PR"), + State: new("open"), + HTMLURL: new("https://github.com/owner/repo/pull/42"), Head: &github.PullRequestBranch{ - SHA: github.Ptr("abcd1234"), - Ref: github.Ptr("feature-branch"), + SHA: new("abcd1234"), + Ref: new("feature-branch"), }, Base: &github.PullRequestBranch{ - SHA: github.Ptr("efgh5678"), - Ref: github.Ptr("main"), + SHA: new("efgh5678"), + Ref: new("main"), }, - Body: github.Ptr("This is a test PR"), - Draft: github.Ptr(false), - MaintainerCanModify: github.Ptr(true), + Body: new("This is a test PR"), + Draft: new(false), + MaintainerCanModify: new(true), User: &github.User{ - Login: github.Ptr("testuser"), + Login: new("testuser"), }, } @@ -2949,12 +2949,12 @@ func Test_CreatePullRequest_MCPAppsFeature_UIGate(t *testing.T) { t.Parallel() mockPR := &github.PullRequest{ - Number: github.Ptr(42), - Title: github.Ptr("Test PR"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42"), - Head: &github.PullRequestBranch{SHA: github.Ptr("abc"), Ref: github.Ptr("feature")}, - Base: &github.PullRequestBranch{SHA: github.Ptr("def"), Ref: github.Ptr("main")}, - User: &github.User{Login: github.Ptr("testuser")}, + Number: new(42), + Title: new("Test PR"), + HTMLURL: new("https://github.com/owner/repo/pull/42"), + Head: &github.PullRequestBranch{SHA: new("abc"), Ref: new("feature")}, + Base: &github.PullRequestBranch{SHA: new("def"), Ref: new("main")}, + User: &github.User{Login: new("testuser")}, } serverTool := CreatePullRequest(translations.NullTranslationHelper) @@ -3050,12 +3050,12 @@ func Test_UpdatePullRequest_MCPAppsFeature_UIGate(t *testing.T) { t.Parallel() mockPR := &github.PullRequest{ - Number: github.Ptr(42), - Title: github.Ptr("Updated"), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42"), - Head: &github.PullRequestBranch{SHA: github.Ptr("abc"), Ref: github.Ptr("feature")}, - Base: &github.PullRequestBranch{SHA: github.Ptr("def"), Ref: github.Ptr("main")}, - User: &github.User{Login: github.Ptr("testuser")}, + Number: new(42), + Title: new("Updated"), + HTMLURL: new("https://github.com/owner/repo/pull/42"), + Head: &github.PullRequestBranch{SHA: new("abc"), Ref: new("feature")}, + Base: &github.PullRequestBranch{SHA: new("def"), Ref: new("main")}, + User: &github.User{Login: new("testuser")}, } serverTool := UpdatePullRequest(translations.NullTranslationHelper) @@ -4186,8 +4186,8 @@ index 5d6e7b2..8a4f5c3 100644 // author, then the raw diff; branch on the Accept header to serve both. prOrDiffHandler := func(authorLogin string) http.HandlerFunc { mockPR := &github.PullRequest{ - Number: github.Ptr(42), - User: &github.User{Login: github.Ptr(authorLogin)}, + Number: new(42), + User: &github.User{Login: new(authorLogin)}, } return func(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.Header.Get("Accept"), "diff") { @@ -4418,19 +4418,19 @@ func TestAddReplyToPullRequestComment(t *testing.T) { // Setup mock reply comment for success case mockReplyComment := &github.PullRequestComment{ - ID: github.Ptr(int64(456)), - Body: github.Ptr("This is a reply to the comment"), - InReplyTo: github.Ptr(int64(123)), - HTMLURL: github.Ptr("https://github.com/owner/repo/pull/42#discussion_r456"), + ID: new(int64(456)), + Body: new("This is a reply to the comment"), + InReplyTo: new(int64(123)), + HTMLURL: new("https://github.com/owner/repo/pull/42#discussion_r456"), User: &github.User{ - Login: github.Ptr("responder"), + Login: new("responder"), }, CreatedAt: &github.Timestamp{Time: time.Now()}, UpdatedAt: &github.Timestamp{Time: time.Now()}, } mockReaction := &github.Reaction{ - ID: github.Ptr(int64(789)), - Content: github.Ptr("rocket"), + ID: new(int64(789)), + Content: new("rocket"), } replyCreatedAfterReactionFailure := &atomic.Bool{} diff --git a/pkg/github/repositories.go b/pkg/github/repositories.go index 21647059b7..4b7e84cb9a 100644 --- a/pkg/github/repositories.go +++ b/pkg/github/repositories.go @@ -496,9 +496,9 @@ SHA MUST be provided for existing file updates. // Create the file options opts := &github.RepositoryContentFileOptions{ - Message: github.Ptr(message), + Message: new(message), Content: contentBytes, - Branch: github.Ptr(branch), + Branch: new(branch), } // If SHA is provided, set it (for updates) @@ -507,7 +507,7 @@ SHA MUST be provided for existing file updates. return utils.NewToolResultError(err.Error()), nil, nil } if sha != "" { - opts.SHA = github.Ptr(sha) + opts.SHA = new(sha) } allowSymlinkWrite, err := OptionalParam[bool](args, "allow_symlink_write") @@ -699,10 +699,10 @@ func CreateRepository(t translations.TranslationHelperFunc) inventory.ServerTool } repo := &github.Repository{ - Name: github.Ptr(name), - Description: github.Ptr(description), - Private: github.Ptr(private), - AutoInit: github.Ptr(autoInit), + Name: new(name), + Description: new(description), + Private: new(private), + AutoInit: new(autoInit), } client, err := deps.GetClient(ctx) @@ -768,7 +768,7 @@ func DeleteRepository(t translations.TranslationHelperFunc) inventory.ServerTool Annotations: &mcp.ToolAnnotations{ Title: t("TOOL_DELETE_REPOSITORY_USER_TITLE", "Delete repository"), ReadOnlyHint: false, - DestructiveHint: github.Ptr(true), + DestructiveHint: new(true), }, InputSchema: &jsonschema.Schema{ Type: "object", @@ -1310,7 +1310,7 @@ func DeleteFile(t translations.TranslationHelperFunc) inventory.ServerTool { Annotations: &mcp.ToolAnnotations{ Title: t("TOOL_DELETE_FILE_USER_TITLE", "Delete file"), ReadOnlyHint: false, - DestructiveHint: github.Ptr(true), + DestructiveHint: new(true), }, InputSchema: &jsonschema.Schema{ Type: "object", @@ -1400,9 +1400,9 @@ func DeleteFile(t translations.TranslationHelperFunc) inventory.ServerTool { // Create a tree entry for the file deletion by setting SHA to nil treeEntries := []*github.TreeEntry{ { - Path: github.Ptr(path), - Mode: github.Ptr("100644"), // Regular file mode - Type: github.Ptr("blob"), + Path: new(path), + Mode: new("100644"), // Regular file mode + Type: new("blob"), SHA: nil, // Setting SHA to nil deletes the file }, } @@ -1428,7 +1428,7 @@ func DeleteFile(t translations.TranslationHelperFunc) inventory.ServerTool { // Create a new commit with the new tree commit := github.Commit{ - Message: github.Ptr(message), + Message: new(message), Tree: newTree, Parents: []*github.Commit{{SHA: baseCommit.SHA}}, } @@ -1454,7 +1454,7 @@ func DeleteFile(t translations.TranslationHelperFunc) inventory.ServerTool { ref.Object.SHA = newCommit.SHA _, resp, err = client.Git.UpdateRef(ctx, owner, repo, *ref.Ref, github.UpdateRef{ SHA: *newCommit.SHA, - Force: github.Ptr(false), + Force: new(false), }) if err != nil { return ghErrors.NewGitHubAPIErrorResponse(ctx, @@ -1708,10 +1708,10 @@ func PushFiles(t translations.TranslationHelperFunc) inventory.ServerTool { } entries = append(entries, &github.TreeEntry{ - Path: github.Ptr(filePath), - Mode: github.Ptr("100644"), - Type: github.Ptr("blob"), - Content: github.Ptr(content), + Path: new(filePath), + Mode: new("100644"), + Type: new("blob"), + Content: new(content), }) } @@ -1803,7 +1803,7 @@ func PushFiles(t translations.TranslationHelperFunc) inventory.ServerTool { // Create a new commit (baseCommit always has a value now) commit := github.Commit{ - Message: github.Ptr(message), + Message: new(message), Tree: newTree, Parents: []*github.Commit{{SHA: baseCommit.SHA}}, } @@ -1823,7 +1823,7 @@ func PushFiles(t translations.TranslationHelperFunc) inventory.ServerTool { ref.Object.SHA = newCommit.SHA updatedRef, resp, err := client.Git.UpdateRef(ctx, owner, repo, *ref.Ref, github.UpdateRef{ SHA: *newCommit.SHA, - Force: github.Ptr(false), + Force: new(false), }) if err != nil { return ghErrors.NewGitHubAPIErrorResponse(ctx, @@ -2345,10 +2345,10 @@ func sanitizeReleaseNameAndBody(release *github.RepositoryRelease) { return } if release.Name != nil { - release.Name = github.Ptr(sanitize.PlainText(*release.Name)) + release.Name = new(sanitize.PlainText(*release.Name)) } if release.Body != nil { - release.Body = github.Ptr(sanitize.Content(*release.Body)) + release.Body = new(sanitize.Content(*release.Body)) } } diff --git a/pkg/github/repositories_helper.go b/pkg/github/repositories_helper.go index 0b46b839f1..519385fc8c 100644 --- a/pkg/github/repositories_helper.go +++ b/pkg/github/repositories_helper.go @@ -34,9 +34,9 @@ func initializeRepository(ctx context.Context, client *github.Client, owner, rep defaultBranch := repository.GetDefaultBranch() fileOpts := &github.RepositoryContentFileOptions{ - Message: github.Ptr("Initial commit"), + Message: new("Initial commit"), Content: []byte(""), - Branch: github.Ptr(defaultBranch), + Branch: new(defaultBranch), } // Create an initial empty commit to create the default branch diff --git a/pkg/github/repositories_test.go b/pkg/github/repositories_test.go index 4719b23164..0c930ae8c0 100644 --- a/pkg/github/repositories_test.go +++ b/pkg/github/repositories_test.go @@ -113,19 +113,19 @@ func Test_GetFileContents(t *testing.T) { // Setup mock directory content for success case mockDirContent := []*github.RepositoryContent{ { - Type: github.Ptr("file"), - Name: github.Ptr("README.md"), - Path: github.Ptr("README.md"), - SHA: github.Ptr(gitBlobSHA(mockRawContent)), - Size: github.Ptr(42), - HTMLURL: github.Ptr("https://github.com/owner/repo/blob/main/README.md"), + Type: new("file"), + Name: new("README.md"), + Path: new("README.md"), + SHA: new(gitBlobSHA(mockRawContent)), + Size: new(42), + HTMLURL: new("https://github.com/owner/repo/blob/main/README.md"), }, { - Type: github.Ptr("dir"), - Name: github.Ptr("src"), - Path: github.Ptr("src"), - SHA: github.Ptr("def456"), - HTMLURL: github.Ptr("https://github.com/owner/repo/tree/main/src"), + Type: new("dir"), + Name: new("src"), + Path: new("src"), + SHA: new("def456"), + HTMLURL: new("https://github.com/owner/repo/tree/main/src"), }, } @@ -147,12 +147,12 @@ func Test_GetFileContents(t *testing.T) { GetReposContentsByOwnerByRepoByPath: func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) fileContent := &github.RepositoryContent{ - Name: github.Ptr("README.md"), - Path: github.Ptr("README.md"), - SHA: github.Ptr(gitBlobSHA(mockRawContent)), - Type: github.Ptr("file"), - Content: github.Ptr(string(mockRawContent)), - Size: github.Ptr(len(mockRawContent)), + Name: new("README.md"), + Path: new("README.md"), + SHA: new(gitBlobSHA(mockRawContent)), + Type: new("file"), + Content: new(string(mockRawContent)), + Size: new(len(mockRawContent)), } contentBytes, _ := json.Marshal(fileContent) _, _ = w.Write(contentBytes) @@ -183,13 +183,13 @@ func Test_GetFileContents(t *testing.T) { pngContent := []byte("\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01") encodedContent := base64.StdEncoding.EncodeToString(pngContent) fileContent := &github.RepositoryContent{ - Name: github.Ptr("test.png"), - Path: github.Ptr("test.png"), - SHA: github.Ptr(gitBlobSHA(pngContent)), - Type: github.Ptr("file"), - Content: github.Ptr(encodedContent), - Size: github.Ptr(len(pngContent)), - Encoding: github.Ptr("base64"), + Name: new("test.png"), + Path: new("test.png"), + SHA: new(gitBlobSHA(pngContent)), + Type: new("file"), + Content: new(encodedContent), + Size: new(len(pngContent)), + Encoding: new("base64"), } contentBytes, _ := json.Marshal(fileContent) _, _ = w.Write(contentBytes) @@ -219,13 +219,13 @@ func Test_GetFileContents(t *testing.T) { pdfContent := []byte("%PDF-1.4 fake pdf content") encodedContent := base64.StdEncoding.EncodeToString(pdfContent) fileContent := &github.RepositoryContent{ - Name: github.Ptr("document.pdf"), - Path: github.Ptr("document.pdf"), - SHA: github.Ptr(gitBlobSHA(pdfContent)), - Type: github.Ptr("file"), - Content: github.Ptr(encodedContent), - Size: github.Ptr(len(pdfContent)), - Encoding: github.Ptr("base64"), + Name: new("document.pdf"), + Path: new("document.pdf"), + SHA: new(gitBlobSHA(pdfContent)), + Type: new("file"), + Content: new(encodedContent), + Size: new(len(pdfContent)), + Encoding: new("base64"), } contentBytes, _ := json.Marshal(fileContent) _, _ = w.Write(contentBytes) @@ -274,13 +274,13 @@ func Test_GetFileContents(t *testing.T) { // Base64 encode the content as GitHub API does encodedContent := base64.StdEncoding.EncodeToString(mockRawContent) fileContent := &github.RepositoryContent{ - Name: github.Ptr("README.md"), - Path: github.Ptr("README.md"), - SHA: github.Ptr(gitBlobSHA(mockRawContent)), - Type: github.Ptr("file"), - Content: github.Ptr(encodedContent), - Size: github.Ptr(len(mockRawContent)), - Encoding: github.Ptr("base64"), + Name: new("README.md"), + Path: new("README.md"), + SHA: new(gitBlobSHA(mockRawContent)), + Type: new("file"), + Content: new(encodedContent), + Size: new(len(mockRawContent)), + Encoding: new("base64"), } contentBytes, _ := json.Marshal(fileContent) _, _ = w.Write(contentBytes) @@ -358,13 +358,13 @@ func Test_GetFileContents(t *testing.T) { // Base64 encode the content as GitHub API does encodedContent := base64.StdEncoding.EncodeToString(mockRawContent) fileContent := &github.RepositoryContent{ - Name: github.Ptr("README.md"), - Path: github.Ptr("README.md"), - SHA: github.Ptr(gitBlobSHA(mockRawContent)), - Type: github.Ptr("file"), - Content: github.Ptr(encodedContent), - Size: github.Ptr(len(mockRawContent)), - Encoding: github.Ptr("base64"), + Name: new("README.md"), + Path: new("README.md"), + SHA: new(gitBlobSHA(mockRawContent)), + Type: new("file"), + Content: new(encodedContent), + Size: new(len(mockRawContent)), + Encoding: new("base64"), } contentBytes, _ := json.Marshal(fileContent) _, _ = w.Write(contentBytes) @@ -391,22 +391,22 @@ func Test_GetFileContents(t *testing.T) { GetReposByOwnerByRepo: mockResponse(t, http.StatusOK, "{\"name\": \"repo\", \"default_branch\": \"main\"}"), "GET /repos/owner/repo/git/trees/refs/heads/main": mockResponse(t, http.StatusOK, &github.Tree{ Entries: []*github.TreeEntry{{ - Path: github.Ptr("large-file.bin"), - Mode: github.Ptr("100644"), - Type: github.Ptr("blob"), - SHA: github.Ptr(strings.Repeat("a", 40)), + Path: new("large-file.bin"), + Mode: new("100644"), + Type: new("blob"), + SHA: new(strings.Repeat("a", 40)), }}, }), GetReposContentsByOwnerByRepoByPath: func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) // File larger than 1MB - Contents API returns metadata but no content fileContent := &github.RepositoryContent{ - Name: github.Ptr("large-file.bin"), - Path: github.Ptr("large-file.bin"), - SHA: github.Ptr(strings.Repeat("a", 40)), - Type: github.Ptr("file"), - Size: github.Ptr(2 * 1024 * 1024), // 2MB - DownloadURL: github.Ptr("https://raw.githubusercontent.com/owner/repo/main/large-file.bin"), + Name: new("large-file.bin"), + Path: new("large-file.bin"), + SHA: new(strings.Repeat("a", 40)), + Type: new("file"), + Size: new(2 * 1024 * 1024), // 2MB + DownloadURL: new("https://raw.githubusercontent.com/owner/repo/main/large-file.bin"), } contentBytes, _ := json.Marshal(fileContent) _, _ = w.Write(contentBytes) @@ -433,13 +433,13 @@ func Test_GetFileContents(t *testing.T) { GetReposContentsByOwnerByRepoByPath: func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) fileContent := &github.RepositoryContent{ - Name: github.Ptr(".gitkeep"), - Path: github.Ptr(".gitkeep"), - SHA: github.Ptr(gitBlobSHA(nil)), - Type: github.Ptr("file"), + Name: new(".gitkeep"), + Path: new(".gitkeep"), + SHA: new(gitBlobSHA(nil)), + Type: new("file"), Content: nil, - Size: github.Ptr(0), - Encoding: github.Ptr("base64"), + Size: new(0), + Encoding: new("base64"), } contentBytes, _ := json.Marshal(fileContent) _, _ = w.Write(contentBytes) @@ -593,8 +593,8 @@ func Test_GetFileContents_SymlinkDisclosure(t *testing.T) { content := []byte("ordinary content") result, requests := runRepositoryReadFixture(t, repositoryReadFixture{ contents: &github.RepositoryContent{ - Type: github.Ptr("file"), Path: github.Ptr("README.md"), SHA: github.Ptr(gitBlobSHA(content)), - Content: github.Ptr(string(content)), Size: github.Ptr(len(content)), + Type: new("file"), Path: new("README.md"), SHA: new(gitBlobSHA(content)), + Content: new(string(content)), Size: new(len(content)), }, }, args("README.md")) require.False(t, result.IsError) @@ -615,9 +615,9 @@ func Test_GetFileContents_SymlinkDisclosure(t *testing.T) { linkSHA := gitBlobSHA([]byte(target)) result, requests := runRepositoryReadFixture(t, repositoryReadFixture{ contents: &github.RepositoryContent{ - Type: github.Ptr("file"), Path: github.Ptr("docs/link"), SHA: github.Ptr(linkSHA), - Content: github.Ptr(base64.StdEncoding.EncodeToString(tc.content)), - Encoding: github.Ptr("base64"), Size: github.Ptr(len(tc.content)), + Type: new("file"), Path: new("docs/link"), SHA: new(linkSHA), + Content: new(base64.StdEncoding.EncodeToString(tc.content)), + Encoding: new("base64"), Size: new(len(tc.content)), }, blobs: map[string][]byte{linkSHA: []byte(target)}, }, args("docs/link")) @@ -644,8 +644,8 @@ func Test_GetFileContents_SymlinkDisclosure(t *testing.T) { target := "../../outside" result, requests := runRepositoryReadFixture(t, repositoryReadFixture{ contents: &github.RepositoryContent{ - Type: github.Ptr("symlink"), Path: github.Ptr("docs/link"), - SHA: github.Ptr(gitBlobSHA([]byte(target))), Target: github.Ptr(target), + Type: new("symlink"), Path: new("docs/link"), + SHA: new(gitBlobSHA([]byte(target))), Target: new(target), }, }, args("docs/link")) require.False(t, result.IsError) @@ -662,9 +662,9 @@ func Test_GetFileContents_SymlinkDisclosure(t *testing.T) { target, content := "target.txt", []byte("resolved") result, requests := runRepositoryReadFixture(t, repositoryReadFixture{ contents: &github.RepositoryContent{ - Type: github.Ptr("symlink"), Path: github.Ptr("docs/link"), - SHA: github.Ptr(gitBlobSHA([]byte(target))), Target: github.Ptr(target), - Content: github.Ptr(string(content)), Size: github.Ptr(len(content)), + Type: new("symlink"), Path: new("docs/link"), + SHA: new(gitBlobSHA([]byte(target))), Target: new(target), + Content: new(string(content)), Size: new(len(content)), }, }, args("docs/link")) require.False(t, result.IsError) @@ -676,8 +676,8 @@ func Test_GetFileContents_SymlinkDisclosure(t *testing.T) { t.Run("submodule is explicit", func(t *testing.T) { result, requests := runRepositoryReadFixture(t, repositoryReadFixture{ contents: &github.RepositoryContent{ - Type: github.Ptr("submodule"), Path: github.Ptr("vendor/module"), - SHA: github.Ptr(strings.Repeat("d", 40)), SubmoduleGitURL: github.Ptr("https://example.com/module.git"), + Type: new("submodule"), Path: new("vendor/module"), + SHA: new(strings.Repeat("d", 40)), SubmoduleGitURL: new("https://example.com/module.git"), }, }, args("vendor/module")) require.False(t, result.IsError) @@ -691,8 +691,8 @@ func Test_GetFileContents_SymlinkDisclosure(t *testing.T) { content := []byte("resolved") result, requests := runRepositoryReadFixture(t, repositoryReadFixture{ contents: &github.RepositoryContent{ - Type: github.Ptr("file"), Path: github.Ptr("bad"), SHA: github.Ptr("bad-sha"), - Content: github.Ptr(string(content)), Size: github.Ptr(len(content)), + Type: new("file"), Path: new("bad"), SHA: new("bad-sha"), + Content: new(string(content)), Size: new(len(content)), }, }, args("bad")) assert.Equal(t, 1, requests) @@ -702,8 +702,8 @@ func Test_GetFileContents_SymlinkDisclosure(t *testing.T) { pathSHA := gitBlobSHA(pathBlob) result, requests = runRepositoryReadFixture(t, repositoryReadFixture{ contents: &github.RepositoryContent{ - Type: github.Ptr("file"), Path: github.Ptr("bad"), SHA: github.Ptr(pathSHA), - Content: github.Ptr(string(content)), Size: github.Ptr(len(content)), + Type: new("file"), Path: new("bad"), SHA: new(pathSHA), + Content: new(string(content)), Size: new(len(content)), }, blobs: map[string][]byte{pathSHA: pathBlob}, }, args("bad")) @@ -723,10 +723,10 @@ func Test_GetFileContents_ContentlessRequestCounts(t *testing.T) { t.Run("normal large file", func(t *testing.T) { result, requests := runRepositoryReadFixture(t, repositoryReadFixture{ contents: &github.RepositoryContent{ - Type: github.Ptr("file"), Path: github.Ptr("large.bin"), SHA: github.Ptr(fileSHA), Size: github.Ptr(largeSize), + Type: new("file"), Path: new("large.bin"), SHA: new(fileSHA), Size: new(largeSize), }, trees: map[string]*github.Tree{commitSHA: {Entries: []*github.TreeEntry{ - {Path: github.Ptr("large.bin"), Mode: github.Ptr("100644"), Type: github.Ptr("blob"), SHA: github.Ptr(fileSHA)}, + {Path: new("large.bin"), Mode: new("100644"), Type: new("blob"), SHA: new(fileSHA)}, }}}, }, args("large.bin")) require.False(t, result.IsError) @@ -740,15 +740,15 @@ func Test_GetFileContents_ContentlessRequestCounts(t *testing.T) { linkSHA := gitBlobSHA([]byte(target)) result, requests := runRepositoryReadFixture(t, repositoryReadFixture{ contents: &github.RepositoryContent{ - Type: github.Ptr("file"), Path: github.Ptr("docs/link"), SHA: github.Ptr(linkSHA), Size: github.Ptr(largeSize), + Type: new("file"), Path: new("docs/link"), SHA: new(linkSHA), Size: new(largeSize), }, blobs: map[string][]byte{linkSHA: []byte(target)}, trees: map[string]*github.Tree{ commitSHA: {Entries: []*github.TreeEntry{ - {Path: github.Ptr("docs"), Mode: github.Ptr("040000"), Type: github.Ptr("tree"), SHA: github.Ptr("docs-tree")}, + {Path: new("docs"), Mode: new("040000"), Type: new("tree"), SHA: new("docs-tree")}, }}, "docs-tree": {Entries: []*github.TreeEntry{ - {Path: github.Ptr("link"), Mode: github.Ptr(gitSymlinkMode), Type: github.Ptr("blob"), SHA: github.Ptr(linkSHA)}, + {Path: new("link"), Mode: new(gitSymlinkMode), Type: new("blob"), SHA: new(linkSHA)}, }}, }, inspect: func(r *http.Request) { @@ -767,9 +767,9 @@ func Test_GetFileContents_ContentlessRequestCounts(t *testing.T) { t.Run("truncated tree fails closed", func(t *testing.T) { result, requests := runRepositoryReadFixture(t, repositoryReadFixture{ contents: &github.RepositoryContent{ - Type: github.Ptr("file"), Path: github.Ptr("large.bin"), SHA: github.Ptr(fileSHA), Size: github.Ptr(largeSize), + Type: new("file"), Path: new("large.bin"), SHA: new(fileSHA), Size: new(largeSize), }, - trees: map[string]*github.Tree{commitSHA: {Truncated: github.Ptr(true)}}, + trees: map[string]*github.Tree{commitSHA: {Truncated: new(true)}}, }, args("large.bin")) assert.Equal(t, 2, requests) assert.Contains(t, getErrorResult(t, result).Text, "truncated") @@ -778,7 +778,7 @@ func Test_GetFileContents_ContentlessRequestCounts(t *testing.T) { t.Run("directory remains one request", func(t *testing.T) { result, requests := runRepositoryReadFixture(t, repositoryReadFixture{ contents: []*github.RepositoryContent{{ - Type: github.Ptr("file"), Path: github.Ptr("docs/readme"), SHA: github.Ptr(fileSHA), + Type: new("file"), Path: new("docs/readme"), SHA: new(fileSHA), }}, }, args("docs")) require.False(t, result.IsError) @@ -789,21 +789,21 @@ func Test_GetFileContents_ContentlessRequestCounts(t *testing.T) { func Test_GetFileContents_DirectoryFieldFiltering(t *testing.T) { mockDirContent := []*github.RepositoryContent{ { - Type: github.Ptr("file"), - Name: github.Ptr("README.md"), - Path: github.Ptr("README.md"), - SHA: github.Ptr("abc123"), - Size: github.Ptr(42), - URL: github.Ptr("https://api.github.com/repos/owner/repo/contents/README.md"), - HTMLURL: github.Ptr("https://github.com/owner/repo/blob/main/README.md"), - DownloadURL: github.Ptr("https://raw.githubusercontent.com/owner/repo/main/README.md"), + Type: new("file"), + Name: new("README.md"), + Path: new("README.md"), + SHA: new("abc123"), + Size: new(42), + URL: new("https://api.github.com/repos/owner/repo/contents/README.md"), + HTMLURL: new("https://github.com/owner/repo/blob/main/README.md"), + DownloadURL: new("https://raw.githubusercontent.com/owner/repo/main/README.md"), }, { - Type: github.Ptr("dir"), - Name: github.Ptr("src"), - Path: github.Ptr("src"), - SHA: github.Ptr("def456"), - HTMLURL: github.Ptr("https://github.com/owner/repo/tree/main/src"), + Type: new("dir"), + Name: new("src"), + Path: new("src"), + SHA: new("def456"), + HTMLURL: new("https://github.com/owner/repo/tree/main/src"), }, } @@ -852,14 +852,14 @@ func Test_GetFileContents_DirectoryFieldFiltering(t *testing.T) { func Test_GetFileContents_DirectoryFieldsTelemetry(t *testing.T) { mockDirContent := []*github.RepositoryContent{ { - Type: github.Ptr("file"), - Name: github.Ptr("README.md"), - Path: github.Ptr("README.md"), - SHA: github.Ptr("abc123"), - Size: github.Ptr(42), - URL: github.Ptr("https://api.github.com/repos/owner/repo/contents/README.md"), - HTMLURL: github.Ptr("https://github.com/owner/repo/blob/main/README.md"), - DownloadURL: github.Ptr("https://raw.githubusercontent.com/owner/repo/main/README.md"), + Type: new("file"), + Name: new("README.md"), + Path: new("README.md"), + SHA: new("abc123"), + Size: new(42), + URL: new("https://api.github.com/repos/owner/repo/contents/README.md"), + HTMLURL: new("https://github.com/owner/repo/blob/main/README.md"), + DownloadURL: new("https://raw.githubusercontent.com/owner/repo/main/README.md"), }, } @@ -919,13 +919,13 @@ func Test_GetFileContents_IFC_InsidersMode(t *testing.T) { w.WriteHeader(http.StatusOK) encodedContent := base64.StdEncoding.EncodeToString(mockRawContent) fileContent := &github.RepositoryContent{ - Name: github.Ptr("README.md"), - Path: github.Ptr("README.md"), - SHA: github.Ptr(gitBlobSHA(mockRawContent)), - Type: github.Ptr("file"), - Content: github.Ptr(encodedContent), - Size: github.Ptr(len(mockRawContent)), - Encoding: github.Ptr("base64"), + Name: new("README.md"), + Path: new("README.md"), + SHA: new(gitBlobSHA(mockRawContent)), + Type: new("file"), + Content: new(encodedContent), + Size: new(len(mockRawContent)), + Encoding: new("base64"), } contentBytes, _ := json.Marshal(fileContent) _, _ = w.Write(contentBytes) @@ -1012,13 +1012,13 @@ func Test_GetFileContents_IFC_InsidersMode(t *testing.T) { w.WriteHeader(http.StatusOK) encodedContent := base64.StdEncoding.EncodeToString(mockRawContent) fileContent := &github.RepositoryContent{ - Name: github.Ptr("README.md"), - Path: github.Ptr("README.md"), - SHA: github.Ptr(gitBlobSHA(mockRawContent)), - Type: github.Ptr("file"), - Content: github.Ptr(encodedContent), - Size: github.Ptr(len(mockRawContent)), - Encoding: github.Ptr("base64"), + Name: new("README.md"), + Path: new("README.md"), + SHA: new(gitBlobSHA(mockRawContent)), + Type: new("file"), + Content: new(encodedContent), + Size: new(len(mockRawContent)), + Encoding: new("base64"), } contentBytes, _ := json.Marshal(fileContent) _, _ = w.Write(contentBytes) @@ -1048,8 +1048,8 @@ func Test_GetFileContents_IFC_InsidersMode(t *testing.T) { GetReposGitRefByOwnerByRepoByRef: mockResponse(t, http.StatusOK, "{\"ref\": \"refs/heads/main\", \"object\": {\"sha\": \"\"}}"), GetReposByOwnerByRepo: mockResponse(t, http.StatusOK, map[string]any{"private": false}), GetReposContentsByOwnerByRepoByPath: mockResponse(t, http.StatusOK, &github.RepositoryContent{ - Type: github.Ptr("file"), Path: github.Ptr("link"), SHA: github.Ptr(linkSHA), - Content: github.Ptr(string(content)), Size: github.Ptr(len(content)), + Type: new("file"), Path: new("link"), SHA: new(linkSHA), + Content: new(string(content)), Size: new(len(content)), }), GetReposGitBlobsByOwnerByRepoByFileSHA: func(w http.ResponseWriter, _ *http.Request) { _, _ = w.Write(target) @@ -1081,9 +1081,9 @@ func Test_GetCommit_IFC_FeatureFlag(t *testing.T) { serverTool := GetCommit(translations.NullTranslationHelper) mockCommit := &github.RepositoryCommit{ - SHA: github.Ptr("abc123def456"), - Commit: &github.Commit{Message: github.Ptr("First commit")}, - HTMLURL: github.Ptr("https://github.com/owner/repo/commit/abc123def456"), + SHA: new("abc123def456"), + Commit: &github.Commit{Message: new("First commit")}, + HTMLURL: new("https://github.com/owner/repo/commit/abc123def456"), } makeMockClient := func(isPrivate bool) *http.Client { @@ -1207,16 +1207,16 @@ func Test_ForkRepository(t *testing.T) { // Setup mock forked repo for success case mockForkedRepo := &github.Repository{ - ID: github.Ptr(int64(123456)), - Name: github.Ptr("repo"), - FullName: github.Ptr("new-owner/repo"), + ID: new(int64(123456)), + Name: new("repo"), + FullName: new("new-owner/repo"), Owner: &github.User{ - Login: github.Ptr("new-owner"), + Login: new("new-owner"), }, - HTMLURL: github.Ptr("https://github.com/new-owner/repo"), - DefaultBranch: github.Ptr("main"), - Fork: github.Ptr(true), - ForksCount: github.Ptr(0), + HTMLURL: new("https://github.com/new-owner/repo"), + DefaultBranch: new("main"), + Fork: new(true), + ForksCount: new(0), } tests := []struct { @@ -1310,22 +1310,22 @@ func Test_CreateBranch(t *testing.T) { // Setup mock repository for default branch test mockRepo := &github.Repository{ - DefaultBranch: github.Ptr("main"), + DefaultBranch: new("main"), } // Setup mock reference for from_branch tests mockSourceRef := &github.Reference{ - Ref: github.Ptr("refs/heads/main"), + Ref: new("refs/heads/main"), Object: &github.GitObject{ - SHA: github.Ptr("abc123def456"), + SHA: new("abc123def456"), }, } // Setup mock created reference mockCreatedRef := &github.Reference{ - Ref: github.Ptr("refs/heads/new-feature"), + Ref: new("refs/heads/new-feature"), Object: &github.GitObject{ - SHA: github.Ptr("abc123def456"), + SHA: new("abc123def456"), }, } @@ -1521,32 +1521,32 @@ func Test_GetCommit(t *testing.T) { assert.ElementsMatch(t, schema.Required, []string{"owner", "repo", "sha"}) mockCommit := &github.RepositoryCommit{ - SHA: github.Ptr("abc123def456"), + SHA: new("abc123def456"), Commit: &github.Commit{ - Message: github.Ptr("First commit"), + Message: new("First commit"), Author: &github.CommitAuthor{ - Name: github.Ptr("Test User"), - Email: github.Ptr("test@example.com"), + Name: new("Test User"), + Email: new("test@example.com"), Date: &github.Timestamp{Time: time.Now().Add(-48 * time.Hour)}, }, }, Author: &github.User{ - Login: github.Ptr("testuser"), + Login: new("testuser"), }, - HTMLURL: github.Ptr("https://github.com/owner/repo/commit/abc123def456"), + HTMLURL: new("https://github.com/owner/repo/commit/abc123def456"), Stats: &github.CommitStats{ - Additions: github.Ptr(10), - Deletions: github.Ptr(2), - Total: github.Ptr(12), + Additions: new(10), + Deletions: new(2), + Total: new(12), }, Files: []*github.CommitFile{ { - Filename: github.Ptr("file1.go"), - Status: github.Ptr("modified"), - Additions: github.Ptr(10), - Deletions: github.Ptr(2), - Changes: github.Ptr(12), - Patch: github.Ptr("@@ -1,2 +1,10 @@"), + Filename: new("file1.go"), + Status: new("modified"), + Additions: new(10), + Deletions: new(2), + Changes: new(12), + Patch: new("@@ -1,2 +1,10 @@"), }, }, } @@ -1635,24 +1635,24 @@ func Test_GetCommit(t *testing.T) { func Test_GetCommit_Detail(t *testing.T) { mockCommit := &github.RepositoryCommit{ - SHA: github.Ptr("abc123def456"), - HTMLURL: github.Ptr("https://github.com/owner/repo/commit/abc123def456"), + SHA: new("abc123def456"), + HTMLURL: new("https://github.com/owner/repo/commit/abc123def456"), Commit: &github.Commit{ - Message: github.Ptr("First commit"), + Message: new("First commit"), }, Stats: &github.CommitStats{ - Additions: github.Ptr(10), - Deletions: github.Ptr(2), - Total: github.Ptr(12), + Additions: new(10), + Deletions: new(2), + Total: new(12), }, Files: []*github.CommitFile{ { - Filename: github.Ptr("file1.go"), - Status: github.Ptr("modified"), - Additions: github.Ptr(10), - Deletions: github.Ptr(2), - Changes: github.Ptr(12), - Patch: github.Ptr("@@ -1,2 +1,10 @@\n+new line"), + Filename: new("file1.go"), + Status: new("modified"), + Additions: new(10), + Deletions: new(2), + Changes: new(12), + Patch: new("@@ -1,2 +1,10 @@\n+new line"), }, }, } @@ -1773,73 +1773,73 @@ func Test_ListCommits(t *testing.T) { // Setup mock commits for success case mockCommits := []*github.RepositoryCommit{ { - SHA: github.Ptr("abc123def456"), + SHA: new("abc123def456"), Commit: &github.Commit{ - Message: github.Ptr("First commit"), + Message: new("First commit"), Author: &github.CommitAuthor{ - Name: github.Ptr("Test User"), - Email: github.Ptr("test@example.com"), + Name: new("Test User"), + Email: new("test@example.com"), Date: &github.Timestamp{Time: time.Now().Add(-48 * time.Hour)}, }, }, Author: &github.User{ - Login: github.Ptr("testuser"), - ID: github.Ptr(int64(12345)), - HTMLURL: github.Ptr("https://github.com/testuser"), - AvatarURL: github.Ptr("https://github.com/testuser.png"), + Login: new("testuser"), + ID: new(int64(12345)), + HTMLURL: new("https://github.com/testuser"), + AvatarURL: new("https://github.com/testuser.png"), }, - HTMLURL: github.Ptr("https://github.com/owner/repo/commit/abc123def456"), + HTMLURL: new("https://github.com/owner/repo/commit/abc123def456"), Stats: &github.CommitStats{ - Additions: github.Ptr(10), - Deletions: github.Ptr(5), - Total: github.Ptr(15), + Additions: new(10), + Deletions: new(5), + Total: new(15), }, Files: []*github.CommitFile{ { - Filename: github.Ptr("src/main.go"), - Status: github.Ptr("modified"), - Additions: github.Ptr(8), - Deletions: github.Ptr(3), - Changes: github.Ptr(11), + Filename: new("src/main.go"), + Status: new("modified"), + Additions: new(8), + Deletions: new(3), + Changes: new(11), }, { - Filename: github.Ptr("README.md"), - Status: github.Ptr("added"), - Additions: github.Ptr(2), - Deletions: github.Ptr(2), - Changes: github.Ptr(4), + Filename: new("README.md"), + Status: new("added"), + Additions: new(2), + Deletions: new(2), + Changes: new(4), }, }, }, { - SHA: github.Ptr("def456abc789"), + SHA: new("def456abc789"), Commit: &github.Commit{ - Message: github.Ptr("Second commit"), + Message: new("Second commit"), Author: &github.CommitAuthor{ - Name: github.Ptr("Another User"), - Email: github.Ptr("another@example.com"), + Name: new("Another User"), + Email: new("another@example.com"), Date: &github.Timestamp{Time: time.Now().Add(-24 * time.Hour)}, }, }, Author: &github.User{ - Login: github.Ptr("anotheruser"), - ID: github.Ptr(int64(67890)), - HTMLURL: github.Ptr("https://github.com/anotheruser"), - AvatarURL: github.Ptr("https://github.com/anotheruser.png"), + Login: new("anotheruser"), + ID: new(int64(67890)), + HTMLURL: new("https://github.com/anotheruser"), + AvatarURL: new("https://github.com/anotheruser.png"), }, - HTMLURL: github.Ptr("https://github.com/owner/repo/commit/def456abc789"), + HTMLURL: new("https://github.com/owner/repo/commit/def456abc789"), Stats: &github.CommitStats{ - Additions: github.Ptr(20), - Deletions: github.Ptr(10), - Total: github.Ptr(30), + Additions: new(20), + Deletions: new(10), + Total: new(30), }, Files: []*github.CommitFile{ { - Filename: github.Ptr("src/utils.go"), - Status: github.Ptr("added"), - Additions: github.Ptr(20), - Deletions: github.Ptr(10), - Changes: github.Ptr(30), + Filename: new("src/utils.go"), + Status: new("added"), + Additions: new(20), + Deletions: new(10), + Changes: new(30), }, }, }, @@ -2077,22 +2077,22 @@ func Test_CreateOrUpdateFile(t *testing.T) { // Setup mock file content response mockFileResponse := &github.RepositoryContentResponse{ Content: &github.RepositoryContent{ - Name: github.Ptr("example.md"), - Path: github.Ptr("docs/example.md"), - SHA: github.Ptr("abc123def456"), - Size: github.Ptr(42), - HTMLURL: github.Ptr("https://github.com/owner/repo/blob/main/docs/example.md"), - DownloadURL: github.Ptr("https://raw.githubusercontent.com/owner/repo/main/docs/example.md"), + Name: new("example.md"), + Path: new("docs/example.md"), + SHA: new("abc123def456"), + Size: new(42), + HTMLURL: new("https://github.com/owner/repo/blob/main/docs/example.md"), + DownloadURL: new("https://raw.githubusercontent.com/owner/repo/main/docs/example.md"), }, Commit: github.Commit{ - SHA: github.Ptr("def456abc789"), - Message: github.Ptr("Add example file"), + SHA: new("def456abc789"), + Message: new("Add example file"), Author: &github.CommitAuthor{ - Name: github.Ptr("Test User"), - Email: github.Ptr("test@example.com"), + Name: new("Test User"), + Email: new("test@example.com"), Date: &github.Timestamp{Time: time.Now()}, }, - HTMLURL: github.Ptr("https://github.com/owner/repo/commit/def456abc789"), + HTMLURL: new("https://github.com/owner/repo/commit/def456abc789"), }, } symlinkTarget := []byte("other.md") @@ -2105,10 +2105,10 @@ func Test_CreateOrUpdateFile(t *testing.T) { tree = &github.Tree{ Entries: []*github.TreeEntry{ { - Path: github.Ptr("docs"), - Mode: github.Ptr("040000"), - Type: github.Ptr("tree"), - SHA: github.Ptr("docs-tree"), + Path: new("docs"), + Mode: new("040000"), + Type: new("tree"), + SHA: new("docs-tree"), }, }, } @@ -2120,10 +2120,10 @@ func Test_CreateOrUpdateFile(t *testing.T) { tree = &github.Tree{ Entries: []*github.TreeEntry{ { - Path: github.Ptr("example.md"), - Mode: github.Ptr(mode), - Type: github.Ptr("blob"), - SHA: github.Ptr(entrySHA), + Path: new("example.md"), + Mode: new(mode), + Type: new("blob"), + SHA: new(entrySHA), }, }, } @@ -2180,12 +2180,12 @@ func Test_CreateOrUpdateFile(t *testing.T) { mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposGitTreesByOwnerByRepoByTree: mockPathTree("100644"), "GET /repos/owner/repo/contents/docs/example.md": mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr("abc123def456"), - Type: github.Ptr("file"), + SHA: new("abc123def456"), + Type: new("file"), }), "GET /repos/{owner}/{repo}/contents/{path:.*}": mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr("abc123def456"), - Type: github.Ptr("file"), + SHA: new("abc123def456"), + Type: new("file"), }), PutReposContentsByOwnerByRepoByPath: expectRequestBody(t, map[string]any{ "message": "Update example file", @@ -2246,12 +2246,12 @@ func Test_CreateOrUpdateFile(t *testing.T) { mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposGitTreesByOwnerByRepoByTree: mockPathTree("100644"), "GET /repos/owner/repo/contents/docs/example.md": mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr("abc123def456"), - Type: github.Ptr("file"), + SHA: new("abc123def456"), + Type: new("file"), }), "GET /repos/{owner}/{repo}/contents/{path:.*}": mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr("abc123def456"), - Type: github.Ptr("file"), + SHA: new("abc123def456"), + Type: new("file"), }), PutReposContentsByOwnerByRepoByPath: expectRequestBody(t, map[string]any{ "message": "Update example file", @@ -2291,12 +2291,12 @@ func Test_CreateOrUpdateFile(t *testing.T) { _, _ = w.Write(symlinkTarget) }, "GET /repos/owner/repo/contents/docs/example.md": mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr("abc123def456"), - Type: github.Ptr("file"), + SHA: new("abc123def456"), + Type: new("file"), }), "GET /repos/{owner}/{repo}/contents/{path:.*}": mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr("abc123def456"), - Type: github.Ptr("file"), + SHA: new("abc123def456"), + Type: new("file"), }), }), requestArgs: map[string]any{ @@ -2331,12 +2331,12 @@ func Test_CreateOrUpdateFile(t *testing.T) { _, _ = w.Write(symlinkTarget) }, "GET /repos/owner/repo/contents/docs/example.md": mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr("abc123def456"), - Type: github.Ptr("file"), + SHA: new("abc123def456"), + Type: new("file"), }), "GET /repos/{owner}/{repo}/contents/{path:.*}": mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr("abc123def456"), - Type: github.Ptr("file"), + SHA: new("abc123def456"), + Type: new("file"), }), }), requestArgs: map[string]any{ @@ -2356,12 +2356,12 @@ func Test_CreateOrUpdateFile(t *testing.T) { name: "allows intentional symbolic link update with explicit opt-in", mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ "GET /repos/owner/repo/contents/docs/example.md": mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr("abc123def456"), - Type: github.Ptr("file"), + SHA: new("abc123def456"), + Type: new("file"), }), "GET /repos/{owner}/{repo}/contents/{path:.*}": mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr("abc123def456"), - Type: github.Ptr("file"), + SHA: new("abc123def456"), + Type: new("file"), }), PutReposContentsByOwnerByRepoByPath: expectRequestBody(t, map[string]any{ "message": "Change link target", @@ -2398,14 +2398,14 @@ func Test_CreateOrUpdateFile(t *testing.T) { name: "rejects explicit symbolic link response without tree inspection", mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ "GET /repos/owner/repo/contents/docs/example.md": mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr("abc123def456"), - Type: github.Ptr("symlink"), - Target: github.Ptr("../../outside"), + SHA: new("abc123def456"), + Type: new("symlink"), + Target: new("../../outside"), }), "GET /repos/{owner}/{repo}/contents/{path:.*}": mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr("abc123def456"), - Type: github.Ptr("symlink"), - Target: github.Ptr("../../outside"), + SHA: new("abc123def456"), + Type: new("symlink"), + Target: new("../../outside"), }), }), requestArgs: map[string]any{ @@ -2430,15 +2430,15 @@ func Test_CreateOrUpdateFile(t *testing.T) { name: "fails closed when git tree is truncated", mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ GetReposGitTreesByOwnerByRepoByTree: mockResponse(t, http.StatusOK, &github.Tree{ - Truncated: github.Ptr(true), + Truncated: new(true), }), "GET /repos/owner/repo/contents/docs/example.md": mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr("abc123def456"), - Type: github.Ptr("file"), + SHA: new("abc123def456"), + Type: new("file"), }), "GET /repos/{owner}/{repo}/contents/{path:.*}": mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr("abc123def456"), - Type: github.Ptr("file"), + SHA: new("abc123def456"), + Type: new("file"), }), }), requestArgs: map[string]any{ @@ -2460,16 +2460,16 @@ func Test_CreateOrUpdateFile(t *testing.T) { "GET /repos/owner/repo/contents/docs/example.md": expectQueryParams(t, map[string]string{ "ref": "main", }).andThen(mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr(symlinkSHA), - Type: github.Ptr("symlink"), - Target: github.Ptr(string(symlinkTarget)), + SHA: new(symlinkSHA), + Type: new("symlink"), + Target: new(string(symlinkTarget)), })), "GET /repos/{owner}/{repo}/contents/{path:.*}": expectQueryParams(t, map[string]string{ "ref": "main", }).andThen(mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr(symlinkSHA), - Type: github.Ptr("symlink"), - Target: github.Ptr(string(symlinkTarget)), + SHA: new(symlinkSHA), + Type: new("symlink"), + Target: new(string(symlinkTarget)), })), }), requestArgs: map[string]any{ @@ -2558,14 +2558,14 @@ func Test_CreateOrUpdateFile(t *testing.T) { "GET /repos/owner/repo/contents/docs/example.md": expectQueryParams(t, map[string]string{ "ref": "release/#candidate", }).andThen(mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr("existing123"), - Type: github.Ptr("file"), + SHA: new("existing123"), + Type: new("file"), })), "GET /repos/{owner}/{repo}/contents/{path:.*}": expectQueryParams(t, map[string]string{ "ref": "release/#candidate", }).andThen(mockResponse(t, http.StatusOK, &github.RepositoryContent{ - SHA: github.Ptr("existing123"), - Type: github.Ptr("file"), + SHA: new("existing123"), + Type: new("file"), })), }), requestArgs: map[string]any{ @@ -2749,13 +2749,13 @@ func Test_CreateRepository(t *testing.T) { // Setup mock repository response mockRepo := &github.Repository{ - Name: github.Ptr("test-repo"), - Description: github.Ptr("Test repository"), - Private: github.Ptr(true), - HTMLURL: github.Ptr("https://github.com/testuser/test-repo"), + Name: new("test-repo"), + Description: new("Test repository"), + Private: new(true), + HTMLURL: new("https://github.com/testuser/test-repo"), CreatedAt: &github.Timestamp{Time: time.Now()}, Owner: &github.User{ - Login: github.Ptr("testuser"), + Login: new("testuser"), }, } @@ -2939,35 +2939,35 @@ func Test_PushFiles(t *testing.T) { // Setup mock objects mockRef := &github.Reference{ - Ref: github.Ptr("refs/heads/main"), + Ref: new("refs/heads/main"), Object: &github.GitObject{ - SHA: github.Ptr("abc123"), - URL: github.Ptr("https://api.github.com/repos/owner/repo/git/trees/abc123"), + SHA: new("abc123"), + URL: new("https://api.github.com/repos/owner/repo/git/trees/abc123"), }, } mockCommit := &github.Commit{ - SHA: github.Ptr("abc123"), + SHA: new("abc123"), Tree: &github.Tree{ - SHA: github.Ptr("def456"), + SHA: new("def456"), }, } mockTree := &github.Tree{ - SHA: github.Ptr("ghi789"), + SHA: new("ghi789"), } mockNewCommit := &github.Commit{ - SHA: github.Ptr("jkl012"), - Message: github.Ptr("Update multiple files"), - HTMLURL: github.Ptr("https://github.com/owner/repo/commit/jkl012"), + SHA: new("jkl012"), + Message: new("Update multiple files"), + HTMLURL: new("https://github.com/owner/repo/commit/jkl012"), } mockUpdatedRef := &github.Reference{ - Ref: github.Ptr("refs/heads/main"), + Ref: new("refs/heads/main"), Object: &github.GitObject{ - SHA: github.Ptr("jkl012"), - URL: github.Ptr("https://api.github.com/repos/owner/repo/git/trees/jkl012"), + SHA: new("jkl012"), + URL: new("https://api.github.com/repos/owner/repo/git/trees/jkl012"), }, } @@ -3250,7 +3250,7 @@ func Test_PushFiles(t *testing.T) { WithRequestMatch( GetReposByOwnerByRepo, &github.Repository{ - DefaultBranch: github.Ptr("main"), + DefaultBranch: new("main"), }, ), // Create initial file using Contents API @@ -3264,7 +3264,7 @@ func Test_PushFiles(t *testing.T) { require.Equal(t, "main", body["branch"]) w.WriteHeader(http.StatusCreated) response := &github.RepositoryContentResponse{ - Commit: github.Commit{SHA: github.Ptr("abc123")}, + Commit: github.Commit{SHA: new("abc123")}, } b, _ := json.Marshal(response) _, _ = w.Write(b) @@ -3328,8 +3328,8 @@ func Test_PushFiles(t *testing.T) { // Second call: returns the updated reference after first file creation w.WriteHeader(http.StatusOK) b, _ := json.Marshal(&github.Reference{ - Ref: github.Ptr("refs/heads/main"), - Object: &github.GitObject{SHA: github.Ptr("init456")}, + Ref: new("refs/heads/main"), + Object: &github.GitObject{SHA: new("init456")}, }) _, _ = w.Write(b) } @@ -3340,7 +3340,7 @@ func Test_PushFiles(t *testing.T) { WithRequestMatch( GetReposByOwnerByRepo, &github.Repository{ - DefaultBranch: github.Ptr("main"), + DefaultBranch: new("main"), }, ), // Create initial empty README.md file using Contents API to initialize repo @@ -3358,12 +3358,12 @@ func Test_PushFiles(t *testing.T) { w.WriteHeader(http.StatusCreated) response := &github.RepositoryContentResponse{ Content: &github.RepositoryContent{ - SHA: github.Ptr("readme123"), + SHA: new("readme123"), }, Commit: github.Commit{ - SHA: github.Ptr("init456"), + SHA: new("init456"), Tree: &github.Tree{ - SHA: github.Ptr("tree456"), + SHA: new("tree456"), }, }, } @@ -3377,9 +3377,9 @@ func Test_PushFiles(t *testing.T) { http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) response := &github.Commit{ - SHA: github.Ptr("init456"), + SHA: new("init456"), Tree: &github.Tree{ - SHA: github.Ptr("tree456"), + SHA: new("tree456"), }, } b, _ := json.Marshal(response) @@ -3479,7 +3479,7 @@ func Test_PushFiles(t *testing.T) { WithRequestMatch( GetReposByOwnerByRepo, &github.Repository{ - DefaultBranch: github.Ptr("main"), + DefaultBranch: new("main"), }, ), // Fail to create initial file using Contents API @@ -3532,15 +3532,15 @@ func Test_PushFiles(t *testing.T) { WithRequestMatch( GetReposByOwnerByRepo, &github.Repository{ - DefaultBranch: github.Ptr("main"), + DefaultBranch: new("main"), }, ), // Create initial file using Contents API WithRequestMatch( PutReposContentsByOwnerByRepoByPath, &github.RepositoryContentResponse{ - Content: &github.RepositoryContent{SHA: github.Ptr("readme123")}, - Commit: github.Commit{SHA: github.Ptr("init456")}, + Content: &github.RepositoryContent{SHA: new("readme123")}, + Commit: github.Commit{SHA: new("init456")}, }, ), ), @@ -3578,15 +3578,15 @@ func Test_PushFiles(t *testing.T) { WithRequestMatch( GetReposByOwnerByRepo, &github.Repository{ - DefaultBranch: github.Ptr("main"), + DefaultBranch: new("main"), }, ), // Create initial file using Contents API WithRequestMatch( PutReposContentsByOwnerByRepoByPath, &github.RepositoryContentResponse{ - Content: &github.RepositoryContent{SHA: github.Ptr("readme123")}, - Commit: github.Commit{SHA: github.Ptr("init456")}, + Content: &github.RepositoryContent{SHA: new("readme123")}, + Commit: github.Commit{SHA: new("init456")}, }, ), // Fail to get commit @@ -4122,12 +4122,12 @@ func Test_ListBranches(t *testing.T) { // Setup mock branches for success case mockBranches := []*github.Branch{ { - Name: github.Ptr("main"), - Commit: &github.RepositoryCommit{SHA: github.Ptr("abc123")}, + Name: new("main"), + Commit: &github.RepositoryCommit{SHA: new("abc123")}, }, { - Name: github.Ptr("develop"), - Commit: &github.RepositoryCommit{SHA: github.Ptr("def456")}, + Name: new("develop"), + Commit: &github.RepositoryCommit{SHA: new("def456")}, }, } @@ -4241,27 +4241,27 @@ func Test_DeleteFile(t *testing.T) { // Setup mock objects for Git Data API mockRef := &github.Reference{ - Ref: github.Ptr("refs/heads/main"), + Ref: new("refs/heads/main"), Object: &github.GitObject{ - SHA: github.Ptr("abc123"), + SHA: new("abc123"), }, } mockCommit := &github.Commit{ - SHA: github.Ptr("abc123"), + SHA: new("abc123"), Tree: &github.Tree{ - SHA: github.Ptr("def456"), + SHA: new("def456"), }, } mockTree := &github.Tree{ - SHA: github.Ptr("ghi789"), + SHA: new("ghi789"), } mockNewCommit := &github.Commit{ - SHA: github.Ptr("jkl012"), - Message: github.Ptr("Delete example file"), - HTMLURL: github.Ptr("https://github.com/owner/repo/commit/jkl012"), + SHA: new("jkl012"), + Message: new("Delete example file"), + HTMLURL: new("https://github.com/owner/repo/commit/jkl012"), } tests := []struct { @@ -4321,9 +4321,9 @@ func Test_DeleteFile(t *testing.T) { "force": false, }).andThen( mockResponse(t, http.StatusOK, &github.Reference{ - Ref: github.Ptr("refs/heads/main"), + Ref: new("refs/heads/main"), Object: &github.GitObject{ - SHA: github.Ptr("jkl012"), + SHA: new("jkl012"), }, }), ), @@ -4422,22 +4422,22 @@ func Test_ListTags(t *testing.T) { // Setup mock tags for success case mockTags := []*github.RepositoryTag{ { - Name: github.Ptr("v1.0.0"), + Name: new("v1.0.0"), Commit: &github.Commit{ - SHA: github.Ptr("v1.0.0-tag-sha"), - URL: github.Ptr("https://api.github.com/repos/owner/repo/commits/abc123"), + SHA: new("v1.0.0-tag-sha"), + URL: new("https://api.github.com/repos/owner/repo/commits/abc123"), }, - ZipballURL: github.Ptr("https://github.com/owner/repo/zipball/v1.0.0"), - TarballURL: github.Ptr("https://github.com/owner/repo/tarball/v1.0.0"), + ZipballURL: new("https://github.com/owner/repo/zipball/v1.0.0"), + TarballURL: new("https://github.com/owner/repo/tarball/v1.0.0"), }, { - Name: github.Ptr("v0.9.0"), + Name: new("v0.9.0"), Commit: &github.Commit{ - SHA: github.Ptr("v0.9.0-tag-sha"), - URL: github.Ptr("https://api.github.com/repos/owner/repo/commits/def456"), + SHA: new("v0.9.0-tag-sha"), + URL: new("https://api.github.com/repos/owner/repo/commits/def456"), }, - ZipballURL: github.Ptr("https://github.com/owner/repo/zipball/v0.9.0"), - TarballURL: github.Ptr("https://github.com/owner/repo/tarball/v0.9.0"), + ZipballURL: new("https://github.com/owner/repo/zipball/v0.9.0"), + TarballURL: new("https://github.com/owner/repo/tarball/v0.9.0"), }, } @@ -4551,28 +4551,28 @@ func Test_GetTag(t *testing.T) { assert.ElementsMatch(t, schema.Required, []string{"owner", "repo", "tag"}) mockAnnotatedTagRef := &github.Reference{ - Ref: github.Ptr("refs/tags/v1.0.0"), + Ref: new("refs/tags/v1.0.0"), Object: &github.GitObject{ - Type: github.Ptr("tag"), - SHA: github.Ptr("v1.0.0-tag-sha"), + Type: new("tag"), + SHA: new("v1.0.0-tag-sha"), }, } mockLightweightTagRef := &github.Reference{ - Ref: github.Ptr("refs/tags/v1.0.1"), + Ref: new("refs/tags/v1.0.1"), Object: &github.GitObject{ - Type: github.Ptr("commit"), - SHA: github.Ptr("abc123"), + Type: new("commit"), + SHA: new("abc123"), }, } mockTagObj := &github.Tag{ - SHA: github.Ptr("v1.0.0-tag-sha"), - Tag: github.Ptr("v1.0.0"), - Message: github.Ptr("Release v1.0.0"), + SHA: new("v1.0.0-tag-sha"), + Tag: new("v1.0.0"), + Message: new("Release v1.0.0"), Object: &github.GitObject{ - Type: github.Ptr("commit"), - SHA: github.Ptr("abc123"), + Type: new("commit"), + SHA: new("abc123"), }, } @@ -4756,12 +4756,12 @@ func Test_ListReleases(t *testing.T) { { ID: 1, TagName: "v1.0.0", - Name: github.Ptr("First Release"), + Name: new("First Release"), }, { ID: 2, TagName: "v0.9.0", - Name: github.Ptr("Beta Release"), + Name: new("Beta Release"), }, } @@ -4854,8 +4854,8 @@ func Test_GetLatestRelease(t *testing.T) { mockRelease := &github.RepositoryRelease{ ID: 1, TagName: "v1.0.0", - Name: github.Ptr("can't \"quote\" AT&T\u200B"), - Body: github.Ptr("
Notes
\u200B"), + Name: new("can't \"quote\" AT&T\u200B"), + Body: new("
Notes
\u200B"), } tests := []struct { @@ -4947,12 +4947,12 @@ func Test_GetReleaseByTag(t *testing.T) { mockRelease := &github.RepositoryRelease{ ID: 1, TagName: "v1.0.0", - Name: github.Ptr("can't \"quote\" AT&T\u200B"), - Body: github.Ptr("
Notes
\u200B"), + Name: new("can't \"quote\" AT&T\u200B"), + Body: new("
Notes
\u200B"), Assets: []*github.ReleaseAsset{ { - ID: github.Ptr(int64(1)), - Name: github.Ptr("release-v1.0.0.tar.gz"), + ID: new(int64(1)), + Name: new("release-v1.0.0.tar.gz"), }, }, } @@ -5115,7 +5115,7 @@ func Test_GetReleaseByTag_IFC_FeatureFlag(t *testing.T) { return &github.RepositoryRelease{ ID: 1, TagName: "v1.0.0", - Name: github.Ptr("v1.0.0"), + Name: new("v1.0.0"), Draft: draft, } } @@ -5278,10 +5278,10 @@ func Test_filterPaths(t *testing.T) { { name: "file name", tree: []*github.TreeEntry{ - {Path: github.Ptr("folder/foo.txt"), Type: github.Ptr("blob")}, - {Path: github.Ptr("bar.txt"), Type: github.Ptr("blob")}, - {Path: github.Ptr("nested/folder/foo.txt"), Type: github.Ptr("blob")}, - {Path: github.Ptr("nested/folder/baz.txt"), Type: github.Ptr("blob")}, + {Path: new("folder/foo.txt"), Type: new("blob")}, + {Path: new("bar.txt"), Type: new("blob")}, + {Path: new("nested/folder/foo.txt"), Type: new("blob")}, + {Path: new("nested/folder/baz.txt"), Type: new("blob")}, }, path: "foo.txt", maxResults: -1, @@ -5290,10 +5290,10 @@ func Test_filterPaths(t *testing.T) { { name: "dir name", tree: []*github.TreeEntry{ - {Path: github.Ptr("folder"), Type: github.Ptr("tree")}, - {Path: github.Ptr("bar.txt"), Type: github.Ptr("blob")}, - {Path: github.Ptr("nested/folder"), Type: github.Ptr("tree")}, - {Path: github.Ptr("nested/folder/baz.txt"), Type: github.Ptr("blob")}, + {Path: new("folder"), Type: new("tree")}, + {Path: new("bar.txt"), Type: new("blob")}, + {Path: new("nested/folder"), Type: new("tree")}, + {Path: new("nested/folder/baz.txt"), Type: new("blob")}, }, path: "folder/", maxResults: -1, @@ -5302,8 +5302,8 @@ func Test_filterPaths(t *testing.T) { { name: "dir and file match", tree: []*github.TreeEntry{ - {Path: github.Ptr("name"), Type: github.Ptr("tree")}, - {Path: github.Ptr("name"), Type: github.Ptr("blob")}, + {Path: new("name"), Type: new("tree")}, + {Path: new("name"), Type: new("blob")}, }, path: "name", // No trailing slash can match both files and directories maxResults: -1, @@ -5312,8 +5312,8 @@ func Test_filterPaths(t *testing.T) { { name: "dir only match", tree: []*github.TreeEntry{ - {Path: github.Ptr("name"), Type: github.Ptr("tree")}, - {Path: github.Ptr("name"), Type: github.Ptr("blob")}, + {Path: new("name"), Type: new("tree")}, + {Path: new("name"), Type: new("blob")}, }, path: "name/", // Trialing slash ensures only directories are matched maxResults: -1, @@ -5322,9 +5322,9 @@ func Test_filterPaths(t *testing.T) { { name: "max results limit 2", tree: []*github.TreeEntry{ - {Path: github.Ptr("folder"), Type: github.Ptr("tree")}, - {Path: github.Ptr("nested/folder"), Type: github.Ptr("tree")}, - {Path: github.Ptr("nested/nested/folder"), Type: github.Ptr("tree")}, + {Path: new("folder"), Type: new("tree")}, + {Path: new("nested/folder"), Type: new("tree")}, + {Path: new("nested/nested/folder"), Type: new("tree")}, }, path: "folder/", maxResults: 2, @@ -5333,9 +5333,9 @@ func Test_filterPaths(t *testing.T) { { name: "max results limit 1", tree: []*github.TreeEntry{ - {Path: github.Ptr("folder"), Type: github.Ptr("tree")}, - {Path: github.Ptr("nested/folder"), Type: github.Ptr("tree")}, - {Path: github.Ptr("nested/nested/folder"), Type: github.Ptr("tree")}, + {Path: new("folder"), Type: new("tree")}, + {Path: new("nested/folder"), Type: new("tree")}, + {Path: new("nested/nested/folder"), Type: new("tree")}, }, path: "folder/", maxResults: 1, @@ -5344,9 +5344,9 @@ func Test_filterPaths(t *testing.T) { { name: "max results limit 0", tree: []*github.TreeEntry{ - {Path: github.Ptr("folder"), Type: github.Ptr("tree")}, - {Path: github.Ptr("nested/folder"), Type: github.Ptr("tree")}, - {Path: github.Ptr("nested/nested/folder"), Type: github.Ptr("tree")}, + {Path: new("folder"), Type: new("tree")}, + {Path: new("nested/folder"), Type: new("tree")}, + {Path: new("nested/nested/folder"), Type: new("tree")}, }, path: "folder/", maxResults: 0, @@ -5648,39 +5648,39 @@ func Test_ListStarredRepositories(t *testing.T) { { StarredAt: &github.Timestamp{Time: starredAt}, Repository: &github.Repository{ - ID: github.Ptr(int64(12345)), - Name: github.Ptr("awesome-repo"), - FullName: github.Ptr("owner/awesome-repo"), - Description: github.Ptr("An awesome repository"), - HTMLURL: github.Ptr("https://github.com/owner/awesome-repo"), - Language: github.Ptr("Go"), - StargazersCount: github.Ptr(100), - ForksCount: github.Ptr(25), - OpenIssuesCount: github.Ptr(5), + ID: new(int64(12345)), + Name: new("awesome-repo"), + FullName: new("owner/awesome-repo"), + Description: new("An awesome repository"), + HTMLURL: new("https://github.com/owner/awesome-repo"), + Language: new("Go"), + StargazersCount: new(100), + ForksCount: new(25), + OpenIssuesCount: new(5), UpdatedAt: &github.Timestamp{Time: updatedAt}, - Private: github.Ptr(false), - Fork: github.Ptr(false), - Archived: github.Ptr(false), - DefaultBranch: github.Ptr("main"), + Private: new(false), + Fork: new(false), + Archived: new(false), + DefaultBranch: new("main"), }, }, { StarredAt: &github.Timestamp{Time: starredAt.Add(-12 * time.Hour)}, Repository: &github.Repository{ - ID: github.Ptr(int64(67890)), - Name: github.Ptr("cool-project"), - FullName: github.Ptr("user/cool-project"), - Description: github.Ptr("A very cool project"), - HTMLURL: github.Ptr("https://github.com/user/cool-project"), - Language: github.Ptr("Python"), - StargazersCount: github.Ptr(500), - ForksCount: github.Ptr(75), - OpenIssuesCount: github.Ptr(10), + ID: new(int64(67890)), + Name: new("cool-project"), + FullName: new("user/cool-project"), + Description: new("A very cool project"), + HTMLURL: new("https://github.com/user/cool-project"), + Language: new("Python"), + StargazersCount: new(500), + ForksCount: new(75), + OpenIssuesCount: new(10), UpdatedAt: &github.Timestamp{Time: updatedAt.Add(-1 * time.Hour)}, - Private: github.Ptr(false), - Fork: github.Ptr(true), - Archived: github.Ptr(false), - DefaultBranch: github.Ptr("master"), + Private: new(false), + Fork: new(true), + Archived: new(false), + DefaultBranch: new("master"), }, }, } @@ -6700,14 +6700,14 @@ func Test_ListRepositoryCollaborators(t *testing.T) { mockCollaborators := []*github.User{ { - Login: github.Ptr("user1"), - ID: github.Ptr(int64(101)), - RoleName: github.Ptr("admin"), + Login: new("user1"), + ID: new(int64(101)), + RoleName: new("admin"), }, { - Login: github.Ptr("user2"), - ID: github.Ptr(int64(102)), - RoleName: github.Ptr("write"), + Login: new("user2"), + ID: new(int64(102)), + RoleName: new("write"), }, } diff --git a/pkg/github/sanitize_coverage_test.go b/pkg/github/sanitize_coverage_test.go index b35081719b..f8f612cfe2 100644 --- a/pkg/github/sanitize_coverage_test.go +++ b/pkg/github/sanitize_coverage_test.go @@ -30,7 +30,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { name: "issue title (REST)", got: func() string { return convertToMinimalIssue(&github.Issue{ - Title: github.Ptr(maliciousText), + Title: new(maliciousText), }).Title }, }, @@ -39,7 +39,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { content: true, got: func() string { return convertToMinimalIssue(&github.Issue{ - Body: github.Ptr(maliciousText), + Body: new(maliciousText), }).Body }, }, @@ -48,7 +48,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { content: true, got: func() string { return convertToMinimalIssueComment(&github.IssueComment{ - Body: github.Ptr(maliciousText), + Body: new(maliciousText), }).Body }, }, @@ -56,7 +56,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { name: "pull request title", got: func() string { return convertToMinimalPullRequest(&github.PullRequest{ - Title: github.Ptr(maliciousText), + Title: new(maliciousText), }).Title }, }, @@ -65,7 +65,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { content: true, got: func() string { return convertToMinimalPullRequest(&github.PullRequest{ - Body: github.Ptr(maliciousText), + Body: new(maliciousText), }).Body }, }, @@ -74,7 +74,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { content: true, got: func() string { return convertToMinimalPullRequestReview(&github.PullRequestReview{ - Body: github.Ptr(maliciousText), + Body: new(maliciousText), }).Body }, }, @@ -92,7 +92,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { name: "release name", got: func() string { return convertToMinimalRelease(&github.RepositoryRelease{ - Name: github.Ptr(maliciousText), + Name: new(maliciousText), }).Name }, }, @@ -101,7 +101,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { content: true, got: func() string { return convertToMinimalRelease(&github.RepositoryRelease{ - Body: github.Ptr(maliciousText), + Body: new(maliciousText), }).Body }, }, @@ -110,7 +110,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { content: true, got: func() string { commit := convertToMinimalCommit(&github.RepositoryCommit{ - Commit: &github.Commit{Message: github.Ptr(maliciousText)}, + Commit: &github.Commit{Message: new(maliciousText)}, }, commitDetailNone) require.NotNil(t, commit.Commit) return commit.Commit.Message @@ -121,7 +121,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { content: true, got: func() string { item := convertCommitResultToMinimalCommit(&github.CommitResult{ - Commit: &github.Commit{Message: github.Ptr(maliciousText)}, + Commit: &github.Commit{Message: new(maliciousText)}, }) require.NotNil(t, item.Commit) return item.Commit.Message @@ -132,7 +132,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { content: true, got: func() string { commits := convertToMinimalPullRequestCommits([]*github.RepositoryCommit{ - {Commit: &github.Commit{Message: github.Ptr(maliciousText)}}, + {Commit: &github.Commit{Message: new(maliciousText)}}, }) require.Len(t, commits, 1) return commits[0].Message @@ -143,7 +143,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { content: true, got: func() string { resp := convertToMinimalFileContentResponse(&github.RepositoryContentResponse{ - Commit: github.Commit{Message: github.Ptr(maliciousText)}, + Commit: github.Commit{Message: new(maliciousText)}, }) require.NotNil(t, resp.Commit) return resp.Commit.Message @@ -154,7 +154,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { content: true, got: func() string { run := convertToMinimalWorkflowRun(&github.WorkflowRun{ - HeadCommit: &github.HeadCommit{Message: github.Ptr(maliciousText)}, + HeadCommit: &github.HeadCommit{Message: new(maliciousText)}, }) require.NotNil(t, run.HeadCommit) return run.HeadCommit.Message @@ -164,7 +164,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { name: "project item content title (issue)", got: func() string { return convertIssueToMinimalProjectItemContent(&github.Issue{ - Title: github.Ptr(maliciousText), + Title: new(maliciousText), }).Title }, }, @@ -172,7 +172,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { name: "project item content title (pull request)", got: func() string { return convertPullRequestToMinimalProjectItemContent(&github.PullRequest{ - Title: github.Ptr(maliciousText), + Title: new(maliciousText), }).Title }, }, @@ -180,7 +180,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { name: "project item content title (draft issue)", got: func() string { return convertDraftIssueToMinimalProjectItemContent(&github.ProjectV2DraftIssue{ - Title: github.Ptr(maliciousText), + Title: new(maliciousText), }).Title }, }, @@ -188,7 +188,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { name: "project pull request ref title (from *github.PullRequest)", got: func() string { return minimalProjectPullRequestRefFromPullRequest(&github.PullRequest{ - Title: github.Ptr(maliciousText), + Title: new(maliciousText), }).Title }, }, @@ -226,7 +226,7 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { name: "issue dependency ref title (issue_dependency_read / issue_dependency_write)", got: func() string { return issueToDependencyRef(&github.Issue{ - Title: github.Ptr(maliciousText), + Title: new(maliciousText), }).Title }, }, @@ -249,8 +249,8 @@ func Test_MinimalConverters_SanitizeUserAuthoredText(t *testing.T) { func Test_SearchIssueResult_SanitizesTitleAndBody(t *testing.T) { result := SearchIssueResult{ Issue: &github.Issue{ - Title: github.Ptr(maliciousText), - Body: github.Ptr(maliciousText), + Title: new(maliciousText), + Body: new(maliciousText), }, } @@ -283,8 +283,8 @@ func Test_SanitizeIssueTitleAndBody(t *testing.T) { t.Run("sanitizes in place", func(t *testing.T) { issue := &github.Issue{ - Title: github.Ptr(maliciousText), - Body: github.Ptr(maliciousText), + Title: new(maliciousText), + Body: new(maliciousText), } sanitizeIssueTitleAndBody(issue) require.NotNil(t, issue.Title) @@ -302,7 +302,7 @@ func Test_MinimalConverters_PreserveCodeFidelity(t *testing.T) { t.Run("commit file patch (get_commit)", func(t *testing.T) { commit := convertToMinimalCommit(&github.RepositoryCommit{ - Files: []*github.CommitFile{{Filename: github.Ptr("a.go"), Patch: github.Ptr(patch)}}, + Files: []*github.CommitFile{{Filename: new("a.go"), Patch: new(patch)}}, }, commitDetailFullPatch) require.Len(t, commit.Files, 1) assert.Equal(t, patch, commit.Files[0].Patch) @@ -310,7 +310,7 @@ func Test_MinimalConverters_PreserveCodeFidelity(t *testing.T) { t.Run("pull request file patch (get_pull_request_files)", func(t *testing.T) { files := convertToMinimalPRFiles([]*github.CommitFile{ - {Filename: github.Ptr("a.go"), Patch: github.Ptr(patch)}, + {Filename: new("a.go"), Patch: new(patch)}, }) require.Len(t, files, 1) assert.Equal(t, patch, files[0].Patch) diff --git a/pkg/github/search_test.go b/pkg/github/search_test.go index 8139ce76aa..f372de1bd7 100644 --- a/pkg/github/search_test.go +++ b/pkg/github/search_test.go @@ -35,24 +35,24 @@ func Test_SearchRepositories(t *testing.T) { // Setup mock search results mockSearchResult := &github.RepositoriesSearchResult{ - Total: github.Ptr(2), - IncompleteResults: github.Ptr(false), + Total: new(2), + IncompleteResults: new(false), Repositories: []*github.Repository{ { - ID: github.Ptr(int64(12345)), - Name: github.Ptr("repo-1"), - FullName: github.Ptr("owner/repo-1"), - HTMLURL: github.Ptr("https://github.com/owner/repo-1"), - Description: github.Ptr("Test repository 1"), - StargazersCount: github.Ptr(100), + ID: new(int64(12345)), + Name: new("repo-1"), + FullName: new("owner/repo-1"), + HTMLURL: new("https://github.com/owner/repo-1"), + Description: new("Test repository 1"), + StargazersCount: new(100), }, { - ID: github.Ptr(int64(67890)), - Name: github.Ptr("repo-2"), - FullName: github.Ptr("owner/repo-2"), - HTMLURL: github.Ptr("https://github.com/owner/repo-2"), - Description: github.Ptr("Test repository 2"), - StargazersCount: github.Ptr(50), + ID: new(int64(67890)), + Name: new("repo-2"), + FullName: new("owner/repo-2"), + HTMLURL: new("https://github.com/owner/repo-2"), + Description: new("Test repository 2"), + StargazersCount: new(50), }, }, } @@ -181,18 +181,18 @@ func Test_SearchRepositories_IFC_InsidersMode(t *testing.T) { makeRepo := func(r repoFixture) *github.Repository { return &github.Repository{ - ID: github.Ptr(int64(1)), - Name: github.Ptr(r.name), - FullName: github.Ptr(r.owner + "/" + r.name), - Private: github.Ptr(r.isPrivate), - Owner: &github.User{Login: github.Ptr(r.owner)}, + ID: new(int64(1)), + Name: new(r.name), + FullName: new(r.owner + "/" + r.name), + Private: new(r.isPrivate), + Owner: &github.User{Login: new(r.owner)}, } } makeMockClient := func(repos []repoFixture) *http.Client { searchResult := &github.RepositoriesSearchResult{ - Total: github.Ptr(len(repos)), - IncompleteResults: github.Ptr(false), + Total: new(len(repos)), + IncompleteResults: new(false), } for _, r := range repos { searchResult.Repositories = append(searchResult.Repositories, makeRepo(r)) @@ -280,16 +280,16 @@ func Test_SearchRepositories_IFC_InsidersMode(t *testing.T) { func Test_SearchRepositories_FullOutput(t *testing.T) { mockSearchResult := &github.RepositoriesSearchResult{ - Total: github.Ptr(1), - IncompleteResults: github.Ptr(false), + Total: new(1), + IncompleteResults: new(false), Repositories: []*github.Repository{ { - ID: github.Ptr(int64(12345)), - Name: github.Ptr("test-repo"), - FullName: github.Ptr("owner/test-repo"), - HTMLURL: github.Ptr("https://github.com/owner/test-repo"), - Description: github.Ptr("Test repository"), - StargazersCount: github.Ptr(100), + ID: new(int64(12345)), + Name: new("test-repo"), + FullName: new("owner/test-repo"), + HTMLURL: new("https://github.com/owner/test-repo"), + Description: new("Test repository"), + StargazersCount: new(100), }, }, } @@ -359,30 +359,30 @@ func Test_SearchCode(t *testing.T) { // Setup mock search results mockSearchResult := &github.CodeSearchResult{ - Total: github.Ptr(2), - IncompleteResults: github.Ptr(false), + Total: new(2), + IncompleteResults: new(false), CodeResults: []*github.CodeResult{ { - Name: github.Ptr("file1.go"), - Path: github.Ptr("path/to/file1.go"), - SHA: github.Ptr("abc123def456"), + Name: new("file1.go"), + Path: new("path/to/file1.go"), + SHA: new("abc123def456"), Repository: &github.Repository{ - Name: github.Ptr("repo"), - FullName: github.Ptr("owner/repo"), + Name: new("repo"), + FullName: new("owner/repo"), }, TextMatches: []*github.TextMatch{ { - Fragment: github.Ptr("func main() { fmt.Println(\"hello\") }"), + Fragment: new("func main() { fmt.Println(\"hello\") }"), }, }, }, { - Name: github.Ptr("file2.go"), - Path: github.Ptr("path/to/file2.go"), - SHA: github.Ptr("def456abc123"), + Name: new("file2.go"), + Path: new("path/to/file2.go"), + SHA: new("def456abc123"), Repository: &github.Repository{ - Name: github.Ptr("repo"), - FullName: github.Ptr("owner/repo"), + Name: new("repo"), + FullName: new("owner/repo"), }, }, }, @@ -512,19 +512,19 @@ func Test_SearchCode(t *testing.T) { func Test_SearchCode_FieldFiltering(t *testing.T) { mockSearchResult := &github.CodeSearchResult{ - Total: github.Ptr(1), - IncompleteResults: github.Ptr(false), + Total: new(1), + IncompleteResults: new(false), CodeResults: []*github.CodeResult{ { - Name: github.Ptr("file1.go"), - Path: github.Ptr("path/to/file1.go"), - SHA: github.Ptr("abc123def456"), + Name: new("file1.go"), + Path: new("path/to/file1.go"), + SHA: new("abc123def456"), Repository: &github.Repository{ - Name: github.Ptr("repo"), - FullName: github.Ptr("owner/repo"), + Name: new("repo"), + FullName: new("owner/repo"), }, TextMatches: []*github.TextMatch{ - {Fragment: github.Ptr("func main() {}")}, + {Fragment: new("func main() {}")}, }, }, }, @@ -569,19 +569,19 @@ func Test_SearchCode_FieldFiltering(t *testing.T) { func Test_SearchCode_FieldsTelemetry(t *testing.T) { mockSearchResult := &github.CodeSearchResult{ - Total: github.Ptr(1), - IncompleteResults: github.Ptr(false), + Total: new(1), + IncompleteResults: new(false), CodeResults: []*github.CodeResult{ { - Name: github.Ptr("file1.go"), - Path: github.Ptr("path/to/file1.go"), - SHA: github.Ptr("abc123def456"), + Name: new("file1.go"), + Path: new("path/to/file1.go"), + SHA: new("abc123def456"), Repository: &github.Repository{ - Name: github.Ptr("repo"), - FullName: github.Ptr("owner/repo"), + Name: new("repo"), + FullName: new("owner/repo"), }, TextMatches: []*github.TextMatch{ - {Fragment: github.Ptr("func main() {}")}, + {Fragment: new("func main() {}")}, }, }, }, @@ -656,21 +656,21 @@ func Test_SearchUsers(t *testing.T) { // Setup mock search results mockSearchResult := &github.UsersSearchResult{ - Total: github.Ptr(2), - IncompleteResults: github.Ptr(false), + Total: new(2), + IncompleteResults: new(false), Users: []*github.User{ { - Login: github.Ptr("user1"), - ID: github.Ptr(int64(1001)), - HTMLURL: github.Ptr("https://github.com/user1"), - AvatarURL: github.Ptr("https://avatars.githubusercontent.com/u/1001"), + Login: new("user1"), + ID: new(int64(1001)), + HTMLURL: new("https://github.com/user1"), + AvatarURL: new("https://avatars.githubusercontent.com/u/1001"), }, { - Login: github.Ptr("user2"), - ID: github.Ptr(int64(1002)), - HTMLURL: github.Ptr("https://github.com/user2"), - AvatarURL: github.Ptr("https://avatars.githubusercontent.com/u/1002"), - Type: github.Ptr("User"), + Login: new("user2"), + ID: new(int64(1002)), + HTMLURL: new("https://github.com/user2"), + AvatarURL: new("https://avatars.githubusercontent.com/u/1002"), + Type: new("User"), }, }, } @@ -843,20 +843,20 @@ func Test_SearchOrgs(t *testing.T) { // Setup mock search results mockSearchResult := &github.UsersSearchResult{ - Total: github.Ptr(int(2)), - IncompleteResults: github.Ptr(false), + Total: new(int(2)), + IncompleteResults: new(false), Users: []*github.User{ { - Login: github.Ptr("org-1"), - ID: github.Ptr(int64(111)), - HTMLURL: github.Ptr("https://github.com/org-1"), - AvatarURL: github.Ptr("https://avatars.githubusercontent.com/u/111?v=4"), + Login: new("org-1"), + ID: new(int64(111)), + HTMLURL: new("https://github.com/org-1"), + AvatarURL: new("https://avatars.githubusercontent.com/u/111?v=4"), }, { - Login: github.Ptr("org-2"), - ID: github.Ptr(int64(222)), - HTMLURL: github.Ptr("https://github.com/org-2"), - AvatarURL: github.Ptr("https://avatars.githubusercontent.com/u/222?v=4"), + Login: new("org-2"), + ID: new(int64(222)), + HTMLURL: new("https://github.com/org-2"), + AvatarURL: new("https://avatars.githubusercontent.com/u/222?v=4"), }, }, } @@ -1001,41 +1001,41 @@ func Test_SearchCommits(t *testing.T) { now := time.Now().Truncate(time.Second) mockSearchResult := &github.CommitsSearchResult{ - Total: github.Ptr(2), - IncompleteResults: github.Ptr(false), + Total: new(2), + IncompleteResults: new(false), Commits: []*github.CommitResult{ { - SHA: github.Ptr("abc123commit"), - HTMLURL: github.Ptr("https://github.com/owner/repo/commit/abc123commit"), + SHA: new("abc123commit"), + HTMLURL: new("https://github.com/owner/repo/commit/abc123commit"), Commit: &github.Commit{ - Message: github.Ptr("Initial commit"), + Message: new("Initial commit"), Author: &github.CommitAuthor{ - Name: github.Ptr("Author Name"), - Email: github.Ptr("author@example.com"), + Name: new("Author Name"), + Email: new("author@example.com"), Date: &github.Timestamp{Time: now}, }, }, Author: &github.User{ - Login: github.Ptr("author"), - ID: github.Ptr(int64(1)), - HTMLURL: github.Ptr("https://github.com/author"), + Login: new("author"), + ID: new(int64(1)), + HTMLURL: new("https://github.com/author"), }, Repository: &github.Repository{ - FullName: github.Ptr("owner/repo"), - HTMLURL: github.Ptr("https://github.com/owner/repo"), - Private: github.Ptr(false), + FullName: new("owner/repo"), + HTMLURL: new("https://github.com/owner/repo"), + Private: new(false), }, }, { // Commit with no resolved GitHub user for author or committer // (common when the commit email isn't linked to an account). - SHA: github.Ptr("def456commit"), - HTMLURL: github.Ptr("https://github.com/owner/repo/commit/def456commit"), + SHA: new("def456commit"), + HTMLURL: new("https://github.com/owner/repo/commit/def456commit"), Commit: &github.Commit{ - Message: github.Ptr("Unlinked author"), + Message: new("Unlinked author"), }, Repository: &github.Repository{ - FullName: github.Ptr("owner/repo"), + FullName: new("owner/repo"), }, }, }, diff --git a/pkg/github/search_utils.go b/pkg/github/search_utils.go index 49ec7bbff2..47b8aa26bd 100644 --- a/pkg/github/search_utils.go +++ b/pkg/github/search_utils.go @@ -135,7 +135,7 @@ func prepareSearchArgs(args map[string]any, targetType string, mode searchMode) // field.: qualifiers require the advanced search API. if strings.Contains(query, "field.") { - opts.AdvancedSearch = github.Ptr(true) + opts.AdvancedSearch = new(true) } // Lexical is the API default, so it leaves search_type unset. diff --git a/pkg/github/secret_scanning_test.go b/pkg/github/secret_scanning_test.go index 58b59296b8..6018962d6e 100644 --- a/pkg/github/secret_scanning_test.go +++ b/pkg/github/secret_scanning_test.go @@ -32,9 +32,9 @@ func Test_GetSecretScanningAlert(t *testing.T) { // Setup mock alert for success case mockAlert := &github.SecretScanningAlert{ - Number: github.Ptr(42), - State: github.Ptr("open"), - HTMLURL: github.Ptr("https://github.com/owner/private-repo/security/secret-scanning/42"), + Number: new(42), + State: new("open"), + HTMLURL: new("https://github.com/owner/private-repo/security/secret-scanning/42"), } tests := []struct { @@ -139,18 +139,18 @@ func Test_ListSecretScanningAlerts(t *testing.T) { // Setup mock alerts for success case resolvedAlert := github.SecretScanningAlert{ - Number: github.Ptr(2), - HTMLURL: github.Ptr("https://github.com/owner/private-repo/security/secret-scanning/2"), - State: github.Ptr("resolved"), - Resolution: github.Ptr("false_positive"), - SecretType: github.Ptr("adafruit_io_key"), + Number: new(2), + HTMLURL: new("https://github.com/owner/private-repo/security/secret-scanning/2"), + State: new("resolved"), + Resolution: new("false_positive"), + SecretType: new("adafruit_io_key"), } openAlert := github.SecretScanningAlert{ - Number: github.Ptr(2), - HTMLURL: github.Ptr("https://github.com/owner/private-repo/security/secret-scanning/3"), - State: github.Ptr("open"), - Resolution: github.Ptr("false_positive"), - SecretType: github.Ptr("adafruit_io_key"), + Number: new(2), + HTMLURL: new("https://github.com/owner/private-repo/security/secret-scanning/3"), + State: new("open"), + Resolution: new("false_positive"), + SecretType: new("adafruit_io_key"), } tests := []struct { diff --git a/pkg/github/security_advisories_test.go b/pkg/github/security_advisories_test.go index fc8e660a6f..27dcf7ec0a 100644 --- a/pkg/github/security_advisories_test.go +++ b/pkg/github/security_advisories_test.go @@ -33,10 +33,10 @@ func Test_ListGlobalSecurityAdvisories(t *testing.T) { // Setup mock advisory for success case mockAdvisory := &github.GlobalSecurityAdvisory{ SecurityAdvisory: github.SecurityAdvisory{ - GHSAID: github.Ptr("GHSA-xxxx-xxxx-xxxx"), - Summary: github.Ptr("Test advisory"), - Description: github.Ptr("This is a test advisory."), - Severity: github.Ptr("high"), + GHSAID: new("GHSA-xxxx-xxxx-xxxx"), + Summary: new("Test advisory"), + Description: new("This is a test advisory."), + Severity: new("high"), }, } @@ -146,10 +146,10 @@ func Test_GetGlobalSecurityAdvisory(t *testing.T) { // Setup mock advisory for success case mockAdvisory := &github.GlobalSecurityAdvisory{ SecurityAdvisory: github.SecurityAdvisory{ - GHSAID: github.Ptr("GHSA-xxxx-xxxx-xxxx"), - Summary: github.Ptr("Test advisory"), - Description: github.Ptr("This is a test advisory."), - Severity: github.Ptr("high"), + GHSAID: new("GHSA-xxxx-xxxx-xxxx"), + Summary: new("Test advisory"), + Description: new("This is a test advisory."), + Severity: new("high"), }, } @@ -256,16 +256,16 @@ func Test_ListRepositorySecurityAdvisories(t *testing.T) { // Setup mock advisories for success cases adv1 := &github.SecurityAdvisory{ - GHSAID: github.Ptr("GHSA-1111-1111-1111"), - Summary: github.Ptr("Repo advisory one"), - Description: github.Ptr("First repo advisory."), - Severity: github.Ptr("high"), + GHSAID: new("GHSA-1111-1111-1111"), + Summary: new("Repo advisory one"), + Description: new("First repo advisory."), + Severity: new("high"), } adv2 := &github.SecurityAdvisory{ - GHSAID: github.Ptr("GHSA-2222-2222-2222"), - Summary: github.Ptr("Repo advisory two"), - Description: github.Ptr("Second repo advisory."), - Severity: github.Ptr("medium"), + GHSAID: new("GHSA-2222-2222-2222"), + Summary: new("Repo advisory two"), + Description: new("Second repo advisory."), + Severity: new("medium"), } tests := []struct { @@ -384,14 +384,14 @@ func Test_ListRepositorySecurityAdvisories_IFC_FeatureFlag(t *testing.T) { toolDef := ListRepositorySecurityAdvisories(translations.NullTranslationHelper) publishedAdvisory := &github.SecurityAdvisory{ - GHSAID: github.Ptr("GHSA-1111-1111-1111"), - Summary: github.Ptr("Published advisory"), - State: github.Ptr("published"), + GHSAID: new("GHSA-1111-1111-1111"), + Summary: new("Published advisory"), + State: new("published"), } draftAdvisory := &github.SecurityAdvisory{ - GHSAID: github.Ptr("GHSA-2222-2222-2222"), - Summary: github.Ptr("Draft advisory"), - State: github.Ptr("draft"), + GHSAID: new("GHSA-2222-2222-2222"), + Summary: new("Draft advisory"), + State: new("draft"), } makeMockClient := func(isPrivate bool, advisories []*github.SecurityAdvisory) *http.Client { @@ -515,16 +515,16 @@ func Test_ListOrgRepositorySecurityAdvisories(t *testing.T) { assert.ElementsMatch(t, schema.Required, []string{"org"}) adv1 := &github.SecurityAdvisory{ - GHSAID: github.Ptr("GHSA-aaaa-bbbb-cccc"), - Summary: github.Ptr("Org repo advisory 1"), - Description: github.Ptr("First advisory"), - Severity: github.Ptr("low"), + GHSAID: new("GHSA-aaaa-bbbb-cccc"), + Summary: new("Org repo advisory 1"), + Description: new("First advisory"), + Severity: new("low"), } adv2 := &github.SecurityAdvisory{ - GHSAID: github.Ptr("GHSA-dddd-eeee-ffff"), - Summary: github.Ptr("Org repo advisory 2"), - Description: github.Ptr("Second advisory"), - Severity: github.Ptr("critical"), + GHSAID: new("GHSA-dddd-eeee-ffff"), + Summary: new("Org repo advisory 2"), + Description: new("Second advisory"), + Severity: new("critical"), } tests := []struct { diff --git a/pkg/github/server_test.go b/pkg/github/server_test.go index 54d8bc890f..f629ace35d 100644 --- a/pkg/github/server_test.go +++ b/pkg/github/server_test.go @@ -123,7 +123,7 @@ func mockRESTPermissionServer(t *testing.T, defaultPerm string, overrides map[st } } resp := gogithub.RepositoryPermissionLevel{ - Permission: gogithub.Ptr(perm), + Permission: new(perm), } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(resp) diff --git a/pkg/github/ui_tools_test.go b/pkg/github/ui_tools_test.go index 5f6f1a61fd..4e6aba185f 100644 --- a/pkg/github/ui_tools_test.go +++ b/pkg/github/ui_tools_test.go @@ -116,34 +116,34 @@ func Test_UIGet(t *testing.T) { // Setup mock data mockAssignees := []*github.User{ - {Login: github.Ptr("user1"), AvatarURL: github.Ptr("https://avatars.githubusercontent.com/u/1")}, - {Login: github.Ptr("user2"), AvatarURL: github.Ptr("https://avatars.githubusercontent.com/u/2")}, + {Login: new("user1"), AvatarURL: new("https://avatars.githubusercontent.com/u/1")}, + {Login: new("user2"), AvatarURL: new("https://avatars.githubusercontent.com/u/2")}, } mockBranches := []*github.Branch{ - {Name: github.Ptr("main"), Protected: github.Ptr(true)}, - {Name: github.Ptr("feature"), Protected: github.Ptr(false)}, + {Name: new("main"), Protected: new(true)}, + {Name: new("feature"), Protected: new(false)}, } dueDate := time.Date(2026, 1, 31, 0, 0, 0, 0, time.UTC) mockMilestones := []*github.Milestone{ - {Number: github.Ptr(1), Title: github.Ptr("with due date"), DueOn: &github.Timestamp{Time: dueDate}}, - {Number: github.Ptr(2), Title: github.Ptr("no due date")}, + {Number: new(1), Title: new("with due date"), DueOn: &github.Timestamp{Time: dueDate}}, + {Number: new(2), Title: new("no due date")}, } mockIssueTypes := []*github.IssueType{ - {Name: github.Ptr("Bug")}, - {Name: github.Ptr("Feature")}, + {Name: new("Bug")}, + {Name: new("Feature")}, } mockReviewers := []*github.User{ - {Login: github.Ptr("octocat"), AvatarURL: github.Ptr("https://avatars.githubusercontent.com/u/583231")}, - {Login: github.Ptr("dependabot[bot]"), AvatarURL: github.Ptr("https://avatars.githubusercontent.com/in/29110")}, - {Login: github.Ptr("github-actions"), Type: github.Ptr("Bot")}, + {Login: new("octocat"), AvatarURL: new("https://avatars.githubusercontent.com/u/583231")}, + {Login: new("dependabot[bot]"), AvatarURL: new("https://avatars.githubusercontent.com/in/29110")}, + {Login: new("github-actions"), Type: new("Bot")}, } mockReviewerTeams := []*github.Team{ - {Slug: github.Ptr("docs"), Name: github.Ptr("Docs")}, + {Slug: new("docs"), Name: new("Docs")}, } tests := []struct { @@ -385,7 +385,7 @@ func Test_UIGet(t *testing.T) { { name: "branches pagination stops at the page cap", mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ - "GET /repos/owner/repo/branches": alwaysNextPageHandler(t, []*github.Branch{{Name: github.Ptr("feature")}}), + "GET /repos/owner/repo/branches": alwaysNextPageHandler(t, []*github.Branch{{Name: new("feature")}}), }), requestArgs: map[string]any{ "method": "branches", @@ -427,7 +427,7 @@ func Test_UIGet(t *testing.T) { { name: "reviewers pagination stops at the page cap", mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ - "GET /repos/owner/repo/collaborators": alwaysNextPageHandler(t, []*github.User{{Login: github.Ptr("octocat")}}), + "GET /repos/owner/repo/collaborators": alwaysNextPageHandler(t, []*github.User{{Login: new("octocat")}}), "GET /repos/owner/repo/teams": mockResponse(t, http.StatusOK, mockReviewerTeams), }), requestArgs: map[string]any{ diff --git a/pkg/lockdown/lockdown_test.go b/pkg/lockdown/lockdown_test.go index 22fab53b03..d724a46c4b 100644 --- a/pkg/lockdown/lockdown_test.go +++ b/pkg/lockdown/lockdown_test.go @@ -89,7 +89,7 @@ func newMockGQLClient(viewerLogin string, isPrivate bool) (*githubv4.Client, *co func newMockRESTServer(t *testing.T, permission string) *gogithub.Client { t.Helper() restServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - resp := gogithub.RepositoryPermissionLevel{Permission: gogithub.Ptr(permission)} + resp := gogithub.RepositoryPermissionLevel{Permission: new(permission)} w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(resp) })) diff --git a/ui/package-lock.json b/ui/package-lock.json index 72de424ec9..31562fa821 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -9,13 +9,15 @@ "version": "1.0.0", "dependencies": { "@github/markdown-toolbar-element": "^2.2.3", - "@modelcontextprotocol/ext-apps": "^1.7.5", + "@modelcontextprotocol/client": "^2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.3", "@primer/octicons-react": "^19.38.0", "@primer/react": "^36.27.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-markdown": "^10.1.0", - "remark-gfm": "^4.0.1" + "remark-gfm": "^4.0.1", + "zod": "^4.2.0" }, "devDependencies": { "@types/node": "^26.6.4", @@ -379,19 +381,6 @@ "integrity": "sha512-wUk72T/pA3OG+YCdVaDcwHwpT2uyVmHlu6CjZyeRfoECWNQovFZAMqp8JVHjweeLxp2lM2ASqa8lLrkCskrB2g==", "license": "MIT" }, - "node_modules/@hono/node-server": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.3.tgz", - "integrity": "sha512-TA//nWMqPhbfdfneACk6t5a9eqbS9lABEPyKn0/xZTah3H3U2XaVg85rJFl0/Fyit0I552YDHgXGVSf3GwqbUw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "hono": "^4" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -454,10 +443,40 @@ "integrity": "sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==", "license": "BSD-3-Clause" }, + "node_modules/@modelcontextprotocol/client": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.2.0.tgz", + "integrity": "sha512-LxCou/CSYQ6dwEnjhLZY0KnEuc8V4UJ3IEQCl/uR2yHobSQIEdJKUlKLRYRF5i5FqRGHy1eK7une3aAWDHLtig==", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/core": "2.2.0", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "jose": "^6.1.3", + "pkce-challenge": "^5.0.0", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/core": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.2.0.tgz", + "integrity": "sha512-iLhmprRmWI8EcosOA3wVvww22z02NkgqhV4fBH6f/odQBsi7xnJc0HGmi21yWO+/iKw2yzqVE127Zhna5/+JXw==", + "license": "MIT", + "dependencies": { + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/@modelcontextprotocol/ext-apps": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/ext-apps/-/ext-apps-1.7.5.tgz", - "integrity": "sha512-TjPH2S2y5UEGKhmI6+XGFuqfqOV4ppe1x6DA3txnUaEWkgtA4G5vo14jGKFZmegdkZ1H4QMLyujLvoU1BEdnAg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/ext-apps/-/ext-apps-2.0.3.tgz", + "integrity": "sha512-Tn+4+cyhO4f4cQSDxNyLiIfEE+qlTTuIbWBJB8JTinEXOhDYgCT4wDTQplxD9MBYZH5pCYGC59GaxiwIT4bu8w==", "license": "MIT", "workspaces": [ "examples/*" @@ -469,12 +488,17 @@ "node": ">=20" }, "peerDependencies": { - "@modelcontextprotocol/sdk": "^1.29.0", + "@modelcontextprotocol/client": "^2.0.0", + "@modelcontextprotocol/core": "^2.0.0", + "@modelcontextprotocol/server": "^2.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", - "zod": "^3.25.0 || ^4.0.0" + "zod": "^4.2.0" }, "peerDependenciesMeta": { + "@modelcontextprotocol/server": { + "optional": true + }, "react": { "optional": true }, @@ -483,47 +507,6 @@ } } }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.31.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.31.0.tgz", - "integrity": "sha512-UvTMgnNlnIBO/22ob2RcVGDlcvOslQs8T59+FTGdA0L27a39fdGF/EDETNtDVK4DZGpwomlsYpRdA8UXcVL/pw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@hono/node-server": "^1.19.9 || ^2.0.5", - "ajv": "^8.17.1", - "ajv-formats": "^3.0.1", - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.5", - "eventsource": "^3.0.2", - "eventsource-parser": "^3.0.0", - "express": "^5.2.1", - "express-rate-limit": "^8.2.1", - "hono": "^4.11.4", - "jose": "^6.1.3", - "json-schema-typed": "^8.0.2", - "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.25 || ^4.0", - "zod-to-json-schema": "^3.25.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@cfworker/json-schema": "^4.1.1", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "@cfworker/json-schema": { - "optional": true - }, - "zod": { - "optional": false - } - } - }, "node_modules/@oddbird/popover-polyfill": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/@oddbird/popover-polyfill/-/popover-polyfill-0.3.8.tgz", @@ -2274,55 +2257,6 @@ } } }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "peer": true, - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ajv": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", - "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", @@ -2346,45 +2280,6 @@ "node": ">=6.0.0" } }, - "node_modules/body-parser": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", - "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", - "license": "MIT", - "peer": true, - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^2.0.0", - "debug": "^4.4.3", - "http-errors": "^2.0.1", - "iconv-lite": "^0.7.2", - "on-finished": "^2.4.1", - "qs": "^6.15.2", - "raw-body": "^3.0.2", - "type-is": "^2.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/body-parser/node_modules/content-type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", - "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -2432,47 +2327,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/camelize": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", @@ -2579,30 +2433,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/content-disposition": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", - "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -2610,50 +2440,11 @@ "license": "MIT", "peer": true }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/cors": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", - "license": "MIT", - "peer": true, - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", - "peer": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2730,16 +2521,6 @@ "node": ">=0.10.0" } }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -2781,28 +2562,6 @@ "node": ">=0.3.1" } }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT", - "peer": true - }, "node_modules/electron-to-chromium": { "version": "1.5.444", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.444.tgz", @@ -2810,49 +2569,6 @@ "license": "ISC", "peer": true }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -2863,13 +2579,6 @@ "node": ">=6" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT", - "peer": true - }, "node_modules/escape-string-regexp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", @@ -2892,22 +2601,11 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/eventsource": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", "license": "MIT", - "peer": true, "dependencies": { "eventsource-parser": "^3.0.1" }, @@ -2920,105 +2618,16 @@ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz", "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=18.0.0" } }, - "node_modules/express": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", - "license": "MIT", - "peer": true, - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.1", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "depd": "^2.0.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express-rate-limit": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.0.tgz", - "integrity": "sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==", - "license": "MIT", - "peer": true, - "dependencies": { - "debug": "^4.4.3", - "ip-address": "^10.2.0" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/express-rate-limit" - }, - "peerDependencies": { - "express": ">= 4.11" - } - }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT", - "peer": true - }, - "node_modules/fast-uri": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", - "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause", - "peer": true - }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -3050,54 +2659,12 @@ "node": ">=8" } }, - "node_modules/finalhandler": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", - "license": "MIT", - "peer": true, - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/focus-visible": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/focus-visible/-/focus-visible-5.2.1.tgz", "integrity": "sha512-8Bx950VD1bWTQJEH/AM6SpEk+SU55aVnp4Ujhuuxy3eMEBCRwBnTBnVXr9YAPvZL3/CNjCa8u4IWfNmEO53whA==", "license": "W3C" }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3113,16 +2680,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/fzy.js": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/fzy.js/-/fzy.js-0.4.1.tgz", @@ -3139,58 +2696,6 @@ "node": ">=6.9.0" } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "peer": true, - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -3201,32 +2706,6 @@ "node": ">=4" } }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", - "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "license": "MIT", - "peer": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", @@ -3293,16 +2772,6 @@ "license": "MIT", "peer": true }, - "node_modules/hono": { - "version": "4.13.12", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.12.tgz", - "integrity": "sha512-6E2QDAc9Ick9Sq77ZrGS/dk2WUYni91aufTw6LJKpV7w8kW5/GxVUc650FOADOmlwg3K+f7Pun6XlV+pYmW6gw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=16.9.0" - } - }, "node_modules/html-url-attributes": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", @@ -3313,77 +2782,12 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/iconv-lite": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", - "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC", - "peer": true - }, "node_modules/inline-style-parser": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", "license": "MIT" }, - "node_modules/ip-address": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.3.tgz", - "integrity": "sha512-A1kdq/tSb5QjvKvAMgIoEvDBIgL7qaqVP/jkvSwYYRZ9iEzvPpopxp2wQfu3SuZRHtpHNxMn8Fs0bS+gf5Xmwg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/is-alphabetical": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", @@ -3473,26 +2877,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT", - "peer": true - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/jose": { "version": "6.2.12", "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.12.tgz", "integrity": "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/panva" } @@ -3516,20 +2911,6 @@ "node": ">=6" } }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT", - "peer": true - }, - "node_modules/json-schema-typed": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", - "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", - "license": "BSD-2-Clause", - "peer": true - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -3867,16 +3248,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/mdast-util-definitions": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", @@ -4221,33 +3592,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/media-typer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", - "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/micromark": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.3.tgz", @@ -4859,33 +4203,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", - "peer": true, - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -4920,37 +4237,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/negotiator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", - "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", - "license": "MIT", - "peer": true, - "dependencies": { - "content-type": "^2.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/negotiator/node_modules/content-type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", - "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/node-releases": { "version": "2.0.57", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.57.tgz", @@ -4970,42 +4256,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "peer": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "peer": true, - "dependencies": { - "wrappy": "1" - } - }, "node_modules/parse-entities": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", @@ -5031,37 +4281,15 @@ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "license": "MIT" }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } }, - "node_modules/path-to-regexp": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", - "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", - "license": "MIT", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5085,7 +4313,6 @@ "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=16.20.0" } @@ -5153,71 +4380,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/proxy-addr": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", - "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/qs": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", - "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "es-define-property": "^1.0.1", - "side-channel": "^1.1.1" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", - "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/raw-body": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", - "license": "MIT", - "peer": true, - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.7.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -5358,16 +4520,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/rolldown": { "version": "1.2.12", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.12.tgz", @@ -5402,23 +4554,6 @@ "@rolldown/binding-win32-x64-msvc": "1.2.12" } }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, "node_modules/sade": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", @@ -5431,13 +4566,6 @@ "node": ">=6" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT", - "peer": true - }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -5457,60 +4585,6 @@ "semver": "bin/semver.js" } }, - "node_modules/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "debug": "^4.4.3", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.1", - "mime-types": "^3.0.2", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/serve-static": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", - "license": "MIT", - "peer": true, - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC", - "peer": true - }, "node_modules/shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", @@ -5523,7 +4597,6 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", - "peer": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -5536,87 +4609,10 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } }, - "node_modules/side-channel": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", - "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4", - "side-channel-list": "^1.0.1", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/source-map-js": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", @@ -5637,16 +4633,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/stringify-entities": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", @@ -5791,16 +4777,6 @@ "node": ">=8.0" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.6" - } - }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -5821,39 +4797,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/type-is": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", - "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", - "license": "MIT", - "peer": true, - "dependencies": { - "content-type": "^2.0.0", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/type-is/node_modules/content-type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", - "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/typescript": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", @@ -5993,16 +4936,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/update-browserslist-db": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz", @@ -6052,16 +4985,6 @@ "node": ">=8" } }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -6195,7 +5118,6 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "license": "ISC", - "peer": true, "dependencies": { "isexe": "^2.0.0" }, @@ -6206,13 +5128,6 @@ "node": ">= 8" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC", - "peer": true - }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -6225,21 +5140,10 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz", "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/zod-to-json-schema": { - "version": "3.25.2", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", - "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", - "license": "ISC", - "peer": true, - "peerDependencies": { - "zod": "^3.25.28 || ^4" - } - }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/ui/package.json b/ui/package.json index bd8097d0e5..efbd84bb16 100644 --- a/ui/package.json +++ b/ui/package.json @@ -15,13 +15,15 @@ }, "dependencies": { "@github/markdown-toolbar-element": "^2.2.3", - "@modelcontextprotocol/ext-apps": "^1.7.5", + "@modelcontextprotocol/client": "^2.0.0", + "@modelcontextprotocol/ext-apps": "^2.0.3", "@primer/octicons-react": "^19.38.0", "@primer/react": "^36.27.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-markdown": "^10.1.0", - "remark-gfm": "^4.0.1" + "remark-gfm": "^4.0.1", + "zod": "^4.2.0" }, "devDependencies": { "@types/node": "^26.6.4", diff --git a/ui/src/hooks/useMcpApp.ts b/ui/src/hooks/useMcpApp.ts index e4146a234f..f45276abf0 100644 --- a/ui/src/hooks/useMcpApp.ts +++ b/ui/src/hooks/useMcpApp.ts @@ -5,7 +5,7 @@ import type { McpUiHostContext, McpUiUpdateModelContextRequest, } from "@modelcontextprotocol/ext-apps"; -import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import type { CallToolResult } from "@modelcontextprotocol/client"; import { useState, useCallback, useEffect } from "react"; interface UseMcpAppOptions { diff --git a/ui/src/lib/toolResult.ts b/ui/src/lib/toolResult.ts index e985537f49..cb39015754 100644 --- a/ui/src/lib/toolResult.ts +++ b/ui/src/lib/toolResult.ts @@ -1,4 +1,4 @@ -import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import type { CallToolResult } from "@modelcontextprotocol/client"; /** * Returns the parsed JSON payload of a *completed* tool result, or `null` when From 36e5c48f2e376f8f4ffa30fd718c38deedb7ae27 Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Fri, 2 Oct 2026 13:22:57 +0200 Subject: [PATCH 11/13] ci: migrate cosign to v3 and dual-publish image signatures (#3382) * ci: migrate cosign to v3 with legacy signature compatibility Keep legacy image payloads, .sig tags, and Rekor v1 service defaults. Document exact cosign v2/v3 image verification and unchanged release archive attestations. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * ci: dual-publish legacy and native cosign image signatures Sign the same immutable digest in separate fail-fast steps. Retain legacy signatures and publish native bundles with v3 service defaults. Remove the verification documentation introduced by this PR as requested; keep compatibility evidence and rollout plans in the PR description. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/docker-publish.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index db5e847a5a..a810a716ca 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -48,7 +48,7 @@ jobs: if: github.event_name != 'pull_request' uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 #v4.1.2 with: - cosign-release: "v2.6.5" + cosign-release: "v3.1.3" # Set up BuildKit Docker container builder to be able to build # multi-platform images and export cache @@ -122,16 +122,20 @@ jobs: oauth_client_secret=${{ secrets.OAUTH_CLIENT_SECRET }} # Sign the resulting Docker image digest except on PRs. - # This will only write to the public Rekor transparency log when the Docker - # repository is public to avoid leaking data. If you would like to publish - # transparency data even for private images, pass --force to cosign below. + # Publish both v2-compatible .sig tags and native Sigstore bundles. + # --yes also consents to publishing signing metadata to the public log. # https://github.com/sigstore/cosign - - name: Sign the published Docker image + - name: Sign the published Docker image (legacy) if: ${{ github.event_name != 'pull_request' }} env: # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable - TAGS: ${{ steps.meta.outputs.tags }} DIGEST: ${{ steps.build-and-push.outputs.digest }} # This step uses the identity token to provision an ephemeral certificate # against the sigstore community Fulcio instance. - run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} + run: cosign sign --yes --new-bundle-format=false --use-signing-config=false --registry-referrers-mode=legacy "${REGISTRY}/${IMAGE_NAME}@${DIGEST}" + + - name: Sign the published Docker image (native bundle) + if: ${{ github.event_name != 'pull_request' }} + env: + DIGEST: ${{ steps.build-and-push.outputs.digest }} + run: cosign sign --yes "${REGISTRY}/${IMAGE_NAME}@${DIGEST}" From f10e4e1f923d46b86f2e80e849aa74084c847184 Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Fri, 2 Oct 2026 16:31:11 +0200 Subject: [PATCH 12/13] build(ui): migrate to React 19 and Primer React 38 (#3383) * build(ui): migrate to React 19 and Primer React 38 Replace removed Box and sx APIs with native styles and CSS Modules, load Primer CSS tokens, use the CSS-based theme provider and Banner, and scope custom-element JSX types to the React runtime. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(ui): use CSS token for reviewer label backgrounds Replace leftover canvas.inset theme paths with the CSS custom property in PR creation and editing. Keep the reviewed native gaps unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- CONTRIBUTING.md | 8 + ui/package-lock.json | 2234 +++----------------------- ui/package.json | 11 +- ui/src/apps/get-me/App.tsx | 131 +- ui/src/apps/issue-write/App.tsx | 526 +++--- ui/src/apps/pr-edit/App.tsx | 382 +++-- ui/src/apps/pr-write/App.tsx | 400 +++-- ui/src/components/AppProvider.tsx | 10 +- ui/src/components/FeedbackFooter.tsx | 18 +- ui/src/components/MarkdownEditor.tsx | 227 ++- ui/src/styles.module.css | 148 ++ 11 files changed, 1434 insertions(+), 2661 deletions(-) create mode 100644 ui/src/styles.module.css diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7ec4f136a8..24f93af022 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,6 +40,14 @@ Type checking uses the native TypeScript 7 compiler through `tsc`; Vite handles transpilation and bundling separately. No TypeScript compiler API integration, typescript-eslint, or ts-node is required. +### MCP Apps UI + +The `ui/` views use React 19 and Primer React 38. With Node.js 20.19+ or 22.12+, run `cd ui && npm ci && npm run typecheck && npm run build && npm audit` before `script/test`. The build writes self-contained HTML to `pkg/github/ui_dist/`, which the Go server embeds; these generated files are not committed. + +Primer 38 no longer exports `Box` or accepts `sx`/styled-system props. Use semantic HTML, native `style` props for dynamic/layout styles, and CSS Modules for nested selectors. `AppProvider` loads Primer's primitive tokens and light/dark themes, so use CSS variables rather than JavaScript theme values. Custom element typings must augment `react/jsx-runtime` rather than the global `JSX` namespace. + +For UI dependency upgrades, compare all four views (`get-me`, `issue-write`, `pr-write`, and `pr-edit`) in light/dark themes and at narrow widths in an MCP Apps host, including menus, Markdown editing/preview, and completed-result views. Include before/after screenshots in the pull request. + ## Submitting a pull request 1. [Fork][fork] and clone the repository diff --git a/ui/package-lock.json b/ui/package-lock.json index 31562fa821..4211483e2b 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -12,17 +12,18 @@ "@modelcontextprotocol/client": "^2.0.0", "@modelcontextprotocol/ext-apps": "^2.0.3", "@primer/octicons-react": "^19.38.0", - "@primer/react": "^36.27.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "@primer/primitives": "^11.10.0", + "@primer/react": "^38.40.1", + "react": "^19.3.0", + "react-dom": "^19.3.0", "react-markdown": "^10.1.0", "remark-gfm": "^4.0.1", "zod": "^4.2.0" }, "devDependencies": { "@types/node": "^26.6.4", - "@types/react": "^18.3.31", - "@types/react-dom": "^18.3.7", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", "@vitejs/plugin-react": "^6.1.1", "typescript": "^7.0.2", "vite": "^8.3.2", @@ -32,237 +33,6 @@ "node": "^26.0.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", - "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", - "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helpers": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/template": "^7.29.7", - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", - "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/parser": "^7.29.8", - "@babel/types": "^7.29.8", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", - "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", - "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/compat-data": "^7.29.7", - "@babel/helper-validator-option": "^7.29.7", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", - "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", - "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", - "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-module-imports": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7", - "@babel/traverse": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", - "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", - "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", - "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", - "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/types": "^7.29.8" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", - "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/runtime": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", @@ -272,165 +42,24 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/template": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", - "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", - "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.8", - "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.8", - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.8", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", - "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-string-parser": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@emotion/is-prop-valid": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", - "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@emotion/memoize": "^0.9.0" - } - }, - "node_modules/@emotion/memoize": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", - "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@emotion/stylis": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", - "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@emotion/unitless": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", - "license": "MIT", - "peer": true - }, - "node_modules/@github/combobox-nav": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@github/combobox-nav/-/combobox-nav-2.3.1.tgz", - "integrity": "sha512-gwxPzLw8XKecy1nP63i9lOBritS3bWmxl02UX6G0TwMQZbMem1BCS1tEZgYd3mkrkiDrUMWaX+DbFCuDFo3K+A==", - "license": "MIT" - }, "node_modules/@github/markdown-toolbar-element": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@github/markdown-toolbar-element/-/markdown-toolbar-element-2.2.3.tgz", "integrity": "sha512-AlquKGee+IWiAMYVB0xyHFZRMnu4n3X4HTvJHu79GiVJ1ojTukCWyxMlF5NMsecoLcBKsuBhx3QPv2vkE/zQ0A==", "license": "MIT" }, - "node_modules/@github/paste-markdown": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@github/paste-markdown/-/paste-markdown-1.5.3.tgz", - "integrity": "sha512-PzZ1b3PaqBzYqbT4fwKEhiORf38h2OcGp2+JdXNNM7inZ7egaSmfmhyNkQILpqWfS0AYtRS3CDq6z03eZ8yOMQ==", + "node_modules/@github/mini-throttle": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@github/mini-throttle/-/mini-throttle-2.1.1.tgz", + "integrity": "sha512-KtOPaB+FiKJ6jcKm9UKyaM5fPURHGf+xcp+b4Mzoi81hOc6M1sIGpMZMAVbNzfa2lW5+RPGKq888Px0j76OZ/A==", "license": "MIT" }, "node_modules/@github/relative-time-element": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@github/relative-time-element/-/relative-time-element-4.5.1.tgz", - "integrity": "sha512-uxCxCwe9vdwUDmRmM84tN0UERlj8MosLV44+r/VDj7DZUVUSTP4vyWlE9mRK6vHelOmT8DS3RMlaMrLlg1h1PQ==", - "license": "MIT" - }, - "node_modules/@github/tab-container-element": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@github/tab-container-element/-/tab-container-element-4.9.0.tgz", - "integrity": "sha512-wUk72T/pA3OG+YCdVaDcwHwpT2uyVmHlu6CjZyeRfoECWNQovFZAMqp8JVHjweeLxp2lM2ASqa8lLrkCskrB2g==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@github/relative-time-element/-/relative-time-element-5.3.1.tgz", + "integrity": "sha512-f++QCIXxGH7rMbpTASRfLyCyPydd4HqwOmATBb/sAUCq72HEZN0MX9znmJSp7NKYxuQxF8j0Mv3ez9P32c70vw==", "license": "MIT" }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", - "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", - "license": "MIT", - "peer": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@lit-labs/react": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@lit-labs/react/-/react-1.2.1.tgz", @@ -439,922 +68,171 @@ }, "node_modules/@lit-labs/ssr-dom-shim": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.6.0.tgz", - "integrity": "sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@modelcontextprotocol/client": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.2.0.tgz", - "integrity": "sha512-LxCou/CSYQ6dwEnjhLZY0KnEuc8V4UJ3IEQCl/uR2yHobSQIEdJKUlKLRYRF5i5FqRGHy1eK7une3aAWDHLtig==", - "license": "MIT", - "dependencies": { - "@modelcontextprotocol/core": "2.2.0", - "cross-spawn": "^7.0.5", - "eventsource": "^3.0.2", - "eventsource-parser": "^3.0.0", - "jose": "^6.1.3", - "pkce-challenge": "^5.0.0", - "zod": "^4.2.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@modelcontextprotocol/core": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.2.0.tgz", - "integrity": "sha512-iLhmprRmWI8EcosOA3wVvww22z02NkgqhV4fBH6f/odQBsi7xnJc0HGmi21yWO+/iKw2yzqVE127Zhna5/+JXw==", - "license": "MIT", - "dependencies": { - "zod": "^4.2.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@modelcontextprotocol/ext-apps": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/ext-apps/-/ext-apps-2.0.3.tgz", - "integrity": "sha512-Tn+4+cyhO4f4cQSDxNyLiIfEE+qlTTuIbWBJB8JTinEXOhDYgCT4wDTQplxD9MBYZH5pCYGC59GaxiwIT4bu8w==", - "license": "MIT", - "workspaces": [ - "examples/*" - ], - "dependencies": { - "@standard-schema/spec": "^1.1.0" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@modelcontextprotocol/client": "^2.0.0", - "@modelcontextprotocol/core": "^2.0.0", - "@modelcontextprotocol/server": "^2.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", - "zod": "^4.2.0" - }, - "peerDependenciesMeta": { - "@modelcontextprotocol/server": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, - "node_modules/@oddbird/popover-polyfill": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@oddbird/popover-polyfill/-/popover-polyfill-0.3.8.tgz", - "integrity": "sha512-+aK7EHL3VggfsWGVqUwvtli2+kP5OWyseAsrefhzR2XWoi2oALUCeoDn63i5WS3ZOmLiXHRNBwHPeta8w+aM1g==", - "license": "BSD-3-Clause" - }, - "node_modules/@oxc-project/types": { - "version": "0.152.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.152.0.tgz", - "integrity": "sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/oxc-project" - } - }, - "node_modules/@primer/behaviors": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@primer/behaviors/-/behaviors-1.11.0.tgz", - "integrity": "sha512-kDHtaw9kfCjPVT61eM731ftckhGyWiPX+OKmUccLN9sMFt9E6FXmsPAbny7O0RBxeW1Xj/NHNdbMEjYEg2dBYg==", - "license": "MIT" - }, - "node_modules/@primer/live-region-element": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@primer/live-region-element/-/live-region-element-0.7.2.tgz", - "integrity": "sha512-wdxCHfcJzE1IPPjZNFR4RTwRcSWb7TN0fRdMH5HcxphLEnuZBWy0TAxk3xPA+/6lwiN3uEJ+ZWV4UF/glXh43A==", - "license": "MIT", - "dependencies": { - "@lit-labs/ssr-dom-shim": "^1.2.0" - } - }, - "node_modules/@primer/octicons-react": { - "version": "19.38.0", - "resolved": "https://registry.npmjs.org/@primer/octicons-react/-/octicons-react-19.38.0.tgz", - "integrity": "sha512-ZzWVrX6kluBaCepsDj/jAxagBOMaRX18LEf723IKpr4E/jdkTdCOtlsN+60Oj7IV9YEVrlUpo9Oky0Tn1heSCA==", - "license": "MIT", - "peerDependencies": { - "react": ">=16.3" - } - }, - "node_modules/@primer/primitives": { - "version": "7.17.1", - "resolved": "https://registry.npmjs.org/@primer/primitives/-/primitives-7.17.1.tgz", - "integrity": "sha512-SiPzEb+up1nDpV2NGwNiY8m6sGnF3OUqRb0has5s6T40vq6Li/g3cYVgl+oolEa4DUoNygEPs09jwJt24f/3zg==", - "license": "MIT" - }, - "node_modules/@primer/react": { - "version": "36.27.0", - "resolved": "https://registry.npmjs.org/@primer/react/-/react-36.27.0.tgz", - "integrity": "sha512-dVyp0f9zbbQYQZ6ztfMET43vVaWhvSz+qWirBzpRjDxvCk8vCQsvWrVGUU/PR0kAxxDHf6hqeLG7vcDL229NLA==", - "license": "MIT", - "dependencies": { - "@github/combobox-nav": "^2.1.5", - "@github/markdown-toolbar-element": "^2.1.0", - "@github/paste-markdown": "^1.4.0", - "@github/relative-time-element": "^4.4.1", - "@github/tab-container-element": "^4.8.0", - "@lit-labs/react": "1.2.1", - "@oddbird/popover-polyfill": "^0.3.1", - "@primer/behaviors": "^1.7.0", - "@primer/live-region-element": "^0.7.0", - "@primer/octicons-react": "^19.9.0", - "@primer/primitives": "^7.16.0", - "@styled-system/css": "^5.1.5", - "@styled-system/props": "^5.1.5", - "@styled-system/theme-get": "^5.1.2", - "@types/react-is": "^18.2.1", - "@types/styled-system": "^5.1.12", - "@types/styled-system__css": "^5.0.16", - "@types/styled-system__theme-get": "^5.0.1", - "clsx": "^1.2.1", - "color2k": "^2.0.3", - "deepmerge": "^4.2.2", - "focus-visible": "^5.2.0", - "fzy.js": "^0.4.1", - "history": "^5.0.0", - "lodash.isempty": "^4.4.0", - "lodash.isobject": "^3.0.2", - "react-intersection-observer": "^9.4.3", - "react-is": "^18.2.0", - "react-markdown": "8.0.7", - "styled-system": "^5.1.5" - }, - "engines": { - "node": ">=12", - "npm": ">=7" - }, - "peerDependencies": { - "@types/react": "^18.0.0", - "@types/react-dom": "^18.0.0", - "@types/styled-components": "^5.1.11", - "react": "^18.0.0", - "react-dom": "^18.0.0", - "styled-components": "5.x" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - }, - "@types/styled-components": { - "optional": true - } - } - }, - "node_modules/@primer/react/node_modules/@types/hast": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", - "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2" - } - }, - "node_modules/@primer/react/node_modules/@types/mdast": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", - "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2" - } - }, - "node_modules/@primer/react/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/@primer/react/node_modules/hast-util-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", - "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@primer/react/node_modules/inline-style-parser": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", - "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", - "license": "MIT" - }, - "node_modules/@primer/react/node_modules/mdast-util-from-markdown": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", - "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "mdast-util-to-string": "^3.1.0", - "micromark": "^3.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-decode-string": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "unist-util-stringify-position": "^3.0.0", - "uvu": "^0.5.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@primer/react/node_modules/mdast-util-to-hast": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", - "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^2.0.0", - "@types/mdast": "^3.0.0", - "mdast-util-definitions": "^5.0.0", - "micromark-util-sanitize-uri": "^1.1.0", - "trim-lines": "^3.0.0", - "unist-util-generated": "^2.0.0", - "unist-util-position": "^4.0.0", - "unist-util-visit": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@primer/react/node_modules/mdast-util-to-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", - "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@primer/react/node_modules/micromark": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", - "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "micromark-core-commonmark": "^1.0.1", - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-chunked": "^1.0.0", - "micromark-util-combine-extensions": "^1.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-encode": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-resolve-all": "^1.0.0", - "micromark-util-sanitize-uri": "^1.0.0", - "micromark-util-subtokenize": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.1", - "uvu": "^0.5.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-core-commonmark": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", - "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-factory-destination": "^1.0.0", - "micromark-factory-label": "^1.0.0", - "micromark-factory-space": "^1.0.0", - "micromark-factory-title": "^1.0.0", - "micromark-factory-whitespace": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-chunked": "^1.0.0", - "micromark-util-classify-character": "^1.0.0", - "micromark-util-html-tag-name": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-resolve-all": "^1.0.0", - "micromark-util-subtokenize": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.1", - "uvu": "^0.5.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-factory-destination": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", - "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-factory-label": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", - "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-factory-space": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", - "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-factory-title": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", - "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-factory-whitespace": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", - "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-util-character": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", - "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-util-chunked": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", - "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-util-classify-character": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", - "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-util-combine-extensions": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", - "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-util-decode-numeric-character-reference": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", - "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-util-decode-string": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", - "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-util-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", - "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/@primer/react/node_modules/micromark-util-html-tag-name": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", - "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/@primer/react/node_modules/micromark-util-normalize-identifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", - "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-util-resolve-all": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", - "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-util-sanitize-uri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", - "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-encode": "^1.0.0", - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-util-subtokenize": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", - "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - } - }, - "node_modules/@primer/react/node_modules/micromark-util-symbol": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", - "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/@primer/react/node_modules/micromark-util-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", - "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/@primer/react/node_modules/property-information": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/@primer/react/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, - "node_modules/@primer/react/node_modules/react-markdown": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz", - "integrity": "sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^2.0.0", - "@types/prop-types": "^15.0.0", - "@types/unist": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^2.0.0", - "prop-types": "^15.0.0", - "property-information": "^6.0.0", - "react-is": "^18.0.0", - "remark-parse": "^10.0.0", - "remark-rehype": "^10.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-object": "^0.4.0", - "unified": "^10.0.0", - "unist-util-visit": "^4.0.0", - "vfile": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=16", - "react": ">=16" - } - }, - "node_modules/@primer/react/node_modules/remark-parse": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", - "integrity": "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-from-markdown": "^1.0.0", - "unified": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@primer/react/node_modules/remark-rehype": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-10.1.0.tgz", - "integrity": "sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^2.0.0", - "@types/mdast": "^3.0.0", - "mdast-util-to-hast": "^12.1.0", - "unified": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@primer/react/node_modules/style-to-object": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", - "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.1.1" - } - }, - "node_modules/@primer/react/node_modules/unified": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", - "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "bail": "^2.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.6.0.tgz", + "integrity": "sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==", + "license": "BSD-3-Clause" }, - "node_modules/@primer/react/node_modules/unist-util-is": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", - "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "node_modules/@modelcontextprotocol/client": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.2.0.tgz", + "integrity": "sha512-LxCou/CSYQ6dwEnjhLZY0KnEuc8V4UJ3IEQCl/uR2yHobSQIEdJKUlKLRYRF5i5FqRGHy1eK7une3aAWDHLtig==", "license": "MIT", "dependencies": { - "@types/unist": "^2.0.0" + "@modelcontextprotocol/core": "2.2.0", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "jose": "^6.1.3", + "pkce-challenge": "^5.0.0", + "zod": "^4.2.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=20" } }, - "node_modules/@primer/react/node_modules/unist-util-position": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", - "integrity": "sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==", + "node_modules/@modelcontextprotocol/core": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.2.0.tgz", + "integrity": "sha512-iLhmprRmWI8EcosOA3wVvww22z02NkgqhV4fBH6f/odQBsi7xnJc0HGmi21yWO+/iKw2yzqVE127Zhna5/+JXw==", "license": "MIT", "dependencies": { - "@types/unist": "^2.0.0" + "zod": "^4.2.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=20" } }, - "node_modules/@primer/react/node_modules/unist-util-stringify-position": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", - "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "node_modules/@modelcontextprotocol/ext-apps": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/ext-apps/-/ext-apps-2.0.3.tgz", + "integrity": "sha512-Tn+4+cyhO4f4cQSDxNyLiIfEE+qlTTuIbWBJB8JTinEXOhDYgCT4wDTQplxD9MBYZH5pCYGC59GaxiwIT4bu8w==", "license": "MIT", + "workspaces": [ + "examples/*" + ], "dependencies": { - "@types/unist": "^2.0.0" + "@standard-schema/spec": "^1.1.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@modelcontextprotocol/client": "^2.0.0", + "@modelcontextprotocol/core": "^2.0.0", + "@modelcontextprotocol/server": "^2.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", + "zod": "^4.2.0" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/server": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } } }, - "node_modules/@primer/react/node_modules/unist-util-visit": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", - "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "node_modules/@oddbird/popover-polyfill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@oddbird/popover-polyfill/-/popover-polyfill-0.5.2.tgz", + "integrity": "sha512-iFrvar5SOMtKFOSjYvs4z9UlLqDdJbMx0mgISLcPedv+g0ac5sgeETLGtipHCVIae6HJPclNEH5aCyD1RZaEHw==", + "license": "BSD-3-Clause" + }, + "node_modules/@oxc-project/types": { + "version": "0.152.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.152.0.tgz", + "integrity": "sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==", + "dev": true, "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.1.1" - }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/oxc-project" } }, - "node_modules/@primer/react/node_modules/unist-util-visit-parents": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", - "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "node_modules/@primer/behaviors": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@primer/behaviors/-/behaviors-1.11.0.tgz", + "integrity": "sha512-kDHtaw9kfCjPVT61eM731ftckhGyWiPX+OKmUccLN9sMFt9E6FXmsPAbny7O0RBxeW1Xj/NHNdbMEjYEg2dBYg==", + "license": "MIT" + }, + "node_modules/@primer/live-region-element": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@primer/live-region-element/-/live-region-element-0.8.1.tgz", + "integrity": "sha512-g7bNs9R2A2LfETMOWYsvtT0b5Q/QLM1Fz1vrTcWa5r2LbEhrPm7cZV5pEOleFKiGybrYWmdXteV2sBAQLXuxvg==", "license": "MIT", "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "@lit-labs/ssr-dom-shim": "^1.6.0" } }, - "node_modules/@primer/react/node_modules/vfile": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", - "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "node_modules/@primer/octicons-react": { + "version": "19.38.0", + "resolved": "https://registry.npmjs.org/@primer/octicons-react/-/octicons-react-19.38.0.tgz", + "integrity": "sha512-ZzWVrX6kluBaCepsDj/jAxagBOMaRX18LEf723IKpr4E/jdkTdCOtlsN+60Oj7IV9YEVrlUpo9Oky0Tn1heSCA==", "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^3.0.0", - "vfile-message": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "peerDependencies": { + "react": ">=16.3" } }, - "node_modules/@primer/react/node_modules/vfile-message": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", - "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "node_modules/@primer/primitives": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/@primer/primitives/-/primitives-11.10.0.tgz", + "integrity": "sha512-ihZxIst4Izry284StKFjpbYvX6R/IBFTLu+qiuAmR93PsKF2hdjoabhSYwKI8iCxOUx94jVP55bI3NoY7STjYQ==", + "license": "MIT", + "workspaces": [ + ".", + "docs/storybook" + ] + }, + "node_modules/@primer/react": { + "version": "38.40.1", + "resolved": "https://registry.npmjs.org/@primer/react/-/react-38.40.1.tgz", + "integrity": "sha512-yTd+QTtWXdqJbWQLCnLibwh280j2TKw1+tREEXtJR6lRYiqrE1dnu5ifCsKhsqIYvxqaKZBNWg4KRqiFG6opZA==", "license": "MIT", "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^3.0.0" + "@github/mini-throttle": "^2.1.1", + "@github/relative-time-element": "^5.0.0", + "@lit-labs/react": "1.2.1", + "@oddbird/popover-polyfill": "^0.5.2", + "@primer/behaviors": "^1.10.3", + "@primer/live-region-element": "^0.8.0", + "@primer/octicons-react": "^19.28.1", + "@primer/primitives": "10.x || 11.x", + "@tanstack/react-virtual": "^3.13.18", + "clsx": "^2.1.1", + "color2k": "^2.0.3", + "deepmerge": "^4.3.1", + "focus-visible": "^5.2.1", + "history": "^5.0.0", + "hsluv": "1.0.1", + "lodash.isempty": "^4.4.0", + "lodash.isobject": "^3.0.2", + "react-intersection-observer": "^10.0.3" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=12", + "npm": ">=7" + }, + "peerDependencies": { + "@types/react": "18.x || 19.x", + "@types/react-dom": "18.x || 19.x", + "@types/react-is": "18.x || 19.x", + "react": "18.x || 19.x", + "react-dom": "18.x || 19.x", + "react-is": "18.x || 19.x" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "@types/react-is": { + "optional": true + } } }, "node_modules/@rolldown/binding-android-arm-eabi": { @@ -1625,137 +503,31 @@ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "license": "MIT" }, - "node_modules/@styled-system/background": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/background/-/background-5.1.2.tgz", - "integrity": "sha512-jtwH2C/U6ssuGSvwTN3ri/IyjdHb8W9X/g8Y0JLcrH02G+BW3OS8kZdHphF1/YyRklnrKrBT2ngwGUK6aqqV3A==", - "license": "MIT", - "dependencies": { - "@styled-system/core": "^5.1.2" - } - }, - "node_modules/@styled-system/border": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@styled-system/border/-/border-5.1.5.tgz", - "integrity": "sha512-JvddhNrnhGigtzWRCVuAHepniyVi6hBlimxWDVAdcTuk7aRn9BYJUwfHslURtwYFsF5FoEs8Zmr1oZq2M1AP0A==", - "license": "MIT", - "dependencies": { - "@styled-system/core": "^5.1.2" - } - }, - "node_modules/@styled-system/color": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/color/-/color-5.1.2.tgz", - "integrity": "sha512-1kCkeKDZkt4GYkuFNKc7vJQMcOmTl3bJY3YBUs7fCNM6mMYJeT1pViQ2LwBSBJytj3AB0o4IdLBoepgSgGl5MA==", - "license": "MIT", - "dependencies": { - "@styled-system/core": "^5.1.2" - } - }, - "node_modules/@styled-system/core": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/core/-/core-5.1.2.tgz", - "integrity": "sha512-XclBDdNIy7OPOsN4HBsawG2eiWfCcuFt6gxKn1x4QfMIgeO6TOlA2pZZ5GWZtIhCUqEPTgIBta6JXsGyCkLBYw==", - "license": "MIT", - "dependencies": { - "object-assign": "^4.1.1" - } - }, - "node_modules/@styled-system/css": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@styled-system/css/-/css-5.1.5.tgz", - "integrity": "sha512-XkORZdS5kypzcBotAMPBoeckDs9aSZVkvrAlq5K3xP8IMAUek+x2O4NtwoSgkYkWWzVBu6DGdFZLR790QWGG+A==", - "license": "MIT" - }, - "node_modules/@styled-system/flexbox": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/flexbox/-/flexbox-5.1.2.tgz", - "integrity": "sha512-6hHV52+eUk654Y1J2v77B8iLeBNtc+SA3R4necsu2VVinSD7+XY5PCCEzBFaWs42dtOEDIa2lMrgL0YBC01mDQ==", - "license": "MIT", - "dependencies": { - "@styled-system/core": "^5.1.2" - } - }, - "node_modules/@styled-system/grid": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/grid/-/grid-5.1.2.tgz", - "integrity": "sha512-K3YiV1KyHHzgdNuNlaw8oW2ktMuGga99o1e/NAfTEi5Zsa7JXxzwEnVSDSBdJC+z6R8WYTCYRQC6bkVFcvdTeg==", - "license": "MIT", - "dependencies": { - "@styled-system/core": "^5.1.2" - } - }, - "node_modules/@styled-system/layout": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/layout/-/layout-5.1.2.tgz", - "integrity": "sha512-wUhkMBqSeacPFhoE9S6UF3fsMEKFv91gF4AdDWp0Aym1yeMPpqz9l9qS/6vjSsDPF7zOb5cOKC3tcKKOMuDCPw==", - "license": "MIT", - "dependencies": { - "@styled-system/core": "^5.1.2" - } - }, - "node_modules/@styled-system/position": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/position/-/position-5.1.2.tgz", - "integrity": "sha512-60IZfMXEOOZe3l1mCu6sj/2NAyUmES2kR9Kzp7s2D3P4qKsZWxD1Se1+wJvevb+1TP+ZMkGPEYYXRyU8M1aF5A==", - "license": "MIT", - "dependencies": { - "@styled-system/core": "^5.1.2" - } - }, - "node_modules/@styled-system/props": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@styled-system/props/-/props-5.1.5.tgz", - "integrity": "sha512-FXhbzq2KueZpGaHxaDm8dowIEWqIMcgsKs6tBl6Y6S0njG9vC8dBMI6WSLDnzMoSqIX3nSKHmOmpzpoihdDewg==", - "license": "MIT", - "dependencies": { - "styled-system": "^5.1.5" - } - }, - "node_modules/@styled-system/shadow": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/shadow/-/shadow-5.1.2.tgz", - "integrity": "sha512-wqniqYb7XuZM7K7C0d1Euxc4eGtqEe/lvM0WjuAFsQVImiq6KGT7s7is+0bNI8O4Dwg27jyu4Lfqo/oIQXNzAg==", - "license": "MIT", - "dependencies": { - "@styled-system/core": "^5.1.2" - } - }, - "node_modules/@styled-system/space": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/space/-/space-5.1.2.tgz", - "integrity": "sha512-+zzYpR8uvfhcAbaPXhH8QgDAV//flxqxSjHiS9cDFQQUSznXMQmxJegbhcdEF7/eNnJgHeIXv1jmny78kipgBA==", - "license": "MIT", - "dependencies": { - "@styled-system/core": "^5.1.2" - } - }, - "node_modules/@styled-system/theme-get": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/theme-get/-/theme-get-5.1.2.tgz", - "integrity": "sha512-afAYdRqrKfNIbVgmn/2Qet1HabxmpRnzhFwttbGr6F/mJ4RDS/Cmn+KHwHvNXangQsWw/5TfjpWV+rgcqqIcJQ==", - "license": "MIT", - "dependencies": { - "@styled-system/core": "^5.1.2" - } - }, - "node_modules/@styled-system/typography": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/typography/-/typography-5.1.2.tgz", - "integrity": "sha512-BxbVUnN8N7hJ4aaPOd7wEsudeT7CxarR+2hns8XCX1zp0DFfbWw4xYa/olA0oQaqx7F1hzDg+eRaGzAJbF+jOg==", + "node_modules/@tanstack/react-virtual": { + "version": "3.14.13", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.14.13.tgz", + "integrity": "sha512-JbDTAwtzZ99aOeCrAfW5EsE5KSq5RWh6Af2dtFwyLIIk48Ja7vm6n4axu/43T3vfjFnEGJalxJ1wyYjSQD6bSg==", "license": "MIT", "dependencies": { - "@styled-system/core": "^5.1.2" + "@tanstack/virtual-core": "3.17.11" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/@styled-system/variant": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@styled-system/variant/-/variant-5.1.5.tgz", - "integrity": "sha512-Yn8hXAFoWIro8+Q5J8YJd/mP85Teiut3fsGVR9CAxwgNfIAiqlYxsk5iHU7VHJks/0KjL4ATSjmbtCDC/4l1qw==", + "node_modules/@tanstack/virtual-core": { + "version": "3.17.11", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.17.11.tgz", + "integrity": "sha512-+ILjvtHup6Y2hzQ6YzwMgX1Q+oQpxEGOXCEsCNaPoIP0VxMbizIBTmYTDtkerkIQS8/CbP1BRuyt8V/8BCsy1g==", "license": "MIT", - "dependencies": { - "@styled-system/core": "^5.1.2", - "@styled-system/css": "^5.1.5" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" } }, "node_modules/@types/debug": { @@ -1816,65 +588,25 @@ "undici-types": "~8.9.0" } }, - "node_modules/@types/prop-types": { - "version": "15.7.15", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", - "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "license": "MIT" - }, "node_modules/@types/react": { - "version": "18.3.31", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz", - "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", + "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", "license": "MIT", "dependencies": { - "@types/prop-types": "*", "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", - "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", "devOptional": true, "license": "MIT", "peerDependencies": { - "@types/react": "^18.0.0" - } - }, - "node_modules/@types/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-zts4lhQn5ia0cF/y2+3V6Riu0MAfez9/LJYavdM8TvcVl+S91A/7VWxyBT8hbRuWspmuCaiGI0F41OJYGrKhRA==", - "license": "MIT", - "dependencies": { - "@types/react": "^18" - } - }, - "node_modules/@types/styled-system": { - "version": "5.1.25", - "resolved": "https://registry.npmjs.org/@types/styled-system/-/styled-system-5.1.25.tgz", - "integrity": "sha512-B1oyjE4oeAbVnkigcB0WqU2gPFuTwLV/KkLa/uJZWFB9JWVKq1Fs0QwodZXZ9Sq6cb9ngY4kDqRY/dictIchjA==", - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/styled-system__css": { - "version": "5.0.22", - "resolved": "https://registry.npmjs.org/@types/styled-system__css/-/styled-system__css-5.0.22.tgz", - "integrity": "sha512-1oOWbdcL1SE2t6hTC3LlwrVHK3Z1Py4KYFehl6NL2XcLxS/L0ELEmN6APNWIYqUywPdeaKlQkRpV5dn0trLjGA==", - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" + "@types/react": "^19.3.0" } }, - "node_modules/@types/styled-system__theme-get": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@types/styled-system__theme-get/-/styled-system__theme-get-5.0.4.tgz", - "integrity": "sha512-dbzwxQ+8x6Bo3EKZMo9M3Knzo77ukwoC/isKW+GAuF5TenXlPkvgzx4t4+Lp0+fKs2M4owSef0KO3gtGW3Hpkw==", - "license": "MIT" - }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -2267,19 +999,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/baseline-browser-mapping": { - "version": "2.11.27", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.27.tgz", - "integrity": "sha512-ElY12DaROGuan+lMmZ8Cvo/ZUbXPe7Enc/9VU/b1T3Kp4dwytRcNdR8DoSJN5SNJT/CuvcCA0DHDVmMOCePdRQ==", - "license": "Apache-2.0", - "peer": true, - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -2290,73 +1009,8 @@ "fill-range": "^7.1.1" }, "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.29.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.3.tgz", - "integrity": "sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "baseline-browser-mapping": "^2.11.26", - "caniuse-lite": "^1.0.30001813", - "electron-to-chromium": "^1.5.439", - "node-releases": "^2.0.57", - "update-browserslist-db": "^1.3.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/camelize": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", - "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", - "license": "MIT", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001814", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001814.tgz", - "integrity": "sha512-/Uaf1lAzr59XcMpW0o96WoEfr+VXK2OX4U9AgFoiSHsVJ4HppnIFUjtYzsyDH2+tgANaQb2/oxYGwCPapN1FpA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0", - "peer": true + "node": ">=8" + } }, "node_modules/ccount": { "version": "2.0.1", @@ -2409,9 +1063,9 @@ } }, "node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "license": "MIT", "engines": { "node": ">=6" @@ -2433,13 +1087,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT", - "peer": true - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2454,28 +1101,6 @@ "node": ">= 8" } }, - "node_modules/css-color-keywords": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", - "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/css-to-react-native": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", - "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "camelize": "^1.0.0", - "css-color-keywords": "^1.0.0", - "postcss-value-parser": "^4.0.2" - } - }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -2553,32 +1178,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/diff": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", - "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.444", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.444.tgz", - "integrity": "sha512-5ss/uJfoDYDHT0lfJzT6FbcskIzROIOPf0BbbFkGcvDzoJU7i//9GDrwwIHQVmIsrAGiF3ihpADBRIsrEFt1rQ==", - "license": "ISC", - "peer": true - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/escape-string-regexp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", @@ -2680,32 +1279,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/fzy.js": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/fzy.js/-/fzy.js-0.4.1.tgz", - "integrity": "sha512-4sPVXf+9oGhzg2tYzgWe4hgAY0wEbkqeuKVEgdnqX8S8VcLosQsDjb0jV+f5uoQlf8INWId1w0IGoufAoik1TA==", - "license": "MIT" - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", @@ -2755,22 +1328,11 @@ "@babel/runtime": "^7.7.6" } }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT", - "peer": true + "node_modules/hsluv": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hsluv/-/hsluv-1.0.1.tgz", + "integrity": "sha512-zCaFTiDqBLQjCCFBu0qg7z9ASYPd+Bxx2GDCVZJsnehjK80S+jByqhuFz0pCd2Aw3FSKr18AWbRlwnKR0YdizQ==", + "license": "MIT" }, "node_modules/html-url-attributes": { "version": "3.0.1", @@ -2812,29 +1374,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/is-decimal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", @@ -2892,47 +1431,6 @@ "url": "https://github.com/sponsors/panva" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "peer": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "peer": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/lightningcss": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", @@ -3216,28 +1714,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "license": "ISC", - "peer": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, "node_modules/markdown-table": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", @@ -3248,78 +1724,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/mdast-util-definitions": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", - "integrity": "sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "unist-util-visit": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-definitions/node_modules/@types/mdast": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", - "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2" - } - }, - "node_modules/mdast-util-definitions/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/mdast-util-definitions/node_modules/unist-util-is": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", - "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-definitions/node_modules/unist-util-visit": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", - "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.1.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-definitions/node_modules/unist-util-visit-parents": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", - "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdast-util-find-and-replace": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", @@ -4203,15 +2607,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4237,25 +2632,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/node-releases": { - "version": "2.0.57", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.57.tgz", - "integrity": "sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/parse-entities": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", @@ -4294,12 +2670,14 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -4346,30 +2724,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT", - "peer": true - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, "node_modules/property-information": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", @@ -4381,34 +2735,30 @@ } }, "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", + "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==", "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.28.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^19.3.0" } }, "node_modules/react-intersection-observer": { - "version": "9.16.0", - "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.16.0.tgz", - "integrity": "sha512-w9nJSEp+DrW9KmQmeWHQyfaP6b03v+TdXynaoA964Wxt7mdR3An11z4NNCQgL4gKSK7y1ver2Fq+JKH6CWEzUA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-10.1.0.tgz", + "integrity": "sha512-V8HDu3+Llg6OEhOxx8LnUSS0t4VS+1Xk9ZatkI8Jct/H0CwKnqFTCu8NT3q7ghJTghTdIrEMPSWr2dkKPG+gdQ==", "license": "MIT", "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0", @@ -4554,43 +2904,11 @@ "@rolldown/binding-win32-x64-msvc": "1.2.12" } }, - "node_modules/sade": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "license": "MIT", - "dependencies": { - "mri": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "peer": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", - "license": "MIT", - "peer": true + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "license": "MIT" }, "node_modules/shebang-command": { "version": "2.0.0", @@ -4665,88 +2983,6 @@ "inline-style-parser": "0.2.7" } }, - "node_modules/styled-components": { - "version": "5.3.11", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz", - "integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/traverse": "^7.4.5", - "@emotion/is-prop-valid": "^1.1.0", - "@emotion/stylis": "^0.8.4", - "@emotion/unitless": "^0.7.4", - "babel-plugin-styled-components": ">= 1.12.0", - "css-to-react-native": "^3.0.0", - "hoist-non-react-statics": "^3.0.0", - "shallowequal": "^1.1.0", - "supports-color": "^5.5.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/styled-components" - }, - "peerDependencies": { - "react": ">= 16.8.0", - "react-dom": ">= 16.8.0", - "react-is": ">= 16.8.0" - } - }, - "node_modules/styled-components/node_modules/babel-plugin-styled-components": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.3.0.tgz", - "integrity": "sha512-nP/y6PbBqS/qtKROnJCgpGo8hYUzlBAVXN1QAjSBANL6vZiQXPQN7FYW/nUwoxY7nZhBEGm9T5tjL9gbzwulDw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-module-imports": "^7.25.9", - "@babel/plugin-syntax-jsx": "^7.25.9", - "picomatch": "^4.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "styled-components": ">= 2" - } - }, - "node_modules/styled-system": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/styled-system/-/styled-system-5.1.5.tgz", - "integrity": "sha512-7VoD0o2R3RKzOzPK0jYrVnS8iJdfkKsQJNiLRDjikOpQVqQHns/DXWaPZOH4tIKkhAT7I6wIsy9FWTWh2X3q+A==", - "license": "MIT", - "dependencies": { - "@styled-system/background": "^5.1.2", - "@styled-system/border": "^5.1.5", - "@styled-system/color": "^5.1.2", - "@styled-system/core": "^5.1.2", - "@styled-system/flexbox": "^5.1.2", - "@styled-system/grid": "^5.1.2", - "@styled-system/layout": "^5.1.2", - "@styled-system/position": "^5.1.2", - "@styled-system/shadow": "^5.1.2", - "@styled-system/space": "^5.1.2", - "@styled-system/typography": "^5.1.2", - "@styled-system/variant": "^5.1.5", - "object-assign": "^4.1.1" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "license": "MIT", - "peer": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", @@ -4858,16 +3094,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-generated": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", - "integrity": "sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/unist-util-is": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", @@ -4936,55 +3162,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/update-browserslist-db": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz", - "integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uvu": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", - "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0", - "diff": "^5.0.0", - "kleur": "^4.0.3", - "sade": "^1.7.3" - }, - "bin": { - "uvu": "bin.js" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -5128,13 +3305,6 @@ "node": ">= 8" } }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC", - "peer": true - }, "node_modules/zod": { "version": "4.6.5", "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz", diff --git a/ui/package.json b/ui/package.json index efbd84bb16..9507f13600 100644 --- a/ui/package.json +++ b/ui/package.json @@ -18,17 +18,18 @@ "@modelcontextprotocol/client": "^2.0.0", "@modelcontextprotocol/ext-apps": "^2.0.3", "@primer/octicons-react": "^19.38.0", - "@primer/react": "^36.27.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "@primer/primitives": "^11.10.0", + "@primer/react": "^38.40.1", + "react": "^19.3.0", + "react-dom": "^19.3.0", "react-markdown": "^10.1.0", "remark-gfm": "^4.0.1", "zod": "^4.2.0" }, "devDependencies": { "@types/node": "^26.6.4", - "@types/react": "^18.3.31", - "@types/react-dom": "^18.3.7", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", "@vitejs/plugin-react": "^6.1.1", "typescript": "^7.0.2", "vite": "^8.3.2", diff --git a/ui/src/apps/get-me/App.tsx b/ui/src/apps/get-me/App.tsx index c181fcab90..27ba66a86d 100644 --- a/ui/src/apps/get-me/App.tsx +++ b/ui/src/apps/get-me/App.tsx @@ -1,7 +1,7 @@ import { StrictMode, useState } from "react"; import type React from "react"; import { createRoot } from "react-dom/client"; -import { Avatar, Box, Text, Link, Heading, Spinner } from "@primer/react"; +import { Avatar, Text, Link, Heading, Spinner } from "@primer/react"; import { OrganizationIcon, LocationIcon, @@ -35,30 +35,30 @@ function AvatarWithFallback({ src, login, size }: { src?: string; login: string; if (!src || imgError) { return ( - - + ); } return ( - setImgError(true)} + style={{ marginRight: 16 }} /> ); } @@ -79,43 +79,62 @@ function UserCard({ }); return ( - {/* Header with avatar and name */} - +
- - +
+ {d.name || user.login} - @{user.login} - - + @{user.login} +
+
{/* Info grid */} - +
{d.company && ( <> - +
{d.company} )} {d.location && ( <> - +
{d.location} )} {d.blog && ( <> - +
- +
{d.email} )} - +
{/* Stats */} - - - +
+
+ {d.public_repos ?? 0} - Repos - - - + Repos +
+
+ {d.followers ?? 0} - Followers - - - + Followers +
+
+ {d.following ?? 0} - Following - - - + Following +
+
+ ); } @@ -165,25 +194,25 @@ function GetMeApp() { const content = (() => { if (error) { - return Error: {error.message}; + return Error: {error.message}; } if (!toolResult) { return ( - +
- Loading user data... - + Loading user data... +
); } const textContent = toolResult.content?.find((c: { type: string }) => c.type === "text"); if (!textContent || !("text" in textContent)) { - return No user data in response; + return No user data in response; } try { const userData = JSON.parse(textContent.text as string) as UserData; return void openLink(url)} />; } catch { - return Failed to parse user data; + return Failed to parse user data; } })(); diff --git a/ui/src/apps/issue-write/App.tsx b/ui/src/apps/issue-write/App.tsx index ee48ea5d33..7c14a29ffc 100644 --- a/ui/src/apps/issue-write/App.tsx +++ b/ui/src/apps/issue-write/App.tsx @@ -1,11 +1,10 @@ import { StrictMode, useState, useCallback, useEffect, useMemo, useRef } from "react"; import { createRoot } from "react-dom/client"; import { - Box, Text, TextInput, Button, - Flash, + Banner, Spinner, FormControl, CounterLabel, @@ -13,6 +12,7 @@ import { ActionList, Label, } from "@primer/react"; +import styles from "../../styles.module.css"; import { IssueOpenedIcon, CheckCircleIcon, @@ -296,46 +296,65 @@ function SuccessView({ const issueUrl = issue.html_url || issue.url || issue.URL || "#"; return ( - - - +
- - +
+ {isUpdate ? "Issue updated successfully" : "Issue created successfully"} -
- - + +
- +
- - +
+
{issue.title || submittedTitle} {issue.number && ( - + #{issue.number} )} - + {owner}/{repo} {submittedLabels.length > 0 && ( - +
{submittedLabels.map((label) => (
)} -
- - +
+
+ ); } @@ -1162,9 +1194,9 @@ function CreateIssueApp() { const selectedOptionName = fieldValue.cleared ? undefined : fieldValue.optionName; const selectedOption = field.options.find((option) => option.name === selectedOptionName); return ( - +
- + {selectedOption ? selectedOption.name : "Select option"} @@ -1179,15 +1211,15 @@ function CreateIssueApp() { onSelect={() => updateIssueFieldValue(field.name, { optionName: option.name })} > - @@ -1198,18 +1230,26 @@ function CreateIssueApp() { - +
); } return ( updateIssueFieldValue(field.name, { value: e.target.value })} + onChange={(e) => + updateIssueFieldValue(field.name, { value: e.target.value }) + } block contrast - sx={{ flex: 1 }} + style={{ flex: 1 }} /> ); }; @@ -1217,17 +1257,22 @@ function CreateIssueApp() { const body_node = (() => { if (appError) { return ( - - Connection error: {appError.message} - + ); } if (!app) { return ( - +
- +
); } @@ -1246,52 +1291,71 @@ function CreateIssueApp() { } return ( - - {/* Repository picker */} - - + {/* Repository picker */} +
+
span:last-child": { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }} - > + size="small" + leadingVisual={selectedRepo?.isPrivate ? LockIcon : RepoIcon} + style={{ maxWidth: "100%", overflow: "hidden" }} + className={styles.truncateLastChild} + > {selectedRepo ? selectedRepo.fullName : "Select repository"} - +
setRepoFilter(e.target.value)} - sx={{ width: "100%" }} - size="small" - autoFocus - /> - + placeholder="Search repositories..." + value={repoFilter} + onChange={(e) => setRepoFilter(e.target.value)} + size="small" + autoFocus + style={{ width: "100%" }} + /> +
{repoSearchLoading ? ( - +
- +
) : repoSearchResults.length > 0 ? ( repoSearchResults.map((r) => ( ) : ( - - +
+ Type to search repositories... - +
)}
- - +
+
{/* Error banner */} {error && ( - - {error} - + )} {/* Title */} - - + + Title {/* Description */} - +
+ as="label" + style={{ + fontWeight: 500, + fontSize: 14, + display: "block", + marginBottom: 8, + }} + > Description - +
{/* Metadata section */} - +
{/* Labels dropdown */} Labels {selectedLabels.length > 0 && ( - {selectedLabels.length} + {selectedLabels.length} )} - +
- +
{labelsLoading ? ( @@ -1420,14 +1510,14 @@ function CreateIssueApp() { }} > - +
{label.text} @@ -1442,11 +1532,18 @@ function CreateIssueApp() { Assignees {selectedAssignees.length > 0 && ( - {selectedAssignees.length} + {selectedAssignees.length} )} - +
- +
{assigneesLoading ? ( @@ -1569,18 +1666,23 @@ function CreateIssueApp() {
- +
{/* Fields section */} {availableIssueFields.length > 0 && ( - - +
+ Fields - +
{availableIssueFields.map((field) => { const fieldValue = fieldValues[field.name]; const hasFieldValue = @@ -1590,56 +1692,95 @@ function CreateIssueApp() { (fieldValue.value !== undefined && fieldValue.value !== "")); return ( - - +
+ {field.name} {field.description && ( - + {field.description} )} - +
{renderIssueFieldInput(field)} {hasFieldValue && ( )} - - +
+
); })} -
- +
+
)} {/* Selected labels display */} {selectedLabels.length > 0 && ( - +
{selectedLabels.map((label) => ( ))} - +
)} {/* Selected metadata display */} {(selectedAssignees.length > 0 || selectedMilestone) && ( - +
{selectedAssignees.length > 0 && ( Assigned to: {selectedAssignees.map((a) => a.text).join(", ")} @@ -1648,36 +1789,51 @@ function CreateIssueApp() { {selectedMilestone && ( Milestone: {selectedMilestone.text} )} - +
)} {/* State and submit actions */} - +
{isUpdateMode && ( - +
{currentState === "open" ? ( <> - - + size="small" + style={{ + marginLeft: "-1px", + borderTopLeftRadius: 0, + borderBottomLeftRadius: 0, + }} + > {selectedStateReason.label} @@ -1695,18 +1851,18 @@ function CreateIssueApp() { - +
{stateReason === "duplicate" && ( - - Duplicate of + + Duplicate of setDuplicateOf(e.target.value)} - size="small" - sx={{ width: 140 }} - /> + type="number" + placeholder="Issue number" + value={duplicateOf} + onChange={(e) => setDuplicateOf(e.target.value)} + size="small" + style={{ width: 140 }} + /> )} @@ -1719,7 +1875,7 @@ function CreateIssueApp() { Reopen issue )} -
+
)} -
-
+
+ ); })(); diff --git a/ui/src/apps/pr-edit/App.tsx b/ui/src/apps/pr-edit/App.tsx index 5b4995affc..791217f33a 100644 --- a/ui/src/apps/pr-edit/App.tsx +++ b/ui/src/apps/pr-edit/App.tsx @@ -1,11 +1,10 @@ import { StrictMode, useState, useCallback, useEffect, useMemo } from "react"; import { createRoot } from "react-dom/client"; import { - Box, Text, TextInput, Button, - Flash, + Banner, Spinner, FormControl, ActionMenu, @@ -15,6 +14,7 @@ import { CounterLabel, Label, } from "@primer/react"; +import styles from "../../styles.module.css"; import { GitPullRequestIcon, CheckCircleIcon, @@ -169,46 +169,65 @@ function SuccessView({ const prUrl = pr.html_url || pr.url || pr.URL || "#"; return ( - - - +
- - +
+ Pull request updated successfully -
- - + + + ); } @@ -516,9 +541,16 @@ function EditPRApp() { if (!app && !appError) { return ( - +
- +
); } @@ -526,7 +558,7 @@ function EditPRApp() { if (appError) { return ( - {appError.message} + ); } @@ -534,9 +566,16 @@ function EditPRApp() { if (toolInput === null) { return ( - +
- +
); } @@ -544,56 +583,78 @@ function EditPRApp() { if (!owner || !repo || !pullNumber) { return ( - Pull request owner, repo, and pullNumber are required. + ); } return ( - - - +
- - - #{pullNumber} · {owner}/{repo} +
+
+ #{pullNumber} · {owner}/{repo} {title && ( - + {title} )} - - +
+ - {error && {error}} + {error && } {isLoadingPR && !initialValues ? ( - +
- Loading pull request... - + Loading pull request... +
) : ( <> - - Title + + Title setTitle(e.target.value)} @@ -603,22 +664,42 @@ function EditPRApp() { /> - - +
+ Description - - - - base +
+ +
+ base - span": { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }}> + {baseBranch || "Select base"} - +
- +
{branchesLoading ? ( Loading... @@ -647,11 +728,27 @@ function EditPRApp() {
- - - - - state +
+ +
+
+ state - - - +
+ + +
+ +
+ reviewers - + {selectedReviewers.length === 0 ? ( "No reviewers" ) : ( <> Reviewers - {selectedReviewers.length} + {selectedReviewers.length} )} - +
- +
{reviewersLoading ? ( Loading... @@ -734,28 +857,51 @@ function EditPRApp() {
{selectedReviewers.length > 0 && ( - +
{selectedReviewers.map((reviewer) => ( ))} - +
)} -
- - - +
+ +
+ - +
)} -
+
); } diff --git a/ui/src/apps/pr-write/App.tsx b/ui/src/apps/pr-write/App.tsx index 6975434bea..a7014a544e 100644 --- a/ui/src/apps/pr-write/App.tsx +++ b/ui/src/apps/pr-write/App.tsx @@ -1,11 +1,10 @@ import { StrictMode, useState, useCallback, useEffect, useMemo } from "react"; import { createRoot } from "react-dom/client"; import { - Box, Text, TextInput, Button, - Flash, + Banner, Spinner, FormControl, ActionMenu, @@ -15,6 +14,7 @@ import { CounterLabel, Label, } from "@primer/react"; +import styles from "../../styles.module.css"; import { GitPullRequestIcon, CheckCircleIcon, @@ -82,46 +82,65 @@ function SuccessView({ const prUrl = pr.html_url || pr.url || pr.URL || "#"; return ( - - - +
- - +
+ Pull request created successfully -
- - + + + ); } @@ -462,9 +487,16 @@ function CreatePRApp() { if (!app && !appError) { return ( - +
- +
); } @@ -472,59 +504,78 @@ function CreatePRApp() { if (appError) { return ( - {appError.message} + ); } return ( - {/* Repository picker */} - - +
span:last-child": { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }} + style={{ maxWidth: "100%", overflow: "hidden" }} + className={styles.truncateLastChild} > {selectedRepo ? selectedRepo.fullName : "Select repository"} - +
setRepoFilter(e.target.value)} - sx={{ width: "100%" }} - size="small" - autoFocus - /> - + placeholder="Search repositories..." + value={repoFilter} + onChange={(e) => setRepoFilter(e.target.value)} + size="small" + autoFocus + style={{ width: "100%" }} + /> +
{repoSearchLoading ? ( - +
- +
) : repoSearchResults.length > 0 ? ( repoSearchResults.map((r) => ( ) : ( - - Type to search repositories... - +
+ Type to search repositories... +
)}
- - +
+ {/* Branch selectors */} - - - base +
+
+ base - span": { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }}> + {baseBranch || "Select base"} - +
- +
{branchesLoading ? ( Loading... @@ -604,19 +685,39 @@ function CreatePRApp() {
- - - ← +
- - compare + ← + +
+ compare - span": { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }}> + {headBranch || "Select head"} - +
- +
{branchesLoading ? ( Loading... @@ -644,15 +745,15 @@ function CreatePRApp() {
- - +
+
{/* Error banner */} - {error && {error}} + {error && } {/* Title */} - - Title + + Title setTitle(e.target.value)} @@ -663,29 +764,55 @@ function CreatePRApp() { {/* Description */} - - +
+ Description - +
{/* Reviewers */} - - reviewers +
+ reviewers - + {selectedReviewers.length === 0 ? ( "No reviewers" ) : ( <> Reviewers - {selectedReviewers.length} + {selectedReviewers.length} )} - +
- +
{reviewersLoading ? ( Loading... @@ -737,29 +864,52 @@ function CreatePRApp() {
{selectedReviewers.length > 0 && ( - +
{selectedReviewers.map((reviewer) => ( ))} - +
)} -
+
{/* Options and Submit */} - - +
+ @@ -810,8 +966,8 @@ function CreatePRApp() { - - +
+
); } diff --git a/ui/src/components/AppProvider.tsx b/ui/src/components/AppProvider.tsx index e27bf96a02..9258ff99d6 100644 --- a/ui/src/components/AppProvider.tsx +++ b/ui/src/components/AppProvider.tsx @@ -1,4 +1,8 @@ -import { ThemeProvider, BaseStyles, Box } from "@primer/react"; +import { BaseStyles } from "@primer/react"; +import { ThemeProvider } from "@primer/react/next"; +import "@primer/primitives/dist/css/primitives.css"; +import "@primer/primitives/dist/css/functional/themes/light.css"; +import "@primer/primitives/dist/css/functional/themes/dark.css"; import type { ReactNode, CSSProperties } from "react"; import { useEffect, useMemo } from "react"; import type { McpUiHostContext } from "@modelcontextprotocol/ext-apps"; @@ -44,10 +48,10 @@ export function AppProvider({ children, hostContext }: AppProviderProps) { return ( - +
{children} - +
); diff --git a/ui/src/components/FeedbackFooter.tsx b/ui/src/components/FeedbackFooter.tsx index 10fbdf44e6..c8f4c38403 100644 --- a/ui/src/components/FeedbackFooter.tsx +++ b/ui/src/components/FeedbackFooter.tsx @@ -1,17 +1,19 @@ -import { Box, Text } from "@primer/react"; +import { Text } from "@primer/react"; export function FeedbackFooter() { return ( - - +
+ Help us improve MCP Apps support in the GitHub MCP Server
github.com/github/github-mcp-server/issues/new?template=insiders-feedback.md
- +
); } diff --git a/ui/src/components/MarkdownEditor.tsx b/ui/src/components/MarkdownEditor.tsx index 5ba25932d0..fde12dae9a 100644 --- a/ui/src/components/MarkdownEditor.tsx +++ b/ui/src/components/MarkdownEditor.tsx @@ -6,7 +6,8 @@ * @see https://github.com/github/markdown-toolbar-element */ import { useId, useRef, useState, useEffect } from "react"; -import { Box, Text, Button, IconButton, useTheme } from "@primer/react"; +import { Text, Button, IconButton, useTheme } from "@primer/react"; +import styles from "../styles.module.css"; import { BoldIcon, ItalicIcon, @@ -25,7 +26,7 @@ import remarkGfm from "remark-gfm"; import "@github/markdown-toolbar-element"; // Declare types for the web component elements -declare global { +declare module "react/jsx-runtime" { namespace JSX { interface IntrinsicElements { "markdown-toolbar": React.DetailedHTMLProps< @@ -168,41 +169,48 @@ export function MarkdownEditor({ }; return ( - {/* Header with tabs and toolbar */} - {/* Write/Preview tabs */} - +
@@ -210,24 +218,32 @@ export function MarkdownEditor({ size="small" variant="invisible" onClick={() => setViewMode("preview")} - sx={{ - fontWeight: viewMode === "preview" ? "semibold" : "normal", - color: viewMode === "preview" ? "fg.default" : "fg.muted", - bg: viewMode === "preview" ? "actionListItem.default.hoverBg" : "transparent", - borderRadius: 2, - "&:hover": { - color: "fg.default", - }, + style={{ + fontWeight: viewMode === "preview" ? 500 : 400, + backgroundColor: + viewMode === "preview" + ? "var(--control-transparent-bgColor-hover)" + : "transparent", + borderRadius: 6, }} + className={styles.editorTab} + data-active={viewMode === "preview"} > Preview - +
{/* Toolbar - uses GitHub's official markdown-toolbar-element */} {viewMode === "write" && ( - +
- @@ -302,10 +319,10 @@ export function MarkdownEditor({ variant="invisible" /> - +
)} -
+ {/* Content area */} {viewMode === "write" ? ( @@ -333,115 +350,51 @@ export function MarkdownEditor({ }} /> ) : ( - :first-child": { mt: 0 }, - // GitHub Flavored Markdown styles - "& h1, & h2, & h3, & h4, & h5, & h6": { - mt: 3, - mb: 2, - fontWeight: "semibold", - lineHeight: 1.25, - }, - "& h1": { fontSize: 4, borderBottom: "1px solid", borderColor: "border.default", pb: 2 }, - "& h2": { fontSize: 3, borderBottom: "1px solid", borderColor: "border.default", pb: 2 }, - "& h3": { fontSize: 2 }, - "& p": { my: 2 }, - "& ul, & ol": { pl: 4, my: 2 }, - "& li": { my: 1 }, - "& code": { - bg: "neutral.muted", - px: 1, - py: "2px", - borderRadius: 1, - fontFamily: "mono", - fontSize: "85%", - }, - "& pre": { - bg: "neutral.muted", - p: 3, - borderRadius: 2, - overflow: "auto", - my: 2, - }, - "& pre code": { - bg: "transparent", - p: 0, - }, - "& blockquote": { - borderLeft: "4px solid", - borderColor: "border.default", - pl: 3, - ml: 0, - mr: 0, - my: 2, - color: "fg.muted", - bg: "canvas.subtle", - }, - "& a": { - color: "accent.fg", - textDecoration: "none", - "&:hover": { textDecoration: "underline" }, - }, - "& table": { - borderCollapse: "collapse", - width: "100%", - my: 2, - }, - "& th, & td": { - border: "1px solid", - borderColor: "border.default", - p: 2, - }, - "& th": { - bg: "canvas.subtle", - fontWeight: "semibold", - }, - "& input[type='checkbox']": { - mr: 2, - }, - "& hr": { - border: "none", - borderTop: "1px solid", - borderColor: "border.default", - my: 3, - }, - }}> + color: "var(--fgColor-default)", + }} + className={styles.markdownPreview} + > {value ? ( {value} ) : ( - + Nothing to preview )} - + )} {/* Footer */} - - + Markdown is supported - -
+ + ); } diff --git a/ui/src/styles.module.css b/ui/src/styles.module.css new file mode 100644 index 0000000000..cfe72d499b --- /dev/null +++ b/ui/src/styles.module.css @@ -0,0 +1,148 @@ +.truncateLastChild > span:last-child { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.truncateChildren > span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.editorTab { + color: var(--fgColor-muted); +} + +.editorTab[data-active="true"], +.editorTab:hover { + color: var(--fgColor-default); +} + +.markdownPreview > :first-child { + margin-top: 0; +} + +.markdownPreview h1, +.markdownPreview h2, +.markdownPreview h3, +.markdownPreview h4, +.markdownPreview h5, +.markdownPreview h6 { + margin-top: 16px; + margin-bottom: 8px; + font-weight: 500; + line-height: 1.25; +} + +.markdownPreview h1 { + font-size: 24px; + border-bottom: 1px solid; + border-color: var(--borderColor-default); + padding-bottom: 8px; +} + +.markdownPreview h2 { + font-size: 20px; + border-bottom: 1px solid; + border-color: var(--borderColor-default); + padding-bottom: 8px; +} + +.markdownPreview h3 { + font-size: 16px; +} + +.markdownPreview p { + margin-top: 8px; + margin-bottom: 8px; +} + +.markdownPreview ul, +.markdownPreview ol { + padding-left: 24px; + margin-top: 8px; + margin-bottom: 8px; +} + +.markdownPreview li { + margin-top: 4px; + margin-bottom: 4px; +} + +.markdownPreview code { + background-color: var(--bgColor-neutral-muted); + padding-left: 4px; + padding-right: 4px; + padding-top: 2px; + padding-bottom: 2px; + border-radius: 3px; + font-family: + ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; + font-size: 85%; +} + +.markdownPreview pre { + background-color: var(--bgColor-neutral-muted); + padding: 16px; + border-radius: 6px; + overflow: auto; + margin-top: 8px; + margin-bottom: 8px; +} + +.markdownPreview pre code { + background-color: transparent; + padding: 0; +} + +.markdownPreview blockquote { + border-left: 4px solid; + border-color: var(--borderColor-default); + padding-left: 16px; + margin-left: 0; + margin-right: 0; + margin-top: 8px; + margin-bottom: 8px; + color: var(--fgColor-muted); + background-color: var(--bgColor-muted); +} + +.markdownPreview a { + color: var(--fgColor-accent); + text-decoration: none; +} +.markdownPreview a:hover { + text-decoration: underline; +} + +.markdownPreview table { + border-collapse: collapse; + width: 100%; + margin-top: 8px; + margin-bottom: 8px; +} + +.markdownPreview th, +.markdownPreview td { + border: 1px solid; + border-color: var(--borderColor-default); + padding: 8px; +} + +.markdownPreview th { + background-color: var(--bgColor-muted); + font-weight: 500; +} + +.markdownPreview input[type="checkbox"] { + margin-right: 8px; +} + +.markdownPreview hr { + border: none; + border-top: 1px solid; + border-color: var(--borderColor-default); + margin-top: 16px; + margin-bottom: 16px; +} From 8c02d0768319d745837daec3e858820e95165452 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Oct 2026 05:52:54 +0000 Subject: [PATCH 13/13] Initial plan