class LittleGhost::PromptResolver
PromptResolver turns conventional ERB files into an agent’s system prompt. It supports ordered application roots and partials without allowing a template name to escape those roots.
resolver = LittleGhost::PromptResolver.new(paths: ["app/prompts"]) prompt = resolver.render("support/system", locals: {product: "Acme"}) prompt.include?("Acme") # => true
In support/system.erb:
<%= partial "shared/rules", locals: {product: product} %>
Earlier invocation roots override configured roots. Template names must be relative, and both lexical traversal and symbolic-link escapes are rejected. Partials use an underscore-prefixed filename and receive only their explicitly supplied locals.
Every configured root is trusted Ruby code because ERB executes inside the current process. Keep roots application-controlled and non-user-writable.
Public Class Methods
# File lib/little_ghost/prompt_resolver.rb, line 79 def initialize(paths: [], max_depth: DEFAULT_MAX_DEPTH) @paths = normalize_roots(paths) @max_depth = Integer(max_depth) raise ArgumentError, "max_depth must be positive" unless @max_depth.positive? @cache = {} @cache_mutex = Mutex.new end
Configures ordered application roots and a partial recursion bound, which defaults to 20 nested templates.
Public Instance Methods
# File lib/little_ghost/prompt_resolver.rb, line 93 def render(name, locals: {}, invocation_paths: []) roots = normalize_invocation_roots(invocation_paths) + @paths render_template(normalize_name(name), locals, roots, []) end
Renders name with validated local variables.
invocation_paths accepts only TrustedPath values because those roots take precedence over application configuration. The wrapper records the caller’s trust decision; it does not make an untrusted directory safe.