prompt_manager 0.5.8 → 1.0.0

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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/README.md +206 -516
  4. data/Rakefile +0 -8
  5. data/docs/api/configuration.md +31 -327
  6. data/docs/api/constants.md +60 -0
  7. data/docs/api/index.md +14 -0
  8. data/docs/api/metadata.md +99 -0
  9. data/docs/api/parsed.md +98 -0
  10. data/docs/api/pm-module.md +131 -0
  11. data/docs/api/render-context.md +51 -0
  12. data/docs/architecture/design-decisions.md +70 -0
  13. data/docs/architecture/index.md +6 -0
  14. data/docs/architecture/processing-pipeline.md +112 -0
  15. data/docs/assets/css/custom.css +1 -0
  16. data/docs/assets/images/prompt_manager.gif +0 -0
  17. data/docs/assets/images/prompt_manager.mp4 +0 -0
  18. data/docs/examples/ai-agent-prompts.md +173 -0
  19. data/docs/examples/code-review-prompt.md +107 -0
  20. data/docs/examples/index.md +7 -0
  21. data/docs/examples/multi-file-composition.md +123 -0
  22. data/docs/getting-started/configuration.md +106 -0
  23. data/docs/getting-started/index.md +7 -0
  24. data/docs/getting-started/installation.md +10 -73
  25. data/docs/getting-started/quick-start.md +50 -225
  26. data/docs/guides/comment-stripping.md +64 -0
  27. data/docs/guides/custom-directives.md +115 -0
  28. data/docs/guides/erb-rendering.md +102 -0
  29. data/docs/guides/includes.md +146 -0
  30. data/docs/guides/index.md +11 -0
  31. data/docs/guides/parameters.md +96 -0
  32. data/docs/guides/parsing.md +127 -0
  33. data/docs/guides/shell-expansion.md +108 -0
  34. data/docs/index.md +54 -214
  35. data/lib/pm/configuration.rb +17 -0
  36. data/lib/pm/directives.rb +61 -0
  37. data/lib/pm/metadata.rb +17 -0
  38. data/lib/pm/parsed.rb +59 -0
  39. data/lib/pm/shell.rb +57 -0
  40. data/lib/pm/version.rb +5 -0
  41. data/lib/pm.rb +121 -0
  42. data/lib/prompt_manager.rb +2 -27
  43. data/mkdocs.yml +101 -66
  44. metadata +42 -101
  45. data/docs/.keep +0 -0
  46. data/docs/advanced/custom-keywords.md +0 -421
  47. data/docs/advanced/dynamic-directives.md +0 -535
  48. data/docs/advanced/performance.md +0 -612
  49. data/docs/advanced/search-integration.md +0 -635
  50. data/docs/api/directive-processor.md +0 -431
  51. data/docs/api/prompt-class.md +0 -354
  52. data/docs/api/storage-adapters.md +0 -462
  53. data/docs/assets/favicon.ico +0 -1
  54. data/docs/assets/logo.svg +0 -24
  55. data/docs/core-features/comments.md +0 -48
  56. data/docs/core-features/directive-processing.md +0 -38
  57. data/docs/core-features/erb-integration.md +0 -68
  58. data/docs/core-features/error-handling.md +0 -197
  59. data/docs/core-features/parameter-history.md +0 -76
  60. data/docs/core-features/parameterized-prompts.md +0 -500
  61. data/docs/core-features/shell-integration.md +0 -79
  62. data/docs/development/architecture.md +0 -544
  63. data/docs/development/contributing.md +0 -425
  64. data/docs/development/roadmap.md +0 -234
  65. data/docs/development/testing.md +0 -822
  66. data/docs/examples/advanced.md +0 -523
  67. data/docs/examples/basic.md +0 -688
  68. data/docs/examples/real-world.md +0 -776
  69. data/docs/examples.md +0 -337
  70. data/docs/getting-started/basic-concepts.md +0 -318
  71. data/docs/migration/v0.9.0.md +0 -459
  72. data/docs/migration/v1.0.0.md +0 -591
  73. data/docs/storage/activerecord-adapter.md +0 -348
  74. data/docs/storage/custom-adapters.md +0 -176
  75. data/docs/storage/filesystem-adapter.md +0 -236
  76. data/docs/storage/overview.md +0 -427
  77. data/examples/advanced_integrations.rb +0 -52
  78. data/examples/directives.rb +0 -102
  79. data/examples/prompts_dir/advanced_demo.txt +0 -79
  80. data/examples/prompts_dir/directive_example.json +0 -1
  81. data/examples/prompts_dir/directive_example.txt +0 -8
  82. data/examples/prompts_dir/todo.json +0 -1
  83. data/examples/prompts_dir/todo.txt +0 -7
  84. data/examples/prompts_dir/toy/8-ball.txt +0 -4
  85. data/examples/rgfzf +0 -44
  86. data/examples/simple.rb +0 -160
  87. data/examples/using_search_proc.rb +0 -68
  88. data/improvement_plan.md +0 -996
  89. data/lib/prompt_manager/directive_processor.rb +0 -47
  90. data/lib/prompt_manager/prompt.rb +0 -195
  91. data/lib/prompt_manager/storage/active_record_adapter.rb +0 -157
  92. data/lib/prompt_manager/storage/file_system_adapter.rb +0 -339
  93. data/lib/prompt_manager/storage.rb +0 -34
  94. data/lib/prompt_manager/version.rb +0 -5
  95. data/prompt_manager_logo.png +0 -0
