prompt_manager 0.5.7 → 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/COMMITS.md +196 -0
  4. data/README.md +485 -203
  5. data/docs/.keep +0 -0
  6. data/docs/advanced/custom-keywords.md +421 -0
  7. data/docs/advanced/dynamic-directives.md +535 -0
  8. data/docs/advanced/performance.md +612 -0
  9. data/docs/advanced/search-integration.md +635 -0
  10. data/docs/api/configuration.md +355 -0
  11. data/docs/api/directive-processor.md +431 -0
  12. data/docs/api/prompt-class.md +354 -0
  13. data/docs/api/storage-adapters.md +462 -0
  14. data/docs/assets/favicon.ico +1 -0
  15. data/docs/assets/logo.svg +24 -0
  16. data/docs/core-features/comments.md +48 -0
  17. data/docs/core-features/directive-processing.md +38 -0
  18. data/docs/core-features/erb-integration.md +68 -0
  19. data/docs/core-features/error-handling.md +197 -0
  20. data/docs/core-features/parameter-history.md +76 -0
  21. data/docs/core-features/parameterized-prompts.md +500 -0
  22. data/docs/core-features/shell-integration.md +79 -0
  23. data/docs/development/architecture.md +544 -0
  24. data/docs/development/contributing.md +425 -0
  25. data/docs/development/roadmap.md +234 -0
  26. data/docs/development/testing.md +822 -0
  27. data/docs/examples/advanced.md +523 -0
  28. data/docs/examples/basic.md +688 -0
  29. data/docs/examples/real-world.md +776 -0
  30. data/docs/examples.md +337 -0
  31. data/docs/getting-started/basic-concepts.md +318 -0
  32. data/docs/getting-started/installation.md +97 -0
  33. data/docs/getting-started/quick-start.md +256 -0
  34. data/docs/index.md +230 -0
  35. data/docs/migration/v0.9.0.md +459 -0
  36. data/docs/migration/v1.0.0.md +591 -0
  37. data/docs/storage/activerecord-adapter.md +348 -0
  38. data/docs/storage/custom-adapters.md +176 -0
  39. data/docs/storage/filesystem-adapter.md +236 -0
  40. data/docs/storage/overview.md +427 -0
  41. data/examples/advanced_integrations.rb +52 -0
  42. data/examples/prompts_dir/advanced_demo.txt +79 -0
  43. data/examples/prompts_dir/directive_example.json +1 -0
  44. data/examples/prompts_dir/directive_example.txt +8 -0
  45. data/examples/prompts_dir/todo.json +1 -1
  46. data/improvement_plan.md +996 -0
  47. data/lib/prompt_manager/storage/file_system_adapter.rb +8 -2
  48. data/lib/prompt_manager/version.rb +1 -1
  49. data/mkdocs.yml +146 -0
  50. data/prompt_manager_logo.png +0 -0
  51. metadata +46 -3
  52. data/LICENSE.txt +0 -21
data/README.md CHANGED
@@ -1,58 +1,92 @@
1
1
  # PromptManager
2
2
 
