Repository: supabase/cli
Pull request #2306 (2024-05-21 02:14:07)

Prod deploy
- **chore(deps): bump google.golang.org/grpc from 1.63.2 to 1.64.0 (#2289)**
- **Chore/bump studio (#2286)**
- **fix: bump edge-runtime to 1.53.0 (#2295)**
- **fix: update auth version to 2.151.0 (#2296)**
- **chore(deps): bump github.com/slack-go/slack from 0.12.5 to 0.13.0 (#2298)**
- **chore(deps): bump github.com/docker/docker from 26.1.2+incompatible to 26.1.3+incompatible (#2300)**
- **chore(deps): bump github.com/matoous/go-nanoid/v2 from 2.0.0 to 2.1.0 (#2301)**
- **fix(db): escape upper case table names for dump (#2302)**
- **chore: upgrade github client library**
- **feat: implement and test release version check**
- **feat: suggest upgrade when cli version is outdated**
- **chore: check upgrade after every successful command**
- **chore: use warning color for suggestions**
- **chore: improve upgrade suggestion**
- **fix(db): handle fdw and pgtle extension (#2303)**
- **chore(deps): bump github.com/golangci/golangci-lint from 1.58.1 to 1.58.2 (#2304)**



Patch:
diff --git a/cmd/bootstrap.go b/cmd/bootstrap.go
index 29ab2eef..64fb1af2 100644
--- a/cmd/bootstrap.go
+++ b/cmd/bootstrap.go
@@ -28,13 +28,16 @@ var (
 		Short:   "Bootstrap a Supabase project from a starter template",
 		Args:    cobra.MaximumNArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
+			ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
 			if !viper.IsSet("WORKDIR") {
 				title := fmt.Sprintf("Enter a directory to bootstrap your project (or leave blank to use %s): ", utils.Bold(utils.CurrentDirAbs))
-				workdir := utils.NewConsole().PromptText(title)
-				viper.Set("WORKDIR", workdir)
+				if workdir, err := utils.NewConsole().PromptText(ctx, title); err != nil {
+					return err
+				} else {
+					viper.Set("WORKDIR", workdir)
+				}
 			}
-			ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
-			client := bootstrap.GetGtihubClient(ctx)
+			client := utils.GetGtihubClient(ctx)
 			templates, err := bootstrap.ListSamples(ctx, client)
 			if err != nil {
 				return err
diff --git a/cmd/branches.go b/cmd/branches.go
index 4550fc09..2dc045a3 100644
--- a/cmd/branches.go
+++ b/cmd/branches.go
@@ -177,7 +177,9 @@ func promptBranchId(ctx context.Context, ref string) error {
 		if len(gitBranch) > 0 {
 			title = fmt.Sprintf("%-2s (or leave blank to use %s): ", title, utils.Aqua(gitBranch))
 		}
-		if name := console.PromptText(title); len(name) > 0 {
+		if name, err := console.PromptText(ctx, title); err != nil {
+			return err
+		} else if len(name) > 0 {
 			gitBranch = name
 		}
 		if len(gitBranch) == 0 {
diff --git a/cmd/init.go b/cmd/init.go
index e3f18caf..a9d2d90a 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -2,6 +2,8 @@ package cmd
 
 import (
 	"fmt"
+	"os"
+	"os/signal"
 
 	"github.com/spf13/afero"
 	"github.com/spf13/cobra"
@@ -40,8 +42,8 @@ var (
 			if !cmd.Flags().Changed("with-intellij-settings") {
 				createIntellijSettings = nil
 			}
-
-			return _init.Run(fsys, createVscodeSettings, createIntellijSettings, initParams)
+			ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
+			return _init.Run(ctx, fsys, createVscodeSettings, createIntellijSettings, initParams)
 		},
 		PostRun: func(cmd *cobra.Command, args []string) {
 			fmt.Println("Finished " + utils.Aqua("supabase init") + ".")
diff --git a/cmd/inspect.go b/cmd/inspect.go
index bbc42ecf..299524fb 100644
--- a/cmd/inspect.go
+++ b/cmd/inspect.go
@@ -1,16 +1,20 @@
 package cmd
 
 import (
+	"fmt"
 	"os"
 	"os/signal"
+	"path/filepath"
 
 	"github.com/spf13/afero"
 	"github.com/spf13/cobra"
 	"github.com/supabase/cli/internal/inspect/bloat"
 	"github.com/supabase/cli/internal/inspect/blocking"
 	"github.com/supabase/cli/internal/inspect/cache"
+	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/flags"
 
+	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/inspect/calls"
 	"github.com/supabase/cli/internal/inspect/index_sizes"
 	"github.com/supabase/cli/internal/inspect/index_usage"
@@ -197,15 +201,34 @@ var (
 			return role_connections.Run(cmd.Context(), flags.DbConfig, afero.NewOsFs())
 		},
 	}
+
+	outputDir string
+
+	reportCmd = &cobra.Command{
+		Use:   "report",
+		Short: "Generate a CSV output for all inspect commands",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			ctx := cmd.Context()
+			if len(outputDir) == 0 {
+				defaultPath := filepath.Join(utils.CurrentDirAbs, "report")
+				title := fmt.Sprintf("Enter a directory to save output files (or leave blank to use %s): ", utils.Bold(defaultPath))
+				if dir, err := utils.NewConsole().PromptText(ctx, title); err != nil {
+					return err
+				} else if len(dir) == 0 {
+					outputDir = defaultPath
+				}
+			}
+			return inspect.Report(ctx, outputDir, flags.DbConfig, afero.NewOsFs())
+		},
+	}
 )
 
 func init() {
-	inspectFlags := inspectDBCmd.PersistentFlags()
+	inspectFlags := inspectCmd.PersistentFlags()
 	inspectFlags.String("db-url", "", "Inspect the database specified by the connection string (must be percent-encoded).")
 	inspectFlags.Bool("linked", true, "Inspect the linked project.")
 	inspectFlags.Bool("local", false, "Inspect the local database.")
-	inspectDBCmd.MarkFlagsMutuallyExclusive("db-url", "linked", "local")
-	inspectCmd.AddCommand(inspectDBCmd)
+	inspectCmd.MarkFlagsMutuallyExclusive("db-url", "linked", "local")
 	inspectDBCmd.AddCommand(inspectCacheHitCmd)
 	inspectDBCmd.AddCommand(inspectReplicationSlotsCmd)
 	inspectDBCmd.AddCommand(inspectIndexUsageCmd)
@@ -225,5 +248,8 @@ func init() {
 	inspectDBCmd.AddCommand(inspectBloatCmd)
 	inspectDBCmd.AddCommand(inspectVacuumStatsCmd)
 	inspectDBCmd.AddCommand(inspectRoleConnectionsCmd)
+	inspectCmd.AddCommand(inspectDBCmd)
+	reportCmd.Flags().StringVar(&outputDir, "output-dir", "", "Path to save CSV files in")
+	inspectCmd.AddCommand(reportCmd)
 	rootCmd.AddCommand(inspectCmd)
 }
diff --git a/cmd/projects.go b/cmd/projects.go
index 9c72661f..e2c1ddb4 100644
--- a/cmd/projects.go
+++ b/cmd/projects.go
@@ -92,16 +92,17 @@ var (
 			return nil
 		},
 		RunE: func(cmd *cobra.Command, args []string) error {
+			ctx := cmd.Context()
 			if len(args) == 0 {
 				title := "Which project do you want to delete?"
-				cobra.CheckErr(flags.PromptProjectRef(cmd.Context(), title))
+				cobra.CheckErr(flags.PromptProjectRef(ctx, title))
 			} else {
 				flags.ProjectRef = args[0]
 			}
-			if err := delete.PreRun(flags.ProjectRef); err != nil {
+			if err := delete.PreRun(ctx, flags.ProjectRef); err != nil {
 				return err
 			}
-			return delete.Run(cmd.Context(), flags.ProjectRef, afero.NewOsFs())
+			return delete.Run(ctx, flags.ProjectRef, afero.NewOsFs())
 		},
 	}
 )
diff --git a/cmd/root.go b/cmd/root.go
index f80c4185..138fbf45 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -1,6 +1,7 @@
 package cmd
 
 import (
+	"context"
 	"fmt"
 	"net"
 	"net/url"
@@ -18,6 +19,7 @@ import (
 	"github.com/supabase/cli/internal/services"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/flags"
+	"golang.org/x/mod/semver"
 )
 
 const (
@@ -116,8 +118,6 @@ var (
 			if viper.GetBool("DEBUG") {
 				ctx = utils.WithTraceContext(ctx)
 				fmt.Fprintln(os.Stderr, cmd.Root().Short)
-			} else {
-				utils.CmdSuggestion = utils.SuggestDebugFlag
 			}
 			cmd.SetContext(ctx)
 			// Setup sentry last to ignore errors from parsing cli flags
@@ -137,11 +137,53 @@ func Execute() {
 	if err := rootCmd.Execute(); err != nil {
 		panic(err)
 	}
-	if utils.CmdSuggestion != utils.SuggestDebugFlag {
+	// Check upgrade last because --version flag is initialised after execute
+	version, err := checkUpgrade(rootCmd.Context(), afero.NewOsFs())
+	if err != nil {
+		fmt.Fprintln(utils.GetDebugLogger(), err)
+	}
+	if semver.Compare(version, "v"+utils.Version) > 0 {
+		fmt.Fprintln(os.Stderr, suggestUpgrade(version))
+	}
+	if len(utils.CmdSuggestion) > 0 {
 		fmt.Fprintln(os.Stderr, utils.CmdSuggestion)
 	}
 }
 
+func checkUpgrade(ctx context.Context, fsys afero.Fs) (string, error) {
+	if shouldFetchRelease(fsys) {
+		version, err := utils.GetLatestRelease(ctx)
+		if len(version) > 0 {
+			err = utils.WriteFile(utils.CliVersionPath, []byte(version), fsys)
+		}
+		return version, err
+	}
+	version, err := afero.ReadFile(fsys, utils.CliVersionPath)
+	if err != nil {
+		return "", errors.Errorf("failed to read cli version: %w", err)
+	}
+	return string(version), nil
+}
+
+func shouldFetchRelease(fsys afero.Fs) bool {
+	// Always fetch latest release when using --version flag
+	if vf := rootCmd.Flag("version"); vf != nil && vf.Changed {
+		return true
+	}
+	if fi, err := fsys.Stat(utils.CliVersionPath); err == nil {
+		expiry := fi.ModTime().Add(time.Hour * 10)
+		// Skip if last checked is less than 10 hours ago
+		return time.Now().After(expiry)
+	}
+	return true
+}
+
+func suggestUpgrade(version string) string {
+	const guide = "https://supabase.com/docs/guides/cli/getting-started#updating-the-supabase-cli"
+	return fmt.Sprintf(`A new version of Supabase CLI is available: %s (currently installed v%s)
+We recommend updating regularly for new features and bug fixes: %s`, utils.Yellow(version), utils.Version, utils.Bold(guide))
+}
+
 func recoverAndExit() {
 	err := recover()
 	if err == nil {
@@ -152,6 +194,11 @@ func recoverAndExit() {
 	case string:
 		msg = err
 	case error:
+		if !errors.Is(err, context.Canceled) &&
+			len(utils.CmdSuggestion) == 0 &&
+			!viper.GetBool("DEBUG") {
+			utils.CmdSuggestion = utils.SuggestDebugFlag
+		}
 		msg = err.Error()
 	default:
 		msg = fmt.Sprintf("%#v", err)
diff --git a/go.mod b/go.mod
index 4983a0c4..9cb2ea3f 100644
--- a/go.mod
+++ b/go.mod
@@ -1,20 +1,20 @@
 module github.com/supabase/cli
 
-go 1.22.0
+go 1.22.2
 
 require (
-	github.com/BurntSushi/toml v1.3.2
+	github.com/BurntSushi/toml v1.4.0
 	github.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d
 	github.com/andybalholm/brotli v1.1.0
 	github.com/cenkalti/backoff/v4 v4.3.0
 	github.com/charmbracelet/bubbles v0.18.0
-	github.com/charmbracelet/bubbletea v0.26.2
+	github.com/charmbracelet/bubbletea v0.26.3
 	github.com/charmbracelet/glamour v0.7.0
-	github.com/charmbracelet/lipgloss v0.10.0
-	github.com/containers/common v0.58.3
+	github.com/charmbracelet/lipgloss v0.11.0
+	github.com/containers/common v0.59.0
 	github.com/deepmap/oapi-codegen v1.16.2
 	github.com/docker/cli v26.1.2+incompatible
-	github.com/docker/docker v26.1.2+incompatible
+	github.com/docker/docker v26.1.3+incompatible
 	github.com/docker/go-connections v0.5.0
 	github.com/docker/go-units v0.5.0
 	github.com/getsentry/sentry-go v0.27.0
@@ -23,22 +23,23 @@ require (
 	github.com/go-git/go-git/v5 v5.12.0
 	github.com/go-xmlfmt/xmlfmt v1.1.2
 	github.com/golang-jwt/jwt/v5 v5.2.1
-	github.com/golangci/golangci-lint v1.58.1
-	github.com/google/go-github/v53 v53.2.0
+	github.com/golangci/golangci-lint v1.58.2
+	github.com/google/go-github/v62 v62.0.0
 	github.com/google/go-querystring v1.1.0
 	github.com/google/uuid v1.6.0
+	github.com/h2non/gock v1.2.0
 	github.com/jackc/pgconn v1.14.3
-	github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa
+	github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438
 	github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65
 	github.com/jackc/pgproto3/v2 v2.3.3
 	github.com/jackc/pgtype v1.14.3
 	github.com/jackc/pgx/v4 v4.18.3
 	github.com/joho/godotenv v1.5.1
-	github.com/matoous/go-nanoid/v2 v2.0.0
+	github.com/matoous/go-nanoid/v2 v2.1.0
 	github.com/mitchellh/mapstructure v1.5.0
 	github.com/muesli/reflow v0.3.0
 	github.com/oapi-codegen/runtime v1.1.1
-	github.com/slack-go/slack v0.12.5
+	github.com/slack-go/slack v0.13.0
 	github.com/spf13/afero v1.11.0
 	github.com/spf13/cobra v1.8.0
 	github.com/spf13/pflag v1.0.5
@@ -50,8 +51,7 @@ require (
 	golang.org/x/mod v0.17.0
 	golang.org/x/oauth2 v0.20.0
 	golang.org/x/term v0.20.0
-	google.golang.org/grpc v1.63.2
-	gopkg.in/h2non/gock.v1 v1.1.2
+	google.golang.org/grpc v1.64.0
 	gopkg.in/yaml.v3 v3.0.1
 	gotest.tools/gotestsum v1.11.0
 )
@@ -63,14 +63,14 @@ require (
 	github.com/4meepo/tagalign v1.3.4 // indirect
 	github.com/Abirdcfly/dupword v0.0.14 // indirect
 	github.com/Antonboom/errname v0.1.13 // indirect
-	github.com/Antonboom/nilnil v0.1.8 // indirect
+	github.com/Antonboom/nilnil v0.1.9 // indirect
 	github.com/Antonboom/testifylint v1.2.0 // indirect
 	github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
 	github.com/Crocmagnon/fatcontext v0.2.2 // indirect
 	github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect
 	github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0 // indirect
 	github.com/Masterminds/semver/v3 v3.2.1 // indirect
-	github.com/Microsoft/go-winio v0.6.1 // indirect
+	github.com/Microsoft/go-winio v0.6.2 // indirect
 	github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect
 	github.com/ProtonMail/go-crypto v1.0.0 // indirect
 	github.com/alecthomas/chroma/v2 v2.8.0 // indirect
@@ -101,20 +101,24 @@ require (
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
 	github.com/charithe/durationcheck v0.0.10 // indirect
 	github.com/charmbracelet/harmonica v0.2.0 // indirect
+	github.com/charmbracelet/x/ansi v0.1.1 // indirect
+	github.com/charmbracelet/x/input v0.1.0 // indirect
+	github.com/charmbracelet/x/term v0.1.1 // indirect
+	github.com/charmbracelet/x/windows v0.1.0 // indirect
 	github.com/chavacava/garif v0.1.0 // indirect
 	github.com/ckaznocha/intrange v0.1.2 // indirect
 	github.com/cloudflare/circl v1.3.7 // indirect
 	github.com/cloudwego/base64x v0.1.4 // indirect
 	github.com/cloudwego/iasm v0.2.0 // indirect
 	github.com/containerd/log v0.1.0 // indirect
-	github.com/containers/storage v1.53.0 // indirect
+	github.com/containers/storage v1.54.0 // indirect
 	github.com/curioswitch/go-reassign v0.2.0 // indirect
-	github.com/cyphar/filepath-securejoin v0.2.4 // indirect
+	github.com/cyphar/filepath-securejoin v0.2.5 // indirect
 	github.com/daixiang0/gci v0.13.4 // indirect
 	github.com/danieljoos/wincred v1.2.1 // indirect
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 	github.com/denis-tingaikin/go-header v0.5.0 // indirect
-	github.com/distribution/reference v0.5.0 // indirect
+	github.com/distribution/reference v0.6.0 // indirect
 	github.com/dlclark/regexp2 v1.4.0 // indirect
 	github.com/dnephin/pflag v1.0.7 // indirect
 	github.com/docker/distribution v2.8.3+incompatible // indirect
@@ -124,7 +128,7 @@ require (
 	github.com/emirpasic/gods v1.18.1 // indirect
 	github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
 	github.com/ettle/strcase v0.2.0 // indirect
-	github.com/fatih/color v1.16.0 // indirect
+	github.com/fatih/color v1.17.0 // indirect
 	github.com/fatih/structtag v1.2.0 // indirect
 	github.com/felixge/httpsnoop v1.0.4 // indirect
 	github.com/firefart/nonamedreturns v1.0.5 // indirect
@@ -140,8 +144,8 @@ require (
 	github.com/go-git/go-billy/v5 v5.5.0 // indirect
 	github.com/go-logr/logr v1.4.1 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
-	github.com/go-openapi/jsonpointer v0.19.6 // indirect
-	github.com/go-openapi/swag v0.22.10 // indirect
+	github.com/go-openapi/jsonpointer v0.21.0 // indirect
+	github.com/go-openapi/swag v0.23.0 // indirect
 	github.com/go-playground/locales v0.14.1 // indirect
 	github.com/go-playground/universal-translator v0.18.1 // indirect
 	github.com/go-playground/validator/v10 v10.20.0 // indirect
@@ -177,7 +181,7 @@ require (
 	github.com/gostaticanalysis/comment v1.4.2 // indirect
 	github.com/gostaticanalysis/forcetypeassert v0.1.0 // indirect
 	github.com/gostaticanalysis/nilerr v0.1.1 // indirect
-	github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
 	github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
 	github.com/hashicorp/go-version v1.6.0 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
@@ -206,7 +210,7 @@ require (
 	github.com/kulti/thelper v0.6.3 // indirect
 	github.com/kunwardeep/paralleltest v1.0.10 // indirect
 	github.com/kyoh86/exportloopref v0.1.11 // indirect
-	github.com/lasiar/canonicalheader v1.0.6 // indirect
+	github.com/lasiar/canonicalheader v1.1.1 // indirect
 	github.com/ldez/gomoddirectives v0.2.4 // indirect
 	github.com/ldez/tagliatelle v0.5.0 // indirect
 	github.com/leodido/go-urn v1.4.0 // indirect
@@ -274,7 +278,7 @@ require (
 	github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
 	github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
 	github.com/sashamelentyev/usestdlibvars v1.25.0 // indirect
-	github.com/securego/gosec/v2 v2.19.0 // indirect
+	github.com/securego/gosec/v2 v2.20.0 // indirect
 	github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
 	github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
@@ -307,40 +311,41 @@ require (
 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
 	github.com/xeipuuv/gojsonschema v1.2.0 // indirect
 	github.com/xen0n/gosmopolitan v1.2.2 // indirect
+	github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
 	github.com/yagipy/maintidx v1.0.0 // indirect
 	github.com/yeya24/promlinter v0.3.0 // indirect
 	github.com/ykadowak/zerologlint v0.1.5 // indirect
 	github.com/yuin/goldmark v1.5.4 // indirect
 	github.com/yuin/goldmark-emoji v1.0.2 // indirect
 	gitlab.com/bosi/decorder v0.4.2 // indirect
-	go-simpler.org/musttag v0.12.1 // indirect
+	go-simpler.org/musttag v0.12.2 // indirect
 	go-simpler.org/sloglint v0.6.0 // indirect
 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
-	go.opentelemetry.io/otel v1.25.0 // indirect
-	go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.25.0 // indirect
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 // indirect
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.25.0 // indirect
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect
-	go.opentelemetry.io/otel/metric v1.25.0 // indirect
-	go.opentelemetry.io/otel/sdk v1.25.0 // indirect
-	go.opentelemetry.io/otel/sdk/metric v1.25.0 // indirect
-	go.opentelemetry.io/otel/trace v1.25.0 // indirect
-	go.opentelemetry.io/proto/otlp v1.1.0 // indirect
+	go.opentelemetry.io/otel v1.27.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 // indirect
+	go.opentelemetry.io/otel/metric v1.27.0 // indirect
+	go.opentelemetry.io/otel/sdk v1.27.0 // indirect
+	go.opentelemetry.io/otel/sdk/metric v1.27.0 // indirect
+	go.opentelemetry.io/otel/trace v1.27.0 // indirect
+	go.opentelemetry.io/proto/otlp v1.2.0 // indirect
 	go.uber.org/atomic v1.9.0 // indirect
 	go.uber.org/automaxprocs v1.5.3 // indirect
 	go.uber.org/multierr v1.9.0 // indirect
 	go.uber.org/zap v1.24.0 // indirect
 	golang.org/x/arch v0.8.0 // indirect
 	golang.org/x/crypto v0.23.0 // indirect
-	golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
+	golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
 	golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect
 	golang.org/x/net v0.25.0 // indirect
 	golang.org/x/sync v0.7.0 // indirect
 	golang.org/x/sys v0.20.0 // indirect
 	golang.org/x/text v0.15.0 // indirect
 	golang.org/x/tools v0.21.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect
 	google.golang.org/protobuf v1.34.1 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/warnings.v0 v0.1.2 // indirect
diff --git a/go.sum b/go.sum
index e443999a..ec7d26c0 100644
--- a/go.sum
+++ b/go.sum
@@ -43,15 +43,15 @@ github.com/Abirdcfly/dupword v0.0.14 h1:3U4ulkc8EUo+CaT105/GJ1BQwtgyj6+VaBVbAX11
 github.com/Abirdcfly/dupword v0.0.14/go.mod h1:VKDAbxdY8YbKUByLGg8EETzYSuC4crm9WwI6Y3S0cLI=
 github.com/Antonboom/errname v0.1.13 h1:JHICqsewj/fNckzrfVSe+T33svwQxmjC+1ntDsHOVvM=
 github.com/Antonboom/errname v0.1.13/go.mod h1:uWyefRYRN54lBg6HseYCFhs6Qjcy41Y3Jl/dVhA87Ns=
-github.com/Antonboom/nilnil v0.1.8 h1:97QG7xrLq4TBK2U9aFq/I8Mcgz67pwMIiswnTA9gIn0=
-github.com/Antonboom/nilnil v0.1.8/go.mod h1:iGe2rYwCq5/Me1khrysB4nwI7swQvjclR8/YRPl5ihQ=
+github.com/Antonboom/nilnil v0.1.9 h1:eKFMejSxPSA9eLSensFmjW2XTgTwJMjZ8hUHtV4s/SQ=
+github.com/Antonboom/nilnil v0.1.9/go.mod h1:iGe2rYwCq5/Me1khrysB4nwI7swQvjclR8/YRPl5ihQ=
 github.com/Antonboom/testifylint v1.2.0 h1:015bxD8zc5iY8QwTp4+RG9I4kIbqwvGX9TrBbb7jGdM=
 github.com/Antonboom/testifylint v1.2.0/go.mod h1:rkmEqjqVnHDRNsinyN6fPSLnoajzFwsCcguJgwADBkw=
 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
-github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
+github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/Crocmagnon/fatcontext v0.2.2 h1:OrFlsDdOj9hW/oBEJBNSuH7QWf+E9WPVHw+x52bXVbk=
 github.com/Crocmagnon/fatcontext v0.2.2/go.mod h1:WSn/c/+MMNiD8Pri0ahRj0o9jVpeowzavOQplBJw6u0=
@@ -63,8 +63,8 @@ github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0
 github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
 github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
-github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
-github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
+github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
+github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
 github.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d h1:wvStE9wLpws31NiWUx+38wny1msZ/tm+eL5xmm4Y7So=
 github.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d/go.mod h1:9XMFaCeRyW7fC9XJOWQ+NdAv8VLG7ys7l3x4ozEGLUQ=
 github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJPdp74zmpA=
@@ -166,14 +166,22 @@ github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iy
 github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ=
 github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
 github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
-github.com/charmbracelet/bubbletea v0.26.2 h1:Eeb+n75Om9gQ+I6YpbCXQRKHt5Pn4vMwusQpwLiEgJQ=
-github.com/charmbracelet/bubbletea v0.26.2/go.mod h1:6I0nZ3YHUrQj7YHIHlM8RySX4ZIthTliMY+W8X8b+Gs=
+github.com/charmbracelet/bubbletea v0.26.3 h1:iXyGvI+FfOWqkB2V07m1DF3xxQijxjY2j8PqiXYqasg=
+github.com/charmbracelet/bubbletea v0.26.3/go.mod h1:bpZHfDHTYJC5g+FBK+ptJRCQotRC+Dhh3AoMxa/2+3Q=
 github.com/charmbracelet/glamour v0.7.0 h1:2BtKGZ4iVJCDfMF229EzbeR1QRKLWztO9dMtjmqZSng=
 github.com/charmbracelet/glamour v0.7.0/go.mod h1:jUMh5MeihljJPQbJ/wf4ldw2+yBP59+ctV36jASy7ps=
 github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
 github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
-github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s=
-github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE=
+github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g=
+github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8=
+github.com/charmbracelet/x/ansi v0.1.1 h1:CGAduulr6egay/YVbGc8Hsu8deMg1xZ/bkaXTPi1JDk=
+github.com/charmbracelet/x/ansi v0.1.1/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
+github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ=
+github.com/charmbracelet/x/input v0.1.0/go.mod h1:ZZwaBxPF7IG8gWWzPUVqHEtWhc1+HXJPNuerJGRGZ28=
+github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI=
+github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw=
+github.com/charmbracelet/x/windows v0.1.0 h1:gTaxdvzDM5oMa/I2ZNF7wN78X/atWemG9Wph7Ika2k4=
+github.com/charmbracelet/x/windows v0.1.0/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ=
 github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc=
 github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -196,10 +204,10 @@ github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I
 github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
 github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
 github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
-github.com/containers/common v0.58.3 h1:Iy/CdYjluEK926QT+ejonz7YvoRHazeW7BAiLIkmUQ4=
-github.com/containers/common v0.58.3/go.mod h1:p4V1SNk+WOISgp01m+axuqCUxaDP3WSZPPzvnJnS/cQ=
-github.com/containers/storage v1.53.0 h1:VSES3C/u1pxjTJIXvLrSmyP7OBtDky04oGu07UvdTEA=
-github.com/containers/storage v1.53.0/go.mod h1:pujcoOSc+upx15Jirdkebhtd8uJiLwbSd/mYT6zDJK8=
+github.com/containers/common v0.59.0 h1:fy9Jz0B7Qs1C030bm73YJtVddaiFSZD3558EV1tgN2g=
+github.com/containers/common v0.59.0/go.mod h1:53VicJCZ2AD0O+Br7VVoyrS7viXF4YmwlTIocWUT8XE=
+github.com/containers/storage v1.54.0 h1:xwYAlf6n9OnIlURQLLg3FYHbO74fQ/2W2N6EtQEUM4I=
+github.com/containers/storage v1.54.0/go.mod h1:PlMOoinRrBSnhYODLxt4EXl0nmJt+X0kjG0Xdt9fMTw=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@@ -209,8 +217,8 @@ github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
 github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
 github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo=
 github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc=
-github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
-github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
+github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo=
+github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
 github.com/daixiang0/gci v0.13.4 h1:61UGkmpoAcxHM2hhNkZEf5SzwQtWJXTSws7jaPyqwlw=
 github.com/daixiang0/gci v0.13.4/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk=
 github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs=
@@ -224,8 +232,8 @@ github.com/deepmap/oapi-codegen v1.16.2/go.mod h1:rdYoEA2GE+riuZ91DvpmBX9hJbQpuY
 github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8=
 github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY=
 github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
-github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
-github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
+github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
+github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
 github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
 github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
 github.com/dnephin/pflag v1.0.7 h1:oxONGlWxhmUct0YzKTgrpQv9AUA1wtPBn7zuSjJqptk=
@@ -235,8 +243,8 @@ github.com/docker/cli v26.1.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvM
 github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
 github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v26.1.2+incompatible h1:UVX5ZOrrfTGZZYEP+ZDq3Xn9PdHNXaSYMFPDumMqG2k=
-github.com/docker/docker v26.1.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v26.1.3+incompatible h1:lLCzRbrVZrljpVNobJu1J2FHk8V0s4BawoZippkc+xo=
+github.com/docker/docker v26.1.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqYyMHOp5vfsD1807oKo=
 github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
 github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
@@ -266,8 +274,8 @@ github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a
 github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q=
 github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A=
 github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
-github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
-github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
+github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
+github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
 github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
 github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
@@ -325,12 +333,11 @@ github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
-github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
+github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
+github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
-github.com/go-openapi/swag v0.22.10 h1:4y86NVn7Z2yYd6pfS4Z+Nyh3aAUL3Nul+LMbhFKy0gA=
-github.com/go-openapi/swag v0.22.10/go.mod h1:Cnn8BYtRlx6BNE3DPN86f/xkapGIcLWzh3CLEb4C1jI=
+github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
+github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
 github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
 github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@@ -343,8 +350,8 @@ github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
 github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
 github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
-github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
 github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
 github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
 github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
@@ -423,8 +430,8 @@ github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9
 github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
 github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e h1:ULcKCDV1LOZPFxGZaA6TlQbiM3J2GCPnkx/bGF6sX/g=
 github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e/go.mod h1:Pm5KhLPA8gSnQwrQ6ukebRcapGb/BG9iUkdaiCcGHJM=
-github.com/golangci/golangci-lint v1.58.1 h1:IYKjkt7nofq/mYXiDUyJiBZQi5kxD0jPCjBy6VXxjz8=
-github.com/golangci/golangci-lint v1.58.1/go.mod h1:IX9uSbhwDDOVTcceKZWmshlally+fOQYv1pZhIJCMNw=
+github.com/golangci/golangci-lint v1.58.2 h1:YHfceEW3CmHmJTPkmVNlqEa4xcjIulYIaVIiB5sNbC4=
+github.com/golangci/golangci-lint v1.58.2/go.mod h1:QH/aRLQIdNuOqEiki+4En6/k2SmeOsRJU/oiYffM1Ow=
 github.com/golangci/misspell v0.5.1 h1:/SjR1clj5uDjNLwYzCahHwIOPmQgoH04AyQIiWGbhCM=
 github.com/golangci/misspell v0.5.1/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
 github.com/golangci/modinfo v0.3.4 h1:oU5huX3fbxqQXdfspamej74DFX0kyGLkw1ppvXoJ8GA=
@@ -455,8 +462,8 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-github/v53 v53.2.0 h1:wvz3FyF53v4BK+AsnvCmeNhf8AkTaeh2SoYu/XUvTtI=
-github.com/google/go-github/v53 v53.2.0/go.mod h1:XhFRObz+m/l+UCm9b7KSIC3lT3NWSXGt7mOsAWEloao=
+github.com/google/go-github/v62 v62.0.0 h1:/6mGCaRywZz9MuHyw9gD1CwsbmBX8GWsbFkwMmHdhl4=
+github.com/google/go-github/v62 v62.0.0/go.mod h1:EMxeUqGJq2xRu9DYBMwel/mr7kZrzUOfQmmpYrZn2a4=
 github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
 github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -469,8 +476,8 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
 github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20230323073829-e72429f035bd h1:r8yyd+DJDmsUhGrRBxH5Pj7KeFK5l+Y3FsgT8keqKtk=
-github.com/google/pprof v0.0.0-20230323073829-e72429f035bd/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
+github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
+github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
@@ -500,8 +507,10 @@ github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW
 github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M=
 github.com/gostaticanalysis/testutil v0.4.0 h1:nhdCmubdmDF6VEatUNjgUZBJKWRqugoISdUv3PPQgHY=
 github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
+github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
+github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
 github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
 github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
 github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
@@ -534,8 +543,8 @@ github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8
 github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
 github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
 github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
-github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw=
-github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
+github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0=
+github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
 github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
 github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
 github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
@@ -627,7 +636,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv
 github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -643,8 +651,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ymEyhQ=
 github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA=
-github.com/lasiar/canonicalheader v1.0.6 h1:LJiiZ/MzkqibXOL2v+J8+WZM21pM0ivrBY/jbm9f5fo=
-github.com/lasiar/canonicalheader v1.0.6/go.mod h1:GfXTLQb3O1qF5qcSTyXTnfNUggUNyzbkOSpzZ0dpUJo=
+github.com/lasiar/canonicalheader v1.1.1 h1:wC+dY9ZfiqiPwAexUApFush/csSPXeIi4QqyxXmng8I=
+github.com/lasiar/canonicalheader v1.1.1/go.mod h1:cXkb3Dlk6XXy+8MVQnF23CYKWlyA7kfQhSw2CcZtZb0=
 github.com/ldez/gomoddirectives v0.2.4 h1:j3YjBIjEBbqZ0NKtBNzr8rtMHTOrLPeiwTkfUJZ3alg=
 github.com/ldez/gomoddirectives v0.2.4/go.mod h1:oWu9i62VcQDYp9EQ0ONTfqLNh+mDLWWDO+SO0qSQw5g=
 github.com/ldez/tagliatelle v0.5.0 h1:epgfuYt9v0CG3fms0pEgIMNPuFf/LpPIfjk4kyqSioo=
@@ -677,9 +685,8 @@ github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s
 github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE=
 github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04=
 github.com/maratori/testpackage v1.1.1/go.mod h1:s4gRK/ym6AMrqpOa/kEbQTV4Q4jb7WeLZzVhVVVOQMc=
-github.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U=
-github.com/matoous/go-nanoid/v2 v2.0.0 h1:d19kur2QuLeHmJBkvYkFdhFBzLoo1XVm2GgTpL+9Tj0=
-github.com/matoous/go-nanoid/v2 v2.0.0/go.mod h1:FtS4aGPVfEkxKxhdWPAspZpZSh1cOjtM7Ej/So3hR0g=
+github.com/matoous/go-nanoid/v2 v2.1.0 h1:P64+dmq21hhWdtvZfEAofnvJULaRR1Yib0+PnU669bE=
+github.com/matoous/go-nanoid/v2 v2.1.0/go.mod h1:KlbGNQ+FhrUNIHUxZdL63t7tl4LaPkZNpUULS8H4uVM=
 github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 h1:gWg6ZQ4JhDfJPqlo2srm/LN17lpybq15AryXIRcWYLE=
 github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
 github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
@@ -766,12 +773,12 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
 github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
-github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
-github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
+github.com/onsi/ginkgo/v2 v2.18.0 h1:W9Y7IWXxPUpAit9ieMOLI7PJZGaW22DTKgiVAuhDTLc=
+github.com/onsi/ginkgo/v2 v2.18.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
-github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
-github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
+github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
+github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
 github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
@@ -878,8 +885,8 @@ github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84d
 github.com/sashamelentyev/usestdlibvars v1.25.0 h1:IK8SI2QyFzy/2OD2PYnhy84dpfNo9qADrRt6LH8vSzU=
 github.com/sashamelentyev/usestdlibvars v1.25.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
-github.com/securego/gosec/v2 v2.19.0 h1:gl5xMkOI0/E6Hxx0XCY2XujA3V7SNSefA8sC+3f1gnk=
-github.com/securego/gosec/v2 v2.19.0/go.mod h1:hOkDcHz9J/XIgIlPDXalxjeVYsHxoWUc5zJSHxcB8YM=
+github.com/securego/gosec/v2 v2.20.0 h1:z/d5qp1niWa2avgFyUIglYTYYuGq2LrJwNj1HRVXsqc=
+github.com/securego/gosec/v2 v2.20.0/go.mod h1:hkiArbBZLwK1cehBcg3oFWUlYPWTBffPwwJVWChu83o=
 github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
 github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
 github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU=
@@ -903,8 +910,8 @@ github.com/sivchari/tenv v1.7.1 h1:PSpuD4bu6fSmtWMxSGWcvqUUgIn7k3yOJhOIzVWn8Ak=
 github.com/sivchari/tenv v1.7.1/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg=
 github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
 github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
-github.com/slack-go/slack v0.12.5 h1:ddZ6uz6XVaB+3MTDhoW04gG+Vc/M/X1ctC+wssy2cqs=
-github.com/slack-go/slack v0.12.5/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
+github.com/slack-go/slack v0.13.0 h1:7my/pR2ubZJ9912p9FtvALYpbt0cQPAqkRy2jaSI1PQ=
+github.com/slack-go/slack v0.13.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
 github.com/sonatard/noctx v0.0.2 h1:L7Dz4De2zDQhW8S0t+KUjY0MAQJd6SgVwhzNIc4ok00=
 github.com/sonatard/noctx v0.0.2/go.mod h1:kzFz+CzWSjQ2OzIm46uJZoXuBpa2+0y3T36U18dWqIo=
 github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
@@ -943,7 +950,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@@ -999,6 +1005,8 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17
 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
 github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU=
 github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg=
+github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
+github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
 github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM=
 github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk=
 github.com/yeya24/promlinter v0.3.0 h1:JVDbMp08lVCP7Y6NP3qHroGAO6z2yGKQtS5JsjqtoFs=
@@ -1022,10 +1030,10 @@ github.com/zalando/go-keyring v0.2.4/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51D
 github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
 gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo=
 gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8=
-go-simpler.org/assert v0.7.0 h1:OzWWZqfNxt8cLS+MlUp6Tgk1HjPkmgdKBq9qvy8lZsA=
-go-simpler.org/assert v0.7.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28=
-go-simpler.org/musttag v0.12.1 h1:yaMcjl/uyVnd1z6GqIhBiFH/PoqNN9f2IgtU7bp7W/0=
-go-simpler.org/musttag v0.12.1/go.mod h1:46HKu04A3Am9Lne5kKP0ssgwY3AeIlqsDzz3UxKROpY=
+go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ=
+go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28=
+go-simpler.org/musttag v0.12.2 h1:J7lRc2ysXOq7eM8rwaTYnNrHd5JwjppzB6mScysB2Cs=
+go-simpler.org/musttag v0.12.2/go.mod h1:uN1DVIasMTQKk6XSik7yrJoEysGtR2GRqvWnI9S7TYM=
 go-simpler.org/sloglint v0.6.0 h1:0YcqSVG7LI9EVBfRPhgPec79BH6X6mwjFuUR5Mr7j1M=
 go-simpler.org/sloglint v0.6.0/go.mod h1:+kJJtebtPePWyG5boFwY46COydAggADDOHM22zOvzBk=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -1035,26 +1043,26 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
-go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k=
-go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg=
-go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.25.0 h1:hDKnobznDpcdTlNzO0S/owRB8tyVr1OoeZZhDoqY+Cs=
-go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.25.0/go.mod h1:kUDQaUs1h8iTIHbQTk+iJRiUvSfJYMMKTtMCaiVu7B0=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 h1:dT33yIHtmsqpixFsSQPwNeY5drM9wTcoL8h0FWF4oGM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0/go.mod h1:h95q0LBGh7hlAC08X2DhSeyIG02YQ0UyioTCVAqRPmc=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.25.0 h1:vOL89uRfOCCNIjkisd0r7SEdJF3ZJFyCNY34fdZs8eU=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.25.0/go.mod h1:8GlBGcDk8KKi7n+2S4BT/CPZQYH3erLu0/k64r1MYgo=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM=
-go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA=
-go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s=
-go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo=
-go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw=
-go.opentelemetry.io/otel/sdk/metric v1.25.0 h1:7CiHOy08LbrxMAp4vWpbiPcklunUshVpAvGBrdDRlGw=
-go.opentelemetry.io/otel/sdk/metric v1.25.0/go.mod h1:LzwoKptdbBBdYfvtGCzGwk6GWMA3aUzBOwtQpR6Nz7o=
-go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM=
-go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I=
-go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
-go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
+go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg=
+go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 h1:bFgvUr3/O4PHj3VQcFEuYKvRZJX1SJDQ+11JXuSB3/w=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0/go.mod h1:xJntEd2KL6Qdg5lwp97HMLQDVeAhrYxmzFseAMDPQ8I=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY=
+go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik=
+go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak=
+go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI=
+go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A=
+go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI=
+go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw=
+go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw=
+go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4=
+go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94=
+go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@@ -1113,8 +1121,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
-golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
+golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
+golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
 golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
 golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
 golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f h1:phY1HzDcf18Aq9A8KkmRtY9WvOFIxN8wgfvy6Zm1DV8=
@@ -1457,12 +1465,10 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
 google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY=
-google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo=
-google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0=
-google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
+google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ=
+google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
 google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -1476,8 +1482,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
 google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
 google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
-google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
+google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
+google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1505,8 +1511,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
-gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
-gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
 gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
 gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
diff --git a/internal/bootstrap/bootstrap.go b/internal/bootstrap/bootstrap.go
index e03916c0..f9473a09 100644
--- a/internal/bootstrap/bootstrap.go
+++ b/internal/bootstrap/bootstrap.go
@@ -9,12 +9,11 @@ import (
 	"os"
 	"path/filepath"
 	"strings"
-	"sync"
 	"time"
 
 	"github.com/cenkalti/backoff/v4"
 	"github.com/go-errors/errors"
-	"github.com/google/go-github/v53/github"
+	"github.com/google/go-github/v62/github"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgx/v4"
 	"github.com/joho/godotenv"
@@ -31,7 +30,6 @@ import (
 	"github.com/supabase/cli/internal/utils/tenant"
 	"github.com/supabase/cli/pkg/api"
 	"github.com/supabase/cli/pkg/fetcher"
-	"golang.org/x/oauth2"
 	"golang.org/x/term"
 )
 
@@ -47,8 +45,10 @@ func Run(ctx context.Context, starter StarterTemplate, fsys afero.Fs, options ..
 		return errors.Errorf("failed to read workdir: %w", err)
 	} else if !empty {
 		title := fmt.Sprintf("Do you want to overwrite existing files in %s directory?", utils.Bold(workdir))
-		if !utils.NewConsole().PromptYesNo(title, true) {
-			return context.Canceled
+		if shouldOverwrite, err := utils.NewConsole().PromptYesNo(ctx, title, true); err != nil {
+			return err
+		} else if !shouldOverwrite {
+			return errors.New(context.Canceled)
 		}
 	}
 	if err := utils.ChangeWorkDir(fsys); err != nil {
@@ -56,11 +56,11 @@ func Run(ctx context.Context, starter StarterTemplate, fsys afero.Fs, options ..
 	}
 	// 0. Download starter template
 	if len(starter.Url) > 0 {
-		client := GetGtihubClient(ctx)
+		client := utils.GetGtihubClient(ctx)
 		if err := downloadSample(ctx, client, starter.Url, fsys); err != nil {
 			return err
 		}
-	} else if err := initBlank.Run(fsys, nil, nil, utils.InitParams{Overwrite: true}); err != nil {
+	} else if err := initBlank.Run(ctx, fsys, nil, nil, utils.InitParams{Overwrite: true}); err != nil {
 		return err
 	}
 	// 1. Login
@@ -287,26 +287,6 @@ func parseExampleEnv(fsys afero.Fs) (map[string]string, error) {
 	return envs, nil
 }
 
-var (
-	githubClient *github.Client
-	clientOnce   sync.Once
-)
-
-func GetGtihubClient(ctx context.Context) *github.Client {
-	clientOnce.Do(func() {
-		var client *http.Client
-		token := os.Getenv("GITHUB_TOKEN")
-		if len(token) > 0 {
-			ts := oauth2.StaticTokenSource(
-				&oauth2.Token{AccessToken: token},
-			)
-			client = oauth2.NewClient(ctx, ts)
-		}
-		githubClient = github.NewClient(client)
-	})
-	return githubClient
-}
-
 type samplesRepo struct {
 	Samples []StarterTemplate `json:"samples"`
 }
diff --git a/internal/branches/create/create.go b/internal/branches/create/create.go
index b003b7b5..89e38a6d 100644
--- a/internal/branches/create/create.go
+++ b/internal/branches/create/create.go
@@ -16,8 +16,10 @@ func Run(ctx context.Context, name, region string, fsys afero.Fs) error {
 	gitBranch := keys.GetGitBranchOrDefault("", fsys)
 	if len(name) == 0 && len(gitBranch) > 0 {
 		title := fmt.Sprintf("Do you want to create a branch named %s?", utils.Aqua(gitBranch))
-		if shouldCreate := utils.NewConsole().PromptYesNo(title, true); !shouldCreate {
-			return context.Canceled
+		if shouldCreate, err := utils.NewConsole().PromptYesNo(ctx, title, true); err != nil {
+			return err
+		} else if !shouldCreate {
+			return errors.New(context.Canceled)
 		}
 		name = gitBranch
 	}
diff --git a/internal/db/dump/dump.go b/internal/db/dump/dump.go
index 3e80b19b..ae812dea 100644
--- a/internal/db/dump/dump.go
+++ b/internal/db/dump/dump.go
@@ -113,7 +113,9 @@ func dumpData(ctx context.Context, config pgconn.Config, schema, excludeTable []
 		extraFlags = append(extraFlags, "--column-inserts", "--rows-per-insert 100000")
 	}
 	for _, table := range excludeTable {
-		extraFlags = append(extraFlags, "--exclude-table "+table)
+		escaped := quoteUpperCase(table)
+		// Use separate flags to avoid error: too many dotted names
+		extraFlags = append(extraFlags, "--exclude-table "+escaped)
 	}
 	if len(extraFlags) > 0 {
 		env = append(env, "EXTRA_FLAGS="+strings.Join(extraFlags, " "))
@@ -121,6 +123,11 @@ func dumpData(ctx context.Context, config pgconn.Config, schema, excludeTable []
 	return dump(ctx, config, dumpDataScript, env, dryRun, stdout)
 }
 
+func quoteUpperCase(table string) string {
+	escaped := strings.ReplaceAll(table, ".", `"."`)
+	return fmt.Sprintf(`"%s"`, escaped)
+}
+
 func dumpRole(ctx context.Context, config pgconn.Config, keepComments, dryRun bool, stdout io.Writer) error {
 	env := []string{}
 	if !keepComments {
@@ -152,7 +159,9 @@ func dump(ctx context.Context, config pgconn.Config, script string, env []string
 			// Bash variable expansion is unsupported:
 			// https://github.com/golang/go/issues/47187
 			parts := strings.Split(key, ":")
-			return envMap[parts[0]]
+			value := envMap[parts[0]]
+			// Escape double quotes in env vars
+			return strings.ReplaceAll(value, `"`, `\"`)
 		})
 		fmt.Println(expanded)
 		return nil
diff --git a/internal/db/dump/templates/dump_schema.sh b/internal/db/dump/templates/dump_schema.sh
index 07e267ac..c45f36d9 100755
--- a/internal/db/dump/templates/dump_schema.sh
+++ b/internal/db/dump/templates/dump_schema.sh
@@ -15,9 +15,11 @@ export PGDATABASE="$PGDATABASE"
 # Explanation of sed substitutions:
 #
 #   - do not alter superuser role "supabase_admin"
+#   - do not alter foreign data wrappers owner
 #   - do not include ACL changes on internal schemas
 #   - do not include RLS policies on cron extension schema
 #   - do not include event triggers
+#   - do not create pgtle schema and extension comments
 #   - do not create publication "supabase_realtime"
 pg_dump \
     --schema-only \
@@ -35,9 +37,11 @@ pg_dump \
 | sed -E 's/^         WHEN TAG IN /-- &/' \
 | sed -E 's/^   EXECUTE FUNCTION /-- &/' \
 | sed -E 's/^ALTER EVENT TRIGGER /-- &/' \
+| sed -E 's/^ALTER FOREIGN DATA WRAPPER (.+) OWNER TO /-- &/' \
 | sed -E 's/^ALTER DEFAULT PRIVILEGES FOR ROLE "supabase_admin"/-- &/' \
 | sed -E "s/^GRANT (.+) ON (.+) \"(${EXCLUDED_SCHEMAS:-})\"/-- &/" \
 | sed -E "s/^REVOKE (.+) ON (.+) \"(${EXCLUDED_SCHEMAS:-})\"/-- &/" \
+| sed -E 's/^(CREATE EXTENSION IF NOT EXISTS "pg_tle").+/\1;/' \
 | sed -E 's/^COMMENT ON EXTENSION (.+)/-- &/' \
 | sed -E 's/^CREATE POLICY "cron_job_/-- &/' \
 | sed -E 's/^ALTER TABLE "cron"/-- &/' \
diff --git a/internal/db/pull/pull.go b/internal/db/pull/pull.go
index 83c86aed..e0e88656 100644
--- a/internal/db/pull/pull.go
+++ b/internal/db/pull/pull.go
@@ -54,7 +54,9 @@ func Run(ctx context.Context, schema []string, config pgconn.Config, name string
 	}
 	// 4. Insert a row to `schema_migrations`
 	fmt.Fprintln(os.Stderr, "Schema written to "+utils.Bold(path))
-	if shouldUpdate := utils.NewConsole().PromptYesNo("Update remote migration history table?", true); shouldUpdate {
+	if shouldUpdate, err := utils.NewConsole().PromptYesNo(ctx, "Update remote migration history table?", true); err != nil {
+		return err
+	} else if shouldUpdate {
 		return repair.UpdateMigrationTable(ctx, conn, []string{timestamp}, repair.Applied, false, fsys)
 	}
 	return nil
diff --git a/internal/db/push/push.go b/internal/db/push/push.go
index 46d67fc1..8cdee030 100644
--- a/internal/db/push/push.go
+++ b/internal/db/push/push.go
@@ -46,8 +46,9 @@ func Run(ctx context.Context, dryRun, ignoreVersionMismatch bool, includeRoles,
 		}
 	} else {
 		msg := fmt.Sprintf("Do you want to push these migrations to the remote database?\n • %s\n\n", strings.Join(pending, "\n • "))
-		if shouldPush := utils.NewConsole().PromptYesNo(msg, true); !shouldPush {
-			utils.CmdSuggestion = ""
+		if shouldPush, err := utils.NewConsole().PromptYesNo(ctx, msg, true); err != nil {
+			return err
+		} else if !shouldPush {
 			return errors.New(context.Canceled)
 		}
 		if includeRoles {
diff --git a/internal/db/reset/reset.go b/internal/db/reset/reset.go
index 0dffec00..01eadff3 100644
--- a/internal/db/reset/reset.go
+++ b/internal/db/reset/reset.go
@@ -48,8 +48,9 @@ func Run(ctx context.Context, version string, config pgconn.Config, fsys afero.F
 	}
 	if !utils.IsLocalDatabase(config) {
 		msg := "Do you want to reset the remote database?"
-		if shouldReset := utils.NewConsole().PromptYesNo(msg, false); !shouldReset {
-			utils.CmdSuggestion = ""
+		if shouldReset, err := utils.NewConsole().PromptYesNo(ctx, msg, false); err != nil {
+			return err
+		} else if !shouldReset {
 			return errors.New(context.Canceled)
 		}
 		return resetRemote(ctx, version, config, fsys, options...)
diff --git a/internal/functions/new/new.go b/internal/functions/new/new.go
index 7abc3658..f1602c92 100644
--- a/internal/functions/new/new.go
+++ b/internal/functions/new/new.go
@@ -48,7 +48,7 @@ func Run(ctx context.Context, slug string, fsys afero.Fs) error {
 		// Templatize index.ts by config.toml if available
 		utils.Config.Api.Port = 54321
 		if err := utils.LoadConfigFS(fsys); err != nil {
-			utils.CmdSuggestion = utils.SuggestDebugFlag
+			utils.CmdSuggestion = ""
 		}
 		config := indexConfig{
 			Port:  utils.Config.Api.Port,
diff --git a/internal/init/init.go b/internal/init/init.go
index 57e131b2..033638dd 100644
--- a/internal/init/init.go
+++ b/internal/init/init.go
@@ -1,6 +1,7 @@
 package init
 
 import (
+	"context"
 	_ "embed"
 	"encoding/json"
 	"fmt"
@@ -30,9 +31,12 @@ var (
 	intelliJDeno string
 )
 
-func Run(fsys afero.Fs, createVscodeSettings, createIntellijSettings *bool, params utils.InitParams) error {
+func Run(ctx context.Context, fsys afero.Fs, createVscodeSettings, createIntellijSettings *bool, params utils.InitParams) error {
 	// 1. Write `config.toml`.
 	if err := utils.InitConfig(params, fsys); err != nil {
+		if errors.Is(err, os.ErrExist) {
+			utils.CmdSuggestion = fmt.Sprintf("Run %s to overwrite existing config file.", utils.Aqua("supabase init --force"))
+		}
 		return err
 	}
 
@@ -59,10 +63,14 @@ func Run(fsys afero.Fs, createVscodeSettings, createIntellijSettings *bool, para
 		}
 	} else {
 		console := utils.NewConsole()
-		if isVscode := console.PromptYesNo("Generate VS Code settings for Deno?", false); isVscode {
+		if isVscode, err := console.PromptYesNo(ctx, "Generate VS Code settings for Deno?", false); err != nil {
+			return err
+		} else if isVscode {
 			return writeVscodeConfig(fsys)
 		}
-		if isIntelliJ := console.PromptYesNo("Generate IntelliJ Settings for Deno?", false); isIntelliJ {
+		if isIntelliJ, err := console.PromptYesNo(ctx, "Generate IntelliJ Settings for Deno?", false); err != nil {
+			return err
+		} else if isIntelliJ {
 			return writeIntelliJConfig(fsys)
 		}
 	}
diff --git a/internal/inspect/bloat/bloat.go b/internal/inspect/bloat/bloat.go
index 6bb2c57c..13d31dd0 100644
--- a/internal/inspect/bloat/bloat.go
+++ b/internal/inspect/bloat/bloat.go
@@ -2,6 +2,7 @@ package bloat
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 
 	"github.com/go-errors/errors"
@@ -9,12 +10,14 @@ import (
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/db/reset"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed bloat.sql
+var BloatQuery string
+
 type Result struct {
 	Type        string
 	Schemaname  string
@@ -28,7 +31,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.BLOAT_QUERY, reset.LikeEscapeSchema(utils.InternalSchemas))
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, BloatQuery, reset.LikeEscapeSchema(utils.InternalSchemas))
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/bloat/bloat.sql b/internal/inspect/bloat/bloat.sql
new file mode 100644
index 00000000..25917d87
--- /dev/null
+++ b/internal/inspect/bloat/bloat.sql
@@ -0,0 +1,61 @@
+WITH constants AS (
+  SELECT current_setting('block_size')::numeric AS bs, 23 AS hdr, 4 AS ma
+), bloat_info AS (
+  SELECT
+    ma,bs,schemaname,tablename,
+    (datawidth+(hdr+ma-(case when hdr%ma=0 THEN ma ELSE hdr%ma END)))::numeric AS datahdr,
+    (maxfracsum*(nullhdr+ma-(case when nullhdr%ma=0 THEN ma ELSE nullhdr%ma END))) AS nullhdr2
+  FROM (
+    SELECT
+      schemaname, tablename, hdr, ma, bs,
+      SUM((1-null_frac)*avg_width) AS datawidth,
+      MAX(null_frac) AS maxfracsum,
+      hdr+(
+        SELECT 1+count(*)/8
+        FROM pg_stats s2
+        WHERE null_frac<>0 AND s2.schemaname = s.schemaname AND s2.tablename = s.tablename
+      ) AS nullhdr
+    FROM pg_stats s, constants
+    GROUP BY 1,2,3,4,5
+  ) AS foo
+), table_bloat AS (
+  SELECT
+    schemaname, tablename, cc.relpages, bs,
+    CEIL((cc.reltuples*((datahdr+ma-
+      (CASE WHEN datahdr%ma=0 THEN ma ELSE datahdr%ma END))+nullhdr2+4))/(bs-20::float)) AS otta
+  FROM bloat_info
+  JOIN pg_class cc ON cc.relname = bloat_info.tablename
+  JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname = bloat_info.schemaname AND nn.nspname <> 'information_schema'
+), index_bloat AS (
+  SELECT
+    schemaname, tablename, bs,
+    COALESCE(c2.relname,'?') AS iname, COALESCE(c2.reltuples,0) AS ituples, COALESCE(c2.relpages,0) AS ipages,
+    COALESCE(CEIL((c2.reltuples*(datahdr-12))/(bs-20::float)),0) AS iotta -- very rough approximation, assumes all cols
+  FROM bloat_info
+  JOIN pg_class cc ON cc.relname = bloat_info.tablename
+  JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname = bloat_info.schemaname AND nn.nspname <> 'information_schema'
+  JOIN pg_index i ON indrelid = cc.oid
+  JOIN pg_class c2 ON c2.oid = i.indexrelid
+)
+SELECT
+  type, schemaname, object_name, bloat, pg_size_pretty(raw_waste) as waste
+FROM
+(SELECT
+  'table' as type,
+  schemaname,
+  tablename as object_name,
+  ROUND(CASE WHEN otta=0 THEN 0.0 ELSE table_bloat.relpages/otta::numeric END,1) AS bloat,
+  CASE WHEN relpages < otta THEN '0' ELSE (bs*(table_bloat.relpages-otta)::bigint)::bigint END AS raw_waste
+FROM
+  table_bloat
+    UNION
+SELECT
+  'index' as type,
+  schemaname,
+  tablename || '::' || iname as object_name,
+  ROUND(CASE WHEN iotta=0 OR ipages=0 THEN 0.0 ELSE ipages/iotta::numeric END,1) AS bloat,
+  CASE WHEN ipages < iotta THEN '0' ELSE (bs*(ipages-iotta))::bigint END AS raw_waste
+FROM
+  index_bloat) bloat_summary
+WHERE NOT schemaname LIKE ANY($1)
+ORDER BY raw_waste DESC, bloat DESC
diff --git a/internal/inspect/blocking/blocking.go b/internal/inspect/blocking/blocking.go
index 2367cf12..77870f94 100644
--- a/internal/inspect/blocking/blocking.go
+++ b/internal/inspect/blocking/blocking.go
@@ -2,6 +2,7 @@ package blocking
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 	"regexp"
 
@@ -9,12 +10,14 @@ import (
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed blocking.sql
+var BlockingQuery string
+
 type Result struct {
 	Blocked_pid        int
 	Blocking_statement string
@@ -29,7 +32,9 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.BLOCKING_QUERY)
+	defer conn.Close(context.Background())
+	// Ref: https://github.com/heroku/heroku-pg-extras/blob/main/commands/blocking.js#L7
+	rows, err := conn.Query(ctx, BlockingQuery)
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/blocking/blocking.sql b/internal/inspect/blocking/blocking.sql
new file mode 100644
index 00000000..ea7f7671
--- /dev/null
+++ b/internal/inspect/blocking/blocking.sql
@@ -0,0 +1,15 @@
+SELECT
+  bl.pid AS blocked_pid,
+  ka.query AS blocking_statement,
+  age(now(), ka.query_start)::text AS blocking_duration,
+  kl.pid AS blocking_pid,
+  a.query AS blocked_statement,
+  age(now(), a.query_start)::text AS blocked_duration
+FROM pg_catalog.pg_locks bl
+JOIN pg_catalog.pg_stat_activity a
+  ON bl.pid = a.pid
+JOIN pg_catalog.pg_locks kl
+JOIN pg_catalog.pg_stat_activity ka
+  ON kl.pid = ka.pid
+  ON bl.transactionid = kl.transactionid AND bl.pid != kl.pid
+WHERE NOT bl.granted
diff --git a/internal/inspect/cache/cache.go b/internal/inspect/cache/cache.go
index e689be65..4b775515 100644
--- a/internal/inspect/cache/cache.go
+++ b/internal/inspect/cache/cache.go
@@ -2,30 +2,34 @@ package cache
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 
 	"github.com/go-errors/errors"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed cache.sql
+var CacheQuery string
+
 type Result struct {
 	Name  string
 	Ratio float64
 }
 
 func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
+	// Ref: https://github.com/heroku/heroku-pg-extras/blob/main/commands/cache_hit.js#L7
 	conn, err := utils.ConnectByConfig(ctx, config, options...)
 	if err != nil {
 		return err
 	}
 	defer conn.Close(context.Background())
-	rows, err := conn.Query(ctx, inspect.CACHE_QUERY)
+	rows, err := conn.Query(ctx, CacheQuery)
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/cache/cache.sql b/internal/inspect/cache/cache.sql
new file mode 100644
index 00000000..fc14b288
--- /dev/null
+++ b/internal/inspect/cache/cache.sql
@@ -0,0 +1,9 @@
+SELECT
+  'index hit rate' AS name,
+  (sum(idx_blks_hit)) / nullif(sum(idx_blks_hit + idx_blks_read),0) AS ratio
+FROM pg_statio_user_indexes
+UNION ALL
+SELECT
+  'table hit rate' AS name,
+  sum(heap_blks_hit) / nullif(sum(heap_blks_hit) + sum(heap_blks_read),0) AS ratio
+FROM pg_statio_user_tables
diff --git a/internal/inspect/calls/calls.go b/internal/inspect/calls/calls.go
index 02808cb0..94bd8eaf 100644
--- a/internal/inspect/calls/calls.go
+++ b/internal/inspect/calls/calls.go
@@ -2,6 +2,7 @@ package calls
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 	"regexp"
 
@@ -9,12 +10,14 @@ import (
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed calls.sql
+var CallsQuery string
+
 type Result struct {
 	Total_exec_time string
 	Prop_exec_time  string
@@ -28,7 +31,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.CALLS_QUERY)
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, CallsQuery)
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/calls/calls.sql b/internal/inspect/calls/calls.sql
new file mode 100644
index 00000000..57c3ecee
--- /dev/null
+++ b/internal/inspect/calls/calls.sql
@@ -0,0 +1,9 @@
+SELECT
+  query,
+  (interval '1 millisecond' * total_exec_time)::text AS total_exec_time,
+  to_char((total_exec_time/sum(total_exec_time) OVER()) * 100, 'FM90D0') || '%'  AS prop_exec_time,
+  to_char(calls, 'FM999G999G990') AS ncalls,
+  (interval '1 millisecond' * (blk_read_time + blk_write_time))::text AS sync_io_time
+FROM pg_stat_statements
+ORDER BY calls DESC
+LIMIT 10
diff --git a/internal/inspect/index_sizes/index_sizes.go b/internal/inspect/index_sizes/index_sizes.go
index 37e6f273..c2fad6c6 100644
--- a/internal/inspect/index_sizes/index_sizes.go
+++ b/internal/inspect/index_sizes/index_sizes.go
@@ -2,6 +2,7 @@ package index_sizes
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 
 	"github.com/go-errors/errors"
@@ -9,12 +10,14 @@ import (
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/db/reset"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed index_sizes.sql
+var IndexSizesQuery string
+
 type Result struct {
 	Name string
 	Size string
@@ -25,7 +28,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.INDEX_SIZES_QUERY, reset.LikeEscapeSchema(utils.InternalSchemas))
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, IndexSizesQuery, reset.LikeEscapeSchema(utils.InternalSchemas))
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/index_sizes/index_sizes.sql b/internal/inspect/index_sizes/index_sizes.sql
new file mode 100644
index 00000000..25b6f96d
--- /dev/null
+++ b/internal/inspect/index_sizes/index_sizes.sql
@@ -0,0 +1,9 @@
+SELECT
+  n.nspname || '.' || c.relname AS name,
+  pg_size_pretty(sum(c.relpages::bigint*8192)::bigint) AS size
+FROM pg_class c
+LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
+WHERE NOT n.nspname LIKE ANY($1)
+AND c.relkind = 'i'
+GROUP BY n.nspname, c.relname
+ORDER BY sum(c.relpages) DESC
diff --git a/internal/inspect/index_usage/index_usage.go b/internal/inspect/index_usage/index_usage.go
index c34d7deb..b2165f34 100644
--- a/internal/inspect/index_usage/index_usage.go
+++ b/internal/inspect/index_usage/index_usage.go
@@ -2,6 +2,7 @@ package index_usage
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 
 	"github.com/go-errors/errors"
@@ -9,12 +10,14 @@ import (
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/db/reset"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed index_usage.sql
+var IndexUsageQuery string
+
 type Result struct {
 	Name                        string
 	Percent_of_times_index_used string
@@ -26,7 +29,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.INDEX_USAGE_QUERY, reset.LikeEscapeSchema(utils.InternalSchemas))
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, IndexUsageQuery, reset.LikeEscapeSchema(utils.InternalSchemas))
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/index_usage/index_usage.sql b/internal/inspect/index_usage/index_usage.sql
new file mode 100644
index 00000000..fa83e97d
--- /dev/null
+++ b/internal/inspect/index_usage/index_usage.sql
@@ -0,0 +1,17 @@
+SELECT
+  schemaname || '.' || relname AS name,
+  CASE
+    WHEN idx_scan IS NULL THEN 'Insufficient data'
+    WHEN idx_scan = 0 THEN 'Insufficient data'
+    ELSE ROUND(100.0 * idx_scan / (seq_scan + idx_scan), 1) || '%'
+  END percent_of_times_index_used,
+  n_live_tup rows_in_table
+FROM pg_stat_user_tables
+WHERE NOT schemaname LIKE ANY($1)
+ORDER BY
+  CASE
+    WHEN idx_scan is null then 1
+    WHEN idx_scan = 0 then 1
+    ELSE 0
+  END,
+  n_live_tup DESC
diff --git a/internal/inspect/locks/locks.go b/internal/inspect/locks/locks.go
index efeb82ae..f7d18f0c 100644
--- a/internal/inspect/locks/locks.go
+++ b/internal/inspect/locks/locks.go
@@ -2,6 +2,7 @@ package locks
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 	"regexp"
 
@@ -9,12 +10,14 @@ import (
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed locks.sql
+var LocksQuery string
+
 type Result struct {
 	Pid           int
 	Relname       string
@@ -29,7 +32,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.LOCKS_QUERY)
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, LocksQuery)
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/locks/locks.sql b/internal/inspect/locks/locks.sql
new file mode 100644
index 00000000..e140f478
--- /dev/null
+++ b/internal/inspect/locks/locks.sql
@@ -0,0 +1,12 @@
+SELECT
+  pg_stat_activity.pid,
+  COALESCE(pg_class.relname, 'null') AS relname,
+  COALESCE(pg_locks.transactionid, 'null') AS transactionid,
+  pg_locks.granted,
+  pg_stat_activity.query,
+  age(now(), pg_stat_activity.query_start)::text AS age
+FROM pg_stat_activity, pg_locks LEFT OUTER JOIN pg_class ON (pg_locks.relation = pg_class.oid)
+WHERE pg_stat_activity.query <> '<insufficient privilege>'
+AND pg_locks.pid = pg_stat_activity.pid
+AND pg_locks.mode = 'ExclusiveLock'
+ORDER BY query_start
diff --git a/internal/inspect/long_running_queries/long_running_queries.go b/internal/inspect/long_running_queries/long_running_queries.go
index e091515d..4e7ce6f8 100644
--- a/internal/inspect/long_running_queries/long_running_queries.go
+++ b/internal/inspect/long_running_queries/long_running_queries.go
@@ -2,18 +2,21 @@ package long_running_queries
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 
 	"github.com/go-errors/errors"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed long_running_queries.sql
+var LongRunningQueriesQuery string
+
 type Result struct {
 	Pid      int
 	Duration string
@@ -25,7 +28,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.LONG_RUNNING_QUERY)
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, LongRunningQueriesQuery)
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/long_running_queries/long_running_queries.sql b/internal/inspect/long_running_queries/long_running_queries.sql
new file mode 100644
index 00000000..68c20ba9
--- /dev/null
+++ b/internal/inspect/long_running_queries/long_running_queries.sql
@@ -0,0 +1,12 @@
+SELECT
+  pid,
+  age(now(), pg_stat_activity.query_start)::text AS duration,
+  query AS query
+FROM
+  pg_stat_activity
+WHERE
+  pg_stat_activity.query <> ''::text
+  AND state <> 'idle'
+  AND age(now(), pg_stat_activity.query_start) > interval '5 minutes'
+ORDER BY
+  age(now(), pg_stat_activity.query_start) DESC
diff --git a/internal/inspect/outliers/outliers.go b/internal/inspect/outliers/outliers.go
index d19a691e..a487c9fb 100644
--- a/internal/inspect/outliers/outliers.go
+++ b/internal/inspect/outliers/outliers.go
@@ -2,6 +2,7 @@ package outliers
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 	"regexp"
 
@@ -9,12 +10,14 @@ import (
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed outliers.sql
+var OutliersQuery string
+
 type Result struct {
 	Total_exec_time string
 	Prop_exec_time  string
@@ -28,7 +31,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.OUTLIERS_QUERY)
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, OutliersQuery)
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/outliers/outliers.sql b/internal/inspect/outliers/outliers.sql
new file mode 100644
index 00000000..d222cc8d
--- /dev/null
+++ b/internal/inspect/outliers/outliers.sql
@@ -0,0 +1,9 @@
+SELECT
+  (interval '1 millisecond' * total_exec_time)::text AS total_exec_time,
+  to_char((total_exec_time/sum(total_exec_time) OVER()) * 100, 'FM90D0') || '%'  AS prop_exec_time,
+  to_char(calls, 'FM999G999G999G990') AS ncalls,
+  (interval '1 millisecond' * (blk_read_time + blk_write_time))::text AS sync_io_time,
+  query
+FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1)
+ORDER BY total_exec_time DESC
+LIMIT 10
diff --git a/internal/inspect/queries.go b/internal/inspect/queries.go
deleted file mode 100644
index 42139729..00000000
--- a/internal/inspect/queries.go
+++ /dev/null
@@ -1,305 +0,0 @@
-package inspect
-
-const BLOAT_QUERY = `WITH constants AS (
-  SELECT current_setting('block_size')::numeric AS bs, 23 AS hdr, 4 AS ma
-), bloat_info AS (
-  SELECT
-    ma,bs,schemaname,tablename,
-    (datawidth+(hdr+ma-(case when hdr%ma=0 THEN ma ELSE hdr%ma END)))::numeric AS datahdr,
-    (maxfracsum*(nullhdr+ma-(case when nullhdr%ma=0 THEN ma ELSE nullhdr%ma END))) AS nullhdr2
-  FROM (
-    SELECT
-      schemaname, tablename, hdr, ma, bs,
-      SUM((1-null_frac)*avg_width) AS datawidth,
-      MAX(null_frac) AS maxfracsum,
-      hdr+(
-        SELECT 1+count(*)/8
-        FROM pg_stats s2
-        WHERE null_frac<>0 AND s2.schemaname = s.schemaname AND s2.tablename = s.tablename
-      ) AS nullhdr
-    FROM pg_stats s, constants
-    GROUP BY 1,2,3,4,5
-  ) AS foo
-), table_bloat AS (
-  SELECT
-    schemaname, tablename, cc.relpages, bs,
-    CEIL((cc.reltuples*((datahdr+ma-
-      (CASE WHEN datahdr%ma=0 THEN ma ELSE datahdr%ma END))+nullhdr2+4))/(bs-20::float)) AS otta
-  FROM bloat_info
-  JOIN pg_class cc ON cc.relname = bloat_info.tablename
-  JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname = bloat_info.schemaname AND nn.nspname <> 'information_schema'
-), index_bloat AS (
-  SELECT
-    schemaname, tablename, bs,
-    COALESCE(c2.relname,'?') AS iname, COALESCE(c2.reltuples,0) AS ituples, COALESCE(c2.relpages,0) AS ipages,
-    COALESCE(CEIL((c2.reltuples*(datahdr-12))/(bs-20::float)),0) AS iotta -- very rough approximation, assumes all cols
-  FROM bloat_info
-  JOIN pg_class cc ON cc.relname = bloat_info.tablename
-  JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname = bloat_info.schemaname AND nn.nspname <> 'information_schema'
-  JOIN pg_index i ON indrelid = cc.oid
-  JOIN pg_class c2 ON c2.oid = i.indexrelid
-)
-SELECT
-  type, schemaname, object_name, bloat, pg_size_pretty(raw_waste) as waste
-FROM
-(SELECT
-  'table' as type,
-  schemaname,
-  tablename as object_name,
-  ROUND(CASE WHEN otta=0 THEN 0.0 ELSE table_bloat.relpages/otta::numeric END,1) AS bloat,
-  CASE WHEN relpages < otta THEN '0' ELSE (bs*(table_bloat.relpages-otta)::bigint)::bigint END AS raw_waste
-FROM
-  table_bloat
-    UNION
-SELECT
-  'index' as type,
-  schemaname,
-  tablename || '::' || iname as object_name,
-  ROUND(CASE WHEN iotta=0 OR ipages=0 THEN 0.0 ELSE ipages/iotta::numeric END,1) AS bloat,
-  CASE WHEN ipages < iotta THEN '0' ELSE (bs*(ipages-iotta))::bigint END AS raw_waste
-FROM
-  index_bloat) bloat_summary
-WHERE NOT schemaname LIKE ANY($1)
-ORDER BY raw_waste DESC, bloat DESC`
-
-// Ref: https://github.com/heroku/heroku-pg-extras/blob/main/commands/blocking.js#L7
-const BLOCKING_QUERY = `SELECT
-  bl.pid AS blocked_pid,
-  ka.query AS blocking_statement,
-  age(now(), ka.query_start)::text AS blocking_duration,
-  kl.pid AS blocking_pid,
-  a.query AS blocked_statement,
-  age(now(), a.query_start)::text AS blocked_duration
-FROM pg_catalog.pg_locks bl
-JOIN pg_catalog.pg_stat_activity a
-  ON bl.pid = a.pid
-JOIN pg_catalog.pg_locks kl
-JOIN pg_catalog.pg_stat_activity ka
-  ON kl.pid = ka.pid
-  ON bl.transactionid = kl.transactionid AND bl.pid != kl.pid
-WHERE NOT bl.granted`
-
-// Ref: https://github.com/heroku/heroku-pg-extras/blob/main/commands/cache_hit.js#L7
-const CACHE_QUERY = `SELECT
-  'index hit rate' AS name,
-  (sum(idx_blks_hit)) / nullif(sum(idx_blks_hit + idx_blks_read),0) AS ratio
-FROM pg_statio_user_indexes
-UNION ALL
-SELECT
-  'table hit rate' AS name,
-  sum(heap_blks_hit) / nullif(sum(heap_blks_hit) + sum(heap_blks_read),0) AS ratio
-FROM pg_statio_user_tables;`
-
-const CALLS_QUERY = `SELECT
-  query,
-  (interval '1 millisecond' * total_exec_time)::text AS total_exec_time,
-  to_char((total_exec_time/sum(total_exec_time) OVER()) * 100, 'FM90D0') || '%'  AS prop_exec_time,
-  to_char(calls, 'FM999G999G990') AS ncalls,
-  (interval '1 millisecond' * (blk_read_time + blk_write_time))::text AS sync_io_time
-FROM pg_stat_statements
-ORDER BY calls DESC
-LIMIT 10`
-
-const INDEX_SIZES_QUERY = `SELECT
-  n.nspname || '.' || c.relname AS name,
-  pg_size_pretty(sum(c.relpages::bigint*8192)::bigint) AS size
-FROM pg_class c
-LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
-WHERE NOT n.nspname LIKE ANY($1)
-AND c.relkind = 'i'
-GROUP BY n.nspname, c.relname
-ORDER BY sum(c.relpages) DESC`
-
-const INDEX_USAGE_QUERY = `SELECT
-  schemaname || '.' || relname AS name,
-  CASE
-    WHEN idx_scan IS NULL THEN 'Insufficient data'
-    WHEN idx_scan = 0 THEN 'Insufficient data'
-    ELSE ROUND(100.0 * idx_scan / (seq_scan + idx_scan), 1) || '%'
-  END percent_of_times_index_used,
-  n_live_tup rows_in_table
-FROM pg_stat_user_tables
-WHERE NOT schemaname LIKE ANY($1)
-ORDER BY
-  CASE
-    WHEN idx_scan is null then 1
-    WHEN idx_scan = 0 then 1
-    ELSE 0
-  END,
-  n_live_tup DESC`
-
-const LOCKS_QUERY = `SELECT
-  pg_stat_activity.pid,
-  COALESCE(pg_class.relname, 'null') AS relname,
-  COALESCE(pg_locks.transactionid, 'null') AS transactionid,
-  pg_locks.granted,
-  pg_stat_activity.query,
-  age(now(), pg_stat_activity.query_start)::text AS age
-FROM pg_stat_activity, pg_locks LEFT OUTER JOIN pg_class ON (pg_locks.relation = pg_class.oid)
-WHERE pg_stat_activity.query <> '<insufficient privilege>'
-AND pg_locks.pid = pg_stat_activity.pid
-AND pg_locks.mode = 'ExclusiveLock'
-ORDER BY query_start`
-
-const LONG_RUNNING_QUERY = `SELECT
-  pid,
-  age(now(), pg_stat_activity.query_start)::text AS duration,
-  query AS query
-FROM
-  pg_stat_activity
-WHERE
-  pg_stat_activity.query <> ''::text
-  AND state <> 'idle'
-  AND age(now(), pg_stat_activity.query_start) > interval '5 minutes'
-ORDER BY
-  age(now(), pg_stat_activity.query_start) DESC`
-
-const OUTLIERS_QUERY = `SELECT
-  (interval '1 millisecond' * total_exec_time)::text AS total_exec_time,
-  to_char((total_exec_time/sum(total_exec_time) OVER()) * 100, 'FM90D0') || '%'  AS prop_exec_time,
-  to_char(calls, 'FM999G999G999G990') AS ncalls,
-  (interval '1 millisecond' * (blk_read_time + blk_write_time))::text AS sync_io_time,
-  query
-FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1)
-ORDER BY total_exec_time DESC
-LIMIT 10`
-
-const REPLICATION_SLOTS_QUERY = `SELECT
-  s.slot_name,
-  s.active,
-  COALESCE(r.state, 'N/A') as state,
-  CASE WHEN r.client_addr IS NULL
-    THEN 'N/A'
-    ELSE r.client_addr::text
-  END replication_client_address,
-  GREATEST(0, ROUND((redo_lsn-restart_lsn)/1024/1024/1024, 2)) as replication_lag_gb
-FROM pg_control_checkpoint(), pg_replication_slots s
-LEFT JOIN pg_stat_replication r ON (r.pid = s.active_pid)`
-
-const ROLE_CONNECTIONS_QUERY = `SELECT
-  rolname,
-  (
-    SELECT
-      count(*)
-    FROM
-      pg_stat_activity
-    WHERE
-      pg_roles.rolname = pg_stat_activity.usename
-  ) AS active_connections,
-  CASE WHEN rolconnlimit = -1
-    THEN current_setting('max_connections')::int8
-    ELSE rolconnlimit
-  END AS connection_limit
-FROM pg_roles
-ORDER BY 2 DESC`
-
-const SEQ_SCANS_QUERY = `SELECT
-  schemaname || '.' || relname AS name,
-  seq_scan as count
-FROM pg_stat_user_tables
-WHERE NOT schemaname LIKE ANY($1)
-ORDER BY seq_scan DESC`
-
-const TABLE_INDEX_SIZES_QUERY = `SELECT
-  n.nspname || '.' || c.relname AS table,
-  pg_size_pretty(pg_indexes_size(c.oid)) AS index_size
-FROM pg_class c
-LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
-WHERE NOT n.nspname LIKE ANY($1)
-AND c.relkind = 'r'
-ORDER BY pg_indexes_size(c.oid) DESC`
-
-const TABLE_RECORD_COUNTS_QUERY = `SELECT
-  schemaname AS schema,
-  relname AS name,
-  n_live_tup AS estimated_count
-FROM pg_stat_user_tables
-WHERE NOT schemaname LIKE ANY($1)
-ORDER BY n_live_tup DESC`
-
-const TABLE_SIZES_QUERY = `SELECT
-  n.nspname AS schema,
-  c.relname AS name,
-  pg_size_pretty(pg_table_size(c.oid)) AS size
-FROM pg_class c
-LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
-WHERE NOT n.nspname LIKE ANY($1)
-AND c.relkind = 'r'
-ORDER BY pg_table_size(c.oid) DESC`
-
-const TOTAL_INDEX_SIZE_QUERY = `SELECT
-  pg_size_pretty(sum(c.relpages::bigint*8192)::bigint) AS size
-FROM pg_class c
-LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
-WHERE NOT n.nspname LIKE ANY($1)
-AND c.relkind = 'i'`
-
-const TOTAL_TABLE_SIZES_QUERY = `SELECT
-  n.nspname AS schema,
-  c.relname AS name,
-  pg_size_pretty(pg_total_relation_size(c.oid)) AS size
-FROM pg_class c
-LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
-WHERE NOT n.nspname LIKE ANY($1)
-AND c.relkind = 'r'
-ORDER BY pg_total_relation_size(c.oid) DESC`
-
-const UNUSED_INDEXES_QUERY = `SELECT
-  schemaname || '.' || relname AS table,
-  indexrelname AS index,
-  pg_size_pretty(pg_relation_size(i.indexrelid)) AS index_size,
-  idx_scan as index_scans
-FROM pg_stat_user_indexes ui
-JOIN pg_index i ON ui.indexrelid = i.indexrelid
-WHERE
-  NOT indisunique AND idx_scan < 50 AND pg_relation_size(relid) > 5 * 8192
-  AND NOT schemaname LIKE ANY($1)
-ORDER BY
-  pg_relation_size(i.indexrelid) / nullif(idx_scan, 0) DESC NULLS FIRST,
-  pg_relation_size(i.indexrelid) DESC`
-
-const VACUUM_STATS_QUERY = `WITH table_opts AS (
-  SELECT
-    pg_class.oid, relname, nspname, array_to_string(reloptions, '') AS relopts
-  FROM
-    pg_class INNER JOIN pg_namespace ns ON relnamespace = ns.oid
-), vacuum_settings AS (
-  SELECT
-    oid, relname, nspname,
-    CASE
-      WHEN relopts LIKE '%autovacuum_vacuum_threshold%'
-        THEN substring(relopts, '.*autovacuum_vacuum_threshold=([0-9.]+).*')::integer
-        ELSE current_setting('autovacuum_vacuum_threshold')::integer
-      END AS autovacuum_vacuum_threshold,
-    CASE
-      WHEN relopts LIKE '%autovacuum_vacuum_scale_factor%'
-        THEN substring(relopts, '.*autovacuum_vacuum_scale_factor=([0-9.]+).*')::real
-        ELSE current_setting('autovacuum_vacuum_scale_factor')::real
-      END AS autovacuum_vacuum_scale_factor
-  FROM
-    table_opts
-)
-SELECT
-  vacuum_settings.nspname AS schema,
-  vacuum_settings.relname AS table,
-  coalesce(to_char(psut.last_vacuum, 'YYYY-MM-DD HH24:MI'), '') AS last_vacuum,
-  coalesce(to_char(psut.last_autovacuum, 'YYYY-MM-DD HH24:MI'), '') AS last_autovacuum,
-  to_char(pg_class.reltuples, '9G999G999G999') AS rowcount,
-  to_char(psut.n_dead_tup, '9G999G999G999') AS dead_rowcount,
-  to_char(autovacuum_vacuum_threshold
-       + (autovacuum_vacuum_scale_factor::numeric * pg_class.reltuples), '9G999G999G999') AS autovacuum_threshold,
-  CASE
-    WHEN autovacuum_vacuum_threshold + (autovacuum_vacuum_scale_factor::numeric * pg_class.reltuples) < psut.n_dead_tup
-    THEN 'yes'
-    ELSE 'no'
-  END AS expect_autovacuum
-FROM
-  pg_stat_user_tables psut INNER JOIN pg_class ON psut.relid = pg_class.oid
-INNER JOIN vacuum_settings ON pg_class.oid = vacuum_settings.oid
-WHERE NOT vacuum_settings.nspname LIKE ANY($1)
-ORDER BY
-  case
-    when pg_class.reltuples = -1 then 1
-    else 0
-  end,
-  1`
diff --git a/internal/inspect/replication_slots/replication_slots.go b/internal/inspect/replication_slots/replication_slots.go
index b66e7a0d..4ad5db57 100644
--- a/internal/inspect/replication_slots/replication_slots.go
+++ b/internal/inspect/replication_slots/replication_slots.go
@@ -2,18 +2,21 @@ package replication_slots
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 
 	"github.com/go-errors/errors"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed replication_slots.sql
+var ReplicationSlotsQuery string
+
 type Result struct {
 	Slot_name                  string
 	Active                     bool
@@ -27,7 +30,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.REPLICATION_SLOTS_QUERY)
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, ReplicationSlotsQuery)
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/replication_slots/replication_slots.sql b/internal/inspect/replication_slots/replication_slots.sql
new file mode 100644
index 00000000..c45add91
--- /dev/null
+++ b/internal/inspect/replication_slots/replication_slots.sql
@@ -0,0 +1,11 @@
+SELECT
+  s.slot_name,
+  s.active,
+  COALESCE(r.state, 'N/A') as state,
+  CASE WHEN r.client_addr IS NULL
+    THEN 'N/A'
+    ELSE r.client_addr::text
+  END replication_client_address,
+  GREATEST(0, ROUND((redo_lsn-restart_lsn)/1024/1024/1024, 2)) as replication_lag_gb
+FROM pg_control_checkpoint(), pg_replication_slots s
+LEFT JOIN pg_stat_replication r ON (r.pid = s.active_pid)
diff --git a/internal/inspect/report.go b/internal/inspect/report.go
new file mode 100644
index 00000000..de721d95
--- /dev/null
+++ b/internal/inspect/report.go
@@ -0,0 +1,79 @@
+package inspect
+
+import (
+	"context"
+	"embed"
+	"fmt"
+	"io/fs"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"
+
+	"github.com/go-errors/errors"
+	"github.com/jackc/pgconn"
+	"github.com/jackc/pgx/v4"
+	"github.com/spf13/afero"
+	"github.com/supabase/cli/internal/db/reset"
+	"github.com/supabase/cli/internal/utils"
+)
+
+//go:embed **/*.sql
+var queries embed.FS
+
+func Report(ctx context.Context, out string, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
+	date := time.Now().Format("2006-01-02")
+	if err := utils.MkdirIfNotExistFS(fsys, out); err != nil {
+		return err
+	}
+	conn, err := utils.ConnectByConfig(ctx, config, options...)
+	if err != nil {
+		return err
+	}
+	defer conn.Close(context.Background())
+	fmt.Fprintln(os.Stderr, "Running queries...")
+	if err := fs.WalkDir(queries, ".", func(path string, d fs.DirEntry, err error) error {
+		if err != nil {
+			return errors.Errorf("failed to walk queries: %w", err)
+		}
+		if d.IsDir() {
+			return nil
+		}
+		query, err := queries.ReadFile(path)
+		if err != nil {
+			return errors.Errorf("failed to read query: %w", err)
+		}
+		name := strings.Split(d.Name(), ".")[0]
+		outPath := filepath.Join(out, fmt.Sprintf("%s_%s.csv", name, date))
+		return copyToCSV(ctx, string(query), outPath, conn.PgConn(), fsys)
+	}); err != nil {
+		return err
+	}
+	if !filepath.IsAbs(out) {
+		out, _ = filepath.Abs(out)
+	}
+	fmt.Fprintln(os.Stderr, "Reports saved to "+utils.Bold(out))
+	return nil
+}
+
+func copyToCSV(ctx context.Context, query, outPath string, conn *pgconn.PgConn, fsys afero.Fs) error {
+	// Create output file
+	f, err := fsys.OpenFile(outPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
+	if err != nil {
+		return errors.Errorf("failed to create output file: %w", err)
+	}
+	defer f.Close()
+	// Execute query
+	csvQuery := wrapQuery(query)
+	if _, err = conn.CopyTo(ctx, f, csvQuery); err != nil {
+		return errors.Errorf("failed to copy output: %w", err)
+	}
+	return nil
+}
+
+var ignoreSchemas = fmt.Sprintf("'{%s}'::text[]", strings.Join(reset.LikeEscapeSchema(utils.InternalSchemas), ","))
+
+func wrapQuery(query string) string {
+	fullQuery := strings.ReplaceAll(query, "$1", ignoreSchemas)
+	return fmt.Sprintf("COPY (%s) TO STDOUT WITH CSV HEADER", fullQuery)
+}
diff --git a/internal/inspect/role_connections/role_connections.go b/internal/inspect/role_connections/role_connections.go
index b0a2aa1f..25a7ff32 100644
--- a/internal/inspect/role_connections/role_connections.go
+++ b/internal/inspect/role_connections/role_connections.go
@@ -2,18 +2,21 @@ package role_connections
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 
 	"github.com/go-errors/errors"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed role_connections.sql
+var RoleConnectionsQuery string
+
 type Result struct {
 	Rolname            string
 	Active_connections int
@@ -25,7 +28,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.ROLE_CONNECTIONS_QUERY)
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, RoleConnectionsQuery)
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/role_connections/role_connections.sql b/internal/inspect/role_connections/role_connections.sql
new file mode 100644
index 00000000..40b10366
--- /dev/null
+++ b/internal/inspect/role_connections/role_connections.sql
@@ -0,0 +1,16 @@
+SELECT
+  rolname,
+  (
+    SELECT
+      count(*)
+    FROM
+      pg_stat_activity
+    WHERE
+      pg_roles.rolname = pg_stat_activity.usename
+  ) AS active_connections,
+  CASE WHEN rolconnlimit = -1
+    THEN current_setting('max_connections')::int8
+    ELSE rolconnlimit
+  END AS connection_limit
+FROM pg_roles
+ORDER BY 2 DESC
diff --git a/internal/inspect/seq_scans/seq_scans.go b/internal/inspect/seq_scans/seq_scans.go
index 3cb4cf55..04911166 100644
--- a/internal/inspect/seq_scans/seq_scans.go
+++ b/internal/inspect/seq_scans/seq_scans.go
@@ -2,6 +2,7 @@ package seq_scans
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 
 	"github.com/go-errors/errors"
@@ -9,12 +10,14 @@ import (
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/db/reset"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed seq_scans.sql
+var SeqScansQuery string
+
 type Result struct {
 	Name  string
 	Count int64
@@ -25,7 +28,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.SEQ_SCANS_QUERY, reset.LikeEscapeSchema(utils.InternalSchemas))
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, SeqScansQuery, reset.LikeEscapeSchema(utils.InternalSchemas))
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/seq_scans/seq_scans.sql b/internal/inspect/seq_scans/seq_scans.sql
new file mode 100644
index 00000000..c8edfc8e
--- /dev/null
+++ b/internal/inspect/seq_scans/seq_scans.sql
@@ -0,0 +1,6 @@
+SELECT
+  schemaname || '.' || relname AS name,
+  seq_scan as count
+FROM pg_stat_user_tables
+WHERE NOT schemaname LIKE ANY($1)
+ORDER BY seq_scan DESC
diff --git a/internal/inspect/table_index_sizes/table_index_sizes.go b/internal/inspect/table_index_sizes/table_index_sizes.go
index 905ad3b5..420f3fdf 100644
--- a/internal/inspect/table_index_sizes/table_index_sizes.go
+++ b/internal/inspect/table_index_sizes/table_index_sizes.go
@@ -2,6 +2,7 @@ package table_index_sizes
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 
 	"github.com/go-errors/errors"
@@ -9,12 +10,14 @@ import (
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/db/reset"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed table_index_sizes.sql
+var TableIndexSizesQuery string
+
 type Result struct {
 	Table      string
 	Index_size string
@@ -25,7 +28,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.TABLE_INDEX_SIZES_QUERY, reset.LikeEscapeSchema(utils.InternalSchemas))
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, TableIndexSizesQuery, reset.LikeEscapeSchema(utils.InternalSchemas))
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/table_index_sizes/table_index_sizes.sql b/internal/inspect/table_index_sizes/table_index_sizes.sql
new file mode 100644
index 00000000..0c9bc6bf
--- /dev/null
+++ b/internal/inspect/table_index_sizes/table_index_sizes.sql
@@ -0,0 +1,8 @@
+SELECT
+  n.nspname || '.' || c.relname AS table,
+  pg_size_pretty(pg_indexes_size(c.oid)) AS index_size
+FROM pg_class c
+LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
+WHERE NOT n.nspname LIKE ANY($1)
+AND c.relkind = 'r'
+ORDER BY pg_indexes_size(c.oid) DESC
diff --git a/internal/inspect/table_record_counts/table_record_counts.go b/internal/inspect/table_record_counts/table_record_counts.go
index 72ca70de..7d65b918 100644
--- a/internal/inspect/table_record_counts/table_record_counts.go
+++ b/internal/inspect/table_record_counts/table_record_counts.go
@@ -2,6 +2,7 @@ package table_record_counts
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 
 	"github.com/go-errors/errors"
@@ -9,12 +10,14 @@ import (
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/db/reset"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed table_record_counts.sql
+var TableRecordCountsQuery string
+
 type Result struct {
 	Schema          string
 	Name            string
@@ -26,7 +29,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.TABLE_RECORD_COUNTS_QUERY, reset.LikeEscapeSchema(utils.PgSchemas))
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, TableRecordCountsQuery, reset.LikeEscapeSchema(utils.PgSchemas))
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/table_record_counts/table_record_counts.sql b/internal/inspect/table_record_counts/table_record_counts.sql
new file mode 100644
index 00000000..1b24f04a
--- /dev/null
+++ b/internal/inspect/table_record_counts/table_record_counts.sql
@@ -0,0 +1,7 @@
+SELECT
+  schemaname AS schema,
+  relname AS name,
+  n_live_tup AS estimated_count
+FROM pg_stat_user_tables
+WHERE NOT schemaname LIKE ANY($1)
+ORDER BY n_live_tup DESC
diff --git a/internal/inspect/table_sizes/table_sizes.go b/internal/inspect/table_sizes/table_sizes.go
index 74c0396d..651b3964 100644
--- a/internal/inspect/table_sizes/table_sizes.go
+++ b/internal/inspect/table_sizes/table_sizes.go
@@ -2,6 +2,7 @@ package table_sizes
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 
 	"github.com/go-errors/errors"
@@ -9,12 +10,14 @@ import (
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/db/reset"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed table_sizes.sql
+var TableSizesQuery string
+
 type Result struct {
 	Schema string
 	Name   string
@@ -26,7 +29,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.TABLE_SIZES_QUERY, reset.LikeEscapeSchema(utils.PgSchemas))
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, TableSizesQuery, reset.LikeEscapeSchema(utils.PgSchemas))
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/table_sizes/table_sizes.sql b/internal/inspect/table_sizes/table_sizes.sql
new file mode 100644
index 00000000..2c8fb306
--- /dev/null
+++ b/internal/inspect/table_sizes/table_sizes.sql
@@ -0,0 +1,9 @@
+SELECT
+  n.nspname AS schema,
+  c.relname AS name,
+  pg_size_pretty(pg_table_size(c.oid)) AS size
+FROM pg_class c
+LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
+WHERE NOT n.nspname LIKE ANY($1)
+AND c.relkind = 'r'
+ORDER BY pg_table_size(c.oid) DESC
diff --git a/internal/inspect/total_index_size/total_index_size.go b/internal/inspect/total_index_size/total_index_size.go
index 73ee9e67..b5b1b61b 100644
--- a/internal/inspect/total_index_size/total_index_size.go
+++ b/internal/inspect/total_index_size/total_index_size.go
@@ -2,6 +2,7 @@ package total_index_size
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 
 	"github.com/go-errors/errors"
@@ -9,12 +10,14 @@ import (
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/db/reset"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed total_index_size.sql
+var TotalIndexSizeQuery string
+
 type Result struct {
 	Size string
 }
@@ -24,7 +27,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.TOTAL_INDEX_SIZE_QUERY, reset.LikeEscapeSchema(utils.InternalSchemas))
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, TotalIndexSizeQuery, reset.LikeEscapeSchema(utils.InternalSchemas))
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/total_index_size/total_index_size.sql b/internal/inspect/total_index_size/total_index_size.sql
new file mode 100644
index 00000000..d1e8ab3d
--- /dev/null
+++ b/internal/inspect/total_index_size/total_index_size.sql
@@ -0,0 +1,6 @@
+SELECT
+  pg_size_pretty(sum(c.relpages::bigint*8192)::bigint) AS size
+FROM pg_class c
+LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
+WHERE NOT n.nspname LIKE ANY($1)
+AND c.relkind = 'i'
diff --git a/internal/inspect/total_table_sizes/total_table_sizes.go b/internal/inspect/total_table_sizes/total_table_sizes.go
index 8fd44195..2a11f704 100644
--- a/internal/inspect/total_table_sizes/total_table_sizes.go
+++ b/internal/inspect/total_table_sizes/total_table_sizes.go
@@ -2,6 +2,7 @@ package total_table_sizes
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 
 	"github.com/go-errors/errors"
@@ -9,12 +10,14 @@ import (
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/db/reset"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed total_table_sizes.sql
+var TotalTableSizesQuery string
+
 type Result struct {
 	Schema string
 	Name   string
@@ -26,7 +29,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.TOTAL_TABLE_SIZES_QUERY, reset.LikeEscapeSchema(utils.PgSchemas))
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, TotalTableSizesQuery, reset.LikeEscapeSchema(utils.PgSchemas))
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/total_table_sizes/total_table_sizes.sql b/internal/inspect/total_table_sizes/total_table_sizes.sql
new file mode 100644
index 00000000..471d0655
--- /dev/null
+++ b/internal/inspect/total_table_sizes/total_table_sizes.sql
@@ -0,0 +1,9 @@
+SELECT
+  n.nspname AS schema,
+  c.relname AS name,
+  pg_size_pretty(pg_total_relation_size(c.oid)) AS size
+FROM pg_class c
+LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
+WHERE NOT n.nspname LIKE ANY($1)
+AND c.relkind = 'r'
+ORDER BY pg_total_relation_size(c.oid) DESC
diff --git a/internal/inspect/unused_indexes/unused_indexes.go b/internal/inspect/unused_indexes/unused_indexes.go
index ca0c4fef..cf429ed5 100644
--- a/internal/inspect/unused_indexes/unused_indexes.go
+++ b/internal/inspect/unused_indexes/unused_indexes.go
@@ -2,6 +2,7 @@ package unused_indexes
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 
 	"github.com/go-errors/errors"
@@ -9,12 +10,14 @@ import (
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/db/reset"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed unused_indexes.sql
+var UnusedIndexesQuery string
+
 type Result struct {
 	Table       string
 	Index       string
@@ -27,7 +30,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.UNUSED_INDEXES_QUERY, reset.LikeEscapeSchema(utils.InternalSchemas))
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, UnusedIndexesQuery, reset.LikeEscapeSchema(utils.InternalSchemas))
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/unused_indexes/unused_indexes.sql b/internal/inspect/unused_indexes/unused_indexes.sql
new file mode 100644
index 00000000..6e775967
--- /dev/null
+++ b/internal/inspect/unused_indexes/unused_indexes.sql
@@ -0,0 +1,13 @@
+SELECT
+  schemaname || '.' || relname AS table,
+  indexrelname AS index,
+  pg_size_pretty(pg_relation_size(i.indexrelid)) AS index_size,
+  idx_scan as index_scans
+FROM pg_stat_user_indexes ui
+JOIN pg_index i ON ui.indexrelid = i.indexrelid
+WHERE
+  NOT indisunique AND idx_scan < 50 AND pg_relation_size(relid) > 5 * 8192
+  AND NOT schemaname LIKE ANY($1)
+ORDER BY
+  pg_relation_size(i.indexrelid) / nullif(idx_scan, 0) DESC NULLS FIRST,
+  pg_relation_size(i.indexrelid) DESC
diff --git a/internal/inspect/vacuum_stats/vacuum_stats.go b/internal/inspect/vacuum_stats/vacuum_stats.go
index cc98b6b0..f8d2c222 100644
--- a/internal/inspect/vacuum_stats/vacuum_stats.go
+++ b/internal/inspect/vacuum_stats/vacuum_stats.go
@@ -2,6 +2,7 @@ package vacuum_stats
 
 import (
 	"context"
+	_ "embed"
 	"fmt"
 	"strings"
 
@@ -10,12 +11,14 @@ import (
 	"github.com/jackc/pgx/v4"
 	"github.com/spf13/afero"
 	"github.com/supabase/cli/internal/db/reset"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/migration/list"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/pgxv5"
 )
 
+//go:embed vacuum_stats.sql
+var VacuumStatsQuery string
+
 type Result struct {
 	Schema               string
 	Table                string
@@ -32,7 +35,8 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if err != nil {
 		return err
 	}
-	rows, err := conn.Query(ctx, inspect.VACUUM_STATS_QUERY, reset.LikeEscapeSchema(utils.InternalSchemas))
+	defer conn.Close(context.Background())
+	rows, err := conn.Query(ctx, VacuumStatsQuery, reset.LikeEscapeSchema(utils.InternalSchemas))
 	if err != nil {
 		return errors.Errorf("failed to query rows: %w", err)
 	}
diff --git a/internal/inspect/vacuum_stats/vacuum_stats.sql b/internal/inspect/vacuum_stats/vacuum_stats.sql
new file mode 100644
index 00000000..70784749
--- /dev/null
+++ b/internal/inspect/vacuum_stats/vacuum_stats.sql
@@ -0,0 +1,45 @@
+WITH table_opts AS (
+  SELECT
+    pg_class.oid, relname, nspname, array_to_string(reloptions, '') AS relopts
+  FROM
+    pg_class INNER JOIN pg_namespace ns ON relnamespace = ns.oid
+), vacuum_settings AS (
+  SELECT
+    oid, relname, nspname,
+    CASE
+      WHEN relopts LIKE '%autovacuum_vacuum_threshold%'
+        THEN substring(relopts, '.*autovacuum_vacuum_threshold=([0-9.]+).*')::integer
+        ELSE current_setting('autovacuum_vacuum_threshold')::integer
+      END AS autovacuum_vacuum_threshold,
+    CASE
+      WHEN relopts LIKE '%autovacuum_vacuum_scale_factor%'
+        THEN substring(relopts, '.*autovacuum_vacuum_scale_factor=([0-9.]+).*')::real
+        ELSE current_setting('autovacuum_vacuum_scale_factor')::real
+      END AS autovacuum_vacuum_scale_factor
+  FROM
+    table_opts
+)
+SELECT
+  vacuum_settings.nspname AS schema,
+  vacuum_settings.relname AS table,
+  coalesce(to_char(psut.last_vacuum, 'YYYY-MM-DD HH24:MI'), '') AS last_vacuum,
+  coalesce(to_char(psut.last_autovacuum, 'YYYY-MM-DD HH24:MI'), '') AS last_autovacuum,
+  to_char(pg_class.reltuples, '9G999G999G999') AS rowcount,
+  to_char(psut.n_dead_tup, '9G999G999G999') AS dead_rowcount,
+  to_char(autovacuum_vacuum_threshold
+       + (autovacuum_vacuum_scale_factor::numeric * pg_class.reltuples), '9G999G999G999') AS autovacuum_threshold,
+  CASE
+    WHEN autovacuum_vacuum_threshold + (autovacuum_vacuum_scale_factor::numeric * pg_class.reltuples) < psut.n_dead_tup
+    THEN 'yes'
+    ELSE 'no'
+  END AS expect_autovacuum
+FROM
+  pg_stat_user_tables psut INNER JOIN pg_class ON psut.relid = pg_class.oid
+INNER JOIN vacuum_settings ON pg_class.oid = vacuum_settings.oid
+WHERE NOT vacuum_settings.nspname LIKE ANY($1)
+ORDER BY
+  case
+    when pg_class.reltuples = -1 then 1
+    else 0
+  end,
+  1
diff --git a/internal/link/link.go b/internal/link/link.go
index acdf552c..d92f13fb 100644
--- a/internal/link/link.go
+++ b/internal/link/link.go
@@ -64,7 +64,7 @@ func PostRun(projectRef string, stdout io.Writer, fsys afero.Fs) error {
 	if updatedConfig.IsEmpty() {
 		return nil
 	}
-	fmt.Fprintln(os.Stderr, "Local config differs from linked project. Try updating", utils.Bold(utils.ConfigPath))
+	fmt.Fprintln(os.Stderr, utils.Yellow("Warning:"), "Local config differs from linked project. Try updating", utils.Bold(utils.ConfigPath))
 	enc := toml.NewEncoder(stdout)
 	enc.Indent = ""
 	if err := enc.Encode(updatedConfig); err != nil {
diff --git a/internal/logout/logout.go b/internal/logout/logout.go
index 49448854..c117abd9 100644
--- a/internal/logout/logout.go
+++ b/internal/logout/logout.go
@@ -11,9 +11,10 @@ import (
 )
 
 func Run(ctx context.Context, stdout *os.File, fsys afero.Fs) error {
-	if !utils.NewConsole().PromptYesNo("Do you want to log out? This will remove the access token from your system.", false) {
-		fmt.Fprintln(os.Stderr, "Not deleting access token.")
-		return nil
+	if shouldLogout, err := utils.NewConsole().PromptYesNo(ctx, "Do you want to log out? This will remove the access token from your system.", false); err != nil {
+		return err
+	} else if !shouldLogout {
+		return errors.New(context.Canceled)
 	}
 
 	if err := utils.DeleteAccessToken(fsys); errors.Is(err, utils.ErrNotLoggedIn) {
diff --git a/internal/migration/fetch/fetch.go b/internal/migration/fetch/fetch.go
index 19872e2c..18df66d7 100644
--- a/internal/migration/fetch/fetch.go
+++ b/internal/migration/fetch/fetch.go
@@ -21,10 +21,11 @@ func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...fu
 	if empty, err := afero.IsEmpty(fsys, utils.MigrationsDir); err != nil {
 		return errors.Errorf("failed to read migrations: %w", err)
 	} else if !empty {
-		console := utils.NewConsole()
 		title := fmt.Sprintf("Do you want to overwrite existing files in %s directory?", utils.Bold(utils.MigrationsDir))
-		if !console.PromptYesNo(title, true) {
-			return context.Canceled
+		if shouldOverwrite, err := utils.NewConsole().PromptYesNo(ctx, title, true); err != nil {
+			return err
+		} else if !shouldOverwrite {
+			return errors.New(context.Canceled)
 		}
 	}
 	result, err := fetchMigrationHistory(ctx, config, options...)
diff --git a/internal/migration/repair/repair.go b/internal/migration/repair/repair.go
index 0c054d04..60425068 100644
--- a/internal/migration/repair/repair.go
+++ b/internal/migration/repair/repair.go
@@ -36,8 +36,9 @@ func Run(ctx context.Context, config pgconn.Config, version []string, status str
 	repairAll := len(version) == 0
 	if repairAll {
 		msg := "Do you want to repair the entire migration history table to match local migration files?"
-		if shouldRepair := utils.NewConsole().PromptYesNo(msg, false); !shouldRepair {
-			utils.CmdSuggestion = ""
+		if shouldRepair, err := utils.NewConsole().PromptYesNo(ctx, msg, false); err != nil {
+			return err
+		} else if !shouldRepair {
 			return errors.New(context.Canceled)
 		}
 		local, err := list.LoadLocalVersions(fsys)
diff --git a/internal/migration/squash/squash.go b/internal/migration/squash/squash.go
index 05576e12..027e57c2 100644
--- a/internal/migration/squash/squash.go
+++ b/internal/migration/squash/squash.go
@@ -44,7 +44,12 @@ func Run(ctx context.Context, version string, config pgconn.Config, fsys afero.F
 		return err
 	}
 	// 2. Update migration history
-	if utils.IsLocalDatabase(config) || !utils.NewConsole().PromptYesNo("Update remote migration history table?", true) {
+	if utils.IsLocalDatabase(config) {
+		return nil
+	}
+	if shouldUpdate, err := utils.NewConsole().PromptYesNo(ctx, "Update remote migration history table?", true); err != nil {
+		return err
+	} else if !shouldUpdate {
 		return nil
 	}
 	return baselineMigrations(ctx, config, version, fsys, options...)
diff --git a/internal/projects/create/create.go b/internal/projects/create/create.go
index c9a36b2f..2cd70785 100644
--- a/internal/projects/create/create.go
+++ b/internal/projects/create/create.go
@@ -48,7 +48,7 @@ func printKeyValue(key, value string) string {
 func promptMissingParams(ctx context.Context, body *api.V1CreateProjectBody) error {
 	var err error
 	if len(body.Name) == 0 {
-		if body.Name, err = promptProjectName(); err != nil {
+		if body.Name, err = promptProjectName(ctx); err != nil {
 			return err
 		}
 	} else {
@@ -72,9 +72,11 @@ func promptMissingParams(ctx context.Context, body *api.V1CreateProjectBody) err
 	return nil
 }
 
-func promptProjectName() (string, error) {
+func promptProjectName(ctx context.Context) (string, error) {
 	title := "Enter your project name: "
-	if name := utils.NewConsole().PromptText(title); len(name) > 0 {
+	if name, err := utils.NewConsole().PromptText(ctx, title); err != nil {
+		return "", err
+	} else if len(name) > 0 {
 		return name, nil
 	}
 	return "", errors.New("project name cannot be empty")
diff --git a/internal/projects/delete/delete.go b/internal/projects/delete/delete.go
index 616afcbf..da099eaa 100644
--- a/internal/projects/delete/delete.go
+++ b/internal/projects/delete/delete.go
@@ -13,13 +13,15 @@ import (
 	"github.com/zalando/go-keyring"
 )
 
-func PreRun(ref string) error {
+func PreRun(ctx context.Context, ref string) error {
 	if err := utils.AssertProjectRefIsValid(ref); err != nil {
 		return err
 	}
 	title := fmt.Sprintf("Do you want to delete project %s? This action is irreversible.", utils.Aqua(ref))
-	if shouldDelete := utils.NewConsole().PromptYesNo(title, false); !shouldDelete {
-		return errors.New("Not deleting project: " + utils.Aqua(ref))
+	if shouldDelete, err := utils.NewConsole().PromptYesNo(ctx, title, false); err != nil {
+		return err
+	} else if !shouldDelete {
+		return errors.New(context.Canceled)
 	}
 	return nil
 }
diff --git a/internal/start/start.go b/internal/start/start.go
index 33126119..059db6f8 100644
--- a/internal/start/start.go
+++ b/internal/start/start.go
@@ -814,7 +814,7 @@ EOF
 	}
 
 	// Start all functions.
-	if !isContainerExcluded(utils.EdgeRuntimeImage, excluded) {
+	if utils.Config.EdgeRuntime.Enabled && !isContainerExcluded(utils.EdgeRuntimeImage, excluded) {
 		dbUrl := fmt.Sprintf("postgresql://%s:%s@%s:%d/%s", dbConfig.User, dbConfig.Password, dbConfig.Host, dbConfig.Port, dbConfig.Database)
 		if err := serve.ServeFunctions(ctx, "", nil, "", dbUrl, w, fsys); err != nil {
 			return err
@@ -957,10 +957,8 @@ EOF
 
 func isContainerExcluded(imageName string, excluded map[string]bool) bool {
 	short := utils.ShortContainerImageName(imageName)
-	if val, ok := excluded[short]; ok && val {
-		return true
-	}
-	return false
+	val, ok := excluded[short]
+	return ok && val
 }
 
 func ExcludableContainers() []string {
diff --git a/internal/storage/rm/rm.go b/internal/storage/rm/rm.go
index 50ff8d14..720bbce0 100644
--- a/internal/storage/rm/rm.go
+++ b/internal/storage/rm/rm.go
@@ -51,7 +51,9 @@ func Run(ctx context.Context, paths []string, recursive bool, fsys afero.Fs) err
 	}
 	for bucket, prefixes := range groups {
 		confirm := fmt.Sprintf("Confirm deleting files in bucket %v?", utils.Bold(bucket))
-		if shouldDelete := utils.NewConsole().PromptYesNo(confirm, false); !shouldDelete {
+		if shouldDelete, err := utils.NewConsole().PromptYesNo(ctx, confirm, false); err != nil {
+			return err
+		} else if !shouldDelete {
 			continue
 		}
 		// Always try deleting first in case the paths resolve to extensionless files
diff --git a/internal/utils/config.go b/internal/utils/config.go
index c68a1fb3..d9a49785 100644
--- a/internal/utils/config.go
+++ b/internal/utils/config.go
@@ -258,6 +258,7 @@ type (
 		Inbucket     inbucket            `toml:"inbucket"`
 		Storage      storage             `toml:"storage"`
 		Auth         auth                `toml:"auth" mapstructure:"auth"`
+		EdgeRuntime  edgeRuntime         `toml:"edge_runtime"`
 		Functions    map[string]function `toml:"functions"`
 		Analytics    analytics           `toml:"analytics"`
 		Experimental experimental        `toml:"experimental" mapstructure:"-"`
@@ -433,6 +434,10 @@ type (
 		SkipNonceCheck bool   `toml:"skip_nonce_check"`
 	}
 
+	edgeRuntime struct {
+		Enabled bool `toml:"enabled"`
+	}
+
 	function struct {
 		VerifyJWT *bool  `toml:"verify_jwt"`
 		ImportMap string `toml:"import_map"`
diff --git a/internal/utils/console.go b/internal/utils/console.go
index ffafb425..a6e1c64e 100644
--- a/internal/utils/console.go
+++ b/internal/utils/console.go
@@ -2,11 +2,14 @@ package utils
 
 import (
 	"bufio"
+	"context"
 	"fmt"
 	"io"
 	"os"
 	"strings"
+	"time"
 
+	"github.com/go-errors/errors"
 	"golang.org/x/term"
 )
 
@@ -14,30 +17,44 @@ type Console struct {
 	IsTTY  bool
 	stdin  *bufio.Scanner
 	logger io.Writer
+	token  chan string
 }
 
 func NewConsole() Console {
-	return Console{
+	c := Console{
 		IsTTY:  term.IsTerminal(int(os.Stdin.Fd())),
 		stdin:  bufio.NewScanner(os.Stdin),
 		logger: GetDebugLogger(),
+		token:  make(chan string),
 	}
+	go func() {
+		// Scan a single line from input or file
+		if !c.stdin.Scan() {
+			fmt.Fprintln(c.logger, io.EOF)
+		}
+		if err := c.stdin.Err(); err != nil {
+			fmt.Fprintln(c.logger, err)
+		}
+		c.token <- strings.TrimSpace(c.stdin.Text())
+	}()
+	return c
 }
 
 // PromptYesNo asks yes/no questions using the label.
-func (c Console) PromptYesNo(label string, def bool) bool {
+func (c Console) PromptYesNo(ctx context.Context, label string, def bool) (bool, error) {
 	choices := "Y/n"
 	if !def {
 		choices = "y/N"
 	}
 	labelWithChoice := fmt.Sprintf("%s [%s] ", label, choices)
 	// Any error will be handled as default value
-	if input := c.PromptText(labelWithChoice); len(input) > 0 {
+	input, err := c.PromptText(ctx, labelWithChoice)
+	if len(input) > 0 {
 		if answer := parseYesNo(input); answer != nil {
-			return *answer
+			return *answer, nil
 		}
 	}
-	return def
+	return def, err
 }
 
 func parseYesNo(s string) *bool {
@@ -51,20 +68,32 @@ func parseYesNo(s string) *bool {
 	return nil
 }
 
+// Prevent interactive terminals from hanging more than 10 minutes
+const ttyTimeout = time.Minute * 10
+
 // PromptText asks for input using the label.
-func (c Console) PromptText(label string) string {
+func (c Console) PromptText(ctx context.Context, label string) (string, error) {
 	fmt.Fprint(os.Stderr, label)
-	// Scan a single line from input or file
-	if !c.stdin.Scan() {
-		fmt.Fprintln(c.logger, io.EOF)
+	// Wait a few ms for input
+	timeout := time.Millisecond
+	if c.IsTTY {
+		timeout = ttyTimeout
 	}
-	if err := c.stdin.Err(); err != nil {
-		fmt.Fprintln(c.logger, err)
+	timer := time.NewTimer(timeout)
+	defer timer.Stop()
+	// Read from stdin
+	var input string
+	select {
+	case input = <-c.token:
+	case <-ctx.Done():
+	case <-timer.C:
 	}
-	token := strings.TrimSpace(c.stdin.Text())
-	// Echo input to stderr for non-interactive terminals
+	// Echo to stderr for non-interactive terminals
 	if !c.IsTTY {
-		fmt.Fprintln(os.Stderr, token)
+		fmt.Fprintln(os.Stderr, input)
+	}
+	if err := ctx.Err(); err != nil {
+		return "", errors.New(err)
 	}
-	return token
+	return input, nil
 }
diff --git a/internal/utils/misc.go b/internal/utils/misc.go
index a1927cf6..85791d26 100644
--- a/internal/utils/misc.go
+++ b/internal/utils/misc.go
@@ -34,13 +34,13 @@ const (
 	DifferImage      = "supabase/pgadmin-schema-diff:cli-0.0.5"
 	MigraImage       = "supabase/migra:3.0.1663481299"
 	PgmetaImage      = "supabase/postgres-meta:v0.80.0"
-	StudioImage      = "supabase/studio:20240506-2976cd6"
+	StudioImage      = "supabase/studio:20240514-6f5cabd"
 	ImageProxyImage  = "darthsim/imgproxy:v3.8.0"
-	EdgeRuntimeImage = "supabase/edge-runtime:v1.49.0"
+	EdgeRuntimeImage = "supabase/edge-runtime:v1.53.3"
 	VectorImage      = "timberio/vector:0.28.1-alpine"
 	PgbouncerImage   = "bitnami/pgbouncer:1.20.1-debian-11-r39"
 	PgProveImage     = "supabase/pg_prove:3.36"
-	GotrueImage      = "supabase/gotrue:v2.149.0"
+	GotrueImage      = "supabase/gotrue:v2.151.0"
 	RealtimeImage    = "supabase/realtime:v2.28.32"
 	StorageImage     = "supabase/storage-api:v1.0.6"
 	LogflareImage    = "supabase/logflare:1.4.0"
@@ -56,14 +56,17 @@ var ServiceImages = []string{
 	KongImage,
 	InbucketImage,
 	PostgrestImage,
-	DifferImage,
-	MigraImage,
 	PgmetaImage,
 	StudioImage,
 	EdgeRuntimeImage,
 	LogflareImage,
 	VectorImage,
 	PgbouncerImage,
+}
+
+var JobImages = []string{
+	DifferImage,
+	MigraImage,
 	PgProveImage,
 }
 
@@ -187,6 +190,7 @@ var (
 	GotrueVersionPath     = filepath.Join(TempDir, "gotrue-version")
 	RestVersionPath       = filepath.Join(TempDir, "rest-version")
 	StorageVersionPath    = filepath.Join(TempDir, "storage-version")
+	CliVersionPath        = filepath.Join(TempDir, "cli-latest")
 	CurrBranchPath        = filepath.Join(SupabaseDirPath, ".branches", "_current_branch")
 	SchemasDir            = filepath.Join(SupabaseDirPath, "schemas")
 	MigrationsDir         = filepath.Join(SupabaseDirPath, "migrations")
diff --git a/internal/utils/release.go b/internal/utils/release.go
new file mode 100644
index 00000000..f855f51b
--- /dev/null
+++ b/internal/utils/release.go
@@ -0,0 +1,49 @@
+package utils
+
+import (
+	"context"
+	"net/http"
+	"os"
+	"sync"
+
+	"github.com/go-errors/errors"
+	"github.com/google/go-github/v62/github"
+	"golang.org/x/oauth2"
+)
+
+var (
+	githubClient *github.Client
+	githubOnce   sync.Once
+)
+
+func GetGtihubClient(ctx context.Context) *github.Client {
+	githubOnce.Do(func() {
+		var client *http.Client
+		token := os.Getenv("GITHUB_TOKEN")
+		if len(token) > 0 {
+			ts := oauth2.StaticTokenSource(
+				&oauth2.Token{AccessToken: token},
+			)
+			client = oauth2.NewClient(ctx, ts)
+		}
+		githubClient = github.NewClient(client)
+	})
+	return githubClient
+}
+
+const (
+	CLI_OWNER = "supabase"
+	CLI_REPO  = "cli"
+)
+
+func GetLatestRelease(ctx context.Context) (string, error) {
+	client := GetGtihubClient(ctx)
+	release, _, err := client.Repositories.GetLatestRelease(ctx, CLI_OWNER, CLI_REPO)
+	if err != nil {
+		return "", errors.Errorf("Failed to fetch latest release: %w", err)
+	}
+	if release.TagName == nil {
+		return "", nil
+	}
+	return *release.TagName, nil
+}
diff --git a/internal/utils/templates/init_config.toml b/internal/utils/templates/init_config.toml
index 2b752aba..19f7c157 100644
--- a/internal/utils/templates/init_config.toml
+++ b/internal/utils/templates/init_config.toml
@@ -150,6 +150,9 @@ url = ""
 # If enabled, the nonce check will be skipped. Required for local sign in with Google auth.
 skip_nonce_check = false
 
+[edge_runtime]
+enabled = true
+
 [analytics]
 enabled = false
 port = 54327
diff --git a/tools/bumpdoc/main.go b/tools/bumpdoc/main.go
index 51793541..e8c754fd 100644
--- a/tools/bumpdoc/main.go
+++ b/tools/bumpdoc/main.go
@@ -11,7 +11,8 @@ import (
 	"os/signal"
 	"regexp"
 
-	"github.com/google/go-github/v53/github"
+	"github.com/google/go-github/v62/github"
+	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/tools/shared"
 )
 
@@ -45,7 +46,7 @@ func updateRefDoc(ctx context.Context, path string, stdin io.Reader) error {
 		return nil
 	}
 	fmt.Fprintf(os.Stderr, "Updating reference doc: %s\n", path)
-	client := shared.NewGtihubClient(ctx)
+	client := utils.GetGtihubClient(ctx)
 	branch := "cli/ref-doc"
 	master := "master"
 	if err := shared.CreateGitBranch(ctx, client, SUPABASE_OWNER, SUPABASE_REPO, branch, master); err != nil {
diff --git a/tools/changelog/main.go b/tools/changelog/main.go
index 112a6a80..9940a05c 100644
--- a/tools/changelog/main.go
+++ b/tools/changelog/main.go
@@ -12,14 +12,9 @@ import (
 	"strings"
 	"time"
 
-	"github.com/google/go-github/v53/github"
+	"github.com/google/go-github/v62/github"
 	"github.com/slack-go/slack"
-	"github.com/supabase/cli/tools/shared"
-)
-
-const (
-	SUPABASE_OWNER = "supabase"
-	SUPABASE_REPO  = "cli"
+	"github.com/supabase/cli/internal/utils"
 )
 
 func main() {
@@ -35,8 +30,8 @@ func main() {
 }
 
 func showChangeLog(ctx context.Context, slackChannel string) error {
-	client := shared.NewGtihubClient(ctx)
-	releases, _, err := client.Repositories.ListReleases(ctx, SUPABASE_OWNER, SUPABASE_REPO, &github.ListOptions{})
+	client := utils.GetGtihubClient(ctx)
+	releases, _, err := client.Repositories.ListReleases(ctx, utils.CLI_OWNER, utils.CLI_REPO, &github.ListOptions{})
 	if err != nil {
 		return err
 	}
@@ -53,7 +48,7 @@ func showChangeLog(ctx context.Context, slackChannel string) error {
 		opts.TagName = "v1.0.0"
 	}
 	fmt.Fprintln(os.Stderr, "Generating changelog for", opts.TagName)
-	notes, _, err := client.Repositories.GenerateReleaseNotes(ctx, SUPABASE_OWNER, SUPABASE_REPO, &opts)
+	notes, _, err := client.Repositories.GenerateReleaseNotes(ctx, utils.CLI_OWNER, utils.CLI_REPO, &opts)
 	if err != nil {
 		return err
 	}
diff --git a/tools/publish/main.go b/tools/publish/main.go
index 86cc5dbc..9ae5197a 100644
--- a/tools/publish/main.go
+++ b/tools/publish/main.go
@@ -17,7 +17,8 @@ import (
 	"text/template"
 
 	"github.com/go-errors/errors"
-	"github.com/google/go-github/v53/github"
+	"github.com/google/go-github/v62/github"
+	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/tools/shared"
 )
 
@@ -66,7 +67,7 @@ func publishPackages(ctx context.Context, version string, beta bool) error {
 		config.Description += " (Beta)"
 		filename += "-beta"
 	}
-	client := shared.NewGtihubClient(ctx)
+	client := utils.GetGtihubClient(ctx)
 	if err := updatePackage(ctx, client, HOMEBREW_REPO, filename+".rb", brewFormulaTemplate, config); err != nil {
 		return err
 	}
diff --git a/tools/selfhost/main.go b/tools/selfhost/main.go
index 362348f7..a58e9617 100644
--- a/tools/selfhost/main.go
+++ b/tools/selfhost/main.go
@@ -10,7 +10,7 @@ import (
 	"os/signal"
 	"strings"
 
-	"github.com/google/go-github/v53/github"
+	"github.com/google/go-github/v62/github"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/tools/shared"
 	"gopkg.in/yaml.v3"
@@ -42,7 +42,7 @@ type ComposeFile struct {
 }
 
 func updateSelfHosted(ctx context.Context, branch string) error {
-	client := shared.NewGtihubClient(ctx)
+	client := utils.GetGtihubClient(ctx)
 	master := "master"
 	if err := shared.CreateGitBranch(ctx, client, SUPABASE_OWNER, SUPABASE_REPO, branch, master); err != nil {
 		return err
diff --git a/tools/shared/github.go b/tools/shared/github.go
index 1288c5ba..989fc502 100644
--- a/tools/shared/github.go
+++ b/tools/shared/github.go
@@ -2,21 +2,11 @@ package shared
 
 import (
 	"context"
-	"os"
 	"strings"
 
-	"github.com/google/go-github/v53/github"
-	"golang.org/x/oauth2"
+	"github.com/google/go-github/v62/github"
 )
 
-func NewGtihubClient(ctx context.Context) *github.Client {
-	ts := oauth2.StaticTokenSource(
-		&oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")},
-	)
-	tc := oauth2.NewClient(ctx, ts)
-	return github.NewClient(tc)
-}
-
 func CreateGitBranch(ctx context.Context, client *github.Client, owner, repo, branch, base string) error {
 	master, _, err := client.Git.GetRef(ctx, owner, repo, "refs/heads/"+base)
 	if err != nil {


Test patch:
diff --git a/internal/branches/create/create_test.go b/internal/branches/create/create_test.go
index 410b3aa8..911aa23f 100644
--- a/internal/branches/create/create_test.go
+++ b/internal/branches/create/create_test.go
@@ -6,6 +6,7 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -13,7 +14,6 @@ import (
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/flags"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestCreateCommand(t *testing.T) {
diff --git a/internal/db/branch/create/create_test.go b/internal/db/branch/create/create_test.go
index eff15e02..8e384234 100644
--- a/internal/db/branch/create/create_test.go
+++ b/internal/db/branch/create/create_test.go
@@ -6,12 +6,12 @@ import (
 	"testing"
 
 	"github.com/docker/docker/api/types"
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestBranchValidation(t *testing.T) {
diff --git a/internal/db/branch/delete/delete_test.go b/internal/db/branch/delete/delete_test.go
index 40356f8a..76957e24 100644
--- a/internal/db/branch/delete/delete_test.go
+++ b/internal/db/branch/delete/delete_test.go
@@ -5,12 +5,12 @@ import (
 	"path/filepath"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestBranchDir(t *testing.T) {
diff --git a/internal/db/branch/switch_/switch__test.go b/internal/db/branch/switch_/switch__test.go
index a5a86761..31b4aaa7 100644
--- a/internal/db/branch/switch_/switch__test.go
+++ b/internal/db/branch/switch_/switch__test.go
@@ -9,6 +9,7 @@ import (
 	"testing"
 
 	"github.com/docker/docker/api/types"
+	"github.com/h2non/gock"
 	"github.com/jackc/pgerrcode"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
@@ -16,7 +17,6 @@ import (
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/testing/pgtest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestSwitchCommand(t *testing.T) {
diff --git a/internal/db/diff/diff_test.go b/internal/db/diff/diff_test.go
index c89761fd..7e42cd9c 100644
--- a/internal/db/diff/diff_test.go
+++ b/internal/db/diff/diff_test.go
@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
+	"github.com/h2non/gock"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgerrcode"
 	"github.com/spf13/afero"
@@ -23,7 +24,6 @@ import (
 	"github.com/supabase/cli/internal/testing/fstest"
 	"github.com/supabase/cli/internal/testing/pgtest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 var dbConfig = pgconn.Config{
diff --git a/internal/db/dump/dump_test.go b/internal/db/dump/dump_test.go
index 459428b4..952af162 100644
--- a/internal/db/dump/dump_test.go
+++ b/internal/db/dump/dump_test.go
@@ -5,13 +5,13 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/jackc/pgconn"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 var dbConfig = pgconn.Config{
diff --git a/internal/db/lint/lint_test.go b/internal/db/lint/lint_test.go
index 7b6359f6..144c229c 100644
--- a/internal/db/lint/lint_test.go
+++ b/internal/db/lint/lint_test.go
@@ -8,6 +8,7 @@ import (
 	"testing"
 
 	"github.com/docker/docker/api/types"
+	"github.com/h2non/gock"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgerrcode"
 	"github.com/spf13/afero"
@@ -16,7 +17,6 @@ import (
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/testing/pgtest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 var dbConfig = pgconn.Config{
diff --git a/internal/db/pull/pull_test.go b/internal/db/pull/pull_test.go
index ffce631c..bcf6297b 100644
--- a/internal/db/pull/pull_test.go
+++ b/internal/db/pull/pull_test.go
@@ -7,6 +7,7 @@ import (
 	"path/filepath"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgerrcode"
 	"github.com/spf13/afero"
@@ -18,7 +19,6 @@ import (
 	"github.com/supabase/cli/internal/testing/fstest"
 	"github.com/supabase/cli/internal/testing/pgtest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 var dbConfig = pgconn.Config{
diff --git a/internal/db/reset/reset_test.go b/internal/db/reset/reset_test.go
index 650c9557..b87b266a 100644
--- a/internal/db/reset/reset_test.go
+++ b/internal/db/reset/reset_test.go
@@ -10,6 +10,7 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
+	"github.com/h2non/gock"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgerrcode"
 	"github.com/spf13/afero"
@@ -20,7 +21,6 @@ import (
 	"github.com/supabase/cli/internal/testing/fstest"
 	"github.com/supabase/cli/internal/testing/pgtest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestResetCommand(t *testing.T) {
diff --git a/internal/db/start/start_test.go b/internal/db/start/start_test.go
index eae42c33..40c9b034 100644
--- a/internal/db/start/start_test.go
+++ b/internal/db/start/start_test.go
@@ -10,6 +10,7 @@ import (
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/volume"
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -17,7 +18,6 @@ import (
 	"github.com/supabase/cli/internal/testing/fstest"
 	"github.com/supabase/cli/internal/testing/pgtest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestInitBranch(t *testing.T) {
diff --git a/internal/db/test/test_test.go b/internal/db/test/test_test.go
index a392d5a8..0fb6003e 100644
--- a/internal/db/test/test_test.go
+++ b/internal/db/test/test_test.go
@@ -5,6 +5,7 @@ import (
 	"errors"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgerrcode"
 	"github.com/spf13/afero"
@@ -13,7 +14,6 @@ import (
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/testing/pgtest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 var dbConfig = pgconn.Config{
diff --git a/internal/encryption/get/get_test.go b/internal/encryption/get/get_test.go
index 6e148a50..8f280189 100644
--- a/internal/encryption/get/get_test.go
+++ b/internal/encryption/get/get_test.go
@@ -5,11 +5,11 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestGetRootKey(t *testing.T) {
diff --git a/internal/encryption/update/update_test.go b/internal/encryption/update/update_test.go
index 34ee5aa0..8a6f6093 100644
--- a/internal/encryption/update/update_test.go
+++ b/internal/encryption/update/update_test.go
@@ -6,12 +6,12 @@ import (
 	"os"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestUpdateRootKey(t *testing.T) {
diff --git a/internal/functions/delete/delete_test.go b/internal/functions/delete/delete_test.go
index 7a58e76c..110208ea 100644
--- a/internal/functions/delete/delete_test.go
+++ b/internal/functions/delete/delete_test.go
@@ -6,11 +6,11 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestDeleteCommand(t *testing.T) {
diff --git a/internal/functions/deploy/deploy_test.go b/internal/functions/deploy/deploy_test.go
index 38b3288c..8d5d5cd2 100644
--- a/internal/functions/deploy/deploy_test.go
+++ b/internal/functions/deploy/deploy_test.go
@@ -12,13 +12,13 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestDeployOne(t *testing.T) {
diff --git a/internal/functions/download/download_test.go b/internal/functions/download/download_test.go
index b4fd57dc..b727f95c 100644
--- a/internal/functions/download/download_test.go
+++ b/internal/functions/download/download_test.go
@@ -1,8 +1,6 @@
 package download
 
 import (
-	"archive/zip"
-	"bytes"
 	"context"
 	"errors"
 	"fmt"
@@ -11,13 +9,13 @@ import (
 	"os"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestMain(m *testing.M) {
@@ -172,13 +170,6 @@ func TestDownloadFunction(t *testing.T) {
 	t.Run("throws error on extract failure", func(t *testing.T) {
 		// Setup deno error
 		t.Setenv("TEST_DENO_ERROR", "extract failed")
-		var body bytes.Buffer
-		archive := zip.NewWriter(&body)
-		w, err := archive.Create("deno")
-		require.NoError(t, err)
-		_, err = w.Write([]byte("binary"))
-		require.NoError(t, err)
-		require.NoError(t, archive.Close())
 		// Setup mock api
 		defer gock.OffAll()
 		gock.New(utils.DefaultApiHost).
@@ -189,7 +180,7 @@ func TestDownloadFunction(t *testing.T) {
 			Get("/v1/projects/" + project + "/functions/" + slug + "/body").
 			Reply(http.StatusOK)
 		// Run test
-		err = downloadFunction(context.Background(), project, slug, "")
+		err := downloadFunction(context.Background(), project, slug, "")
 		// Check error
 		assert.ErrorContains(t, err, "Error downloading function: exit status 1\nextract failed\n")
 		assert.Empty(t, apitest.ListUnmatchedRequests())
diff --git a/internal/functions/list/list_test.go b/internal/functions/list/list_test.go
index 74f97d21..e9c7f26b 100644
--- a/internal/functions/list/list_test.go
+++ b/internal/functions/list/list_test.go
@@ -5,12 +5,12 @@ import (
 	"errors"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestFunctionsListCommand(t *testing.T) {
diff --git a/internal/functions/serve/serve_test.go b/internal/functions/serve/serve_test.go
index 9f223b9a..84ee2d23 100644
--- a/internal/functions/serve/serve_test.go
+++ b/internal/functions/serve/serve_test.go
@@ -6,12 +6,12 @@ import (
 	"testing"
 
 	"github.com/docker/docker/api/types"
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestServeCommand(t *testing.T) {
diff --git a/internal/gen/types/typescript/typescript_test.go b/internal/gen/types/typescript/typescript_test.go
index 31fec482..802c34c1 100644
--- a/internal/gen/types/typescript/typescript_test.go
+++ b/internal/gen/types/typescript/typescript_test.go
@@ -7,6 +7,7 @@ import (
 	"testing"
 
 	"github.com/docker/docker/api/types"
+	"github.com/h2non/gock"
 	"github.com/jackc/pgconn"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
@@ -15,7 +16,6 @@ import (
 	"github.com/supabase/cli/internal/testing/pgtest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestGenLocalCommand(t *testing.T) {
diff --git a/internal/hostnames/common_test.go b/internal/hostnames/common_test.go
index ede96379..c085afa5 100644
--- a/internal/hostnames/common_test.go
+++ b/internal/hostnames/common_test.go
@@ -5,8 +5,8 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/stretchr/testify/assert"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestVerifyCNAME(t *testing.T) {
diff --git a/internal/init/init_test.go b/internal/init/init_test.go
index 1a46d8a5..09bf6ab0 100644
--- a/internal/init/init_test.go
+++ b/internal/init/init_test.go
@@ -1,6 +1,7 @@
 package init
 
 import (
+	"context"
 	"encoding/json"
 	"os"
 	"testing"
@@ -18,7 +19,7 @@ func TestInitCommand(t *testing.T) {
 		fsys := &afero.MemMapFs{}
 		require.NoError(t, fsys.Mkdir(".git", 0755))
 		// Run test
-		assert.NoError(t, Run(fsys, nil, nil, utils.InitParams{}))
+		assert.NoError(t, Run(context.Background(), fsys, nil, nil, utils.InitParams{}))
 		// Validate generated config.toml
 		exists, err := afero.Exists(fsys, utils.ConfigPath)
 		assert.NoError(t, err)
@@ -50,14 +51,14 @@ func TestInitCommand(t *testing.T) {
 		_, err := fsys.Create(utils.ConfigPath)
 		require.NoError(t, err)
 		// Run test
-		assert.Error(t, Run(fsys, nil, nil, utils.InitParams{}))
+		assert.Error(t, Run(context.Background(), fsys, nil, nil, utils.InitParams{}))
 	})
 
 	t.Run("throws error on permission denied", func(t *testing.T) {
 		// Setup in-memory fs
 		fsys := &fstest.OpenErrorFs{DenyPath: utils.ConfigPath}
 		// Run test
-		err := Run(fsys, nil, nil, utils.InitParams{})
+		err := Run(context.Background(), fsys, nil, nil, utils.InitParams{})
 		// Check error
 		assert.ErrorIs(t, err, os.ErrPermission)
 	})
@@ -66,14 +67,14 @@ func TestInitCommand(t *testing.T) {
 		// Setup read-only fs
 		fsys := afero.NewReadOnlyFs(afero.NewMemMapFs())
 		// Run test
-		assert.Error(t, Run(fsys, nil, nil, utils.InitParams{}))
+		assert.Error(t, Run(context.Background(), fsys, nil, nil, utils.InitParams{}))
 	})
 
 	t.Run("throws error on seed failure", func(t *testing.T) {
 		// Setup in-memory fs
 		fsys := &fstest.OpenErrorFs{DenyPath: utils.SeedDataPath}
 		// Run test
-		err := Run(fsys, nil, nil, utils.InitParams{})
+		err := Run(context.Background(), fsys, nil, nil, utils.InitParams{})
 		// Check error
 		assert.ErrorIs(t, err, os.ErrPermission)
 	})
@@ -82,7 +83,7 @@ func TestInitCommand(t *testing.T) {
 		// Setup in-memory fs
 		fsys := &afero.MemMapFs{}
 		// Run test
-		assert.NoError(t, Run(fsys, utils.Ptr(true), nil, utils.InitParams{}))
+		assert.NoError(t, Run(context.Background(), fsys, utils.Ptr(true), nil, utils.InitParams{}))
 		// Validate generated vscode settings
 		exists, err := afero.Exists(fsys, settingsPath)
 		assert.NoError(t, err)
@@ -96,7 +97,7 @@ func TestInitCommand(t *testing.T) {
 		// Setup in-memory fs
 		fsys := &afero.MemMapFs{}
 		// Run test
-		assert.NoError(t, Run(fsys, utils.Ptr(false), nil, utils.InitParams{}))
+		assert.NoError(t, Run(context.Background(), fsys, utils.Ptr(false), nil, utils.InitParams{}))
 		// Validate vscode settings file isn't generated
 		exists, err := afero.Exists(fsys, settingsPath)
 		assert.NoError(t, err)
@@ -110,7 +111,7 @@ func TestInitCommand(t *testing.T) {
 		// Setup in-memory fs
 		fsys := &afero.MemMapFs{}
 		// Run test
-		assert.NoError(t, Run(fsys, nil, utils.Ptr(true), utils.InitParams{}))
+		assert.NoError(t, Run(context.Background(), fsys, nil, utils.Ptr(true), utils.InitParams{}))
 		// Validate generated intellij deno config
 		exists, err := afero.Exists(fsys, denoPath)
 		assert.NoError(t, err)
@@ -121,7 +122,7 @@ func TestInitCommand(t *testing.T) {
 		// Setup in-memory fs
 		fsys := &afero.MemMapFs{}
 		// Run test
-		assert.NoError(t, Run(fsys, nil, utils.Ptr(false), utils.InitParams{}))
+		assert.NoError(t, Run(context.Background(), fsys, nil, utils.Ptr(false), utils.InitParams{}))
 		// Validate intellij deno config file isn't generated
 		exists, err := afero.Exists(fsys, denoPath)
 		assert.NoError(t, err)
diff --git a/internal/inspect/bloat/bloat_test.go b/internal/inspect/bloat/bloat_test.go
new file mode 100644
index 00000000..5cbbfcbf
--- /dev/null
+++ b/internal/inspect/bloat/bloat_test.go
@@ -0,0 +1,43 @@
+package bloat
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/db/reset"
+	"github.com/supabase/cli/internal/testing/pgtest"
+	"github.com/supabase/cli/internal/utils"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestBloat(t *testing.T) {
+	t.Run("inspects bloat", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(BloatQuery, reset.LikeEscapeSchema(utils.InternalSchemas)).
+			Reply("SELECT 1", Result{
+				Type:        "index hit rate",
+				Schemaname:  "public",
+				Object_name: "table",
+				Bloat:       "0.9",
+				Waste:       "0.1",
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/blocking/blocking_test.go b/internal/inspect/blocking/blocking_test.go
new file mode 100644
index 00000000..d41f2406
--- /dev/null
+++ b/internal/inspect/blocking/blocking_test.go
@@ -0,0 +1,42 @@
+package blocking
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/testing/pgtest"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestBloatCommand(t *testing.T) {
+	t.Run("inspects blocking", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(BlockingQuery).
+			Reply("SELECT 1", Result{
+				Blocked_pid:        1,
+				Blocking_statement: "select 1",
+				Blocking_duration:  "2s",
+				Blocking_pid:       1,
+				Blocked_statement:  "select 1",
+				Blocked_duration:   "2s",
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/cache/cache_test.go b/internal/inspect/cache/cache_test.go
index 974ba174..36b50818 100644
--- a/internal/inspect/cache/cache_test.go
+++ b/internal/inspect/cache/cache_test.go
@@ -7,7 +7,6 @@ import (
 	"github.com/jackc/pgconn"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
-	"github.com/supabase/cli/internal/inspect"
 	"github.com/supabase/cli/internal/testing/pgtest"
 )
 
@@ -26,7 +25,7 @@ func TestCacheCommand(t *testing.T) {
 		// Setup mock postgres
 		conn := pgtest.NewConn()
 		defer conn.Close(t)
-		conn.Query(inspect.CACHE_QUERY).
+		conn.Query(CacheQuery).
 			Reply("SELECT 1", Result{
 				Name:  "index hit rate",
 				Ratio: 0.9,
@@ -43,7 +42,7 @@ func TestCacheCommand(t *testing.T) {
 		// Setup mock postgres
 		conn := pgtest.NewConn()
 		defer conn.Close(t)
-		conn.Query(inspect.CACHE_QUERY).
+		conn.Query(CacheQuery).
 			Reply("SELECT 1", []interface{}{})
 		// Run test
 		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
diff --git a/internal/inspect/calls/calls_test.go b/internal/inspect/calls/calls_test.go
new file mode 100644
index 00000000..5620ec6a
--- /dev/null
+++ b/internal/inspect/calls/calls_test.go
@@ -0,0 +1,41 @@
+package calls
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/testing/pgtest"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestCallsCommand(t *testing.T) {
+	t.Run("inspects calls", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(CallsQuery).
+			Reply("SELECT 1", Result{
+				Total_exec_time: "0.9",
+				Prop_exec_time:  "0.9",
+				Ncalls:          "0.9",
+				Sync_io_time:    "0.9",
+				Query:           "SELECT 1",
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/index_sizes/index_sizes_test.go b/internal/inspect/index_sizes/index_sizes_test.go
new file mode 100644
index 00000000..72742e74
--- /dev/null
+++ b/internal/inspect/index_sizes/index_sizes_test.go
@@ -0,0 +1,40 @@
+package index_sizes
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/db/reset"
+	"github.com/supabase/cli/internal/testing/pgtest"
+	"github.com/supabase/cli/internal/utils"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestIndexSizes(t *testing.T) {
+	t.Run("inspects index sizes", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(IndexSizesQuery, reset.LikeEscapeSchema(utils.InternalSchemas)).
+			Reply("SELECT 1", Result{
+				Name: "test_table_idx",
+				Size: "100GB",
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/index_usage/index_usage_test.go b/internal/inspect/index_usage/index_usage_test.go
new file mode 100644
index 00000000..24d61ebf
--- /dev/null
+++ b/internal/inspect/index_usage/index_usage_test.go
@@ -0,0 +1,41 @@
+package index_usage
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/db/reset"
+	"github.com/supabase/cli/internal/testing/pgtest"
+	"github.com/supabase/cli/internal/utils"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestIndexUsage(t *testing.T) {
+	t.Run("inspects index usage", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(IndexUsageQuery, reset.LikeEscapeSchema(utils.InternalSchemas)).
+			Reply("SELECT 1", Result{
+				Name:                        "test_table_idx",
+				Percent_of_times_index_used: "0.9",
+				Rows_in_table:               300,
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/locks/locks_test.go b/internal/inspect/locks/locks_test.go
new file mode 100644
index 00000000..7c342b2d
--- /dev/null
+++ b/internal/inspect/locks/locks_test.go
@@ -0,0 +1,42 @@
+package locks
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/testing/pgtest"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestLocksCommand(t *testing.T) {
+	t.Run("inspects locks", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(LocksQuery).
+			Reply("SELECT 1", Result{
+				Pid:           1,
+				Relname:       "rel",
+				Transactionid: "9301",
+				Granted:       true,
+				Query:         "select 1",
+				Age:           "300ms",
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/long_running_queries/long_running_queries_test.go b/internal/inspect/long_running_queries/long_running_queries_test.go
new file mode 100644
index 00000000..edb97d6d
--- /dev/null
+++ b/internal/inspect/long_running_queries/long_running_queries_test.go
@@ -0,0 +1,39 @@
+package long_running_queries
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/testing/pgtest"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestLongQueriesCommand(t *testing.T) {
+	t.Run("inspects long running queries", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(LongRunningQueriesQuery).
+			Reply("SELECT 1", Result{
+				Pid:      1,
+				Duration: "300ms",
+				Query:    "select 1",
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/outliers/outliers_test.go b/internal/inspect/outliers/outliers_test.go
new file mode 100644
index 00000000..b34a87cf
--- /dev/null
+++ b/internal/inspect/outliers/outliers_test.go
@@ -0,0 +1,41 @@
+package outliers
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/testing/pgtest"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestOutliersCommand(t *testing.T) {
+	t.Run("inspects outliers", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(OutliersQuery).
+			Reply("SELECT 1", Result{
+				Total_exec_time: "0.9",
+				Prop_exec_time:  "0.9",
+				Ncalls:          "0.9",
+				Sync_io_time:    "0.9",
+				Query:           "SELECT 1",
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/replication_slots/replication_slots_test.go b/internal/inspect/replication_slots/replication_slots_test.go
new file mode 100644
index 00000000..8ed02f13
--- /dev/null
+++ b/internal/inspect/replication_slots/replication_slots_test.go
@@ -0,0 +1,41 @@
+package replication_slots
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/testing/pgtest"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestReplicationCommand(t *testing.T) {
+	t.Run("inspects replication slots", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(ReplicationSlotsQuery).
+			Reply("SELECT 1", Result{
+				Slot_name:                  "test",
+				Active:                     true,
+				State:                      "active",
+				Replication_client_address: "127.0.0.1",
+				Replication_lag_gb:         "0.9",
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/report_test.go b/internal/inspect/report_test.go
new file mode 100644
index 00000000..d019976b
--- /dev/null
+++ b/internal/inspect/report_test.go
@@ -0,0 +1,110 @@
+package inspect
+
+import (
+	"context"
+	"fmt"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/inspect/bloat"
+	"github.com/supabase/cli/internal/inspect/blocking"
+	"github.com/supabase/cli/internal/inspect/cache"
+	"github.com/supabase/cli/internal/inspect/calls"
+	"github.com/supabase/cli/internal/inspect/index_sizes"
+	"github.com/supabase/cli/internal/inspect/index_usage"
+	"github.com/supabase/cli/internal/inspect/locks"
+	"github.com/supabase/cli/internal/inspect/long_running_queries"
+	"github.com/supabase/cli/internal/inspect/outliers"
+	"github.com/supabase/cli/internal/inspect/replication_slots"
+	"github.com/supabase/cli/internal/inspect/role_connections"
+	"github.com/supabase/cli/internal/inspect/seq_scans"
+	"github.com/supabase/cli/internal/inspect/table_index_sizes"
+	"github.com/supabase/cli/internal/inspect/table_record_counts"
+	"github.com/supabase/cli/internal/inspect/table_sizes"
+	"github.com/supabase/cli/internal/inspect/total_index_size"
+	"github.com/supabase/cli/internal/inspect/total_table_sizes"
+	"github.com/supabase/cli/internal/inspect/unused_indexes"
+	"github.com/supabase/cli/internal/inspect/vacuum_stats"
+	"github.com/supabase/cli/internal/testing/pgtest"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestReportCommand(t *testing.T) {
+	t.Run("runs all queries", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(wrapQuery(bloat.BloatQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(blocking.BlockingQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(cache.CacheQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(calls.CallsQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(index_sizes.IndexSizesQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(index_usage.IndexUsageQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(locks.LocksQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(long_running_queries.LongRunningQueriesQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(outliers.OutliersQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(replication_slots.ReplicationSlotsQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(role_connections.RoleConnectionsQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(seq_scans.SeqScansQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(table_index_sizes.TableIndexSizesQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(table_record_counts.TableRecordCountsQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(table_sizes.TableSizesQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(total_index_size.TotalIndexSizeQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(total_table_sizes.TotalTableSizesQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(unused_indexes.UnusedIndexesQuery)).
+			Reply("COPY 0").
+			Query(wrapQuery(vacuum_stats.VacuumStatsQuery)).
+			Reply("COPY 0")
+		// Run test
+		err := Report(context.Background(), ".", dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+		matches, err := afero.Glob(fsys, "*.csv")
+		assert.NoError(t, err)
+		assert.Len(t, matches, 19)
+	})
+}
+
+func TestWrapQuery(t *testing.T) {
+	t.Run("wraps query in csv", func(t *testing.T) {
+		assert.Equal(t,
+			"COPY (SELECT 1) TO STDOUT WITH CSV HEADER",
+			wrapQuery("SELECT 1"),
+		)
+	})
+
+	t.Run("replaces placeholder value", func(t *testing.T) {
+		assert.Equal(t,
+			fmt.Sprintf("COPY (SELECT 'a' LIKE ANY(%s)) TO STDOUT WITH CSV HEADER", ignoreSchemas),
+			wrapQuery("SELECT 'a' LIKE ANY($1)"),
+		)
+	})
+}
diff --git a/internal/inspect/role_connections/role_connections_test.go b/internal/inspect/role_connections/role_connections_test.go
new file mode 100644
index 00000000..79c15355
--- /dev/null
+++ b/internal/inspect/role_connections/role_connections_test.go
@@ -0,0 +1,39 @@
+package role_connections
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/testing/pgtest"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestRoleCommand(t *testing.T) {
+	t.Run("inspects role connections", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(RoleConnectionsQuery).
+			Reply("SELECT 1", Result{
+				Rolname:            "postgres",
+				Active_connections: 1,
+				Connection_limit:   10,
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/seq_scans/seq_scans_test.go b/internal/inspect/seq_scans/seq_scans_test.go
new file mode 100644
index 00000000..3bfb2ba6
--- /dev/null
+++ b/internal/inspect/seq_scans/seq_scans_test.go
@@ -0,0 +1,40 @@
+package seq_scans
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/db/reset"
+	"github.com/supabase/cli/internal/testing/pgtest"
+	"github.com/supabase/cli/internal/utils"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestSequentialScansCommand(t *testing.T) {
+	t.Run("inspects sequential scans", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(SeqScansQuery, reset.LikeEscapeSchema(utils.InternalSchemas)).
+			Reply("SELECT 1", Result{
+				Name:  "test_table",
+				Count: 99999,
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/table_index_sizes/table_index_sizes_test.go b/internal/inspect/table_index_sizes/table_index_sizes_test.go
new file mode 100644
index 00000000..0a58a710
--- /dev/null
+++ b/internal/inspect/table_index_sizes/table_index_sizes_test.go
@@ -0,0 +1,40 @@
+package table_index_sizes
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/db/reset"
+	"github.com/supabase/cli/internal/testing/pgtest"
+	"github.com/supabase/cli/internal/utils"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestTableIndexSizesCommand(t *testing.T) {
+	t.Run("inspects table index sizes", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(TableIndexSizesQuery, reset.LikeEscapeSchema(utils.InternalSchemas)).
+			Reply("SELECT 1", Result{
+				Table:      "public.test_table",
+				Index_size: "3GB",
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/table_record_counts/table_record_counts_test.go b/internal/inspect/table_record_counts/table_record_counts_test.go
new file mode 100644
index 00000000..c5f97683
--- /dev/null
+++ b/internal/inspect/table_record_counts/table_record_counts_test.go
@@ -0,0 +1,41 @@
+package table_record_counts
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/db/reset"
+	"github.com/supabase/cli/internal/testing/pgtest"
+	"github.com/supabase/cli/internal/utils"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestTableRecordCountsCommand(t *testing.T) {
+	t.Run("inspects table record counts", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(TableRecordCountsQuery, reset.LikeEscapeSchema(utils.PgSchemas)).
+			Reply("SELECT 1", Result{
+				Schema:          "public",
+				Name:            "test_table",
+				Estimated_count: 100,
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/table_sizes/table_sizes_test.go b/internal/inspect/table_sizes/table_sizes_test.go
new file mode 100644
index 00000000..f96e17e1
--- /dev/null
+++ b/internal/inspect/table_sizes/table_sizes_test.go
@@ -0,0 +1,41 @@
+package table_sizes
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/db/reset"
+	"github.com/supabase/cli/internal/testing/pgtest"
+	"github.com/supabase/cli/internal/utils"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestTableSizesCommand(t *testing.T) {
+	t.Run("inspects table sizes", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(TableSizesQuery, reset.LikeEscapeSchema(utils.PgSchemas)).
+			Reply("SELECT 1", Result{
+				Schema: "schema",
+				Name:   "test_table",
+				Size:   "3GB",
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/total_index_size/total_index_size_test.go b/internal/inspect/total_index_size/total_index_size_test.go
new file mode 100644
index 00000000..6a6591f2
--- /dev/null
+++ b/internal/inspect/total_index_size/total_index_size_test.go
@@ -0,0 +1,39 @@
+package total_index_size
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/db/reset"
+	"github.com/supabase/cli/internal/testing/pgtest"
+	"github.com/supabase/cli/internal/utils"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestTotalIndexSizeCommand(t *testing.T) {
+	t.Run("inspects size of all indexes", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(TotalIndexSizeQuery, reset.LikeEscapeSchema(utils.InternalSchemas)).
+			Reply("SELECT 1", Result{
+				Size: "8GB",
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/total_table_sizes/total_table_sizes_test.go b/internal/inspect/total_table_sizes/total_table_sizes_test.go
new file mode 100644
index 00000000..a207456c
--- /dev/null
+++ b/internal/inspect/total_table_sizes/total_table_sizes_test.go
@@ -0,0 +1,41 @@
+package total_table_sizes
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/db/reset"
+	"github.com/supabase/cli/internal/testing/pgtest"
+	"github.com/supabase/cli/internal/utils"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestTotalTableSizesCommand(t *testing.T) {
+	t.Run("inspects total table sizes", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(TotalTableSizesQuery, reset.LikeEscapeSchema(utils.PgSchemas)).
+			Reply("SELECT 1", Result{
+				Schema: "public",
+				Name:   "test_table",
+				Size:   "3GB",
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/unused_indexes/unused_indexes_test.go b/internal/inspect/unused_indexes/unused_indexes_test.go
new file mode 100644
index 00000000..d295c164
--- /dev/null
+++ b/internal/inspect/unused_indexes/unused_indexes_test.go
@@ -0,0 +1,42 @@
+package unused_indexes
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/db/reset"
+	"github.com/supabase/cli/internal/testing/pgtest"
+	"github.com/supabase/cli/internal/utils"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestUnusedIndexesCommand(t *testing.T) {
+	t.Run("inspects unused indexes", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(UnusedIndexesQuery, reset.LikeEscapeSchema(utils.InternalSchemas)).
+			Reply("SELECT 1", Result{
+				Table:       "test_table",
+				Index:       "test_table_idx",
+				Index_size:  "3GB",
+				Index_scans: 2,
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/inspect/vacuum_stats/vacuum_stats_test.go b/internal/inspect/vacuum_stats/vacuum_stats_test.go
new file mode 100644
index 00000000..3e1b93f3
--- /dev/null
+++ b/internal/inspect/vacuum_stats/vacuum_stats_test.go
@@ -0,0 +1,46 @@
+package vacuum_stats
+
+import (
+	"context"
+	"testing"
+
+	"github.com/jackc/pgconn"
+	"github.com/spf13/afero"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/db/reset"
+	"github.com/supabase/cli/internal/testing/pgtest"
+	"github.com/supabase/cli/internal/utils"
+)
+
+var dbConfig = pgconn.Config{
+	Host:     "127.0.0.1",
+	Port:     5432,
+	User:     "admin",
+	Password: "password",
+	Database: "postgres",
+}
+
+func TestVacuumCommand(t *testing.T) {
+	t.Run("inspects vacuum stats", func(t *testing.T) {
+		// Setup in-memory fs
+		fsys := afero.NewMemMapFs()
+		// Setup mock postgres
+		conn := pgtest.NewConn()
+		defer conn.Close(t)
+		conn.Query(VacuumStatsQuery, reset.LikeEscapeSchema(utils.InternalSchemas)).
+			Reply("SELECT 1", Result{
+				Schema:               "test_schema",
+				Table:                "test_table",
+				Last_vacuum:          "2021-01-01 00:00:00",
+				Last_autovacuum:      "2021-01-01 00:00:00",
+				Rowcount:             "1000",
+				Dead_rowcount:        "100",
+				Autovacuum_threshold: "100",
+				Expect_autovacuum:    "yes",
+			})
+		// Run test
+		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
+		// Check error
+		assert.NoError(t, err)
+	})
+}
diff --git a/internal/link/link_test.go b/internal/link/link_test.go
index e73a46a4..0a4a071f 100644
--- a/internal/link/link_test.go
+++ b/internal/link/link_test.go
@@ -6,6 +6,7 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgerrcode"
 	"github.com/jackc/pgx/v4"
@@ -19,7 +20,6 @@ import (
 	"github.com/supabase/cli/internal/utils/tenant"
 	"github.com/supabase/cli/pkg/api"
 	"github.com/zalando/go-keyring"
-	"gopkg.in/h2non/gock.v1"
 )
 
 var dbConfig = pgconn.Config{
diff --git a/internal/login/login_test.go b/internal/login/login_test.go
index b8782ed3..faf351e9 100644
--- a/internal/login/login_test.go
+++ b/internal/login/login_test.go
@@ -8,6 +8,7 @@ import (
 	"os"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -15,7 +16,6 @@ import (
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/internal/utils/credentials"
 	"github.com/zalando/go-keyring"
-	"gopkg.in/h2non/gock.v1"
 )
 
 type MockEncryption struct {
diff --git a/internal/logout/logout_test.go b/internal/logout/logout_test.go
index c0456cb0..c2bdd2b0 100644
--- a/internal/logout/logout_test.go
+++ b/internal/logout/logout_test.go
@@ -41,7 +41,7 @@ func TestLogoutCommand(t *testing.T) {
 		// Run test
 		err := Run(context.Background(), os.Stdout, fsys)
 		// Check error
-		assert.NoError(t, err)
+		assert.ErrorIs(t, err, context.Canceled)
 		saved, err := credentials.Get(utils.AccessTokenKey)
 		assert.NoError(t, err)
 		assert.Equal(t, token, saved)
diff --git a/internal/migration/squash/squash_test.go b/internal/migration/squash/squash_test.go
index 3089227e..a47b4fd2 100644
--- a/internal/migration/squash/squash_test.go
+++ b/internal/migration/squash/squash_test.go
@@ -14,6 +14,7 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
+	"github.com/h2non/gock"
 	"github.com/jackc/pgconn"
 	"github.com/jackc/pgerrcode"
 	"github.com/jackc/pgx/v4"
@@ -27,7 +28,6 @@ import (
 	"github.com/supabase/cli/internal/testing/fstest"
 	"github.com/supabase/cli/internal/testing/pgtest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 var dbConfig = pgconn.Config{
diff --git a/internal/orgs/create/create_test.go b/internal/orgs/create/create_test.go
index bc7c8d7c..f491bdc3 100644
--- a/internal/orgs/create/create_test.go
+++ b/internal/orgs/create/create_test.go
@@ -6,11 +6,11 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestOrganizationCreateCommand(t *testing.T) {
diff --git a/internal/orgs/list/list_test.go b/internal/orgs/list/list_test.go
index 75d56d2e..00030dbd 100644
--- a/internal/orgs/list/list_test.go
+++ b/internal/orgs/list/list_test.go
@@ -6,11 +6,11 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestOrganizationListCommand(t *testing.T) {
diff --git a/internal/projects/apiKeys/api_keys_test.go b/internal/projects/apiKeys/api_keys_test.go
index db24bac0..57130f15 100644
--- a/internal/projects/apiKeys/api_keys_test.go
+++ b/internal/projects/apiKeys/api_keys_test.go
@@ -5,12 +5,12 @@ import (
 	"errors"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestProjectApiKeysCommand(t *testing.T) {
diff --git a/internal/projects/create/create_test.go b/internal/projects/create/create_test.go
index 51ef30a7..c67c7a35 100644
--- a/internal/projects/create/create_test.go
+++ b/internal/projects/create/create_test.go
@@ -5,12 +5,12 @@ import (
 	"errors"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestProjectCreateCommand(t *testing.T) {
diff --git a/internal/projects/delete/delete_test.go b/internal/projects/delete/delete_test.go
index 07a54108..b83d4cf8 100644
--- a/internal/projects/delete/delete_test.go
+++ b/internal/projects/delete/delete_test.go
@@ -6,6 +6,7 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -13,7 +14,6 @@ import (
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
 	"github.com/zalando/go-keyring"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestDeleteCommand(t *testing.T) {
diff --git a/internal/projects/list/list_test.go b/internal/projects/list/list_test.go
index 7f8043dd..d5450bfb 100644
--- a/internal/projects/list/list_test.go
+++ b/internal/projects/list/list_test.go
@@ -5,12 +5,12 @@ import (
 	"errors"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestProjectListCommand(t *testing.T) {
diff --git a/internal/restrictions/update/update_test.go b/internal/restrictions/update/update_test.go
index f25d86dd..5a6583ea 100644
--- a/internal/restrictions/update/update_test.go
+++ b/internal/restrictions/update/update_test.go
@@ -6,11 +6,11 @@ import (
 	"testing"
 
 	"github.com/go-errors/errors"
+	"github.com/h2non/gock"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestUpdateRestrictionsCommand(t *testing.T) {
diff --git a/internal/secrets/list/list_test.go b/internal/secrets/list/list_test.go
index 129b3d30..138438de 100644
--- a/internal/secrets/list/list_test.go
+++ b/internal/secrets/list/list_test.go
@@ -5,12 +5,12 @@ import (
 	"errors"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestSecretListCommand(t *testing.T) {
diff --git a/internal/secrets/set/set_test.go b/internal/secrets/set/set_test.go
index 480cf6e6..a0193750 100644
--- a/internal/secrets/set/set_test.go
+++ b/internal/secrets/set/set_test.go
@@ -5,13 +5,13 @@ import (
 	"errors"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestSecretSetCommand(t *testing.T) {
diff --git a/internal/secrets/unset/unset_test.go b/internal/secrets/unset/unset_test.go
index 313a8e68..4db6750e 100644
--- a/internal/secrets/unset/unset_test.go
+++ b/internal/secrets/unset/unset_test.go
@@ -5,12 +5,12 @@ import (
 	"errors"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestSecretUnsetCommand(t *testing.T) {
diff --git a/internal/sso/get/get_test.go b/internal/sso/get/get_test.go
index 6fdad706..bd6fd0ef 100644
--- a/internal/sso/get/get_test.go
+++ b/internal/sso/get/get_test.go
@@ -5,10 +5,10 @@ import (
 	"fmt"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestSSOProvidersShowCommand(t *testing.T) {
diff --git a/internal/sso/list/list_test.go b/internal/sso/list/list_test.go
index 15cbad6a..333ffd4a 100644
--- a/internal/sso/list/list_test.go
+++ b/internal/sso/list/list_test.go
@@ -4,10 +4,10 @@ import (
 	"context"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestSSOProvidersListCommand(t *testing.T) {
diff --git a/internal/sso/remove/remove_test.go b/internal/sso/remove/remove_test.go
index 80195c49..f284f7e0 100644
--- a/internal/sso/remove/remove_test.go
+++ b/internal/sso/remove/remove_test.go
@@ -5,10 +5,10 @@ import (
 	"fmt"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestSSOProvidersRemoveCommand(t *testing.T) {
diff --git a/internal/sso/update/update_test.go b/internal/sso/update/update_test.go
index 29a5eabb..ca62a4fd 100644
--- a/internal/sso/update/update_test.go
+++ b/internal/sso/update/update_test.go
@@ -8,11 +8,11 @@ import (
 	"testing"
 
 	"github.com/google/uuid"
+	"github.com/h2non/gock"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func response(providerId string, domains []string) map[string]any {
diff --git a/internal/start/start_test.go b/internal/start/start_test.go
index 843ed8d4..7e27a268 100644
--- a/internal/start/start_test.go
+++ b/internal/start/start_test.go
@@ -11,6 +11,7 @@ import (
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/volume"
+	"github.com/h2non/gock"
 	"github.com/jackc/pgconn"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
@@ -18,7 +19,6 @@ import (
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/testing/pgtest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestStartCommand(t *testing.T) {
diff --git a/internal/status/status_test.go b/internal/status/status_test.go
index 8b63d8e4..c22e2219 100644
--- a/internal/status/status_test.go
+++ b/internal/status/status_test.go
@@ -11,12 +11,12 @@ import (
 	"testing"
 
 	"github.com/docker/docker/api/types"
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestStatusCommand(t *testing.T) {
diff --git a/internal/stop/stop_test.go b/internal/stop/stop_test.go
index 733fd2ec..dbb8b903 100644
--- a/internal/stop/stop_test.go
+++ b/internal/stop/stop_test.go
@@ -10,12 +10,12 @@ import (
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/volume"
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestStopCommand(t *testing.T) {
diff --git a/internal/storage/cp/cp_test.go b/internal/storage/cp/cp_test.go
index 371394ec..a310a351 100644
--- a/internal/storage/cp/cp_test.go
+++ b/internal/storage/cp/cp_test.go
@@ -6,6 +6,7 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -15,7 +16,6 @@ import (
 	"github.com/supabase/cli/pkg/api"
 	"github.com/supabase/cli/pkg/fetcher"
 	"github.com/supabase/cli/pkg/storage"
-	"gopkg.in/h2non/gock.v1"
 )
 
 var mockFile = storage.ObjectResponse{
diff --git a/internal/storage/ls/ls_test.go b/internal/storage/ls/ls_test.go
index 69d8bad9..f1682759 100644
--- a/internal/storage/ls/ls_test.go
+++ b/internal/storage/ls/ls_test.go
@@ -6,6 +6,7 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/storage/client"
@@ -15,7 +16,6 @@ import (
 	"github.com/supabase/cli/pkg/api"
 	"github.com/supabase/cli/pkg/fetcher"
 	"github.com/supabase/cli/pkg/storage"
-	"gopkg.in/h2non/gock.v1"
 )
 
 var mockFile = storage.ObjectResponse{
diff --git a/internal/storage/mv/mv_test.go b/internal/storage/mv/mv_test.go
index 299851c5..2cb1c57e 100644
--- a/internal/storage/mv/mv_test.go
+++ b/internal/storage/mv/mv_test.go
@@ -5,6 +5,7 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
@@ -13,7 +14,6 @@ import (
 	"github.com/supabase/cli/pkg/api"
 	"github.com/supabase/cli/pkg/fetcher"
 	"github.com/supabase/cli/pkg/storage"
-	"gopkg.in/h2non/gock.v1"
 )
 
 var mockFile = storage.ObjectResponse{
diff --git a/internal/storage/rm/rm_test.go b/internal/storage/rm/rm_test.go
index 0ce155a4..4dbe8275 100644
--- a/internal/storage/rm/rm_test.go
+++ b/internal/storage/rm/rm_test.go
@@ -5,6 +5,7 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/internal/testing/apitest"
@@ -14,7 +15,6 @@ import (
 	"github.com/supabase/cli/pkg/api"
 	"github.com/supabase/cli/pkg/fetcher"
 	"github.com/supabase/cli/pkg/storage"
-	"gopkg.in/h2non/gock.v1"
 )
 
 var mockFile = storage.ObjectResponse{
diff --git a/internal/testing/apitest/helper.go b/internal/testing/apitest/helper.go
index e126f016..37f5549e 100644
--- a/internal/testing/apitest/helper.go
+++ b/internal/testing/apitest/helper.go
@@ -11,7 +11,7 @@ import (
 	"github.com/docker/docker/api/types/volume"
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/pkg/stdcopy"
-	"gopkg.in/h2non/gock.v1"
+	"github.com/h2non/gock"
 )
 
 const mockHost = "http://127.0.0.1"
diff --git a/internal/utils/api_test.go b/internal/utils/api_test.go
index 4e045a8c..09365897 100644
--- a/internal/utils/api_test.go
+++ b/internal/utils/api_test.go
@@ -7,11 +7,11 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/utils/cloudflare"
-	"gopkg.in/h2non/gock.v1"
 )
 
 const host = "api.supabase.io"
diff --git a/internal/utils/connect_test.go b/internal/utils/connect_test.go
index 3749c59b..585d44fe 100644
--- a/internal/utils/connect_test.go
+++ b/internal/utils/connect_test.go
@@ -7,6 +7,7 @@ import (
 	"testing"
 
 	"github.com/go-errors/errors"
+	"github.com/h2non/gock"
 	"github.com/jackc/pgconn"
 	"github.com/spf13/viper"
 	"github.com/stretchr/testify/assert"
@@ -14,7 +15,6 @@ import (
 	"github.com/supabase/cli/internal/testing/apitest"
 	"github.com/supabase/cli/internal/testing/pgtest"
 	"github.com/supabase/cli/internal/utils/cloudflare"
-	"gopkg.in/h2non/gock.v1"
 )
 
 var dbConfig = pgconn.Config{
diff --git a/internal/utils/console_test.go b/internal/utils/console_test.go
new file mode 100644
index 00000000..83dfad8e
--- /dev/null
+++ b/internal/utils/console_test.go
@@ -0,0 +1,65 @@
+package utils
+
+import (
+	"context"
+	"os"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"github.com/supabase/cli/internal/testing/fstest"
+)
+
+func TestPromptYesNo(t *testing.T) {
+	t.Run("defaults on closed stdin", func(t *testing.T) {
+		c := NewConsole()
+		c.IsTTY = false
+		// Run test
+		val, err := c.PromptYesNo(context.Background(), "test", false)
+		// Check error
+		assert.NoError(t, err)
+		assert.False(t, val)
+	})
+
+	t.Run("parses piped stdin", func(t *testing.T) {
+		defer fstest.MockStdin(t, "y")()
+		c := NewConsole()
+		// Run test
+		val, err := c.PromptYesNo(context.Background(), "test", false)
+		// Check error
+		assert.NoError(t, err)
+		assert.True(t, val)
+	})
+}
+
+func TestPromptText(t *testing.T) {
+	t.Run("defaults on timeout", func(t *testing.T) {
+		// Setup stdin
+		r, _, err := os.Pipe()
+		require.NoError(t, err)
+		// Replace stdin
+		oldStdin := os.Stdin
+		defer func() {
+			os.Stdin = oldStdin
+		}()
+		os.Stdin = r
+		c := NewConsole()
+		// Run test
+		val, err := c.PromptText(context.Background(), "test")
+		// Check error
+		assert.NoError(t, err)
+		assert.Empty(t, val)
+	})
+
+	t.Run("throws error on cancel", func(t *testing.T) {
+		c := NewConsole()
+		// Setup cancelled context
+		ctx, cancel := context.WithCancel(context.Background())
+		cancel()
+		// Run test
+		val, err := c.PromptText(ctx, "test")
+		// Check error
+		assert.ErrorIs(t, err, context.Canceled)
+		assert.Empty(t, val)
+	})
+}
diff --git a/internal/utils/docker_test.go b/internal/utils/docker_test.go
index 1908c352..a3f92bd0 100644
--- a/internal/utils/docker_test.go
+++ b/internal/utils/docker_test.go
@@ -11,11 +11,11 @@ import (
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/pkg/jsonmessage"
 	"github.com/docker/docker/pkg/stdcopy"
+	"github.com/h2non/gock"
 	"github.com/spf13/viper"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"github.com/supabase/cli/internal/testing/apitest"
-	"gopkg.in/h2non/gock.v1"
 )
 
 const (
diff --git a/internal/utils/flags/project_ref_test.go b/internal/utils/flags/project_ref_test.go
index 699419a8..9932d617 100644
--- a/internal/utils/flags/project_ref_test.go
+++ b/internal/utils/flags/project_ref_test.go
@@ -7,6 +7,7 @@ import (
 	"testing"
 
 	"github.com/go-errors/errors"
+	"github.com/h2non/gock"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -14,7 +15,6 @@ import (
 	"github.com/supabase/cli/internal/testing/fstest"
 	"github.com/supabase/cli/internal/utils"
 	"github.com/supabase/cli/pkg/api"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestProjectRef(t *testing.T) {
diff --git a/internal/utils/release_test.go b/internal/utils/release_test.go
new file mode 100644
index 00000000..00b44f49
--- /dev/null
+++ b/internal/utils/release_test.go
@@ -0,0 +1,60 @@
+package utils
+
+import (
+	"context"
+	"net/http"
+	"testing"
+
+	"github.com/go-errors/errors"
+	"github.com/google/go-github/v62/github"
+	"github.com/h2non/gock"
+	"github.com/stretchr/testify/assert"
+	"github.com/supabase/cli/internal/testing/apitest"
+)
+
+func TestLatestRelease(t *testing.T) {
+	t.Run("fetches latest release", func(t *testing.T) {
+		// Setup api mock
+		defer gock.OffAll()
+		gock.New("https://api.github.com").
+			Get("/repos/supabase/cli/releases/latest").
+			Reply(http.StatusOK).
+			JSON(github.RepositoryRelease{TagName: Ptr("v2")})
+		// Run test
+		version, err := GetLatestRelease(context.Background())
+		// Check error
+		assert.NoError(t, err)
+		assert.Equal(t, version, "v2")
+		assert.Empty(t, apitest.ListUnmatchedRequests())
+	})
+
+	t.Run("ignores missing version", func(t *testing.T) {
+		// Setup api mock
+		defer gock.OffAll()
+		gock.New("https://api.github.com").
+			Get("/repos/supabase/cli/releases/latest").
+			Reply(http.StatusOK).
+			JSON(github.RepositoryRelease{})
+		// Run test
+		version, err := GetLatestRelease(context.Background())
+		// Check error
+		assert.NoError(t, err)
+		assert.Empty(t, version)
+		assert.Empty(t, apitest.ListUnmatchedRequests())
+	})
+
+	t.Run("throws error on network error", func(t *testing.T) {
+		errNetwork := errors.New("network error")
+		// Setup api mock
+		defer gock.OffAll()
+		gock.New("https://api.github.com").
+			Get("/repos/supabase/cli/releases/latest").
+			ReplyError(errNetwork)
+		// Run test
+		version, err := GetLatestRelease(context.Background())
+		// Check error
+		assert.ErrorIs(t, err, errNetwork)
+		assert.Empty(t, version)
+		assert.Empty(t, apitest.ListUnmatchedRequests())
+	})
+}
diff --git a/internal/utils/templates/init_config.test.toml b/internal/utils/templates/init_config.test.toml
index aab9265d..0c88d0c7 100644
--- a/internal/utils/templates/init_config.test.toml
+++ b/internal/utils/templates/init_config.test.toml
@@ -150,6 +150,9 @@ url = "https://login.microsoftonline.com/tenant"
 # If enabled, the nonce check will be skipped. Required for local sign in with Google auth.
 skip_nonce_check = true
 
+[edge_runtime]
+enabled = true
+
 [analytics]
 enabled = false
 port = 54327
diff --git a/internal/utils/tenant/gotrue_test.go b/internal/utils/tenant/gotrue_test.go
index 78897991..fcc646e0 100644
--- a/internal/utils/tenant/gotrue_test.go
+++ b/internal/utils/tenant/gotrue_test.go
@@ -6,9 +6,9 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/stretchr/testify/assert"
 	"github.com/supabase/cli/pkg/fetcher"
-	"gopkg.in/h2non/gock.v1"
 )
 
 var mockApi = TenantAPI{Fetcher: fetcher.NewFetcher(
diff --git a/internal/utils/tenant/postgrest_test.go b/internal/utils/tenant/postgrest_test.go
index 40999b9a..2be44db4 100644
--- a/internal/utils/tenant/postgrest_test.go
+++ b/internal/utils/tenant/postgrest_test.go
@@ -6,8 +6,8 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/stretchr/testify/assert"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestPostgrestVersion(t *testing.T) {
diff --git a/internal/utils/tenant/storage_test.go b/internal/utils/tenant/storage_test.go
index 0152e48b..f3c0545b 100644
--- a/internal/utils/tenant/storage_test.go
+++ b/internal/utils/tenant/storage_test.go
@@ -6,8 +6,8 @@ import (
 	"net/http"
 	"testing"
 
+	"github.com/h2non/gock"
 	"github.com/stretchr/testify/assert"
-	"gopkg.in/h2non/gock.v1"
 )
 
 func TestStorageVersion(t *testing.T) {
diff --git a/test/branch_test.go b/test/branch_test.go
index 7d32dd7a..a3d86902 100644
--- a/test/branch_test.go
+++ b/test/branch_test.go
@@ -1,6 +1,7 @@
 package integration
 
 import (
+	"context"
 	"encoding/json"
 	"os"
 
@@ -15,13 +16,10 @@ func (suite *DBTestSuite) TestBranchCreate() {
 	// create branch
 	branch := "test-branch"
 	create, args, err := suite.cmd.Traverse([]string{"db", "branch", "create", branch})
-	if err != nil {
-		suite.Fail("failed to find create command")
-	}
+	require.NoError(suite.T(), err)
+	create.SetContext(context.Background())
 	err = create.RunE(create, args)
-	if err != nil {
-		suite.Fail("failed to create branch", err)
-	}
+	require.NoError(suite.T(), err)
 
 	// check if branch dir exists
 	_, err = os.Stat("supabase/.branches/" + branch)
diff --git a/test/db_test.go b/test/db_test.go
index b2baf3cf..45de0ef2 100644
--- a/test/db_test.go
+++ b/test/db_test.go
@@ -2,6 +2,7 @@ package integration
 
 // Basic imports
 import (
+	"context"
 	"io"
 	"net/http"
 	"os"
@@ -80,18 +81,13 @@ func (suite *DBTestSuite) SetupTest() {
 	// create supabase dir
 	suite.cmd = clicmd.GetRootCmd()
 	init, _, err := suite.cmd.Find([]string{"init"})
-	if err != nil {
-		suite.Fail("failed to find init command")
-	}
+	require.NoError(suite.T(), err)
+	init.SetContext(context.Background())
 	err = init.RunE(init, []string{})
-	if err != nil {
-		suite.Fail("failed to init supabase cli")
-	}
+	require.NoError(suite.T(), err)
 
 	err = os.Mkdir("supabase/.branches", os.FileMode(0755))
-	if err != nil {
-		suite.Fail("failed to create supabase/.branches directory")
-	}
+	require.NoError(suite.T(), err)
 }
 
 func (suite *DBTestSuite) TeardownTest() {
diff --git a/test/init_test.go b/test/init_test.go
index 0a49c4d6..bdc8b7b7 100644
--- a/test/init_test.go
+++ b/test/init_test.go
@@ -2,6 +2,7 @@ package integration
 
 // Basic imports
 import (
+	"context"
 	"os"
 	"testing"
 
@@ -23,6 +24,7 @@ func (suite *InitTestSuite) TestInit() {
 	// init supabase
 	init, _, err := suite.cmd.Find([]string{"init"})
 	require.NoError(suite.T(), err)
+	init.SetContext(context.Background())
 	require.NoError(suite.T(), init.RunE(init, []string{}))
 
 	// check if init dir exists
diff --git a/test/link_test.go b/test/link_test.go
index 1d0704ce..afef6c6f 100644
--- a/test/link_test.go
+++ b/test/link_test.go
@@ -68,6 +68,7 @@ func (suite *LinkTestSuite) SetupTest() {
 
 	// init supabase
 	init, _, err := suite.cmd.Find([]string{"init"})
+	init.SetContext(context.Background())
 	require.NoError(suite.T(), err)
 	require.NoError(suite.T(), init.RunE(init, []string{}))
 
diff --git a/test/login_test.go b/test/login_test.go
index edf70d4f..81f9cb89 100644
--- a/test/login_test.go
+++ b/test/login_test.go
@@ -2,6 +2,7 @@ package integration
 
 // Basic imports
 import (
+	"context"
 	"net/http"
 	"os"
 	"path/filepath"
@@ -33,6 +34,7 @@ func (suite *LoginTestSuite) TestLink() {
 	// run command
 	login, _, err := suite.cmd.Find([]string{"login"})
 	require.NoError(suite.T(), err)
+	login.SetContext(context.Background())
 	key := "sbp_" + gonanoid.MustGenerate(supabase.KeyAlphabet, supabase.KeyLength)
 
 	// change stdin to read from a file
@@ -73,6 +75,7 @@ func (suite *LoginTestSuite) SetupTest() {
 	// init supabase
 	init, _, err := suite.cmd.Find([]string{"init"})
 	require.NoError(suite.T(), err)
+	init.SetContext(context.Background())
 	require.NoError(suite.T(), init.RunE(init, []string{}))
 
 	// implement mocks
diff --git a/test/secrets_test.go b/test/secrets_test.go
index 7a98d8e7..b195e23e 100644
--- a/test/secrets_test.go
+++ b/test/secrets_test.go
@@ -73,6 +73,7 @@ func (suite *SecretsTestSuite) SetupTest() {
 	// init supabase
 	init, _, err := suite.cmd.Find([]string{"init"})
 	require.NoError(suite.T(), err)
+	init.SetContext(context.Background())
 	require.NoError(suite.T(), init.RunE(init, []string{}))
 
 	// add `link` dir
diff --git a/test/status_test.go b/test/status_test.go
index d8403512..d309d243 100644
--- a/test/status_test.go
+++ b/test/status_test.go
@@ -75,6 +75,7 @@ func (suite *StatusTestSuite) SetupTest() {
 	// init supabase
 	init, _, err := suite.cmd.Find([]string{"init"})
 	require.NoError(suite.T(), err)
+	init.SetContext(context.Background())
 	require.NoError(suite.T(), init.RunE(init, []string{}))
 
 	// implement mocks