data/README.md CHANGED
@@ -1,625 +1,315 @@
1
- # PromptManager
2
-
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>
36
-
37
- ## Table of Contents
38
-
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)
1
+ # PM (PromptManager)
2
+
3
+ <table>
4
+ <tr>
5
+ <td width="50%" align="center" valign="top">
6
+ <img src="docs/assets/images/prompt_manager.gif" alt="PromptManager" width="70%"><br>
7
+ <em>"Prompts with superpowers"</em>
8
+ </td>
9
+ <td width="50%" valign="top">
10
+ Like an enchanted librarian enhancing books of knowledge, PromptManager (PM) 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.
11
+
12
+ <h3>Key Features</h3>
13
+
14
+ - <strong>YAML Metadata</strong> - Parse from markdown strings or files<br>
15
+ - <strong>Conditional Shell Expansion</strong> - $ENVAR, ${ENVAR}, $(command) substitution<br>
16
+ - <strong>Conditional ERB Templates</strong> - On-demand rendering with named parameters<br>
17
+ - <strong>Reclusive Include System</strong> - Compose prompts from multiple files<br>
18
+ - <strong>Custom Directives</strong> - Register custom methods for ERB templates<br>
19
+ - <strong>Configurable Pipeline</strong> - Enable/disable stages per prompt or globally<br>
20
+ - <strong>Comment Stripping</strong> - HTML comments removed before processing
21
+ </td>
22
+ </tr>
23
+ </table>
90
24
 
91
25
  ## Installation
92
26
 
93
- Install the gem and add to the application's Gemfile by executing:
94
-
95
- bundle add prompt_manager
96
-
97
- If bundler is not being used to manage dependencies, install the gem by executing:
98
-
99
- gem install prompt_manager
100
-
101
- ## Quick Start
102
-
103
- ```ruby
104
- require 'prompt_manager'
105
-
106
- # Configure storage adapter
107
- PromptManager::Prompt.storage_adapter =
108
- PromptManager::Storage::FileSystemAdapter.config do |config|
109
- config.prompts_dir = '~/.prompts'
110
- end.new
111
-
112
- # Load and use a prompt
113
- prompt = PromptManager::Prompt.new(id: 'greeting')
114
- prompt.parameters = {
115
- "[NAME]" => "Alice",
116
- "[LANGUAGE]" => "English"
117
- }
118
-
119
- # Get the processed prompt text
120
- result = prompt.to_s
121
- ```
122
-
123
- ## Core Features
124
-
125
- ### Parameterized Prompts
126
-
127
- The heart of PromptManager is its ability to manage parameterized prompts - text templates with embedded keywords that can be replaced with dynamic values.
128
-
129
- #### Keyword Syntax
130
-
131
- By default, keywords are enclosed in square brackets: `[KEYWORD]`, `[MULTIPLE WORDS]`, or `[WITH_UNDERSCORES]`.
132
-
133
27
  ```ruby
134
- prompt_text = "Hello [NAME], please translate this to [LANGUAGE]"
28
+ require 'pm'
135
29
  ```
