0.01
A long-lived project that still receives updates
Manage the parameterized prompts (text) used in generative AI (aka chatGPT, OpenAI, et.al.) using storage adapters such as FileSystemAdapter, SqliteAdapter, and ActiveRecordAdapter.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies
 Project Readme

PromptManager

Caution

⚠️ Breaking Changes are Coming ⚠️

See Roadmap for details about upcoming changes.


PromptManager - The Enchanted Librarian of AI Prompts

[Comprehensive Documentation Website](https://madbomber.github.io/prompt_manager/)
Like an enchanted librarian organizing floating books of knowledge, PromptManager helps you masterfully orchestrate and organize your AI prompts through wisdom and experience. Each prompt becomes a living entity that can be categorized, parameterized, and interconnected with golden threads of relationships.

Key Features

  • 📚 Multiple Storage Adapters
  • 🔧 Parameterized Prompts
  • 📋 Directive Processing
  • 🎨 ERB Integration
  • 🌍 Shell Integration
  • 📖 Inline Documentation
  • 📊 Parameter History
  • ⚡ Error Handling
  • 🔌 Extensible Architecture

Table of Contents

  • Installation
  • Quick Start
  • Core Features
    • Parameterized Prompts
      • Keyword Syntax
      • Custom Patterns
      • Working with Parameters
    • Directive Processing
      • Built-in Directives
      • Directive Syntax
      • Custom Directive Processors
    • ERB and Shell Integration
      • ERB Templates
      • Environment Variables
    • Comments and Documentation
      • Line Comments
      • Block Comments
      • Blank Lines
    • Parameter History
    • Error Handling
  • Storage Adapters
    • FileSystemAdapter
      • Configuration
      • File Structure
      • Custom Search
      • Extra Methods
    • ActiveRecordAdapter
      • Configuration
      • Database Setup
    • Custom Adapters
  • Configuration
    • Initialization Options
    • Global Configuration
  • Advanced Usage
    • Custom Keyword Patterns
    • Dynamic Directives
    • Search Capabilities
  • Examples
    • Basic Usage
    • With Search
    • Custom Storage
  • Extensible Architecture
    • Extension Points
    • Potential Extensions
  • Roadmap
    • v0.9.0 - Modern Prompt Format (Breaking Changes)
    • v1.0.0 - Stability Release
    • Future Enhancements
  • Development
  • Contributing
  • License

Installation

Install the gem and add to the application's Gemfile by executing:

bundle add prompt_manager

If bundler is not being used to manage dependencies, install the gem by executing:

gem install prompt_manager

Quick Start

require 'prompt_manager'

# Configure storage adapter
PromptManager::Prompt.storage_adapter =
  PromptManager::Storage::FileSystemAdapter.config do |config|
    config.prompts_dir = '~/.prompts'
  end.new

# Load and use a prompt
prompt = PromptManager::Prompt.new(id: 'greeting')
prompt.parameters = {
  "[NAME]" => "Alice",
  "[LANGUAGE]" => "English"
}

# Get the processed prompt text
result = prompt.to_s

Core Features

Parameterized Prompts

The heart of PromptManager is its ability to manage parameterized prompts - text templates with embedded keywords that can be replaced with dynamic values.

Keyword Syntax

By default, keywords are enclosed in square brackets: [KEYWORD], [MULTIPLE WORDS], or [WITH_UNDERSCORES].

prompt_text = "Hello [NAME], please translate this to [LANGUAGE]"

Custom Patterns

You can customize the keyword pattern to match your preferences:

# Use {{mustache}} style
PromptManager::Prompt.parameter_regex = /(\{\{[A-Za-z_]+\}\})/

# Use :colon style
PromptManager::Prompt.parameter_regex = /(:[a-z_]+)/

The regex must include capturing parentheses () to extract the keyword.

Working with Parameters

prompt = PromptManager::Prompt.new(id: 'example')

# Get all keywords found in the prompt
keywords = prompt.keywords  #=> ["[NAME]", "[LANGUAGE]"]

# Set parameter values
prompt.parameters = {
  "[NAME]" => "Alice",
  "[LANGUAGE]" => "French"
}

# Get processed text with substitutions
final_text = prompt.to_s

# Save changes
prompt.save

Directive Processing

Directives are special line oriented instructions in your prompts that begin with // starting in column 1. They're inspired by IBM JCL and provide powerful prompt composition capabilities. A character string that begins with // but is not at the very beginning of the line will NOT be processed as a directive.

Built-in Directives

//include (alias: //import) - Include content from other files:

//include common/header.txt
//import [TEMPLATE_NAME].txt

Main prompt content here...

Features:

  • Loop protection prevents circular includes
  • Supports keyword substitution in file paths
  • Processes included files recursively

Directive Syntax

//directive_name [PARAM1] [PARAM2] options

# Dynamic directives using keywords
//[COMMAND] [OPTIONS]

Custom Directive Processors

You can create custom directive processors:

class MyDirectiveProcessor < PromptManager::DirectiveProcessor
  def process_directive(directive, prompt)
    case directive
    when /^\/\/model (.+)$/
      set_model($1)
    when /^\/\/temperature (.+)$/
      set_temperature($1.to_f)
    else
      super
    end
  end
end

prompt = PromptManager::Prompt.new(
  id: 'example',
  directives_processor: MyDirectiveProcessor.new
)

ERB and Shell Integration

ERB Templates

Enable ERB processing for dynamic content generation:

prompt = PromptManager::Prompt.new(
  id: 'dynamic',
  erb_flag: true
)

Example prompt with ERB:

Today's date is <%= Date.today %>
<% 5.times do |i| %>
  Item <%= i + 1 %>
<% end %>

Environment Variables

Enable automatic environment variable substitution:

prompt = PromptManager::Prompt.new(
  id: 'with_env',
  envar_flag: true
)

Environment variables are automatically replaced in the prompt text.

Comments and Documentation

PromptManager supports comprehensive inline documentation:

Line Comments

Lines beginning with # are treated as comments:

# This is a comment
# Description: This prompt does something useful

Actual prompt text here...

Block Comments

Everything after __END__ is ignored:

Main prompt content...

__END__
Development notes:
- This section is completely ignored
- Great for documentation
- TODO items

Blank Lines

Blank lines are automatically removed from the final output.

Parameter History

PromptManager maintains a history of parameter values (since v0.3.0):

# Parameters are stored as arrays
prompt.parameters = {
  "[NAME]" => ["Alice", "Bob", "Charlie"]  # Charlie is most recent
}

# The last value is always the most recent
current_name = prompt.parameters["[NAME]"].last

# Useful for:
# - Implementing value history in UIs
# - Providing dropdown selections
# - Tracking parameter usage over time

Error Handling

PromptManager provides specific error classes for better debugging:

begin
  prompt = PromptManager::Prompt.new(id: 'missing')
rescue PromptManager::StorageError => e
  # Handle storage-related errors
  puts "Storage error: #{e.message}"
rescue PromptManager::ParameterError => e
  # Handle parameter substitution errors
  puts "Parameter error: #{e.message}"
rescue PromptManager::ConfigurationError => e
  # Handle configuration errors
  puts "Configuration error: #{e.message}"
end

Storage Adapters

Storage adapters provide the persistence layer for prompts. PromptManager includes two built-in adapters and supports custom implementations.

FileSystemAdapter

Stores prompts as text files in a directory structure.

Configuration

PromptManager::Storage::FileSystemAdapter.config do |config|
  config.prompts_dir       = "~/.prompts"      # Required
  config.search_proc       = nil               # Optional custom search
  config.prompt_extension  = '.txt'            # Default
  config.params_extension  = '.json'           # Default
end

File Structure

~/.prompts/
├── greeting.txt        # Prompt text
├── greeting.json       # Parameters
├── email/
│   ├── welcome.txt
│   └── welcome.json

Custom Search

Integrate with external search tools:

config.search_proc = ->(query) {
  # Use ripgrep for fast searching
  `rg -l "#{query}" #{config.prompts_dir}`.split("\n")
}

Extra Methods

  • list - Returns array of all prompt IDs
  • path(id) - Returns Pathname to prompt file

ActiveRecordAdapter

Stores prompts in a database using ActiveRecord.

Configuration

PromptManager::Storage::ActiveRecordAdapter.config do |config|
  config.model              = PromptModel      # Your AR model
  config.id_column          = :prompt_id       # Column for ID
  config.text_column        = :content         # Column for text
  config.parameters_column  = :params          # Column for parameters
end

Database Setup

class CreatePrompts < ActiveRecord::Migration[7.0]
  def change
    create_table :prompts do |t|
      t.string :prompt_id, null: false, index: { unique: true }
      t.text :content
      t.json :params
      t.timestamps
    end
  end
end

Custom Adapters

Create your own storage adapter:

class RedisAdapter
  def initialize(redis_client)
    @redis = redis_client
  end

  def get(id)
    prompt_text = @redis.get("prompt:#{id}:text")
    parameters = JSON.parse(@redis.get("prompt:#{id}:params") || '{}')
    [prompt_text, parameters]
  end

  def save(id, text, parameters)
    @redis.set("prompt:#{id}:text", text)
    @redis.set("prompt:#{id}:params", parameters.to_json)
  end

  def delete(id)
    @redis.del("prompt:#{id}:text", "prompt:#{id}:params")
  end

  def list
    @redis.keys("prompt:*:text").map { |k| k.split(':')[1] }
  end
end

Configuration

Initialization Options

When creating a prompt instance:

prompt = PromptManager::Prompt.new(
  id: 'example',
  context: ['additional', 'context'],
  directives_processor: CustomProcessor.new,
  external_binding: binding,
  erb_flag: true,
  envar_flag: true
)

Options:

  • id - Unique identifier for the prompt
  • context - Additional context array
  • directives_processor - Custom directive processor
  • external_binding - Ruby binding for ERB
  • erb_flag - Enable ERB processing
  • envar_flag - Enable environment variable substitution

Global Configuration

Set the storage adapter globally:

PromptManager::Prompt.storage_adapter = adapter_instance

Advanced Usage

Custom Keyword Patterns

Examples of different keyword patterns:

# Handlebars style: {{name}}
PromptManager::Prompt.parameter_regex = /(\{\{[a-z_]+\}\})/

# Colon prefix: :name
PromptManager::Prompt.parameter_regex = /(:[a-z_]+)/

# Dollar sign: $NAME
PromptManager::Prompt.parameter_regex = /(\$[A-Z_]+)/

# Percentage: %name%
PromptManager::Prompt.parameter_regex = /(%[a-z_]+%)/

Dynamic Directives

Create directives that change based on parameters:

# Set directive name via parameter
//[DIRECTIVE_TYPE] [OPTIONS]

# Conditional directives
//include templates/[TEMPLATE_TYPE].txt

Search Capabilities

Implement powerful search across prompts:

# With FileSystemAdapter
adapter.search_proc = ->(query) {
  # Custom search implementation
  results = []
  Dir.glob("#{prompts_dir}/**/*.txt").each do |file|
    content = File.read(file)
    if content.include?(query)
      results << File.basename(file, '.txt')
    end
  end
  results
}

# With ActiveRecordAdapter
PromptModel.where("content LIKE ?", "%#{query}%").pluck(:prompt_id)

Examples

Basic Usage

# examples/simple.rb
require 'prompt_manager'

# Setup
PromptManager::Prompt.storage_adapter =
  PromptManager::Storage::FileSystemAdapter.config do |c|
    c.prompts_dir = '~/.prompts'
  end.new

# Create and use a prompt
prompt = PromptManager::Prompt.new(id: 'story')
prompt.parameters = {
  "[GENRE]" => "fantasy",
  "[CHARACTER]" => "wizard"
}

puts prompt.to_s

Advanced Integration with LLM and Streaming

See examples/advanced_integrations.rb for a complete example that demonstrates:

  • ERB templating for dynamic content generation
  • Shell integration for environment variable substitution
  • OpenAI API integration with streaming responses
  • Professional UI with spinner feedback using tty-spinner

This example shows how to create sophisticated AI prompts that adapt to your system environment and stream responses in real-time.

With Search

See examples/using_search_proc.rb for advanced search integration.

Custom Storage

# examples/redis_storage.rb
class RedisStorage
  # ... implementation
end

PromptManager::Prompt.storage_adapter = RedisStorage.new(Redis.new)

Extensible Architecture

PromptManager is designed to be extended:

Extension Points

  1. Storage Adapters - Implement your own persistence layer
  2. Directive Processors - Add custom directives
  3. Search Processors - Integrate external search tools
  4. Serializers - Support different parameter formats

Potential Extensions

  • CloudStorageAdapter - S3, Google Cloud Storage
  • RedisAdapter - For caching and fast access
  • ApiAdapter - REST API backend
  • GraphQLAdapter - GraphQL endpoint storage
  • GitAdapter - Version controlled prompts

Roadmap

v0.9.0 - Modern Prompt Format (Breaking Changes)

  • Markdown Support: Full .md file support with YAML front matter
  • Modern Parameter Syntax: Support for {{keyword}} format
  • Enhanced API: New set_parameter() and get_parameter() methods
  • Parameter Validation: Built-in validation based on specifications
  • HTML Comments: Support for <!-- comments -->
  • Migration Tools: Automated conversion utilities

v1.0.0 - Stability Release

  • Performance optimizations
  • Complete documentation
  • Production hardening

Future Enhancements

  • Additional storage adapters
  • Enhanced directive system with plugins
  • Prompt versioning and inheritance
  • Performance optimizations for large collections

Development

Looking for feedback and contributors to enhance the capability of prompt_manager.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/MadBomber/prompt_manager.

License

The gem is available as open source under the terms of the MIT License.