Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions docs/cli-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,27 @@ set it to `""` to disable Skill generation. The optional `skill.include` value
points at repo-local Skill resources merged into generated Skill files. Keep the
include directory outside `skill.root`.

Generated CLIs expose `--version` and `-v`. The release version is
build metadata, not `cli.yaml` data: pass `Version`, `Commit`, and `Date` through
`lathe.RunOptions`, or inject `github.com/lathe-cli/lathe/pkg/lathe.Version`,
`Commit`, and `Date` with Go `-ldflags` in your release build.

To let the generated CLI update itself from GitHub Releases, add:

```yaml
update:
github:
owner: acme
repo: acmectl
asset: "acmectl_{{ .Version }}_{{ .OS }}_{{ .Arch }}.tar.gz"
```

`asset` is a Go template with `Name`, `Version`, `Tag`, `OS`, and `Arch`.
`update` fetches the latest GitHub release, reports when the current binary is
already current, and asks for confirmation before replacing the executable. Pass
`--yes` to skip the prompt. The release asset must expose a `sha256:` digest; zip,
tar.gz, and tgz assets must contain a binary named after `cli.name`.

## Declare API Specs

Create `specs/sources.yaml`:
Expand Down
1 change: 1 addition & 0 deletions internal/codegen/render/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ var reservedRootCommands = map[string]bool{
"commands": true,
"help": true,
"search": true,
"update": true,
}

func MergeOverlay(specs []runtime.CommandSpec, overrides map[string]overlay.Override) []runtime.CommandSpec {
Expand Down
6 changes: 6 additions & 0 deletions internal/codegen/render/skill.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,12 @@ func renderSkillMD(manifest *config.Manifest, refs []moduleRef) string {
fmt.Fprintf(&b, "- `%s commands show <path...> --json`: source of truth for one command.\n", cli)
fmt.Fprintf(&b, "- `%s commands schema --json`: catalog schema version for parser compatibility.\n", cli)
fmt.Fprintf(&b, "- `%s search \"<intent>\" --json`: ranked candidate commands.\n\n", cli)
b.WriteString("## Maintenance Commands\n\n")
fmt.Fprintf(&b, "- `%s --version` or `%s -v`: print CLI build version.\n", cli, cli)
if manifest.Update.GitHub != nil {
fmt.Fprintf(&b, "- `%s update`: update this CLI from configured GitHub Releases. Run only when the user explicitly asks to update `%s`; it may replace the current executable. Use `--yes` only when explicitly authorized.\n", cli, cli)
}
b.WriteString("\n")
b.WriteString("## References\n\n")
b.WriteString("- Read `references/catalog.md` for the command discovery protocol and catalog field meanings.\n")
for _, ref := range refs {
Expand Down
23 changes: 21 additions & 2 deletions pkg/config/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import (
)

type Manifest struct {
CLI CLIInfo `yaml:"cli"`
Auth AuthInfo `yaml:"auth"`
CLI CLIInfo `yaml:"cli"`
Auth AuthInfo `yaml:"auth"`
Update UpdateInfo `yaml:"update,omitempty"`
}

type CLIInfo struct {
Expand All @@ -26,6 +27,16 @@ type AuthInfo struct {
Validate *AuthValidate `yaml:"validate,omitempty"`
}

type UpdateInfo struct {
GitHub *GitHubUpdate `yaml:"github,omitempty"`
}

type GitHubUpdate struct {
Owner string `yaml:"owner"`
Repo string `yaml:"repo"`
Asset string `yaml:"asset"`
}

type AuthValidate struct {
Method string `yaml:"method"`
Path string `yaml:"path"`
Expand Down Expand Up @@ -59,6 +70,14 @@ func Load(bytes []byte) (*Manifest, error) {
if m.CLI.Name == "" {
return nil, fmt.Errorf("cli.name is required")
}
if m.Update.GitHub != nil {
m.Update.GitHub.Owner = strings.TrimSpace(m.Update.GitHub.Owner)
m.Update.GitHub.Repo = strings.TrimSpace(m.Update.GitHub.Repo)
m.Update.GitHub.Asset = strings.TrimSpace(m.Update.GitHub.Asset)
if m.Update.GitHub.Owner == "" || m.Update.GitHub.Repo == "" || m.Update.GitHub.Asset == "" {
return nil, fmt.Errorf("update.github.owner, update.github.repo, and update.github.asset are required")
}
}
m.CLI.CommandPath = strings.ToLower(strings.TrimSpace(m.CLI.CommandPath))
if m.CLI.CommandPath == "" {
m.CLI.CommandPath = CommandPathAuto
Expand Down
5 changes: 5 additions & 0 deletions pkg/lathe/lathe.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ func NewApp(m *config.Manifest) *cobra.Command {
Use: m.CLI.Name,
Short: m.CLI.Short,
Long: agentHint(m.CLI.Name, m.CLI.Short),
Version: versionInfo(),
SilenceUsage: true,
}
cmd.CompletionOptions.DisableDefaultCmd = true
cmd.SetVersionTemplate("{{.Use}} {{.Version}}\n")
cmd.PersistentFlags().String("hostname", "", fmt.Sprintf("Server hostname (overrides $%s)", m.CLI.HostEnv))
cmd.PersistentFlags().StringP("output", "o", "table", "Output format: table|json|yaml|raw")
cmd.PersistentFlags().Bool("insecure", false, "Skip TLS certificate verification for this invocation")
Expand All @@ -48,6 +50,9 @@ func NewApp(m *config.Manifest) *cobra.Command {
cmd.AddCommand(authCmd)
cmd.AddCommand(commandsCmd(m))
cmd.AddCommand(searchCmd(m))
if m.Update.GitHub != nil {
cmd.AddCommand(updateCmd(m))
}
cmd.AddCommand(metaCmd(m.CLI.Name))
return cmd
}
Expand Down
Loading