pupt-lib 1.2.6 → 1.3.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 (182) hide show
  1. package/README.md +292 -256
  2. package/dist/index.d.ts +458 -29
  3. package/dist/index.js +2030 -360
  4. package/dist/jsx-dev-runtime.d.ts +483 -0
  5. package/dist/jsx-runtime/index.d.ts +2 -29
  6. package/dist/jsx-runtime/jsx-dev-runtime.d.ts +2 -26
  7. package/package.json +4 -4
  8. package/dist/api.d.ts +0 -38
  9. package/dist/api.d.ts.map +0 -1
  10. package/dist/component.d.ts +0 -83
  11. package/dist/component.d.ts.map +0 -1
  12. package/dist/components/ask/Choice.d.ts +0 -156
  13. package/dist/components/ask/Choice.d.ts.map +0 -1
  14. package/dist/components/ask/Confirm.d.ts +0 -62
  15. package/dist/components/ask/Confirm.d.ts.map +0 -1
  16. package/dist/components/ask/Date.d.ts +0 -79
  17. package/dist/components/ask/Date.d.ts.map +0 -1
  18. package/dist/components/ask/Editor.d.ts +0 -67
  19. package/dist/components/ask/Editor.d.ts.map +0 -1
  20. package/dist/components/ask/File.d.ts +0 -85
  21. package/dist/components/ask/File.d.ts.map +0 -1
  22. package/dist/components/ask/Label.d.ts +0 -28
  23. package/dist/components/ask/Label.d.ts.map +0 -1
  24. package/dist/components/ask/MultiSelect.d.ts +0 -148
  25. package/dist/components/ask/MultiSelect.d.ts.map +0 -1
  26. package/dist/components/ask/Number.d.ts +0 -73
  27. package/dist/components/ask/Number.d.ts.map +0 -1
  28. package/dist/components/ask/Option.d.ts +0 -29
  29. package/dist/components/ask/Option.d.ts.map +0 -1
  30. package/dist/components/ask/Path.d.ts +0 -73
  31. package/dist/components/ask/Path.d.ts.map +0 -1
  32. package/dist/components/ask/Rating.d.ts +0 -79
  33. package/dist/components/ask/Rating.d.ts.map +0 -1
  34. package/dist/components/ask/ReviewFile.d.ts +0 -79
  35. package/dist/components/ask/ReviewFile.d.ts.map +0 -1
  36. package/dist/components/ask/Secret.d.ts +0 -67
  37. package/dist/components/ask/Secret.d.ts.map +0 -1
  38. package/dist/components/ask/Select.d.ts +0 -136
  39. package/dist/components/ask/Select.d.ts.map +0 -1
  40. package/dist/components/ask/Text.d.ts +0 -67
  41. package/dist/components/ask/Text.d.ts.map +0 -1
  42. package/dist/components/ask/index.d.ts +0 -34
  43. package/dist/components/ask/index.d.ts.map +0 -1
  44. package/dist/components/ask/utils.d.ts +0 -26
  45. package/dist/components/ask/utils.d.ts.map +0 -1
  46. package/dist/components/control/ForEach.d.ts +0 -32
  47. package/dist/components/control/ForEach.d.ts.map +0 -1
  48. package/dist/components/control/If.d.ts +0 -24
  49. package/dist/components/control/If.d.ts.map +0 -1
  50. package/dist/components/data/Code.d.ts +0 -31
  51. package/dist/components/data/Code.d.ts.map +0 -1
  52. package/dist/components/data/Data.d.ts +0 -31
  53. package/dist/components/data/Data.d.ts.map +0 -1
  54. package/dist/components/data/File.d.ts +0 -37
  55. package/dist/components/data/File.d.ts.map +0 -1
  56. package/dist/components/data/Json.d.ts +0 -25
  57. package/dist/components/data/Json.d.ts.map +0 -1
  58. package/dist/components/data/Xml.d.ts +0 -25
  59. package/dist/components/data/Xml.d.ts.map +0 -1
  60. package/dist/components/data/index.d.ts +0 -6
  61. package/dist/components/data/index.d.ts.map +0 -1
  62. package/dist/components/examples/Example.d.ts +0 -17
  63. package/dist/components/examples/Example.d.ts.map +0 -1
  64. package/dist/components/examples/ExampleInput.d.ts +0 -13
  65. package/dist/components/examples/ExampleInput.d.ts.map +0 -1
  66. package/dist/components/examples/ExampleOutput.d.ts +0 -13
  67. package/dist/components/examples/ExampleOutput.d.ts.map +0 -1
  68. package/dist/components/examples/Examples.d.ts +0 -13
  69. package/dist/components/examples/Examples.d.ts.map +0 -1
  70. package/dist/components/examples/index.d.ts +0 -5
  71. package/dist/components/examples/index.d.ts.map +0 -1
  72. package/dist/components/index.d.ts +0 -11
  73. package/dist/components/index.d.ts.map +0 -1
  74. package/dist/components/meta/Uses.d.ts +0 -98
  75. package/dist/components/meta/Uses.d.ts.map +0 -1
  76. package/dist/components/meta/index.d.ts +0 -2
  77. package/dist/components/meta/index.d.ts.map +0 -1
  78. package/dist/components/post-execution/OpenUrl.d.ts +0 -29
  79. package/dist/components/post-execution/OpenUrl.d.ts.map +0 -1
  80. package/dist/components/post-execution/PostExecution.d.ts +0 -13
  81. package/dist/components/post-execution/PostExecution.d.ts.map +0 -1
  82. package/dist/components/post-execution/ReviewFile.d.ts +0 -29
  83. package/dist/components/post-execution/ReviewFile.d.ts.map +0 -1
  84. package/dist/components/post-execution/RunCommand.d.ts +0 -35
  85. package/dist/components/post-execution/RunCommand.d.ts.map +0 -1
  86. package/dist/components/post-execution/index.d.ts +0 -5
  87. package/dist/components/post-execution/index.d.ts.map +0 -1
  88. package/dist/components/reasoning/Step.d.ts +0 -25
  89. package/dist/components/reasoning/Step.d.ts.map +0 -1
  90. package/dist/components/reasoning/Steps.d.ts +0 -14
  91. package/dist/components/reasoning/Steps.d.ts.map +0 -1
  92. package/dist/components/reasoning/index.d.ts +0 -3
  93. package/dist/components/reasoning/index.d.ts.map +0 -1
  94. package/dist/components/structural/Audience.d.ts +0 -25
  95. package/dist/components/structural/Audience.d.ts.map +0 -1
  96. package/dist/components/structural/Constraint.d.ts +0 -31
  97. package/dist/components/structural/Constraint.d.ts.map +0 -1
  98. package/dist/components/structural/Context.d.ts +0 -25
  99. package/dist/components/structural/Context.d.ts.map +0 -1
  100. package/dist/components/structural/Criterion.d.ts +0 -13
  101. package/dist/components/structural/Criterion.d.ts.map +0 -1
  102. package/dist/components/structural/Format.d.ts +0 -37
  103. package/dist/components/structural/Format.d.ts.map +0 -1
  104. package/dist/components/structural/Prompt.d.ts +0 -43
  105. package/dist/components/structural/Prompt.d.ts.map +0 -1
  106. package/dist/components/structural/Role.d.ts +0 -37
  107. package/dist/components/structural/Role.d.ts.map +0 -1
  108. package/dist/components/structural/Section.d.ts +0 -31
  109. package/dist/components/structural/Section.d.ts.map +0 -1
  110. package/dist/components/structural/SuccessCriteria.d.ts +0 -25
  111. package/dist/components/structural/SuccessCriteria.d.ts.map +0 -1
  112. package/dist/components/structural/Task.d.ts +0 -25
  113. package/dist/components/structural/Task.d.ts.map +0 -1
  114. package/dist/components/structural/Tone.d.ts +0 -25
  115. package/dist/components/structural/Tone.d.ts.map +0 -1
  116. package/dist/components/structural/index.d.ts +0 -12
  117. package/dist/components/structural/index.d.ts.map +0 -1
  118. package/dist/components/utility/Cwd.d.ts +0 -11
  119. package/dist/components/utility/Cwd.d.ts.map +0 -1
  120. package/dist/components/utility/DateTime.d.ts +0 -23
  121. package/dist/components/utility/DateTime.d.ts.map +0 -1
  122. package/dist/components/utility/Hostname.d.ts +0 -11
  123. package/dist/components/utility/Hostname.d.ts.map +0 -1
  124. package/dist/components/utility/Timestamp.d.ts +0 -11
  125. package/dist/components/utility/Timestamp.d.ts.map +0 -1
  126. package/dist/components/utility/UUID.d.ts +0 -11
  127. package/dist/components/utility/UUID.d.ts.map +0 -1
  128. package/dist/components/utility/Username.d.ts +0 -11
  129. package/dist/components/utility/Username.d.ts.map +0 -1
  130. package/dist/components/utility/index.d.ts +0 -7
  131. package/dist/components/utility/index.d.ts.map +0 -1
  132. package/dist/create-prompt.d.ts +0 -118
  133. package/dist/create-prompt.d.ts.map +0 -1
  134. package/dist/index.d.ts.map +0 -1
  135. package/dist/jsx-runtime/index.d.ts.map +0 -1
  136. package/dist/jsx-runtime/jsx-dev-runtime.d.ts.map +0 -1
  137. package/dist/render.d.ts +0 -19
  138. package/dist/render.d.ts.map +0 -1
  139. package/dist/services/babel-plugins/index.d.ts +0 -3
  140. package/dist/services/babel-plugins/index.d.ts.map +0 -1
  141. package/dist/services/babel-plugins/name-hoisting.d.ts +0 -8
  142. package/dist/services/babel-plugins/name-hoisting.d.ts.map +0 -1
  143. package/dist/services/babel-plugins/uses-to-import.d.ts +0 -16
  144. package/dist/services/babel-plugins/uses-to-import.d.ts.map +0 -1
  145. package/dist/services/browser-support.d.ts +0 -185
  146. package/dist/services/browser-support.d.ts.map +0 -1
  147. package/dist/services/file-search-engine.d.ts +0 -162
  148. package/dist/services/file-search-engine.d.ts.map +0 -1
  149. package/dist/services/formula-parser.d.ts +0 -9
  150. package/dist/services/formula-parser.d.ts.map +0 -1
  151. package/dist/services/input-iterator.d.ts +0 -54
  152. package/dist/services/input-iterator.d.ts.map +0 -1
  153. package/dist/services/module-evaluator.d.ts +0 -38
  154. package/dist/services/module-evaluator.d.ts.map +0 -1
  155. package/dist/services/module-loader.d.ts +0 -92
  156. package/dist/services/module-loader.d.ts.map +0 -1
  157. package/dist/services/preprocessor.d.ts +0 -52
  158. package/dist/services/preprocessor.d.ts.map +0 -1
  159. package/dist/services/prop-validator.d.ts +0 -21
  160. package/dist/services/prop-validator.d.ts.map +0 -1
  161. package/dist/services/search-engine.d.ts +0 -10
  162. package/dist/services/search-engine.d.ts.map +0 -1
  163. package/dist/services/transformer.d.ts +0 -38
  164. package/dist/services/transformer.d.ts.map +0 -1
  165. package/dist/types/component.d.ts +0 -30
  166. package/dist/types/component.d.ts.map +0 -1
  167. package/dist/types/context.d.ts +0 -341
  168. package/dist/types/context.d.ts.map +0 -1
  169. package/dist/types/element.d.ts +0 -47
  170. package/dist/types/element.d.ts.map +0 -1
  171. package/dist/types/index.d.ts +0 -11
  172. package/dist/types/index.d.ts.map +0 -1
  173. package/dist/types/input.d.ts +0 -61
  174. package/dist/types/input.d.ts.map +0 -1
  175. package/dist/types/module.d.ts +0 -61
  176. package/dist/types/module.d.ts.map +0 -1
  177. package/dist/types/render.d.ts +0 -92
  178. package/dist/types/render.d.ts.map +0 -1
  179. package/dist/types/search.d.ts +0 -59
  180. package/dist/types/search.d.ts.map +0 -1
  181. package/dist/types/symbols.d.ts +0 -9
  182. package/dist/types/symbols.d.ts.map +0 -1
