package promptkit import ( "context" "fmt" "io/fs" "os" "path/filepath" "gitea.maximumdirect.net/eric/ConfigKit" "gitea.maximumdirect.net/eric/promptkit/internal/domain" "gitea.maximumdirect.net/eric/promptkit/internal/filecatalog" ) // CollectionInput mounts source at the selected collection's configuration // destination and enables ConfigKit imports for that input. The source is not // read or validated until ConfigKit loads it. Callers may adjust the returned // input, including its filesystem context or imports setting. func CollectionInput(namespace string, source configkit.Source) (configkit.Input, error) { if !domain.IsValidResourceNamespace(namespace) { return configkit.Input{}, fmt.Errorf("%w: invalid collection namespace %q", ErrInvalidConfig, namespace) } return configkit.Input{ Source: source, Destination: configkit.Keys("collections", namespace), Imports: true, }, nil } // DirectoryInputs selects the YAML files beneath an explicitly chosen host // directory as one strict-composition group. The returned order is deterministic; // it does not assign precedence among those files. Discovery is recursive over // .yaml and .yml files. An unavailable root fails; an empty directory yields // no inputs. Imports are disabled unless callers enable them on the inputs. func DirectoryInputs(directory string) ([]configkit.Input, error) { root, err := filepath.Abs(directory) if err != nil { return nil, fmt.Errorf("resolve configuration directory: %w", err) } info, err := os.Stat(root) if err != nil { return nil, fmt.Errorf("inspect configuration directory: %w", err) } if !info.IsDir() { return nil, fmt.Errorf("configuration root is not a directory: %s", root) } root, err = filepath.EvalSymlinks(root) if err != nil { return nil, fmt.Errorf("resolve configuration directory: %w", err) } files, err := filecatalog.FindYAMLFiles(context.Background(), root) if err != nil { return nil, fmt.Errorf("discover configuration files: %w", err) } inputs := make([]configkit.Input, 0, len(files)) for _, file := range files { inputs = append(inputs, configkit.Input{ Source: configkit.File(file), Filesystem: &configkit.FilesystemContext{Root: root}, }) } return inputs, nil } // FilesystemInputs selects the YAML files beneath root in a caller-owned // filesystem as one strict-composition group. Discovery is recursive over // .yaml and .yml files, with deterministic ordering and no precedence among // files. The caller retains ownership of the filesystem; an unavailable root // fails and an empty directory yields no inputs. Imports remain disabled unless // callers enable them on inputs. func FilesystemInputs(label string, filesystem fs.FS, root string) ([]configkit.Input, error) { if filesystem == nil { return nil, fmt.Errorf("configuration filesystem is nil") } root = filecatalog.CleanFSRoot(root) if !fs.ValidPath(root) { return nil, fmt.Errorf("configuration root is not a valid filesystem path: %s", root) } info, err := fs.Stat(filesystem, root) if err != nil { return nil, fmt.Errorf("inspect configuration root: %w", err) } if !info.IsDir() { return nil, fmt.Errorf("configuration root is not a directory: %s", root) } files, err := filecatalog.FindFSYAMLFiles(context.Background(), filesystem, root) if err != nil { return nil, fmt.Errorf("discover configuration files: %w", err) } namespace := configkit.NewFilesystem(label, filesystem) inputs := make([]configkit.Input, 0, len(files)) for _, file := range files { inputs = append(inputs, configkit.Input{ Source: namespace.File(file), Filesystem: &configkit.FilesystemContext{FS: &namespace, Root: root}, }) } return inputs, nil }