Skip to content

generate Command

gtb generate scaffolds new projects and commands. It is part of the framework-developer CLI (the gtb binary), used while building a tool, not a runtime command shipped in your tool. See the Scaffolding and Generating Commands how-tos for task walkthroughs.

Usage

gtb generate <subcommand> [flags]

Subcommands

Subcommand Purpose
project Generate a new project skeleton.
command Generate a new command or subcommand.
add-flag Add a flag to an existing command.
protect <command-path> Mark a command as protected from regeneration overwrites.
unprotect <command-path> Allow a command to be overwritten again.
docs Generate Markdown documentation for a command or package.
man Generate roff man pages for the command tree.

generate project

Generate a new project skeleton. Run without --name (or without --repo for a hosted project) in an interactive terminal to launch the wizard, whose pages are described in the scaffolding how-to; otherwise supply the flags directly. The wizard and the flags produce the same manifest.

Core:

Flag Default Description
--name, -n β€” Project name (e.g. als).
--repo, -r β€” Repository in org/repo format.
--forge-backend github The forge the project is hosted on: github, gitlab, gitea, codeberg or bitbucket. Decides the release source, the host default and the CI skeleton (only GitHub and GitLab ship one; the others get no CI files and the run says so). Recorded as release_source.backend and enables the forge's feature. Replaces --git-backend, which is gone.
--no-forge false The project is not hosted on a forge: no backend, no repository; requires --module.
--module (<host>/<org>/<repo>) Go module path. Required with --no-forge; otherwise an override for a vanity import path. Recorded as module_path.
--forge-credentials β€” Further forges to enable for credential capture (their init <forge> wizard and adapter), never the release source.
--release-channel (forge when hosted) Where self-update releases from when update is enabled. forge reads the backend's releases and needs a hosted project. static reads a pointer and per-tag manifests under --release-base-url with no forge involved (spec 0203); it is the one channel a --no-forge project can take. direct was withdrawn and is refused by name. Recorded as release_source.type.
--release-base-url The static channel's location: an https URL with no userinfo, query or placeholder host, under which the release publishes latest.json and <tag>/release.json (layout). Required with --release-channel static, refused with any other. Recorded as release_source.static.base_url; gtb set release_source.static.base_url changes it (set the URL before switching the type).
--host (backend's canonical host) Git host, for a self-managed instance.
--private false Mark the repository private (requires a token for updates).
--description, -d A tool built with gtb Project description.
--features, -f update,init,mcp,docs,doctor,changelog,keychain Features to enable: see below. The flag replaces the default set rather than adding to it. A forge name is refused here; the forge is chosen with --forge-backend.
--chat-providers (none) go/chat provider modules wired into the binary, for the tool's own code or for the ai feature, recorded as chat.providers: see adapters. Independent of ai; with ai and no list, every known provider; an explicit empty list with ai is refused.
--chat-default-provider (the only provider, when one is linked) The tool's default chat provider. Required when --chat-providers links more than one: the generator does not choose for you. Must be one of the linked providers. Recorded as chat.default.provider and shipped as the tool's embedded default: see chat defaults.
--chat-default-model (the provider module's choice) Default model for the default provider.
--chat-base-url β€” API endpoint for the default provider. Required by openai-compatible and azure-openai; HTTPS, no userinfo, no placeholder host.
--chat-api-version β€” Dated API version. Required by azure-openai, which has no default.
--chat-project β€” Cloud project, for gemini-vertex (optional; falls back to GOOGLE_CLOUD_PROJECT at runtime).
--chat-location β€” Region, for gemini-vertex and bedrock (optional; falls back to the platform's environment at runtime).
--go-version (running toolchain) Go version for go.mod. Recorded as version.go; regenerate renders that, never the toolchain it happens to run on.
--telemetry-endpoint β€” Where the telemetry feature sends usage events, an http or https URL. Plain http is accepted for a collector on a private network and every generate and regenerate warns about it. Recorded as telemetry.endpoint.
--telemetry-otel-endpoint β€” OpenTelemetry collector endpoint. Recorded as telemetry.otel_endpoint.
--auto-initialise false Run the first-run bootstrap automatically when the config is missing. Recorded as bootstrap.auto_initialise.
--skip-config-check β€” Commands that run without a config file (repeatable). Recorded as bootstrap.skip_config_check.
--auxiliary-commands β€” Commands that take the root pre-run's auxiliary fast path (repeatable). Recorded as bootstrap.auxiliary_commands.
--config-layers (framework default) Config-stack layers the tool wires, lowest precedence first; the order is the precedence. defaults must be lowest, flags highest, and project below env. Recorded as config.layers.
--config-formats (none) Config formats the tool reads beyond YAML, which is built in: toml, json, hcl, ini, xml, dotenv, properties. Each is a blank import in cmd/<name>/config.go. Recorded as config.formats.
--config-format yaml The format of the tool's own config file, the one init writes: yaml, toml, json or hcl, and it must be in --config-formats unless it is YAML. Recorded as config.format.
--help-type none Help channel type: slack, teams, or none (with --slack-*/--teams-*).
--path, -p . Destination path.
--overwrite ask File-conflict handling: allow, deny, or ask.
--no-verify false Skip go mod tidy and golangci-lint after generation; exit 0 unverified. Without it a failed or unavailable step exits 3 with its reason (see regenerate's exit codes). Either way go.mod carries the direct requirements the scaffold's imports imply.
--env-prefix β€” Env-var prefix for config overrides (e.g. MY_APP).
--update-policy (framework default: disabled) Self-update posture: disabled, prompt, or enabled.
--mcp-mode (compact) MCP publication mode: compact (three discovery tools) or direct (one native tool per command). Recorded as mcp.mode; gtb set mcp.mode changes it later. See AI Agents & MCP.
--update-check-interval (framework default: 24h) Interval between self-update checks, as a Go duration (e.g. 24h, 168h).
--ci-component-source (gitlab.com/phpboyscout/cicd) Override the phpboyscout/cicd component include base in the scaffolded GitLab pipeline.
--template β€” Custom template overlay source <src>@<ref> (local path or forge repo); repeatable, layered in order.

Help channel (used when --help-type is slack/teams): --slack-channel, --slack-team, --teams-channel, --teams-team. The channel is required for its type; a type outside slack, teams, none is refused.

Features accepted by --features:

Group Values Notes
Built-in commands (default on) update, init, docs, doctor, changelog Wired via props.SetFeatures.
Built-in commands (opt-in) ai, config, telemetry, man
Forges (not selectable here) A forge feature is implied by --forge-backend (one) and --forge-credentials (more). Each enabled forge feature adds that forge's init <forge> credential wizard, config section, embedded asset bundle and linked adapter. After generation gtb enable <forge> / gtb disable <forge> still toggle them. Constants live in pkg/setup/forge, not props.
Links (build-time) keychain, mcp Not SetFeatures toggles: each selects a cmd/<name>/<id>.go blank import, which the manifest's entry owns. gtb enable <id>/gtb disable <id> write or remove the file; a hand-deleted file comes back on the next regenerate. mcp defaults on (a manifest that says nothing links it; leaving it out of --features records mcp: false), the keychain off. A binary without mcp.go ships without go/mcp and the MCP SDK.

--features replaces the default set rather than extending it, so a selection must name every feature the tool should ship with: --features gitlab alone yields a tool with the six default built-ins off. An unrecognised name is rejected before anything is written.

Every feature can be toggled after generation with gtb enable/gtb disable, which leaves the tree in line in one command: the root command, the adapter files and any derived field a newly enabled feature needs (the ai feature's provider list) are written by the same run, and no regenerate is needed afterwards.

Adapters. A chat provider or a forge is a module the tool blank-imports from its own main package, and the generator writes those imports from the manifest into two DO NOT EDIT files beside keychain.go. Each exists only while the tool uses it: chat.go while a provider is linked or ai is on, forge.go while a forge feature is enabled. A tool with neither has neither file.

Two things are easy to run together and are deliberately separate. chat.providers is the wiring: which provider modules the binary links, a shortcut for a tool that uses go/chat from its own code as much as for one that uses GTB's AI features, and the place new providers appear as they become available. The ai feature is the switch for GTB's AI-based features: the ai config section and ai.provider, the init ai wizard, docs ask, the chat defaults bundle and the doctor check. A tool can link providers without ai (keryx does), and ai needs at least one linked provider to do anything.

File Derived from Modules
cmd/<name>/chat.go chat.providers in the manifest, whether or not ai is enabled claude, claude-local β†’ go/chat-anthropic; openai, openai-compatible, codex-local β†’ go/chat-openai; gemini, gemini-vertex, agy-local β†’ go/chat-gemini; bedrock β†’ go/chat-bedrock; azure-openai β†’ go/chat-openai-azure
cmd/<name>/forge.go the enabled forge features, implied by --forge-backend and --forge-credentials github β†’ go/forge-github; gitlab β†’ go/forge-gitlab; gitea, codeberg β†’ go/forge-gitea; bitbucket β†’ go/forge-bitbucket

Nothing is linked unless asked: --chat-providers names the list, and the ai feature with no list takes every known provider, since a tool that offers GTB's AI features is configured by its consumers the way gtb itself is. Generation only emits the import; what the running tool's init wizard and doctor can set up for a provider is a separate, narrower question (today: the three API-key providers and the local CLIs, which need nothing; Vertex, Bedrock and Azure are configured through go/chat's own settings). A name no module registers is refused, at generation and at regenerate.

A durable override is a manifest field, never a deleted file. Every DO NOT EDIT file under cmd/<name>/ (chat.go, forge.go, keychain.go, the chat/assets bundle) and pkg/cmd/root/signing.go is re-emitted from the manifest by every command that writes the manifest, so its presence is a fact about the manifest and its absence is temporary. To ship no chat provider with ai on, set chat.providers: []; to drop the linked modules, gtb unset chat.providers (gtb disable ai turns the AI features off and leaves the wiring, saying so); to drop the keychain, gtb disable keychain; to turn signing off, gtb disable signing. A project generated before the chat: block existed has no block at all, and gets the full list written into its manifest the first time it is regenerated (or enable ai is run) with ai enabled.

The generated go.mod names no gtb tool line. The manifest's version.gtb is the pin, regenerate refuses an older gtb, and the README carries go install gitlab.com/phpboyscout/go-tool-base/cli/cmd/gtb@<version>. Nor does it name golangci-lint or mockery: the justfile and CI run the installed binaries, and the golangci-lint tool line pinned v1 and dragged an old viper whose cloud.google.com/go/compute made go mod tidy ambiguous the moment chat-gemini was linked. Only the framework's own cmd/changelog and cmd/docs remain.

Every author setting has one home. Each flag above that says "recorded as" names the manifest field it writes, and regenerate reads that field back unchanged; cli/pkg/generator/author_settings.go is the table, and a test holds it and SkeletonConfig to each other (spec 0197 D1, D2). Nothing about a generated project depends on the machine regenerate runs on.

Chat defaults. AI in a generated tool is one decision with several parts: which providers to link, which is the default, which model, and the endpoint a few providers need. The manifest records the answer under chat: and the generator ships it as the tool's lowest config layer, beside the file that links the modules:

chat:
  providers: [claude, claude-local]
  default:
    provider: claude
    model: claude-opus-5     # optional
    base_url: ""             # openai-compatible, azure-openai
    api_version: ""          # azure-openai
    project: ""              # gemini-vertex
    location: ""             # gemini-vertex, bedrock
File Role
cmd/<name>/chat/assets/config.yaml The author's defaults, registered by chat.go as an ai defaults bundle. The lowest layer of the running tool's config: an end user's own file overrides every key.
cmd/<name>/chat/assets/init/config.yaml The same values as an init template, so a tool with the init feature seeds them into the end user's file as a visible, editable starting point.

Both are DO NOT EDIT files rewritten from the manifest by regenerate project, and both are absent when the manifest names no default. One linked provider is its own default. With several, name one: an older manifest that links several and names none regenerates without an author default and warns naming chat.default.provider; the tool then falls back to its runtime resolution. Credentials never appear in the manifest or these files; they are the end user's, captured by init ai or supplied through the environment.

Git lifecycle (the new project is git-initialised with an initial commit by default):

Flag Default Description
--no-git false Skip the post-generation git init and initial commit.
--push false After the initial commit, add the derived remote as origin and push (push failures are non-fatal). Conflicts with --no-git.
--git-branch main Default branch the initial commit lands on.

Release signing (off by default; supplying --signing-email implies --signing):

Flag Default Description
--signing false Enable consumer-side release-signature verification (scaffolds internal/trustkeys, wires props.Signing).
--signing-require-checksum false Fail a self-update closed without a verified checksum. Safe from day one. Recorded as signing.require_checksum; renders props.Tool.Signing.RequireChecksum.
--signing-require-signature false Fail a self-update closed without a valid signature. Not before your first signed release has shipped: an unsigned release then fails every consumer's update. Recorded as signing.require_signature.
--signing-email β€” Release WKD email (external_key_email); enables the external trust-anchor leg.
--signing-key-source both Trust-anchor source: embedded, external, or both.
--signing-require-external-crosscheck false Fail signing closed when the external (WKD) resolver is unreachable.
--signing-key-id β€” Signing key id/ARN/alias (or PEM path for local) the release pipeline signs with; wires the GoReleaser signs block. Refused without --signing or --signing-email, since only the signing path renders it.
--signing-backend (aws-kms when --signing-key-id set) gtb sign backend for the release pipeline.
--signing-kms-region (eu-west-2) AWS region for the aws-kms backend.
--signing-public-key (internal/trustkeys/keys/signing-key-v1.asc) Path to the embedded public key the signature identifies.

Environment

Variable Effect
GTB_FRAMEWORK_REPLACE=<dir> Development only. Every go.mod the generator writes (on generate project and on regenerate) gains replace gitlab.com/phpboyscout/go-tool-base => <dir>, so a scaffold tidies and builds against that framework working tree rather than the latest release. The e2e suite sets it to the repo root; set it yourself to test a template change against a branch. It is read at render time and recorded nowhere: regenerate with it unset and the directive is gone. Never publish a project with it set.

generate command

Generate a new command or subcommand (optionally AI-converted from a script).

Flag Default Description
--name, -n β€” Command name (kebab-case).
--short, -s / --long, -l β€” Short / long help text.
--parent root Parent command to nest under; use parent/child for deep nesting.
--args β€” Positional-arg validator (e.g. ExactArgs(1), ArbitraryArgs).
--alias, -a β€” Command alias(es) (repeatable).
--flag, -f β€” Flag spec(s) to add (repeatable): name:type:description:persistent:shorthand:required:default:defaultIsCode.
--assets false Include assets-directory support.
--script β€” Path to a script (bash/python/js) to convert to Go. Mutually exclusive with --prompt.
--prompt β€” Natural-language description (or a file path) to generate from. Mutually exclusive with --script.
--agentless false Use the original retry loop instead of the autonomous repair agent.
--max-steps 0 (β†’20) Max repair-agent reasoning steps.
--non-interactive (true when CI is set) Never pause for input: disables the repair agent's query_user tool.
--persistent-pre-run / --pre-run false Generate the corresponding hook.
--with-initializer false Generate an Initializer for this command.
--with-config-validation false Generate a config-validation stub for this command.
--force false Overwrite existing files.
--protected false Mark the command as protected from regeneration (tri-state: --protected, --protected=false, or omitted for nil).
--mcp-enabled true Expose this command as an MCP tool (tri-state: --mcp-enabled=false withholds it from the MCP surface; it stays runnable on the CLI).
--path, -p . Filesystem project root (not a command path).

All generate subcommands also accept these persistent flags (for AI-assisted generation): --provider (any chat provider the gtb binary links; gtb generate --help lists them, and the list is chat.ProviderModules()), --model (AI model), and --dry-run (preview changes without writing files).

generate add-flag

Add a new flag to an existing command (see Add Flags).

generate docs

Generate Markdown docs for a command or package.

Flag Default Description
--command β€” Name/path of the command to document.
--package β€” Package to document (relative to project root).
--parent β€” Parent command name (if not in the manifest).
--agentless false Skip AI generation; write boilerplate only.
--public-api false Module is publicly published: defer package API reference to pkg.go.dev (otherwise a local go doc hint). Equivalent to module_published: true in the manifest.
--no-ai-attribution false Keep AI/model attribution out of the generated frontmatter authors:: human author(s) only. Default: the AI model is appended as an additive co-author.
--path . Project root.

One of --command/--package/--source is required. (--source is deprecated; use --command.)

Docs are emitted in the project's layout (docs_layout in .gtb/manifest.yaml): the DiΓ‘taxis quadrant tree (docs/reference/cli/, docs/explanation/components/) for new projects, or the legacy flat tree (docs/commands/, docs/packages/). See Generating Documentation β†’ "Documentation layout".

generate man

Generate roff man pages for the command tree.

Flag Default Description
--dir ./man Output directory (pages under <dir>/man<section>).
--section 1 Man section number.
--source (<tool> <version>) TH source footer.
--manual (<Tool> Manual) TH manual title.
--date (none, reproducible) Stamp this date (YYYY-MM-DD or RFC3339) into the .TH header. Omit for reproducible output with no date trailer.

generate protect / generate unprotect

gtb generate protect <command-path> marks a command so regeneration won't overwrite it; unprotect reverses it. See Configure Generator Ignore.

Run any subcommand with --help for the complete, authoritative flag set.