prompt_manager 1.0.1 → 1.1.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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +130 -25
  3. data/README.md +127 -263
  4. data/lib/pm/configuration.rb +34 -4
  5. data/lib/pm/core_directives.rb +21 -20
  6. data/lib/pm/directive.rb +26 -34
  7. data/lib/pm/directives.rb +55 -16
  8. data/lib/pm/errors.rb +71 -0
  9. data/lib/pm/metadata.rb +110 -9
  10. data/lib/pm/parsed.rb +51 -46
  11. data/lib/pm/render_scope.rb +60 -0
  12. data/lib/pm/renderer.rb +59 -0
  13. data/lib/pm/shell.rb +95 -40
  14. data/lib/pm/version.rb +1 -1
  15. data/lib/pm.rb +116 -57
  16. data/lib/prompt_manager.rb +6 -0
  17. metadata +13 -113
  18. data/.envrc +0 -3
  19. data/.irbrc +0 -14
  20. data/COMMITS.md +0 -196
  21. data/Rakefile +0 -12
  22. data/docs/api/configuration.md +0 -59
  23. data/docs/api/constants.md +0 -60
  24. data/docs/api/index.md +0 -14
  25. data/docs/api/metadata.md +0 -99
  26. data/docs/api/parsed.md +0 -98
  27. data/docs/api/pm-module.md +0 -131
  28. data/docs/api/render-context.md +0 -51
  29. data/docs/architecture/design-decisions.md +0 -70
  30. data/docs/architecture/index.md +0 -6
  31. data/docs/architecture/processing-pipeline.md +0 -112
  32. data/docs/assets/css/custom.css +0 -1
  33. data/docs/assets/images/prompt_manager.gif +0 -0
  34. data/docs/assets/images/prompt_manager.mp4 +0 -0
  35. data/docs/examples/ai-agent-prompts.md +0 -173
  36. data/docs/examples/code-review-prompt.md +0 -107
  37. data/docs/examples/index.md +0 -7
  38. data/docs/examples/multi-file-composition.md +0 -123
  39. data/docs/getting-started/configuration.md +0 -106
  40. data/docs/getting-started/index.md +0 -7
  41. data/docs/getting-started/installation.md +0 -34
  42. data/docs/getting-started/quick-start.md +0 -81
  43. data/docs/guides/comment-stripping.md +0 -64
  44. data/docs/guides/custom-directives.md +0 -180
  45. data/docs/guides/erb-rendering.md +0 -102
  46. data/docs/guides/includes.md +0 -184
  47. data/docs/guides/index.md +0 -11
  48. data/docs/guides/parameters.md +0 -96
  49. data/docs/guides/parsing.md +0 -127
  50. data/docs/guides/shell-expansion.md +0 -108
  51. data/docs/index.md +0 -70
  52. data/mkdocs.yml +0 -181
