Add filtered PromptKit catalog commands

This commit is contained in:
2026-10-17 18:45:39 +00:00
parent dc4261a1ca
commit 504ea03574
3 changed files with 195 additions and 14 deletions

View File

@@ -24,10 +24,14 @@ type definitionInput[I any] struct {
}
type catalogInput[I any] struct {
selection ckconfig.Selection
format string
captured commandkit.FlagValues[I]
selected I
selection ckconfig.Selection
namespace commandkit.Supplied[string]
allNamespaces commandkit.Supplied[bool]
kinds []promptkit.ResourceKind
filter promptkit.NamespaceFilter
format string
captured commandkit.FlagValues[I]
selected I
}
type requirementsInput[I any] struct {
@@ -48,7 +52,7 @@ type inputAssignment struct {
path string
}
// Command captures the optional PromptKit catalog, named inspection,
// Command captures the optional PromptKit catalogs, named inspection,
// requirements discovery, and concrete request validation callbacks and adds
// only supplied operations. Discovery and validation share the
// `requirements` group and remain independently optional. An absent suite
@@ -63,7 +67,7 @@ type inputAssignment struct {
// render YAML with `yaml` as the only format and default. Named-show formats are
// unchanged.
func Command[I, S any](suite Suite[I, S]) commandkit.Command {
if suite.InspectPrompt == nil && suite.InspectProfile == nil && suite.InspectRequirements == nil && suite.ValidateRequest == nil && suite.ListPrompts == nil && suite.ListProfiles == nil {
if suite.InspectPrompt == nil && suite.InspectProfile == nil && suite.InspectRequirements == nil && suite.ValidateRequest == nil && suite.ListPrompts == nil && suite.ListProfiles == nil && suite.ListBackends == nil && suite.ListNamespaces == nil && suite.ListCollections == nil {
command := commandkit.NoCommand()
for _, issue := range suite.issues {
command = commandkit.WithError(command, issue)
@@ -83,12 +87,21 @@ func Command[I, S any](suite Suite[I, S]) commandkit.Command {
if len(showCommands) != 0 {
rootCommands = append(rootCommands, commandkit.Group("show", "Show a named PromptKit definition.", showCommands...))
}
listCommands := make([]commandkit.Command, 0, 2)
listCommands := make([]commandkit.Command, 0, 5)
if suite.ListPrompts != nil {
listCommands = append(listCommands, newCatalogListCommand(suite, "prompts", true))
listCommands = append(listCommands, newCatalogListCommand(suite, "prompts"))
}
if suite.ListProfiles != nil {
listCommands = append(listCommands, newCatalogListCommand(suite, "profiles", false))
listCommands = append(listCommands, newCatalogListCommand(suite, "profiles"))
}
if suite.ListBackends != nil {
listCommands = append(listCommands, newCatalogListCommand(suite, "backends"))
}
if suite.ListNamespaces != nil {
listCommands = append(listCommands, newCatalogListCommand(suite, "namespaces"))
}
if suite.ListCollections != nil {
listCommands = append(listCommands, newCatalogListCommand(suite, "collections"))
}
if len(listCommands) != 0 {
rootCommands = append(rootCommands, commandkit.Group("list", "List PromptKit catalogs.", listCommands...))
@@ -110,7 +123,7 @@ func Command[I, S any](suite Suite[I, S]) commandkit.Command {
return command
}
func newCatalogListCommand[I, S any](suite Suite[I, S], name string, prompts bool) commandkit.Command {
func newCatalogListCommand[I, S any](suite Suite[I, S], name string) commandkit.Command {
flags := commandkit.CaptureFlags(suite.Selectors, func(input **catalogInput[I]) *commandkit.FlagValues[I] {
if input == nil || *input == nil {
return nil
@@ -125,6 +138,23 @@ func newCatalogListCommand[I, S any](suite Suite[I, S], name string, prompts boo
commandkit.ValueFlag(commandkit.FlagMeta{Name: "format", Usage: "Catalog format: yaml."}, stringDefault("yaml"), parseDefinitionString,
func(input **catalogInput[I], value commandkit.Supplied[string]) { (*input).format = value.Value }),
)
if name == "prompts" || name == "profiles" || name == "backends" {
flags = append(flags,
commandkit.ValueFlag(commandkit.FlagMeta{Name: "namespace", Usage: "Select one native namespace."}, (*string)(nil), parseDefinitionString,
func(input **catalogInput[I], value commandkit.Supplied[string]) { (*input).namespace = value }),
commandkit.BoolFlag(commandkit.FlagMeta{Name: "all-namespaces", Usage: "Select identities from every namespace."}, false,
func(input **catalogInput[I], value commandkit.Supplied[bool]) { (*input).allNamespaces = value }),
)
}
if name == "namespaces" {
flags = append(flags,
commandkit.RepeatFlag(commandkit.FlagMeta{Name: "kind", Usage: "Resource kind to include."}, []promptkit.ResourceKind(nil),
func(value string) (promptkit.ResourceKind, error) { return promptkit.ResourceKind(value), nil },
func(input **catalogInput[I], value commandkit.Supplied[[]promptkit.ResourceKind]) {
(*input).kinds = value.Value
}),
)
}
return commandkit.NewCommand(commandkit.CommandSpec[*catalogInput[I]]{
Name: name,
Summary: "List PromptKit " + name + ".",
@@ -139,6 +169,9 @@ func newCatalogListCommand[I, S any](suite Suite[I, S], name string, prompts boo
if err := checkCatalogFormat(input.format); err != nil {
return err
}
if err := prepareCatalogFilter(input, name); err != nil {
return err
}
selected, err := prepareCatalogInput(suite, input)
if err != nil {
return err
@@ -147,14 +180,53 @@ func newCatalogListCommand[I, S any](suite Suite[I, S], name string, prompts boo
return nil
},
Run: func(invocation commandkit.Invocation, input *catalogInput[I]) commandkit.Outcome {
if prompts {
switch name {
case "prompts":
return runCatalogList(invocation, suite, input, suite.ListPrompts, encodePromptCatalog)
case "profiles":
return runCatalogList(invocation, suite, input, suite.ListProfiles, encodeProfileCatalog)
case "backends":
return runCatalogList(invocation, suite, input, suite.ListBackends, encodeProfileCatalog)
case "namespaces":
list := func(ctx context.Context, request CatalogRequest, value I, state S) (commandkit.Acquired[[]string], error) {
return suite.ListNamespaces(ctx, NamespaceRequest{Selection: request.Selection, Kinds: input.kinds}, value, state)
}
return runCatalogList(invocation, suite, input, list, encodeProfileCatalog)
case "collections":
list := func(ctx context.Context, request CatalogRequest, value I, state S) (commandkit.Acquired[[]promptkit.CollectionInfo], error) {
return suite.ListCollections(ctx, request.Selection, value, state)
}
return runCatalogList(invocation, suite, input, list, encodeCollectionCatalog)
default:
return definitionFailure(fmt.Errorf("unknown PromptKit catalog %q", name), nil)
}
return runCatalogList(invocation, suite, input, suite.ListProfiles, encodeProfileCatalog)
},
})
}
func prepareCatalogFilter[I any](input *catalogInput[I], name string) error {
input.filter = promptkit.NamespaceFilter{Mode: promptkit.NamespaceDefault}
if name != "prompts" && name != "profiles" && name != "backends" {
return nil
}
if input.allNamespaces.Set && input.allNamespaces.Value && input.namespace.Set {
return errors.New("--namespace cannot be combined with --all-namespaces")
}
if input.allNamespaces.Set && input.allNamespaces.Value {
input.filter.Mode = promptkit.NamespaceAll
return nil
}
if input.namespace.Set {
if input.namespace.Value == "" {
input.filter.Mode = promptkit.NamespaceUnqualified
} else {
input.filter.Mode = promptkit.NamespaceNamed
input.filter.Name = input.namespace.Value
}
}
return nil
}
func checkCatalogFormat(format string) error {
if format != "yaml" {
return fmt.Errorf("unsupported catalog format %q", format)
@@ -328,7 +400,7 @@ func runCatalogList[T any, I, S any](
}
}
acquired, err := list(invocation.Context, CatalogRequest{Selection: input.selection}, input.selected, state)
acquired, err := list(invocation.Context, CatalogRequest{Selection: input.selection, Filter: input.filter}, input.selected, state)
if err != nil {
return definitionFailure(err, acquired.Diagnostics)
}
@@ -675,6 +747,19 @@ func encodeProfileCatalog(profiles []string) ([]byte, error) {
return encodePromptYAMLBytes(profiles)
}
type collectionCatalogEntry struct {
Namespace string `yaml:"namespace"`
Version *string `yaml:"version,omitempty"`
}
func encodeCollectionCatalog(collections []promptkit.CollectionInfo) ([]byte, error) {
entries := make([]collectionCatalogEntry, len(collections))
for index, collection := range collections {
entries[index] = collectionCatalogEntry{Namespace: collection.Namespace, Version: collection.ReleaseVersion}
}
return encodePromptYAMLBytes(entries)
}
func encodePromptYAMLBytes(value any) ([]byte, error) {
var output bytes.Buffer
if err := encodePromptYAML(&output, value); err != nil {