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
data/README.md
CHANGED
|
@@ -1,625 +1,315 @@
|
|
|
1
|
-
# PromptManager
|
|
2
|
-
|
|
3
|
-
>
|
|
4
|
-
>
|
|
5
|
-
|
|
6
|
-
<br
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
28
|
+
require 'pm'
|
|
135
29
|
```
|
|
136
30
|
|
|
137
|
-
|
|
31
|
+
## Configuration
|
|
138
32
|
|
|
139
|
-
|
|
33
|
+
Set global defaults with `PM.configure`:
|
|
140
34
|
|
|
141
35
|
```ruby
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
#
|
|
146
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
"[NAME]" => "Alice",
|
|
162
|
-
"[LANGUAGE]" => "French"
|
|
163
|
-
}
|
|
48
|
+
PM.parse('code_review.md')
|
|
49
|
+
#=> reads /usr/share/prompts/code_review.md
|
|
164
50
|
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
# Dynamic directives using keywords
|
|
198
|
-
//[COMMAND] [OPTIONS]
|
|
66
|
+
```ruby
|
|
67
|
+
PM.config.reset!
|
|
199
68
|
```
|
|
200
69
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
You can create custom directive processors:
|
|
70
|
+
Access the current configuration:
|
|
204
71
|
|
|
205
72
|
```ruby
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
#### ERB Templates
|
|
78
|
+
## Usage
|
|
228
79
|
|
|
229
|
-
|
|
80
|
+
`PM.parse` accepts a file path, a Symbol, a single word, or a string:
|
|
230
81
|
|
|
231
82
|
```ruby
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
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
|
-
|
|
241
|
-
|
|
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
|
-
|
|
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
|
-
|
|
116
|
+
Parse it:
|
|
250
117
|
|
|
251
118
|
```ruby
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
269
|
-
#
|
|
270
|
-
|
|
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
|
-
|
|
130
|
+
# Override defaults
|
|
131
|
+
parsed.to_s('code' => File.read('app.py'), 'language' => 'python')
|
|
278
132
|
|
|
279
|
-
|
|
280
|
-
|
|
133
|
+
# All params have defaults — no arguments needed
|
|
134
|
+
parsed.to_s
|
|
281
135
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
143
|
+
### Shell expansion
|
|
294
144
|
|
|
295
|
-
|
|
145
|
+
Shell references are expanded during parsing (when `shell: true`, the default):
|
|
296
146
|
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
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
|
-
|
|
165
|
+
Shell expansion is also available directly:
|
|
315
166
|
|
|
316
167
|
```ruby
|
|
317
|
-
|
|
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
|
-
|
|
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
|
-
|
|
173
|
+
Use `include` in ERB to compose prompts from multiple files:
|
|
336
174
|
|
|
337
|
-
|
|
175
|
+
```md
|
|
176
|
+
---
|
|
177
|
+
title: Full Review
|
|
178
|
+
parameters:
|
|
179
|
+
code: null
|
|
180
|
+
---
|
|
181
|
+
<%= include 'common/header.md' %>
|
|
338
182
|
|
|
339
|
-
|
|
183
|
+
Review this code:
|
|
184
|
+
<%= code %>
|
|
340
185
|
|
|
341
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
-
|
|
373
|
-
|
|
374
|
-
- `list` - Returns array of all prompt IDs
|
|
375
|
-
- `path(id)` - Returns Pathname to prompt file
|
|
216
|
+
### Custom directives
|
|
376
217
|
|
|
377
|
-
|
|
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
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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
|
-
|
|
226
|
+
Register multiple names for the same directive (aliases):
|
|
393
227
|
|
|
394
228
|
```ruby
|
|
395
|
-
|
|
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
|
-
|
|
408
|
-
|
|
409
|
-
Create your own storage adapter:
|
|
232
|
+
All three names call the same block. Use any of them in ERB:
|
|
410
233
|
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
-
|
|
439
|
-
|
|
440
|
-
### Initialization Options
|
|
240
|
+
Use them in any prompt file:
|
|
441
241
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
260
|
+
PM.register(:current_file) { |ctx| ctx.metadata.name || 'unknown' }
|
|
261
|
+
PM.register(:depth) { |ctx| ctx.depth.to_s }
|
|
469
262
|
```
|
|
470
263
|
|
|
471
|
-
|
|
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
|
-
|
|
479
|
-
|
|
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
|
-
|
|
504
|
-
|
|
505
|
-
Implement powerful search across prompts:
|
|
271
|
+
Reset to built-in directives only:
|
|
506
272
|
|
|
507
273
|
```ruby
|
|
508
|
-
|
|
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
|
-
|
|
277
|
+
### Disabling processing stages
|
|
526
278
|
|
|
527
|
-
|
|
279
|
+
Set `shell: false` or `erb: false` in the metadata to skip those stages:
|
|
528
280
|
|
|
529
|
-
```
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
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
|
-
|
|
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
|
-
|
|
292
|
+
### HTML comment stripping
|
|
559
293
|
|
|
560
|
-
|
|
294
|
+
HTML comments are stripped before any other processing:
|
|
561
295
|
|
|
562
|
-
|
|
296
|
+
```md
|
|
297
|
+
<!-- This comment will be removed -->
|
|
298
|
+
---
|
|
299
|
+
title: My Prompt
|
|
300
|
+
---
|
|
301
|
+
Content here. <!-- This too -->
|
|
302
|
+
```
|
|
563
303
|
|
|
564
|
-
|
|
304
|
+
Comments are also available directly:
|
|
565
305
|
|
|
566
306
|
```ruby
|
|
567
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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`
|