@@ -1,106 +0,0 @@
1
- # Configuration
2
-
3
- PM has three global settings controlled through `PM.configure`.
4
-
5
- ## Setting Defaults
6
-
7
- ```ruby
8
- PM.configure do |config|
9
- config.prompts_dir = '~/.prompts' # default: ''
10
- config.shell = true # default: true
11
- config.erb = true # default: true
12
- end
13
- ```
14
-
15
- ## prompts_dir
16
-
17
- Prepended to relative file paths passed to `PM.parse`. Absolute paths bypass it. Symbols and single words are converted to `.md` basenames first.
18
-
19
- ```ruby
20
- PM.configure { |c| c.prompts_dir = '/usr/share/prompts' }
21
-
22
- PM.parse('code_review.md')
23
- #=> reads /usr/share/prompts/code_review.md
24
-
25
- PM.parse(:code_review)
26
- #=> reads /usr/share/prompts/code_review.md
27
-
28
- PM.parse('code_review')
29
- #=> reads /usr/share/prompts/code_review.md
30
-
31
- PM.parse('/absolute/path/review.md')
32
- #=> reads /absolute/path/review.md (prompts_dir ignored)
33
- ```
34
-
35
- Subdirectories work naturally:
36
-
37
- ```ruby
38
- PM.configure { |c| c.prompts_dir = '~/.prompts' }
39
-
40
- PM.parse('agents/summarize.md')
41
- #=> reads ~/.prompts/agents/summarize.md
42
- ```
43
-
44
- ## shell
45
-
46
- Controls whether shell expansion (`$VAR`, `$(command)`) runs during parsing. Defaults to `true`.
47
-
48
- ```ruby
49
- PM.configure { |c| c.shell = false }
50
-
51
- # All files now skip shell expansion by default
52
- ```
53
-
54
- Per-file YAML metadata overrides the global setting:
55
-
56
- ```markdown
57
- ---
58
- title: Override Example
59
- shell: true
60
- ---
61
- This file gets shell expansion even when the global default is false.
62
- User: $USER
63
- ```
64
-
65
- ## erb
66
-
67
- Controls whether ERB rendering runs during `to_s`. Defaults to `true`.
68
-
69
- ```ruby
70
- PM.configure { |c| c.erb = false }
71
-
72
- # All files now skip ERB rendering by default
73
- ```
74
-
75
- Per-file metadata overrides this too:
76
-
77
- ```markdown
78
- ---
79
- title: Override Example
80
- erb: true
81
- ---
82
- This file renders ERB even when the global default is false.
83
- Result: <%= 2 + 2 %>
84
- ```
85
-
86
- ## Resetting
87
-
88
- Restore all settings to their defaults:
89
-
90
- ```ruby
91
- PM.config.reset!
92
-
93
- PM.config.prompts_dir #=> ''
94
- PM.config.shell #=> true
95
- PM.config.erb #=> true
96
- ```
97
-
98
- ## Accessing Current Settings
99
-
100
- ```ruby
101
- PM.config.prompts_dir
102
- PM.config.shell
103
- PM.config.erb
104
- ```
105
-
106
- `PM.config` returns the singleton `PM::Configuration` instance. `PM.configure` yields the same instance.
@@ -1,7 +0,0 @@
1
- # Getting Started
2
-
3
- This section covers everything you need to start using PM in your Ruby projects.
4
-
5
- - [Installation](installation.md) -- Add PM to your project
6
- - [Quick Start](quick-start.md) -- Parse your first prompt in a few lines
7
- - [Configuration](configuration.md) -- Set global defaults for prompt directories, shell expansion, and ERB rendering
@@ -1,34 +0,0 @@
1
- # Installation
2
-
3
- ## Gemfile
4
-
5
- Add PM to your Gemfile:
6
-
7
- ```ruby
8
- gem 'prompt_manager'
9
- ```
10
-
11
- Then run:
12
-
13
- ```bash
14
- bundle install
15
- ```
16
-
17
- ## Manual Install
18
-
19
- ```bash
20
- gem install prompt_manager
21
- ```
22
-
23
- ## Require
24
-
25
- ```ruby
26
- require 'pm'
27
- ```
28
-
29
- The `PM` module is the primary interface. All public methods are accessed through it.
30
-
31
- ## Requirements
32
-
33
- - Ruby >= 3.2.0
34
- - No runtime dependencies beyond the standard library (plus `ostruct` on Ruby 4.0+)
@@ -1,81 +0,0 @@
1
- # Quick Start
2
-
3
- ## Parse a String
4
-
5
- ```ruby
6
- require 'pm'
7
-
8
- parsed = PM.parse("---\ntitle: Hello\n---\nContent here")
9
-
10
- parsed.metadata.title #=> "Hello"
11
- parsed.content #=> "\nContent here\n"
12
- ```
13
-
14
- ## Parse a File
15
-
16
- Given `greeting.md`:
17
-
18
- ```markdown
19
- ---
20
- title: Greeting
21
- parameters:
22
- name: null
23
- ---
24
- Hello, <%= name %>! Welcome.
25
- ```
26
-
27
- ```ruby
28
- # Any of these forms work:
29
- parsed = PM.parse('greeting.md') # explicit .md extension
30
- parsed = PM.parse(:greeting) # Symbol — .md appended
31
- parsed = PM.parse('greeting') # single word — .md appended
32
-
33
- parsed.metadata.title #=> "Greeting"
34
- parsed.metadata.parameters #=> {"name" => nil}
35
-
36
- # Render with parameters
37
- puts parsed.to_s('name' => 'Alice')
38
- #=> "Hello, Alice! Welcome."
39
- ```
40
-
41
- When parsing a file, PM also adds `directory`, `name`, `created_at`, and `modified_at` to the metadata.
42
-
43
- ## Parameters
44
-
45
- Parameters declared in the YAML front-matter define template variables:
46
-
47
- - **`null` value** -- parameter is required; `to_s` raises `ArgumentError` if missing
48
- - **Any other value** -- used as the default; can be overridden in `to_s`
49
-
50
- ```ruby
51
- parsed = PM.parse("---\nparameters:\n lang: ruby\n code: null\n---\n<%= lang %>: <%= code %>")
52
-
53
- # Use defaults where available, supply required params
54
- parsed.to_s('code' => 'puts "hi"') #=> "ruby: puts \"hi\""
55
-
56
- # Override defaults
57
- parsed.to_s('code' => 'print("hi")', 'lang' => 'python')
58
- ```
59
-
60
- ## Shell Expansion
61
-
62
- Environment variables and commands are expanded at parse time:
63
-
64
- ```markdown
65
- ---
66
- title: Info
67
- ---
68
- User: $USER
69
- Date: $(date +%Y-%m-%d)
70
- ```
71
-
72
- ```ruby
73
- parsed = PM.parse('info.md')
74
- parsed.content #=> "User: dewayne\nDate: 2025-01-15\n"
75
- ```
76
-
77
- ## Next Steps
78
-
79
- - [Configuration](configuration.md) -- Set a prompts directory and global defaults
80
- - [Parsing Guide](../guides/parsing.md) -- File vs string parsing in depth
81
- - [Parameters Guide](../guides/parameters.md) -- Required and default parameters
@@ -1,64 +0,0 @@
1
- # Comment Stripping
2
-
3
- HTML comments are stripped before any other processing stage.
4
-
5
- ## Automatic Stripping
6
-
7
- Comments are removed during `PM.parse`:
8
-
9
- ```markdown
10
- <!-- This comment will be removed -->
11
- ---
12
- title: My Prompt
13
- ---
14
- Content here. <!-- This too -->
15
- ```
16
-
17
- After parsing:
18
-
19
- ```ruby
20
- parsed = PM.parse(source)
21
- parsed.content #=> "\nContent here. \n"
22
- ```
23
-
24
- ## Multiline Comments
25
-
26
- Multiline comments are fully removed:
27
-
28
- ```markdown
29
- <!--
30
- This entire block
31
- is removed
32
- -->
33
- ---
34
- title: Example
35
- ---
36
- Visible content.
37
- ```
38
-
39
- ## Comments Before Metadata
40
-
41
- Comments before the YAML front-matter are stripped first, so the metadata parser sees clean input:
42
-
43
- ```markdown
44
- <!-- Editor note: draft version -->
45
- ---
46
- title: My Prompt
47
- ---
48
- Content
49
- ```
50
-
51
- ## Direct Access
52
-
53
- The comment stripping method is available as a standalone utility:
54
-
55
- ```ruby
56
- PM.strip_comments("Hello <!-- removed --> World")
57
- #=> "Hello World"
58
- ```
59
-
60
- ## Use Cases
61
-
62
- - **Editor notes** -- Leave comments for prompt authors that don't appear in the rendered output
63
- - **Disabled sections** -- Temporarily comment out parts of a prompt
64
- - **Documentation** -- Annotate prompt files without affecting the AI input
@@ -1,180 +0,0 @@
1
- # Custom Directives
2
-
3
- Register custom methods that become available inside ERB templates.
4
-
5
- ## Registering a Directive
6
-
7
- ```ruby
8
- PM.register(:read) { |_ctx, path| File.read(path) }
9
- PM.register(:env) { |_ctx, key| ENV.fetch(key, '') }
10
- PM.register(:run) { |_ctx, cmd| `#{cmd}`.chomp }
11
- ```
12
-
13
- Use them in any prompt file:
14
-
15
- ```markdown
16
- ---
17
- title: Deploy Prompt
18
- ---
19
- Hostname: <%= read '/etc/hostname' %>
20
- Environment: <%= env 'DEPLOY_ENV' %>
21
- Recent commits: <%= run 'git log --oneline -5' %>
22
- ```
23
-
24
- ## Aliases
25
-
26
- Register multiple names for the same directive by passing additional names:
27
-
28
- ```ruby
29
- PM.register(:webpage, :website, :web) { |_ctx, url| fetch_page(url) }
30
- ```
31
-
32
- All names point to the same block. Use any of them in ERB:
33
-
34
- ```markdown
35
- <%= webpage 'https://example.com' %>
36
- <%= web 'https://example.com' %>
37
- ```
38
-
39
- Duplicate detection still applies — if any name is already registered, an error is raised.
40
-
41
- ## The RenderContext
42
-
43
- The first argument to every directive block is a `PM::RenderContext` with access to the current render state:
44
-
45
- | Field | Type | Description |
46
- |-------|------|-------------|
47
- | `directory` | String | Directory of the file being rendered |
48
- | `params` | Hash | Merged parameter values |
49
- | `metadata` | PM::Metadata | Current file's metadata |
50
- | `depth` | Integer | Include nesting depth (0 for top-level) |
51
- | `included` | Set | File paths already in the include chain |
52
-
53
- ```ruby
54
- PM.register(:current_file) { |ctx| ctx.metadata.name || 'unknown' }
55
- PM.register(:nesting) { |ctx| ctx.depth.to_s }
56
- ```
57
-
58
- The context is always the first argument. Additional arguments come from the ERB call:
59
-
60
- ```ruby
61
- PM.register(:greet) { |_ctx, name| "Hello, #{name}!" }
62
- ```
63
-
64
- ```markdown
65
- <%= greet 'Alice' %>
66
- ```
67
-
68
- ## Duplicate Registration
69
-
70
- Registering a name that already exists raises an error:
71
-
72
- ```ruby
73
- PM.register(:include) { |_ctx, path| path }
74
- #=> RuntimeError: Directive already registered: include
75
- ```
76
-
77
- This protects built-in directives from being overwritten.
78
-
79
- ## Resetting Directives
80
-
81
- Remove all custom directives and restore only the built-ins:
82
-
83
- ```ruby
84
- PM.reset_directives!
85
- ```
86
-
87
- After reset, only the built-in directives are registered: `include`, `insert`, and `read`.
88
-
89
- ## Directives in Included Files
90
-
91
- Custom directives are available in included files too. They share the same directive registry:
92
-
93
- ```markdown
94
- <!-- parent.md -->
95
- <%= include 'child.md' %>
96
-
97
- <!-- child.md -->
98
- <%= read '/etc/hostname' %>
99
- ```
100
-
101
- ## Name Format
102
-
103
- Both symbols and strings are accepted:
104
-
105
- ```ruby
106
- PM.register(:my_helper) { |_ctx| "works" }
107
- PM.register('other_helper') { |_ctx| "also works" }
108
- ```
109
-
110
- ## Class-Based Directives
111
-
112
- For organized groups of directives, subclass `PM::Directive`:
113
-
114
- ```ruby
115
- class MyDirectives < PM::Directive
116
- desc "Fetch environment variable"
117
- def env(ctx, key)
118
- ENV.fetch(key, '')
119
- end
120
-
121
- desc "Run a shell command"
122
- def run(ctx, cmd)
123
- `#{cmd}`.chomp
124
- end
125
- alias_method :exec, :run
126
-
127
- # No desc — not registered as a directive
128
- def helper
129
- "internal"
130
- end
131
- end
132
-
133
- PM::Directive.register_all
134
- ```
135
-
136
- `desc` marks the next method as a directive. Methods without `desc` are ordinary helpers and won't be registered. `alias_method` aliases are detected automatically via `UnboundMethod#original_name`.
137
-
138
- ### Category name
139
-
140
- The class name determines a category heading (useful for help output):
141
-
142
- ```ruby
143
- MyDirectives.category_name #=> "My"
144
- PM::CoreDirectives.category_name #=> "Core"
145
- ```
146
-
147
- The last segment of the class name is used, with `Directives` stripped and camelCase split.
148
-
149
- ### Dispatch customization
150
-
151
- Override `build_dispatch_block` to customize how methods are called when dispatched through `PM.directives`:
152
-
153
- ```ruby
154
- class MyDirectives < PM::Directive
155
- class << self
156
- # Default: proc { |ctx, *args| inst.send(method_name, ctx, *args) }
157
- def build_dispatch_block(inst, method_name)
158
- proc { |_ctx, *args| inst.send(method_name, args.flatten) }
159
- end
160
- end
161
- end
162
- ```
163
-
164
- ### Subclass tracking
165
-
166
- All `PM::Directive` subclasses (direct and indirect) are tracked centrally:
167
-
168
- ```ruby
169
- PM::Directive.directive_subclasses
170
- #=> [PM::CoreDirectives, MyDirectives, ...]
171
- ```
172
-
173
- `register_all` iterates this list, creates a singleton instance per subclass, and registers each described method with `PM.register`.
174
-
175
- ## Listing Directives
176
-
177
- ```ruby
178
- PM.directives
179
- #=> { include: #<Proc>, insert: #<Proc>, read: #<Proc>, ... }
180
- ```
@@ -1,102 +0,0 @@
1
- # ERB Rendering
2
-
3
- ERB templates are evaluated on demand when `to_s` is called.
4
-
5
- ## Basics
6
-
7
- Any `<%= expression %>` in the content is evaluated as Ruby:
8
-
9
- ```markdown
10
- ---
11
- title: Math
12
- ---
13
- Result: <%= 2 + 2 %>
14
- Today: <%= Time.now.strftime('%Y-%m-%d') %>
15
- ```
16
-
17
- ```ruby
18
- parsed = PM.parse('math.md')
19
- parsed.to_s #=> "Result: 4\nToday: 2025-01-15\n"
20
- ```
21
-
22
- ## Template Parameters
23
-
24
- Parameters declared in the YAML front-matter are available as local methods inside ERB:
25
-
26
- ```markdown
27
- ---
28
- title: Greeting
29
- parameters:
30
- name: World
31
- ---
32
- Hello, <%= name %>!
33
- ```
34
-
35
- ```ruby
36
- parsed = PM.parse('greeting.md')
37
- parsed.to_s #=> "Hello, World!"
38
- parsed.to_s('name' => 'Alice') #=> "Hello, Alice!"
39
- ```
40
-
41
- See [Parameters](parameters.md) for details on required vs default values.
42
-
43
- ## Registered Directives
44
-
45
- Custom directives registered with `PM.register` are also available in ERB:
46
-
47
- ```ruby
48
- PM.register(:upcase) { |_ctx, str| str.upcase }
49
- ```
50
-
51
- ```markdown
52
- ---
53
- title: Example
54
- ---
55
- <%= upcase 'hello' %>
56
- ```
57
-
58
- See [Custom Directives](custom-directives.md) for the full guide.
59
-
60
- ## Built-in Directives
61
-
62
- PM includes one built-in directive:
63
-
64
- - **`include`** -- Include and render another prompt file. See [Including Files](includes.md).
65
-
66
- ## Disabling ERB
67
-
68
- Set `erb: false` in the file's YAML metadata:
69
-
70
- ```markdown
71
- ---
72
- title: Raw Template
73
- erb: false
74
- ---
75
- This <%= expression %> is preserved as-is.
76
- ```
77
-
78
- When ERB is disabled:
79
-
80
- - `to_s` returns the content without template evaluation
81
- - Parameters are ignored
82
- - The `include` directive does not execute
83
- - `metadata.includes` is an empty array after `to_s`
84
-
85
- ### Global Default
86
-
87
- ```ruby
88
- PM.configure { |c| c.erb = false }
89
- ```
90
-
91
- Per-file metadata always overrides the global setting. A file with `erb: true` gets ERB rendering even when the global default is `false`.
92
-
93
- ## Pipeline Position
94
-
95
- ERB rendering is the last stage of the pipeline, executed on demand:
96
-
97
- 1. Strip HTML comments
98
- 2. Extract YAML metadata
99
- 3. Shell expansion (at parse time)
100
- 4. **ERB rendering** (at `to_s` time)
101
-
102
- This means shell variables are already expanded before ERB sees the content. If content contains both `$VAR` and `<%= param %>`, the shell variable is resolved first, then ERB fills in the parameters.