136
30
 
137
- #### Custom Patterns
31
+ ## Configuration
138
32
 
139
- You can customize the keyword pattern to match your preferences:
33
+ Set global defaults with `PM.configure`:
140
34
 
141
35
  ```ruby
142
- # Use {{mustache}} style
143
- PromptManager::Prompt.parameter_regex = /(\{\{[A-Za-z_]+\}\})/
144
-
145
- # Use :colon style
146
- PromptManager::Prompt.parameter_regex = /(:[a-z_]+)/
36
+ PM.configure do |config|
37
+ config.prompts_dir = '~/.prompts' # default: ''
38
+ config.shell = true # default: true
39
+ config.erb = true # default: true
40
+ end
147
41
  ```
148
42
 
149
- The regex must include capturing parentheses `()` to extract the keyword.
150
-
151
- #### Working with Parameters
43
+ **`prompts_dir`** is prepended to relative file paths passed to `PM.parse`. Absolute paths bypass it:
152
44
 
153
45
  ```ruby
154
- prompt = PromptManager::Prompt.new(id: 'example')
155
-
156
- # Get all keywords found in the prompt
157
- keywords = prompt.keywords #=> ["[NAME]", "[LANGUAGE]"]
46
+ PM.configure { |c| c.prompts_dir = '/usr/share/prompts' }
158
47
 
159
- # Set parameter values
160
- prompt.parameters = {
161
- "[NAME]" => "Alice",
162
- "[LANGUAGE]" => "French"
163
- }
48
+ PM.parse('code_review.md')
49
+ #=> reads /usr/share/prompts/code_review.md
164
50
 
165
- # Get processed text with substitutions
166
- final_text = prompt.to_s
167
-
168
- # Save changes
169
- prompt.save
51
+ PM.parse('/absolute/path/review.md')
52
+ #=> reads /absolute/path/review.md (prompts_dir ignored)
170
53
  ```
171
54
 
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
55
+ **`shell`** and **`erb`** set the global defaults for new parses. Per-file YAML metadata overrides the global setting:
177
56
 
178
- **`//include` (alias: `//import`)** - Include content from other files:
179
-
180
- ```text
181
- //include common/header.txt
182
- //import [TEMPLATE_NAME].txt
57
+ ```ruby
58
+ PM.configure { |c| c.shell = false }
183
59
 
184
- Main prompt content here...
60
+ # All files now default to shell: false
61
+ # A file with "shell: true" in its YAML still gets shell expansion
185
62
  ```
186
63
 
187
- Features:
188
- - Loop protection prevents circular includes
189
- - Supports keyword substitution in file paths
190
- - Processes included files recursively
191
-
192
- #### Directive Syntax
64
+ Reset all settings to defaults:
193
65
 
194
- ```text
195
- //directive_name [PARAM1] [PARAM2] options
196
-
197
- # Dynamic directives using keywords
198
- //[COMMAND] [OPTIONS]
66
+ ```ruby
67
+ PM.config.reset!
199
68
  ```
200
69
 
201
- #### Custom Directive Processors
202
-
203
- You can create custom directive processors:
70
+ Access the current configuration:
204
71
 
