From 6ab8d7039101ff92c4f1dd72f265d7b6898dd6ae Mon Sep 17 00:00:00 2001 From: Josh Holtz Date: Wed, 9 Sep 2026 09:32:52 -0500 Subject: [PATCH] fix(schema): error on unknown command names, accept colon capability IDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cobra's Find falls back to the deepest match instead of failing, so an unknown name silently returned the root schema with exit 0 — an agent pasting a capability ID from rc commands (apps:create) got a valid-looking empty schema. Colon IDs now resolve as paths, and anything unresolved is a hard error. Co-Authored-By: Claude Fable 5 --- internal/cli/schema.go | 16 +++++++++++++++- internal/cli/schema_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/internal/cli/schema.go b/internal/cli/schema.go index a88c52b0..15d68be4 100644 --- a/internal/cli/schema.go +++ b/internal/cli/schema.go @@ -1,6 +1,7 @@ package cli import ( + "fmt" "strings" "github.com/spf13/cobra" @@ -29,10 +30,23 @@ Use this from an agent rather than scraping the human --help output.`, Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { rt := RuntimeFrom(cmd.Context()) - target, _, err := root.Find(args) + // `rc commands` emits colon-form capability IDs (apps:create), so + // accept them here as if space-separated. + tokens := make([]string, 0, len(args)) + for _, a := range args { + tokens = append(tokens, strings.Split(a, ":")...) + } + target, rest, err := root.Find(tokens) if err != nil { return err } + // Find never fails on unknown names; it stops at the deepest match + // and returns the leftovers, which for a pure command path means + // the name didn't resolve. Without this check an unknown name + // silently yields the root schema. + if len(rest) > 0 { + return fmt.Errorf("unknown command %q — run `rc commands` to list valid names", strings.Join(args, " ")) + } return rt.Out.RenderJSON(commandSchema(target)) }, } diff --git a/internal/cli/schema_test.go b/internal/cli/schema_test.go index f5b9da84..edfaf6a6 100644 --- a/internal/cli/schema_test.go +++ b/internal/cli/schema_test.go @@ -135,3 +135,27 @@ func hasRunnableDescendant(c *cobra.Command) bool { } return false } + +func TestSchemaUnknownCommandErrors(t *testing.T) { + root := NewRootCmd("test") + root.SetArgs([]string{"schema", "apps:bogus"}) + root.SetOut(&strings.Builder{}) + root.SetErr(&strings.Builder{}) + if err := root.Execute(); err == nil { + t.Fatal("expected an error for an unknown command name, got nil") + } +} + +func TestSchemaAcceptsColonCapabilityIDs(t *testing.T) { + root := NewRootCmd("test") + var out strings.Builder + root.SetArgs([]string{"schema", "apps:create"}) + root.SetOut(&out) + root.SetErr(&strings.Builder{}) + if err := root.Execute(); err != nil { + t.Fatalf("schema apps:create: %v", err) + } + if !strings.Contains(out.String(), `"name": "create"`) { + t.Fatalf("expected the apps create schema, got: %s", out.String()) + } +}