package/README.md CHANGED
@@ -8,14 +8,15 @@ A TypeScript library for creating AI prompts as versionable, composable, shareab
8
8
 
9
9
  **Features:**
10
10
 
11
- - **JSX Syntax** - Write prompts using familiar JSX/TSX syntax with built-in components
12
- - **Composable** - Build complex prompts from reusable, shareable components
13
- - **Version Controlled** - Prompts live in files, tracked in git, reviewed in PRs
14
- - **Shareable** - Publish prompt libraries to npm, consume others' work via npm, URLs, or local files
15
- - **UI Agnostic** - Library handles prompt logic; any UI (CLI, web, desktop) can collect inputs
16
- - **LLM Agnostic** - Target Claude, GPT, Gemini, or others with environment-based output optimization
17
- - **Research-Backed** - Built-in components encode prompt engineering best practices
18
- - **Browser & Node.js** - Works in both environments with runtime JSX transformation
11
+ - **JSX Syntax** Write prompts using familiar JSX/TSX syntax with 50+ built-in components
12
+ - **`.prompt` Files** Simplified format with no imports, no exports — just JSX
13
+ - **Composable** Build complex prompts from reusable, shareable components
14
+ - **Provider Targeting** Adapt output for Claude, GPT, Gemini, and others via environment config
15
+ - **Presets** Role, task, constraint, and steps presets encode prompt engineering best practices
16
+ - **Smart Defaults** — Auto-generated role, format, and constraint sections with full opt-out control
17
+ - **Version Controlled** Prompts live in files, tracked in git, reviewed in PRs
18
+ - **Shareable** Publish prompt libraries to npm, consume others' work via npm, URLs, or local files
19
+ - **Browser & Node.js** — Works in both environments with runtime JSX transformation
19
20
 