205
72
  ```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
- )
73
+ PM.config.prompts_dir #=> ''
74
+ PM.config.shell #=> true
75
+ PM.config.erb #=> true
223
76
  ```
224
77
 
225
- ### ERB and Shell Integration
226
-
227
- #### ERB Templates
78
+ ## Usage
228
79
 
229
- Enable ERB processing for dynamic content generation:
80
+ `PM.parse` accepts a file path, a Symbol, a single word, or a string:
230
81
 
231
82
  ```ruby
232
- prompt = PromptManager::Prompt.new(
233
- id: 'dynamic',
234
- erb_flag: true
235
- )
236
- ```
83
+ # File path (String ending in .md or Pathname)
84
+ parsed = PM.parse('code_review.md')
85
+ parsed = PM.parse(Pathname.new('code_review.md'))
237
86
 
238
- Example prompt with ERB:
87
+ # Symbol or single word (treated as basename, .md appended)
88
+ parsed = PM.parse(:code_review) #=> parses code_review.md
89
+ parsed = PM.parse('code_review') #=> parses code_review.md
239
90
 
240
- ```text
241
- Today's date is <%= Date.today %>
242
- <% 5.times do |i| %>
243
- Item <%= i + 1 %>
244
- <% end %>
91
+ # String content
92
+ parsed = PM.parse("---\ntitle: Hello\n---\nContent here")
245
93
  ```
246
94
 
247
- #### Environment Variables
95
+ When given a file path, `parse` adds `directory`, `name`, `created_at`, and `modified_at` to the metadata. Both forms run the full processing pipeline.
96
+
97
+ Given a file `code_review.md`:
98
+
99
+ ```md
100
+ ---
101
+ title: Code Review
102
+ provider: openai
103
+ model: gpt-4
104
+ temperature: 0.3
105
+ parameters:
106
+ language: ruby
107
+ code: null
108
+ style_guide: ~/guides/default.md
109
+ ---
110
+ Review the following <%= language %> code using the style guide
111
+ at <%= style_guide %>:
112
+
113
+ <%= code %>
114
+ ```
248
115
 
249
- Enable automatic environment variable substitution:
116
+ Parse it:
250
117
 
251
118
  ```ruby
252
- prompt = PromptManager::Prompt.new(
253
- id: 'with_env',
254
- envar_flag: true
255
- )
119
+ parsed = PM.parse('code_review.md')
120
+ parsed.metadata.parameters
121
+ #=> {'language' => 'ruby', 'code' => nil, 'style_guide' => '~/guides/default.md'}
256
122
  ```
257
123
 
258
- Environment variables are automatically replaced in the prompt text.
259
-
260
- ### Comments and Documentation
261
-
262
- PromptManager supports comprehensive inline documentation:
263
-
264
- #### Line Comments
265
-
266
- Lines beginning with `#` are treated as comments:
124
+ Build the prompt with `to_s`:
267
125
 
268
- ```text
269
- # This is a comment
270
- # Description: This prompt does something useful
271
-
272
- Actual prompt text here...
273
- ```
274
-
275
- #### Block Comments
126
+ ```ruby
127
+ # Provide required params, accept defaults for the rest
128
+ parsed.to_s('code' => File.read('app.rb'))
276
129
 
277
- Everything after `__END__` is ignored:
130
+ # Override defaults
131
+ parsed.to_s('code' => File.read('app.py'), 'language' => 'python')
278
132
 
279
- ```text
280
- Main prompt content...
133
+ # All params have defaults — no arguments needed
134
+ parsed.to_s
281
135
 
