CLI
Use gsx to scaffold a project, run the development loop, generate Go, and format .gsx files.
gsx [global flags] <command> [arguments]Choose a command
| Task | Command |
|---|---|
| Create a starter project | gsx init [dir] |
| Generate, build, and reload while editing | gsx dev [dir] |
Generate .x.go files | gsx generate [paths...] |
Format .gsx files | gsx fmt [paths...] |
| Fix whitespace after upgrading | gsx fix whitespace [paths...] |
| Inspect the resolved project setup | gsx info |
| Remove the generation cache | gsx clean --cache |
| Start the editor language server | gsx lsp |
| Show version and build information | gsx version |
| List commands | gsx help |
Global flags
-C is the only true global flag. It must appear before the command name and sets the base directory for commands that resolve project paths or configuration:
gsx -C ./web generate .| Flag | Effect |
|---|---|
-C dir | Use dir as the base for project paths and configuration. |
-q and -v may also appear before or after generate, but they affect that command only:
gsx -q generate
gsx generate -v ./viewsgsx init
Create the simple gsx + Vite starter in a new directory:
gsx init myappgsx init [dir] [flags]| Flag | Effect |
|---|---|
--template simple | Select the starter template. simple is the only current template. |
--module path | Set the Go module path. The default is the target directory name. |
--force | Overwrite an existing go.mod or package.json. |
--yes, -y | Run setup commands without prompting. |
Interactive setup
In a terminal, gsx init asks for a project name when dir is omitted. It then scaffolds the project and asks before running each setup command.
gsx initPress Enter, y, or yes to run a step. Skipping one step does not skip the remaining steps. Ending input (Ctrl-D) stops init: it runs nothing more and prints the steps it did not run. At the project-name prompt, nothing is created.
Non-interactive setup
Use --yes when a script should scaffold and run every setup command:
gsx init myapp --module example.com/acme/myapp --yesWhen standard input is not a terminal (a pipe, a file, or /dev/null) and --yes is omitted, init scaffolds the files, then prints the setup commands instead of running them.
init exits 0 on success, 2 for invalid usage or an existing protected project file, and 1 when scaffolding or a setup command fails, or input ends at a prompt.
gsx dev
Run the development loop from the project directory:
gsx devPass an optional project directory with gsx dev [dir].
Relevant source changes regenerate as needed, build, swap the server, and reload the browser. After the first successful build, later generation or build failures leave the last working server running. A .env change only restarts the backend with fresh environment values; it does not regenerate or build. See the development loop for the full file-by-file behavior.
| Flag | Effect |
|---|---|
--web command | Set the front-door command. The default is npx vite. |
--no-web | Run generation and the Go server without the front door. |
--build command | Set the server build command. |
--run command | Set the built-server command. |
--log | Copy backend output to the default per-project log file. |
--log-file path | Copy backend output to path. |
--no-log | Disable backend file logging, including logging from gsx.toml. |
Common customizations
Run without Vite when another tool provides the front door:
gsx dev --no-webOverride the build and run commands for one session:
gsx dev --build "go build -o ./tmp/app ./cmd/site" --run "./tmp/app"Command flag values are split on whitespace. For arguments that need exact boundaries, use arrays in the [dev] configuration.
Before the first successful build there is no previous server to keep alive. Fix the startup error and save again. A clean signal-driven shutdown exits 0; invalid flags exit 2, and fatal startup errors exit 1.
gsx generate
Generate a sibling .x.go file for every .gsx file under the selected paths:
gsx generate
gsx generate ./views ./emailWith no path, the command uses .. Directory paths are searched recursively, skipping hidden directories, vendor, node_modules, testdata, and directories listed in a go.mod ignore directive (for example ignore ./frontend).
| Flag | Effect |
|---|---|
--no-cache | Regenerate without reading or writing cached results. |
--json | Write diagnostics as one JSON array to stdout. |
--watch | Keep running and regenerate when source files change. |
--format=ndjson | In watch mode, write one machine-readable event per line. |
-q | Suppress the success summary. |
-v | List each written or removed file before the summary. |
Generate flags may appear before or after path arguments.
Diagnostics and exit status
Normal diagnostics go to stderr. Use JSON when another program consumes them:
gsx generate --json ./views| Exit | Meaning |
|---|---|
0 | Generation succeeded, including when everything was already current. |
1 | A source diagnostic or operational error prevented generation. |
2 | The command, configuration, or a path was invalid. |
Warnings, such as duplicate-attribute, are printed but do not change the exit status. The [lint] table in gsx.toml can turn each lint off or make it an error; see Lints.
When a .gsx file has a generation error, gsx replaces its previous generated file with a deliberately non-compiling marker. This prevents go build from silently using stale output. Fix the .gsx file and run generate again.
Deleting a .gsx file removes its generated sibling on the next generation run. Only files with gsx's generated-file header are removed; a hand-written file with the same name is left alone. I/O and project-loading failures do not replace files with error markers.
Test coverage
go tool cover -func and -html fail on packages with gsx components (expected declaration, found component). Generated code maps errors back to .gsx with //line directives, and Go's coverage tooling handles them inconsistently (golang/go#41222). Drop the template entries before reporting:
go test -coverprofile=cover.out ./...
grep -v '\.gsx:' cover.out > cover.go.out
go tool cover -func cover.go.outComponents are then not measured. Don't rename .gsx: to .x.go: in the profile: -html renders, but -func reports wrong percentages.
Watch mode
Use watch mode when an integration needs generation without the full dev loop:
gsx generate --watchFor a machine-readable stream, use newline-delimited JSON:
gsx generate --watch --format=ndjsonHuman watch output goes to stderr. In NDJSON mode, stdout contains only event objects; diagnostic fields use the same shape as --json.
gsx fmt
Rewrite .gsx files in place before committing:
gsx fmt -w .Paths may be files or directories. Directories are searched recursively; with no path, the command formats .. It skips the same directories as gsx generate.
| Flag | Effect |
|---|---|
| none | Write formatted source to stdout. |
-w | Rewrite changed files in place. |
-l | List files whose source would change. |
-d | Show a unified diff. |
-imports=goimports | Remove unused imports and normalize import declarations. This is the default. |
-imports=gofmt | Format existing imports without removing, merging, or regrouping them. |
-no-imports | Alias for -imports=gofmt. |
-stdin-filename=PATH | Read the source from stdin and treat it as PATH. |
The goimports mode cannot add a missing import; it organizes imports already present in the .gsx file. A CLI import mode overrides the [formatter] setting.
gsx fmt also formats CSS in <style> and JavaScript in executable <script> bodies. Interpolation holes are preserved. If an embedded body cannot be formatted safely, that body is left unchanged.
js`/css` attribute values are re-indented the same way as <script>/<style> bodies. Plain "…" string attributes are left verbatim.
Text flow
Inline elements (code, a, span, em, b, img, and other phrasing-content tags) stay in the surrounding text flow: the formatter never breaks their children open to meet the width budget. If a line has no other legal break point, it stays over budget rather than exploding an inline element.
Long prose wraps between words. The formatter never adds whitespace where text and markup touch.
{" "} sticks to the word before it and never starts a line.
Author layout still wins: breaking the line right after an opening tag's > keeps that element block-formatted, even if its content would otherwise fit inline.
Formatting stdin
-stdin-filename formats content that is not on disk. PATH names the file in -l/-d output and error messages and selects its gsx.toml, .editorconfig, and package for import analysis — nothing is read from it, and it need not exist. Path arguments and -w are rejected in this mode.
A pre-commit hook uses it to check the staged blob rather than the working copy:
git show ":$f" | gsx fmt -l -stdin-filename "$f"Check formatting in CI
-l and -d exit 1 when any file differs, so either works as a CI check:
gsx fmt -l .| Exit | Meaning |
|---|---|
0 | Nothing failed; with -l/-d, nothing differs. |
1 | With -l/-d only: at least one file differs and nothing failed. |
2 | Something failed: a usage error (invalid flag, import-mode combination, or path) or a read, parse, analysis, or write failure on any file. |
A failure wins over a difference, so a script can tell "run -w" from "this file is broken".
gsx fix
gsx fix whitespace [-all] [-w] [-l] [-d] [paths...] applies the punctuation-space and redundant-space-literal fixes in place. By default it fixes only line breaks that v0.4 rendered as nothing, so output matches v0.4 there and is unchanged elsewhere; run it beforegsx fmt when upgrading. -all applies every finding, including same-line spaces such as those an earlier gsx fmt joined onto the line, and changes output. -l lists and -d diffs without writing, and exit 1 when a file would change; add -w to write as well.
gsx info
Inspect the configuration that gsx resolves for the current project:
gsx infoThe human view shows the active config path and resolved filters, renderers, attribute rules, lint levels, minification, formatter width, and environment overrides. Use JSON for automation:
gsx info --jsonHuman and JSON output are different inspection views, not identical encodings of the same fields. Scripts should consume --json rather than parse the human table.
Resolution failures exit 1. Invalid arguments or project configuration exit 2; a successful inspection exits 0.
gsx clean
Remove the generation cache:
gsx clean --cacheThe command exits successfully when the cache is disabled or absent. It refuses to remove a directory that does not contain gsx's cache marker, which protects against an unsafe GSXCACHE value. A refusal or removal failure exits 1; invalid flags exit 2.
gsx lsp
Editors start the language server over standard input and output:
gsx lspYou normally do not run this command yourself. See Editor setup for VS Code, Neovim, Zed, and generic client configuration.
gsx version
Print the installed version:
gsx versionWhen available, the output also includes the commit revision, commit time, dirty-tree state, and Go toolchain version. Local builds without an embedded module version report (devel).
Environment variables
| Variable | Effect |
|---|---|
| `GSX_MINIFY=none | full` |
GSXCACHE=off | Disable the generation cache. |
GSXCACHE=path | Use path instead of the operating-system user cache directory. |
For minification, a programmatic option in a custom gsx binary wins over GSX_MINIFY, which wins over gsx.toml. Use --no-cache for a single uncached generation run.
Stability
The CLI is alpha and ships in tagged releases with the runtime (one module, one tag; see Releases and versioning). This page lists the commands that are available now; see Status for the broader shipped surface.