Where
codegen/gocli.go — Options.CLIDir emits a single file. In the example that is example/tasks/cli/cli_gen.go: 1,917 lines, 101 references to cobra, with Client, Transport, Request and Do in the same package as the command tree. There is no option that emits one without the other.
What
A Go program that wants the typed client — the request encoder, the filter vocabulary, Do, the typed problem bodies — must import a package that brings spf13/cobra and a whole second command tree with it.
The consumer this bites is not hypothetical. An existing admin CLI already built on cobra + resty wants the generated encoder so its flags stop drifting from the API, and what it can take instead is a package that wants to be the CLI. Same for any server-to-server caller in Go: a sync job or an internal service consuming an sqlb API takes a command-line framework to make one typed request.
This is the split the TypeScript emitter already makes, and argues for. client.gen.ts imports nothing; queries.gen.ts imports @tanstack/react-query. The framework binding is a separate file precisely so a consumer that does not want the framework does not pay for it. docs/dart/README.md reaches the same conclusion from the other direction — one file that "imports nothing", explicitly because Dart has no framework layer to make optional.
Go is the one emitter where a dependency-free client is possible and is not offered. Nothing about Client/Do/Request needs cobra; the coupling is the file boundary, not the design.
Fix
Split the emission the way TSDir already splits it — a client.gen.go carrying Client, Transport, Request, Do and the typed row/param/problem types, importing only the standard library, and a cli_gen.go carrying the cobra tree and importing the first.
Two things worth settling with it:
- One knob or two.
CLIDir could simply emit both files, which costs existing consumers nothing and makes the client importable on its own. A separate ClientDir would let a consumer take the client without generating a command tree at all — better for the server-to-server case, and the more honest shape if the two are really different artefacts.
- Where the runtime lives.
codegen/gocli_runtime.go suggests some of this is already a distinct body of code; whichever file the shared runtime lands in is the one the eject story has to keep working, since docs/eject.md promises a generated layer that imports pgx and the standard library and nothing else — and today the Go client is the one generated artefact that cannot make that claim.
Not a docs problem
Worth recording, because it was the first hypothesis and it is wrong: the Go CLI is discoverable. It is in README.md:73, the docs/architecture.md fan-out diagram, docs/cli/README.md and docs/vision.md. An external evaluation on 2026-08-01 nonetheless concluded three separate times that sqlb "emits no Go client" and listed a Go client emitter as an outstanding upstream ask. Re-reading, the likely cause is that every one of those mentions describes it as a CLI — a command tree, a --help vocabulary, a cobra binary — and a reader looking for a client library to embed does not recognise their own use case in any of them. If the split above lands, naming the client half separately is most of the fix for that too.
Filed 2026-08-01, from an external adoption evaluation.
Where
codegen/gocli.go —
Options.CLIDiremits a single file. In the example that isexample/tasks/cli/cli_gen.go: 1,917 lines, 101 references tocobra, withClient,Transport,RequestandDoin the same package as the command tree. There is no option that emits one without the other.What
A Go program that wants the typed client — the request encoder, the filter vocabulary,
Do, the typed problem bodies — must import a package that bringsspf13/cobraand a whole second command tree with it.The consumer this bites is not hypothetical. An existing admin CLI already built on cobra + resty wants the generated encoder so its flags stop drifting from the API, and what it can take instead is a package that wants to be the CLI. Same for any server-to-server caller in Go: a sync job or an internal service consuming an sqlb API takes a command-line framework to make one typed request.
This is the split the TypeScript emitter already makes, and argues for.
client.gen.tsimports nothing;queries.gen.tsimports@tanstack/react-query. The framework binding is a separate file precisely so a consumer that does not want the framework does not pay for it.docs/dart/README.mdreaches the same conclusion from the other direction — one file that "imports nothing", explicitly because Dart has no framework layer to make optional.Go is the one emitter where a dependency-free client is possible and is not offered. Nothing about
Client/Do/Requestneeds cobra; the coupling is the file boundary, not the design.Fix
Split the emission the way
TSDiralready splits it — aclient.gen.gocarryingClient,Transport,Request,Doand the typed row/param/problem types, importing only the standard library, and acli_gen.gocarrying the cobra tree and importing the first.Two things worth settling with it:
CLIDircould simply emit both files, which costs existing consumers nothing and makes the client importable on its own. A separateClientDirwould let a consumer take the client without generating a command tree at all — better for the server-to-server case, and the more honest shape if the two are really different artefacts.codegen/gocli_runtime.gosuggests some of this is already a distinct body of code; whichever file the shared runtime lands in is the one the eject story has to keep working, sincedocs/eject.mdpromises a generated layer that imports pgx and the standard library and nothing else — and today the Go client is the one generated artefact that cannot make that claim.Not a docs problem
Worth recording, because it was the first hypothesis and it is wrong: the Go CLI is discoverable. It is in
README.md:73, thedocs/architecture.mdfan-out diagram,docs/cli/README.mdanddocs/vision.md. An external evaluation on 2026-08-01 nonetheless concluded three separate times that sqlb "emits no Go client" and listed a Go client emitter as an outstanding upstream ask. Re-reading, the likely cause is that every one of those mentions describes it as a CLI — a command tree, a--helpvocabulary, a cobra binary — and a reader looking for a client library to embed does not recognise their own use case in any of them. If the split above lands, naming the client half separately is most of the fix for that too.Filed 2026-08-01, from an external adoption evaluation.