282
- __END__
283
- Development notes:
284
- - This section is completely ignored
285
- - Great for documentation
286
- - TODO items
136
+ # Missing a required parameter raises an error
137
+ parsed.to_s
138
+ #=> ArgumentError: Missing required parameters: code
287
139
  ```
288
140
 
289
- #### Blank Lines
290
-
291
- Blank lines are automatically removed from the final output.
141
+ Parameters with a `null` default in the YAML are required. Parameters with any other default are optional.
292
142
 
293
- ### Parameter History
143
+ ### Shell expansion
294
144
 
295
- PromptManager maintains a history of parameter values (since v0.3.0):
145
+ Shell references are expanded during parsing (when `shell: true`, the default):
296
146
 
297
- ```ruby
298
- # Parameters are stored as arrays
299
- prompt.parameters = {
300
- "[NAME]" => ["Alice", "Bob", "Charlie"] # Charlie is most recent
301
- }
302
-
303
- # The last value is always the most recent
304
- current_name = prompt.parameters["[NAME]"].last
305
-
306
- # Useful for:
307
- # - Implementing value history in UIs
308
- # - Providing dropdown selections
309
- # - Tracking parameter usage over time
147
+ ```md
148
+ ---
149
+ title: Deploy Check
150
+ parameters:
151
+ environment: null
152
+ ---
153
+ Current user: $USER
154
+ Home directory: ${HOME}
155
+ Date: $(date +%Y-%m-%d)
156
+ Git branch: $(git rev-parse --abbrev-ref HEAD)
157
+ Deploy to: <%= environment %>
310
158
  ```
311
159
 
312
- ### Error Handling
160
+ - `$ENVAR` and `${ENVAR}` are replaced with the environment variable's value.
161
+ - `$(command)` is executed and replaced with its stdout.
162
+ - Missing environment variables are replaced with an empty string.
163
+ - Failed commands raise an error.
313
164
 
314
- PromptManager provides specific error classes for better debugging:
165
+ Shell expansion is also available directly:
315
166
 
316
167
  ```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
168
+ PM.expand_shell(string)
329
169
  ```
330
170
 
331
- ## Storage Adapters
332
-
333
- Storage adapters provide the persistence layer for prompts. PromptManager includes two built-in adapters and supports custom implementations.
171
+ ### Including other prompt files
334
172
 
335
- ### FileSystemAdapter
173
+ Use `include` in ERB to compose prompts from multiple files:
336
174
 
337
- Stores prompts as text files in a directory structure.
175
+ ```md
176
+ ---
177
+ title: Full Review
178
+ parameters:
179
+ code: null
180
+ ---
181
+ <%= include 'common/header.md' %>
338
182
 
339
- #### Configuration
183
+ Review this code:
184
+ <%= code %>
340
185
 
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
186
+ <%= include 'common/footer.md' %>
348
187
  ```
349
188
 
350
- #### File Structure
189
+ Included files go through the full processing pipeline (comment stripping, metadata extraction, shell expansion, ERB rendering). The parent's parameter values are passed to included files.
351
190
 
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
191
+ Nested includes work — A can include B which includes C. Circular includes raise an error.
362
192
 
363
- Integrate with external search tools:
193
+ After calling `to_s`, the parent's metadata has an `includes` key with a tree of what was included:
364
194
 
365
195
  ```ruby
366
- config.search_proc = ->(query) {
367
- # Use ripgrep for fast searching
368
- `rg -l "#{query}" #{config.prompts_dir}`.split("\n")
369
- }
196
+ parsed = PM.parse('full_review.md')
197
+ parsed.to_s('code' => File.read('app.rb'))
198
+
199
+ parsed.metadata.includes
200
+ #=> [
201
+ # {
202
+ # path: "/prompts/common/header.md",
203
+ # depth: 1,
204
+ # metadata: { title: "Header", ... },
205
+ # includes: []
206
+ # },
207
+ # {
208
+ # path: "/prompts/common/footer.md",
209
+ # depth: 1,
210
+ # metadata: { title: "Footer", ... },
211
+ # includes: []
212
+ # }
213
+ # ]
370
214
  ```
371
215
 
372
- #### Extra Methods
373
-
374
- - `list` - Returns array of all prompt IDs
375
- - `path(id)` - Returns Pathname to prompt file
216
+ ### Custom directives
376
217
 
377
- ### ActiveRecordAdapter
378
-
379
- Stores prompts in a database using ActiveRecord.
380
-
381
- #### Configuration
218
+ Register custom methods available in ERB templates:
382
219
 
383
220
  ```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
