From 30027fa244306c9b15a9517f6a18076dfd09822f Mon Sep 17 00:00:00 2001 From: samzong Date: Sun, 4 Oct 2026 00:54:00 -0400 Subject: [PATCH 1/2] build: run lathe generator from the go.mod tool directive Signed-off-by: samzong --- .lathe-template.yaml | 4 +- Makefile | 2 +- go.mod | 20 +++-- go.sum | 56 +++++++----- internal/generated/app/app_gen.go | 108 +++++++++++++++++------- internal/generated/modules_gen.go | 11 +-- skills/appctl/SKILL.md | 13 ++- skills/appctl/references/catalog.md | 20 +++-- skills/appctl/references/modules/app.md | 4 +- 9 files changed, 159 insertions(+), 79 deletions(-) diff --git a/.lathe-template.yaml b/.lathe-template.yaml index 65cfa90..34e4187 100644 --- a/.lathe-template.yaml +++ b/.lathe-template.yaml @@ -5,7 +5,7 @@ defaults: app_slug: cli-first-python-app cli_name: appctl go_module: example.com/cli-first-python-app - lathe_version: v0.5.3-0.20260816043253-e33580932a42 + lathe_version: v0.6.3 replacements: - variable: app_name files: [README.md] @@ -19,7 +19,7 @@ replacements: - variable: go_module files: [go.mod, cmd/appctl/main.go] - variable: lathe_version - files: [go.mod, Makefile] + files: [go.mod] renames: - from: cmd/appctl variable: cli_name diff --git a/Makefile b/Makefile index fb1c795..8ad3056 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ cli-sync: cp cli.yaml cmd/appctl/cli.yaml - go run github.com/lathe-cli/lathe/cmd/lathe@v0.5.3-0.20260816043253-e33580932a42 bootstrap + go tool lathe bootstrap go mod tidy cli-build: diff --git a/go.mod b/go.mod index cf14562..ec0a928 100644 --- a/go.mod +++ b/go.mod @@ -1,19 +1,27 @@ module example.com/cli-first-python-app -go 1.25.13 +go 1.26.0 require ( - github.com/lathe-cli/lathe v0.5.3-0.20260816043253-e33580932a42 + github.com/lathe-cli/lathe v0.6.3 github.com/spf13/cobra v1.10.2 ) require ( - github.com/gofrs/flock v0.13.0 // indirect + github.com/agnivade/levenshtein v1.2.1 // indirect + github.com/gofrs/flock v0.13.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/lathe-cli/kitup/go v0.1.4 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 // indirect - github.com/spf13/pflag v1.0.9 // indirect - golang.org/x/sys v0.47.0 // indirect - golang.org/x/term v0.45.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/vektah/gqlparser/v2 v2.5.58 // indirect + golang.org/x/sys v0.48.0 // indirect + golang.org/x/term v0.46.0 // indirect golang.org/x/text v0.32.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529 // indirect + google.golang.org/protobuf v1.36.12 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +tool github.com/lathe-cli/lathe/cmd/lathe diff --git a/go.sum b/go.sum index 869ef0b..6a47de6 100644 --- a/go.sum +++ b/go.sum @@ -1,40 +1,50 @@ +github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= +github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= +github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= -github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= +github.com/gofrs/flock v0.13.1 h1:jjREztyBeSKBZYAC+mgc1laB+xsgy4kYMf3FbKF2UBo= +github.com/gofrs/flock v0.13.1/go.mod h1:sf4BFiHwnvgxa25DlQoDqXQnwRMEOwqxRq37P6MzzmE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lathe-cli/lathe v0.5.3-0.20260816043253-e33580932a42 h1:eknDAb1CeWtRsntyTzmf9Dnbr3zjo1tNvGnoyVUkAfQ= -github.com/lathe-cli/lathe v0.5.3-0.20260816043253-e33580932a42/go.mod h1:/b5JLEkz9hQqKDXEnNsY/U4IGDJqxRlVD7Tw7iIyOSA= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.16.0 h1:O9DK+vNMDVGLr2BeZqmpLeMjiMNkuXfcqntWbZV6S5g= -github.com/rogpeppe/go-internal v1.16.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs= +github.com/lathe-cli/kitup/go v0.1.4 h1:WfA/EBSllkVGWAUIA91R5VzhjN21J1Ocr2/hxQNhOPU= +github.com/lathe-cli/kitup/go v0.1.4/go.mod h1:dZgJDmFRKjaFBZyaP1qlzOB9IEafnf1/ai4KX4hMA4c= +github.com/lathe-cli/lathe v0.6.3 h1:4w2AO8kBWNahKmbFmKoAJBCOepPpR0+Cz1WY6kYYTWM= +github.com/lathe-cli/lathe v0.6.3/go.mod h1:341Xd/WLg/SmfuoUEbJ/hREX4u96o4ZmTyCN0sPxagc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 h1:1EYB5IzjZawrrnELUi78f9fPu57HuXjmddZPjrls/28= github.com/santhosh-tekuri/jsonschema/v6 v6.0.3/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= -github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +github.com/vektah/gqlparser/v2 v2.5.58 h1:yHxQ3EjU2OGuDMh6noxxmZova1HkBM3CbdGtL+rvjOc= +github.com/vektah/gqlparser/v2 v2.5.58/go.mod h1:9O4Ox6Ngd3Y12bMD3w6i3CRQXh8W1oC1q0m6olCymDM= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= -golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= -golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= +golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= +golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= +golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE= +golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc= golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529 h1:zUWMZsvo/IJcD1t6MNCPO/azZTwz0TvwCBqr5aifoVY= +google.golang.org/genproto/googleapis/api v0.0.0-20260420184626-e10c466a9529/go.mod h1:a5OGAgyRr4lqco7AG9hQM9Fwh0N2ZV4grR0eXFEsXQg= +google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= +google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/generated/app/app_gen.go b/internal/generated/app/app_gen.go index 6cb4cce..f4f6822 100644 --- a/internal/generated/app/app_gen.go +++ b/internal/generated/app/app_gen.go @@ -3,12 +3,11 @@ package app import ( - "github.com/spf13/cobra" - "github.com/lathe-cli/lathe/pkg/runtime" + "github.com/spf13/cobra" ) -const generatedSchemaVersion = 11 +const generatedSchemaVersion = 22 func Mount(root *cobra.Command) error { if err := runtime.AssertSchema(generatedSchemaVersion); err != nil { @@ -25,7 +24,7 @@ func MountFlat(root *cobra.Command) error { } var Specs = []runtime.CommandSpec{ - { + runtime.CommandSpec{ Group: "Health", Use: "get", Short: "Check application health", @@ -33,10 +32,14 @@ var Specs = []runtime.CommandSpec{ Method: "GET", PathTpl: "/health", DefaultHostname: "http://127.0.0.1:3000", - Output: runtime.OutputHints{ResponseMediaType: "application/json"}, - Security: &runtime.SecurityHint{Public: true}, + Output: runtime.OutputHints{ + ResponseMediaType: "application/json", + }, + Security: &runtime.SecurityHint{ + Public: true, + }, }, - { + runtime.CommandSpec{ Group: "Tasks", Use: "create", Short: "Create a task", @@ -47,12 +50,22 @@ var Specs = []runtime.CommandSpec{ RequestBody: &runtime.RequestBody{ Required: true, MediaType: "application/json", - Schema: &runtime.SchemaSpec{Type: "object", Properties: map[string]*runtime.SchemaSpec{"title": &runtime.SchemaSpec{Type: "string"}}, Required: []string{"title"}}, + Schema: &runtime.SchemaSpec{ + Type: "object", + Properties: map[string]*runtime.SchemaSpec{"title": &runtime.SchemaSpec{ + Type: "string", + }}, + Required: []string{"title"}, + }, + }, + Output: runtime.OutputHints{ + ResponseMediaType: "application/json", + }, + Security: &runtime.SecurityHint{ + Public: true, }, - Output: runtime.OutputHints{ResponseMediaType: "application/json"}, - Security: &runtime.SecurityHint{Public: true}, }, - { + runtime.CommandSpec{ Group: "Tasks", Use: "delete", Short: "Delete a task", @@ -60,12 +73,19 @@ var Specs = []runtime.CommandSpec{ Method: "DELETE", PathTpl: "/tasks/{id}", DefaultHostname: "http://127.0.0.1:3000", - Params: []runtime.ParamSpec{ - {Name: "id", Flag: "id", In: "path", GoType: "string", Help: "id (path, required)", Required: true}, + Params: []runtime.ParamSpec{runtime.ParamSpec{ + Name: "id", + Flag: "id", + In: "path", + GoType: "string", + Help: "id (path, required)", + Required: true, + }}, + Security: &runtime.SecurityHint{ + Public: true, }, - Security: &runtime.SecurityHint{Public: true}, }, - { + runtime.CommandSpec{ Group: "Tasks", Use: "get", Short: "Get a task", @@ -73,13 +93,22 @@ var Specs = []runtime.CommandSpec{ Method: "GET", PathTpl: "/tasks/{id}", DefaultHostname: "http://127.0.0.1:3000", - Params: []runtime.ParamSpec{ - {Name: "id", Flag: "id", In: "path", GoType: "string", Help: "id (path, required)", Required: true}, + Params: []runtime.ParamSpec{runtime.ParamSpec{ + Name: "id", + Flag: "id", + In: "path", + GoType: "string", + Help: "id (path, required)", + Required: true, + }}, + Output: runtime.OutputHints{ + ResponseMediaType: "application/json", + }, + Security: &runtime.SecurityHint{ + Public: true, }, - Output: runtime.OutputHints{ResponseMediaType: "application/json"}, - Security: &runtime.SecurityHint{Public: true}, }, - { + runtime.CommandSpec{ Group: "Tasks", Use: "list", Short: "List tasks", @@ -87,10 +116,15 @@ var Specs = []runtime.CommandSpec{ Method: "GET", PathTpl: "/tasks", DefaultHostname: "http://127.0.0.1:3000", - Output: runtime.OutputHints{DefaultColumns: []string{"id", "completed", "title"}, ResponseMediaType: "application/json"}, - Security: &runtime.SecurityHint{Public: true}, + Output: runtime.OutputHints{ + DefaultColumns: []string{"id", "completed", "title"}, + ResponseMediaType: "application/json", + }, + Security: &runtime.SecurityHint{ + Public: true, + }, }, - { + runtime.CommandSpec{ Group: "Tasks", Use: "update", Short: "Update a task", @@ -98,15 +132,31 @@ var Specs = []runtime.CommandSpec{ Method: "PATCH", PathTpl: "/tasks/{id}", DefaultHostname: "http://127.0.0.1:3000", - Params: []runtime.ParamSpec{ - {Name: "id", Flag: "id", In: "path", GoType: "string", Help: "id (path, required)", Required: true}, - }, + Params: []runtime.ParamSpec{runtime.ParamSpec{ + Name: "id", + Flag: "id", + In: "path", + GoType: "string", + Help: "id (path, required)", + Required: true, + }}, RequestBody: &runtime.RequestBody{ Required: true, MediaType: "application/json", - Schema: &runtime.SchemaSpec{Type: "object", Properties: map[string]*runtime.SchemaSpec{"completed": &runtime.SchemaSpec{Type: "boolean"}, "title": &runtime.SchemaSpec{Type: "string"}}}, + Schema: &runtime.SchemaSpec{ + Type: "object", + Properties: map[string]*runtime.SchemaSpec{"completed": &runtime.SchemaSpec{ + Type: "boolean", + }, "title": &runtime.SchemaSpec{ + Type: "string", + }}, + }, + }, + Output: runtime.OutputHints{ + ResponseMediaType: "application/json", + }, + Security: &runtime.SecurityHint{ + Public: true, }, - Output: runtime.OutputHints{ResponseMediaType: "application/json"}, - Security: &runtime.SecurityHint{Public: true}, }, } diff --git a/internal/generated/modules_gen.go b/internal/generated/modules_gen.go index 664e93e..90765df 100644 --- a/internal/generated/modules_gen.go +++ b/internal/generated/modules_gen.go @@ -4,19 +4,20 @@ package generated import ( app "example.com/cli-first-python-app/internal/generated/app" + latheruntime "github.com/lathe-cli/lathe/pkg/runtime" "github.com/spf13/cobra" ) -// Mount mounts every generated command and capability declared by codegen. +var latheSourceProvenance = []latheruntime.SourceProvenance{ + {ID: "app", Backend: "openapi3", Kind: "local", RepoURL: "", PinnedTag: "", ResolvedSHA: "", Reproducible: false}, +} + func Mount(root *cobra.Command) error { return MountModules(root) } -// MountModules mounts every module and generated capability under root. -// The import list above is the single source of truth for which modules -// are compiled into this binary. main.go wires this call after -// app.NewApp() so the framework package never imports downstream code. func MountModules(root *cobra.Command) error { + latheruntime.AttachSourceProvenance(root, latheSourceProvenance) if err := app.MountFlat(root); err != nil { return err } diff --git a/skills/appctl/SKILL.md b/skills/appctl/SKILL.md index efd21f7..d5843a4 100644 --- a/skills/appctl/SKILL.md +++ b/skills/appctl/SKILL.md @@ -13,20 +13,22 @@ Use this skill when a user asks you to operate `appctl`, inspect its API command 1. Search for candidates with `appctl search "" --json`; use `--limit` when needed. Search is only candidate discovery. 2. Inspect the exact command with `appctl commands show --json` before executing an unfamiliar command. -3. If the command detail has `auth.required=true`, run `appctl auth status --hostname ` before execution. Use `http.default_hostname` when present unless the user provides `--hostname` or `$APPCTL_HOST`. -4. Execute only after flags, body, auth, HTTP path, and output hints are clear from `commands show`. +3. If the command detail has `auth.required=true`, run `appctl auth status -o json` before execution and read `hostname` and `source`. Host resolution order: `--hostname` > `$APPCTL_HOST` > the selected host (`appctl auth use `) > `http.default_hostname` > the single host in `hosts.yml`. If none applies, stop and ask the user to authenticate or select a host. +4. Execute only after flags, body, auth, HTTP path, `mutation`, `dry_run`, and output hints are clear from `commands show`. When `mutation` is not `read`, preview with `--` if `dry_run.mode` is `http_preview`; if preview is unavailable, obtain explicit user confirmation before execution. ## General Commands - `appctl commands --json`: full generated command catalog. - `appctl commands --include-hidden --json`: include hidden generated commands. - `appctl commands show --json`: source of truth for one command. -- `appctl commands schema --json`: catalog schema version for parser compatibility. +- `appctl commands schema --json`: catalog schema version, surfaces, and dry-run result shape. - `appctl search "" --json`: ranked candidate commands. +- `appctl auth status -o json`: resolved `hostname`, its `source`, the `selected` host, and every logged-in host. ## Maintenance Commands - `appctl --version` or `appctl -v`: print CLI build version. +- `appctl __lathe verify --json`: verify the compiled contract and read `provenance` (schema versions and per-source revision; `reproducible=false` marks local sources). The binary's report wins over this Skill. ## References @@ -37,10 +39,13 @@ Use this skill when a user asks you to operate `appctl`, inspect its API command - Do not guess flags or request body shape from command names. - Do not execute directly from search results; confirm with `commands show` first. +- When `mutation` is not `read`, preview before execution if `dry_run.mode` is `http_preview`. If preview is unavailable, obtain explicit user confirmation before execution. `unknown` is not safe to treat as read. - Prefer `-o json` for machine-readable command output unless the user asks for human-readable output. +- When `output.binary` is present, success output is raw bytes. Pass `-- ` (it refuses existing paths; choose a path the user approved) or `-` only when piping to another program, never into the conversation. `-o` then affects only error output. - With `-o json` or `-o yaml`, branch on `error.code` and process exit status; `error.message` and `error.hint` are safe human guidance, and `error.http.status` is the only optional HTTP context. - A configured stream pause is successful (`exit 0`); inspect the collected output field mapped from the pause event instead of treating it as an error. - For collected streams, choose one mode: `-o json` for one stable document, `--stream` in the default output mode when catalog `output.streaming.policy.live` is present, or `-o raw` for wire events. - Use `--file`, `--set`, or `--set-str` for JSON request bodies according to `commands show` body requirements. -- When `body.runtime_schema` is present, normal execution fetches and validates against that schema before the target request; `--dry-run` stays network-free and skips this preflight. +- JSON `body.schema` validates compiled types, nullable values, nested properties/items, required fields, and resolved `allOf` structure before HTTP execution, including workflows and dry-run. Template bodies validate the payload at `body.merge_path`. Enum, format, unions, additional properties, and unresolved references are not validated by this static preflight. +- When `body.runtime_schema` is present, normal execution fetches and validates against that schema before the target request; an `http_preview` dry-run stays network-free and skips this preflight. - For sensitive flags, prefer safe modes from `flags[].input_modes`: `---env`, `---file`, or `---stdin`. diff --git a/skills/appctl/references/catalog.md b/skills/appctl/references/catalog.md index 0ea8f77..4385162 100644 --- a/skills/appctl/references/catalog.md +++ b/skills/appctl/references/catalog.md @@ -15,23 +15,25 @@ Key fields: - `path`: command path to pass to `commands show` or execute after the CLI name. - `shortcuts`: root-level commands that execute the same operation with preset flag values. - `http`: HTTP method and path template. -- `http.default_hostname`: optional source-level host selected after explicit `--hostname` and `$APPCTL_HOST`; when present it is used before the single-host fallback from `hosts.yml`. +- `http.default_hostname`: optional source-level host used after `--hostname`, `$APPCTL_HOST`, and the host selected with `auth use`, and before the single-host fallback from `hosts.yml`. - `flags`: CLI flags, parameter location, type, required state, defaults, enum values, format, input modes, and help. - `body`: request body requirement, media type, and optional `runtime_schema` preflight source, including its active-context prerequisites. -- `auth`: whether auth is required and which scopes are declared. +- `auth`: `required`, declared `scopes`, and `requirements` — any one entry satisfies the command, every scheme inside an entry is required, `{}` allows anonymous access; execution fails with `not_authenticated` before sending when the stored credential plus header and query parameters satisfy no entry. +- `mutation`: `read`, `write`, or `unknown`. Do not infer write vs read from the HTTP method alone. Treat any value other than `read` as requiring preview when `dry_run.mode` is `http_preview`, or explicit user confirmation when preview is unavailable. +- `dry_run`: preview contract. `http_preview` is declared only when the generated runner can preview; use `--` and print the resolved HTTP request JSON. `unsupported` means the command has no preview, including all workflow commands. A `--dry-run` flag on a custom command is not a preview contract. - `examples`: runnable examples with optional body shape, output hints, and follow-up commands. -- `output`: list path, default columns, response media type, pagination, and streaming hints; a streaming policy describes collection, terminal outcomes, and optional live projection. +- `output`: list path, default columns, response media type, pagination, streaming hints, and `binary.flag` for binary downloads; a streaming policy describes collection, terminal outcomes, and optional live projection. - `flags[].context`: an optional account-scoped default with explicit flag, declared environment, then stored-value precedence. - `sets_context`: a successful operation persists its declared parameter as the selected host's active context. - `notes`, `prerequisites`, and `known_errors`: overlay-provided operation context that is not inferred from the API spec. ## Command Detail -Run `appctl commands show --json` before executing an unfamiliar command. This is the source of truth for flags, body, auth, HTTP path, and output hints. +Run `appctl commands show --json` before executing an unfamiliar command. This is the source of truth for flags, body, auth, HTTP path, `mutation`, `dry_run`, and output hints. ## Schema -Run `appctl commands schema --json` to read the catalog schema version before parsing catalog JSON with durable tooling. +Run `appctl commands schema --json` to read `catalog_schema_version`, `surfaces`, and `dry_run.result` before parsing catalog JSON with durable tooling. `dry_run.result=http_preview` means a preview prints the resolved HTTP request JSON (`method`, `url`, `hostname`, `host_source`, `headers`, `body`, `auth`, `output`). ## Sensitive Flags @@ -50,14 +52,18 @@ When a flag entry has `input_modes`, prefer safe modes over putting secrets dire - `--set key.path=value`: build JSON with type inference for booleans, null, integers, and floats. - `--set-str key.path=value`: build JSON while forcing the value to remain a string. -If `body.runtime_schema` is present, normal execution fetches that JSON Schema and validates the body before the target request. `--dry-run` does not fetch it. +JSON `body.schema` validates compiled types, nullable values, nested properties/items, required fields, and resolved `allOf` structure before sending, including during dry-run and workflows. For templates, it validates `body.merge_path`. Enum, format, unions, additional properties, and unresolved references are outside this static preflight. Failures exit 2 and identify the body path without body values. + +If `body.runtime_schema` is present, normal execution fetches that JSON Schema and validates the body before the target request. An `http_preview` dry-run does not fetch it. ## Output Use `-o json` for machine-readable command output. Other supported formats are `table`, `yaml`, and `raw`. For collected streams, choose one mode: JSON or YAML for one document, `--stream` in the default output mode when `output.streaming.policy.live` is present, or raw for wire events. +When `output.binary` is present, success output is raw bytes. Pass `-- ` (it refuses existing paths; choose a path the user approved) or `-` only when piping to another program, never into the conversation. `-o` then affects only error output. + On a non-zero exit with JSON or YAML output, read `error.code`, `error.message`, and `error.hint`; optional `error.http` contains only `status`. A configured pause exits zero and is represented by the field mapping in its stream collection policy. ## Auth -If command detail returns `auth.required=true`, run `appctl auth status --hostname ` before execution. Use `http.default_hostname` when present unless the user provides `--hostname` or `$APPCTL_HOST`; if no matching host is logged in, stop and ask the user to authenticate. +If command detail returns `auth.required=true`, run `appctl auth status -o json` before execution and read `hostname` and `source`. Host resolution order: `--hostname` > `$APPCTL_HOST` > the selected host (`appctl auth use `) > `http.default_hostname` > the single host in `hosts.yml`. When more than one host is logged in and the host was chosen implicitly, the CLI also prints a `current host: ` line on stderr; read provenance from `auth status`, not from that line. If no matching host is logged in, stop and ask the user to authenticate. Map that status to declared schemes: bearer satisfies http bearer, oauth2, and openIdConnect; basic satisfies http basic; apikey satisfies an apiKey header whose name is `param`, or any http scheme when stored for the `Authorization` header; header or query parameters passed to the command can supply apiKey schemes. diff --git a/skills/appctl/references/modules/app.md b/skills/appctl/references/modules/app.md index 544bfaa..6c6cc39 100644 --- a/skills/appctl/references/modules/app.md +++ b/skills/appctl/references/modules/app.md @@ -4,9 +4,9 @@ - Backend: `openapi3` - Default hostname: `http://127.0.0.1:3000` -- Repository: `unknown` -- Pinned tag: ``unknown`` +- Source kind: `local` - Files: `openapi.yaml` +- Reproducible: no — follows the local working tree; no immutable revision recorded ## Health From 799182215c64e6ff9b250d4a385d1d731bb49948 Mon Sep 17 00:00:00 2001 From: samzong Date: Sun, 4 Oct 2026 00:59:26 -0400 Subject: [PATCH 2/2] docs: require go 1.26 to match lathe v0.6.3 Signed-off-by: samzong --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 58b8d4a..a433aea 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A minimal Python application where the CLI is ready on day one. Change the application, update OpenAPI, and test through the generated `appctl` CLI. -Requires Python 3.14+, uv, and Go 1.25+. +Requires Python 3.14+, uv, and Go 1.26+. ```sh uv sync