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
@@ -0,0 +1,500 @@
1
+ # Parameterized Prompts
2
+
3
+ Parameterized prompts are the heart of PromptManager. They allow you to create reusable template prompts with placeholders (keywords) that can be filled with different values.
4
+
5
+ ## Basic Concepts
6
+
7
+ ### What are Keywords?
8
+
9
+ Keywords are placeholders in your prompt text that get replaced with actual values. By default, they follow the pattern `[UPPERCASE_TEXT]`:
10
+
11
+ ```text
12
+ Hello [NAME]! Today is [DATE] and you have [COUNT] messages.
13
+ ```
14
+
15
+ ### Parameter Substitution
16
+
17
+ When you provide parameter values, PromptManager replaces keywords with their corresponding values:
18
+
19
+ ```ruby
20
+ prompt.parameters = {
21
+ "[NAME]" => "Alice",
22
+ "[DATE]" => "2024-01-15",
23
+ "[COUNT]" => "3"
24
+ }
25
+
26
+ puts prompt.to_s
27
+ # Output: Hello Alice! Today is 2024-01-15 and you have 3 messages.
28
+ ```
29
+
30
+ ## Keyword Formats
31
+
32
+ ### Default Format
33
+
34
+ The default keyword pattern is `[UPPERCASE_WITH_UNDERSCORES_AND_SPACES]`:
35
+
36
+ ```text
37
+ # Valid keywords
38
+ [NAME]
39
+ [USER_NAME]
40
+ [FIRST_NAME]
41
+ [ORDER NUMBER] # Spaces allowed
42
+ [API_KEY]
43
+ ```
44
+
45
+ ### Custom Keyword Patterns
46
+
47
+ You can customize the keyword pattern to match your preferences:
48
+
49
+ === "Mustache Style"
50
+
51
+ ```ruby
52
+ PromptManager::Prompt.parameter_regex = /(\{\{[a-z_]+\}\})/
53
+
54
+ # Now use: {{name}}, {{user_name}}, {{api_key}}
55
+ prompt_text = "Hello {{name}}, your key is {{api_key}}"
56
+ ```
57
+
58
+ === "Colon Style"
59
+
60
+ ```ruby
61
+ PromptManager::Prompt.parameter_regex = /(:[a-z_]+)/
62
+
63
+ # Now use: :name, :user_name, :api_key
64
+ prompt_text = "Hello :name, your key is :api_key"
65
+ ```
66
+
67
+ === "Dollar Style"
68
+
69
+ ```ruby
70
+ PromptManager::Prompt.parameter_regex = /(\$[A-Z_]+)/
71
+
72
+ # Now use: $NAME, $USER_NAME, $API_KEY
73
+ prompt_text = "Hello $NAME, your key is $API_KEY"
74
+ ```
75
+
76
+ !!! warning "Regex Requirements"
77
+ Your custom regex must include capturing parentheses `()` to extract the keyword. The capture group should include the delimiter characters.
78
+
79
+ ## Working with Parameters
80
+
81
+ ### Setting Parameters
82
+
83
+ There are several ways to set parameter values:
84
+
85
+ === "Direct Assignment"
86
+
87
+ ```ruby
88
+ prompt.parameters = {
89
+ "[NAME]" => "Alice",
90
+ "[EMAIL]" => "alice@example.com"
91
+ }
92
+ ```
93
+
94
+ === "Individual Assignment"
95
+
96
+ ```ruby
97
+ prompt.parameters["[NAME]"] = "Bob"
98
+ prompt.parameters["[EMAIL]"] = "bob@example.com"
99
+ ```
100
+
101
+ === "Batch Update"
102
+
103
+ ```ruby
104
+ new_params = {
105
+ "[NAME]" => "Charlie",
106
+ "[ROLE]" => "Administrator"
107
+ }
108
+ prompt.parameters.merge!(new_params)
109
+ ```
110
+
111
+ ### Parameter History (v0.3.0+)
112
+
113
+ Since version 0.3.0, parameters maintain a history of values as arrays:
114
+
115
+ ```ruby
116
+ # Setting a single value
117
+ prompt.parameters["[NAME]"] = "Alice"
118
+
119
+ # Internally stored as: ["Alice"]
120
+ # The last value is always the most recent
121
+
122
+ # Adding more values
123
+ prompt.parameters["[NAME]"] = ["Alice", "Bob", "Charlie"]
124
+
125
+ # Get the current value
126
+ current_name = prompt.parameters["[NAME]"].last # "Charlie"
127
+
128
+ # Get the full history
129
+ all_names = prompt.parameters["[NAME]"] # ["Alice", "Bob", "Charlie"]
130
+ ```
131
+
132
+ This history is useful for:
133
+
134
+ - Building dropdown lists in UIs
135
+ - Providing auto-completion
136
+ - Tracking parameter usage over time
137
+ - Implementing "recent values" functionality
138
+
139
+ ### Getting Available Keywords
140
+
141
+ Discover what keywords are available in a prompt:
142
+
143
+ ```ruby
144
+ prompt = PromptManager::Prompt.new(id: 'email_template')
145
+ keywords = prompt.keywords
146
+ puts "Required parameters: #{keywords.join(', ')}"
147
+ # Output: Required parameters: [TO_NAME], [FROM_NAME], [SUBJECT], [BODY]
148
+ ```
149
+
150
+ ### Checking for Missing Parameters
151
+
152
+ ```ruby
153
+ def check_missing_parameters(prompt)
154
+ required = prompt.keywords.to_set
155
+ provided = prompt.parameters.keys.to_set
156
+ missing = required - provided
157
+
158
+ unless missing.empty?
159
+ puts "⚠️ Missing parameters: #{missing.to_a.join(', ')}"
160
+ return false
161
+ end
162
+
163
+ puts "✅ All parameters provided"
164
+ true
165
+ end
166
+ ```
167
+
168
+ ## Advanced Parameter Techniques
169
+
170
+ ### Conditional Parameters
171
+
172
+ Use ERB to make parameters conditional:
173
+
174
+ ```text title="conditional_greeting.txt"
175
+ Hello [NAME]!
176
+
177
+ <% if '[ROLE]' == 'admin' %>
178
+ You have administrative privileges.
179
+ <% elsif '[ROLE]' == 'user' %>
180
+ You have standard user access.
181
+ <% else %>
182
+ Please contact support to set up your account.
183
+ <% end %>
184
+
185
+ Your last login was [LAST_LOGIN].
186
+ ```
187
+
188
+ ```ruby
189
+ prompt = PromptManager::Prompt.new(id: 'conditional_greeting', erb_flag: true)
190
+ prompt.parameters = {
191
+ "[NAME]" => "Alice",
192
+ "[ROLE]" => "admin",
193
+ "[LAST_LOGIN]" => "2024-01-15 09:30"
194
+ }
195
+ ```
196
+
197
+ ### Nested Parameter Substitution
198
+
199
+ Parameters can reference other parameters:
200
+
201
+ ```text title="nested_example.txt"
202
+ Welcome to [COMPANY_NAME], [USER_NAME]!
203
+
204
+ Your profile: [USER_PROFILE_URL]
205
+ Support email: [SUPPORT_EMAIL]
206
+ ```
207
+
208
+ ```ruby
209
+ prompt.parameters = {
210
+ "[COMPANY_NAME]" => "Acme Corp",
211
+ "[USER_NAME]" => "alice",
212
+ "[USER_PROFILE_URL]" => "https://[COMPANY_NAME].com/users/[USER_NAME]".downcase,
213
+ "[SUPPORT_EMAIL]" => "support@[COMPANY_NAME].com".downcase
214
+ }
215
+
216
+ # First pass replaces top-level parameters
217
+ # Additional processing may be needed for nested substitution
218
+ ```
219
+
220
+ ### Dynamic Parameter Generation
221
+
222
+ Generate parameters programmatically:
223
+
224
+ ```ruby
225
+ def generate_user_parameters(user)
226
+ {
227
+ "[USER_ID]" => user.id.to_s,
228
+ "[USER_NAME]" => user.full_name,
229
+ "[USER_EMAIL]" => user.email,
230
+ "[USER_ROLE]" => user.role.upcase,
231
+ "[JOIN_DATE]" => user.created_at.strftime('%B %Y'),
232
+ "[LAST_ACTIVE]" => time_ago_in_words(user.last_seen_at)
233
+ }
234
+ end
235
+
236
+ # Usage
237
+ user = User.find(123)
238
+ prompt.parameters = generate_user_parameters(user)
239
+ ```
240
+
241
+ ### Parameter Validation
242
+
243
+ Implement custom validation for your parameters:
244
+
245
+ ```ruby
246
+ class ParameterValidator
247
+ def self.validate(prompt)
248
+ errors = []
249
+
250
+ prompt.parameters.each do |key, value|
251
+ case key
252
+ when "[EMAIL]"
253
+ unless value =~ URI::MailTo::EMAIL_REGEXP
254
+ errors << "#{key} must be a valid email address"
255
+ end
256
+ when "[DATE]"
257
+ begin
258
+ Date.parse(value)
259
+ rescue ArgumentError
260
+ errors << "#{key} must be a valid date"
261
+ end
262
+ when "[COUNT]"
263
+ unless value.to_i.to_s == value && value.to_i >= 0
264
+ errors << "#{key} must be a non-negative integer"
265
+ end
266
+ end
267
+ end
268
+
269
+ errors
270
+ end
271
+ end
272
+
273
+ # Usage
274
+ errors = ParameterValidator.validate(prompt)
275
+ if errors.any?
276
+ puts "Validation errors:"
277
+ errors.each { |error| puts " - #{error}" }
278
+ end
279
+ ```
280
+
281
+ ## Real-World Examples
282
+
283
+ ### Email Template
284
+
285
+ ```text title="welcome_email.txt"
286
+ Subject: Welcome to [COMPANY_NAME], [FIRST_NAME]!
287
+
288
+ Dear [FULL_NAME],
289
+
290
+ Welcome to [COMPANY_NAME]! We're excited to have you join our community.
291
+
292
+ Here are your account details:
293
+ - Username: [USERNAME]
294
+ - Email: [EMAIL]
295
+ - Account Type: [ACCOUNT_TYPE]
296
+ - Member Since: [JOIN_DATE]
297
+
298
+ Next steps:
299
+ 1. Complete your profile at [PROFILE_URL]
300
+ 2. Download our mobile app: [APP_STORE_URL]
301
+ 3. Join our community forum: [FORUM_URL]
302
+
303
+ If you have any questions, contact us at [SUPPORT_EMAIL] or
304
+ call [SUPPORT_PHONE].
305
+
306
+ Best regards,
307
+ [SENDER_NAME]
308
+ [SENDER_TITLE]
309
+ [COMPANY_NAME]
310
+ ```
311
+
312
+ ### API Documentation Template
313
+
314
+ ```text title="api_doc_template.txt"
315
+ # [API_NAME] API Documentation
316
+
317
+ ## Endpoint: [HTTP_METHOD] [ENDPOINT_URL]
318
+
319
+ ### Description
320
+ [DESCRIPTION]
321
+
322
+ ### Authentication
323
+ [AUTH_TYPE]: `[AUTH_HEADER]: [AUTH_VALUE]`
324
+
325
+ ### Parameters
326
+
327
+ | Parameter | Type | Required | Description |
328
+ |-----------|------|----------|-------------|
329
+ [PARAMETER_TABLE]
330
+
331
+ ### Example Request
332
+
333
+ ```[REQUEST_LANGUAGE]
334
+ [REQUEST_EXAMPLE]
335
+ ```
336
+
337
+ ### Example Response
338
+
339
+ ```json
340
+ [RESPONSE_EXAMPLE]
341
+ ```
342
+
343
+ ### Error Codes
344
+
345
+ [ERROR_TABLE]
346
+
347
+ ---
348
+ Last updated: [LAST_UPDATED]
349
+ API Version: [API_VERSION]
350
+ ```
351
+
352
+ ### Customer Support Template
353
+
354
+ ```text title="support_response.txt"
355
+ //include common/support_header.txt
356
+
357
+ Dear [CUSTOMER_NAME],
358
+
359
+ Thank you for contacting [COMPANY_NAME] support regarding
360
+ ticket #[TICKET_ID].
361
+
362
+ Issue Summary: [ISSUE_SUMMARY]
363
+ Priority Level: [PRIORITY]
364
+ Assigned Agent: [AGENT_NAME]
365
+
366
+ <% if '[PRIORITY]' == 'urgent' %>
367
+ 🚨 This is marked as urgent. We're prioritizing your request
368
+ and aim to resolve it within [URGENT_SLA] hours.
369
+ <% else %>
370
+ We aim to resolve your issue within [STANDARD_SLA] business days.
371
+ <% end %>
372
+
373
+ Our preliminary investigation shows:
374
+ [INVESTIGATION_NOTES]
375
+
376
+ Next steps:
377
+ [NEXT_STEPS]
378
+
379
+ You can track your ticket status at: [TICKET_URL]
380
+
381
+ Best regards,
382
+ [AGENT_NAME]
383
+ [COMPANY_NAME] Support Team
384
+
385
+ //include common/support_footer.txt
386
+ ```
387
+
388
+ ## Best Practices
389
+
390
+ ### 1. Use Descriptive Keywords
391
+
392
+ ```ruby
393
+ # Good - Clear and descriptive
394
+ "[USER_FIRST_NAME]", "[ORDER_TOTAL_AMOUNT]", "[DELIVERY_DATE]"
395
+
396
+ # Avoid - Ambiguous abbreviations
397
+ "[UFN]", "[OTA]", "[DD]"
398
+ ```
399
+
400
+ ### 2. Consistent Naming Convention
401
+
402
+ ```ruby
403
+ # Choose one style and stick with it
404
+ "[USER_NAME]" # snake_case
405
+ "[UserName]" # PascalCase
406
+ "[user_name]" # lowercase
407
+
408
+ # Be consistent with plurality
409
+ "[ITEM]" + "[ITEM_COUNT]" # Singular + count
410
+ "[ITEMS]" # Plural when multiple
411
+ ```
412
+
413
+ ### 3. Document Your Keywords
414
+
415
+ ```text
416
+ # Email welcome template
417
+ # Keywords:
418
+ # [USER_NAME] - Full display name of the user
419
+ # [EMAIL] - User's email address
420
+ # [JOIN_DATE] - Date user created account (YYYY-MM-DD format)
421
+ # [COMPANY_NAME] - Our company name
422
+
423
+ Welcome [USER_NAME]!
424
+ Your account ([EMAIL]) was created on [JOIN_DATE].
425
+ ```
426
+
427
+ ### 4. Provide Defaults
428
+
429
+ ```ruby
430
+ def apply_defaults(prompt)
431
+ defaults = {
432
+ "[DATE]" => Date.today.to_s,
433
+ "[TIME]" => Time.now.strftime('%H:%M'),
434
+ "[COMPANY_NAME]" => "Your Company",
435
+ "[SUPPORT_EMAIL]" => "support@yourcompany.com"
436
+ }
437
+
438
+ defaults.each do |key, value|
439
+ prompt.parameters[key] ||= value
440
+ end
441
+ end
442
+ ```
443
+
444
+ ### 5. Handle Missing Parameters Gracefully
445
+
446
+ ```ruby
447
+ class SafePrompt
448
+ def initialize(prompt)
449
+ @prompt = prompt
450
+ end
451
+
452
+ def to_s
453
+ text = @prompt.to_s
454
+
455
+ # Check for unreplaced keywords
456
+ unreplaced = text.scan(@prompt.class.parameter_regex).flatten
457
+
458
+ if unreplaced.any?
459
+ puts "⚠️ Warning: Unreplaced keywords: #{unreplaced.join(', ')}"
460
+
461
+ # Optionally replace with placeholder
462
+ unreplaced.each do |keyword|
463
+ text.gsub!(keyword, "[MISSING:#{keyword}]")
464
+ end
465
+ end
466
+
467
+ text
468
+ end
469
+ end
470
+
471
+ # Usage
472
+ safe_prompt = SafePrompt.new(prompt)
473
+ puts safe_prompt.to_s
474
+ ```
475
+
476
+ ## Troubleshooting
477
+
478
+ ### Keywords Not Being Replaced
479
+
480
+ 1. **Check keyword format**: Ensure keywords match your regex pattern
481
+ 2. **Verify parameter keys**: Keys must exactly match keywords (case-sensitive)
482
+ 3. **Confirm parameter values**: Make sure values are set and not nil
483
+
484
+ ### Parameter History Issues
485
+
486
+ 1. **Array format**: Remember parameters are arrays since v0.3.0
487
+ 2. **Access latest value**: Use `.last` to get the most recent value
488
+ 3. **Backward compatibility**: Single values are automatically converted to arrays
489
+
490
+ ### Performance with Large Parameter Sets
491
+
492
+ 1. **Cache keyword extraction**: Don't re-parse keywords unnecessarily
493
+ 2. **Batch parameter updates**: Use `merge!` instead of individual assignments
494
+ 3. **Consider parameter validation**: Validate early to catch errors sooner
495
+
496
+ ## Next Steps
497
+
498
+ - Learn about [Directive Processing](directive-processing.md) for including other files
499
+ - Explore [ERB Integration](erb-integration.md) for dynamic content generation
500
+ - See [Advanced Examples](../examples/advanced.md) for complex use cases
@@ -0,0 +1,79 @@
1
+ # Shell Integration
2
+
3
+ PromptManager can automatically substitute environment variables and integrate with shell commands.
4
+
5
+ ## Environment Variables
6
+
7
+ Enable environment variable substitution:
8
+
9
+ ```ruby
10
+ prompt = PromptManager::Prompt.new(
11
+ id: 'system_prompt',
12
+ envar_flag: true
13
+ )
14
+ ```
15
+
16
+ Environment variables in your prompt text will be automatically replaced.
17
+
18
+ ## Example
19
+
20
+ ```text title="system_prompt.txt"
21
+ System: $USER
22
+ Home: $HOME
23
+ Path: $PATH
24
+
25
+ Working directory: <%= Dir.pwd %>
26
+ ```
27
+
28
+ ## Shell Command Execution
29
+
30
+ PromptManager also supports shell command substitution using `$(command)` syntax:
31
+
32
+ ```text title="system_info.txt"
33
+ Current system load: $(uptime)
34
+ Disk usage: $(df -h / | tail -1)
35
+ Memory info: $(vm_stat | head -5)
36
+ ```
37
+
38
+ Commands are executed when the prompt is processed, with output substituted in place.
39
+
40
+ ## Advanced Example
41
+
42
+ The [advanced_integrations.rb](https://github.com/MadBomber/prompt_manager/blob/main/examples/advanced_integrations.rb) example demonstrates comprehensive shell integration:
43
+
44
+ ```text
45
+ ### Hardware Platform Details
46
+ **Architecture**: $HOSTTYPE$MACHTYPE
47
+ **Hostname**: $HOSTNAME
48
+ **Operating System**: $OSTYPE
49
+ **Shell**: $SHELL (version: $BASH_VERSION)
50
+ **User**: $USER
51
+ **Home Directory**: $HOME
52
+ **Current Path**: $PWD
53
+ **Terminal**: $TERM
54
+
55
+ ### Environment Configuration
56
+ **PATH**: $PATH
57
+ **Language**: $LANG
58
+ **Editor**: $EDITOR
59
+ **Pager**: $PAGER
60
+
61
+ ### Performance Context
62
+ **Load Average**: <%= `uptime`.strip rescue 'Unable to determine' %>
63
+ **Memory Info**: <%= `vm_stat | head -5`.strip rescue 'Unable to determine' if RUBY_PLATFORM.include?('darwin') %>
64
+ **Disk Usage**: <%= `df -h / | tail -1`.strip rescue 'Unable to determine' %>
65
+ ```
66
+
67
+ This creates dynamic prompts that capture real-time system information for analysis.
68
+
69
+ ## Configuration
70
+
71
+ Set environment variables that your prompts will use:
72
+
73
+ ```bash
74
+ export API_KEY="your-api-key"
75
+ export ENVIRONMENT="production"
76
+ export OPENAI_API_KEY="your-openai-key"
77
+ ```
78
+
79
+ For more shell integration examples, see the [Examples documentation](../examples.md).