221
+ PM.register(:read) { |_ctx, path| File.read(path) }
222
+ PM.register(:env) { |_ctx, key| ENV.fetch(key, '') }
223
+ PM.register(:run) { |_ctx, cmd| `#{cmd}`.chomp }
390
224
  ```
391
225
 
392
- #### Database Setup
226
+ Register multiple names for the same directive (aliases):
393
227
 
394
228
  ```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
229
+ PM.register(:webpage, :website, :web) { |_ctx, url| fetch_page(url) }
405
230
  ```
406
231
 
407
- ### Custom Adapters
408
-
409
- Create your own storage adapter:
232
+ All three names call the same block. Use any of them in ERB:
410
233
 
411
- ```ruby
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
435
- end
234
+ ```markdown
235
+ <%= webpage 'https://example.com' %>
236
+ <%= website 'https://example.com' %>
237
+ <%= web 'https://example.com' %>
436
238
  ```
437
239
 
438
- ## Configuration
439
-
440
- ### Initialization Options
240
+ Use them in any prompt file:
441
241
 
442
- When creating a prompt instance:
443
-
444
- ```ruby
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
- )
242
+ ```md
243
+ ---
244
+ title: Deploy Prompt
245
+ ---
246
+ Hostname: <%= read '/etc/hostname' %>
247
+ Environment: <%= env 'DEPLOY_ENV' %>
248
+ Recent commits: <%= run 'git log --oneline -5' %>
453
249
  ```
454
250
 
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
462
-
463
- ### Global Configuration
251
+ The first argument to every directive block is a `PM::RenderContext` with access to the current render state:
464
252
 
465
- Set the storage adapter globally:
253
+ - `ctx.directory` — directory of the file being rendered
254
+ - `ctx.params` — merged parameter values
255
+ - `ctx.metadata` — the current file's metadata
256
+ - `ctx.depth` — include nesting depth
257
+ - `ctx.included` — Set of file paths already in the include chain
466
258
 
467
259
  ```ruby
468
- PromptManager::Prompt.storage_adapter = adapter_instance
260
+ PM.register(:current_file) { |ctx| ctx.metadata.name || 'unknown' }
261
+ PM.register(:depth) { |ctx| ctx.depth.to_s }
469
262
  ```
470
263
 
471
- ## Advanced Usage
472
-
473
- ### Custom Keyword Patterns
474
-
475
- Examples of different keyword patterns:
264
+ Registering a name that already exists raises an error:
476
265
 
477
266
  ```ruby
478
- # Handlebars style: {{name}}
479
- PromptManager::Prompt.parameter_regex = /(\{\{[a-z_]+\}\})/
480
-
481
- # Colon prefix: :name
482
- PromptManager::Prompt.parameter_regex = /(:[a-z_]+)/
483
-
484
- # Dollar sign: $NAME
485
- PromptManager::Prompt.parameter_regex = /(\$[A-Z_]+)/
486
-
487
- # Percentage: %name%
488
- PromptManager::Prompt.parameter_regex = /(%[a-z_]+%)/
489
- ```
490
-
491
- ### Dynamic Directives
492
-
493
- Create directives that change based on parameters:
494
-
495
- ```text
496
- # Set directive name via parameter
497
- //[DIRECTIVE_TYPE] [OPTIONS]
498
-
499
- # Conditional directives
500
- //include templates/[TEMPLATE_TYPE].txt
267
+ PM.register(:include) { |_ctx, path| path }
268
+ #=> RuntimeError: Directive already registered: include
501
269
  ```
502
270
 
503
- ### Search Capabilities
504
-
505
- Implement powerful search across prompts:
271
+ Reset to built-in directives only:
506
272
 
507
273
  ```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
