Documentation
¶
Overview ¶
Package generator implements the code generation engine that powers project scaffolding, command generation, and regeneration from manifest definitions.
The core Generator type orchestrates a shared pipeline: asset generation, command registration, child re-registration, manifest updates, and documentation output. It operates on a manifest-first architecture where .gtb/manifest.yaml is the single source of truth for project structure.
Key entry points are Generator.GenerateSkeleton for new projects, [Generator.GenerateCommand] for adding commands, and Generator.RegenerateProject for rebuilding registration files from an existing manifest.
Index ¶
- Constants
- Variables
- func AppendIgnorePattern(fs afero.Fs, projectPath, pattern string) (changed bool, err error)
- func ApplyAuthorSettingsTo(m *Manifest, cfg SkeletonConfig)
- func CheckAIFlags(c *Config) error
- func ConfigFormats() []string
- func ConfigSourceKinds() []string
- func DefaultChatProviders() []string
- func DeriveEnvPrefix(name string) string
- func DiffAuthorSettings(before, after *Manifest) []string
- func EncodeManifestFile(fs afero.Fs, manifestPath string, m *Manifest) error
- func FeatureEnabledIn(features []ManifestFeature, name string) bool
- func ForgeBackends() []props.FeatureID
- func IsOverrideOnlySourceKind(kind string) bool
- func IsWritableConfigFormat(format string) bool
- func KnownChatProviders() []string
- func ManifestPathFor(projectPath string) string
- func ManifestWarnings(m *Manifest) []string
- func PascalCase(s string) string
- func PreviewAppendIgnorePatterns(fs afero.Fs, projectPath string, patterns []string) (content string, err error)
- func PreviewRemoveIgnorePattern(fs afero.Fs, projectPath, pattern string) (content string, changed bool, err error)
- func RemoveIgnorePattern(fs afero.Fs, projectPath, pattern string) (changed bool, err error)
- func ScaffoldIgnoreFile(fs afero.Fs, projectPath string) error
- func SealIgnorePattern(fs afero.Fs, projectPath, pattern string) (changed bool, err error)
- func UnsealIgnorePattern(fs afero.Fs, projectPath, pattern string) (changed bool, err error)
- func ValidateCIComponentSource(source string) error
- func ValidateChatDefault(d ManifestChatDefault, providers []string, features []ManifestFeature) error
- func ValidateChatDefaultProvider(provider string, providers []string, features []ManifestFeature) error
- func ValidateChatProviders(providers []string, features []ManifestFeature) error
- func ValidateCommandName(name string) error
- func ValidateConfigFormats(formats []string, format string) error
- func ValidateConfigKeyCase(keyCase string) error
- func ValidateConfigLayers(layers []string) error
- func ValidateConfigSources(sources []ManifestConfigSource, layers []string, features []ManifestFeature) error
- func ValidateConfigStack(layers []string, sources []ManifestConfigSource, features []ManifestFeature) error
- func ValidateDescription(desc string) error
- func ValidateEnvPrefix(prefix string) error
- func ValidateExternalCommand(ec *ManifestExternalCommand) error
- func ValidateFeatureName(name string) error
- func ValidateFlagDefaultCode(def string) error
- func ValidateFlagName(name string) error
- func ValidateFlagShorthand(shorthand string) error
- func ValidateFlagType(flagType string) error
- func ValidateForgeBackend(backend string) error
- func ValidateHelpType(helpType string) error
- func ValidateHost(host string) error
- func ValidateLongDescription(desc string) error
- func ValidateMCPMode(mode string) error
- func ValidateMCPTitle(title string) error
- func ValidateManifest(m *Manifest) error
- func ValidateModulePath(path string) error
- func ValidateName(name string) error
- func ValidateOrg(org, releaseProvider string) error
- func ValidatePackagePath(pkgPath string) error
- func ValidateParentPath(parent string) error
- func ValidateReleaseBaseURL(baseURL string) error
- func ValidateReleaseChannel(channel string) error
- func ValidateReleaseSourceType(sourceType string) error
- func ValidateRepo(repo string) error
- func ValidateRepoName(name string) error
- func ValidateSelectableFeatureName(name string) error
- func ValidateSigningBackend(backend string) error
- func ValidateSigningExternalKeyEmail(email string) error
- func ValidateSigningKMSRegion(region string) error
- func ValidateSigningKeyID(keyID string) error
- func ValidateSigningKeySource(source string) error
- func ValidateSigningPublicKey(publicKey string) error
- func ValidateSlackChannel(channel string) error
- func ValidateSlackTeam(team string) error
- func ValidateTeamsChannel(channel string) error
- func ValidateTeamsTeam(team string) error
- func ValidateTelemetryEndpoint(endpoint string) error
- func ValidateTemplateSource(ts *TemplateSource) error
- func ValidateUpdateCheckInterval(interval string) error
- func ValidateUpdatePolicy(policy string) error
- type AuthorSetting
- type CommandContext
- type CommandFlag
- type CommandPipeline
- type Config
- type DryRunResult
- type ExternalCommandSpec
- type FilePreview
- type Generator
- func (g *Generator) AddTemplateSource(ctx context.Context, ts TemplateSource) error
- func (g *Generator) ApplyAuthorSettings(ctx context.Context, cfg SkeletonConfig) error
- func (g *Generator) ApplyFeatures(ctx context.Context, desired map[string]bool) ([]string, error)
- func (g *Generator) AttachExternalAdapter(ctx context.Context) error
- func (g *Generator) AttachExternalCommand(ctx context.Context, spec ExternalCommandSpec) error
- func (g *Generator) CheckIgnorePaths(paths []string) []IgnoreCheckResult
- func (g *Generator) CurrentFeatures() ([]ManifestFeature, error)
- func (g *Generator) CurrentSigning() (ManifestSigning, error)
- func (g *Generator) DetachExternalCommand(ctx context.Context, module string) error
- func (g *Generator) DisableSigning(ctx context.Context) error
- func (g *Generator) DivergedUnignoredFiles() ([]string, error)
- func (g *Generator) EnableRealTemplateClone() *Generator
- func (g *Generator) EnableSigning(ctx context.Context, signing ManifestSigning) error
- func (g *Generator) FeatureEnabled(name string) (bool, error)
- func (g *Generator) FindCommandParentPath(name string) ([]string, error)
- func (g *Generator) Generate(ctx context.Context) error
- func (g *Generator) GenerateCommandFile(ctx context.Context, cmdDir string, data *templates.CommandData) error
- func (g *Generator) GenerateDocs(ctx context.Context, target string, isPackage bool) error
- func (g *Generator) GenerateDryRun(ctx context.Context) (*DryRunResult, error)
- func (g *Generator) GenerateSkeleton(ctx context.Context, config SkeletonConfig) error
- func (g *Generator) GenerateSkeletonDryRun(ctx context.Context, config SkeletonConfig) (*DryRunResult, error)
- func (g *Generator) GetSetting(path string) ([]string, error)
- func (g *Generator) ListExternalCommands() ([]ManifestExternalCommand, bool, error)
- func (g *Generator) ListIgnoreRules() (*IgnoreListing, error)
- func (g *Generator) ListTemplateSources() ([]TemplateSource, error)
- func (g *Generator) LoadManifest() (*Manifest, error)
- func (g *Generator) RegenerateCommand(ctx context.Context, cmd ManifestCommand, parentPath []string) error
- func (g *Generator) RegenerateManifest(ctx context.Context) error
- func (g *Generator) RegenerateProject(ctx context.Context) error
- func (g *Generator) RegenerateProjectDryRun(ctx context.Context) (*DryRunResult, error)
- func (g *Generator) Remove(ctx context.Context) error
- func (g *Generator) RemoveTemplateSource(ctx context.Context, name string) error
- func (g *Generator) SealedTrackedFiles() ([]string, error)
- func (g *Generator) SetMCPEnabled(ctx context.Context, commandPath string, enabled bool) error
- func (g *Generator) SetMCPHints(ctx context.Context, commandPath string, hints setup.MCPHints, clear bool) error
- func (g *Generator) SetProtection(ctx context.Context, commandName string, protected bool) error
- func (g *Generator) SetSetting(ctx context.Context, path string, values []string) error
- func (g *Generator) UnsetSetting(ctx context.Context, path string) error
- func (g *Generator) UpdateTemplateSource(ctx context.Context, name string) error
- type IgnoreCheckResult
- type IgnoreListEntry
- type IgnoreListing
- type IgnoreRules
- type Manifest
- type ManifestBootstrap
- type ManifestCI
- type ManifestChat
- type ManifestChatDefault
- type ManifestCommand
- type ManifestCommandUpdate
- type ManifestConfig
- type ManifestConfigSource
- type ManifestDirectSource
- type ManifestExternalAttach
- type ManifestExternalCommand
- type ManifestFeature
- type ManifestFlag
- type ManifestGenerator
- type ManifestHelp
- type ManifestMCP
- type ManifestMCPHints
- type ManifestProperties
- type ManifestReleaseSource
- type ManifestSigning
- type ManifestStaticSource
- type ManifestTelemetry
- type ManifestVersion
- type MultilineString
- type OverwriteMode
- type PipelineOptions
- type PipelineResult
- type RuleState
- type SettingKind
- type SkeletonConfig
- type StepWarning
- type TemplateContractData
- type TemplateDescriptor
- type TemplateSource
- type TemplateSourceType
Constants ¶
const ( // ReleaseChannelForge releases from the project's forge backend. ReleaseChannelForge = "forge" // ReleaseChannelStatic releases from a static location: a pointer and // per-tag manifests under one base URL, no forge involved. ReleaseChannelStatic = "static" )
Release channels a self-updating tool can be generated with (spec 0195 D7, spec 0203 D1).
const ( DefaultFileMode = 0o644 DefaultDirMode = 0o755 )
const ( // DefaultCICDComponentSource is the default include base for the // phpboyscout/cicd components. It is overridable via the manifest // `ci.component_source` value (O1) so a mirrored/self-hosted downstream // can repoint the include base; the default works out-of-the-box. DefaultCICDComponentSource = "gitlab.com/phpboyscout/cicd" // CICDComponentVersion is the phpboyscout/cicd component version the // scaffold pins (go-lint, go-test, go-security, goreleaser, // zensical-pages, colophon). Mirrors the framework's own pin; kept // current automatically by the Renovate customManager in renovate.json5 // (do not hand-bump — let Renovate propose it). CICDComponentVersion = "v0.57.0" // ColophonVersion is the colophon release the scaffolded GitHub release // workflow installs. Kept current by the Renovate customManager in // renovate.json5. ColophonVersion = "v0.19.1" )
CICD component pins for the scaffolded GitLab pipeline.
These are kept in LOCKSTEP with the framework's own root .gitlab-ci.yml (resolved O2 of the 2026-06-15 generator-gitlab-ci-refresh spec): when the framework upgrades its phpboyscout/cicd component pins, bump these in the same change. The scaffolded renovate config (extending the cicd preset) then keeps the pins current for the downstream tool.
Releases go through colophon (#110): on GitLab it is the phpboyscout/cicd colophon component and rides this pin; a GitHub workflow cannot include a component, so it installs colophon itself at ColophonVersion.
const ( // DocsLayoutDiataxis is the Diátaxis-structured docs tree (how-to / // reference / explanation / tutorials). The default for new projects. DocsLayoutDiataxis = "diataxis" // DocsLayoutFlat is the legacy flat tree (docs/commands + docs/packages). DocsLayoutFlat = "flat" )
Documentation tree layouts recorded in ManifestProperties.DocsLayout.
const ConfigKeyGeneratorAI = "generator.ai"
ConfigKeyGeneratorAI is the person's own default for AI generation in projects whose manifest does not say. gtb init writes it (spec 0211 D7).
const ExitCodeNotVerified = 3
ExitCodeNotVerified is the exit code of ErrProjectNotVerified. 2 is errorhandling.ExitCodeUsage, which the same commands already return for a bare or mistyped invocation.
const FrameworkReplaceEnv = "GTB_FRAMEWORK_REPLACE"
FrameworkReplaceEnv names a go-tool-base working tree the generated go.mod should replace the module with. Development only: the e2e suite sets it so a scaffold that references framework API newer than the latest release still tidies, and a developer sets it to test templates against a branch (the generator manual-test skill did this by hand). Never set it for a project you intend to publish; the directive says so in the file.
const KeychainFeature = string(keychain.KeychainFeature)
KeychainFeature is the keychain link's manifest name. It is a catalogue feature of kind link: selected by its file rather than a SetFeatures toggle.
const NonInteractiveEnv = "GTB_NON_INTERACTIVE"
NonInteractiveEnv set to "true" makes the generator take every default rather than prompt.
const SkipLintEnv = "GTB_SKIP_LINT"
SkipLintEnv opts a generation out of the golangci-lint pass.
The pass is best-effort already — a failure is logged and generation continues — so skipping it changes no outcome a caller can observe, only how long a generation takes. That matters because it is by far the most expensive step: a full lint of a real Go project, and golangci-lint keys its findings cache on ABSOLUTE paths, so a project generated into a fresh directory is a cold cache however the cache directory is shared.
It exists for the e2e suite, which scaffolds or regenerates a project in 38 of its scenarios and is asserting what the generator WRITES, not that golangci-lint approves of it. Lint-cleanliness of generated output is a property worth holding, but it is one scenario's job rather than every scenario's tax.
Deliberately an environment variable and not a flag: the seven code paths that lint are reached from as many commands, and a switch for a test concern does not belong in a user's --help. It follows GTB_NON_INTERACTIVE's convention — the literal string "true", nothing else.
Variables ¶
var ( // ErrNotGoToolBaseProject is the placeholder-free sentinel for "no // .gtb/manifest.yaml here". Call sites attach the offending path via // errors.Wrapf so errors.Is keeps matching. ErrNotGoToolBaseProject = errors.NewSentinel("gtb.generator.not_go_tool_base_project", "the current project is not a gtb project (.gtb/manifest.yaml not found)") ErrParentPathNotFound = errors.NewSentinel("gtb.generator.parent_path_not_found", "parent path not found in manifest") ErrModuleNotFound = errors.NewSentinel("gtb.generator.module_not_found", "could not find module name in go.mod") ErrFuncNotFound = errors.NewSentinel("gtb.generator.func_not_found", "target function not found") ErrParentCommandFileNotFound = errors.NewSentinel("gtb.generator.parent_command_file_not_found", "parent command file not found") )
var ( ErrInvalidForgeBackend = errors.NewSentinel("gtb.generator.invalid_forge_backend", "unknown forge backend") ErrInvalidModulePath = errors.NewSentinel("gtb.generator.invalid_module_path", "invalid Go module path") ErrInvalidReleaseChannel = errors.NewSentinel("gtb.generator.invalid_release_channel", "unknown release channel") // ErrBackendContradictsType reports a release_source.backend naming one // forge while release_source.type names another. ErrBackendContradictsType = errors.NewSentinel("gtb.generator.backend_contradicts_type", "release_source.backend contradicts release_source.type") ErrAmbiguousForgeBackend = errors.NewSentinel("gtb.generator.ambiguous_forge_backend", "the manifest enables more than one forge and its release source names none of them; set release_source.backend") )
Sentinels for the backend, channel and module-path fields.
var BreakingChanges = map[string]string{
"v2.10.0": "Breaking changes to Assets interface and command signatures. Please refer to the migration guide.",
}
BreakingChanges is a map of version strings to descriptions of breaking changes introduced in that version. The keys should be valid semantic version strings (e.g., "v2.10.0"). The values are messages displayed to the user when they upgrade across these versions.
var DefaultSelectedFeatures = defaultSelectedFromCatalogue()
DefaultSelectedFeatures is what `gtb generate project` selects when --features is omitted: every catalogue feature that is default-enabled in the framework, plus every link kind. A link (keychain) declares no runtime default because its presence is its enablement, but a new project gets it unless the author opts out, so selecting it by default is the generator's policy rather than the framework's. Derived rather than written out so a change to a framework default cannot leave the generator's default set stale.
Forge features are Default:false, so they are correctly absent: a scaffolded tool opts into a forge explicitly.
var ErrAIFlagsConflict = errors.NewSentinel("gtb.generator.ai_flags_conflict", "--no-ai cannot be combined with --ai, --prompt, --script or --update-docs")
ErrAIFlagsConflict is returned when a run both asks for a model and refuses one.
var ErrChatDefaultNotLinked = errors.NewSentinel("gtb.generator.chat_default_not_linked", "the default chat provider is not one the tool links")
ErrChatDefaultNotLinked is a default provider outside the linked set.
var ErrChatDefaultRequired = errors.NewSentinel("gtb.generator.chat_default_required", "several chat providers are linked; name the default")
ErrChatDefaultRequired is several providers linked and no default named: the generator does not guess which the author wants (spec 0196 D1).
var ErrChatEndpointRequired = errors.NewSentinel("gtb.generator.chat_endpoint_required", "the default chat provider needs an endpoint")
ErrChatEndpointRequired is a default provider whose module refuses to construct without addressing the author has not given (spec 0196 D2).
var ErrChatProvidersRequired = errors.NewSentinel("gtb.generator.chat_providers_required", "the ai feature needs at least one chat provider")
ErrChatProvidersRequired is the ai feature selected with no provider to serve it (spec 0194 OQ5).
var ErrCommandProtected = errors.NewSentinel("gtb.generator.command_protected", "command is protected")
var ErrInvalidInput = errors.NewSentinel("gtb.generator.invalid_input", "invalid generator input")
ErrInvalidInput is the sentinel wrapped by every Validate* failure. Discriminate with errors.Is in callers that need to distinguish validation failures from other error shapes.
var ErrInvalidOverwriteMode = errors.NewSentinel("gtb.generator.invalid_overwrite_mode",
"invalid overwrite mode: must be allow, deny, or ask")
ErrInvalidOverwriteMode is returned for an --overwrite value outside the three modes.
var ErrInvalidPackageName = errors.NewSentinel("gtb.generator.invalid_package_name", "invalid package name")
var ErrLintLocked = errors.NewSentinel("gtb.generator.lint_locked", "another golangci-lint is running; rerun when it has finished")
ErrLintLocked reports golangci-lint refusing to start because another instance holds its lock (two gtb runs at once, or a lint in another shell). It is not a finding about the project.
var ErrNoCommandsDirectory = errors.NewSentinel("gtb.generator.no_commands_directory", "pkg/cmd directory not found")
ErrNoCommandsDirectory is a manifest rebuild pointed at a directory with no pkg/cmd to scan.
var ErrNoFrontmatter = errors.NewSentinel("gtb.generator.no_frontmatter", "AI documentation response contained no frontmatter")
ErrNoFrontmatter signals that an AI documentation response contained no YAML frontmatter fence at all, even after stripping any conversational preamble. The generator treats this as a generation failure and falls back to deterministic boilerplate rather than committing a frontmatter-less page.
var ErrProjectNotVerified = errors.NewSentinel("gtb.generator.project_not_verified", "files were written but a verification step failed")
ErrProjectNotVerified is a generate or regenerate whose files were written but whose verification (go mod tidy, golangci-lint) failed. It carries ExitCodeNotVerified so a script can tell it from success (0) and from a usage error (2), and its message names the step (spec 0197 D10).
var ErrSettingReadOnly = errors.NewSentinel("gtb.generator.setting_read_only", "not set this way")
ErrSettingReadOnly is a manifest field the generator records, or one that has its own command, which set does not write.
var ErrUnknownChatProvider = errors.NewSentinel("gtb.generator.unknown_chat_provider", "chat provider is not one a known module registers")
ErrUnknownChatProvider names a chat provider no known module registers.
var ErrUnknownSetting = errors.NewSentinel("gtb.generator.unknown_setting", "not an author setting")
ErrUnknownSetting is a path the author-settings table does not name.
var SelectableFeatures = nonForgeFeatures()
SelectableFeatures is the set `gtb generate project --features` accepts: every toggleable feature that is not a forge, plus keychain. Keychain is a real choice at generation time that `gtb enable`/`gtb disable` cannot flip afterwards. A forge is not chosen here: the backend implies one and --forge-credentials adds the rest (spec 0195 D1, D6), though a forge stays toggleable once the project exists.
var ToggleableFeatures = featureNamesFromCatalogue()
ToggleableFeatures is the set of features that `gtb enable <feature>` and `gtb disable <feature>` can flip in a generated project's manifest: every catalogue feature, the keychain link included, which the shared sync writes or removes as cmd/<name>/keychain.go from the manifest's entry (spec 0197 D8, spec 0199 OQ3).
Functions ¶
func AppendIgnorePattern ¶
AppendIgnorePattern appends a single pattern to a project's .gtb/ignore, creating the file (with an explanatory header) when it is absent. It is idempotent: re-adding a pattern already present as a rule line leaves the file byte-identical and reports changed=false. Existing comments, blank lines, and ordering are preserved — the pattern is appended after them.
func ApplyAuthorSettingsTo ¶ added in v0.43.0
func ApplyAuthorSettingsTo(m *Manifest, cfg SkeletonConfig)
ApplyAuthorSettingsTo overlays cfg's author settings onto m without writing anything, for a dry run's diff.
func CheckAIFlags ¶ added in v0.49.0
CheckAIFlags refuses a run that both asks for a model and refuses one.
func ConfigFormats ¶ added in v0.46.0
func ConfigFormats() []string
ConfigFormats returns the formats a tool may link beyond YAML.
func ConfigSourceKinds ¶ added in v0.46.0
func ConfigSourceKinds() []string
ConfigSourceKinds returns every kind a slot may declare: the shipped kinds, then the override-only ones.
func DefaultChatProviders ¶ added in v0.42.0
func DefaultChatProviders() []string
DefaultChatProviders is what a new project ships when the operator does not narrow the list: every known provider, because a generated tool is configured by its consumers the way gtb itself is (spec 0194 OQ4).
func DeriveEnvPrefix ¶ added in v0.46.0
DeriveEnvPrefix is the environment prefix a project name gives: upper-case, hyphens as underscores (my-tool becomes MY_TOOL).
func DiffAuthorSettings ¶ added in v0.43.0
DiffAuthorSettings lists the author settings that differ between two manifests, one line each as `path: old -> new`, sorted by path. It is what `gtb wizard --dry-run` prints.
func EncodeManifestFile ¶
EncodeManifestFile serialises m to the given manifest.yaml path on fs at the canonical two-space indent and writes it with DefaultFileMode. It is the single serialise-and-write helper for every manifest write site (scaffold, generate command, regenerate, add-flag, scan), so the render+write boilerplate — and the on-disk byte layout — cannot drift between sites.
func FeatureEnabledIn ¶ added in v0.43.0
func FeatureEnabledIn(features []ManifestFeature, name string) bool
FeatureEnabledIn resolves a feature's effective enabled state from a manifest feature list: an explicit entry wins, else the framework default.
func ForgeBackends ¶ added in v0.43.0
ForgeBackends lists every forge a project can be hosted on, in catalogue order: the forge features the framework registers profiles for. The wizard's chooser, --forge-backend and the manifest validator all read it.
func IsOverrideOnlySourceKind ¶ added in v0.46.0
IsOverrideOnlySourceKind reports whether only an author's override builds kind.
func IsWritableConfigFormat ¶ added in v0.46.0
IsWritableConfigFormat reports whether a tool's own file may be in format.
func KnownChatProviders ¶ added in v0.42.0
func KnownChatProviders() []string
KnownChatProviders is every provider a known module registers, in the framework's table order. The generator emits an import; whether the running tool can be configured for the provider through GTB's wizard is that tool's concern, not a precondition for linking it (spec 0194 D5, revised).
func ManifestPathFor ¶
ManifestPathFor returns the canonical .gtb/manifest.yaml path under the given project root.
func ManifestWarnings ¶ added in v0.43.0
ValidateManifest runs every user-influenced field of a loaded Manifest through the validators above. Used by regenerate and manifest-update paths so a tampered manifest fails fast before driving file writes.
Only [Manifest.Properties.Name] is unconditionally required — a manifest missing the tool name is structurally broken. Other fields are optional in the YAML schema and are validated only when populated; empty fields short-circuit to nil, matching the forgiving behaviour of the fine-grained validators above. ManifestWarnings are the things a manifest is allowed to say that an author should still hear about: none of them stops a generate. Today that is a telemetry endpoint over plain HTTP, which is a legitimate setup for a collector on a private network and a mistake for one on the internet.
func PascalCase ¶
func PreviewAppendIgnorePatterns ¶
func PreviewAppendIgnorePatterns(fs afero.Fs, projectPath string, patterns []string) (content string, err error)
PreviewAppendIgnorePatterns returns what .gtb/ignore would contain after appending patterns in sequence, composing them in memory without writing anything. It backs `--dry-run` on `gtb ignore add` with multiple patterns.
func PreviewRemoveIgnorePattern ¶
func PreviewRemoveIgnorePattern(fs afero.Fs, projectPath, pattern string) (content string, changed bool, err error)
PreviewRemoveIgnorePattern returns what .gtb/ignore would contain after removing pattern, without writing anything. It backs `--dry-run` on `gtb ignore remove`.
func RemoveIgnorePattern ¶
RemoveIgnorePattern drops the exact literal rule line matching pattern from a project's .gtb/ignore, preserving every other line (comments, blanks, and ordering). Matching is on the literal rule line, not on a path the pattern happens to glob, so `remove justfile` never touches an overlapping `*.yml`. It reports changed=false when no such line exists.
func ScaffoldIgnoreFile ¶
ScaffoldIgnoreFile writes a fresh, comment-only .gtb/ignore (the header and nothing else) into a project, creating the .gtb directory as needed. It is a no-op when the file already exists, so it never clobbers a user's rules. A comment-only file ignores nothing, so scaffolding it is behaviourally inert — its whole value is discoverability.
func SealIgnorePattern ¶
SealIgnorePattern appends a sealed rule for pattern — "<pattern> sealed" — which forbids every generator write to the path, wiring included (spec 0188 D3). Sealing implies ignoring, so one line is enough. Idempotent and comment-preserving, like AppendIgnorePattern.
func UnsealIgnorePattern ¶
UnsealIgnorePattern rewrites a sealed rule back to a bare one, so the path stays *ignored* rather than becoming fully managed again. Dropping the line outright would silently hand the file back to the generator, which is unlikely to be what someone unsealing wants (D8).
It reports changed=false when no sealed rule for pattern exists.
func ValidateCIComponentSource ¶
ValidateCIComponentSource accepts an empty string (meaning "use the framework default") and otherwise requires a bare host/path component source such as `gitlab.com/phpboyscout/cicd`. The value is interpolated verbatim into an unquoted YAML include path in the scaffolded .gitlab-ci.yml, so it is restricted to a strict character class (alphanumerics, `.`, `-`, `_`, `/`) and rejected if it carries a URL scheme, whitespace, control characters, or template delimiters.
func ValidateChatDefault ¶ added in v0.43.0
func ValidateChatDefault(d ManifestChatDefault, providers []string, features []ManifestFeature) error
ValidateChatDefault applies spec 0196's rules for the author's default: with ai enabled and several providers linked a default is required; it must be a linked provider; and the providers that refuse to construct without an endpoint must be given one (openai-compatible and azure-openai a base URL, azure-openai an API version, as their modules document). Project and location stay optional because gemini-vertex and bedrock fall back to the environment for them.
func ValidateChatDefaultProvider ¶ added in v0.43.0
func ValidateChatDefaultProvider(provider string, providers []string, features []ManifestFeature) error
ValidateChatDefaultProvider is the provider half of ValidateChatDefault, for the wizard's select: required between several providers, and one of them. The endpoint half is a later page's.
func ValidateChatProviders ¶ added in v0.42.0
func ValidateChatProviders(providers []string, features []ManifestFeature) error
ValidateChatProviders rejects names no module registers, and an empty list when the ai feature is enabled.
func ValidateCommandName ¶
ValidateCommandName enforces the naming rule for generated commands — kebab-case plus underscore, a lowercase letter first, at most 64 characters: ^[a-z][a-z0-9_-]{0,63}$. Path separators and dots are rejected explicitly (before the character-class check) because the name flows into filepath.Join(path, "pkg", "cmd", name) and FS.RemoveAll — this rule is the gate that forecloses path traversal from CLI flags and tampered manifests alike.
func ValidateConfigFormats ¶ added in v0.46.0
ValidateConfigFormats refuses an unknown format, an own format that cannot be written, and an own format the tool does not link (spec 0204 D2).
func ValidateConfigKeyCase ¶ added in v0.48.0
ValidateConfigKeyCase accepts an empty string (folded, the default) and the two key modes props.ConfigSpec understands.
func ValidateConfigLayers ¶
ValidateConfigLayers refuses what the tool would refuse at startup: an unknown or repeated layer, or an order breaking spec 0204 D1.
An unknown name would otherwise reach the emitter and render a props constant that does not exist, failing the generated project's build with an error pointing at generated source rather than at the manifest that caused it.
func ValidateConfigSources ¶ added in v0.46.0
func ValidateConfigSources(sources []ManifestConfigSource, layers []string, features []ManifestFeature) error
ValidateConfigSources refuses an unknown kind, a slot D15 refuses, and a keychain source beside an explicitly disabled keychain feature (D12).
func ValidateConfigStack ¶ added in v0.46.0
func ValidateConfigStack(layers []string, sources []ManifestConfigSource, features []ManifestFeature) error
ValidateConfigStack checks a declared layer list and its source slots together, since a source's layer is named for its slot and the builtin-only layer check would refuse it.
func ValidateDescription ¶
ValidateDescription enforces a bounded-length, control-character-free description that is safe to interpolate into YAML/TOML string values and Markdown prose. The rule explicitly forbids `{{` / `}}` as a belt-and-braces guard: text/template does not re-parse interpolated data, so this is not exploitable today, but matching the pattern lets a future change (e.g. switching to html/template with its `{{`-as-action reparsing) remain safe.
func ValidateEnvPrefix ¶
ValidateEnvPrefix accepts an empty string (meaning "no prefix") and otherwise requires an upper-snake-case prefix matching `^[A-Z][A-Z0-9_]{0,31}$`. Shell metacharacters are excluded by the class; length is bounded so the rendered env-var name stays below POSIX limits.
func ValidateExternalCommand ¶
func ValidateExternalCommand(ec *ManifestExternalCommand) error
ValidateExternalCommand validates a single manifest external_commands entry: the module path, the required version pin, the optional import path and alias, and every attach descriptor. It is the gate that forecloses a tampered manifest driving a go.mod require or a rendered constructor call outside the rules.
func ValidateFeatureName ¶
ValidateFeatureName rejects any name that is not one of the toggleable built-in features (see ToggleableFeatures). Used by `gtb enable <feature>` / `gtb disable <feature>` so an unknown name fails fast with the valid set listed, rather than silently writing a junk manifest entry.
func ValidateFlagDefaultCode ¶
ValidateFlagDefaultCode gates a flag default that renders as Go source (`default_is_code: true` emits the value verbatim via jen.Id). Only a bare identifier or a dot-joined selector (`defaultTimeout`, `time.Second`) is accepted — the shapes jen.Id is legitimately used for. Calls, operators, literals, and anything statement-like are rejected so a tampered manifest cannot compile arbitrary Go into a regenerated tool. Empty means "zero value" and is accepted.
func ValidateFlagName ¶
ValidateFlagName enforces the naming rule for a generated command's flag: kebab-case, a lowercase letter first, at most 64 characters (^[a-z][a-z0-9-]{0,63}$). The name becomes a Go identifier in the generated options struct and a cobra flag registration, so the same constraint as command names applies.
func ValidateFlagShorthand ¶
ValidateFlagShorthand accepts an empty string (meaning "no shorthand") and otherwise requires exactly one ASCII letter, which becomes cobra's single-rune shorthand (the `-x` form). Anything longer or non-letter is rejected before it reaches the StringVarP registration in the generated code.
func ValidateFlagType ¶
ValidateFlagType rejects a flag type the command generator can't render. The empty string and "string" are accepted (both map to a string flag); every other value must be one the generator's flagFuncMap knows. Without this gate an unknown type silently degrades to a string flag in the generated code.
func ValidateForgeBackend ¶ added in v0.43.0
ValidateForgeBackend accepts a registered forge or empty (not hosted).
func ValidateHelpType ¶ added in v0.43.0
ValidateHelpType restricts the help channel to the known enum: "slack", "teams", "none", or empty.
func ValidateHost ¶
ValidateHost enforces an RFC 1123 hostname (optionally with `:port`). Punycode labels (`xn--...`) are accepted; raw Unicode labels are rejected — callers that need an internationalised host must supply the punycode form explicitly so homoglyph attacks fail visibly at input time rather than in a rendered URL.
func ValidateLongDescription ¶
ValidateLongDescription enforces a bounded-length, control-character-free long description. Unlike ValidateDescription, newlines are permitted — multi-line long descriptions are legitimate — and `|` need not be banned because the Markdown table sink escapes it (escapeMarkdownTableCell).
func ValidateMCPMode ¶ added in v0.43.0
ValidateMCPMode accepts an empty string (compact, the default) and the two publication modes props.Tool.MCP understands.
func ValidateMCPTitle ¶ added in v0.43.0
ValidateMCPTitle applies the description rule to an MCP display title: it reaches an MCP client verbatim and is interpolated into generated Go, so the same bound, control-character and template-lookalike rules hold.
func ValidateManifest ¶
func ValidateModulePath ¶ added in v0.43.0
ValidateModulePath accepts a syntactically valid Go module path, or empty (derived for a hosted project).
func ValidateName ¶
ValidateName enforces the naming rule for the scaffolded tool — lowercase alphanumeric with optional hyphens, a letter first, and at most 64 characters. This tight rule simultaneously forecloses path traversal, Unicode spoofing, and YAML/TOML/Markdown/shell injection because none of the dangerous characters are in the class.
func ValidateOrg ¶
ValidateOrg enforces the owner syntax of the forge the project is hosted on: GitLab allows `/`-separated nested namespaces (the one profile that declares NestedNamespaces), the others a single owner segment. CODEOWNERS silently drops invalid `@`-mentions, so catching bad input early prevents the scaffolded project from shipping broken ownership rules.
func ValidatePackagePath ¶
ValidatePackagePath validates the `--package` argument to `generate docs`: it must be a clean, `/`-separated path relative to the project root, with no absolute prefix and no `..` traversal. This forecloses writing the generated doc outside docs/ (the value flows verbatim into filepath.Join for both the source dir and the output path). Segments share the public-key character class.
func ValidateParentPath ¶
ValidateParentPath validates a `/`-separated parent command path as supplied via --parent. The literal "root" (and the empty string) mean the root command and are accepted as-is; every other segment must be a valid command name.
func ValidateReleaseBaseURL ¶ added in v0.45.0
ValidateReleaseBaseURL accepts the static channel's base URL the way a chat provider endpoint is accepted (spec 0203 D1): absolute https, no userinfo, no placeholder host. Plain http is refused, not warned; a tool downloads executables from here.
func ValidateReleaseChannel ¶ added in v0.43.0
ValidateReleaseChannel accepts forge, static or empty.
func ValidateReleaseSourceType ¶
ValidateReleaseSourceType restricts the manifest release-source provider to the enum the skeleton assets support: "github", "gitlab", or empty for the host-derived default. "gitea" and "bitbucket" are reserved for the forge adapters and rejected until skeleton asset sets exist.
func ValidateRepo ¶
ValidateRepo enforces Go module path rules: a domain-style first component followed by one or more `[a-zA-Z0-9._~-]+` path segments, no leading/trailing `/`, and no `..` segments. `go mod tidy` would also reject invalid paths, but failing early surfaces a useful error at generation time rather than at first build.
func ValidateRepoName ¶
ValidateRepoName enforces the shape of the manifest's bare repository name (release_source.repo — a single path segment, unlike the CLI --repo module path handled by ValidateRepo). The value is joined into `{{ .Repo }}` / `{{ .ModulePath }}` and rendered raw into CI-executed files (.gitlab-ci.yml, .goreleaser.yaml), so quotes, whitespace, brackets, and separators are all outside the class.
func ValidateSelectableFeatureName ¶
ValidateSelectableFeatureName rejects any name that `gtb generate project --features` cannot act on: the toggleable features, forges excluded (a forge is chosen with --forge-backend).
Without it an unknown name was written verbatim into the manifest and then silently dropped at emission, so the generated tool quietly lacked the feature the operator asked for.
func ValidateSigningBackend ¶
ValidateSigningBackend accepts an empty string (meaning "default") and otherwise enforces the registered-backend-name character class.
func ValidateSigningExternalKeyEmail ¶
ValidateSigningExternalKeyEmail accepts an empty string and otherwise enforces an email-shaped character class: single `@`, no whitespace, no control characters. The value is written raw into the `// gtb:signing` annotation of the generated provenance file, so a newline could otherwise break out of the comment line.
func ValidateSigningKMSRegion ¶
ValidateSigningKMSRegion accepts an empty string and otherwise enforces the AWS region character class.
func ValidateSigningKeyID ¶
ValidateSigningKeyID accepts an empty string and otherwise enforces the KMS key-id / ARN / alias character class (which also admits the local backend's PEM paths). The value is rendered into the generated .goreleaser.yaml, so quotes, whitespace, and control characters are all outside the class. A literal `..` substring is rejected as defence-in-depth: legitimate KMS ids, ARNs, aliases, and the local backend's relative PEM paths never contain it, mirroring how ValidateCommandName forecloses traversal.
func ValidateSigningKeySource ¶
ValidateSigningKeySource restricts the trust-anchor source to the known enum: "embedded", "external", "both", or empty for the framework default.
func ValidateSigningPublicKey ¶
ValidateSigningPublicKey accepts an empty string and otherwise requires a clean, slash-separated path relative to the project root (the manifest documents the field as the path to the armored public-key file, e.g. internal/trustkeys/keys/signing-key-v1.asc). A single leading `./` is normalised away as a friendliness affordance, so `./key.asc` is treated as `key.asc`. Absolute paths, `..` escapes, backslashes, and any other unclean forms are still rejected — the path must resolve inside the project root.
func ValidateSlackChannel ¶
ValidateSlackChannel accepts an empty string and otherwise enforces Slack's own channel-name rules — lowercase, alphanumeric, hyphens, 1–80 characters.
func ValidateSlackTeam ¶
ValidateSlackTeam accepts an empty string and otherwise enforces Slack's workspace-name rules.
func ValidateTeamsChannel ¶
ValidateTeamsChannel accepts an empty string and otherwise enforces generic "safe for YAML and Markdown" rules: bounded length, no control characters, no template-brace sequences.
func ValidateTeamsTeam ¶
ValidateTeamsTeam mirrors ValidateTeamsChannel.
func ValidateTelemetryEndpoint ¶
ValidateTelemetryEndpoint accepts an empty string (meaning "no endpoint") and otherwise requires a syntactically valid HTTP or HTTPS URL, bounded in length and free of control characters.
func ValidateTemplateSource ¶
func ValidateTemplateSource(ts *TemplateSource) error
ValidateTemplateSource validates a single manifest templates entry: the type discriminator, the location character class (no traversal), the ref/SHA shape, and the optional name. It is the gate that forecloses a tampered manifest driving a fetch or write outside the rules.
func ValidateUpdateCheckInterval ¶
ValidateUpdateCheckInterval accepts an empty string (meaning "use the framework default of 24h") and otherwise requires a valid, non-negative Go duration (as understood by time.ParseDuration, e.g. "24h", "168h", "30m"). The value is rendered into the generated tool's props.Tool.UpdateCheckInterval as a time.Duration expression, so a malformed or negative value is rejected rather than silently falling back. A length cap bounds the parse input.
func ValidateUpdatePolicy ¶
ValidateUpdatePolicy accepts an empty string (meaning "use the framework default of disabled") and otherwise requires one of the three known posture values: disabled, prompt, or enabled. The value selects a typed props.UpdatePolicy constant rendered into the generated tool, so an unknown value is rejected rather than silently treated as disabled.
Types ¶
type AuthorSetting ¶ added in v0.43.0
type AuthorSetting struct {
// Field is the SkeletonConfig field, dotted for a nested one.
Field string
Kind SettingKind
// Flag is the generate project flag; empty for a run option or a field
// the wizard and gtb set alone write. Generator.AI is the one setting
// without a flag, because --ai on generate is the run's own ask (spec
// 0211 D5, an exception to spec 0197 D4).
Flag string
// Wizard is the page key that asks it; empty when the wizard does not.
Wizard string
// Manifest is the dotted path from the manifest root, and the vocabulary
// gtb set understands.
Manifest string
}
AuthorSetting classifies one SkeletonConfig field: how an author reaches it and where the manifest keeps it. Spec 0197 D1: the manifest owns every author setting, and TestEverySkeletonConfigFieldIsClassified holds this table and SkeletonConfig to each other.
func AuthorSettingByPath ¶ added in v0.43.0
func AuthorSettingByPath(path string) (AuthorSetting, bool)
AuthorSettingByPath finds the table row for a manifest path.
func AuthorSettings ¶ added in v0.43.0
func AuthorSettings() []AuthorSetting
AuthorSettings returns the classification of every SkeletonConfig field.
type CommandContext ¶
type CommandContext struct {
// Identity
Name string
ParentPath []string // empty = direct child of root
// Display
Short string
Long string
// Routing / feature options
Aliases []string
Args string
WithAssets bool
WithInitializer bool
WithConfigValidation bool
PersistentPreRun bool
PreRun bool
Protected *bool
MCPEnabled *bool // tri-state MCP exposure; mirrors Protected
MCPHints setup.MCPHints
Hidden bool
// Project-level settings (carried from the originating generator).
//
// Every one of these must be threaded through ToConfig as well as named
// here. Overwrite was declared on Config but missing from this type, so
// swapping g.config for a command context silently reset --overwrite to
// its "ask" default for every command file in the run (issue #13). Taking
// the whole *Config in buildCommandContext keeps the omission visible in
// one place rather than spread across four call sites.
ProjectPath string
DryRun bool
Force bool
UpdateDocs bool
AI bool
NoAI bool
AIProvider string
AIModel string
Overwrite OverwriteMode
}
CommandContext holds the fully resolved configuration for a single command generation or regeneration pass. It is a value type so recursive invocations cannot accidentally share or mutate each other's state.
func (CommandContext) ToConfig ¶
func (c CommandContext) ToConfig() *Config
ToConfig converts the CommandContext into a *Config suitable for constructing a Generator scoped to this specific command.
type CommandFlag ¶
type CommandPipeline ¶
type CommandPipeline struct {
// contains filtered or unexported fields
}
CommandPipeline owns the ordered post-generation steps that are shared by both generate command and regenerate project. Constructing a pipeline and calling Run centralises all registration, hash, manifest, and documentation logic so a fix in one place applies to both entrypoints.
func (*CommandPipeline) Run ¶
func (p *CommandPipeline) Run(ctx context.Context, data templates.CommandData, cmdDir string) (PipelineResult, error)
Run executes the pipeline steps in order for the given command data and directory. Fatal steps (asset generation) return an error immediately. Advisory steps (registration, manifest) log a warning and accumulate into PipelineResult so callers can inspect partial failures.
type Config ¶
type Config struct {
// NoVerify skips the post-processing steps (go mod tidy, golangci-lint)
// after generate project and regenerate project, for a caller who knows
// the tree cannot be verified here (offline, pre-tag). The run then exits
// 0 having emitted files it did not verify (spec 0197 D10).
NoVerify bool
// AI and NoAI are the run's own answer on calling a model (spec 0211 D4);
// without either, the manifest and then the person's config decide.
AI bool
NoAI bool
AIModel string
AIProvider string
// MaxSteps bounds the autonomous repair agent's ReAct loop. Zero uses the
// framework default (gochat.DefaultMaxSteps). Raise it for complex commands
// the agent cannot finish within the default budget.
MaxSteps int
// NonInteractive withholds the repair agent's query_user tool so a
// generation never blocks for input (CI / --non-interactive).
NonInteractive bool
Aliases []string
Args string
DryRun bool
// GitInit controls the post-generation git step on `generate project`
// (init + stage + initial commit). It is opt-out: the CLI sets it true by
// default and false under --no-git. Only honoured by GenerateSkeleton.
GitInit bool
// GitPush, when true (--push), adds the derived remote as origin and pushes
// the initial commit. Opt-in; requires GitInit. Push failures are non-fatal.
GitPush bool
// GitBranch is the default branch the initial commit lands on (default
// "main", overridable via --git-branch).
GitBranch string
Flags []string
Force bool
Hidden bool
Overwrite OverwriteMode // default ask
Long string
Name string
Parent string
Path string
PersistentPreRun bool
PreRun bool
Prompt string
Protected *bool
MCPEnabled *bool // tri-state MCP exposure; mirrors Protected
MCPHints setup.MCPHints
ScriptPath string
Short string
UpdateDocs bool
// PublicAPI marks the module as publicly published (the --public-api flag),
// so generated package docs defer their API reference to pkg.go.dev. Default
// false: the reference is stubbed to a local `go doc` hint (no dead links for
// a private/unpublished module). The manifest module_published property is the
// persistent equivalent.
PublicAPI bool
// NoAIAttribution, when true (the --no-ai-attribution flag on `generate
// docs`), flips the documentation system prompt so the frontmatter `authors:`
// field carries the project's human author(s) only — the model is instructed
// to add no AI/model/assistant identity. Default false: AI attribution is
// additive (existing human authors preserved, AI model appended as a
// co-author). See docs.go authorsDirectives and issue #7.
NoAIAttribution bool
WithAssets bool
WithConfigValidation bool
WithInitializer bool
}
type DryRunResult ¶
type DryRunResult struct {
Created []FilePreview `json:"created,omitempty"`
Modified []FilePreview `json:"modified,omitempty"`
// Actions describes non-file side effects the run would perform (e.g. the
// post-generation git init/commit/push), surfaced as plain text lines.
Actions []string `json:"actions,omitempty"`
}
DryRunResult contains the preview of planned file operations.
func (*DryRunResult) Print ¶
func (r *DryRunResult) Print(w io.Writer)
Print writes a human-readable preview of the dry-run result to w.
type ExternalCommandSpec ¶
type ExternalCommandSpec = ManifestExternalCommand
ExternalCommandSpec is the input to AttachExternalCommand: one declarative external-module attachment to record. It mirrors the stored manifest form.
type FilePreview ¶
type FilePreview struct {
Path string `json:"path"`
Content []byte `json:"content,omitempty"`
Diff string `json:"diff,omitempty"`
}
FilePreview represents a single file operation in a dry run.
type Generator ¶
type Generator struct {
// contains filtered or unexported fields
}
func (*Generator) AddTemplateSource ¶
func (g *Generator) AddTemplateSource(ctx context.Context, ts TemplateSource) error
AddTemplateSource appends a source to the manifest (rejecting a duplicate name) and regenerates so the overlay is applied and the pin/hashes are recorded. The supplied source is validated first.
func (*Generator) ApplyAuthorSettings ¶ added in v0.43.0
func (g *Generator) ApplyAuthorSettings(ctx context.Context, cfg SkeletonConfig) error
ApplyAuthorSettings writes a wizard's answers into an existing project: the author-settable fields of the manifest are replaced from cfg, everything the wizard does not ask (commands, hashes, the generator's recorded fields) is kept, the result is validated, and the derived files are synced (spec 0197 D13). Nothing is written when validation refuses.
func (*Generator) ApplyFeatures ¶
ApplyFeatures flips the requested built-in features in the manifest's properties.features block and re-renders the generated root command so its props.SetFeatures(...) wiring matches. desired maps a toggleable feature name to its requested enabled state (the same method serves the single-name CLI path and the multi-select wizard).
The manifest is normalised against the framework defaults: an entry is kept only when it differs from the default, so returning a feature to its default state removes the entry and the rendered root drops the now-redundant toggle (and the whole SetFeatures call when nothing differs). Unknown names are a hard error. It returns the feature names that actually changed — an empty result means every requested feature was already in the requested state, and nothing is written.
func (*Generator) AttachExternalAdapter ¶
AttachExternalAdapter scaffolds the author-owned adapter (pkg/cmd/external/ attach.go) if absent — never overwriting an existing one — sets the adapter manifest flag, and re-renders the root to spread external.Commands(p) into NewCmdRoot. The author then fills in Commands to attach any shape the declarative vocabulary cannot express.
func (*Generator) AttachExternalCommand ¶
func (g *Generator) AttachExternalCommand(ctx context.Context, spec ExternalCommandSpec) error
AttachExternalCommand records a declarative external-command attachment in the manifest, re-renders the root so the attach calls are wired in, and (on a real filesystem) pins the module require via `go get module@version` and tidies. The attachment is manifest-driven, so it survives every future regenerate / enable / disable — replacing the cmd/<tool>/main.go + .gtb/ignore workaround.
func (*Generator) CheckIgnorePaths ¶
func (g *Generator) CheckIgnorePaths(paths []string) []IgnoreCheckResult
CheckIgnorePaths resolves each path against the project's .gtb/ignore rules and returns the winning rule for each. It reads the ignore file fresh; it does not touch the manifest, so it works even before the first regenerate.
func (*Generator) CurrentFeatures ¶
func (g *Generator) CurrentFeatures() ([]ManifestFeature, error)
CurrentFeatures returns the project's current manifest feature entries so a caller (e.g. the enable/disable wizard) can present the current state. A project that has never toggled a feature returns an empty slice (every feature is at its framework default).
func (*Generator) CurrentSigning ¶
func (g *Generator) CurrentSigning() (ManifestSigning, error)
CurrentSigning returns the project's current manifest signing block, so a caller (e.g. `gtb enable signing`) can merge flag overrides onto the existing posture rather than replacing it. A project that has never enabled signing returns the zero ManifestSigning.
func (*Generator) DetachExternalCommand ¶
DetachExternalCommand removes the declarative attachment for the given module, re-renders the root (dropping its wiring), and — on a real filesystem — tidies so the now-unused require is pruned from go.mod.
func (*Generator) DisableSigning ¶
DisableSigning sets signing.enabled = false in the manifest and selectively regenerates: it drops the Signing: field from the root command and removes the generated signing.go. It never deletes internal/trustkeys or any author-added *.asc.
func (*Generator) DivergedUnignoredFiles ¶
DivergedUnignoredFiles returns the tracked files whose on-disk content no longer matches the hash recorded in the manifest AND which no ignore rule covers — precisely the set that will raise a conflict prompt on the next regenerate. Files that are ignored, or missing on disk, are excluded. It errors when no manifest exists. Backs the `doctor` diverged-files check.
func (*Generator) EnableRealTemplateClone ¶
EnableRealTemplateClone wires the production provider-aware git clone for custom template sources. Call this on a Generator before resolving git sources in production paths; tests inject a fake via withTemplateClone instead. Returns the Generator for chaining.
func (*Generator) EnableSigning ¶
func (g *Generator) EnableSigning(ctx context.Context, signing ManifestSigning) error
EnableSigning sets the manifest signing posture, then selectively regenerates only the signing-affected files: it writes the trustkeys package, keys/.gitkeep and signing.go, and re-renders the root command (pkg/cmd/root/cmd.go) to add the Signing: field. It does not run a full regenerate — unrelated files are left untouched.
func (*Generator) FeatureEnabled ¶
FeatureEnabled reports whether the named feature is currently enabled in the project, honouring a manifest override and otherwise falling back to the framework default.
func (*Generator) FindCommandParentPath ¶
func (*Generator) GenerateCommandFile ¶
func (*Generator) GenerateDocs ¶
GenerateDocs generates documentation for the command or package.
func (*Generator) GenerateDryRun ¶
func (g *Generator) GenerateDryRun(ctx context.Context) (*DryRunResult, error)
GenerateDryRun previews what Generate would do without writing to disk.
func (*Generator) GenerateSkeleton ¶
func (g *Generator) GenerateSkeleton(ctx context.Context, config SkeletonConfig) error
func (*Generator) GenerateSkeletonDryRun ¶
func (g *Generator) GenerateSkeletonDryRun(ctx context.Context, config SkeletonConfig) (*DryRunResult, error)
GenerateSkeletonDryRun previews what GenerateSkeleton would do without writing to disk.
func (*Generator) GetSetting ¶ added in v0.43.0
GetSetting reads one author setting back as the strings set would take.
func (*Generator) ListExternalCommands ¶
func (g *Generator) ListExternalCommands() ([]ManifestExternalCommand, bool, error)
ListExternalCommands returns the declared external-command attachments and whether the adapter channel is wired.
func (*Generator) ListIgnoreRules ¶
func (g *Generator) ListIgnoreRules() (*IgnoreListing, error)
func (*Generator) ListTemplateSources ¶
func (g *Generator) ListTemplateSources() ([]TemplateSource, error)
ListTemplateSources returns the template sources recorded in the project manifest, in layer order.
func (*Generator) LoadManifest ¶ added in v0.43.0
LoadManifest reads the project's manifest, for a caller that composes the generator's pieces itself (gtb wizard).
func (*Generator) RegenerateCommand ¶
func (g *Generator) RegenerateCommand(ctx context.Context, cmd ManifestCommand, parentPath []string) error
RegenerateCommand regenerates a single command's cmd.go from its full ManifestCommand record and persists the refreshed cmd.go hash back to the manifest. It reuses the exact mapping the `regenerate project` path uses (prepareRegenerationData → performGeneration → postGenerate), so a command's aliases, persistent/required/shorthand flags, and pre-run hooks survive the regeneration rather than being dropped by a hand-built partial CommandData.
Unlike regenerateCommandRecursive it does NOT recurse into subcommands: it is the entrypoint for the targeted `generate add-flag` regeneration, which must only rewrite the command whose flag set changed. Child registrations in the regenerated cmd.go are preserved by the pipeline's reRegisterChildCommands step.
func (*Generator) RegenerateManifest ¶
func (*Generator) RegenerateProject ¶
func (*Generator) RegenerateProjectDryRun ¶
func (g *Generator) RegenerateProjectDryRun(ctx context.Context) (*DryRunResult, error)
RegenerateProjectDryRun previews what RegenerateProject would do without writing to disk.
func (*Generator) RemoveTemplateSource ¶
RemoveTemplateSource drops a named source from the manifest and regenerates. Removing the source restores any embedded scaffold a `replaces:` had suppressed (the embedded files re-enter the walk) and drops the source's tracked overlay output from the manifest. The on-disk overlay files themselves are left in place unless a regenerated embedded file overwrites them — a clean, reversible swap consistent with the rest of regenerate.
func (*Generator) SealedTrackedFiles ¶
SealedTrackedFiles returns the manifest-tracked files a `sealed` rule covers.
They are reported separately from drift: a sealed path is not a file that has wandered, it is one the generator has been told never to write. What makes it worth surfacing is that sealing also blocks the wiring, so a sealed parent silently loses any subcommand added since — the command compiles away to nothing rather than failing loudly (spec 0188 D7).
func (*Generator) SetMCPEnabled ¶
SetMCPEnabled records a command's MCP-exposure decision in the manifest and re-renders that single command's cmd.go so the generated setup.ExcludeFromMCP / setup.IncludeInMCP marker matches. It is the engine behind `gtb enable mcp` (enabled=true) and `gtb disable mcp` (enabled=false).
func (*Generator) SetMCPHints ¶ added in v0.43.0
func (g *Generator) SetMCPHints(ctx context.Context, commandPath string, hints setup.MCPHints, clear bool) error
SetMCPHints records a command's MCP hints in the manifest and re-renders its cmd.go so the generated setup.AnnotateMCP call matches. It is the engine behind `gtb annotate`: a set field replaces the recorded one, an unset field leaves it alone, and clear drops the block entirely.
func (*Generator) SetProtection ¶
func (*Generator) SetSetting ¶ added in v0.43.0
SetSetting writes one author setting into the manifest by its dotted path, validates the result as the flags are validated, and runs the derived-file sync, so a set never needs a regenerate after it (spec 0197 D6). Nothing is written when validation refuses.
func (*Generator) UnsetSetting ¶ added in v0.43.0
UnsetSetting zeroes one author setting.
func (*Generator) UpdateTemplateSource ¶
UpdateTemplateSource re-resolves a named git source's ref to a new SHA (clearing the old pin so the clone re-resolves) and regenerates. This is the only pin-advancing path (D7). A local source has no pin to advance; updating it simply regenerates against the current on-disk tree.
type IgnoreCheckResult ¶
type IgnoreCheckResult struct {
Path string // the queried path
Ignored bool // final decision under last-match-wins + ! negation
Matched bool // whether any rule matched at all
Rule string // the winning rule line as written (empty when Matched is false)
Negated bool // whether the winning rule was a negation
// State is the resolved tier: managed, ignored or sealed. Ignored stays for
// the render question specifically; State is what distinguishes a path the
// generator may still wire from one it must not touch at all (spec 0188 D7).
State RuleState
}
IgnoreCheckResult reports the ignore decision for a single path, naming the rule that decided it. Backs `gtb ignore check`.
type IgnoreListEntry ¶
type IgnoreListEntry struct {
Path string
Ignored bool
Rule string // winning rule line (empty when no rule matched)
State RuleState
}
IgnoreListEntry is one tracked file and its ignore status, attributed to the winning rule. Backs `gtb ignore list`.
type IgnoreListing ¶
type IgnoreListing struct {
Rules []string // active rule lines, in file order
Entries []IgnoreListEntry // tracked files governed by a rule, sorted by path
StaleRules []string // rules that match no tracked file
}
IgnoreListing is the resolved view `gtb ignore list` prints: every active rule, the tracked files each governs, and any rule matching no tracked file.
type IgnoreRules ¶
type IgnoreRules struct {
// contains filtered or unexported fields
}
IgnoreRules holds compiled ignore patterns from a .gtb/ignore file. Patterns are evaluated top-to-bottom; later patterns override earlier ones. Negation (!) re-includes a previously excluded file.
func LoadIgnoreRules ¶
func LoadIgnoreRules(fs afero.Fs, projectPath string) *IgnoreRules
LoadIgnoreRules reads the .gtb/ignore file from the project directory. Returns empty rules (nothing ignored) if the file doesn't exist.
func (*IgnoreRules) Explain ¶
func (r *IgnoreRules) Explain(relPath string) (rule string, negated, matched bool)
Explain evaluates all rules top-to-bottom and returns the winning rule for the given relative path: the last rule that matched (last-match-wins), the original rule line as written (with any ! or trailing /), whether that rule was a negation, and whether any rule matched at all. When matched is false the path is governed by no rule (and is therefore not ignored). This backs `gtb ignore check`, which must name the deciding rule — a question the flat file cannot answer, because negation means the winner can be a later ! line.
func (*IgnoreRules) IsIgnored ¶
func (r *IgnoreRules) IsIgnored(relPath string) bool
IsIgnored reports whether the generator must not *render* the path — rewrite it wholesale from source. A sealed path is also ignored: sealing is a superset.
It deliberately does not answer the wiring question. A caller that performs a localised, structure-aware edit asks IsSealed instead (spec 0188 D2).
func (*IgnoreRules) IsSealed ¶
func (r *IgnoreRules) IsSealed(relPath string) bool
IsSealed reports whether the generator must not touch the path at all, wiring included. This is the predicate the localised writers consult — subcommand registration, child re-registration, hook-stub injection — because refusing those leaves a manifest declaring a child its parent never registers, or a cmd.go referencing a hook stub that does not exist: a project that does not build. A plain ignore rule is not enough to ask for that; `sealed` is.
func (*IgnoreRules) Rules ¶
func (r *IgnoreRules) Rules() []string
Rules returns the original rule lines in file order (with any ! or trailing / preserved). It backs `gtb ignore list`, which must surface every active rule and flag those that currently match no tracked file.
func (*IgnoreRules) State ¶
func (r *IgnoreRules) State(relPath string) RuleState
State evaluates all rules top-to-bottom and returns what may be done to the path: managed, ignored, or sealed. Later rules override earlier ones, and a negation (!) returns a path all the way to managed.
The `sealed` attribute is tracked separately from the ignore decision so that a rule which says nothing about sealing leaves an earlier rule's decision standing, and `-sealed` can drop a path from sealed back to ignored without re-managing it (spec 0188 D4).
type Manifest ¶
type Manifest struct {
Properties ManifestProperties `yaml:"properties"`
ReleaseSource ManifestReleaseSource `yaml:"release_source"`
Version ManifestVersion `yaml:"version"`
Hashes map[string]string `yaml:"hashes,omitempty"` // project-level file hashes (relative path → SHA256)
Commands []ManifestCommand `yaml:"commands,omitempty"`
}
func DecodeManifestFile ¶
DecodeManifestFile reads and unmarshals the manifest at the given manifest.yaml path from fs. It is the single read/decode helper routed through by every manifest-loading site (including the generate-flag command in internal/cmd), so the read+unmarshal boilerplate — and its error wording — lives in one place.
func ManifestFromSkeletonConfig ¶ added in v0.43.0
func ManifestFromSkeletonConfig(config SkeletonConfig, fileHashes map[string]string, gtbVersion string) Manifest
ManifestFromSkeletonConfig is the pure config-to-manifest mapping the generate path uses, exported for the round-trip tests of its callers.
func (*Manifest) GetReleaseSource ¶
GetReleaseSource returns the release source type, owner, and repo.
func (*Manifest) TrackedFiles ¶
TrackedFiles returns every file the manifest records a hash for, keyed by project-relative, slash-separated path.
A manifest keeps hashes in two places: project-level skeleton files in Hashes, and per-command generated files (cmd.go, init.go, main_test.go) in each command's own Hashes map, keyed by bare filename. Consumers that walked only the first — `gtb ignore list` and the `doctor` diverged-files check — were structurally blind to exactly the files that raise a conflict on regenerate, so a live ignore rule covering a command file was reported stale and doctor passed a project that could not be regenerated (issue #13).
The deprecated per-command Hash field is folded in as cmd.go, matching what the conflict resolver reads.
type ManifestBootstrap ¶
type ManifestBootstrap struct {
// AutoInitialise runs a non-interactive init to write the default config
// when it is missing, instead of failing. Defaults to false.
AutoInitialise bool `yaml:"auto_initialise,omitempty"`
// SkipConfigCheck lists commands (by Name() or full CommandPath()) whose
// missing-config gate is relaxed to a tolerant load so they own bootstrap.
SkipConfigCheck []string `yaml:"skip_config_check,omitempty"`
// AuxiliaryCommands names commands that take the root pre-run's
// auxiliary fast path (props.Tool.AuxiliaryCommands); spec 0197 D4.
AuxiliaryCommands []string `yaml:"auxiliary_commands,omitempty"`
}
ManifestBootstrap holds config-bootstrap lifecycle policy for generated tools — the manifest representation of props.Tool.Bootstrap. Empty scaffolds nothing (framework default: a missing config is a hard error when init is enabled). See https://gitlab.com/phpboyscout/go-tool-base/-/wikis/specs/0114-bootstrap-auto-initialise-skip-config-check.
type ManifestCI ¶
type ManifestCI struct {
// ComponentSource is the include base for the phpboyscout/cicd
// components in the scaffolded .gitlab-ci.yml. Empty means "use the
// framework default" (DefaultCICDComponentSource,
// gitlab.com/phpboyscout/cicd); a mirrored/self-hosted downstream sets
// this to repoint the include base. Defaulted on render so a manifest
// with no ci block still produces a complete pipeline.
ComponentSource string `yaml:"component_source,omitempty"`
}
ManifestCI holds CI-pipeline configuration for generated tools. It is the manifest representation of the scaffolded GitLab pipeline's configurable inputs. Currently it carries only the phpboyscout/cicd component source so a mirrored or self-hosted downstream can repoint the include base; the component versions are pinned by a generator constant kept in lockstep with the framework (not manifest-driven). See https://gitlab.com/phpboyscout/go-tool-base/-/wikis/specs/0082-generator-gitlab-ci-refresh.
type ManifestChat ¶ added in v0.42.0
type ManifestChat struct {
Providers []string `yaml:"providers"`
Default ManifestChatDefault `yaml:"default,omitempty"`
// ToolBridge set false leaves go/chat-mcptools unlinked even though
// claude-local or codex-local is selected: the tool carries no MCP SDK,
// and those providers refuse tools. Unset links it when they need it.
ToolBridge *bool `yaml:"tool_bridge,omitempty"`
}
ManifestChat is the manifest's chat block: which providers the tool links. An absent block (nil Providers) is a project generated before the block existed and reads as the default set on regenerate (spec 0194 D7); an explicit empty list means "ai enabled, link no provider" and is kept as written (#45). The two must marshal differently, so the list carries no omitempty and IsZero decides whether the block is emitted at all.
func (ManifestChat) IsZero ¶ added in v0.43.0
func (c ManifestChat) IsZero() bool
IsZero reports an absent block, which yaml omits; an empty non-nil list is not zero and is written as `providers: []`.
type ManifestChatDefault ¶ added in v0.43.0
type ManifestChatDefault struct {
Provider string `yaml:"provider,omitempty"`
Model string `yaml:"model,omitempty"`
BaseURL string `yaml:"base_url,omitempty"`
APIVersion string `yaml:"api_version,omitempty"`
Project string `yaml:"project,omitempty"`
Location string `yaml:"location,omitempty"`
}
ManifestChatDefault is the author's default for the tool's chat client: the provider, optionally its model, and the addressing a few providers refuse to construct without. It is rendered into the tool's embedded defaults (cmd/<name>/chat/assets) so the running tool reads it as its lowest config layer, and the end user overrides it in their own file (spec 0196 D3, D4). Credentials never appear here.
func (ManifestChatDefault) IsZero ¶ added in v0.43.0
func (d ManifestChatDefault) IsZero() bool
IsZero reports a default the author has not stated; yaml omits the block.
type ManifestCommand ¶
type ManifestCommand struct {
Name string `yaml:"name"`
Description MultilineString `yaml:"description"`
LongDescription MultilineString `yaml:"long_description,omitempty"`
Aliases []string `yaml:"aliases,omitempty"`
Hidden bool `yaml:"hidden,omitempty"`
Args string `yaml:"args,omitempty"`
Hash string `yaml:"hash,omitempty"` // Deprecated: use Hashes
Hashes map[string]string `yaml:"hashes,omitempty"`
WithAssets bool `yaml:"with_assets,omitempty"`
WithInitializer bool `yaml:"with_initializer,omitempty"`
WithConfigValidation bool `yaml:"with_config_validation,omitempty"`
Protected *bool `yaml:"protected,omitempty"`
// MCPEnabled is the tri-state MCP-exposure decision for this command:
// nil = inherit (default exposed), true = explicitly exposed, false =
// excluded from the MCP tool surface. Build-time only; see
// https://gitlab.com/phpboyscout/go-tool-base/-/wikis/specs/0089-mcp-command-exposure-gating.
MCPEnabled *bool `yaml:"mcp_enabled,omitempty"`
// MCPHints are the MCP tool annotations the command declares about itself
// (spec 0201 D5). Absent means it states nothing; each hint is tri-state.
MCPHints *ManifestMCPHints `yaml:"mcp_hints,omitempty"`
PersistentPreRun bool `yaml:"persistent_pre_run,omitempty"`
PreRun bool `yaml:"pre_run,omitempty"`
MutuallyExclusive [][]string `yaml:"mutually_exclusive,omitempty"`
RequiredTogether [][]string `yaml:"required_together,omitempty"`
Flags []ManifestFlag `yaml:"flags,omitempty"`
Commands []ManifestCommand `yaml:"commands,omitempty"`
Warning string `yaml:"-"` // Used for comments
}
func (ManifestCommand) MarshalYAML ¶
func (c ManifestCommand) MarshalYAML() (any, error)
type ManifestCommandUpdate ¶
type ManifestCommandUpdate struct {
Name string
Description string
LongDescription string
Aliases []string
Args string
Hashes map[string]string
Flags []ManifestFlag
WithAssets bool
WithInitializer bool
WithConfigValidation bool
PersistentPreRun bool
PreRun bool
Protected *bool
MCPEnabled *bool
MCPHints *ManifestMCPHints
Hidden bool
}
ManifestCommandUpdate carries all fields that updateCommandRecursive writes to a ManifestCommand entry. Adding a new manifest field means adding it here rather than extending the function signature.
type ManifestConfig ¶ added in v0.46.0
type ManifestConfig struct {
// Layers is the stack by name, lowest precedence first (see
// props.ConfigLayer). Empty means the project states nothing and inherits
// the framework default.
Layers []string `yaml:"layers,omitempty"`
// Formats are the config formats the tool links beyond YAML, which is
// built in and never listed (spec 0204 D2). Each is a blank import in
// cmd/<name>/config.go.
Formats []string `yaml:"formats,omitempty"`
// Format is the tool's own config format; empty is YAML.
Format string `yaml:"format,omitempty"`
// Sources are the declared config source slots, each placed in Layers
// (spec 0204 D15).
Sources []ManifestConfigSource `yaml:"sources,omitempty"`
// KeyCase is the config stack's key mode, folded or exact; empty is
// folded (spec 0209).
KeyCase string `yaml:"key_case,omitempty"`
}
ManifestConfig is the project's declared configuration stack.
type ManifestConfigSource ¶ added in v0.46.0
type ManifestConfigSource struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
// Required, when absent, is true (spec 0204 D6).
Required *bool `yaml:"required,omitempty"`
// Writable, when absent, is the kind's default (spec 0204 D7, D12).
Writable *bool `yaml:"writable,omitempty"`
}
ManifestConfigSource is one declared config source slot. Where it connects is runtime configuration under config.sources.<name>, never recorded here (spec 0204 R1).
type ManifestDirectSource ¶ added in v0.43.0
type ManifestDirectSource struct {
URLTemplate string `yaml:"url_template,omitempty"`
ChecksumURLTemplate string `yaml:"checksum_url_template,omitempty"`
SignatureURLTemplate string `yaml:"signature_url_template,omitempty"`
VersionURL string `yaml:"version_url,omitempty"`
VersionFormat string `yaml:"version_format,omitempty"`
VersionKey string `yaml:"version_key,omitempty"`
PinnedVersion string `yaml:"pinned_version,omitempty"`
}
ManifestDirectSource mirrors the keys the withdrawn direct channel recorded.
type ManifestExternalAttach ¶
type ManifestExternalAttach struct {
// Constructor is the exported symbol to call, e.g. "NewCmdSign".
Constructor string `yaml:"constructor"`
// Args are injection tokens from the closed vocabulary
// ([templates.ExternalArgTokens]), rendered in order. Empty means a
// zero-argument constructor.
Args []string `yaml:"args,omitempty"`
// Wrap is true when Constructor returns *cobra.Command and must be wrapped
// via setup.Wrap("", …); false when it returns *setup.Command and is
// attached directly. It describes the constructor's return type, not gating
// — declarative attachments are un-gated (always-on) in v1.
Wrap bool `yaml:"wrap"`
// Name, if set, is the expected top-level command name, used only for
// best-effort collision detection. It does not affect the rendered call.
Name string `yaml:"name,omitempty"`
}
ManifestExternalAttach describes a single external constructor call to render onto the generated root.
type ManifestExternalCommand ¶
type ManifestExternalCommand struct {
// Module is the Go module path providing the commands, e.g.
// "gitlab.com/phpboyscout/go/signing-cli". Used for the go.mod require.
Module string `yaml:"module"`
// Version is the module version to require, e.g. "v0.1.0". Required — an
// explicit pin; there is no implicit latest resolution.
Version string `yaml:"version"`
// ImportPath is the package to import for the constructors. Defaults to
// Module when empty (the signing-cli case: the constructors live in the
// module root package).
ImportPath string `yaml:"import_path,omitempty"`
// Alias is the import alias for ImportPath in the generated root. Defaults
// to the import path's base name when empty.
Alias string `yaml:"alias,omitempty"`
// Attach lists the constructor calls to render onto the root. At least one
// entry is required — a module with nothing to attach is meaningless.
Attach []ManifestExternalAttach `yaml:"attach"`
}
ManifestExternalCommand declares one external module whose Cobra command builders are attached to the generated project's root (the declarative channel). It carries a provenance pin (module + version) and the call descriptors the generator needs to render each attachment, and holds no behaviour — mirroring TemplateSource.
type ManifestFeature ¶
type ManifestFlag ¶
type ManifestFlag struct {
Name string `yaml:"name"`
Type string `yaml:"type"`
Description MultilineString `yaml:"description"`
Persistent bool `yaml:"persistent,omitempty"`
Shorthand string `yaml:"shorthand,omitempty"`
Default string `yaml:"default,omitempty"`
DefaultIsCode bool `yaml:"default_is_code,omitempty"`
Required bool `yaml:"required,omitempty"`
Hidden bool `yaml:"hidden,omitempty"`
Warning string `yaml:"-"` // Used for comments
}
func (ManifestFlag) MarshalYAML ¶
func (f ManifestFlag) MarshalYAML() (any, error)
type ManifestGenerator ¶ added in v0.49.0
type ManifestGenerator struct {
// AI is the project's recorded choice on gtb calling a model while
// generating it. Absent defers to the person's generator.ai (spec 0211).
AI *bool `yaml:"ai,omitempty"`
}
ManifestGenerator records how gtb itself may work on the project, as opposed to what the generated tool does.
type ManifestHelp ¶
type ManifestMCP ¶ added in v0.43.0
type ManifestMCP struct {
Mode string `yaml:"mode,omitempty"`
}
ManifestMCP is the properties.mcp block.
type ManifestMCPHints ¶ added in v0.43.0
type ManifestMCPHints struct {
Title string `yaml:"title,omitempty"`
ReadOnly *bool `yaml:"read_only,omitempty"`
Destructive *bool `yaml:"destructive,omitempty"`
Idempotent *bool `yaml:"idempotent,omitempty"`
OpenWorld *bool `yaml:"open_world,omitempty"`
}
ManifestMCPHints is the manifest spelling of setup.MCPHints: the display title and the four MCP behavioural hints, rendered into the command's cmd.go as one setup.AnnotateMCP call and read back from it.
func ManifestMCPHintsFrom ¶ added in v0.43.0
func ManifestMCPHintsFrom(h setup.MCPHints) *ManifestMCPHints
ManifestMCPHintsFrom returns the manifest block for hints, or nil when they state nothing, so an empty block never appears in the manifest.
func (*ManifestMCPHints) Setup ¶ added in v0.43.0
func (h *ManifestMCPHints) Setup() setup.MCPHints
Setup converts the block to the setup value; a nil block states nothing.
type ManifestProperties ¶
type ManifestProperties struct {
Name string `yaml:"name"`
Description MultilineString `yaml:"description"`
Features []ManifestFeature `yaml:"features"`
EnvPrefix string `yaml:"env_prefix,omitempty"`
// UpdatePolicy is the generated tool's self-update posture baseline
// (disabled / prompt / enabled). Empty = framework default (disabled).
UpdatePolicy string `yaml:"update_policy,omitempty"`
// UpdateCheckInterval is the generated tool's baseline self-update-check
// throttle as a Go duration string (e.g. "24h"). Empty = framework
// default (24h).
UpdateCheckInterval string `yaml:"update_check_interval,omitempty"`
Help ManifestHelp `yaml:"help,omitempty"`
Telemetry ManifestTelemetry `yaml:"telemetry,omitempty"`
Signing ManifestSigning `yaml:"signing,omitempty"`
// Chat records the chat providers the tool ships, each a blank import of
// the module that registers it in cmd/<name>/chat.go. A project generated
// before this field existed has no block; regenerate reads that as every
// provider the framework could configure at the time and writes the list
// out explicitly (spec 0194 D5, D7).
Chat ManifestChat `yaml:"chat,omitempty"`
Bootstrap ManifestBootstrap `yaml:"bootstrap,omitempty"`
Generator ManifestGenerator `yaml:"generator,omitempty"`
CI ManifestCI `yaml:"ci,omitempty"`
// Templates records the custom template-overlay sources applied to the
// project, in render (layer) order: embedded base → templates[0] →
// templates[1] → … (last writer wins for a shared path). Each entry is
// provenance + pinning only; suppression behaviour lives in the source's
// own gtb-template.yaml descriptor. See
// https://gitlab.com/phpboyscout/go-tool-base/-/wikis/specs/0080-generator-custom-partial-templates.
Templates []TemplateSource `yaml:"templates,omitempty"`
// ModulePath is the Go module path (spec 0195 D5). A hosted project
// derives <host>/<org>/<repo>; one that is not names its own. Absent on a
// manifest that predates the field; regenerate derives and records it.
ModulePath string `yaml:"module_path,omitempty"`
// ForgeCredentials are forges enabled for their credential wizard and
// adapter only, never the release source (spec 0195 D6).
ForgeCredentials []props.FeatureID `yaml:"forge_credentials,omitempty"`
// Config declares the project's configuration stack (spec 0204). It is
// declared here rather than hand-wired in the scaffolded main because the
// manifest reconstructs byte-exactly from scratch: a hand-wired stack would
// be a hole reconstruction cannot fill (spec 0183 D8).
Config ManifestConfig `yaml:"config,omitempty"`
// LegacyConfigLayers is spec 0183's config_layers, read so the first
// regenerate can move it into Config.Layers (spec 0204 D13) and never
// written.
LegacyConfigLayers []string `yaml:"config_layers,omitempty"`
// MCP is the tool's MCP publication mode (spec 0201 D3): "direct" publishes
// one native tool per command; absent or "compact" publishes the three
// discovery tools. Rendered into props.Tool.MCP; the binary never reads it.
MCP ManifestMCP `yaml:"mcp,omitempty"`
// DocsLayout records the documentation tree layout: [DocsLayoutDiataxis]
// (the default for newly generated projects) or [DocsLayoutFlat] (the legacy
// docs/commands + docs/packages tree). Empty is treated as flat for backward
// compatibility with projects generated before this field existed.
DocsLayout string `yaml:"docs_layout,omitempty"`
// ModulePublished indicates the module is publicly published (e.g. on
// pkg.go.dev), allowing generated explanation docs to defer the package API
// reference there. Default false: the API reference is stubbed locally, since
// an unpublished module has no pkg.go.dev page to link.
ModulePublished bool `yaml:"module_published,omitempty"`
// ExternalCommands declares external Cobra command trees attached to the
// generated project's root via the declarative channel — the manifest
// records the module pin + the call descriptors, and the generator renders
// the attach calls into pkg/cmd/root/cmd.go on every root render (so they
// survive regenerate / enable / disable). See
// https://gitlab.com/phpboyscout/go-tool-base/-/wikis/specs/0182-external-command-attachment.
ExternalCommands []ManifestExternalCommand `yaml:"external_commands,omitempty"`
// ExternalCommandsAdapter, when true, wires the user-owned adapter escape
// hatch (pkg/cmd/external/attach.go, exposing external.Commands(p)) into the
// generated root. The adapter file is scaffolded once and thereafter
// author-owned; this flag only records that the root must emit the call.
ExternalCommandsAdapter bool `yaml:"external_commands_adapter,omitempty"`
}
func (ManifestProperties) ResolvedDocsLayout ¶
func (p ManifestProperties) ResolvedDocsLayout() string
ResolvedDocsLayout returns the effective documentation layout, defaulting an empty or unrecognised value to DocsLayoutFlat for backward compatibility with projects generated before the docs_layout field existed.
type ManifestReleaseSource ¶
type ManifestReleaseSource struct {
Type string `yaml:"type"`
// Backend is the forge the project is hosted on (spec 0195 D2). Absent on
// a manifest that predates the field; regenerate derives and records it.
Backend props.FeatureID `yaml:"backend,omitempty"`
Host string `yaml:"host"`
Owner string `yaml:"owner"`
Repo string `yaml:"repo"`
Private bool `yaml:"private,omitempty"`
// Static is the static release channel's one setting when Type is static
// (spec 0203 D1).
Static ManifestStaticSource `yaml:"static,omitempty"`
// Direct is the withdrawn direct channel's block (spec 0195 D7). It is
// kept so an older manifest loads; nothing reads it and regenerate drops
// it (spec 0203 D9).
Direct ManifestDirectSource `yaml:"direct,omitempty"`
}
type ManifestSigning ¶
type ManifestSigning struct {
// Enabled gates all signing scaffolding. Defaults to false; set true
// by `gtb enable signing` or `gtb generate project --signing`.
Enabled bool `yaml:"enabled,omitempty"`
// ExternalKeyEmail derives the WKD URL and enables the external
// (WKD) trust-anchor leg. Empty leaves verification embedded-only.
ExternalKeyEmail string `yaml:"external_key_email,omitempty"`
// RequireSignature fails an update closed when no valid signature is
// present. Stays false until a signed release has shipped (the N+1
// rollout); only ever flipped via `gtb enable signing --require-signature`.
RequireSignature bool `yaml:"require_signature,omitempty"`
// RequireChecksum is the author's baseline for checksum enforcement on
// self-update downloads (props.Tool.RequireChecksum); spec 0197 D4.
RequireChecksum bool `yaml:"require_checksum,omitempty"`
// KeySource selects the trust-anchor source: "embedded", "external"
// or "both" (the framework default when empty).
KeySource string `yaml:"key_source,omitempty"`
// RequireExternalCrosscheck fails closed when the external (WKD)
// resolver is unreachable, rather than degrading to embedded-only.
RequireExternalCrosscheck bool `yaml:"require_external_crosscheck,omitempty"`
// Backend selects the `gtb sign` backend the generated release
// pipeline signs with (e.g. "aws-kms", "local"). Defaults to
// "aws-kms" when a key id is recorded. Drives the GoReleaser signs
// block; backend-specific args (e.g. kms_region) are emitted only for
// the backends that take them.
Backend string `yaml:"backend,omitempty"`
// KeyID is the backend-specific signing key identifier passed to
// `gtb sign --key-id` (a KMS id/ARN/alias, or a PEM path for the
// local backend). Recording it is what turns the GoReleaser signs
// block on; empty leaves the release pipeline untouched.
KeyID string `yaml:"key_id,omitempty"`
// KMSRegion is the AWS region for the aws-kms backend
// (`gtb sign --kms-region`). Defaults to "eu-west-2". Ignored by
// backends that don't take a region.
KMSRegion string `yaml:"kms_region,omitempty"`
// PublicKey is the path to the armored public-key file the signature
// identifies (`gtb sign --public-key`), relative to the project root.
// Defaults to the embedded-key convention
// internal/trustkeys/keys/signing-key-v1.asc.
PublicKey string `yaml:"public_key,omitempty"`
}
ManifestSigning holds self-update signature-verification configuration for generated tools. It is the manifest representation of the generated internal/trustkeys package, the props.Tool.Signing wiring, and the generated signing.go enforcement defaults. Signing is disabled by default — a project with no signing block scaffolds nothing signing-related. See https://gitlab.com/phpboyscout/go-tool-base/-/wikis/specs/0071-signing-generator-feature.
func ApplySigningDefaults ¶
func ApplySigningDefaults(s ManifestSigning) ManifestSigning
ApplySigningDefaults fills the release-pipeline defaults when a signing key id has been recorded, so the generated GoReleaser signs block is always complete. With no key id the release pipeline is left untouched. Exported so the generate command can default at its boundary, keeping the persisted manifest and the rendered .goreleaser.yaml consistent — the same defaulting EnableSigning applies.
type ManifestStaticSource ¶ added in v0.45.0
type ManifestStaticSource struct {
BaseURL string `yaml:"base_url,omitempty"`
}
ManifestStaticSource is release_source.static: the base URL the pointer and the per-tag manifests are published under.
type ManifestTelemetry ¶
type ManifestTelemetry struct {
Endpoint string `yaml:"endpoint,omitempty"`
OTelEndpoint string `yaml:"otel_endpoint,omitempty"`
}
ManifestTelemetry holds telemetry configuration for generated tools.
type ManifestVersion ¶
type ManifestVersion struct {
GoToolBase string `yaml:"gtb"`
// Go is the go directive the project was generated with, so regenerate
// rewrites the go line only when this changes and never because the
// author's toolchain moved (spec 0197 D3). Absent in older manifests.
Go string `yaml:"go,omitempty"`
}
type MultilineString ¶
type MultilineString string
func (MultilineString) MarshalYAML ¶
func (s MultilineString) MarshalYAML() (any, error)
type OverwriteMode ¶ added in v0.43.0
type OverwriteMode string
OverwriteMode is the file-conflict policy a generation or regeneration runs under.
const ( OverwriteAsk OverwriteMode = "ask" OverwriteAllow OverwriteMode = "allow" OverwriteDeny OverwriteMode = "deny" )
The three overwrite modes; empty parses as OverwriteAsk.
func ParseOverwriteMode ¶ added in v0.43.0
func ParseOverwriteMode(s string) (OverwriteMode, error)
ParseOverwriteMode maps a flag value onto an OverwriteMode; empty is ask.
type PipelineOptions ¶
type PipelineOptions struct {
SkipAssets bool // do not generate asset files
SkipDocumentation bool // do not run documentation generation
SkipRegistration bool // do not modify the parent cmd.go
// BoilerplateDocsOnly writes a missing doc page without consulting a chat
// provider: a regenerate is a deterministic rewrite, and its AI pass is
// what --update-docs or --ai asks for (#35, spec 0211).
BoilerplateDocsOnly bool
}
PipelineOptions controls which steps CommandPipeline executes. The zero value enables all steps.
type PipelineResult ¶
type PipelineResult struct {
Warnings []StepWarning
}
PipelineResult is returned by CommandPipeline.Run and carries any advisory warnings accumulated during execution. A non-empty Warnings slice does not indicate overall failure — the pipeline continued past those steps.
type RuleState ¶
type RuleState int
RuleState is what the rules say may be done to a path. The generator does two different things to a generated file and they warrant different answers (spec 0188): rendering rewrites it from source, while wiring is the localised, structure-aware edit that registers a subcommand in its parent or injects a hook stub into main.go.
const ( // StateManaged: the generator owns the path. Render and wiring both proceed. StateManaged RuleState = iota // StateIgnored: a bare rule matched. Rendering is refused. Wiring still // proceeds where refusing it would leave the project unbuildable — see // IsSealed and spec 0188 D2. StateIgnored // StateSealed: a rule set the `sealed` attribute. No write of any kind. StateSealed )
type SettingKind ¶ added in v0.43.0
type SettingKind string
SettingKind says whether a SkeletonConfig field is an author setting the manifest owns or an option of one generate run.
const ( // KindSetting is recorded in the manifest and round-trips through // regenerate; it has a flag, and a wizard page when it is a decision. KindSetting SettingKind = "setting" // KindRunOption shapes one run (where to write) and is not recorded. KindRunOption SettingKind = "run option" )
type SkeletonConfig ¶
type SkeletonConfig struct {
Name string
Repo string
Host string
Description string
Path string
GoVersion string // overrides autodetected version when set
Features []ManifestFeature
Private bool // true if the repository requires authentication to access
HelpType string // "slack", "teams", or ""
SlackChannel string
SlackTeam string
TeamsChannel string
TeamsTeam string
TelemetryEndpoint string // populated from manifest telemetry.endpoint
TelemetryOTelEndpoint string // populated from manifest telemetry.otel_endpoint
EnvPrefix string // environment variable prefix for config overrides
// ConfigLayers declares which config-stack layers the project wires. Empty
// inherits the framework default, which is what every project generated
// before this field existed does.
ConfigLayers []string
// ConfigFormats are the config formats the tool links; ConfigFormat is its
// own (spec 0204 D2).
ConfigFormats []string
ConfigFormat string
// ConfigSources are the declared config source slots (spec 0204 D15).
ConfigSources []ManifestConfigSource
// ConfigKeyCase is the config stack's key mode, folded or exact (spec 0209).
ConfigKeyCase string
Signing ManifestSigning // self-update signature-verification posture (disabled by default)
Chat ManifestChat // chat providers the tool links (cmd/<name>/chat.go)
Bootstrap ManifestBootstrap // config-bootstrap lifecycle policy (auto-init / skip-config-check)
Generator ManifestGenerator // how gtb itself may work on the project (spec 0211)
// MCPMode is the MCP publication mode: "" or "compact" for the discovery
// facade, "direct" for one native tool per command (spec 0201 D3).
MCPMode string
// UpdatePolicy is the generated tool's self-update posture baseline
// (disabled / prompt / enabled). Empty leaves it unset so the framework
// default (disabled) applies; wired into the generated root command's
// props.Tool.UpdatePolicy. See https://gitlab.com/phpboyscout/go-tool-base/-/wikis/specs/0087-forced-update-feature.
UpdatePolicy string
// UpdateCheckInterval is the generated tool's baseline self-update-check
// throttle as a Go duration string (e.g. "24h"). Empty leaves it unset so
// the framework default (24h) applies; wired into the generated root
// command's props.Tool.UpdateCheckInterval.
UpdateCheckInterval string
// CIComponentSource overrides the phpboyscout/cicd include base in the
// scaffolded GitLab pipeline. Empty falls back to
// DefaultCICDComponentSource. GitLab-only; ignored for GitHub projects.
CIComponentSource string
// Templates carries the custom template-overlay sources (in layer order)
// through generation. The wizard/flags populate it and
// writeSkeletonManifest persists it.
Templates []TemplateSource
// ForgeBackend is the forge the project is hosted on (spec 0195 D2 and
// D4). Empty means not hosted: no forge feature, adapter or credential
// wizard, and ModulePath must be given.
ForgeBackend props.FeatureID
// ForgeCredentials are further forges enabled for credential capture and
// their adapter, without affecting the release source (spec 0195 D6).
ForgeCredentials []props.FeatureID
// ModulePath is the Go module path; empty derives <host>/<repo> for a
// hosted project (spec 0195 D5).
ModulePath string
// ReleaseChannel is forge or static when the update feature is enabled
// (spec 0195 D7, spec 0203 D1); empty falls back to the backend's source
// type.
ReleaseChannel string
// ReleaseBaseURL is the static channel's location (spec 0203 D1), read
// only when ReleaseChannel is static.
ReleaseBaseURL string
}
func SkeletonConfigFromManifest ¶ added in v0.43.0
func SkeletonConfigFromManifest(m Manifest) SkeletonConfig
SkeletonConfigFromManifest is the inverse of ManifestFromSkeletonConfig: the author settings a manifest records, as the config the generate path takes. It is what gtb wizard loads its pages from (spec 0197 D13).
type StepWarning ¶
StepWarning records a non-fatal failure within a pipeline step.
type TemplateContractData ¶
type TemplateContractData struct {
Name string
Description string
Repo string
Host string
Org string
RepoName string
ModulePath string
ReleaseProvider string
GoVersion string
GoToolBaseVersion string
EnvPrefix string
EnabledFeatures []string
DisabledFeatures []string
Private bool
// SigningEnabled / HelpType expose only presence/shape, never secrets.
SigningEnabled bool
HelpType string
}
TemplateContractData is the documented, stable, metadata-only and secret-free subset of skeletonTemplateData exposed to overlay templates. It deliberately excludes any resolved credential, env var, absolute host path, or forge token — the information-disclosure threat. Adding a field is a safe additive change; removing one is a contract-version bump.
func (TemplateContractData) GetReleaseProvider ¶
func (d TemplateContractData) GetReleaseProvider() string
GetReleaseProvider satisfies releaseProviderAccessor.
type TemplateDescriptor ¶
type TemplateDescriptor struct {
// Contract is the data-contract version the set targets. Zero is treated
// as version 1 (the only version) for descriptors that omit it; any
// value above supportedContractVersion is rejected.
Contract int `yaml:"contract"`
// Description is a human-facing summary of the template set.
Description string `yaml:"description"`
// Replaces names the embedded scaffolds this set supersedes. Each entry
// must be a known alias in suppressibleAliases.
Replaces []string `yaml:"replaces"`
}
TemplateDescriptor is the parsed gtb-template.yaml at a source root. It declares the data-contract version the set targets and the embedded scaffolds it suppresses before the overlay renders. Absent descriptor / empty Replaces ⇒ pure overlay (nothing suppressed).
type TemplateSource ¶
type TemplateSource struct {
// Name is an optional operator-assigned handle used by
// `gtb template update/remove <name>`. Defaults to the repo/dir base
// name when unset.
Name string `yaml:"name,omitempty"`
// Type is "git" or "local".
Type TemplateSourceType `yaml:"type"`
// Location is the forge repo path (org/repo, nested GitLab groups
// supported) or a full clone URL for git sources, or a filesystem path
// for local sources.
Location string `yaml:"location"`
// Ref is the branch/tag/commit the operator specified, recorded verbatim
// (provenance). Empty/"" defaults to the source's default branch.
Ref string `yaml:"ref,omitempty"`
// Resolved is the commit SHA Ref resolved to at generate time — the pin
// regenerate reproduces from. Empty for local sources.
Resolved string `yaml:"resolved,omitempty"`
// Fingerprint is a content fingerprint of a local source's tree at
// generate time, so regenerate can warn when the on-disk source drifts.
// Empty for git sources (the resolved SHA is the pin).
Fingerprint string `yaml:"fingerprint,omitempty"`
// Hashes records each rendered overlay file's SHA256 keyed by output
// relative path, so a source's footprint is self-contained and can be
// removed cleanly (D5).
Hashes map[string]string `yaml:"hashes,omitempty"`
}
TemplateSource is the minimal consumer-manifest record for one custom template-overlay source: provenance (where it came from, what ref the operator asked for) plus the pin (the resolved commit SHA for git, a content fingerprint for local) and the per-source rendered-output hashes.
The *behaviour* of a source (which embedded scaffolds it replaces, which data-contract version it targets) lives with the template set in its gtb-template.yaml descriptor, never here — the consumer manifest stays provenance-only so consuming projects do not repeat the author's intent.
func ParseTemplateSpec ¶
func ParseTemplateSpec(fs afero.Fs, spec, name string) (TemplateSource, error)
ParseTemplateSpec parses a `<src>@<ref>` spec into a TemplateSource. The `@<ref>` suffix is optional (defaults to the source's default branch for git). The source type is inferred: a value that exists as a directory on fs, or that looks like a filesystem path (starts with ./ ../ / or ~), is a local source; otherwise it is a git source. The parsed entry is validated before return so a malformed spec fails at the CLI boundary.
An explicit name (e.g. from `--name`) overrides the inferred default; pass "" to take the default (the repo/dir base name).
type TemplateSourceType ¶
type TemplateSourceType string
TemplateSourceType discriminates the two custom-template source backends.
const ( // TemplateSourceLocal reads a template overlay directly from a // filesystem path. No network, no cache, no SHA pin — a content // fingerprint is recorded so regenerate can warn on drift. TemplateSourceLocal TemplateSourceType = "local" // TemplateSourceGit clones a template overlay from a forge repo (public // over https/go-git, private via the configured forge auth) and pins the // resolved commit SHA for byte-stable regeneration. TemplateSourceGit TemplateSourceType = "git" )
Source Files
¶
- adapters.go
- ai.go
- ai_choice.go
- apply_settings.go
- ast.go
- ast_extract.go
- ast_extract_properties.go
- author_settings.go
- commands.go
- config_formats.go
- config_sources.go
- conflict.go
- context.go
- diff_pager.go
- doc.go
- docs.go
- docs_migrate.go
- dryrun.go
- env_variables.go
- errors.go
- external.go
- external_validate.go
- features.go
- files.go
- forge_backend.go
- generator.go
- gitinit.go
- gomod_seed.go
- hash.go
- ignore.go
- ignore_command.go
- links.go
- manifest.go
- manifest_query.go
- manifest_recover.go
- manifest_scan.go
- manifest_update.go
- materialise.go
- overwrite.go
- pipeline.go
- provenance.go
- pure_group.go
- regenerate.go
- releases.go
- removal.go
- retired.go
- settings.go
- signing.go
- signing_goreleaser.go
- skeleton.go
- skeleton_sync.go
- stubs.go
- sync.go
- template_escape.go
- templatesource.go
- templatesource_apply.go
- templatesource_clone_real.go
- templatesource_fetch.go
- templatesource_manage.go
- templatesource_spec.go
- templatesource_validate.go
- tty_unix.go
- validate.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package gomod seeds a scaffold's go.mod with the direct requirements the generator's own import lines imply, and nothing more (spec 0200).
|
Package gomod seeds a scaffold's go.mod with the direct requirements the generator's own import lines imply, and nothing more (spec 0200). |
|
Package templates provides Go template definitions and data structures used by the generator to produce CLI command scaffolding, registration code (cmd.go), and implementation stubs (main.go).
|
Package templates provides Go template definitions and data structures used by the generator to produce CLI command scaffolding, registration code (cmd.go), and implementation stubs (main.go). |
|
Package verifier provides post-generation verification strategies that validate generated projects compile and pass tests.
|
Package verifier provides post-generation verification strategies that validate generated projects compile and pass tests. |