From 20b994398daaf63760b672adb8fbdcca05dfe273 Mon Sep 17 00:00:00 2001 From: Ernesto Ongaro Date: Fri, 28 Aug 2026 13:58:28 +0100 Subject: [PATCH 1/2] fix(schema): spell --schema flags and placeholders the way --help does --schema still rendered query-param flags and positional placeholders with slugify() ("--basemodelid", "") after #77 and #80 moved the flag registry, usage line and Arguments block to canonicalName() ("--base-model-id", ""). An agent reading --schema was told a flag name that no longer appears anywhere else in the CLI. Use canonicalName in both places and assert the kebab-case spelling in the bodyless-GET schema test. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Ei8UYaG1bW5PJhk93EaqzA --- internal/openapi/schema.go | 4 ++-- internal/openapi/schema_test.go | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/internal/openapi/schema.go b/internal/openapi/schema.go index b28b609..079916b 100644 --- a/internal/openapi/schema.go +++ b/internal/openapi/schema.go @@ -354,14 +354,14 @@ func describeBody(op *operationInfo, field string, maxDepth int, names SchemaFla for _, p := range op.PathParams { doc.Args = append(doc.Args, SchemaArg{ Name: p.Name, - Placeholder: "<" + slugify(p.Name) + ">", + Placeholder: "<" + canonicalName(p.Name) + ">", Type: p.Type, Description: p.Description, }) } for _, q := range op.QueryParams { doc.QueryParams = append(doc.QueryParams, SchemaQueryParam{ - Flag: "--" + slugify(q.Name), + Flag: "--" + canonicalName(q.Name), Name: q.Name, Type: q.Type, Enum: q.Enum, diff --git a/internal/openapi/schema_test.go b/internal/openapi/schema_test.go index dcaef88..7d227d5 100644 --- a/internal/openapi/schema_test.go +++ b/internal/openapi/schema_test.go @@ -622,9 +622,14 @@ func TestSchema_BodylessGetDescribesArgsAndQueryParams(t *testing.T) { t.Fatalf("args = %v, want 1 entry", doc.Args) } arg := doc.Args[0] - if arg.Name != "widgetId" || arg.Placeholder != "" || arg.Type != "string" || arg.Description != "Widget UUID" { + if arg.Name != "widgetId" || arg.Placeholder != "" || arg.Type != "string" || arg.Description != "Widget UUID" { t.Errorf("arg = %+v, want the widgetId path param", arg) } + // The placeholder and flag spellings must match what --help and the usage + // line print (canonicalName), not the older bare-lowercase slugify form. + if strings.Contains(arg.Placeholder, "widgetid") { + t.Errorf("placeholder %q uses the old slugify spelling; want kebab-case", arg.Placeholder) + } byFlag := map[string]SchemaQueryParam{} for _, q := range doc.QueryParams { From 9a7e9756e386b8057d1f9e8c5d032a6300a7d597 Mon Sep 17 00:00:00 2001 From: Ernesto Ongaro Date: Fri, 28 Aug 2026 15:06:11 +0100 Subject: [PATCH 2/2] fix(schema): report query flags through resolveQueryFlags canonicalName alone still disagreed with the CLI when flag registration renames a collision: a "baseUrl" param registers as --param-base-url (the global --base-url is reserved) and a second param canonicalising to the same name gets a -2 suffix. Iterate resolveQueryFlags(op) so --schema advertises exactly the flags the command accepts, and add a test that covers both rename paths and checks each advertised flag is registered. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Ei8UYaG1bW5PJhk93EaqzA --- internal/openapi/schema.go | 7 +++-- internal/openapi/schema_test.go | 52 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/internal/openapi/schema.go b/internal/openapi/schema.go index 079916b..e2d4a56 100644 --- a/internal/openapi/schema.go +++ b/internal/openapi/schema.go @@ -359,9 +359,12 @@ func describeBody(op *operationInfo, field string, maxDepth int, names SchemaFla Description: p.Description, }) } - for _, q := range op.QueryParams { + // Use the same resolution as flag registration so collision renames + // (--param-base-url, --foo-2) are reported exactly as the CLI accepts them. + for _, qf := range resolveQueryFlags(op) { + q := qf.Param doc.QueryParams = append(doc.QueryParams, SchemaQueryParam{ - Flag: "--" + canonicalName(q.Name), + Flag: "--" + qf.Name, Name: q.Name, Type: q.Type, Enum: q.Enum, diff --git a/internal/openapi/schema_test.go b/internal/openapi/schema_test.go index 7d227d5..5e9a2a6 100644 --- a/internal/openapi/schema_test.go +++ b/internal/openapi/schema_test.go @@ -1137,3 +1137,55 @@ func TestSchema_StaticDepthLimitMatchesDescriber(t *testing.T) { t.Errorf("static truncation = %s, describer truncation = %s", got, want) } } + +// collidingQueryTestSpec has query params whose canonical flag names collide: +// "baseUrl" with the global --base-url, and "branchId"/"branch_id" with each +// other. Flag registration renames these (see resolveQueryFlags); --schema must +// advertise the renamed flags, not the bare canonical spelling. +const collidingQueryTestSpec = `{ + "openapi": "3.1.0", + "info": {"title": "test", "version": "1.0"}, + "paths": { + "/api/v1/widgets": { + "get": { + "operationId": "widgetsList", + "tags": ["widgets"], + "parameters": [ + {"name": "baseUrl", "in": "query", "description": "Filter by URL", "schema": {"type": "string"}}, + {"name": "branchId", "in": "query", "description": "Branch (camel)", "schema": {"type": "string"}}, + {"name": "branch_id", "in": "query", "description": "Branch (snake)", "schema": {"type": "string"}} + ], + "responses": {"200": {"description": "ok"}} + } + } + } +}` + +func TestSchema_QueryFlagsReflectCollisionRenames(t *testing.T) { + doc, err := runSchemaCmd(t, collidingQueryTestSpec, "list") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + got := map[string]string{} + for _, q := range doc.QueryParams { + got[q.Name] = q.Flag + } + want := map[string]string{ + "baseUrl": "--param-base-url", + "branchId": "--branch-id", + "branch_id": "--branch-id-2", + } + for name, flag := range want { + if got[name] != flag { + t.Errorf("query param %q advertised as %q, want %q (the flag the CLI actually registers)", name, got[name], flag) + } + } + + // And the advertised flags must all be accepted by the command itself. + cmd := subcommand(t, collidingQueryTestSpec, "list") + for _, flag := range want { + if cmd.Flags().Lookup(strings.TrimPrefix(flag, "--")) == nil { + t.Errorf("--schema advertises %s but the command does not register it", flag) + } + } +}