519
- }
520
-
521
- # With ActiveRecordAdapter
522
- PromptModel.where("content LIKE ?", "%#{query}%").pluck(:prompt_id)
274
+ PM.reset_directives!
523
275
  ```
524
276
 
525
- ## Examples
277
+ ### Disabling processing stages
526
278
 
527
- ### Basic Usage
279
+ Set `shell: false` or `erb: false` in the metadata to skip those stages:
528
280
 
529
- ```ruby
530
- # examples/simple.rb
531
- require 'prompt_manager'
532
-
533
- # Setup
534
- PromptManager::Prompt.storage_adapter =
535
- PromptManager::Storage::FileSystemAdapter.config do |c|
536
- c.prompts_dir = '~/.prompts'
537
- end.new
538
-
539
- # Create and use a prompt
540
- prompt = PromptManager::Prompt.new(id: 'story')
541
- prompt.parameters = {
542
- "[GENRE]" => "fantasy",
543
- "[CHARACTER]" => "wizard"
544
- }
545
-
546
- puts prompt.to_s
281
+ ```md
282
+ ---
283
+ title: Raw Template
284
+ shell: false
285
+ erb: false
286
+ ---
287
+ This $USER and <%= name %> content is preserved as-is.
547
288
  ```
548
289
 
549
- ### Advanced Integration with LLM and Streaming
550
-
551
- See [examples/advanced_integrations.rb](examples/advanced_integrations.rb) for a complete example that demonstrates:
552
-
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`
290
+ Both default to `true` when not specified. You can change these defaults globally via `PM.configure` (see [Configuration](#configuration)). Per-file metadata always overrides the global setting.
557
291
 
558
- This example shows how to create sophisticated AI prompts that adapt to your system environment and stream responses in real-time.
292
+ ### HTML comment stripping
559
293
 
560
- ### With Search
294
+ HTML comments are stripped before any other processing:
561
295
 
562
- See [examples/using_search_proc.rb](examples/using_search_proc.rb) for advanced search integration.
296
+ ```md
297
+ <!-- This comment will be removed -->
298
+ ---
299
+ title: My Prompt
300
+ ---
301
+ Content here. <!-- This too -->
302
+ ```
563
303
 
564
- ### Custom Storage
304
+ Comments are also available directly:
565
305
 
566
306
  ```ruby
567
- # examples/redis_storage.rb
568
- class RedisStorage
569
- # ... implementation
570
- end
571
-
572
- PromptManager::Prompt.storage_adapter = RedisStorage.new(Redis.new)
307
+ PM.strip_comments(string)
573
308
  ```
574
309
 
575
- ## Extensible Architecture
576
-
577
- PromptManager is designed to be extended:
578
-
579
- ### Extension Points
580
-
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
585
-
586
- ### Potential Extensions
587
-
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
593
-
594
- ## Roadmap
595
-
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
603
-
604
- ### v1.0.0 - Stability Release
605
- - Performance optimizations
606
- - Complete documentation
607
- - Production hardening
608
-
609
- ### Future Enhancements
610
- - Additional storage adapters
611
- - Enhanced directive system with plugins
612
- - Prompt versioning and inheritance
613
- - Performance optimizations for large collections
614
-
615
- ## Development
616
-
617
- Looking for feedback and contributors to enhance the capability of prompt_manager.
618
-
619
- ## Contributing
620
-
621
- Bug reports and pull requests are welcome on GitHub at [https://github.com/MadBomber/prompt_manager](https://github.com/MadBomber/prompt_manager).
622
-
623
- ## License
310
+ ## Processing pipeline
624
311
 
625
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
312
+ 1. Strip HTML comments
313
+ 2. Extract YAML metadata and markdown content
314
+ 3. Shell expansion (`$ENVAR`, `$(command)`) when `shell: true`
315
+ 4. ERB rendering on demand via `to_s` when `erb: true`