On this page
First-class support for strictcli projects: reading the schema document the CLI dumps, and rendering its commands, flags, arguments and groups as pages.
#internal/strictclisupport
#internal/strictclisupport
Package strictclisupport is first-class support for strictcli-based projects.
It reads .strictcli/schema.json -- the document <app> --dump-schema writes -- for the CLI's structure (the app, its commands, flags, arguments and groups) and renders that structure as Markdown documentation pages.
#Version 2 only
The reader accepts schema_version 2 and nothing else. v2 is not a superset of v1: it deletes the type, repeatable and arg-level required keys the v1 reader read, and adds constructs (selectors, constraints, update declarations) v1 had no encoding for. A schema of any other version is a hard error naming the regeneration command, because the alternative -- reading a v1 document with v2 rules -- is a page that silently labels every flag str and every positional argument required. That page shipped once already, which is why there is no fallback here.
#KindIndex
const KindIndex PageKind = "index"KindIndex is the CLI reference index page.
#KindCommand
const KindCommand PageKind = "command"KindCommand is a top-level command's page.
#KindGroup
const KindGroup PageKind = "group"KindGroup is a command group's page.
#SupportedSchemaVersion
const SupportedSchemaVersion = 2SupportedSchemaVersion is the one dumped-schema format this reader understands. There is no v1 path and no negotiated fallback.
#PageKind
type PageKind stringPageKind is which CLI page a description belongs to. Every default description and every recognition of one is decided per kind.
#FlagHelp
type FlagHelp structFlagHelp is one declaration a reader sees, as IterFlagHelp reports it.
#SchemaError
type SchemaError structSchemaError is returned when a schema document is present but cannot be read on v2's terms: a wrong or absent schema_version, an absent project_id, or a project_id naming a different project.
It is the counterpart of the Python surface's ValueError, and a distinct type so the command layer renders it as the refusal it is rather than as an unexpected internal failure.
#SchemaDiscoveryError
type SchemaDiscoveryError structSchemaDiscoveryError is returned when .strictcli/schema.json discovery finds nothing or finds more than one candidate.
It is a hard error: a page that asked for a CLI table cannot be rendered from a schema nobody named, and picking one of several silently would document a different app than the author meant.
#Structure
type Structure structStructure is a CLI's structure as the renderers read it: the app's own facts, its behavioral-completeness declarations, and its commands and groups in declaration order.
The per-command and per-group entries stay decoded objects rather than becoming typed records, for two reasons the Python model had as well: every key a schema carries reaches the renderers untouched, whether this version of selfdoc knows it or not, and the staleness measurement hashes a command's whole entry, so anything dropped here would silently change what a hash covers.
#NoSchemaError
type NoSchemaError structNoSchemaError is returned by ExtractCLIStructure when the project has no dumped schema at all -- the one condition ReadSchemaJSON answers in band instead.
#Object
type Object = extractors.JSONObjectObject is one decoded JSON object of a dumped schema, remembering the order its keys appeared in.
The order is not a nicety: the index page lists commands and groups in the order the schema declares them, so a model that lost the order would reshuffle every generated page from one run to the next.
#ComputeDefaultCLIDescription
func ComputeDefaultCLIDescription(kind PageKind, name, appName, helpText string) (string, error)ComputeDefaultCLIDescription returns the machine-default description for a CLI page.
When the page's help text is at least minHelpForFirstSentence characters long the default is the first sentence of that help; otherwise it is a long-form template naming the app and the command. The index default is always the fixed long-form template.
An unrecognized kind is an error: the caller knows which page it is writing, and inventing a description for a page whose kind is unknown would publish one.
#IsDefaultCLIDescription
func IsDefaultCLIDescription(value string, kind PageKind, name, appName, helpText string) (bool, error)IsDefaultCLIDescription reports whether value is a machine-generated default CLI description.
Every historical machine form is recognized, so machine residue is reseeded rather than frozen as if a person had written it:
- the current first-sentence form (help at least minHelpForFirstSentence characters), or - the long-form default template (a shorter help, or the index), or - the historical help[:155] truncation, with or without a trailing ellipsis, or - any prefix of the raw help at least 100 characters long (a truncated default from any prior cut point).
An empty value counts as a machine default: it is a blank machine placeholder the caller should reseed. CLI machine text is derivable from the schema, so this is a live recompute -- no static set can cover the truncated prefix family.
name is accepted for symmetry with ComputeDefaultCLIDescription and is not read: which template a value is compared against is decided by the kind and the help text alone.
#ExpectedCLIPageFilenames
func ExpectedCLIPageFilenames(structure *Structure) []stringExpectedCLIPageFilenames returns the filenames GenerateCLIPages would write for structure.
The stale-file cleanup pass reads it to know which CLI page names are current, so the pages a prior run generated are not deleted as stale before the new ones have been written.
#GenerateCLIPages
func GenerateCLIPages(structure *Structure, docsDir string, handle *effects.Handle) ([]string, error)GenerateCLIPages generates the Markdown documentation pages for structure into docsDir: an index page, one page per top-level command and one page per command group.
Every page carries generated: true frontmatter and is written read-only through handle, atomically. A page whose description frontmatter has been hand-edited keeps that description; one still carrying a machine default has it recomputed and is marked seeded: true.
It returns the generated filenames, relative to docsDir.
#IterFlagTokens
func IterFlagTokens(flags []any) []stringIterFlagTokens returns every --token an invocation can actually type, in declaration order, recursing through selector scopes.
A member-spelled selector's own name is not among them -- it is never typed -- while each of its choices is, and a scoped flag is a token like any other. This is what a completeness check must compare a page against; comparing against flag NAMES would demand that a page document a token no user can write.
#IterFlagHelp
func IterFlagHelp(flags []any) []FlagHelpIterFlagHelp returns the label and help of every declaration a reader sees, in declaration order.
It recurses through selector scopes, so a scoped flag's help is measured like any other. A selector's own label is its bare name, and each choice is labelled by the token that elects it.
#UsesStrictcli
func UsesStrictcli(sourcePaths []string, baseDir string) boolUsesStrictcli reports whether the project at baseDir has a .strictcli/schema.json file.
sourcePaths is accepted for call-site symmetry and is not read: detection is the presence of the schema document and nothing else.
#DiscoverSchemaDirs
func DiscoverSchemaDirs(baseDir string) []stringDiscoverSchemaDirs discovers the directories under baseDir that hold a .strictcli/schema.json.
It walks baseDir, pruning vendored and build directories and every hidden directory, and records each visited directory that holds a schema as a path relative to baseDir -- the project root being ".". That relative path is the value a schema-dir attribute takes.
The result is sorted. A walk error is not reported: an unreadable directory holds no schema anyone can name, which is the same answer as a directory that holds none.
#ReadSchemaJSON
func ReadSchemaJSON(baseDir string) (*Structure, error)ReadSchemaJSON reads baseDir's .strictcli/schema.json and translates it into the structure the renderers read.
It returns a nil structure and a nil error when the document does not exist -- "this project has no dumped schema" is an answer, not a failure. Every per-entry field the renderers read (value_schema, presence, default, choices, elect_by, nullable, negatable, unique, prefixed, variadic, hidden, deprecated, passthrough, flag_sets, constraints, update_of, write_mode) and every effects-regime per-command field (effect, consequential, grants, dry_run_supported, dry_run_unsupported_reason) is carried through untouched.
strictcli omits the app-level global_flags, infra and deprecated keys when they are empty and some emitters write an explicit null; both normalize to empty containers, so a renderer can truth-test them.
A document declaring a schema_version other than SupportedSchemaVersion, carrying no project_id, or naming a different project than the manifest, is a SchemaError.
#CommandName
func CommandName(entry *Object) string { return getString(entry, "name") }CommandName is the name a command or group entry declares.
The entries of a Structure are decoded objects rather than typed records -- see the type's own documentation for why -- so these accessors are how a consumer reads the fields every consumer reads, without each one restating how a missing or wrongly-typed field degrades.
#CommandHelp
func CommandHelp(entry *Object) string { return getString(entry, "help") }CommandHelp is the help text a command or group entry declares, "" when it declares none.
#CommandFlags
func CommandFlags(entry *Object) []any { return getList(entry, "flags") }CommandFlags are a command's flag declarations, in declaration order. Each element is a decoded object, which is what IterFlagTokens and IterFlagHelp read.
#CommandArgs
func CommandArgs(entry *Object) []any { return getList(entry, "args") }CommandArgs are a command's positional-argument declarations, in declaration order.
#GroupCommands
func GroupCommands(group *Object) []*ObjectGroupCommands are a group's subcommands, in declaration order.
#Field
func Field(entry *Object, key string) string { return getString(entry, key) }Field is the string value of one declared field of any schema entry -- a flag's env var, an argument's help -- and "" when the entry omits it or declares it as something other than a string.
#ExtractCLIStructure
func ExtractCLIStructure(sourcePaths []string, baseDir string) (*Structure, error)ExtractCLIStructure reads the CLI structure from baseDir's dumped schema.
It is ReadSchemaJSON with the absent document turned into a NoSchemaError, for the call sites that have already established the project uses strictcli. sourcePaths is accepted for call-site symmetry and is not read.
#PlainValue
func PlainValue(v any) anyPlainValue converts a decoded schema value into the plain Go value model -- map[string]any, []any and scalars.
It is what the schema-hash input needs: the hash is canonical JSON with sorted keys, so the ordered model this package reads a schema with carries nothing the hash uses, and a reflect-based encoder cannot walk it.
#SchemaError.Error
func (e *SchemaError) Error() string { return e.Message }Error returns the diagnostic.
#SchemaDiscoveryError.Error
func (e *SchemaDiscoveryError) Error() string { return e.Message }Error returns the diagnostic.
#NoSchemaError.Error
func (e *NoSchemaError) Error() stringError names the directory and the command that writes the missing document.