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
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

cli-sync:
cp cli.yaml cmd/appctl/cli.yaml
go run github.com/lathe-cli/lathe/cmd/lathe@v0.4.4 bootstrap
go run github.com/lathe-cli/lathe/cmd/lathe@v0.5.3-0.20260816043253-e33580932a42 bootstrap
go mod tidy

cli-build:
Expand All @@ -14,4 +14,3 @@ test: cli-build
check: cli-sync test
uv run --locked python -m compileall -q src tests
go vet ./...

11 changes: 7 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
module example.com/cli-first-python-app

go 1.25.7
go 1.25.13

require (
github.com/lathe-cli/lathe v0.4.4
github.com/lathe-cli/lathe v0.5.3-0.20260816043253-e33580932a42
github.com/spf13/cobra v1.10.2
)

require (
github.com/gofrs/flock v0.13.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 // indirect
github.com/spf13/pflag v1.0.9 // indirect
golang.org/x/sys v0.46.0 // indirect
golang.org/x/term v0.44.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/term v0.45.0 // indirect
golang.org/x/text v0.32.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
35 changes: 28 additions & 7 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
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/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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/lathe-cli/lathe v0.4.4 h1:5TNnLpLccH7YAxt3n1Lv5GOrf4i+U264BGvb3v2Haws=
github.com/lathe-cli/lathe v0.4.4/go.mod h1:R7LnEOMjrGHDutF3l2VyY5yc7AAoKLsFSj4JeSuLJRc=
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/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=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
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=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
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=
11 changes: 5 additions & 6 deletions internal/generated/app/app_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion internal/generated/modules_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions skills/appctl/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,9 @@ 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.
- Prefer `-o json` for machine-readable command output unless the user asks for human-readable 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.
- For sensitive flags, prefer safe modes from `flags[].input_modes`: `--<flag>-env`, `--<flag>-file`, or `--<flag>-stdin`.
12 changes: 9 additions & 3 deletions skills/appctl/references/catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ Key fields:
- `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`.
- `flags`: CLI flags, parameter location, type, required state, defaults, enum values, format, input modes, and help.
- `body`: request body requirement and media type.
- `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.
- `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.
- `output`: list path, default columns, response media type, pagination, and streaming hints; 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
Expand Down Expand Up @@ -48,9 +50,13 @@ 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.

## Output

Use `-o json` for machine-readable command output. Other supported formats are `table`, `yaml`, and `raw`.
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.

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

Expand Down
2 changes: 1 addition & 1 deletion skills/appctl/references/modules/app.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
- Auth: public
- Body: required; media type `application/json`
- Flags: none
- Output: response media `application/json`

### `appctl tasks delete`

Expand Down Expand Up @@ -66,4 +67,3 @@
- Flags:
- `--id` (path, required): id
- Output: response media `application/json`

9 changes: 4 additions & 5 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def cli_failure(self, *args):
text=True,
)
self.assertNotEqual(result.returncode, 0)
return json.loads(result.stderr)["error"]["message"]
return json.loads(result.stderr)["error"]

def test_generated_cli_is_the_application_acceptance_surface(self):
self.assertEqual(self.cli("health", "get"), {"status": "ok"})
Expand All @@ -54,11 +54,11 @@ def test_generated_cli_is_the_application_acceptance_surface(self):
self.assertEqual(self.cli("tasks", "list"), [])

def test_generated_cli_surfaces_api_errors(self):
self.assertIn("HTTP 400", self.cli_failure("tasks", "create", "--set-str", "title="))
self.assertIn("HTTP 404", self.cli_failure("tasks", "get", "--id", "missing"))
self.assertEqual(self.cli_failure("tasks", "create", "--set-str", "title=")["http"]["status"], 400)
self.assertEqual(self.cli_failure("tasks", "get", "--id", "missing")["http"]["status"], 404)
task = self.cli("tasks", "create", "--set", "title=Keep the contract honest")
error = self.cli_failure("tasks", "update", "--id", task["id"], "--file", "test/empty.json")
self.assertIn("title or completed is required", error)
self.assertEqual(error["http"]["status"], 400)

def test_http_boundary_rejects_unsupported_input(self):
status, headers, _ = self.request("PUT", "/tasks")
Expand Down Expand Up @@ -88,4 +88,3 @@ def request(self, method, path, content_type=None, body=None):

if __name__ == "__main__":
unittest.main()

Loading