2026-10-11 01:43:02 +00:00
package ckprompt
import (
"context"
"errors"
"fmt"
"io"
"strconv"
commandkit "gitea.maximumdirect.net/eric/CommandKit"
"gitea.maximumdirect.net/eric/CommandKit/integrations/ckconfig"
configkit "gitea.maximumdirect.net/eric/ConfigKit"
promptkit "gitea.maximumdirect.net/eric/promptkit"
)
// PromptRequest selects one named prompt for inspection.
type PromptRequest struct {
2026-10-11 02:06:06 +00:00
// Selection carries the configuration and overlay values supplied to the command.
2026-10-11 01:43:02 +00:00
Selection ckconfig . Selection
2026-10-11 02:06:06 +00:00
// ID is the selected named prompt.
ID string
2026-10-11 01:43:02 +00:00
}
// ProfileRequest selects one named profile for inspection.
type ProfileRequest struct {
2026-10-11 02:06:06 +00:00
// Selection carries the configuration and overlay values supplied to the command.
2026-10-11 01:43:02 +00:00
Selection ckconfig . Selection
2026-10-11 02:06:06 +00:00
// ID is the selected named profile.
ID string
2026-10-11 01:43:02 +00:00
}
2026-10-17 18:44:10 +00:00
// CatalogRequest selects the configuration and native namespace filter for a
// prompt, profile, or backend catalog operation. A zero Filter preserves
// PromptKit's native default selection.
type CatalogRequest struct {
// Selection carries the configuration and overlay supplied to the command.
Selection ckconfig . Selection
// Filter is forwarded unchanged to each filterable native catalog. Its zero
// value preserves PromptKit's default namespace selection.
Filter promptkit . NamespaceFilter
}
// NamespaceRequest selects the configuration and native resource kinds for a
// namespace catalog operation. An empty Kinds slice preserves PromptKit's
// native default selection.
type NamespaceRequest struct {
// Selection carries the configuration and overlay supplied to the command.
Selection ckconfig . Selection
// Kinds is forwarded unchanged to the native namespace catalog. An empty
// slice preserves PromptKit's default kind selection.
Kinds [ ] promptkit . ResourceKind
}
2026-10-12 16:54:00 +00:00
// RequirementsRequest selects a prompt context for native requirements
// inspection.
type RequirementsRequest struct {
// Selection carries the configuration and overlay values supplied to the command.
Selection ckconfig . Selection
2026-10-12 17:26:11 +00:00
// Inspection forwards PromptKit's native prompt, version, profile, variable,
// and session discovery context. Optional version, profile, and session
// strings retain PromptKit's empty-value semantics.
2026-10-12 16:54:00 +00:00
Inspection promptkit . RequirementInspectionRequest
}
// Suite describes application-owned input, shared state, and optional catalog,
2026-10-17 18:49:18 +00:00
// named inspection, requirements discovery, concrete request validation, and
// smoke testing capabilities. Its private issues retain adapter construction
// failures.
2026-10-11 13:07:00 +00:00
// Factory results and callback effects remain caller-owned: Command does not
// deep-copy arbitrary objects or repair aliases returned by factories.
2026-10-12 02:38:27 +00:00
// A reached operation prepares one input, applies its captured selectors, and
// checks the input before creating state and calling the selected provider.
2026-10-11 13:07:00 +00:00
// Factories are not invoked during construction, installation, help, or version,
// and need not run after failure.
2026-10-11 01:43:02 +00:00
type Suite [ I , S any ] struct {
2026-10-17 19:15:58 +00:00
// NewInput is called once when inspection, requirements, validation, smoke,
// or catalog input preparation is reached, before applying selectors and
2026-10-12 22:25:43 +00:00
// calling CheckInput.
2026-10-11 13:07:00 +00:00
// If nil, input is the zero value of I, including nil for pointer types.
2026-10-11 02:06:06 +00:00
NewInput func ( ) I
2026-10-17 19:15:58 +00:00
// NewState is called once when an inspection, validation, smoke, or catalog
2026-10-12 22:25:43 +00:00
// callback is reached, after input checks pass. If nil, state is the zero
// value of S, including nil for pointer types.
2026-10-11 02:06:06 +00:00
NewState func ( ) S
2026-10-17 19:15:58 +00:00
// Selectors declares the original typed flags accepted by inspection,
// requirements, validation, smoke, and catalog commands.
2026-10-11 02:06:06 +00:00
Selectors [ ] commandkit . Flag [ I ]
2026-10-17 19:15:58 +00:00
// CheckInput validates selected inspection, requirements, validation, smoke,
// or catalog input before state creation.
2026-10-11 02:06:06 +00:00
CheckInput func ( ckconfig . Selection , I ) error
2026-10-26 03:26:14 +00:00
// InspectPrompt inspects the selected prompt after input and state
// preparation. Its acquisition carries independent loader diagnostics and
// terminal context alongside any native inspection result.
2026-10-11 02:06:06 +00:00
InspectPrompt func ( context . Context , PromptRequest , I , S ) ( commandkit . Acquired [ * promptkit . PromptInspection ] , error )
2026-10-26 03:26:14 +00:00
// InspectProfile inspects the selected profile after input and state
// preparation. Its acquisition carries independent loader diagnostics and
// terminal context alongside any native inspection result.
2026-10-11 01:43:02 +00:00
InspectProfile func ( context . Context , ProfileRequest , I , S ) ( commandkit . Acquired [ * promptkit . ProfileInspection ] , error )
2026-10-12 16:54:00 +00:00
// InspectRequirements inspects native PromptKit requirements after input and
2026-10-26 03:26:14 +00:00
// state preparation. Its acquisition carries independent loader diagnostics
// and terminal context alongside any native inspection result. It is
// optional; a nil callback omits `promptkit requirements show` without
// disabling validation or other capabilities.
2026-10-12 16:54:00 +00:00
InspectRequirements func ( context . Context , RequirementsRequest , I , S ) ( commandkit . Acquired [ * promptkit . RequirementInspection ] , error )
2026-10-12 22:25:43 +00:00
// ValidateRequest validates a concrete PromptKit request after input and
// state preparation. It is optional; a nil callback omits
// `promptkit requirements validate` without disabling discovery or other
2026-10-26 03:26:14 +00:00
// capabilities. The acquisition carries independent diagnostics, terminal
// context, and an empty value on success; PromptKit errors are returned
// unchanged where supported.
2026-10-12 22:13:16 +00:00
ValidateRequest func ( context . Context , ValidationRequest , I , S ) ( commandkit . Acquired [ struct { } ] , error )
2026-10-17 18:49:18 +00:00
// SmokeTest runs one concrete native smoke-test operation after input and
// state preparation. It is optional; nil omits only the smoke-test command.
2026-10-26 03:26:14 +00:00
// The acquisition retains a native report, independent diagnostics, and
// terminal context even when the callback also returns an error. For
// `test all`, CommandKit calls the captured callback sequentially for each
// selected kind after preparing input and state once.
2026-10-17 18:49:18 +00:00
SmokeTest func ( context . Context , SmokeRequest , I , S ) ( commandkit . Acquired [ * promptkit . SmokeTestReport ] , error )
2026-10-12 02:10:45 +00:00
// ListPrompts lists prompt identities after input and state preparation.
2026-10-17 18:44:10 +00:00
// It is optional independently of the other catalogs; nil omits only this
// catalog command.
ListPrompts func ( context . Context , CatalogRequest , I , S ) ( commandkit . Acquired [ [ ] promptkit . PromptIdentity ] , error )
// ListProfiles lists profile IDs after input and state preparation. It is
// optional independently of the other catalogs; nil omits only this catalog
// command.
ListProfiles func ( context . Context , CatalogRequest , I , S ) ( commandkit . Acquired [ [ ] string ] , error )
// ListBackends lists backend IDs after input and state preparation. It is
// optional independently of the other catalogs; nil omits only this catalog
// command.
ListBackends func ( context . Context , CatalogRequest , I , S ) ( commandkit . Acquired [ [ ] string ] , error )
// ListNamespaces lists native namespace names after input and state
// preparation. It is optional independently of the other catalogs; nil omits
// only this catalog command.
ListNamespaces func ( context . Context , NamespaceRequest , I , S ) ( commandkit . Acquired [ [ ] string ] , error )
// ListCollections lists collection information after input and state
// preparation. It is optional independently of the other catalogs; nil
// omits only this catalog command.
ListCollections func ( context . Context , ckconfig . Selection , I , S ) ( commandkit . Acquired [ [ ] promptkit . CollectionInfo ] , error )
2026-10-11 01:43:02 +00:00
issues [ ] error
}
2026-10-12 22:13:16 +00:00
// WithRequestValidation attaches PromptKit-backed concrete request validation
2026-10-12 22:25:43 +00:00
// to suite while preserving its other capabilities and retained issues. Those
// issues remain attached if capabilities are narrowed before Command captures
// the suite. The
// builder, when present, runs before the engine loader and receives the seed,
// input, state, and invocation context. Its returned request is passed to
// PromptKit unchanged; the loader receives the seed's original configuration
// selection. A nil builder forwards the seed request unchanged. A builder
// failure prevents engine loading and preserves its supported error identity.
2026-10-26 14:18:21 +00:00
// Native validation errors supply terminal explanations and are returned with
// supported identity, and
2026-10-26 03:26:14 +00:00
// configuration diagnostics and terminal context from engine loading remain
// separate in the acquisition. A nil loader is retained as a suite wiring
// issue and does not install a validation callback. Callback references and
// objects remain caller-owned.
2026-10-12 22:13:16 +00:00
func WithRequestValidation [ I , S any ] (
suite Suite [ I , S ] ,
load func ( context . Context , ckconfig . Selection , I , S ) ( * promptkit . Engine , error ) ,
build func ( context . Context , ValidationRequest , I , S ) ( promptkit . RunRequest , error ) ,
) Suite [ I , S ] {
if load == nil {
issues := append ( [ ] error ( nil ) , suite . issues ... )
suite . issues = append ( issues , errors . New ( "PromptKit engine loader is nil" ) )
return suite
}
suite . ValidateRequest = func ( ctx context . Context , request ValidationRequest , input I , state S ) ( commandkit . Acquired [ struct { } ] , error ) {
if err := promptKitContextError ( ctx ) ; err != nil {
return commandkit . Acquired [ struct { } ] { } , fmt . Errorf ( "PromptKit request validation before building: %w" , err )
}
concreteRequest := request . Request
if build != nil {
built , err := build ( ctx , request , input , state )
if err != nil {
return commandkit . Acquired [ struct { } ] { } , err
}
concreteRequest = built
}
if err := promptKitContextError ( ctx ) ; err != nil {
return commandkit . Acquired [ struct { } ] { } , fmt . Errorf ( "PromptKit request validation after building: %w" , err )
}
2026-10-26 03:26:14 +00:00
engine , _ , messages , err := loadPromptKitEngine ( ctx , request . Selection , input , state , "" , load )
2026-10-12 22:13:16 +00:00
if err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ struct { } ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-12 22:13:16 +00:00
}
if err := promptKitContextError ( ctx ) ; err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ struct { } ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , fmt . Errorf ( "PromptKit request validation before native validation: %w" , err )
2026-10-12 22:13:16 +00:00
}
if err := engine . ValidateRequest ( ctx , concreteRequest ) ; err != nil {
2026-10-26 14:18:21 +00:00
messages = messages . withOperationError ( err )
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ struct { } ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-12 22:13:16 +00:00
}
if err := promptKitContextError ( ctx ) ; err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ struct { } ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , fmt . Errorf ( "PromptKit request validation after native validation: %w" , err )
2026-10-12 22:13:16 +00:00
}
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ struct { } ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , nil
2026-10-12 22:13:16 +00:00
}
return suite
}
2026-10-11 01:43:02 +00:00
type engineLoader [ I , S any ] func ( context . Context , ckconfig . Selection , I , S ) ( * promptkit . Engine , error )
2026-10-26 03:26:14 +00:00
type promptKitAcquisition struct {
diagnostics [ ] commandkit . Diagnostic
explanation commandkit . Explanation
}
2026-10-26 14:18:21 +00:00
// withOperationError deliberately presents a native PromptKit operation error.
// Loader and request-builder errors remain application-owned. Native structured
// context takes precedence when an acquisition already supplies it.
func ( messages promptKitAcquisition ) withOperationError ( err error ) promptKitAcquisition {
if err == nil || messages . explanation . Message != "" {
return messages
}
for _ , detail := range messages . explanation . Details {
if detail != nil {
return messages
}
}
messages . explanation . Message = err . Error ( )
return messages
}
2026-10-11 01:43:02 +00:00
// ConfigScope adapts PromptKit's captured configuration inspection to the
2026-10-12 02:38:27 +00:00
// standard configuration commands, including native comparison when the scope
// declares Diff. The loader is not called during declaration construction or
// installation; it is called only when a config operation selects the
// promptkit scope, with that operation's input and shared state. PromptKit
2026-10-26 03:26:14 +00:00
// independent diagnostics, terminal context, and supported error identity are
// returned through ckconfig's acquisition and comparison contracts.
2026-10-11 01:43:02 +00:00
func ConfigScope [ I , S any ] ( load func ( context . Context , ckconfig . Selection , I , S ) ( * promptkit . Engine , error ) ) ckconfig . Scope [ I , S ] {
scope := ckconfig . Scope [ I , S ] {
Name : "promptkit" ,
Summary : "PromptKit engine configuration." ,
2026-10-12 02:01:10 +00:00
Operations : [ ] ckconfig . Operation { ckconfig . Validate , ckconfig . Show , ckconfig . Sources , ckconfig . Explain , ckconfig . Diff } ,
2026-10-11 01:43:02 +00:00
}
if load == nil {
return ckconfig . WithScopeError ( scope , errors . New ( "PromptKit configuration loader is nil" ) )
}
scope . Acquire = func ( ctx context . Context , request ckconfig . Request , input I , state S ) ( commandkit . Acquired [ ckconfig . View ] , error ) {
2026-10-26 03:26:14 +00:00
_ , inspection , messages , err := loadPromptKitEngine ( ctx , request . Selection , input , state , "" , load )
2026-10-11 01:43:02 +00:00
if err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ ckconfig . View ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-11 01:43:02 +00:00
}
return commandkit . Acquired [ ckconfig . View ] {
Value : ckconfig . View {
Effective : inspection ,
Sources : inspection ,
Explanation : inspection ,
} ,
2026-10-26 03:26:14 +00:00
Diagnostics : messages . diagnostics ,
Explanation : messages . explanation ,
2026-10-11 01:43:02 +00:00
} , nil
}
2026-10-12 02:01:10 +00:00
scope . Compare = func ( ctx context . Context , request ckconfig . ComparisonRequest [ I ] , state S ) ( commandkit . Acquired [ [ ] configkit . Difference ] , error ) {
if err := promptKitContextError ( ctx ) ; err != nil {
return commandkit . Acquired [ [ ] configkit . Difference ] { } , fmt . Errorf ( "PromptKit scope %q comparison before left load: %w" , scope . Name , err )
}
2026-10-26 03:26:14 +00:00
_ , leftInspection , messages , err := loadPromptKitEngine ( ctx , request . Left . Selection , request . Left . Input , state , "left" , load )
2026-10-12 02:01:10 +00:00
if err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ [ ] configkit . Difference ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , fmt . Errorf ( "PromptKit scope %q left comparison load: %w" , scope . Name , err )
2026-10-12 02:01:10 +00:00
}
if err := promptKitContextError ( ctx ) ; err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ [ ] configkit . Difference ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , fmt . Errorf ( "PromptKit scope %q comparison after left load: %w" , scope . Name , err )
2026-10-12 02:01:10 +00:00
}
2026-10-26 03:26:14 +00:00
_ , rightInspection , rightMessages , err := loadPromptKitEngine ( ctx , request . Right . Selection , request . Right . Input , state , "right" , load )
messages = appendPromptKitAcquisition ( messages , rightMessages )
2026-10-12 02:01:10 +00:00
if err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ [ ] configkit . Difference ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , fmt . Errorf ( "PromptKit scope %q right comparison load: %w" , scope . Name , err )
2026-10-12 02:01:10 +00:00
}
if err := promptKitContextError ( ctx ) ; err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ [ ] configkit . Difference ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , fmt . Errorf ( "PromptKit scope %q comparison before native diff: %w" , scope . Name , err )
2026-10-12 02:01:10 +00:00
}
differences , err := leftInspection . Diff ( rightInspection )
2026-10-26 14:18:21 +00:00
messages = messages . withOperationError ( err )
2026-10-12 02:01:10 +00:00
if err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ [ ] configkit . Difference ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , fmt . Errorf ( "PromptKit scope %q native configuration diff: %w" , scope . Name , err )
2026-10-12 02:01:10 +00:00
}
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ [ ] configkit . Difference ] { Value : differences , Diagnostics : messages . diagnostics , Explanation : messages . explanation } , nil
2026-10-12 02:01:10 +00:00
}
2026-10-11 01:43:02 +00:00
return scope
}
2026-10-12 02:01:10 +00:00
func promptKitContextError ( ctx context . Context ) error {
if ctx == nil {
return nil
}
return ctx . Err ( )
}
2026-10-17 18:44:10 +00:00
// FromEngine supplies eight PromptKit-backed callbacks: prompt, profile,
// backend, namespace, and collection catalogs, plus named prompt and profile
// inspection and requirements discovery.
2026-10-12 22:25:43 +00:00
// The requirements callback forwards RequirementsRequest.Inspection to
// Engine.InspectRequirements. Concrete request validation is an independent
2026-10-17 18:44:10 +00:00
// capability and can be composed explicitly with WithRequestValidation. Smoke
2026-10-17 19:15:58 +00:00
// testing is also an explicit capability installed with WithSmokeTesting.
2026-10-12 22:25:43 +00:00
// FromEngine does not call the loader during construction. Each selected
// operation passes its context, supplied selection, initialized input, and
// shared invocation state to the loader before calling PromptKit's public
// listing or inspection method. Applications can narrow, wrap, or replace these
// callbacks before Command captures them. A nil loader is retained as an
2026-10-26 03:26:14 +00:00
// adapter issue. Each callback returns independent loader diagnostics and
2026-10-26 14:18:21 +00:00
// terminal context alongside the native operation's value and error. Native
// operation errors supply terminal explanations when structured context is absent.
2026-10-11 01:43:02 +00:00
func FromEngine [ I , S any ] ( load func ( context . Context , ckconfig . Selection , I , S ) ( * promptkit . Engine , error ) ) Suite [ I , S ] {
if load == nil {
return Suite [ I , S ] { issues : [ ] error { errors . New ( "PromptKit engine loader is nil" ) } }
}
return Suite [ I , S ] {
2026-10-17 18:44:10 +00:00
ListPrompts : func ( ctx context . Context , request CatalogRequest , input I , state S ) ( commandkit . Acquired [ [ ] promptkit . PromptIdentity ] , error ) {
2026-10-26 03:26:14 +00:00
engine , _ , messages , err := loadPromptKitEngine ( ctx , request . Selection , input , state , "" , load )
2026-10-12 02:10:45 +00:00
if err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ [ ] promptkit . PromptIdentity ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-12 02:10:45 +00:00
}
2026-10-17 18:44:10 +00:00
prompts , err := engine . ListPrompts ( ctx , request . Filter )
2026-10-26 14:18:21 +00:00
messages = messages . withOperationError ( err )
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ [ ] promptkit . PromptIdentity ] { Value : prompts , Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-12 02:10:45 +00:00
} ,
2026-10-17 18:44:10 +00:00
ListProfiles : func ( ctx context . Context , request CatalogRequest , input I , state S ) ( commandkit . Acquired [ [ ] string ] , error ) {
2026-10-26 03:26:14 +00:00
engine , _ , messages , err := loadPromptKitEngine ( ctx , request . Selection , input , state , "" , load )
2026-10-12 02:10:45 +00:00
if err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ [ ] string ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-12 02:10:45 +00:00
}
2026-10-17 18:44:10 +00:00
profiles , err := engine . ListProfiles ( ctx , request . Filter )
2026-10-26 14:18:21 +00:00
messages = messages . withOperationError ( err )
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ [ ] string ] { Value : profiles , Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-12 02:10:45 +00:00
} ,
2026-10-17 18:44:10 +00:00
ListBackends : func ( ctx context . Context , request CatalogRequest , input I , state S ) ( commandkit . Acquired [ [ ] string ] , error ) {
2026-10-26 03:26:14 +00:00
engine , _ , messages , err := loadPromptKitEngine ( ctx , request . Selection , input , state , "" , load )
2026-10-17 18:44:10 +00:00
if err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ [ ] string ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-17 18:44:10 +00:00
}
backends , err := engine . ListBackends ( ctx , request . Filter )
2026-10-26 14:18:21 +00:00
messages = messages . withOperationError ( err )
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ [ ] string ] { Value : backends , Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-17 18:44:10 +00:00
} ,
ListNamespaces : func ( ctx context . Context , request NamespaceRequest , input I , state S ) ( commandkit . Acquired [ [ ] string ] , error ) {
2026-10-26 03:26:14 +00:00
engine , _ , messages , err := loadPromptKitEngine ( ctx , request . Selection , input , state , "" , load )
2026-10-17 18:44:10 +00:00
if err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ [ ] string ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-17 18:44:10 +00:00
}
namespaces , err := engine . ListNamespaces ( ctx , request . Kinds ... )
2026-10-26 14:18:21 +00:00
messages = messages . withOperationError ( err )
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ [ ] string ] { Value : namespaces , Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-17 18:44:10 +00:00
} ,
ListCollections : func ( ctx context . Context , selection ckconfig . Selection , input I , state S ) ( commandkit . Acquired [ [ ] promptkit . CollectionInfo ] , error ) {
2026-10-26 03:26:14 +00:00
engine , _ , messages , err := loadPromptKitEngine ( ctx , selection , input , state , "" , load )
2026-10-17 18:44:10 +00:00
if err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ [ ] promptkit . CollectionInfo ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-17 18:44:10 +00:00
}
collections , err := engine . ListCollections ( ctx )
2026-10-26 14:18:21 +00:00
messages = messages . withOperationError ( err )
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ [ ] promptkit . CollectionInfo ] { Value : collections , Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-17 18:44:10 +00:00
} ,
2026-10-11 01:43:02 +00:00
InspectPrompt : func ( ctx context . Context , request PromptRequest , input I , state S ) ( commandkit . Acquired [ * promptkit . PromptInspection ] , error ) {
2026-10-26 03:26:14 +00:00
engine , _ , messages , err := loadPromptKitEngine ( ctx , request . Selection , input , state , "" , load )
2026-10-11 01:43:02 +00:00
if err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ * promptkit . PromptInspection ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-11 01:43:02 +00:00
}
2026-10-17 18:34:03 +00:00
inspection , err := engine . InspectPrompt ( ctx , request . ID )
2026-10-26 14:18:21 +00:00
messages = messages . withOperationError ( err )
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ * promptkit . PromptInspection ] { Value : inspection , Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-11 01:43:02 +00:00
} ,
InspectProfile : func ( ctx context . Context , request ProfileRequest , input I , state S ) ( commandkit . Acquired [ * promptkit . ProfileInspection ] , error ) {
2026-10-26 03:26:14 +00:00
engine , _ , messages , err := loadPromptKitEngine ( ctx , request . Selection , input , state , "" , load )
2026-10-11 01:43:02 +00:00
if err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ * promptkit . ProfileInspection ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-11 01:43:02 +00:00
}
inspection , err := engine . InspectProfile ( ctx , request . ID )
2026-10-26 14:18:21 +00:00
messages = messages . withOperationError ( err )
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ * promptkit . ProfileInspection ] { Value : inspection , Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-11 01:43:02 +00:00
} ,
2026-10-12 16:54:00 +00:00
InspectRequirements : func ( ctx context . Context , request RequirementsRequest , input I , state S ) ( commandkit . Acquired [ * promptkit . RequirementInspection ] , error ) {
2026-10-26 03:26:14 +00:00
engine , _ , messages , err := loadPromptKitEngine ( ctx , request . Selection , input , state , "" , load )
2026-10-12 16:54:00 +00:00
if err != nil {
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ * promptkit . RequirementInspection ] { Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-12 16:54:00 +00:00
}
inspection , err := engine . InspectRequirements ( ctx , request . Inspection )
2026-10-26 14:18:21 +00:00
messages = messages . withOperationError ( err )
2026-10-26 03:26:14 +00:00
return commandkit . Acquired [ * promptkit . RequirementInspection ] { Value : inspection , Diagnostics : messages . diagnostics , Explanation : messages . explanation } , err
2026-10-12 16:54:00 +00:00
} ,
2026-10-11 01:43:02 +00:00
}
}
func loadPromptKitEngine [ I , S any ] (
ctx context . Context ,
selection ckconfig . Selection ,
input I ,
state S ,
2026-10-12 01:55:08 +00:00
side string ,
2026-10-11 01:43:02 +00:00
load engineLoader [ I , S ] ,
2026-10-26 03:26:14 +00:00
) ( * promptkit . Engine , promptkit . ConfigurationInspection , promptKitAcquisition , error ) {
2026-10-11 01:43:02 +00:00
engine , err := load ( ctx , selection , input , state )
if err != nil {
var loadErr * configkit . LoadError
if errors . As ( err , & loadErr ) {
2026-10-26 03:26:14 +00:00
messages := promptKitDiagnostics ( "promptkit" , side , loadErr . Sources ( ) , loadErr . Diagnostics ( ) )
return nil , promptkit . ConfigurationInspection { } , messages , err
2026-10-11 01:43:02 +00:00
}
2026-10-26 03:26:14 +00:00
return nil , promptkit . ConfigurationInspection { } , promptKitAcquisition { } , err
2026-10-11 01:43:02 +00:00
}
if engine == nil {
2026-10-26 03:26:14 +00:00
return nil , promptkit . ConfigurationInspection { } , promptKitAcquisition { } , errors . New ( "PromptKit configuration loader returned a nil engine" )
2026-10-11 01:43:02 +00:00
}
inspection , err := engine . InspectConfiguration ( )
2026-10-26 03:26:14 +00:00
messages := promptKitDiagnostics ( "promptkit" , side , inspection . Sources ( ) , inspection . Diagnostics ( ) )
2026-10-26 14:18:21 +00:00
messages = messages . withOperationError ( err )
2026-10-11 01:43:02 +00:00
if err != nil {
2026-10-26 03:26:14 +00:00
return nil , inspection , messages , err
2026-10-11 01:43:02 +00:00
}
2026-10-12 01:55:08 +00:00
if ctx != nil {
if err := ctx . Err ( ) ; err != nil {
2026-10-26 03:26:14 +00:00
return nil , promptkit . ConfigurationInspection { } , messages , err
2026-10-12 01:55:08 +00:00
}
}
2026-10-26 03:26:14 +00:00
return engine , inspection , messages , nil
2026-10-11 01:43:02 +00:00
}
2026-10-26 03:26:14 +00:00
func appendPromptKitAcquisition ( left , right promptKitAcquisition ) promptKitAcquisition {
left . diagnostics = append ( left . diagnostics , right . diagnostics ... )
left . explanation . Details = append ( left . explanation . Details , right . explanation . Details ... )
if left . explanation . Message == "" {
left . explanation . Message = right . explanation . Message
2026-10-11 01:43:02 +00:00
}
2026-10-26 03:26:14 +00:00
return left
}
func promptKitDiagnostics ( scope , side string , sources [ ] configkit . SourceInfo , records [ ] configkit . Diagnostic ) promptKitAcquisition {
if len ( records ) == 0 {
return promptKitAcquisition { }
2026-10-11 01:43:02 +00:00
}
// Snapshot provider-owned slices before deferring their report to invocation.
sources = append ( [ ] configkit . SourceInfo ( nil ) , sources ... )
records = append ( [ ] configkit . Diagnostic ( nil ) , records ... )
2026-10-11 01:46:54 +00:00
for index := range records {
if records [ index ] . Primary != nil {
primary := * records [ index ] . Primary
records [ index ] . Primary = & primary
}
records [ index ] . Related = append ( [ ] configkit . Location ( nil ) , records [ index ] . Related ... )
}
2026-10-26 03:26:14 +00:00
var warnings , failures [ ] configkit . Diagnostic
for _ , record := range records {
if record . Severity == configkit . SeverityError {
failures = append ( failures , record )
} else {
warnings = append ( warnings , record )
}
}
messages := promptKitAcquisition { }
if len ( warnings ) > 0 {
messages . diagnostics = [ ] commandkit . Diagnostic { {
Severity : commandkit . SeverityWarning ,
Write : func ( writer io . Writer ) error {
return writePromptKitDiagnostics ( writer , scope , side , sources , warnings )
} ,
} }
}
if len ( failures ) > 0 {
messages . explanation = commandkit . Explanation { Details : [ ] commandkit . Detail { func ( writer io . Writer ) error {
return writePromptKitDiagnostics ( writer , scope , side , sources , failures )
} } }
}
return messages
}
func writePromptKitDiagnostics ( writer io . Writer , scope , side string , sources [ ] configkit . SourceInfo , records [ ] configkit . Diagnostic ) error {
heading := "Configuration scope " + strconv . Quote ( scope )
if side != "" {
heading += " (" + side + ")"
}
if _ , err := fmt . Fprintf ( writer , "%s:\n" , heading ) ; err != nil {
return err
}
if len ( sources ) > 0 {
if _ , err := io . WriteString ( writer , "Source IDs:\n" ) ; err != nil {
return err
}
for _ , source := range sources {
if _ , err := fmt . Fprintf ( writer , " %s: label=%s, path=%s\n" , strconv . Quote ( source . ID ) , strconv . Quote ( source . Label ) , strconv . Quote ( source . Path ) ) ; err != nil {
2026-10-11 01:43:02 +00:00
return err
}
2026-10-26 03:26:14 +00:00
}
}
return configkit . WriteDiagnostics ( writer , records )
2026-10-11 01:43:02 +00:00
}