feat(core): plugin display_name + description; CLI prompts; SDK fields

This commit is contained in:
Alex Dunmow 2026-06-03 11:31:23 +08:00
parent dae3aa918a
commit 041a7c2e3f
4 changed files with 61 additions and 7 deletions

View File

@ -76,6 +76,23 @@ func newPluginInitCmd() *cobra.Command {
return fmt.Errorf("plugin name is required") return fmt.Errorf("plugin name is required")
} }
} }
rawName := name
name = strings.ToLower(name)
fmt.Printf("Display name [%s]: ", rawName)
displayName := ""
if scanner.Scan() {
displayName = strings.TrimSpace(scanner.Text())
}
if displayName == "" {
displayName = rawName
}
fmt.Print("Description (one-line summary, optional): ")
description := ""
if scanner.Scan() {
description = strings.TrimSpace(scanner.Text())
}
kind, err := promptKind(scanner) kind, err := promptKind(scanner)
if err != nil { if err != nil {
@ -92,7 +109,8 @@ func newPluginInitCmd() *cobra.Command {
if _, err := cli.Reg.CreatePlugin(ctx, connect.NewRequest(&v1.CreatePluginRequest{ if _, err := cli.Reg.CreatePlugin(ctx, connect.NewRequest(&v1.CreatePluginRequest{
ScopeSlug: scopeAPISlug(scope), ScopeSlug: scopeAPISlug(scope),
Name: name, Name: name,
Description: "", DisplayName: displayName,
Description: description,
Kind: kind, Kind: kind,
Categories: cats, Categories: cats,
})); err != nil { })); err != nil {
@ -100,7 +118,7 @@ func newPluginInitCmd() *cobra.Command {
} }
fmt.Printf("\nCreated %s/%s\n", scope, name) fmt.Printf("\nCreated %s/%s\n", scope, name)
if err := upsertPluginMod(scope, name, kind, cats); err != nil { if err := upsertPluginMod(scope, name, displayName, description, kind, cats); err != nil {
return err return err
} }
fmt.Println("plugin.mod updated") fmt.Println("plugin.mod updated")
@ -576,7 +594,7 @@ func submodulePaths(repoDir string) []string {
return paths return paths
} }
func upsertPluginMod(scope, name, kind string, categories []string) error { func upsertPluginMod(scope, name, displayName, description, kind string, categories []string) error {
const file = "plugin.mod" const file = "plugin.mod"
existing, _ := os.ReadFile(file) existing, _ := os.ReadFile(file)
mod, _ := core.ParseModFull(existing) mod, _ := core.ParseModFull(existing)
@ -588,6 +606,8 @@ func upsertPluginMod(scope, name, kind string, categories []string) error {
} }
mod.Plugin.Scope = scope mod.Plugin.Scope = scope
mod.Plugin.Name = name mod.Plugin.Name = name
mod.Plugin.DisplayName = displayName
mod.Plugin.Description = description
mod.Plugin.Kind = kind mod.Plugin.Kind = kind
mod.Plugin.Categories = categories mod.Plugin.Categories = categories
return writeMod(file, mod) return writeMod(file, mod)
@ -597,8 +617,14 @@ func writeMod(path string, m *core.ModFile) error {
var b strings.Builder var b strings.Builder
b.WriteString("[plugin]\n") b.WriteString("[plugin]\n")
b.WriteString(fmt.Sprintf("name = %q\n", m.Plugin.Name)) b.WriteString(fmt.Sprintf("name = %q\n", m.Plugin.Name))
if m.Plugin.DisplayName != "" {
b.WriteString(fmt.Sprintf("display_name = %q\n", m.Plugin.DisplayName))
}
b.WriteString(fmt.Sprintf("scope = %q\n", m.Plugin.Scope)) b.WriteString(fmt.Sprintf("scope = %q\n", m.Plugin.Scope))
b.WriteString(fmt.Sprintf("version = %q\n", m.Plugin.Version)) b.WriteString(fmt.Sprintf("version = %q\n", m.Plugin.Version))
if m.Plugin.Description != "" {
b.WriteString(fmt.Sprintf("description = %q\n", m.Plugin.Description))
}
if m.Plugin.Kind != "" { if m.Plugin.Kind != "" {
b.WriteString(fmt.Sprintf("kind = %q\n", m.Plugin.Kind)) b.WriteString(fmt.Sprintf("kind = %q\n", m.Plugin.Kind))
} }

View File

@ -102,6 +102,7 @@ type Plugin struct {
Categories []string `protobuf:"bytes,8,rep,name=categories,proto3" json:"categories,omitempty"` Categories []string `protobuf:"bytes,8,rep,name=categories,proto3" json:"categories,omitempty"`
UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
Kind string `protobuf:"bytes,10,opt,name=kind,proto3" json:"kind,omitempty"` Kind string `protobuf:"bytes,10,opt,name=kind,proto3" json:"kind,omitempty"`
DisplayName string `protobuf:"bytes,11,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -206,6 +207,13 @@ func (x *Plugin) GetKind() string {
return "" return ""
} }
func (x *Plugin) GetDisplayName() string {
if x != nil {
return x.DisplayName
}
return ""
}
type Category struct { type Category struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Slug string `protobuf:"bytes,1,opt,name=slug,proto3" json:"slug,omitempty"` Slug string `protobuf:"bytes,1,opt,name=slug,proto3" json:"slug,omitempty"`
@ -713,6 +721,7 @@ type CreatePluginRequest struct {
Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
Kind string `protobuf:"bytes,4,opt,name=kind,proto3" json:"kind,omitempty"` Kind string `protobuf:"bytes,4,opt,name=kind,proto3" json:"kind,omitempty"`
Categories []string `protobuf:"bytes,5,rep,name=categories,proto3" json:"categories,omitempty"` Categories []string `protobuf:"bytes,5,rep,name=categories,proto3" json:"categories,omitempty"`
DisplayName string `protobuf:"bytes,6,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -782,6 +791,13 @@ func (x *CreatePluginRequest) GetCategories() []string {
return nil return nil
} }
func (x *CreatePluginRequest) GetDisplayName() string {
if x != nil {
return x.DisplayName
}
return ""
}
type CreatePluginResponse struct { type CreatePluginResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Plugin *Plugin `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` Plugin *Plugin `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"`
@ -2188,7 +2204,7 @@ const file_orchestrator_v1_plugin_registry_proto_rawDesc = "" +
"\x04slug\x18\x02 \x01(\tR\x04slug\x12!\n" + "\x04slug\x18\x02 \x01(\tR\x04slug\x12!\n" +
"\fdisplay_name\x18\x03 \x01(\tR\vdisplayName\x129\n" + "\fdisplay_name\x18\x03 \x01(\tR\vdisplayName\x129\n" +
"\n" + "\n" +
"created_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\"\xb9\x02\n" + "created_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\"\xdc\x02\n" +
"\x06Plugin\x12\x0e\n" + "\x06Plugin\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\x12\x1d\n" + "\x02id\x18\x01 \x01(\tR\x02id\x12\x1d\n" +
"\n" + "\n" +
@ -2206,7 +2222,8 @@ const file_orchestrator_v1_plugin_registry_proto_rawDesc = "" +
"\n" + "\n" +
"updated_at\x18\t \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\x12\x12\n" + "updated_at\x18\t \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\x12\x12\n" +
"\x04kind\x18\n" + "\x04kind\x18\n" +
" \x01(\tR\x04kind\"\x82\x01\n" + " \x01(\tR\x04kind\x12!\n" +
"\fdisplay_name\x18\v \x01(\tR\vdisplayName\"\x82\x01\n" +
"\bCategory\x12\x12\n" + "\bCategory\x12\x12\n" +
"\x04slug\x18\x01 \x01(\tR\x04slug\x12!\n" + "\x04slug\x18\x01 \x01(\tR\x04slug\x12!\n" +
"\fdisplay_name\x18\x02 \x01(\tR\vdisplayName\x12 \n" + "\fdisplay_name\x18\x02 \x01(\tR\vdisplayName\x12 \n" +
@ -2240,7 +2257,7 @@ const file_orchestrator_v1_plugin_registry_proto_rawDesc = "" +
"\x04slug\x18\x01 \x01(\tR\x04slug\"s\n" + "\x04slug\x18\x01 \x01(\tR\x04slug\"s\n" +
"\x10GetScopeResponse\x12,\n" + "\x10GetScopeResponse\x12,\n" +
"\x05scope\x18\x01 \x01(\v2\x16.orchestrator.v1.ScopeR\x05scope\x121\n" + "\x05scope\x18\x01 \x01(\v2\x16.orchestrator.v1.ScopeR\x05scope\x121\n" +
"\aplugins\x18\x02 \x03(\v2\x17.orchestrator.v1.PluginR\aplugins\"\x9e\x01\n" + "\aplugins\x18\x02 \x03(\v2\x17.orchestrator.v1.PluginR\aplugins\"\xc1\x01\n" +
"\x13CreatePluginRequest\x12\x1d\n" + "\x13CreatePluginRequest\x12\x1d\n" +
"\n" + "\n" +
"scope_slug\x18\x01 \x01(\tR\tscopeSlug\x12\x12\n" + "scope_slug\x18\x01 \x01(\tR\tscopeSlug\x12\x12\n" +
@ -2249,7 +2266,8 @@ const file_orchestrator_v1_plugin_registry_proto_rawDesc = "" +
"\x04kind\x18\x04 \x01(\tR\x04kind\x12\x1e\n" + "\x04kind\x18\x04 \x01(\tR\x04kind\x12\x1e\n" +
"\n" + "\n" +
"categories\x18\x05 \x03(\tR\n" + "categories\x18\x05 \x03(\tR\n" +
"categories\"G\n" + "categories\x12!\n" +
"\fdisplay_name\x18\x06 \x01(\tR\vdisplayName\"G\n" +
"\x14CreatePluginResponse\x12/\n" + "\x14CreatePluginResponse\x12/\n" +
"\x06plugin\x18\x01 \x01(\v2\x17.orchestrator.v1.PluginR\x06plugin\"E\n" + "\x06plugin\x18\x01 \x01(\v2\x17.orchestrator.v1.PluginR\x06plugin\"E\n" +
"\x10GetPluginRequest\x12\x1d\n" + "\x10GetPluginRequest\x12\x1d\n" +

View File

@ -13,7 +13,15 @@ type ModFile struct {
} }
type ModPlugin struct { type ModPlugin struct {
// Name is the lowercase identifier used for the plugin slug in URLs and DB
// lookups. The CLI normalises this on write; the registry normalises on
// create. Use DisplayName for human-readable presentation.
Name string `toml:"name"` Name string `toml:"name"`
// DisplayName is the human-readable form (any case). Optional; if empty
// the registry falls back to the input name with its original case.
DisplayName string `toml:"display_name,omitempty"`
// Description is the short summary surfaced in the registry. Optional.
Description string `toml:"description,omitempty"`
// Scope is the plugin owner namespace as it appears in plugin.mod. It may // Scope is the plugin owner namespace as it appears in plugin.mod. It may
// include the leading "@" (e.g. "@themes") or omit it (e.g. "themes") — // include the leading "@" (e.g. "@themes") or omit it (e.g. "themes") —
// both forms are accepted. Consumers comparing scopes should trim the "@" // both forms are accepted. Consumers comparing scopes should trim the "@"

View File

@ -58,6 +58,7 @@ message Plugin {
repeated string categories = 8; repeated string categories = 8;
google.protobuf.Timestamp updated_at = 9; google.protobuf.Timestamp updated_at = 9;
string kind = 10; string kind = 10;
string display_name = 11;
} }
message Category { message Category {
@ -101,6 +102,7 @@ message CreatePluginRequest {
string description = 3; string description = 3;
string kind = 4; string kind = 4;
repeated string categories = 5; repeated string categories = 5;
string display_name = 6;
} }
message CreatePluginResponse { message CreatePluginResponse {
Plugin plugin = 1; Plugin plugin = 1;