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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/README.md +206 -516
- data/Rakefile +0 -8
- data/docs/api/configuration.md +31 -327
- data/docs/api/constants.md +60 -0
- data/docs/api/index.md +14 -0
- data/docs/api/metadata.md +99 -0
- data/docs/api/parsed.md +98 -0
- data/docs/api/pm-module.md +131 -0
- data/docs/api/render-context.md +51 -0
- data/docs/architecture/design-decisions.md +70 -0
- data/docs/architecture/index.md +6 -0
- data/docs/architecture/processing-pipeline.md +112 -0
- data/docs/assets/css/custom.css +1 -0
- data/docs/assets/images/prompt_manager.gif +0 -0
- data/docs/assets/images/prompt_manager.mp4 +0 -0
- data/docs/examples/ai-agent-prompts.md +173 -0
- data/docs/examples/code-review-prompt.md +107 -0
- data/docs/examples/index.md +7 -0
- data/docs/examples/multi-file-composition.md +123 -0
- data/docs/getting-started/configuration.md +106 -0
- data/docs/getting-started/index.md +7 -0
- data/docs/getting-started/installation.md +10 -73
- data/docs/getting-started/quick-start.md +50 -225
- data/docs/guides/comment-stripping.md +64 -0
- data/docs/guides/custom-directives.md +115 -0
- data/docs/guides/erb-rendering.md +102 -0
- data/docs/guides/includes.md +146 -0
- data/docs/guides/index.md +11 -0
- data/docs/guides/parameters.md +96 -0
- data/docs/guides/parsing.md +127 -0
- data/docs/guides/shell-expansion.md +108 -0
- data/docs/index.md +54 -214
- data/lib/pm/configuration.rb +17 -0
- data/lib/pm/directives.rb +61 -0
- data/lib/pm/metadata.rb +17 -0
- data/lib/pm/parsed.rb +59 -0
- data/lib/pm/shell.rb +57 -0
- data/lib/pm/version.rb +5 -0
- data/lib/pm.rb +121 -0
- data/lib/prompt_manager.rb +2 -27
- data/mkdocs.yml +101 -66
- metadata +42 -101
- data/docs/.keep +0 -0
- data/docs/advanced/custom-keywords.md +0 -421
- data/docs/advanced/dynamic-directives.md +0 -535
- data/docs/advanced/performance.md +0 -612
- data/docs/advanced/search-integration.md +0 -635
- data/docs/api/directive-processor.md +0 -431
- data/docs/api/prompt-class.md +0 -354
- data/docs/api/storage-adapters.md +0 -462
- data/docs/assets/favicon.ico +0 -1
- data/docs/assets/logo.svg +0 -24
- data/docs/core-features/comments.md +0 -48
- data/docs/core-features/directive-processing.md +0 -38
- data/docs/core-features/erb-integration.md +0 -68
- data/docs/core-features/error-handling.md +0 -197
- data/docs/core-features/parameter-history.md +0 -76
- data/docs/core-features/parameterized-prompts.md +0 -500
- data/docs/core-features/shell-integration.md +0 -79
- data/docs/development/architecture.md +0 -544
- data/docs/development/contributing.md +0 -425
- data/docs/development/roadmap.md +0 -234
- data/docs/development/testing.md +0 -822
- data/docs/examples/advanced.md +0 -523
- data/docs/examples/basic.md +0 -688
- data/docs/examples/real-world.md +0 -776
- data/docs/examples.md +0 -337
- data/docs/getting-started/basic-concepts.md +0 -318
- data/docs/migration/v0.9.0.md +0 -459
- data/docs/migration/v1.0.0.md +0 -591
- data/docs/storage/activerecord-adapter.md +0 -348
- data/docs/storage/custom-adapters.md +0 -176
- data/docs/storage/filesystem-adapter.md +0 -236
- data/docs/storage/overview.md +0 -427
- data/examples/advanced_integrations.rb +0 -52
- data/examples/directives.rb +0 -102
- data/examples/prompts_dir/advanced_demo.txt +0 -79
- data/examples/prompts_dir/directive_example.json +0 -1
- data/examples/prompts_dir/directive_example.txt +0 -8
- data/examples/prompts_dir/todo.json +0 -1
- data/examples/prompts_dir/todo.txt +0 -7
- data/examples/prompts_dir/toy/8-ball.txt +0 -4
- data/examples/rgfzf +0 -44
- data/examples/simple.rb +0 -160
- data/examples/using_search_proc.rb +0 -68
- data/improvement_plan.md +0 -996
- data/lib/prompt_manager/directive_processor.rb +0 -47
- data/lib/prompt_manager/prompt.rb +0 -195
- data/lib/prompt_manager/storage/active_record_adapter.rb +0 -157
- data/lib/prompt_manager/storage/file_system_adapter.rb +0 -339
- data/lib/prompt_manager/storage.rb +0 -34
- data/lib/prompt_manager/version.rb +0 -5
- 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
|
data/prompt_manager_logo.png
DELETED
|
Binary file
|