20
21
  ## Installation
21
22
 
@@ -29,11 +30,44 @@ For build-time JSX transformation (recommended for production), also install the
29
30
  npm install --save-dev @babel/core @babel/plugin-transform-react-jsx @babel/preset-typescript
30
31
  ```
31
32
 
32
- ## Quick Start
33
+ ## Quick Start with `.prompt`
33
34
 
34
- Create a prompt file `greeting.tsx`:
35
+ The simplest way to write a prompt no imports or exports needed:
36
+
37
+ ```xml
38
+ <!-- greeting.prompt -->
39
+ <Prompt name="greeting" description="A friendly greeting prompt">
40
+ <Role>You are a friendly assistant.</Role>
41
+ <Task>
42
+ Greet the user named <Ask.Text name="userName" label="User's name" /> warmly.
43
+ </Task>
44
+ <Constraint type="must">Keep the greeting under 50 words.</Constraint>
45
+ </Prompt>
46
+ ```
47
+
48
+ Load and render it:
49
+
50
+ ```typescript
51
+ import { createPromptFromSource, render } from 'pupt-lib';
52
+ import { readFile } from 'fs/promises';
53
+
54
+ const source = await readFile('./greeting.prompt', 'utf-8');
55
+ const element = await createPromptFromSource(source, 'greeting.prompt');
56
+ const result = await render(element, {
57
+ inputs: { userName: 'Alice' },
58
+ });
59
+
60
+ console.log(result.text);
61
+ ```
62
+
63
+ ## Quick Start with `.tsx`
64
+
65
+ The same prompt as a TypeScript JSX file — explicit imports, full type safety:
35
66
 
36
67
  ```tsx
