A small VSCode extension that wires up your Go 1.24+ tool directives for use inside VSCode.
Status: stop-gap. This exists to bridge the gap until the official Go extension supports
go toolnatively (golang/vscode-go#3799). When that lands, run Clean Generated Integration and uninstall — nothing is locked in.
- Go 1.24+ (for
tooldirectives) and the official Go extension (golang.go). - macOS / Linux (the integration uses bash shims). This includes Windows via WSL or Dev Containers, where the workspace is Linux. Native Windows is not supported yet (#1).
- The Go extension only honours
go.alternateToolsin a trusted workspace — accept the Workspace Trust prompt. See How the Go extension picks up your tools.
Go 1.24 lets you track developer tools in go.mod:
tool (
github.com/golangci/golangci-lint/cmd/golangci-lint
golang.org/x/tools/cmd/goimports
)
…and run them with go tool <name>. This extension scaffolds the glue so the Go extension and your tasks can use those tools without a global install.
- Open your workspace (a single Go module, or a monorepo with many
go.modfiles). - When tool directives are found, the extension prompts you to set up the
integration. You can also run Init Go Project from the Command Palette
(
goToolsInit.init) at any time. - To undo it, run Clean Generated Integration (
goToolsInit.clean) — it previews and removes only what this extension created (the.tools/shims, itsgo.alternateToolsentries, itsgo.lintToolvalue, and itsgo tooltasks), leaving your own settings and tasks untouched. Handy once you no longer need the stop-gap (see Relationship to upstream).
It globs every go.mod in the workspace (skipping vendor/ and testdata/),
parses each module's tool directives independently, derives the binary name from
the last segment of each import path, and generates:
.tools/<binary>(workspace root) — one executable bash shim per unique tool name:The shim has no#!/usr/bin/env bash exec go tool <binary> "$@"
cd, sogo toolresolves to the calling module's pinned tool via the working directory. A single root shim therefore serves every module — there is no per-module copy..vscode/settings.json(workspace root) —go.alternateToolsmapping each binary to its root shim, e.g.${workspaceFolder}/.tools/golangci-lint.go.alternateToolsis a single global map (one entry per tool name), so a tool shared by several modules has exactly one entry.go.lintToolmay also be set (see below). Existing settings are merged, not overwritten..vscode/tasks.json(workspace root) — one task per tool, with sensible default args. Labels are prefixed by module directory to avoid collisions, e.g.services/auth: golangci-lint run ./...,services/api: goimports -l .. Each task runs in its module's directory. Existing tasks are merged.
go.alternateTools only changes which binary a tool resolves to — it does not,
by itself, make the Go extension run that tool. Two things matter:
- Workspace Trust. The Go extension only reads
go.alternateToolsfrom.vscode/settings.jsonin a trusted workspace. Accept VS Code's Workspace Trust prompt (or "Trust" the folder) — in Restricted Mode the mappings, and the Go extension itself, are ignored. - Linting.
go.lintToolis unset by default, so golangci-lint never runs as a linter on its own. This extension setsgo.lintTooltogolangci-lint(orgolangci-lint-v2for/v2/modules) only when every configured module declares it — and only if you haven't already chosen a linter. The pinned binary is then used via the shim. - Per-module is your choice.
go.lintToolis workspace-global: it can only name one linter for the whole workspace. Because whether a module declares golangci-lint is a deliberate per-module decision, a mixed workspace (some modules with it, some without) is left alone —go.lintToolis not set, so no module gets a linter forced on it. Each module'stasks.jsonentries still reflect exactly what that module declares. Setgo.lintToolyourself if you want one linter everywhere. - Formatting.
goimports/gofumptare mapped ingo.alternateTools, but the Go extension formats via gopls by default. To route formatting through the pinned binary, setgo.formatTooltogoimports/gofumptyourself (gofumpt is often better enabled viagopls:"formatting.gofumpt": true). - Tasks in
tasks.jsonrungo tool <name>directly and work regardless of the above.
- Monorepo aware — when more than one module has unconfigured tool directives, you get a multi-select list (shown as paths relative to the workspace root) to choose which modules to configure.
- Auto-prompt — on activation, if unconfigured modules with tool directives
exist, you're asked whether to set up the integration.
- Single module: "Go tool directives detected in
<module>. Set up VSCode integration?" - Multiple: "Go tool directives detected in N modules…", then the picker.
- Single module: "Go tool directives detected in
- Preview before writing — once you confirm and choose modules, a modal lists every shim, setting, and task that will be created or changed; nothing is written until you click Apply.
- Don't Ask Again — sets
"goToolsInit.ignore": truein the workspace-root.vscode/settings.json, suppressing the auto-prompt for this workspace. Never for Any Workspace suppresses it on this machine entirely (stored in the extension's global state). The Init Go Project command always bypasses both flags, and (with multiple modules) lists every module, marking configured ones(already configured). - Idempotent — safe to re-run when you add new tools; it only adds what is missing and refreshes the shims.
- No tool directives anywhere → tells you, and suggests
go get -tool <import-path>. - On success, shows a notification listing what was generated.
This extension is a stop-gap. The Go team is tracking native go tool support in
the official Go extension — see
golang/vscode-go#3799. If/when
that lands, the go.alternateTools half of this extension becomes unnecessary; the
generated tasks.json entries (plain go tool <name> runners) are likely to stay
useful regardless. Track the upstream issue before investing heavily here.
- Shims are marked executable on Unix-like systems (best effort; chmod is skipped on Windows).
- JSON files are read tolerantly (comments and trailing commas are accepted on
read), but are rewritten as plain JSON, so hand-written comments in
settings.json/tasks.jsonare not preserved.
npm install
npm run compile # or: npm run watchPress F5 in VSCode to launch an Extension Development Host.