3
- Manage the parameterized prompts (text) used in generative AI (aka chatGPT, OpenAI, _et.al._) using storage adapters such as FileSystemAdapter and ActiveRecordAdapter.
4
-
5
- **Current Version**: 0.5.3
6
-
7
- **Breaking Change** in version 0.3.0 - The value of the parameters Hash for a keyword is now an Array instead of a single value. The last value in the Array is always the most recent value used for the given keyword. This was done to support the use of a Readline::History object editing in the [aia](https://github.com/MadBomber/aia) CLI tool
8
- ### Latest Capabilities
9
- <!-- Tocer[start]: Auto-generated, don't remove. -->
3
+ > [!CAUTION]
4
+ > ## ⚠️ Breaking Changes are Coming ⚠️
5
+ > See [Roadmap](#roadmap) for details about upcoming changes.
6
+ <br />
7
+ <div align="center">
8
+ <table>
9
+ <tr>
10
+ <td width="40%" align="center" valign="top">
11
+ <a href="https://madbomber.github.io/blog/" target="_blank">
12
+ <img src="prompt_manager_logo.png" alt="PromptManager - The Enchanted Librarian of AI Prompts" width="800">
13
+ </a>
14
+ <br /><br />
15
+ [Comprehensive Documentation Website](https://madbomber.github.io/prompt_manager/)
16
+ </td>
17
+ <td width="60%" align="left" valign="top">
18
+ 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.
19
+ <br/><br/>
20
+ <h3>Key Features</h3>
21
+ <ul>
22
+ <li><strong>📚 <a href="#storage-adapters">Multiple Storage Adapters</a></strong>
23
+ <li><strong>🔧 <a href="#parameterized-prompts">Parameterized Prompts</a></strong>
24
+ <li><strong>📋 <a href="#directive-processing">Directive Processing</a></strong>
25
+ <li><strong>🎨 <a href="#erb-and-shell-integration">ERB Integration</a></strong>
26
+ <li><strong>🌍 <a href="#erb-and-shell-integration">Shell Integration</a></strong>
27
+ <li><strong>📖 <a href="#comments-and-documentation">Inline Documentation</a></strong>
28
+ <li><strong>📊 <a href="#parameter-history">Parameter History</a></strong>
29
+ <li><strong>⚡ <a href="#error-handling">Error Handling</a></strong>
30
+ <li><strong>🔌 <a href="#extensible-architecture">Extensible Architecture</a></strong>
31
+ </ul>
32
+ </td>
33
+ </tr>
34
+ </table>
35
+ </div>
10
36
 
11
37
  ## Table of Contents
12
38
 
13
- - [Latest Capabilities](#latest-capabilities)
14
- - [Installation](#installation)
15
- - [Usage](#usage)
16
- - [Overview](#overview)
17
- - [Prompt Initialization Options](#prompt-initialization-options)
18
- - [Generative AI (gen-AI)](#generative-ai-gen-ai)
19
- - [What does a keyword look like?](#what-does-a-keyword-look-like)
20
- - [All about directives](#all-about-directives)
21
- - [Example Prompt with Directives](#example-prompt-with-directives)
22
- - [Accessing and Setting Parameter Values](#accessing-and-setting-parameter-values)
23
- - [Dynamic Directives](#dynamic-directives)
24
- - [Executing Directives](#executing-directives)
25
- - [Comments Are Ignored](#comments-are-ignored)
26
- - [Storage Adapters](#storage-adapters)
27
- - [FileSystemAdapter](#filesystemadapter)
28
- - [Configuration](#configuration)
29
- - [prompts_dir](#prompts_dir)
30
- - [search_proc](#search_proc)
31
- - [File Extensions](#file-extensions)
32
- - [Example Prompt Text File](#example-prompt-text-file)
33
- - [Example Prompt Parameters JSON File](#example-prompt-parameters-json-file)
34
- - [Extra Functionality](#extra-functionality)
35
- - [ActiveRecordAdapter](#activerecordadapter)
36
- - [Configuration](#configuration-1)
37
- - [model](#model)
38
- - [id_column](#id_column)
39
- - [text_column](#text_column)
40
- - [parameters_column](#parameters_column)
41
- - [Other Potential Storage Adapters](#other-potential-storage-adapters)
42
- - [Development](#development)
43
- - [Contributing](#contributing)
44
- - [License](#license)
45
-
46
- <!-- Tocer[finish]: Auto-generated, don't remove. -->
47
-
48
-
49
- - **Directive Processing:** Processes directives such as `//include` (aliased as `//import`) with loop protection.
50
- - **ERB Processing:** Supports ERB templating within prompts.
51
- - **Environment Variable Embedding:** Automatically substitutes system environment variables in prompts.
52
- - **Improved Parameter Handling:** Refactored to maintain a history of parameter values and added error handling for parameter substitution.
53
- - **ActiveRecord Adapter:** Facilitates storing and retrieving prompts via an ActiveRecord model.
54
- - **Enhanced Error Handling:** Added specific error classes for better error handling, including StorageError, ParameterError, and ConfigurationError.
55
- - **Customizable Keyword Pattern:** Ability to customize the pattern used for detecting keywords in prompts.
39
+ * [Installation](#installation)
40
+ * [Quick Start](#quick-start)
41
+ * [Core Features](#core-features)
42
+ * [Parameterized Prompts](#parameterized-prompts)
43
+ * [Keyword Syntax](#keyword-syntax)
44
+ * [Custom Patterns](#custom-patterns)
45
+ * [Working with Parameters](#working-with-parameters)
46
+ * [Directive Processing](#directive-processing)
47
+ * [Built\-in Directives](#built-in-directives)
48
+ * [Directive Syntax](#directive-syntax)
49
+ * [Custom Directive Processors](#custom-directive-processors)
50
+ * [ERB and Shell Integration](#erb-and-shell-integration)
51
+ * [ERB Templates](#erb-templates)
52
+ * [Environment Variables](#environment-variables)
53
+ * [Comments and Documentation](#comments-and-documentation)
54
+ * [Line Comments](#line-comments)
55
+ * [Block Comments](#block-comments)
56
+ * [Blank Lines](#blank-lines)
57
+ * [Parameter History](#parameter-history)
58
+ * [Error Handling](#error-handling)
59
+ * [Storage Adapters](#storage-adapters)
60
+ * [FileSystemAdapter](#filesystemadapter)
61
+ * [Configuration](#configuration)
62
+ * [File Structure](#file-structure)
63
+ * [Custom Search](#custom-search)
64
+ * [Extra Methods](#extra-methods)
65
+ * [ActiveRecordAdapter](#activerecordadapter)
66
+ * [Configuration](#configuration-1)
67
+ * [Database Setup](#database-setup)
68
+ * [Custom Adapters](#custom-adapters)
69
+ * [Configuration](#configuration-2)
70
+ * [Initialization Options](#initialization-options)
71
+ * [Global Configuration](#global-configuration)
72
+ * [Advanced Usage](#advanced-usage)
73
+ * [Custom Keyword Patterns](#custom-keyword-patterns)
74
+ * [Dynamic Directives](#dynamic-directives)
75
+ * [Search Capabilities](#search-capabilities)
76
+ * [Examples](#examples)
77
+ * [Basic Usage](#basic-usage)
78
+ * [With Search](#with-search)
79
+ * [Custom Storage](#custom-storage)
80
+ * [Extensible Architecture](#extensible-architecture)
81
+ * [Extension Points](#extension-points)
82
+ * [Potential Extensions](#potential-extensions)
83
+ * [Roadmap](#roadmap)
84
+ * [v0\.9\.0 \- Modern Prompt Format (Breaking Changes)](#v090---modern-prompt-format-breaking-changes)
85
+ * [v1\.0\.0 \- Stability Release](#v100---stability-release)
86
+ * [Future Enhancements](#future-enhancements)
87
+ * [Development](#development)
88
+ * [Contributing](#contributing)
89
+ * [License](#license)
56
90
 
57
91
  ## Installation
58
92
 
@@ -64,271 +98,519 @@ If bundler is not being used to manage dependencies, install the gem by executin
64
98
 
65
99
  gem install prompt_manager
66
100
 
67
- ## Usage
101
+ ## Quick Start
68
102
 
69
- See [examples/simple.rb](examples/simple.rb)
103
+ ```ruby
104
+ require 'prompt_manager'
70
105
 
71
- See also [examples/using_search_proc.rb](examples/using_search_proc.rb)
106
+ # Configure storage adapter
107
+ PromptManager::Prompt.storage_adapter =
108
+ PromptManager::Storage::FileSystemAdapter.config do |config|
109
+ config.prompts_dir = '~/.prompts'
110
+ end.new
72
111
 
73
- ## Overview
112
+ # Load and use a prompt
113
+ prompt = PromptManager::Prompt.new(id: 'greeting')
114
+ prompt.parameters = {
115
+ "[NAME]" => "Alice",
116
+ "[LANGUAGE]" => "English"
117
+ }
74
118
 
75
- ### Prompt Initialization Options
76
- - `id`: A String name for the prompt.
77
- - `context`: An Array for additional context.
78
- - `directives_processor`: An instance of PromptManager::DirectiveProcessor (default), can be customized.
79
- - `external_binding`: A Ruby binding to be used for ERB processing.
80
- - `erb_flag`: Boolean flag to enable ERB processing in the prompt text.
81
- - `envar_flag`: Boolean flag to enable environment variable substitution in the prompt text.
119
+ # Get the processed prompt text
120
+ result = prompt.to_s
121
+ ```
82
122
 
83
- The `prompt_manager` gem provides functionality to manage prompts that have keywords and directives for use with generative AI processes.
123
+ ## Core Features
84
124
 
85
- ### Generative AI (gen-AI)
125
+ ### Parameterized Prompts
86
126
 
87
- Gen-AI deals with the conversion (some would say execution) of a human natural language text (the "prompt") into something else using what are known as large language models (LLM) such as those available from OpenAI. A parameterized prompt is one in which there are embedded keywords (parameters) which are place holders for other text to be inserted into the prompt.
127
+ The heart of PromptManager is its ability to manage parameterized prompts - text templates with embedded keywords that can be replaced with dynamic values.
88
128
 
89
- The prompt_manager uses a regular expression to identify these keywords within the prompt. It uses the keywords as keys in a `parameters` Hash which is stored with the prompt text in a serialized form - for example as JSON.
129
+ #### Keyword Syntax
90
130
 
91
- #### What does a keyword look like?
131
+ By default, keywords are enclosed in square brackets: `[KEYWORD]`, `[MULTIPLE WORDS]`, or `[WITH_UNDERSCORES]`.
92
132
 
93
- By default, any text matching `[UPPERCASE_TEXT]` enclosed in square brackets is treated as a keyword. [KEYWORDS CAN ALSO HAVE SPACES] as well as the underscore character.
133
+ ```ruby
134
+ prompt_text = "Hello [NAME], please translate this to [LANGUAGE]"
135
+ ```
94
136
 
95
- You can customize the keyword pattern by setting a different regular expression:
137
+ #### Custom Patterns
138
+
139
+ You can customize the keyword pattern to match your preferences:
96
140
 
97
141
  ```ruby
98
- # Use {{param}} style instead of [PARAM]
142
+ # Use {{mustache}} style
99
143
  PromptManager::Prompt.parameter_regex = /(\{\{[A-Za-z_]+\}\})/
144
+
145
+ # Use :colon style
146
+ PromptManager::Prompt.parameter_regex = /(:[a-z_]+)/
100
147
  ```
101
148
 
102
- The regex must include capturing parentheses () to extract the keyword. The default regex is `/(\[[A-Z _|]+\])/`.
103
- #### All about directives
149
+ The regex must include capturing parentheses `()` to extract the keyword.
104
150
 
105
- A directive is a line in the prompt text that starts with the two characters '//' - slash slash - just like in the old days of IBM JCL - Job Control Language. A prompt can have zero or more directives. Directives can have parameters and can make use of keywords.
151
+ #### Working with Parameters
106
152
 
107
- The `prompt_manager` collects directives and provides a DirectiveProcessor class that currently implements the `//include` directive (also aliased as `//import`), which allows including content from other files with loop protection. It extracts keywords from directive lines and provides the substitution of those keywords with other text just like it does for the prompt.
153
+ ```ruby
154
+ prompt = PromptManager::Prompt.new(id: 'example')
108
155
 
109
- ##### Example Prompt with Directives
156
+ # Get all keywords found in the prompt
157
+ keywords = prompt.keywords #=> ["[NAME]", "[LANGUAGE]"]
110
158
 
111
- Here is an example prompt text file with comments, directives and keywords:
159
+ # Set parameter values
160
+ prompt.parameters = {
161
+ "[NAME]" => "Alice",
162
+ "[LANGUAGE]" => "French"
163
+ }
164
+
165
+ # Get processed text with substitutions
166
+ final_text = prompt.to_s
167
+
168
+ # Save changes
169
+ prompt.save
170
+ ```
171
+
172
+ ### Directive Processing
173
+
174
+ 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.
175
+
176
+ #### Built-in Directives
177
+
178
+ **`//include` (alias: `//import`)** - Include content from other files:
112
179
 
113
180
  ```text
114
- # prompts/sing_a_song.txt
115
- # Desc: Has the computer sing a song
181
+ //include common/header.txt
182
+ //import [TEMPLATE_NAME].txt
116
183
 
117
- //TextToSpeech [LANGUAGE] [VOICE NAME]
184
+ Main prompt content here...
185
+ ```
118
186
 
119
- Say the lyrics to the song [SONG NAME]. Please provide only the lyrics without commentary.
187
+ Features:
188
+ - Loop protection prevents circular includes
189
+ - Supports keyword substitution in file paths
190
+ - Processes included files recursively
120
191
 
121
- __END__
122
- Computers will never replace Frank Sinatra
192
+ #### Directive Syntax
193
+
194
+ ```text
195
+ //directive_name [PARAM1] [PARAM2] options
196
+
197
+ # Dynamic directives using keywords
198
+ //[COMMAND] [OPTIONS]
199
+ ```
200
+
201
+ #### Custom Directive Processors
202
+
203
+ You can create custom directive processors:
204
+
205
+ ```ruby
206
+ class MyDirectiveProcessor < PromptManager::DirectiveProcessor
207
+ def process_directive(directive, prompt)
208
+ case directive
209
+ when /^\/\/model (.+)$/
210
+ set_model($1)
211
+ when /^\/\/temperature (.+)$/
212
+ set_temperature($1.to_f)
213
+ else
214
+ super
215
+ end
216
+ end
217
+ end
218
+
219
+ prompt = PromptManager::Prompt.new(
220
+ id: 'example',
221
+ directives_processor: MyDirectiveProcessor.new
222
+ )
123
223
  ```
124
224
 
125
- ##### Accessing and Setting Parameter Values
225
+ ### ERB and Shell Integration
126
226
 
127
- Getting and setting keywords from a prompt is straightforward:
227
+ #### ERB Templates
228
+
229
+ Enable ERB processing for dynamic content generation:
128
230
 
129
231
  ```ruby
130
- prompt = PromptManager::Prompt.new(id: 'some_id')
131
- prompt.keywords #=> an Array of keywords found in the prompt text
232
+ prompt = PromptManager::Prompt.new(
233
+ id: 'dynamic',
234
+ erb_flag: true
235
+ )
236
+ ```
132
237
 
133
- # Update the parameters hash with keyword values
134
- prompt.parameters = {
135
- "[KEYWORD1]" => "value1",
136
- "[KEYWORD2]" => "value2"
137
- }
238
+ Example prompt with ERB:
138
239
 
139
- # Build the final prompt text
140
- # This substitutes values for keywords and processes directives
141
- # Comments are removed and the result is ready for the LLM
142
- final_text = prompt.to_s
240
+ ```text
241
+ Today's date is <%= Date.today %>
242
+ <% 5.times do |i| %>
243
+ Item <%= i + 1 %>
244
+ <% end %>
245
+ ```
143
246
 
144
- # Save any parameter changes back to storage
145
- prompt.save
247
+ #### Environment Variables
248
+
249
+ Enable automatic environment variable substitution:
250
+
251
+ ```ruby
252
+ prompt = PromptManager::Prompt.new(
253
+ id: 'with_env',
254
+ envar_flag: true
255
+ )
146
256
  ```
147
257
 
148
- Internally, directives are stored in a Hash where each key is the full directive line (including the // characters) and the value is the result string from executing that directive. The directives are processed in the order they appear in the prompt text.
258
+ Environment variables are automatically replaced in the prompt text.
259
+
260
+ ### Comments and Documentation
261
+
262
+ PromptManager supports comprehensive inline documentation:
149
263
 
150
- ##### Dynamic Directives
264
+ #### Line Comments
151
265
 
152
- Since directies are collected after the keywords in the prompt have been substituted for their values, it is possible to have dynamically generated directives as part of a prompt. For example:
266
+ Lines beginning with `#` are treated as comments:
153
267
 
154
268
  ```text
155
- //[COMMAND] [OPTIONS]
156
- # or
157
- [SOMETHING]
269
+ # This is a comment
270
+ # Description: This prompt does something useful
271
+
272
+ Actual prompt text here...
158
273
  ```
159
- ... where [COMMAND] gets replaced by some directive name. [SOMETHING] could be replaced by "//directive options"
160
274
 
161
- ##### Executing Directives
275
+ #### Block Comments
162
276
 
163
- The `prompt_manager` gem provides a basic DirectiveProcessor class that handles the `//include` directive (aliased as `//import`), which adds the contents of a file to the prompt with loop protection to prevent circular includes.
277
+ Everything after `__END__` is ignored:
164
278
 
165
- Additionally, you can extend with your own directives or downstream processes. Here are some ideas on how directives could be used in prompt downstream process:
279
+ ```text
280
+ Main prompt content...
166
281
 
167
- - "//model gpt-5" could be used to set the LLM model to be used for a specific prompt.
168
- - "//backend mods" could be used to set the backend prompt processor on the command line to be the `mods` utility.
169
- - "//chat" could be used to send the prompts and then start up a chat session about the prompt and its response.
282
+ __END__
283
+ Development notes:
284
+ - This section is completely ignored
285
+ - Great for documentation
286
+ - TODO items
287
+ ```
170
288
 
171
- Its all up to how your application wants to support directives or not.
289
+ #### Blank Lines
172
290
 
291
+ Blank lines are automatically removed from the final output.
173
292
 
174
- #### Comments Are Ignored
293
+ ### Parameter History
175
294
 
176
- The `prompt_manager` gem ignores comments. A line that begins with the '#' - pound (aka hash) character - is a line comment. Any lines that follow a line that is '__END__ at the end of a file are considered comments. Basically the '__END__' the end of the file. Nothing is process following that line.
295
+ PromptManager maintains a history of parameter values (since v0.3.0):
177
296
 
178
- The gem also ignores blank lines.
297
+ ```ruby
298
+ # Parameters are stored as arrays
299
+ prompt.parameters = {
300
+ "[NAME]" => ["Alice", "Bob", "Charlie"] # Charlie is most recent
301
+ }
179
302
 
180
- ## Storage Adapters
303
+ # The last value is always the most recent
304
+ current_name = prompt.parameters["[NAME]"].last
181
305
 
182
- A storage adapter is a class instance that ties the `PromptManager::Prompt` class to a storage facility that holds the actual prompts. Currently there are 2 storage adapters implemented: FileSystemAdapter and ActiveRecordAdapter.
306
+ # Useful for:
307
+ # - Implementing value history in UIs
308
+ # - Providing dropdown selections
309
+ # - Tracking parameter usage over time
310
+ ```
183
311
 
184
- The `PromptManager::Prompt` to support a small set of methods. A storage adapter can provide "extra" class or instance methods that can be used through the Prompt class. See the `test/prompt_manager/prompt_test.rb` for guidance on creating a new storage adapter.
312
+ ### Error Handling
313
+
314
+ PromptManager provides specific error classes for better debugging:
315
+
316
+ ```ruby
317
+ begin
318
+ prompt = PromptManager::Prompt.new(id: 'missing')
319
+ rescue PromptManager::StorageError => e
320
+ # Handle storage-related errors
321
+ puts "Storage error: #{e.message}"
322
+ rescue PromptManager::ParameterError => e
323
+ # Handle parameter substitution errors
324
+ puts "Parameter error: #{e.message}"
325
+ rescue PromptManager::ConfigurationError => e
326
+ # Handle configuration errors
327
+ puts "Configuration error: #{e.message}"
328
+ end
329
+ ```
330
+
331
+ ## Storage Adapters
332
+
333
+ Storage adapters provide the persistence layer for prompts. PromptManager includes two built-in adapters and supports custom implementations.
185
334
 
186
335
  ### FileSystemAdapter
187
336
 
188
- This is the first storage adapter developed. It saves prompts as text files within the file system inside a designated `prompts_dir` (directory) such as `~/.prompts` or where it makes the most sense to you. Another example would be to have your directory on a shared file system so that others can use the same prompts.
337
+ Stores prompts as text files in a directory structure.
189
338
 
190
- The `prompt ID` is the basename of the text file. For example `todo.txt` is the file for the prompt ID `todo` (see the examples directory.)
339
+ #### Configuration
191
340
 
192
- The parameters for the `todo` prompt ID are saved in the same directory as `todo.txt` in a JSON file named `todo.json` (also in the examples directory.)
341
+ ```ruby
342
+ PromptManager::Storage::FileSystemAdapter.config do |config|
343
+ config.prompts_dir = "~/.prompts" # Required
344
+ config.search_proc = nil # Optional custom search
345
+ config.prompt_extension = '.txt' # Default
346
+ config.params_extension = '.json' # Default
347
+ end
348
+ ```
349
+
350
+ #### File Structure
351
+
352
+ ```
353
+ ~/.prompts/
354
+ ├── greeting.txt # Prompt text
355
+ ├── greeting.json # Parameters
356
+ ├── email/
357
+ │ ├── welcome.txt
358
+ │ └── welcome.json
359
+ ```
360
+
361
+ #### Custom Search
362
+
363
+ Integrate with external search tools:
364
+
365
+ ```ruby
366
+ config.search_proc = ->(query) {
367
+ # Use ripgrep for fast searching
368
+ `rg -l "#{query}" #{config.prompts_dir}`.split("\n")
369
+ }
370
+ ```
371
+
372
+ #### Extra Methods
373
+
374
+ - `list` - Returns array of all prompt IDs
375
+ - `path(id)` - Returns Pathname to prompt file
376
+
377
+ ### ActiveRecordAdapter
378
+
379
+ Stores prompts in a database using ActiveRecord.
193
380
 
194
381
  #### Configuration
195
382
 
196
- Use a `config` block to establish the configuration for the class.
383
+ ```ruby
384
+ PromptManager::Storage::ActiveRecordAdapter.config do |config|
385
+ config.model = PromptModel # Your AR model
386
+ config.id_column = :prompt_id # Column for ID
387
+ config.text_column = :content # Column for text
388
+ config.parameters_column = :params # Column for parameters
389
+ end
390
+ ```
391
+
392
+ #### Database Setup
393
+
394
+ ```ruby
395
+ class CreatePrompts < ActiveRecord::Migration[7.0]
396
+ def change
397
+ create_table :prompts do |t|
398
+ t.string :prompt_id, null: false, index: { unique: true }
399
+ t.text :content
400
+ t.json :params
401
+ t.timestamps
402
+ end
403
+ end
404
+ end
405
+ ```
406
+
407
+ ### Custom Adapters
408
+
409
+ Create your own storage adapter:
197
410
 
198
411
  ```ruby
199
- PromptManager::Storage::FileSystemAdapter.config do |o|
200
- o.prompts_dir = "path/to/prompts_directory"
201
- o.search_proc = nil # default
202
- o.prompt_extension = '.txt' # default
203
- o.params_extension = '.json' # default
412
+ class RedisAdapter
413
+ def initialize(redis_client)
414
+ @redis = redis_client
415
+ end
416
+
417
+ def get(id)
418
+ prompt_text = @redis.get("prompt:#{id}:text")
419
+ parameters = JSON.parse(@redis.get("prompt:#{id}:params") || '{}')
420
+ [prompt_text, parameters]
421
+ end
422
+
423
+ def save(id, text, parameters)
424
+ @redis.set("prompt:#{id}:text", text)
425
+ @redis.set("prompt:#{id}:params", parameters.to_json)
426
+ end
427
+
428
+ def delete(id)
429
+ @redis.del("prompt:#{id}:text", "prompt:#{id}:params")
430
+ end
431
+
432
+ def list
433
+ @redis.keys("prompt:*:text").map { |k| k.split(':')[1] }
434
+ end
204
435
  end
205
436
  ```
206
437
 
207
- The `config` block returns `self` so that means you can do this to setup the storage adapter with the Prompt class:
438
+ ## Configuration
439
+
440
+ ### Initialization Options
441
+
442
+ When creating a prompt instance:
208
443
 
209
444
  ```ruby
210
- PromptManager::Prompt
211
- .storage_adapter =
212
- PromptManager::Storage::FileSystemAdapter
213
- .config do |config|
214
- config.prompts_dir = 'path/to/prompts_dir'
215
- end.new
445
+ prompt = PromptManager::Prompt.new(
446
+ id: 'example',
447
+ context: ['additional', 'context'],
448
+ directives_processor: CustomProcessor.new,
449
+ external_binding: binding,
450
+ erb_flag: true,
451
+ envar_flag: true
452
+ )
216
453
  ```
217
454
 
218
- ##### prompts_dir
455
+ Options:
456
+ - `id` - Unique identifier for the prompt
457
+ - `context` - Additional context array
458
+ - `directives_processor` - Custom directive processor
459
+ - `external_binding` - Ruby binding for ERB
460
+ - `erb_flag` - Enable ERB processing
461
+ - `envar_flag` - Enable environment variable substitution
219
462
 
220
- This is either a `String` or a `Pathname` object. All file paths are maintained in the class as `Pathname` objects. If you provide a `String` it will be converted. Relative paths will be converted to absolute paths.
463
+ ### Global Configuration
221
464
 
222
- An `ArgumentError` will be raised when `prompts_dir` does not exist or if it is not a directory.
465
+ Set the storage adapter globally:
223
466
 
224
- ##### search_proc
467
+ ```ruby
468
+ PromptManager::Prompt.storage_adapter = adapter_instance
469
+ ```
225
470
 
226
- The default for `search_proc` is nil which means that the search will be preformed by a default `search` method which is basically reading all the prompt files to see which ones contain the search term. It will return an Array of prompt IDs for each prompt file found that contains the search term. Its up to the application to select which returned prompt ID to use.
471
+ ## Advanced Usage
227
472
 
228
- There are faster ways to search and select files. For example there are specialized search and selection utilities that are available for the command line. The `examples` directory contains a `bash` script named `rgfzf` that uses `rg` (aka `ripgrep`) to do the searching and `fzf` to do the selecting.
473
+ ### Custom Keyword Patterns
229
474
 
230
- See [examples/using_search_proc.rb](examples/using_search_proc.rb)
475
+ Examples of different keyword patterns:
231
476
 
232
- ##### File Extensions
477
+ ```ruby
478
+ # Handlebars style: {{name}}
479
+ PromptManager::Prompt.parameter_regex = /(\{\{[a-z_]+\}\})/
233
480
 
234
- These two configuration options are `String` objects that must start with a period "." utherwise an `ArgumentError` will be raised.
481
+ # Colon prefix: :name
482
+ PromptManager::Prompt.parameter_regex = /(:[a-z_]+)/
235
483
 
236
- * prompt_extension - default: '.txt'
237
- * params_extension - default: '.json'
484
+ # Dollar sign: $NAME
485
+ PromptManager::Prompt.parameter_regex = /(\$[A-Z_]+)/
238
486
 
239
- Currently the `FileSystemAdapter` only supports a JSON serializer for its parameters Hash. Using any other values for these extensions will cause problems.
487
+ # Percentage: %name%
488
+ PromptManager::Prompt.parameter_regex = /(%[a-z_]+%)/
489
+ ```
240
490
 
241
- They exist so that there is a platform on to which other storage adapters can be built or serializers added. This is not currently on the roadmap.
491
+ ### Dynamic Directives
242
492
 
243
- #### Example Prompt Text File
493
+ Create directives that change based on parameters:
244
494
 
245
495
  ```text
246
- # ~/.prompts/joke.txt
247
- # Desc: Tell some jokes
496
+ # Set directive name via parameter
497
+ //[DIRECTIVE_TYPE] [OPTIONS]
248
498
 
249
- Tell me a few [KIND] jokes about [SUBJECT]
499
+ # Conditional directives
500
+ //include templates/[TEMPLATE_TYPE].txt
250
501
  ```
251
502
 
252
- Note the command lines at the top. This is a convention I use. It is not part of the software. I find it helpful in documenting the prompt.
253
-
254
- #### Example Prompt Parameters JSON File
255
-
256
- ```json
257
- {
258
- "[KIND]": [
259
- "pun",
260
- "family friendly"
261
- ],
262
- "[SUBJECT]": [
263
- "parrot",
264
- "garbage man",
265
- "snowman",
266
- "weather girl"
267
- ]
503
+ ### Search Capabilities
504
+
505
+ Implement powerful search across prompts:
506
+
507
+ ```ruby
508
+ # With FileSystemAdapter
509
+ adapter.search_proc = ->(query) {
510
+ # Custom search implementation
511
+ results = []
512
+ Dir.glob("#{prompts_dir}/**/*.txt").each do |file|
513
+ content = File.read(file)
514
+ if content.include?(query)
515
+ results << File.basename(file, '.txt')
516
+ end
517
+ end
518
+ results
268
519
  }
520
+
521
+ # With ActiveRecordAdapter
522
+ PromptModel.where("content LIKE ?", "%#{query}%").pluck(:prompt_id)
269
523
  ```
270
524
 
271
- The last value in the keyword's Array is the most recent value used for that keyword. This is a functionality established since v0.3.0. Its purpose is to provide a history of values from which a user can select to repeat a previous value or to select ta previous value and edit it into something new.
525
+ ## Examples
272
526
 
273
- #### Extra Functionality
527
+ ### Basic Usage
274
528
 
275
- The `FileSystemAdapter` adds two new methods for use by the `Prompt` class:
529
+ ```ruby
530
+ # examples/simple.rb
531
+ require 'prompt_manager'
276
532
 
277
- - list - returns an Array of prompt IDs
278
- - path and path(prompt_id) - returns a `Pathname` object to the prompt file
533
+ # Setup
534
+ PromptManager::Prompt.storage_adapter =
535
+ PromptManager::Storage::FileSystemAdapter.config do |c|
536
+ c.prompts_dir = '~/.prompts'
537
+ end.new
279
538
 
280
- Use the `path(prompt_id)` form against the `Prompt` class
281
- Use `prompt.path` when you have an instance of a `Prompt`
539
+ # Create and use a prompt
540
+ prompt = PromptManager::Prompt.new(id: 'story')
541
+ prompt.parameters = {
542
+ "[GENRE]" => "fantasy",
543
+ "[CHARACTER]" => "wizard"
544
+ }
282
545
 
283
- ### ActiveRecordAdapter
546
+ puts prompt.to_s
547
+ ```
284
548
 
285
- The `ActiveRecordAdapter` assumes that there is a database already configured by the application program that is requiring `prompt_manager` which has a model that contains prompt content. This model must have at least three columns which contain content for:
549
+ ### Advanced Integration with LLM and Streaming
286
550
 
287
- - a prompt ID
288
- - prompt text
289
- - prompt parameters
551
+ See [examples/advanced_integrations.rb](examples/advanced_integrations.rb) for a complete example that demonstrates:
290
552
 
291
- The model and the columns for these three elements can have any name. Those names are provided to the `ActiveRecordAdapter` in its config block.
553
+ - **ERB templating** for dynamic content generation
554
+ - **Shell integration** for environment variable substitution
555
+ - **OpenAI API integration** with streaming responses
556
+ - **Professional UI** with spinner feedback using `tty-spinner`
292
557
 
558
+ This example shows how to create sophisticated AI prompts that adapt to your system environment and stream responses in real-time.
293
559
 
294
- #### Configuration
560
+ ### With Search
295
561
 
296
- Use a `config` block to establish the configuration for the class.
562
+ See [examples/using_search_proc.rb](examples/using_search_proc.rb) for advanced search integration.
297
563
 
298
- The `PromptManager::Prompt` class expects an instance of a storage adapter class. By convention storage adapter class config methods will return `self` so that a simple `new` after the config will establish the instance.
564
+ ### Custom Storage
299
565
 
300
566
  ```ruby
301
- PromptManager::Prompt
302
- .storage_adapter =
303
- PromptManager::Storage::ActiveRecordAdapter.config do |config|
304
- config.model = DbPromptModel # any ActiveRecord::Base model
305
- config.id_column = :prompt_name
306
- config.text_column = :prompt_text
307
- config.parameters_column = :prompt_params
308
- end.new # adapters an instances of the adapter class
567
+ # examples/redis_storage.rb
568
+ class RedisStorage
569
+ # ... implementation
570
+ end
571
+
572
+ PromptManager::Prompt.storage_adapter = RedisStorage.new(Redis.new)
309
573
  ```
310
574
 
311
- ##### model
312
- The `model` configuration parameter is the actual class name of the `ActiveRecord::Base` or `ApplicationRecord` (if you are using a rails application) that contains the content used for prompts.
575
+ ## Extensible Architecture
576
+
577
+ PromptManager is designed to be extended:
578
+
579
+ ### Extension Points
313
580
 
314
- ##### id_column
315
- The `id_column` contains the name of the column that contains the "prompt ID" content. It can be either a `String` or `Symbol` value.
581
+ 1. **Storage Adapters** - Implement your own persistence layer
582
+ 2. **Directive Processors** - Add custom directives
583
+ 3. **Search Processors** - Integrate external search tools
584
+ 4. **Serializers** - Support different parameter formats
316
585
 
317
- ##### text_column
318
- The `text_column` contains name of the column that contains the actual raw text of the prompt. This raw text can include the keywords which will be replaced by values from the parameters Hash. The column name value can be either a `String` or a `Symbol`.
586
+ ### Potential Extensions
319
587
 
320
- ##### parameters_column
321
- The `parameters_column` contains the name of the column that contains the parameters used to replace keywords in the prompt text. This column in the database model is expected to be serialized. The `ActiveRecordAdapter` currently has a kludge bit of code that assumes that the serialization is done with JSON. The value of the parameters_column can be either a `String` or a `Symbol`.
588
+ - **CloudStorageAdapter** - S3, Google Cloud Storage
589
+ - **RedisAdapter** - For caching and fast access
590
+ - **ApiAdapter** - REST API backend
591
+ - **GraphQLAdapter** - GraphQL endpoint storage
592
+ - **GitAdapter** - Version controlled prompts
322
593
 
323
- TODO: fix the kludge so that any serialization can be used.
594
+ ## Roadmap
324
595
 
325
- ### Other Potential Storage Adapters
596
+ ### v0.9.0 - Modern Prompt Format (Breaking Changes)
597
+ - **Markdown Support**: Full `.md` file support with YAML front matter
598
+ - **Modern Parameter Syntax**: Support for `{{keyword}}` format
599
+ - **Enhanced API**: New `set_parameter()` and `get_parameter()` methods
600
+ - **Parameter Validation**: Built-in validation based on specifications
601
+ - **HTML Comments**: Support for `<!-- comments -->`
602
+ - **Migration Tools**: Automated conversion utilities
326
603
 
327
- There are many possibilities to expand this plugin concept of the storage adapter. Here are some for consideration:
604
+ ### v1.0.0 - Stability Release
605
+ - Performance optimizations
606
+ - Complete documentation
607
+ - Production hardening
328
608
 
329
- - RedisAdapter - For caching prompts or temporary storage
330
- - ApiAdapter - Use some end-point to CRUD a prompt
331
- - CloudStorageAdapter - Store prompts in cloud storage services
609
+ ### Future Enhancements
610
+ - Additional storage adapters
611
+ - Enhanced directive system with plugins
612
+ - Prompt versioning and inheritance
613
+ - Performance optimizations for large collections
332
614
 
333
615
  ## Development
334
616