68
+ // greeting.tsx
69
+ import { Prompt, Role, Task, Constraint, Ask } from 'pupt-lib';
70
+
37
71
  export default (
38
72
  <Prompt name="greeting" description="A friendly greeting prompt">
39
73
  <Role>You are a friendly assistant.</Role>
@@ -45,318 +79,317 @@ export default (
45
79
  );
46
80
  ```
47
81
 
48
- Render the prompt:
82
+ Load with `createPrompt` (reads the file for you):
49
83
 
50
84
  ```typescript
51
- import { createPrompt, render, createInputIterator } from 'pupt-lib';
85
+ import { createPrompt, render } from 'pupt-lib';
52
86
 
53
- // Load the prompt
54
87
  const element = await createPrompt('./greeting.tsx');
88
+ const result = await render(element, {
89
+ inputs: { userName: 'Alice' },
90
+ });
91
+
92
+ console.log(result.text);
93
+ ```
94
+
95
+ ## Collecting User Inputs
96
+
97
+ When prompts contain `<Ask.*>` components, use `createInputIterator` to collect values interactively:
98
+
99
+ ```typescript
100
+ import { createPrompt, createInputIterator, render } from 'pupt-lib';
55
101
 
56
- // Collect inputs
102
+ const element = await createPrompt('./my-prompt.tsx');
57
103
  const iterator = createInputIterator(element);
58
- iterator.start();
104
+
105
+ // Start iteration
106
+ await iterator.start();
107
+
108
+ // Loop through each input requirement
59
109
  while (!iterator.isDone()) {
60
110
  const req = iterator.current();
61
- const answer = await askUser(req.label); // Your UI logic
62
- await iterator.submit(answer);
63
- iterator.advance();
111
+ console.log(`${req.label} (${req.type}, required: ${req.required})`);
112
+
113
+ const answer = await askUser(req); // your UI logic
114
+ const validation = await iterator.submit(answer);
115
+
116
+ if (!validation.valid) {
117
+ console.log('Errors:', validation.errors.map(e => e.message));
118
+ continue; // re-prompt for same input
119
+ }
120
+
121
+ await iterator.advance();
64
122
  }
65
123
 
66
- // Render to text
67
- const result = render(element, { inputs: iterator.getValues() });
68
- console.log(result.text);
124
+ // Render with collected values
125
+ const result = await render(element, { inputs: iterator.getValues() });
69
126
  ```
70
127
 
71
- Or render directly with pre-supplied values:
128
+ Or use non-interactive mode to auto-fill defaults:
72
129
 
73
130
  ```typescript
74
- import { createPrompt, render } from 'pupt-lib';
75
-
76
- const element = await createPrompt('./greeting.tsx');
77
- const result = render(element, {
78
- inputs: { userName: 'Alice' }
79
- });
80
- console.log(result.text);
131
+ const iterator = createInputIterator(element, { nonInteractive: true });
132
+ const values = await iterator.runNonInteractive();
133
+ const result = await render(element, { inputs: values });
81
134
  ```
82
135
 
83
- ## Components
84
-
85
- ### Structural Components
86
-
87
- | Component | Description |
88
- |-----------|-------------|
89
- | `<Prompt>` | Root container for a complete prompt. Props: `name`, `description`, `tags`, `version` |
90
- | `<Section>` | Creates a labeled section with optional XML delimiters |
91
- | `<Role>` | Defines the AI persona/role. Props: `expertise`, `domain` |
92
- | `<Task>` | Clearly states what the AI should do |
93
- | `<Context>` | Provides background information |
94
- | `<Constraint>` | Adds boundaries/rules. Props: `type` (`must`, `should`, `must-not`) |
95
- | `<Format>` | Specifies output format. Props: `type` (`json`, `markdown`, `code`, `list`), `schema` |
96
- | `<Audience>` | Who the output is for. Props: `expertise`, `domain` |
97
- | `<Tone>` | Emotional quality of output. Props: `type`, `formality` |
98
- | `<SuccessCriteria>` | Container for success criteria |
99
- | `<Criterion>` | Individual success criterion. Props: `priority` |
100
-
101
- ### Example Components
102
-
103
- | Component | Description |
104
- |-----------|-------------|
105
- | `<Examples>` | Container for multiple examples |
106
- | `<Example>` | Single example with input/output |
107
- | `<ExampleInput>` | Input portion of an example |
108
- | `<ExampleOutput>` | Expected output of an example |
109
-
110
- ### Reasoning Components
111
-
112
- | Component | Description |
113
- |-----------|-------------|
114
- | `<Steps>` | Encourages step-by-step thinking |
115
- | `<Step>` | Individual step in a reasoning chain. Props: `number` |
116
-
117
- ### Data Components
118
-
119
- | Component | Description |
120
- |-----------|-------------|
121
- | `<Data>` | Embeds data with optional formatting. Props: `name`, `format` |
122
- | `<Code>` | Embeds code with language hint. Props: `language`, `filename` |
123
- | `<File>` | Embeds file contents. Props: `path` |
124
- | `<Json>` | Embeds JSON data. Props: `name` |
125
- | `<Xml>` | Embeds XML data. Props: `name` |
126
-
127
- ### Utility Components
128
-
129
- | Component | Description |
130
- |-----------|-------------|
131
- | `<UUID>` | Generates a unique identifier |
132
- | `<Timestamp>` | Current Unix timestamp |
133
- | `<DateTime>` | Current date/time. Props: `format` |
134
- | `<Hostname>` | Current machine hostname |
135
- | `<Username>` | Current system username |
136
- | `<Cwd>` | Current working directory |
137
-
138
- ### Control Flow Components
139
-
140
- | Component | Description |
141
- |-----------|-------------|
142
- | `<If>` | Conditional rendering. Props: `when` (Excel formula or JS expression) |
143
- | `<ForEach>` | Loop over items. Props: `items`, `as` |
144
- | `<Scope>` | Resolve components from a specific library. Props: `from` |
145
-
146
- ### User Input Components (Ask.*)
147
-
148
- | Component | Description |
149
- |-----------|-------------|
150
- | `<Ask.Text>` | Single-line text input. Props: `name`, `label`, `required`, `default`, `pattern` |
151
- | `<Ask.Editor>` | Multi-line text editor. Props: `name`, `label`, `language` |
152
- | `<Ask.Select>` | Single choice from options. Props: `name`, `label`, `options` or `<Option>` children |
153
- | `<Ask.MultiSelect>` | Multiple choices. Props: `name`, `label`, `options` or `<Option>` children |
154
- | `<Ask.Confirm>` | Yes/no question. Props: `name`, `label`, `default` |
155
- | `<Ask.Choice>` | Binary choice with custom labels. Props: `name`, `label`, `options` |
156
- | `<Ask.Number>` | Numeric input. Props: `name`, `label`, `min`, `max`, `default` |
157
- | `<Ask.File>` | File path input. Props: `name`, `label`, `mustExist` |
158
- | `<Ask.Path>` | Directory path input. Props: `name`, `label`, `mustBeDirectory` |
159
- | `<Ask.Date>` | Date selection. Props: `name`, `label` |
160
- | `<Ask.Secret>` | Masked input for sensitive values. Props: `name`, `label`, `validator` |
161
- | `<Ask.Rating>` | Numeric scale input. Props: `name`, `label`, `min`, `max`, `labels` or `<Label>` children |
162
- | `<Ask.ReviewFile>` | File input with post-execution review. Props: `name`, `label` |
163
- | `<Option>` | Option for Select/MultiSelect. Props: `value`, `label`, children (render text) |
164
- | `<Label>` | Label for Rating scale. Props: `value`, children (label text) |
165
-
166
- ### Meta Components
167
-
168
- | Component | Description |
169
- |-----------|-------------|
170
- | `<Uses>` | Declares module dependencies. Props: `src`, `optional` |
171
-
172
- ### Post-Execution Components
173
-
174
- | Component | Description |
175
- |-----------|-------------|
176
- | `<PostExecution>` | Container for post-execution actions |
177
- | `<ReviewFile>` | Open a file for review after prompt execution. Props: `file`, `editor` |
178
- | `<OpenUrl>` | Open a URL in browser. Props: `url`, `browser` |
179
- | `<RunCommand>` | Execute a shell command. Props: `command`, `cwd` |
180
-
181
- ## API Summary
182
-
183
- | Export | Type | Description |
184
- |--------|------|-------------|
185
- | `render(element, options?)` | Function | Render a PuptElement tree to text |
186
- | `createPrompt(filePath)` | Function | Create a prompt from a JSX/TSX file |
187
- | `createPromptFromSource(source, filename)` | Function | Create a prompt from source string |
188
- | `createInputIterator(element)` | Function | Create iterator for collecting user inputs |
189
- | `Component<Props>` | Class | Base class for custom components |
190
- | `Pupt` | Class | High-level API for loading and managing prompt libraries |
191
- | `createSearchEngine(config?)` | Function | Create fuzzy search engine for prompts |
192
- | `createRegistry()` | Function | Create a new component registry |
193
- | `defaultRegistry` | Const | The default global component registry |
194
-
195
- ## Detailed API Usage
196
-
197
- ### Rendering Prompts
136
+ ## Working with Results
137
+
138
+ `render()` returns a `RenderResult` discriminated union:
198
139
 
199
140
  ```typescript
200
- import { render, createPromptFromSource } from 'pupt-lib';
141
+ const result = await render(element, { inputs: { userName: 'Alice' } });
201
142
 
202
- // From source string
203
- const element = await createPromptFromSource(`
204
- <Prompt name="example">
205
- <Role>You are a helpful assistant.</Role>
206
- <Task>Help the user with their question.</Task>
207
- </Prompt>
208
- `, 'example.tsx');
143
+ if (result.ok) {
144
+ console.log(result.text); // The rendered prompt string
145
+ console.log(result.postExecution); // Post-execution actions (if any)
146
+ if (result.errors) {
147
+ console.log('Warnings:', result.errors); // Non-fatal validation warnings
148
+ }
149
+ } else {
150
+ console.log('Errors:', result.errors); // Validation/runtime errors
151
+ console.log(result.text); // Best-effort partial output
152
+ }
153
+ ```
154
+
155
+ ## `.prompt` vs `.tsx`
156
+
157
+ | Feature | `.prompt` | `.tsx` |
158
+ |---------|-----------|--------|
159
+ | Imports | Auto-injected for all built-in components | Explicit `import` statements |
160
+ | Exports | Auto-wrapped with `export default` | Explicit `export default` |
161
+ | Custom components | Via `<Uses>` declarations | Standard `import` |
162
+ | Component definitions | Not supported | Define components in same file |
163
+ | Target audience | Non-technical users, simple prompts | Developers, complex prompts |
164
+ | Behavior | Identical after preprocessing | Identical after preprocessing |
165
+
166
+ Both formats go through the same transformation pipeline and produce identical output.
167
+
168
+ ## Component Overview
169
+
170
+ pupt-lib includes 50+ built-in components organized by category:
171
+
172
+ | Category | Count | Key Components |
173
+ |----------|-------|----------------|
174
+ | **Structural** | 24 | `Prompt`, `Role`, `Task`, `Context`, `Constraint`, `Format`, `Audience`, `Tone` |
175
+ | **Ask (User Input)** | 15 | `Ask.Text`, `Ask.Number`, `Ask.Select`, `Ask.Confirm`, `Ask.MultiSelect` |
176
+ | **Data** | 5 | `Code`, `Data`, `File`, `Json`, `Xml` |
177
+ | **Examples** | 5 | `Examples`, `Example`, `ExampleInput`, `ExampleOutput`, `NegativeExample` |
178
+ | **Reasoning** | 3 | `Steps`, `Step`, `ChainOfThought` |
179
+ | **Control Flow** | 2 | `If`, `ForEach` |
180
+ | **Post-Execution** | 4 | `PostExecution`, `ReviewFile`, `OpenUrl`, `RunCommand` |
181
+ | **Utility** | 6 | `UUID`, `Timestamp`, `DateTime`, `Hostname`, `Username`, `Cwd` |
182
+ | **Meta** | 1 | `Uses` |
209
183
 
210
- // Render with options
211
- const result = render(element, {
212
- inputs: new Map([['userName', 'Alice']]), // or { userName: 'Alice' }
213
- trim: true, // Trim whitespace (default: true)
184
+ See [docs/COMPONENTS.md](docs/COMPONENTS.md) for the full reference with all props.
185
+
186
+ ## Environment & Providers
187
+
188
+ Components adapt their output based on the target LLM provider. Set the provider via the `env` option:
189
+
190
+ ```typescript
191
+ import { render, createEnvironment } from 'pupt-lib';
192
+
193
+ // Target Anthropic Claude
194
+ const result = await render(element, {
195
+ inputs: { ... },
196
+ env: createEnvironment({
197
+ llm: { provider: 'anthropic' },
198
+ }),
214
199
  });
215
200
 
216
- console.log(result.text); // The rendered prompt text
217
- console.log(result.postExecution); // Array of post-execution actions
201
+ // Target OpenAI GPT (uses markdown delimiters instead of XML)
202
+ const result2 = await render(element, {
203
+ inputs: { ... },
204
+ env: createEnvironment({
205
+ llm: { provider: 'openai' },
206
+ }),
207
+ });
218
208
  ```
219
209
 
220
- ### Collecting User Inputs
210
+ Supported providers: `anthropic`, `openai`, `google`, `meta`, `mistral`, `deepseek`, `xai`, `cohere`. Provider can also be auto-inferred from model name:
221
211
 
222
212
  ```typescript
223
- import { createPrompt, createInputIterator, render } from 'pupt-lib';
213
+ env: createEnvironment({
214
+ llm: { model: 'claude-sonnet-4-5-20250929' }, // auto-infers provider: 'anthropic'
215
+ })
216
+ ```
224
217
 
225
- const element = await createPrompt('./my-prompt.tsx');
226
- const iterator = createInputIterator(element);
218
+ Each provider has adaptations for role prefix style, constraint framing, format preference, and instruction style.
227
219
 
228
- // Start iteration
229
- iterator.start();
220
+ ## Presets
230
221
 
231
- // Iterate through each input requirement
232
- while (!iterator.isDone()) {
233
- const req = iterator.current();
222
+ Many components accept a `preset` prop that loads pre-configured settings:
223
+
224
+ ```tsx
225
+ <Role preset="engineer" />
226
+ // Renders: "You are a senior Software Engineer with expertise in software development, programming, system design."
234
227
 
235
- console.log(`Name: ${req.name}`);
236
- console.log(`Type: ${req.type}`);
237
- console.log(`Label: ${req.label}`);
238
- console.log(`Required: ${req.required}`);
228
+ <Steps preset="debugging" />
229
+ // Renders step-by-step phases: Reproduce → Isolate → Fix → Verify
239
230
 
240
- // Collect input from user (your UI logic)
241
- const answer = await promptUser(req);
231
+ <Constraint preset="cite-sources" />
232
+ // Renders: "Cite sources for factual claims" with type "must"
233
+ ```
242
234
 
243
- // Submit and validate
244
- const result = await iterator.submit(answer);
245
- if (!result.valid) {
246
- console.log(`Validation error: ${result.errors.map(e => e.message).join(', ')}`);
247
- continue; // Re-prompt for same input
248
- }
235
+ Available preset categories:
249
236
 
250
- iterator.advance();
251
- }
237
+ | Preset | Keys | Used by |
238
+ |--------|------|---------|
239
+ | **Role** | `assistant`, `engineer`, `writer`, `analyst`, `teacher`, +25 more | `<Role>` |
240
+ | **Task** | `summarize`, `code-review`, `translate`, `explain`, `generate-code`, +5 more | `<Task>` |
241
+ | **Constraint** | `be-concise`, `cite-sources`, `no-opinions`, `no-hallucination`, +4 more | `<Constraint>` |
242
+ | **Steps** | `analysis`, `problem-solving`, `code-generation`, `debugging`, `research` | `<Steps>` |
243
+ | **Guardrails** | `standard`, `strict`, `minimal` | `<Guardrails>` |
252
244
 
253
- // Render with collected values
254
- const result = render(element, { inputs: iterator.getValues() });
245
+ See [docs/COMPONENTS.md](docs/COMPONENTS.md) for the full list of preset keys.
246
+
247
+ ## Prompt Defaults
248
+
249
+ `<Prompt>` auto-generates Role, Format, and Constraint sections when you don't provide them. This means a minimal prompt:
250
+
251
+ ```xml
252
+ <Prompt name="helper">
253
+ <Task>Help the user with their question.</Task>
254
+ </Prompt>
255
+ ```
256
+
257
+ ...automatically includes a default role ("You are a helpful Assistant"), format guidance, and basic constraints.
258
+
259
+ **Controlling defaults:**
260
+
261
+ ```tsx
262
+ // Disable all defaults
263
+ <Prompt name="bare-prompt" bare>
264
+ <Task>Just the task, nothing else.</Task>
265
+ </Prompt>
266
+
267
+ // Disable specific defaults
268
+ <Prompt name="custom" noRole noFormat>
269
+ <Task>Only constraints are auto-generated.</Task>
270
+ </Prompt>
271
+
272
+ // Fine-grained control
273
+ <Prompt name="custom" defaults={{ role: true, format: false, constraints: true }}>
274
+ <Task>Role and constraints, but no format section.</Task>
275
+ </Prompt>
276
+
277
+ // Shorthand: customize the default role
278
+ <Prompt name="expert" role="engineer" expertise="TypeScript">
279
+ <Task>Review this code.</Task>
280
+ </Prompt>
281
+ ```
282
+
283
+ **Slots** let you replace default sections with custom components:
284
+
285
+ ```tsx
286
+ <Prompt name="custom" slots={{ role: MyCustomRole }}>
287
+ <Task>Uses MyCustomRole instead of the default role.</Task>
288
+ </Prompt>
255
289
  ```
256
290
 
257
- ### Creating Custom Components
291
+ ## Creating Custom Components
292
+
293
+ Extend `Component` and implement `render(props, resolvedValue, context)`:
258
294
 
259
295
  ```typescript
260
- import { Component, RenderContext, PuptNode } from 'pupt-lib';
296
+ import { Component } from 'pupt-lib';
297
+ import type { PuptNode, RenderContext } from 'pupt-lib';
298
+ import { z } from 'zod';
261
299
 
262
- interface WarningProps {
263
- level: 'info' | 'warning' | 'error';
264
- children?: PuptNode;
265
- }
300
+ const warningSchema = z.object({
301
+ level: z.enum(['info', 'warning', 'error']),
302
+ });
303
+
304
+ type WarningProps = z.infer<typeof warningSchema> & { children?: PuptNode };
266
305
 
267
306
  class Warning extends Component<WarningProps> {
268
- render(props: WarningProps, context: RenderContext): PuptNode {
307
+ static schema = warningSchema;
308
+
309
+ render(props: WarningProps, _resolvedValue: void, context: RenderContext): PuptNode {
269
310
  const prefix = {
270
311
  info: 'INFO',
271
312
  warning: 'WARNING',
272
- error: 'ERROR'
313
+ error: 'ERROR',
273
314
  }[props.level];
274
315
 
275
316
  return `[${prefix}] ${props.children}`;
276
317
  }
277
318
  }
278
-
279
- // Register with the default registry
280
- import { defaultRegistry } from 'pupt-lib';
281
- defaultRegistry.register('Warning', Warning);
282
319
  ```
283
320
 
284
- ### Using the Pupt Class
321
+ Use in `.prompt` files with `<Uses>`:
285
322
 
286
- ```typescript
287
- import { Pupt } from 'pupt-lib';
288
-
289
- // Initialize with module sources
290
- const pupt = new Pupt({
291
- modules: [
292
- '@acme/prompts', // npm package
293
- 'https://example.com/prompts.js', // URL
294
- './local-prompts/', // local path
295
- ],
296
- });
323
+ ```xml
324
+ <Uses component="Warning" from="./my-components" />
297
325
 
298
- await pupt.init();
299
-
300
- // List all prompts
301
- const prompts = pupt.getPrompts();
302
- console.log(prompts.map(p => p.name));
303
-
304
- // Filter by tags
305
- const devPrompts = pupt.getPrompts({ tags: ['development'] });
326
+ <Prompt name="example">
327
+ <Warning level="info">This is a custom component.</Warning>
328
+ </Prompt>
329
+ ```
306
330
 
307
- // Search prompts
308
- const results = pupt.searchPrompts('code review');
331
+ The `Component` base class provides helpers:
332
+ - `getProvider(context)` Get the current LLM provider
333
+ - `getDelimiter(context)` — Get the delimiter style (`xml`, `markdown`, or `none`)
334
+ - `hasContent(children)` — Check if children have meaningful content
309
335
 
310
- // Get a specific prompt
311
- const prompt = pupt.getPrompt('security-review');
312
- if (prompt) {
313
- const result = prompt.render({ inputs: { code: sourceCode } });
314
- console.log(result.text);
315
- }
336
+ ## Advanced Features
316
337
 
317
- // Get all unique tags
318
- const tags = pupt.getTags();
319
- ```
338
+ **Conditional rendering** with `<If>`:
320
339
 
321
- ### Conditional Rendering
340
+ ```tsx
341
+ // Boolean condition
342
+ <If when={isAdmin}>
343
+ <Task>Perform admin operations.</Task>
344
+ </If>
322
345
 
323
- ```typescript
324
- // Excel formula syntax (accessible to non-technical users)
346
+ // Excel-style formula (evaluated against input values)
325
347
  <If when='=AND(count>5, userType="admin")'>
326
348
  <Ask.Text name="adminCode" label="Admin authorization code" />
327
349
  </If>
328
350
 
329
- // JavaScript expression (for power users)
330
- <If when={items.filter(i => i.priority > 3).length >= 2}>
331
- <Task>Handle high-priority items first</Task>
351
+ // Provider-specific content
352
+ <If provider="anthropic">
353
+ <Context>Use XML tags for structured output.</Context>
354
+ </If>
355
+ <If notProvider="anthropic">
356
+ <Context>Use markdown headers for structured output.</Context>
332
357
  </If>
333
358
  ```
334
359
 
335
- ### Post-Execution Actions
360
+ **Iteration** with `<ForEach>`:
336
361
 
337
- ```typescript
338
- import { createPrompt, render } from 'pupt-lib';
362
+ ```tsx
363
+ <ForEach items={['bug fix', 'feature', 'refactor']} as="type">
364
+ <Step>Handle the {type} case.</Step>
365
+ </ForEach>
366
+ ```
339
367
 
340
- const element = await createPrompt('./generate-code.tsx');
341
- const result = render(element, { inputs: { ... } });
342
-
343
- // Handle post-execution actions
344
- for (const action of result.postExecution) {
345
- switch (action.type) {
346
- case 'reviewFile':
347
- openInEditor(action.file, action.editor);
348
- break;
349
- case 'openUrl':
350
- openBrowser(action.url);
351
- break;
352
- case 'runCommand':
353
- exec(action.command, { cwd: action.cwd });
354
- break;
355
- }
356
- }
368
+ **Container composition** with `<Constraints>` and `<Contexts>`:
369
+
370
+ ```tsx
371
+ // Extend default constraints with additional ones
372
+ <Constraints extend>
373
+ <Constraint type="must">Always include code examples.</Constraint>
374
+ </Constraints>
375
+
376
+ // Replace defaults entirely
377
+ <Constraints>
378
+ <Constraint type="must">Only respond in JSON.</Constraint>
379
+ </Constraints>
357
380
  ```
358
381
 
359
- ### TypeScript Configuration
382
+ **Post-execution actions** — actions to perform after the LLM responds:
383
+
384
+ ```tsx
385
+ <PostExecution>
386
+ <ReviewFile file="output.ts" />
387
+ <OpenUrl url="https://docs.example.com" />
388
+ <RunCommand command="npm test" />
389
+ </PostExecution>
390
+ ```
391
+
392
+ ## TypeScript Configuration
360
393
 
361
394
  For build-time JSX transformation, configure `tsconfig.json`:
362
395
 
@@ -374,12 +407,15 @@ Or use the Babel preset for runtime transformation:
374
407
  ```javascript
375
408
  // babel.config.js
376
409
  module.exports = {
377
- presets: [
378
- 'pupt-lib/babel-preset'
379
- ]
410
+ presets: ['pupt-lib/babel-preset'],
380
411
  };
381
412
  ```
382
413
 
414
+ ## Reference Documentation
415
+
416
+ - **[docs/COMPONENTS.md](docs/COMPONENTS.md)** — Full component reference (all 50+ components with all props)
417
+ - **[docs/API.md](docs/API.md)** — Full API reference (functions, types, base class, utilities)
418
+
383
419
  ## License
384
420
 
385
421
  MIT