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
@@ -1,339 +0,0 @@
1
- # prompt_manager/lib/prompt_manager/storage/file_system_adapter.rb
2
-
3
- # Use the local (or remote) file system as a place to
4
- # store and access prompts.
5
- #
6
- # Adds two additional methods to the Prompt class:
7
- # list - returns Array of prompt IDs
8
- # path - returns a Pathname object to the prompt's text file
9
- # path(prompt_id) - same as path on the prompt instance
10
- #
11
- # Allows sub-directories of the prompts_dir to be
12
- # used like categories. For example the prompt_id "toy/magic"
13
- # is found in the `magic.txt` file inside the `toy` sub-directory
14
- # of the prompts_dir.
15
- #
16
- # There can be many layers of categories (sub-directories)
17
- #
18
- # This adapter serves as the file-based storage backend for PromptManager::Prompt,
19
- # enabling prompt retrieval and persistence using file system operations.
20
-
21
- require 'json' # basic serialization of parameters
22
- require 'pathname'
23
- require 'fileutils'
24
-
25
- class PromptManager::Storage::FileSystemAdapter
26
- # Placeholder for search proc
27
- SEARCH_PROC = nil
28
- # File extension for parameters
29
- PARAMS_EXTENSION = '.json'.freeze
30
- # File extension for prompts
31
- PROMPT_EXTENSION = '.txt'.freeze
32
- # Regular expression for valid prompt IDs
33
- PROMPT_ID_FORMAT = /^[a-zA-Z0-9\-\/_]+$/
34
-
35
- class << self
36
- # Accessors for configuration options
37
- attr_accessor :prompts_dir, :search_proc,
38
- :params_extension, :prompt_extension
39
-
40
- # Configure the adapter
41
- def config
42
- if block_given?
43
- yield self
44
- validate_configuration
45
- else
46
- raise ArgumentError, "No block given to config"
47
- end
48
-
49
- self
50
- end
51
-
52
- # Expansion methods on the Prompt class specific to
53
- # this storage adapter.
54
-
55
- # Ignore the incoming prompt_id
56
- def list(prompt_id = nil)
57
- new.list
58
- end
59
-
60
- def path(prompt_id)
61
- new.path(prompt_id)
62
- end
63
-
64
- #################################################
65
- private
66
-
67
- # Validate the configuration
68
- def validate_configuration
69
- validate_prompts_dir
70
- validate_search_proc
71
- validate_prompt_extension
72
- validate_params_extension
73
- end
74
-
75
- # Validate the prompts directory
76
- def validate_prompts_dir
77
- # This is a work around for a Ruby scope issue where the
78
- # class getter/setter method is becoming confused with a
79
- # local variable when anything other than plain 'ol get and
80
- # set are used. This error is in both Ruby v3.2.2 and
81
- # v3.3.0-preview3.
82
- #
83
- prompts_dir_local = self.prompts_dir
84
-
85
- raise ArgumentError, "prompts_dir must be set" if prompts_dir_local.nil? || prompts_dir_local.to_s.strip.empty?
86
-
87
- unless prompts_dir_local.is_a?(Pathname)
88
- prompts_dir_local = Pathname.new(prompts_dir_local)
89
- end
90
-
91
- prompts_dir_local = prompts_dir_local.expand_path
92
-
93
- unless prompts_dir_local.exist?
94
- FileUtils.mkdir_p(prompts_dir_local)
95
- end
96
- raise(ArgumentError, "prompts_dir: #{prompts_dir_local} is not a directory") unless prompts_dir_local.directory?
97
-
98
- self.prompts_dir = prompts_dir_local
99
- end
100
-
101
- # Validate the search proc
102
- def validate_search_proc
103
- search_proc_local = self.search_proc
104
-
105
- if search_proc_local.nil?
106
- search_proc_local = SEARCH_PROC
107
- else
108
- raise(ArgumentError, "search_proc invalid; does not respond to call") unless search_proc_local.respond_to?(:call)
109
- end
110
-
111
- self.search_proc = search_proc_local
112
- end
113
-
114
- # Validate the prompt extension
115
- def validate_prompt_extension
116
- prompt_extension_local = self.prompt_extension
117
-
118
- if prompt_extension_local.nil?
119
- prompt_extension_local = PROMPT_EXTENSION
120
- else
121
- unless prompt_extension_local.is_a?(String) &&
122
- prompt_extension_local.start_with?('.')
123
- raise(ArgumentError, "Invalid prompt_extension: #{prompt_extension_local}")
124
- end
125
- end
126
-
127
- self.prompt_extension = prompt_extension_local
128
- end
129
-
130
- # Validate the params extension
131
- def validate_params_extension
132
- params_extension_local = self.params_extension
133
-
134
- if params_extension_local.nil?
135
- params_extension_local = PARAMS_EXTENSION
136
- else
137
- unless params_extension_local.is_a?(String) &&
138
- params_extension_local.start_with?('.')
139
- raise(ArgumentError, "Invalid params_extension: #{params_extension_local}")
140
- end
141
- end
142
-
143
- self.params_extension = params_extension_local
144
- end
145
- end
146
-
147
- ##################################################
148
- ###
149
- ## Instance
150
- #
151
-
152
- # Accessors for instance variables
153
- def prompts_dir = self.class.prompts_dir
154
- def search_proc = self.class.search_proc
155
- def prompt_extension = self.class.prompt_extension
156
- def params_extension = self.class.params_extension
157
-
158
- # Initialize the adapter
159
- def initialize
160
- # NOTE: validate because main program may have made
161
- # changes outside of the config block
162
- self.class.send(:validate_configuration) # send gets around private designations of a method
163
- end
164
-
165
- # Get a prompt by ID
166
- def get(id:)
167
- validate_id(id)
168
- verify_id(id)
169
-
170
- {
171
- id: id,
172
- text: prompt_text(id),
173
- parameters: parameter_values(id)
174
- }
175
- end
176
-
177
- # Retrieve prompt text by its id
178
- def prompt_text(prompt_id)
179
- read_file(file_path(prompt_id, prompt_extension))
180
- end
181
-
182
- # Retrieve parameter values by its id
183
- def parameter_values(prompt_id)
184
- # Parse parameters from the prompt text
185
- prompt_text_content = prompt_text(prompt_id)
186
- parsed_parameters = parse_parameters_from_text(prompt_text_content)
187
-
188
- # Load parameters from JSON file if it exists
189
- params_path = file_path(prompt_id, params_extension)
190
- file_parameters = params_path.exist? ? deserialize(read_file(params_path)) : {}
191
-
192
- # Only preserve JSON values for parameters that exist in the current text
193
- parsed_parameters.each_key do |key|
194
- if file_parameters.key?(key) && file_parameters[key].is_a?(Array)
195
- parsed_parameters[key] = file_parameters[key]
196
- end
197
- end
198
-
199
- parsed_parameters
200
- end
201
-
202
- # Parse parameters from the prompt text
203
- def parse_parameters_from_text(text)
204
- parameters = {}
205
-
206
- text.scan(PromptManager::Prompt.parameter_regex).each do |match|
207
- variable_name = match.shift
208
- parameters[variable_name] = [] unless parameters.key?(variable_name)
209
- end
210
-
211
- parameters
212
- end
213
-
214
- # Save prompt text and parameter values to corresponding files
215
- def save(
216
- id:,
217
- text: "",
218
- parameters: {}
219
- )
220
- validate_id(id)
221
-
222
- prompt_filepath = file_path(id, prompt_extension)
223
- params_filepath = file_path(id, params_extension)
224
-
225
- write_with_error_handling(prompt_filepath, text)
226
- write_with_error_handling(params_filepath, serialize(parameters))
227
- end
228
-
229
- # Delete prompt text and parameter values files
230
- def delete(id:)
231
- validate_id(id)
232
-
233
- prompt_filepath = file_path(id, prompt_extension)
234
- params_filepath = file_path(id, params_extension)
235
-
236
- delete_with_error_handling(prompt_filepath)
237
- delete_with_error_handling(params_filepath)
238
- end
239
-
240
- # Search for prompts
241
- def search(for_what)
242
- search_term = for_what.downcase
243
-
244
- if search_proc.is_a? Proc
245
- search_proc.call(search_term)
246
- else
247
- search_prompts(search_term)
248
- end
249
- end
250
-
251
- # Return an Array of prompt IDs
252
- def list(*)
253
- prompt_ids = []
254
-
255
- Pathname.glob(prompts_dir.join("**/*#{prompt_extension}")).each do |file_path|
256
- prompt_id = file_path.relative_path_from(prompts_dir).to_s.gsub(prompt_extension, '')
257
- prompt_ids << prompt_id
258
- end
259
-
260
- prompt_ids.sort
261
- end
262
-
263
- # Returns a Pathname object for a prompt ID text file
264
- # However, it is possible that the file does not exist.
265
- def path(id)
266
- validate_id(id)
267
- file_path(id, prompt_extension)
268
- end
269
-
270
- ##########################################
271
- private
272
-
273
- # Validate that the ID contains good characters.
274
- def validate_id(id)
275
- raise ArgumentError, "Invalid ID format id: #{id}" unless id =~ PROMPT_ID_FORMAT
276
- end
277
-
278
- # Verify that the ID exists
279
- def verify_id(id)
280
- unless file_path(id, prompt_extension).exist?
281
- raise ArgumentError, "Invalid prompt_id: #{id}"
282
- end
283
- end
284
-
285
- # Write to a file with error handling
286
- def write_with_error_handling(file_path, content)
287
- begin
288
- file_path.write content
289
- true
290
- rescue IOError => e
291
- raise "Failed to write to file: #{e.message}"
292
- end
293
- end
294
-
295
- # Delete a file with error handling
296
- def delete_with_error_handling(file_path)
297
- begin
298
- file_path.delete
299
- true
300
- rescue IOError => e
301
- raise "Failed to delete file: #{e.message}"
302
- end
303
- end
304
-
305
- # Get the file path for a prompt ID and extension
306
- def file_path(id, extension)
307
- prompts_dir + "#{id}#{extension}"
308
- end
309
-
310
- # Read a file
311
- def read_file(full_path)
312
- raise IOError, 'File does not exist' unless File.exist?(full_path)
313
- File.read(full_path)
314
- end
315
-
316
- # Search for prompts
317
- def search_prompts(search_term)
318
- prompt_ids = []
319
-
320
- Pathname.glob(prompts_dir.join("**/*#{prompt_extension}")).each do |prompt_path|
321
- if prompt_path.read.downcase.include?(search_term)
322
- prompt_id = prompt_path.relative_path_from(prompts_dir).to_s.gsub(prompt_extension, '')
323
- prompt_ids << prompt_id
324
- end
325
- end
326
-
327
- prompt_ids.sort
328
- end
329
-
330
- # Serialize data to JSON
331
- def serialize(data)
332
- data.to_json
333
- end
334
-
335
- # Deserialize JSON data
336
- def deserialize(data)
337
- JSON.parse(data)
338
- end
339
- end
@@ -1,34 +0,0 @@
1
- # prompt_manager/lib/prompt_manager/storage.rb
2
-
3
- # The Storage module provides a namespace for different storage adapters
4
- # that handle persistence of prompts. Each adapter implements a common
5
- # interface for saving, retrieving, searching, and deleting prompts.
6
- #
7
- # Available adapters:
8
- # - FileSystemAdapter: Stores prompts in text files on the local filesystem
9
- # - ActiveRecordAdapter: Stores prompts in a database using ActiveRecord
10
- #
11
- # To use an adapter, configure it before using PromptManager::
12
- #
13
- # Example with FileSystemAdapter:
14
- # PromptManager::Storage::FileSystemAdapter.config do |config|
15
- # config.prompts_dir = Pathname.new('/path/to/prompts')
16
- # end
17
- # PromptManager::Prompt.storage_adapter = PromptManager::Storage::FileSystemAdapter.new
18
- #
19
- # Example with ActiveRecordAdapter:
20
- # PromptManager::Storage::ActiveRecordAdapter.config do |config|
21
- # config.model = MyPromptModel
22
- # config.id_column = :prompt_id
23
- # config.text_column = :content
24
- # config.parameters_column = :params
25
- # end
26
- # PromptManager::Prompt.storage_adapter = PromptManager::Storage::ActiveRecordAdapter.new
27
- module PromptManager
28
- # The Storage module provides adapters for different storage backends.
29
- # Each adapter implements a common interface for managing prompts.
30
- # Note: PromptManager::Prompt uses one of these adapters as its storage backend to
31
- # perform all CRUD operations on prompt data.
32
- module Storage
33
- end
34
- end
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module PromptManager
4
- VERSION = "0.5.8"
5
- end
Binary file