package ckprompt import ( "bytes" "context" "errors" "fmt" "io" "strconv" "strings" commandkit "gitea.maximumdirect.net/eric/CommandKit" "gitea.maximumdirect.net/eric/CommandKit/integrations/ckconfig" promptkit "gitea.maximumdirect.net/eric/promptkit" "go.yaml.in/yaml/v4" ) type definitionInput[I any] struct { selection ckconfig.Selection id string format string captured commandkit.FlagValues[I] selected I } type catalogInput[I any] struct { 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 { selection ckconfig.Selection inspection promptkit.RequirementInspectionRequest format string captured commandkit.FlagValues[I] selected I } type variableAssignment struct { key string value string } type inputAssignment struct { name string path string } // 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 // claims no namespace; attached construction issues remain reportable. It adds // no configuration family; callers compose ConfigScope independently. // Construction, installation, help, and version do not run suite factories or // providers. Command captures suite callbacks and selector metadata; // callback-referenced objects and effects remain caller-owned. Suite documents // invocation input and state initialization, zero values, and caller ownership // of returned objects. // Catalog commands accept config/overlay selection and suite selectors, and // 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 && suite.ListBackends == nil && suite.ListNamespaces == nil && suite.ListCollections == nil { command := commandkit.NoCommand() for _, issue := range suite.issues { command = commandkit.WithError(command, issue) } return command } suite.Selectors = append([]commandkit.Flag[I](nil), suite.Selectors...) rootCommands := make([]commandkit.Command, 0, 3) showCommands := make([]commandkit.Command, 0, 2) if suite.InspectPrompt != nil { showCommands = append(showCommands, newDefinitionCommand(suite, "prompt", true)) } if suite.InspectProfile != nil { showCommands = append(showCommands, newDefinitionCommand(suite, "profile", false)) } if len(showCommands) != 0 { rootCommands = append(rootCommands, commandkit.Group("show", "Show a named PromptKit definition.", showCommands...)) } listCommands := make([]commandkit.Command, 0, 5) if suite.ListPrompts != nil { listCommands = append(listCommands, newCatalogListCommand(suite, "prompts")) } if suite.ListProfiles != nil { 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...)) } requirementsCommands := make([]commandkit.Command, 0, 2) if suite.InspectRequirements != nil { requirementsCommands = append(requirementsCommands, newRequirementsCommand(suite)) } if suite.ValidateRequest != nil { requirementsCommands = append(requirementsCommands, newValidationCommand(suite)) } if len(requirementsCommands) != 0 { rootCommands = append(rootCommands, commandkit.Group("requirements", "Inspect and validate prompt requirements.", requirementsCommands...)) } command := commandkit.RootGroup("promptkit", "Inspect, validate, and list PromptKit definitions and requirements.", rootCommands...) for _, issue := range suite.issues { command = commandkit.WithError(command, issue) } return 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 } return &(*input).captured }) flags = append(flags, commandkit.ValueFlag(commandkit.FlagMeta{Name: "config", Usage: "Configuration input path."}, (*string)(nil), parseDefinitionString, func(input **catalogInput[I], value commandkit.Supplied[string]) { (*input).selection.Config = value }), commandkit.ValueFlag(commandkit.FlagMeta{Name: "overlay", Usage: "Configuration overlay path."}, (*string)(nil), parseDefinitionString, func(input **catalogInput[I], value commandkit.Supplied[string]) { (*input).selection.Overlay = value }), 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 + ".", NewInput: func() *catalogInput[I] { return &catalogInput[I]{} }, Flags: flags, CheckInput: func(input *catalogInput[I]) error { if input == nil { return errors.New("PromptKit catalog input must not be nil") } 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 } input.selected = selected return nil }, Run: func(invocation commandkit.Invocation, input *catalogInput[I]) commandkit.Outcome { 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) } }, }) } 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) } return nil } func checkRequirementsFormat(format string) error { if format != "yaml" { return fmt.Errorf("unsupported requirements format %q; expected yaml", format) } return nil } func prepareCatalogInput[I, S any](suite Suite[I, S], input *catalogInput[I]) (I, error) { return prepareSuiteInput(suite, input.selection, input.captured) } func newDefinitionCommand[I, S any](suite Suite[I, S], name string, prompt bool) commandkit.Command { flags := commandkit.CaptureFlags(suite.Selectors, func(input **definitionInput[I]) *commandkit.FlagValues[I] { if input == nil || *input == nil { return nil } return &(*input).captured }) flags = append(flags, commandkit.ValueFlag(commandkit.FlagMeta{Name: "config", Usage: "Configuration input path."}, (*string)(nil), parseDefinitionString, func(input **definitionInput[I], value commandkit.Supplied[string]) { (*input).selection.Config = value }), commandkit.ValueFlag(commandkit.FlagMeta{Name: "overlay", Usage: "Configuration overlay path."}, (*string)(nil), parseDefinitionString, func(input **definitionInput[I], value commandkit.Supplied[string]) { (*input).selection.Overlay = value }), commandkit.ValueFlag(commandkit.FlagMeta{Name: "format", Usage: "Report format: text or yaml."}, stringDefault("text"), parseDefinitionString, func(input **definitionInput[I], value commandkit.Supplied[string]) { (*input).format = value.Value }), ) spec := commandkit.CommandSpec[*definitionInput[I]]{ Name: name, Summary: "Inspect a named PromptKit " + name + ".", NewInput: func() *definitionInput[I] { return &definitionInput[I]{} }, Flags: flags, Arguments: []commandkit.Argument[*definitionInput[I]]{ commandkit.ValueArgument(commandkit.ArgumentMeta{Name: "id", Usage: "Prompt or profile ID."}, true, parseDefinitionString, func(input **definitionInput[I], value commandkit.Supplied[string]) { (*input).id = value.Value }), }, CheckInput: func(input *definitionInput[I]) error { if err := checkDefinitionFormat(input.format); err != nil { return err } selected, err := prepareDefinitionInput(suite, input) if err != nil { return err } input.selected = selected return nil }, Run: func(invocation commandkit.Invocation, input *definitionInput[I]) commandkit.Outcome { return runDefinitionInspection(invocation, suite, input, prompt) }, } return commandkit.NewCommand(spec) } func newRequirementsCommand[I, S any](suite Suite[I, S]) commandkit.Command { flags := commandkit.CaptureFlags(suite.Selectors, func(input **requirementsInput[I]) *commandkit.FlagValues[I] { if input == nil || *input == nil { return nil } return &(*input).captured }) flags = append(flags, commandkit.ValueFlag(commandkit.FlagMeta{Name: "config", Usage: "Configuration input path."}, (*string)(nil), parseDefinitionString, func(input **requirementsInput[I], value commandkit.Supplied[string]) { (*input).selection.Config = value }), commandkit.ValueFlag(commandkit.FlagMeta{Name: "overlay", Usage: "Configuration overlay path."}, (*string)(nil), parseDefinitionString, func(input **requirementsInput[I], value commandkit.Supplied[string]) { (*input).selection.Overlay = value }), commandkit.ValueFlag(commandkit.FlagMeta{Name: "profile", Usage: "Profile identity for discovery."}, stringDefault(""), parseDefinitionString, func(input **requirementsInput[I], value commandkit.Supplied[string]) { (*input).inspection.ProfileID = value.Value }), commandkit.ValueFlag(commandkit.FlagMeta{Name: "session-id", Usage: "Session context for discovery."}, stringDefault(""), parseDefinitionString, func(input **requirementsInput[I], value commandkit.Supplied[string]) { (*input).inspection.SessionID = value.Value }), commandkit.ValueFlag(commandkit.FlagMeta{Name: "format", Usage: "Report format: yaml."}, stringDefault("yaml"), parseDefinitionString, func(input **requirementsInput[I], value commandkit.Supplied[string]) { (*input).format = value.Value }), commandkit.RepeatFlag(commandkit.FlagMeta{Name: "var", Usage: "Caller variable assignment KEY=VALUE."}, []variableAssignment(nil), parseVariableAssignment, func(input **requirementsInput[I], value commandkit.Supplied[[]variableAssignment]) { variables := make(map[string]string, len(value.Value)) for _, assignment := range value.Value { variables[assignment.key] = assignment.value } (*input).inspection.Vars = variables }), ) spec := commandkit.CommandSpec[*requirementsInput[I]]{ Name: "show", Summary: "Inspect requirements for a named PromptKit prompt.", NewInput: func() *requirementsInput[I] { return &requirementsInput[I]{} }, Flags: flags, Arguments: []commandkit.Argument[*requirementsInput[I]]{ commandkit.ValueArgument(commandkit.ArgumentMeta{Name: "id", Usage: "Prompt ID."}, true, parseDefinitionString, func(input **requirementsInput[I], value commandkit.Supplied[string]) { (*input).inspection.PromptID = value.Value }), }, CheckInput: func(input *requirementsInput[I]) error { if err := checkRequirementsFormat(input.format); err != nil { return err } selected, err := prepareSuiteInput(suite, input.selection, input.captured) if err != nil { return err } input.selected = selected return nil }, Run: func(invocation commandkit.Invocation, input *requirementsInput[I]) commandkit.Outcome { return runRequirementsInspection(invocation, suite, input) }, } return commandkit.NewCommand(spec) } func prepareDefinitionInput[I, S any](suite Suite[I, S], input *definitionInput[I]) (I, error) { return prepareSuiteInput(suite, input.selection, input.captured) } func prepareSuiteInput[I, S any](suite Suite[I, S], selection ckconfig.Selection, captured commandkit.FlagValues[I]) (I, error) { var selected I if suite.NewInput != nil { selected = suite.NewInput() } if err := captured.ApplyTo(&selected, suite.Selectors...); err != nil { return selected, err } if suite.CheckInput != nil { if err := suite.CheckInput(selection, selected); err != nil { return selected, err } } return selected, nil } func runCatalogList[T any, I, S any]( invocation commandkit.Invocation, suite Suite[I, S], input *catalogInput[I], list func(context.Context, CatalogRequest, I, S) (commandkit.Acquired[T], error), encode func(T) ([]byte, error), ) commandkit.Outcome { if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { return definitionFailure(err, nil) } } var state S if suite.NewState != nil { state = suite.NewState() } if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { return definitionFailure(err, nil) } } acquired, err := list(invocation.Context, CatalogRequest{Selection: input.selection, Filter: input.filter}, input.selected, state) if err != nil { return definitionFailure(err, acquired.Diagnostics) } if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { return definitionFailure(err, acquired.Diagnostics) } } output, err := encode(acquired.Value) if err != nil { return definitionFailure(err, acquired.Diagnostics) } return writePromptReport(invocation, output, acquired.Diagnostics) } func runDefinitionInspection[I, S any](invocation commandkit.Invocation, suite Suite[I, S], input *definitionInput[I], prompt bool) commandkit.Outcome { if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { return definitionFailure(err, nil) } } var state S if suite.NewState != nil { state = suite.NewState() } if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { return definitionFailure(err, nil) } } if prompt { request := PromptRequest{Selection: input.selection, ID: input.id} acquired, err := suite.InspectPrompt(invocation.Context, request, input.selected, state) if err != nil { return definitionFailure(err, acquired.Diagnostics) } if acquired.Value == nil { return definitionFailure(errors.New("PromptKit prompt inspection returned nil"), acquired.Diagnostics) } if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { return definitionFailure(err, acquired.Diagnostics) } } return renderPromptReport(invocation, promptReportFromInspection(acquired.Value), input.format, acquired.Diagnostics) } request := ProfileRequest{Selection: input.selection, ID: input.id} acquired, err := suite.InspectProfile(invocation.Context, request, input.selected, state) if err != nil { return definitionFailure(err, acquired.Diagnostics) } if acquired.Value == nil { return definitionFailure(errors.New("PromptKit profile inspection returned nil"), acquired.Diagnostics) } if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { return definitionFailure(err, acquired.Diagnostics) } } return renderProfileReport(invocation, profileReportFromInspection(acquired.Value), input.format, acquired.Diagnostics) } type promptInputReport struct { Name string `yaml:"name"` Required bool `yaml:"required"` ContentType string `yaml:"content_type"` Description string `yaml:"description"` } type promptOutputContractReport struct { Format string `yaml:"format"` ValidationMode string `yaml:"validation_mode"` SchemaPath string `yaml:"schema_path"` RepairAttempts int `yaml:"repair_attempts"` } type promptInspectionReport struct { PromptID string `yaml:"prompt_id"` PromptVersion *string `yaml:"version,omitempty"` PromptHash string `yaml:"prompt_hash"` DefaultProfileID string `yaml:"default_profile_id"` Inputs []promptInputReport `yaml:"inputs"` OutputContract promptOutputContractReport `yaml:"output_contract"` } type requirementsInspectionReport struct { PromptID string `yaml:"prompt_id"` PromptVersion *string `yaml:"version,omitempty"` ProfileID string `yaml:"profile_id"` Complete bool `yaml:"complete"` Inputs []requirementInputReport `yaml:"inputs"` Variables []requirementVariableReport `yaml:"variables"` Issues []requirementIssueReport `yaml:"issues"` } type requirementInputReport struct { Name string `yaml:"name"` Kind string `yaml:"kind"` Declared bool `yaml:"declared"` ContentType *string `yaml:"content_type"` Description *string `yaml:"description"` Explanation string `yaml:"explanation"` } type requirementVariableReport struct { Name string `yaml:"name"` Kind string `yaml:"kind"` Supplied bool `yaml:"supplied"` Explanation string `yaml:"explanation"` } type requirementIssueReport struct { Code string `yaml:"code"` InputName string `yaml:"input_name"` VariableName string `yaml:"variable_name"` Description string `yaml:"description"` } type modelParamsReport struct { BackendID string `yaml:"backend_id"` Endpoint string `yaml:"endpoint"` Model string `yaml:"model"` Temperature float64 `yaml:"temperature"` MaxTokens int `yaml:"max_tokens"` TopP float64 `yaml:"top_p"` TimeoutSeconds int `yaml:"timeout_seconds"` ServiceTier string `yaml:"service_tier"` ReasoningEffort string `yaml:"reasoning_effort"` APIKeyEnv string `yaml:"api_key_env"` ExtraParams map[string]any `yaml:"extra_params"` } type profileInspectionReport struct { ProfileID string `yaml:"profile_id"` EffectiveModelParams modelParamsReport `yaml:"effective_model_params"` APIKeyRequired bool `yaml:"api_key_required"` } func promptReportFromInspection(inspection *promptkit.PromptInspection) promptInspectionReport { report := promptInspectionReport{ PromptID: inspection.PromptID, PromptVersion: inspection.PromptVersion, PromptHash: inspection.PromptHash, DefaultProfileID: inspection.DefaultProfileID, Inputs: make([]promptInputReport, 0, len(inspection.Inputs)), OutputContract: promptOutputContractReport{ Format: string(inspection.OutputContract.Format), ValidationMode: string(inspection.OutputContract.ValidationMode), SchemaPath: inspection.OutputContract.SchemaPath, RepairAttempts: inspection.OutputContract.RepairAttempts, }, } for _, input := range inspection.Inputs { report.Inputs = append(report.Inputs, promptInputReport{Name: input.Name, Required: input.Required, ContentType: input.ContentType, Description: input.Description}) } return report } func requirementsReportFromInspection(inspection *promptkit.RequirementInspection) requirementsInspectionReport { report := requirementsInspectionReport{ PromptID: inspection.PromptID, PromptVersion: inspection.PromptVersion, ProfileID: inspection.ProfileID, Complete: inspection.Complete, Inputs: make([]requirementInputReport, 0, len(inspection.Inputs)), Variables: make([]requirementVariableReport, 0, len(inspection.Variables)), Issues: make([]requirementIssueReport, 0, len(inspection.Issues)), } for _, input := range inspection.Inputs { report.Inputs = append(report.Inputs, requirementInputReport{ Name: input.Name, Kind: string(input.Kind), Declared: input.Declared, ContentType: input.ContentType, Description: input.Description, Explanation: input.Explanation, }) } for _, variable := range inspection.Variables { report.Variables = append(report.Variables, requirementVariableReport{ Name: variable.Name, Kind: string(variable.Kind), Supplied: variable.Supplied, Explanation: variable.Explanation, }) } for _, issue := range inspection.Issues { report.Issues = append(report.Issues, requirementIssueReport{ Code: string(issue.Code), InputName: issue.InputName, VariableName: issue.VariableName, Description: issue.Description, }) } return report } func profileReportFromInspection(inspection *promptkit.ProfileInspection) profileInspectionReport { params := inspection.EffectiveModelParams extra := params.ExtraParams if extra == nil { extra = map[string]any{} } return profileInspectionReport{ ProfileID: inspection.ProfileID, EffectiveModelParams: modelParamsReport{ BackendID: params.BackendID, Endpoint: params.Endpoint, Model: params.Model, Temperature: params.Temperature, MaxTokens: params.MaxTokens, TopP: params.TopP, TimeoutSeconds: params.TimeoutSeconds, ServiceTier: params.ServiceTier, ReasoningEffort: params.ReasoningEffort, APIKeyEnv: params.APIKeyEnv, ExtraParams: extra, }, APIKeyRequired: inspection.APIKeyRequired, } } func runRequirementsInspection[I, S any](invocation commandkit.Invocation, suite Suite[I, S], input *requirementsInput[I]) commandkit.Outcome { if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { return definitionFailure(err, nil) } } var state S if suite.NewState != nil { state = suite.NewState() } if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { return definitionFailure(err, nil) } } request := RequirementsRequest{Selection: input.selection, Inspection: input.inspection} acquired, err := suite.InspectRequirements(invocation.Context, request, input.selected, state) if err != nil { return definitionFailure(err, acquired.Diagnostics) } if acquired.Value == nil { return definitionFailure(errors.New("PromptKit requirements inspection returned nil"), acquired.Diagnostics) } if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { return definitionFailure(err, acquired.Diagnostics) } } output, err := encodePromptYAMLBytes(requirementsReportFromInspection(acquired.Value)) if err != nil { return definitionFailure(err, acquired.Diagnostics) } return writePromptReport(invocation, output, acquired.Diagnostics) } func renderPromptReport(invocation commandkit.Invocation, report promptInspectionReport, format string, diagnostics []commandkit.Diagnostic) commandkit.Outcome { var output bytes.Buffer if format == "yaml" { if err := encodePromptYAML(&output, report); err != nil { return definitionFailure(err, diagnostics) } } else { output.WriteString(formatPromptText(report)) } return writePromptReport(invocation, output.Bytes(), diagnostics) } func renderProfileReport(invocation commandkit.Invocation, report profileInspectionReport, format string, diagnostics []commandkit.Diagnostic) commandkit.Outcome { var output bytes.Buffer if format == "yaml" { if err := encodePromptYAML(&output, report); err != nil { return definitionFailure(err, diagnostics) } } else { text, err := formatProfileText(report) if err != nil { return definitionFailure(err, diagnostics) } output.WriteString(text) } return writePromptReport(invocation, output.Bytes(), diagnostics) } func formatPromptText(report promptInspectionReport) string { var output strings.Builder fmt.Fprintf(&output, "Prompt ID: %s\n", strconv.Quote(report.PromptID)) if report.PromptVersion != nil { fmt.Fprintf(&output, "Prompt version: %s\n", strconv.Quote(*report.PromptVersion)) } fmt.Fprintf(&output, "Prompt hash: %s\n", strconv.Quote(report.PromptHash)) fmt.Fprintf(&output, "Default profile ID: %s\n", strconv.Quote(report.DefaultProfileID)) output.WriteString("Inputs:\n") for _, input := range report.Inputs { fmt.Fprintf(&output, " - name: %s\n required: %t\n content_type: %s\n description: %s\n", strconv.Quote(input.Name), input.Required, strconv.Quote(input.ContentType), strconv.Quote(input.Description)) } if len(report.Inputs) == 0 { output.WriteString(" []\n") } fmt.Fprintf(&output, "Output contract:\n format: %s\n validation_mode: %s\n schema_path: %s\n repair_attempts: %d\n", strconv.Quote(report.OutputContract.Format), strconv.Quote(report.OutputContract.ValidationMode), strconv.Quote(report.OutputContract.SchemaPath), report.OutputContract.RepairAttempts) return output.String() } func formatProfileText(report profileInspectionReport) (string, error) { params := report.EffectiveModelParams var output strings.Builder fmt.Fprintf(&output, "Profile ID: %s\n", strconv.Quote(report.ProfileID)) output.WriteString("Effective model params:\n") fmt.Fprintf(&output, " backend_id: %s\n endpoint: %s\n model: %s\n temperature: %s\n max_tokens: %d\n top_p: %s\n timeout_seconds: %d\n service_tier: %s\n reasoning_effort: %s\n api_key_env: %s\n", strconv.Quote(params.BackendID), strconv.Quote(params.Endpoint), strconv.Quote(params.Model), strconv.FormatFloat(params.Temperature, 'g', -1, 64), params.MaxTokens, strconv.FormatFloat(params.TopP, 'g', -1, 64), params.TimeoutSeconds, strconv.Quote(params.ServiceTier), strconv.Quote(params.ReasoningEffort), strconv.Quote(params.APIKeyEnv)) output.WriteString(" extra_params:\n") var nested strings.Builder if err := encodePromptYAML(&nested, params.ExtraParams); err != nil { return "", err } for _, line := range strings.Split(strings.TrimSuffix(nested.String(), "\n"), "\n") { output.WriteString(" ") output.WriteString(line) output.WriteByte('\n') } fmt.Fprintf(&output, "API key required: %t\n", report.APIKeyRequired) return output.String(), nil } func encodePromptYAML(writer io.Writer, value any) error { encoder := yaml.NewEncoder(writer) encoder.SetIndent(2) return encoder.Encode(value) } type promptCatalogEntry struct { ID string `yaml:"id"` Version *string `yaml:"version,omitempty"` } func encodePromptCatalog(prompts []promptkit.PromptIdentity) ([]byte, error) { entries := make([]promptCatalogEntry, len(prompts)) for index, prompt := range prompts { entries[index] = promptCatalogEntry{ID: prompt.ID, Version: prompt.Version} } return encodePromptYAMLBytes(entries) } func encodeProfileCatalog(profiles []string) ([]byte, error) { if profiles == nil { profiles = []string{} } 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 { return nil, err } return output.Bytes(), nil } func writePromptReport(invocation commandkit.Invocation, output []byte, diagnostics []commandkit.Diagnostic) commandkit.Outcome { if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { return definitionFailure(err, diagnostics) } } written, err := invocation.Streams.Out.Write(output) if err == nil && written != len(output) { err = io.ErrShortWrite } if err != nil { outcome := definitionFailure(err, diagnostics) outcome.ReportIncomplete = true outcome.DeliveryError = err return outcome } outcome := commandkit.Success() outcome.Diagnostics = diagnostics return outcome } func definitionFailure(cause error, diagnostics []commandkit.Diagnostic) commandkit.Outcome { outcome := commandkit.Failure(cause) outcome.Diagnostics = diagnostics return outcome } func checkDefinitionFormat(format string) error { if format != "text" && format != "yaml" { return fmt.Errorf("unsupported format %q; expected text or yaml", format) } return nil } func parseDefinitionString(value string) (string, error) { return value, nil } func parseVariableAssignment(raw string) (variableAssignment, error) { key, value, found := strings.Cut(raw, "=") if !found { return variableAssignment{}, fmt.Errorf("variable assignment %q must contain =", raw) } return variableAssignment{key: key, value: value}, nil } func parseInputAssignment(raw string) (inputAssignment, error) { name, path, found := strings.Cut(raw, "=") if !found { return inputAssignment{}, fmt.Errorf("input assignment %q must contain =", raw) } return inputAssignment{name: name, path: path}, nil } func stringDefault(value string) *string { return &value }