Check PromptKit selector collisions against installed flags
This commit is contained in:
@@ -245,7 +245,7 @@ Both commands require one ID operand and accept `--config`, `--overlay`, and
|
||||
`--format text|yaml` (default `text`). Prompt inspection also accepts singleton
|
||||
`--version`, defaulting to the empty string. PromptKit owns version selection:
|
||||
an omitted version works for a single-version ID, while an ambiguous ID is an
|
||||
operational error. These commands do not accept `--scope`; their namespace is
|
||||
operational error. These commands have no built-in `--scope` selection; their namespace is
|
||||
`promptkit`.
|
||||
|
||||
Prompt YAML has these fields. Input order is preserved and an empty input list is
|
||||
|
||||
@@ -69,6 +69,9 @@ architecture. The issues below affect behavior within the implemented structure.
|
||||
- R-08 complete: unknown scopes and absent default app scopes report available
|
||||
choices in standard scope order without runtime acquisition.
|
||||
|
||||
- R-09 complete: PromptKit selector collisions are checked against installed
|
||||
standard flags; profile-only `version` and custom `scope` selectors are usable.
|
||||
|
||||
## Findings and priority
|
||||
|
||||
P1 means a supported invocation is blocked or its report content is corrupted.
|
||||
|
||||
@@ -128,6 +128,10 @@ func RegisterPromptKit[C any](app *App, suite PromptKitSuite[C]) error {
|
||||
func normalizePromptKitSuite[C any](suite PromptKitSuite[C]) (PromptKitSuite[C], error) {
|
||||
normalized := suite
|
||||
normalized.Selectors = append([]Selector[C](nil), suite.Selectors...)
|
||||
standardNames := map[string]bool{"help": true}
|
||||
for _, flag := range promptKitInputFlags(PromptKitSuite[C]{}, suite.InspectPrompt != nil) {
|
||||
standardNames[flag.binding.meta.Name] = true
|
||||
}
|
||||
seenNames := make(map[string]struct{}, len(normalized.Selectors))
|
||||
seenShorts := make(map[string]struct{}, len(normalized.Selectors))
|
||||
for i, selector := range normalized.Selectors {
|
||||
@@ -139,8 +143,7 @@ func normalizePromptKitSuite[C any](suite PromptKitSuite[C]) (PromptKitSuite[C],
|
||||
if !validFlagName(meta.Name) {
|
||||
return PromptKitSuite[C]{}, fmt.Errorf("PromptKit selector flag name %q is invalid", meta.Name)
|
||||
}
|
||||
switch meta.Name {
|
||||
case "config", "overlay", "scope", "format", "version", "help":
|
||||
if standardNames[meta.Name] {
|
||||
return PromptKitSuite[C]{}, fmt.Errorf("PromptKit selector flag --%s conflicts with a standard flag", meta.Name)
|
||||
}
|
||||
if meta.Short == "h" {
|
||||
|
||||
@@ -404,3 +404,47 @@ func sortedKeys(values map[string]any) []string {
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func TestPromptKitSelectorNamesCollideOnlyWithInstalledFlags(t *testing.T) {
|
||||
for _, name := range []string{"version", "scope"} {
|
||||
var captured commandkit.Supplied[string]
|
||||
suite := commandkit.PromptKitSuite[commandkit.Supplied[string]]{
|
||||
Selectors: []commandkit.Selector[commandkit.Supplied[string]]{{Binding: commandkit.ValueFlag[commandkit.Supplied[string]](commandkit.FlagMeta{Name: name}, nil, func(s string) (string, error) { return s, nil }, func(c *commandkit.Supplied[string], v commandkit.Supplied[string]) { *c = v })}},
|
||||
InspectProfile: func(_ context.Context, _ commandkit.Selection, c commandkit.Supplied[string], _ string) (commandkit.Acquired[*promptkit.ProfileInspection], error) {
|
||||
captured = c
|
||||
return commandkit.Acquired[*promptkit.ProfileInspection]{Value: &promptkit.ProfileInspection{}}, nil
|
||||
},
|
||||
}
|
||||
app, _ := commandkit.New(commandkit.Identity{Name: "example"})
|
||||
if err := commandkit.RegisterPromptKit(app, suite); err != nil {
|
||||
t.Fatalf("%s: %v", name, err)
|
||||
}
|
||||
result := app.Execute(context.Background(), []string{"promptkit", "show", "profile", "example", "--" + name, "selected"}, commandkit.Streams{})
|
||||
if result.Kind != commandkit.OutcomeSuccess || captured != (commandkit.Supplied[string]{Value: "selected", Set: true}) {
|
||||
t.Fatalf("%s: result=%#v captured=%#v", name, result, captured)
|
||||
}
|
||||
suite.InspectPrompt = func(context.Context, commandkit.Selection, commandkit.Supplied[string], string, string) (commandkit.Acquired[*promptkit.PromptInspection], error) {
|
||||
return commandkit.Acquired[*promptkit.PromptInspection]{Value: &promptkit.PromptInspection{}}, nil
|
||||
}
|
||||
other, _ := commandkit.New(commandkit.Identity{Name: "example"})
|
||||
err := commandkit.RegisterPromptKit(other, suite)
|
||||
if name == "version" && err == nil {
|
||||
t.Fatal("accepted collision with installed prompt version flag")
|
||||
}
|
||||
if name == "scope" && err != nil {
|
||||
t.Fatalf("scope selector conflicts with no installed flag: %v", err)
|
||||
}
|
||||
}
|
||||
for _, name := range []string{"config", "overlay", "format", "help"} {
|
||||
app, _ := commandkit.New(commandkit.Identity{Name: "example"})
|
||||
suite := commandkit.PromptKitSuite[struct{}]{
|
||||
InspectProfile: func(context.Context, commandkit.Selection, struct{}, string) (commandkit.Acquired[*promptkit.ProfileInspection], error) {
|
||||
return commandkit.Acquired[*promptkit.ProfileInspection]{}, nil
|
||||
},
|
||||
Selectors: []commandkit.Selector[struct{}]{{Binding: commandkit.ValueFlag[struct{}](commandkit.FlagMeta{Name: name}, nil, func(s string) (string, error) { return s, nil }, func(*struct{}, commandkit.Supplied[string]) {})}},
|
||||
}
|
||||
if err := commandkit.RegisterPromptKit(app, suite); err == nil {
|
||||
t.Fatalf("accepted standard flag collision --%s", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user