@riotprompt/riotprompt 0.0.12 → 0.0.14

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 (61) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +136 -33
  3. package/dist/builder.js +3 -0
  4. package/dist/builder.js.map +1 -1
  5. package/dist/chat.d.ts +2 -0
  6. package/dist/chat.js +2 -0
  7. package/dist/chat.js.map +1 -1
  8. package/dist/cli.cjs +1617 -0
  9. package/dist/cli.d.ts +8 -0
  10. package/dist/config.d.ts +7 -0
  11. package/dist/conversation-logger.js +6 -1
  12. package/dist/conversation-logger.js.map +1 -1
  13. package/dist/execution/anthropic.d.ts +5 -0
  14. package/dist/execution/anthropic.js +70 -0
  15. package/dist/execution/anthropic.js.map +1 -0
  16. package/dist/execution/gemini.d.ts +5 -0
  17. package/dist/execution/gemini.js +122 -0
  18. package/dist/execution/gemini.js.map +1 -0
  19. package/dist/execution/index.d.ts +10 -0
  20. package/dist/execution/index.js +53 -0
  21. package/dist/execution/index.js.map +1 -0
  22. package/dist/execution/openai.d.ts +5 -0
  23. package/dist/execution/openai.js +45 -0
  24. package/dist/execution/openai.js.map +1 -0
  25. package/dist/execution/provider.d.ts +18 -0
  26. package/dist/formatter.js +42 -14
  27. package/dist/formatter.js.map +1 -1
  28. package/dist/loader.js +3 -0
  29. package/dist/loader.js.map +1 -1
  30. package/dist/model-config.d.ts +1 -0
  31. package/dist/model-config.js +19 -18
  32. package/dist/model-config.js.map +1 -1
  33. package/dist/override.js +3 -0
  34. package/dist/override.js.map +1 -1
  35. package/dist/prompt.d.ts +19 -1
  36. package/dist/prompt.js +11 -2
  37. package/dist/prompt.js.map +1 -1
  38. package/dist/recipes.d.ts +108 -0
  39. package/dist/recipes.js +198 -30
  40. package/dist/recipes.js.map +1 -1
  41. package/dist/riotprompt.cjs +930 -123
  42. package/dist/riotprompt.cjs.map +1 -1
  43. package/dist/riotprompt.d.ts +3 -0
  44. package/dist/riotprompt.js +6 -0
  45. package/dist/riotprompt.js.map +1 -1
  46. package/dist/serializer.d.ts +5 -0
  47. package/dist/serializer.js +220 -0
  48. package/dist/serializer.js.map +1 -0
  49. package/dist/writer.d.ts +2 -0
  50. package/dist/writer.js +91 -0
  51. package/dist/writer.js.map +1 -0
  52. package/guide/architecture.md +51 -0
  53. package/guide/configuration.md +51 -0
  54. package/guide/development.md +62 -0
  55. package/guide/index.md +55 -0
  56. package/guide/usage.md +99 -0
  57. package/package.json +15 -3
  58. package/vite.config.cli.ts +49 -0
  59. package/BUG-ANALYSIS.md +0 -523
  60. package/CODE-REVIEW-SUMMARY.md +0 -330
  61. package/FIXES-APPLIED.md +0 -437
@@ -22,6 +22,9 @@ export { ConversationLogger, ConversationReplayer } from './conversation-logger'
22
22
  export { ToolRegistry } from './tools';
23
23
  export { StrategyExecutor, IterationStrategyFactory } from './iteration-strategy';
24
24
  export { MetricsCollector, ReflectionReportGenerator } from './reflection';
25
+ export * as Serializer from './serializer';
26
+ export * as Writer from './writer';
27
+ export * as Execution from './execution/index';
25
28
  export { ModelRegistry, getModelRegistry, resetModelRegistry, getPersonaRole, getEncoding, supportsToolCalls, getModelFamily, configureModel } from './model-config';
26
29
  export type { Content } from './items/content';
27
30
  export type { Context } from './items/context';
@@ -29,5 +29,11 @@ export { ConversationLogger, ConversationReplayer } from './conversation-logger.
29
29
  export { ToolRegistry } from './tools.js';
30
30
  export { IterationStrategyFactory, StrategyExecutor } from './iteration-strategy.js';
31
31
  export { MetricsCollector, ReflectionReportGenerator } from './reflection.js';
32
+ import * as serializer from './serializer.js';
33
+ export { serializer as Serializer };
34
+ import * as writer from './writer.js';
35
+ export { writer as Writer };
36
+ import * as index from './execution/index.js';
37
+ export { index as Execution };
32
38
  export { ModelRegistry, configureModel, getEncoding, getModelFamily, getModelRegistry, getPersonaRole, resetModelRegistry, supportsToolCalls } from './model-config.js';
33
39
  //# sourceMappingURL=riotprompt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"riotprompt.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"riotprompt.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,5 @@
1
+ import { Prompt } from './prompt';
2
+ export declare const toJSON: (prompt: Prompt) => string;
3
+ export declare const toXML: (prompt: Prompt) => string;
4
+ export declare const fromJSON: (jsonString: string) => Prompt;
5
+ export declare const fromXML: (xmlString: string) => Prompt;
@@ -0,0 +1,220 @@
1
+ import { XMLParser } from 'fast-xml-parser';
2
+ import { create as create$1 } from './prompt.js';
3
+ import { create } from './items/section.js';
4
+
5
+ const toJSON = (prompt)=>{
6
+ return JSON.stringify(prompt, null, 2);
7
+ };
8
+ const escapeXML = (str)=>{
9
+ return str.replace(/[<>&'"]/g, (c)=>{
10
+ switch(c){
11
+ case '<':
12
+ return '&lt;';
13
+ case '>':
14
+ return '&gt;';
15
+ case '&':
16
+ return '&amp;';
17
+ case '\'':
18
+ return '&apos;';
19
+ case '"':
20
+ return '&quot;';
21
+ default:
22
+ return c;
23
+ }
24
+ });
25
+ };
26
+ const itemToXML = (item)=>{
27
+ if (typeof item === 'string') {
28
+ return `<item>${escapeXML(item)}</item>`;
29
+ }
30
+ // Check if it's a section
31
+ if (item && typeof item === 'object' && 'items' in item) {
32
+ return sectionToXML(item);
33
+ }
34
+ // Check if it's a weighted item
35
+ if (item && typeof item === 'object' && 'text' in item) {
36
+ const weightAttr = item.weight !== undefined && item.weight !== null ? ` weight="${item.weight}"` : '';
37
+ return `<item${weightAttr}>${escapeXML(item.text)}</item>`;
38
+ }
39
+ return '';
40
+ };
41
+ const sectionToXML = (section, tagName = 'section')=>{
42
+ const titleAttr = section.title ? ` title="${escapeXML(section.title)}"` : '';
43
+ const weightAttr = section.weight ? ` weight="${section.weight}"` : '';
44
+ let xml = `<${tagName}${titleAttr}${weightAttr}>`;
45
+ if (section.items && Array.isArray(section.items)) {
46
+ xml += section.items.map((item)=>itemToXML(item)).join('');
47
+ }
48
+ xml += `</${tagName}>`;
49
+ return xml;
50
+ };
51
+ const toXML = (prompt)=>{
52
+ let xml = '<?xml version="1.0" encoding="UTF-8"?>\n<prompt>';
53
+ if (prompt.persona) {
54
+ xml += sectionToXML(prompt.persona, 'persona');
55
+ }
56
+ if (prompt.instructions) {
57
+ xml += sectionToXML(prompt.instructions, 'instructions');
58
+ }
59
+ if (prompt.contents) {
60
+ xml += sectionToXML(prompt.contents, 'contents');
61
+ }
62
+ if (prompt.contexts) {
63
+ xml += sectionToXML(prompt.contexts, 'contexts');
64
+ }
65
+ xml += '</prompt>';
66
+ return xml;
67
+ };
68
+ // ============================================================================
69
+ // DESERIALIZATION
70
+ // ============================================================================
71
+ // --- JSON Parsing ---
72
+ const parseSectionFromJSON = (jsonSection)=>{
73
+ if (!jsonSection || !jsonSection.items) {
74
+ throw new Error("Invalid section structure");
75
+ }
76
+ const section = create({
77
+ title: jsonSection.title,
78
+ weight: jsonSection.weight
79
+ });
80
+ for (const item of jsonSection.items){
81
+ if (typeof item === 'object' && 'items' in item) {
82
+ // It's a nested section
83
+ section.add(parseSectionFromJSON(item));
84
+ } else if (typeof item === 'object' && 'text' in item) {
85
+ section.add({
86
+ text: item.text,
87
+ weight: item.weight
88
+ });
89
+ } else if (typeof item === 'string') {
90
+ section.add(item);
91
+ }
92
+ }
93
+ return section;
94
+ };
95
+ const fromJSON = (jsonString)=>{
96
+ const json = JSON.parse(jsonString);
97
+ // We treat the root json object as matching Prompt interface
98
+ // But we need to convert plain objects back to Section instances with methods
99
+ const persona = json.persona ? parseSectionFromJSON(json.persona) : undefined;
100
+ const instructions = json.instructions ? parseSectionFromJSON(json.instructions) : create({
101
+ title: 'Instructions'
102
+ });
103
+ const contents = json.contents ? parseSectionFromJSON(json.contents) : undefined;
104
+ const contexts = json.contexts ? parseSectionFromJSON(json.contexts) : undefined;
105
+ return create$1({
106
+ persona,
107
+ instructions,
108
+ contents,
109
+ contexts
110
+ });
111
+ };
112
+ // --- XML Parsing ---
113
+ const parseNodeToSection = (node)=>{
114
+ // Node structure with preserveOrder: true
115
+ // It seems attributes can be a sibling property ":@" OR inside the children array depending on version/config/content.
116
+ // Children are in the array value of the key "section", "persona", etc.
117
+ const children = node.section || node.persona || node.instructions || node.contents || node.contexts || [];
118
+ let attributes = node[":@"] || {};
119
+ // Fallback: check if attributes are inside the children array (as seen in some tests/mocks)
120
+ if (!node[":@"] && Array.isArray(children)) {
121
+ for (const child of children){
122
+ if (child[":@"]) {
123
+ attributes = child[":@"];
124
+ break;
125
+ }
126
+ }
127
+ }
128
+ const title = attributes["@_title"];
129
+ const weight = attributes["@_weight"] ? Number(attributes["@_weight"]) : undefined;
130
+ const section = create({
131
+ title,
132
+ weight
133
+ });
134
+ if (Array.isArray(children)) {
135
+ for (const child of children){
136
+ const key = Object.keys(child)[0]; // "item" or "section" or ":@"
137
+ // console.log(`Processing child key: ${key}`);
138
+ if (key === ":@") continue; // Already handled or just attributes
139
+ if (key === "item") {
140
+ // Item structure: [ { "#text": "Value" }, { ":@": ... } ]
141
+ const itemContent = child.item;
142
+ let text = "";
143
+ let itemWeight = undefined;
144
+ for (const part of itemContent){
145
+ const keys = Object.keys(part);
146
+ // console.log('Processing item part keys:', keys);
147
+ if (keys.includes("#text")) {
148
+ text = part["#text"];
149
+ } else if (keys.includes(":@")) {
150
+ const attrs = part[":@"];
151
+ // console.log('Found attributes:', attrs);
152
+ // Check both with and without prefix just in case
153
+ const w = attrs["@_weight"] || attrs["weight"];
154
+ if (w !== undefined) itemWeight = Number(w);
155
+ } else {
156
+ // Fallback for cases where attributes might be directly on the part (unexpected but possible)
157
+ const w = part["@_weight"] || part["weight"];
158
+ if (w !== undefined) itemWeight = Number(w);
159
+ }
160
+ }
161
+ // console.log(`Adding item: ${text}`);
162
+ section.add({
163
+ text,
164
+ weight: itemWeight
165
+ });
166
+ } else if (key === "section") {
167
+ section.add(parseNodeToSection(child));
168
+ }
169
+ }
170
+ }
171
+ return section;
172
+ };
173
+ const fromXML = (xmlString)=>{
174
+ const parser = new XMLParser({
175
+ ignoreAttributes: false,
176
+ attributeNamePrefix: "@_",
177
+ preserveOrder: true,
178
+ trimValues: true
179
+ });
180
+ const parsed = parser.parse(xmlString);
181
+ // parsed is [ { "?xml": ... }, { "prompt": [ ... ] } ]
182
+ let promptNode = null;
183
+ for (const node of parsed){
184
+ if (node.prompt) {
185
+ promptNode = node.prompt;
186
+ break;
187
+ }
188
+ }
189
+ if (!promptNode) throw new Error("Invalid XML: missing <prompt> root");
190
+ let persona;
191
+ let instructions = create({
192
+ title: "Instructions"
193
+ });
194
+ let contents;
195
+ let contexts;
196
+ for (const child of promptNode){
197
+ if (child.persona) {
198
+ persona = parseNodeToSection(child);
199
+ persona.title = "Persona"; // Force title for standard sections?
200
+ } else if (child.instructions) {
201
+ instructions = parseNodeToSection(child);
202
+ instructions.title = "Instructions";
203
+ } else if (child.contents) {
204
+ contents = parseNodeToSection(child);
205
+ contents.title = "Contents";
206
+ } else if (child.contexts) {
207
+ contexts = parseNodeToSection(child);
208
+ contexts.title = "Contexts";
209
+ }
210
+ }
211
+ return create$1({
212
+ persona,
213
+ instructions,
214
+ contents,
215
+ contexts
216
+ });
217
+ };
218
+
219
+ export { fromJSON, fromXML, toJSON, toXML };
220
+ //# sourceMappingURL=serializer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serializer.js","sources":["../src/serializer.ts"],"sourcesContent":["import { XMLParser } from \"fast-xml-parser\";\nimport { Prompt, create as createPrompt } from \"./prompt\";\nimport { Section, create as createSection } from \"./items/section\";\nimport { Instruction } from \"./items/instruction\";\nimport { Context } from \"./items/context\";\nimport { Content } from \"./items/content\";\nimport { Weighted } from \"./items/weighted\";\n\nexport const toJSON = (prompt: Prompt): string => {\n return JSON.stringify(prompt, null, 2);\n}\n\nconst escapeXML = (str: string): string => {\n return str.replace(/[<>&'\"]/g, (c) => {\n switch (c) {\n case '<': return '&lt;';\n case '>': return '&gt;';\n case '&': return '&amp;';\n case '\\'': return '&apos;';\n case '\"': return '&quot;';\n default: return c;\n }\n });\n}\n\nconst itemToXML = (item: any): string => {\n if (typeof item === 'string') {\n return `<item>${escapeXML(item)}</item>`;\n }\n \n // Check if it's a section\n if (item && typeof item === 'object' && 'items' in item) {\n return sectionToXML(item);\n }\n\n // Check if it's a weighted item\n if (item && typeof item === 'object' && 'text' in item) {\n const weightAttr = (item.weight !== undefined && item.weight !== null) ? ` weight=\"${item.weight}\"` : '';\n return `<item${weightAttr}>${escapeXML(item.text)}</item>`;\n }\n\n return '';\n}\n\nconst sectionToXML = (section: any, tagName: string = 'section'): string => {\n const titleAttr = section.title ? ` title=\"${escapeXML(section.title)}\"` : '';\n const weightAttr = section.weight ? ` weight=\"${section.weight}\"` : '';\n \n let xml = `<${tagName}${titleAttr}${weightAttr}>`;\n \n if (section.items && Array.isArray(section.items)) {\n xml += section.items.map((item: any) => itemToXML(item)).join('');\n }\n \n xml += `</${tagName}>`;\n return xml;\n}\n\nexport const toXML = (prompt: Prompt): string => {\n let xml = '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<prompt>';\n\n if (prompt.persona) {\n xml += sectionToXML(prompt.persona, 'persona');\n }\n\n if (prompt.instructions) {\n xml += sectionToXML(prompt.instructions, 'instructions');\n }\n\n if (prompt.contents) {\n xml += sectionToXML(prompt.contents, 'contents');\n }\n\n if (prompt.contexts) {\n xml += sectionToXML(prompt.contexts, 'contexts');\n }\n\n xml += '</prompt>';\n return xml;\n}\n\n// ============================================================================\n// DESERIALIZATION\n// ============================================================================\n\n// --- JSON Parsing ---\n\nconst parseSectionFromJSON = <T extends Weighted>(jsonSection: any): Section<T> => {\n if (!jsonSection || !jsonSection.items) {\n throw new Error(\"Invalid section structure\");\n }\n\n const section = createSection<T>({\n title: jsonSection.title,\n weight: jsonSection.weight\n });\n\n for (const item of jsonSection.items) {\n if (typeof item === 'object' && 'items' in item) {\n // It's a nested section\n section.add(parseSectionFromJSON<T>(item));\n } else if (typeof item === 'object' && 'text' in item) {\n section.add({\n text: item.text,\n weight: item.weight\n } as T);\n } else if (typeof item === 'string') {\n section.add(item);\n }\n }\n\n return section;\n}\n\nexport const fromJSON = (jsonString: string): Prompt => {\n const json = JSON.parse(jsonString);\n \n // We treat the root json object as matching Prompt interface\n // But we need to convert plain objects back to Section instances with methods\n \n const persona = json.persona ? parseSectionFromJSON<Instruction>(json.persona) : undefined;\n const instructions = json.instructions ? parseSectionFromJSON<Instruction>(json.instructions) : createSection<Instruction>({ title: 'Instructions' });\n \n const contents = json.contents ? parseSectionFromJSON<Content>(json.contents) : undefined;\n const contexts = json.contexts ? parseSectionFromJSON<Context>(json.contexts) : undefined;\n\n return createPrompt({\n persona,\n instructions,\n contents,\n contexts\n });\n}\n\n// --- XML Parsing ---\n\nconst parseNodeToSection = <T extends Weighted>(node: any): Section<T> => {\n // Node structure with preserveOrder: true\n // It seems attributes can be a sibling property \":@\" OR inside the children array depending on version/config/content.\n \n // Children are in the array value of the key \"section\", \"persona\", etc.\n const children = node.section || node.persona || node.instructions || node.contents || node.contexts || [];\n \n let attributes = node[\":@\"] || {};\n \n // Fallback: check if attributes are inside the children array (as seen in some tests/mocks)\n if (!node[\":@\"] && Array.isArray(children)) {\n for (const child of children) {\n if (child[\":@\"]) {\n attributes = child[\":@\"];\n break;\n }\n }\n }\n\n const title = attributes[\"@_title\"];\n const weight = attributes[\"@_weight\"] ? Number(attributes[\"@_weight\"]) : undefined;\n \n const section = createSection<T>({ title, weight });\n \n if (Array.isArray(children)) {\n for (const child of children) {\n const key = Object.keys(child)[0]; // \"item\" or \"section\" or \":@\"\n // console.log(`Processing child key: ${key}`);\n if (key === \":@\") continue; // Already handled or just attributes\n \n if (key === \"item\") {\n // Item structure: [ { \"#text\": \"Value\" }, { \":@\": ... } ]\n const itemContent = child.item;\n let text = \"\";\n let itemWeight = undefined;\n \n for (const part of itemContent) {\n const keys = Object.keys(part);\n // console.log('Processing item part keys:', keys);\n if (keys.includes(\"#text\")) {\n text = part[\"#text\"];\n } else if (keys.includes(\":@\")) {\n const attrs = part[\":@\"];\n // console.log('Found attributes:', attrs);\n // Check both with and without prefix just in case\n const w = attrs[\"@_weight\"] || attrs[\"weight\"];\n if (w !== undefined) itemWeight = Number(w);\n } else {\n // Fallback for cases where attributes might be directly on the part (unexpected but possible)\n const w = part[\"@_weight\"] || part[\"weight\"];\n if (w !== undefined) itemWeight = Number(w);\n }\n }\n // console.log(`Adding item: ${text}`);\n section.add({ text, weight: itemWeight } as T);\n } else if (key === \"section\") {\n section.add(parseNodeToSection<T>(child));\n }\n }\n }\n \n return section;\n}\n\nexport const fromXML = (xmlString: string): Prompt => {\n const parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n preserveOrder: true,\n trimValues: true\n });\n\n const parsed = parser.parse(xmlString);\n // parsed is [ { \"?xml\": ... }, { \"prompt\": [ ... ] } ]\n\n let promptNode = null;\n for (const node of parsed) {\n if (node.prompt) {\n promptNode = node.prompt;\n break;\n }\n }\n\n if (!promptNode) throw new Error(\"Invalid XML: missing <prompt> root\");\n\n let persona: Section<Instruction> | undefined;\n let instructions: Section<Instruction> = createSection({ title: \"Instructions\" });\n let contents: Section<Content> | undefined;\n let contexts: Section<Context> | undefined;\n\n for (const child of promptNode) {\n if (child.persona) {\n persona = parseNodeToSection<Instruction>(child);\n persona.title = \"Persona\"; // Force title for standard sections?\n } else if (child.instructions) {\n instructions = parseNodeToSection<Instruction>(child);\n instructions.title = \"Instructions\";\n } else if (child.contents) {\n contents = parseNodeToSection<Content>(child);\n contents.title = \"Contents\";\n } else if (child.contexts) {\n contexts = parseNodeToSection<Context>(child);\n contexts.title = \"Contexts\";\n }\n }\n\n return createPrompt({\n persona,\n instructions,\n contents,\n contexts\n });\n}\n"],"names":["toJSON","prompt","JSON","stringify","escapeXML","str","replace","c","itemToXML","item","sectionToXML","weightAttr","weight","undefined","text","section","tagName","titleAttr","title","xml","items","Array","isArray","map","join","toXML","persona","instructions","contents","contexts","parseSectionFromJSON","jsonSection","Error","createSection","add","fromJSON","jsonString","json","parse","createPrompt","parseNodeToSection","node","children","attributes","child","Number","key","Object","keys","itemContent","itemWeight","part","includes","attrs","w","fromXML","xmlString","parser","XMLParser","ignoreAttributes","attributeNamePrefix","preserveOrder","trimValues","parsed","promptNode"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Prompt } from './prompt';
2
+ export declare const saveToDirectory: (prompt: Prompt, basePath: string) => Promise<void>;
package/dist/writer.js ADDED
@@ -0,0 +1,91 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
+
4
+ const saveToDirectory = async (prompt, basePath)=>{
5
+ // Ensure base directory exists
6
+ await fs.mkdir(basePath, {
7
+ recursive: true
8
+ });
9
+ // 1. Save Persona
10
+ if (prompt.persona) {
11
+ await saveSection(prompt.persona, path.join(basePath, 'persona'));
12
+ }
13
+ // 2. Save Instructions
14
+ if (prompt.instructions) {
15
+ await saveSection(prompt.instructions, path.join(basePath, 'instructions'));
16
+ }
17
+ // 3. Save Context
18
+ if (prompt.contexts) {
19
+ await saveSection(prompt.contexts, path.join(basePath, 'context'));
20
+ }
21
+ // 4. Save Content (if any)
22
+ if (prompt.contents) {
23
+ await saveSection(prompt.contents, path.join(basePath, 'content'));
24
+ }
25
+ };
26
+ const saveSection = async (section, targetPath)=>{
27
+ // If section has subsections, create a directory and recurse
28
+ // If section only has items, check if we can save as single file (e.g. name of section + .md)
29
+ // Simplification: We will use a mixed approach.
30
+ // If the section is "flat" (only items), we can write it to a file.
31
+ // If the section is "nested" (has subsections), we create a directory.
32
+ // However, to be consistent with Loader logic:
33
+ // Loader reads files in a directory as subsections.
34
+ // So if we have a section "Instructions", and we save it as a directory "instructions":
35
+ // Its items become content of files?
36
+ // Strategy:
37
+ // 1. Create directory `targetPath`.
38
+ // 2. If the section has items (text), verify if they have titles (subsections).
39
+ // 3. For each item:
40
+ // - If it's a string/Instruction/Content/Context (leaf):
41
+ // - If the parent section is the ROOT (e.g. 'persona'), and it's just text, maybe we prefer `persona.md`.
42
+ // - If it's a subsection:
43
+ // - Recurse into subdirectory.
44
+ // Let's refine based on typical usage:
45
+ // - Persona: usually one big text. -> `persona.md`
46
+ // - Instructions: usually one big text or list of files. -> `instructions.md` or `instructions/part1.md`
47
+ // - Context: usually list of files. -> `context/data.json`, `context/info.md`
48
+ // We need to differentiate based on the targetPath provided by caller.
49
+ // If targetPath ends in "persona" (directory name), we can choose to write `targetPath.md` instead if it's flat.
50
+ // But `saveToDirectory` created `basePath/persona` (directory path string).
51
+ // Let's check complexity:
52
+ const hasSubsections = section.items.some((item)=>typeof item === 'object' && 'items' in item);
53
+ if (!hasSubsections) {
54
+ // Flat section.
55
+ // If it has multiple items, we can join them? Or write as numbered files?
56
+ // Usually, a flat section implies content for a single file.
57
+ // We prefer to write to `targetPath.md`.
58
+ const content = section.items.map((item)=>{
59
+ if (typeof item === 'string') return item;
60
+ return item.text;
61
+ }).join('\n\n');
62
+ // Check if we should write to .md file instead of directory
63
+ // targetPath is e.g. ".../persona". We want ".../persona.md".
64
+ await fs.writeFile(`${targetPath}.md`, content);
65
+ return;
66
+ }
67
+ // Nested section
68
+ await fs.mkdir(targetPath, {
69
+ recursive: true
70
+ });
71
+ for(let i = 0; i < section.items.length; i++){
72
+ const item = section.items[i];
73
+ if (typeof item === 'object' && 'items' in item) {
74
+ // Subsection
75
+ // Use title as filename/dirname
76
+ const subTitle = item.title || `part-${i + 1}`;
77
+ const subPath = path.join(targetPath, subTitle);
78
+ await saveSection(item, subPath);
79
+ } else {
80
+ // Leaf item mixed with subsections.
81
+ // Write to a file.
82
+ // If it's just text, we need a filename.
83
+ const fileName = `item-${i + 1}.md`;
84
+ const content = typeof item === 'string' ? item : item.text;
85
+ await fs.writeFile(path.join(targetPath, fileName), content);
86
+ }
87
+ }
88
+ };
89
+
90
+ export { saveToDirectory };
91
+ //# sourceMappingURL=writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writer.js","sources":["../src/writer.ts"],"sourcesContent":["import * as fs from 'fs/promises';\nimport * as path from 'path';\nimport { Prompt } from './prompt';\nimport { Section } from './items/section';\nimport { Weighted } from './items/weighted';\n\nexport const saveToDirectory = async (prompt: Prompt, basePath: string) => {\n // Ensure base directory exists\n await fs.mkdir(basePath, { recursive: true });\n\n // 1. Save Persona\n if (prompt.persona) {\n await saveSection(prompt.persona, path.join(basePath, 'persona'));\n }\n\n // 2. Save Instructions\n if (prompt.instructions) {\n await saveSection(prompt.instructions, path.join(basePath, 'instructions'));\n }\n\n // 3. Save Context\n if (prompt.contexts) {\n await saveSection(prompt.contexts, path.join(basePath, 'context'));\n }\n\n // 4. Save Content (if any)\n if (prompt.contents) {\n await saveSection(prompt.contents, path.join(basePath, 'content'));\n }\n}\n\nconst saveSection = async (section: Section<Weighted>, targetPath: string) => {\n // If section has subsections, create a directory and recurse\n // If section only has items, check if we can save as single file (e.g. name of section + .md)\n \n // Simplification: We will use a mixed approach.\n // If the section is \"flat\" (only items), we can write it to a file.\n // If the section is \"nested\" (has subsections), we create a directory.\n \n // However, to be consistent with Loader logic:\n // Loader reads files in a directory as subsections.\n // So if we have a section \"Instructions\", and we save it as a directory \"instructions\":\n // Its items become content of files?\n \n // Strategy:\n // 1. Create directory `targetPath`.\n // 2. If the section has items (text), verify if they have titles (subsections).\n // 3. For each item:\n // - If it's a string/Instruction/Content/Context (leaf):\n // - If the parent section is the ROOT (e.g. 'persona'), and it's just text, maybe we prefer `persona.md`.\n // - If it's a subsection:\n // - Recurse into subdirectory.\n\n // Let's refine based on typical usage:\n // - Persona: usually one big text. -> `persona.md`\n // - Instructions: usually one big text or list of files. -> `instructions.md` or `instructions/part1.md`\n // - Context: usually list of files. -> `context/data.json`, `context/info.md`\n \n // We need to differentiate based on the targetPath provided by caller.\n // If targetPath ends in \"persona\" (directory name), we can choose to write `targetPath.md` instead if it's flat.\n \n // But `saveToDirectory` created `basePath/persona` (directory path string).\n \n // Let's check complexity:\n const hasSubsections = section.items.some(item => \n typeof item === 'object' && 'items' in item\n );\n\n if (!hasSubsections) {\n // Flat section. \n // If it has multiple items, we can join them? Or write as numbered files?\n // Usually, a flat section implies content for a single file.\n // We prefer to write to `targetPath.md`.\n const content = section.items.map(item => {\n if (typeof item === 'string') return item;\n return (item as Weighted).text;\n }).join('\\n\\n');\n \n // Check if we should write to .md file instead of directory\n // targetPath is e.g. \".../persona\". We want \".../persona.md\".\n await fs.writeFile(`${targetPath}.md`, content);\n return;\n }\n\n // Nested section\n await fs.mkdir(targetPath, { recursive: true });\n\n for (let i = 0; i < section.items.length; i++) {\n const item = section.items[i];\n \n if (typeof item === 'object' && 'items' in item) {\n // Subsection\n // Use title as filename/dirname\n const subTitle = (item as Section<Weighted>).title || `part-${i + 1}`;\n const subPath = path.join(targetPath, subTitle);\n await saveSection(item as Section<Weighted>, subPath);\n } else {\n // Leaf item mixed with subsections.\n // Write to a file. \n // If it's just text, we need a filename.\n const fileName = `item-${i + 1}.md`;\n const content = typeof item === 'string' ? item : (item as Weighted).text;\n await fs.writeFile(path.join(targetPath, fileName), content);\n }\n }\n}\n\n"],"names":["saveToDirectory","prompt","basePath","fs","mkdir","recursive","persona","saveSection","path","join","instructions","contexts","contents","section","targetPath","hasSubsections","items","some","item","content","map","text","writeFile","i","length","subTitle","title","subPath","fileName"],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ # Architecture
2
+
3
+ **Purpose**: High-level overview of the internal design of `riotprompt`.
4
+
5
+ ## Core Concepts
6
+
7
+ ### Prompt Structure
8
+ A `Prompt` in RiotPrompt is not a string; it is a structured object containing four main components:
9
+
10
+ 1. **Persona**: Defines *who* the AI is (System Prompt).
11
+ 2. **Instructions**: Defines *what* the AI should do (User Prompt / Task).
12
+ 3. **Context**: Background information, data, or documents needed to perform the task.
13
+ 4. **Content**: Specific input data to be processed in this execution (optional, often merged with Context in simpler use cases).
14
+
15
+ ### Section System
16
+ The fundamental building block is the `Section<T>`.
17
+ * A `Section` contains a list of items (of type `T`) and can also contain nested `Sections`.
18
+ * This allows for recursive, hierarchical structures (e.g., a Context section containing a "Market Data" section, which contains a "Q1 Results" section).
19
+ * **Weighted Items**: Most items extend `Weighted`, allowing them to have associated weights or parameters for advanced optimization (though simple usage ignores this).
20
+
21
+ ## Module Structure
22
+
23
+ The project is organized into distinct logical modules:
24
+
25
+ * **`src/riotprompt.ts`**: The main entry point. Exports all sub-modules.
26
+ * **`src/prompt.ts`**: Defines the `Prompt` interface and factory.
27
+ * **`src/items/`**: Contains definitions for `Section`, `Instruction`, `Context`, `Content`.
28
+ * **`src/loader.ts`**: Logic for loading prompt parts from the filesystem. It handles traversing directories and parsing Markdown files (extracting headers as section titles).
29
+ * **`src/formatter.ts`**: Responsible for taking a `Prompt` object and converting it into a specific format (e.g., a Chat Request object or a flat string). It handles model-specific nuances via adapters.
30
+ * **`src/serializer.ts`**: Handles converting the internal `Prompt` structure to portable formats like JSON and XML.
31
+ * **`src/cli.ts`**: The command-line interface implementation, using `commander` and `cardigantime` for config.
32
+
33
+ ## Data Flow (CLI)
34
+
35
+ 1. **Input**: User provides a directory path.
36
+ 2. **Loader**: `loader.ts` scans the directory.
37
+ * `persona.md` or `persona/` -> `Section<Instruction>` (Persona)
38
+ * `instructions.md` or `instructions/` -> `Section<Instruction>` (Instructions)
39
+ * `context/` -> `Section<Context>` (Context)
40
+ 3. **Assembly**: Parts are combined into a `Prompt` object.
41
+ 4. **Processing**:
42
+ * If **Serialization** is requested: `Serializer` converts `Prompt` to JSON/XML.
43
+ * If **Formatting** is requested: `Formatter` applies model-specific rules (e.g., role assignment) to generate a Chat Request.
44
+ 5. **Output**: Result is written to stdout or file.
45
+
46
+ ## Design Decisions
47
+
48
+ * **Composition over Concatenation**: By keeping prompt parts separate until the final moment, we allow for dynamic injection, reordering, and model-specific formatting without string manipulation hell.
49
+ * **FileSystem as Source**: We treat the filesystem as a primary way to organize complex prompts. Folders represent Sections, files represent Items. This makes prompts version-controllable and easy to navigate.
50
+ * **Type Safety**: Extensive use of TypeScript generic types (`Section<T>`) ensures that we don't accidentally mix up Context (data) with Instructions (logic).
51
+
@@ -0,0 +1,51 @@
1
+ # Configuration Reference
2
+
3
+ **Purpose**: Detailed guide on configuring `riotprompt`.
4
+
5
+ ## Configuration File
6
+
7
+ RiotPrompt uses `cardigantime` for configuration management. It looks for a `riotprompt.yaml` file in the current working directory or parent directories.
8
+
9
+ ### Schema
10
+
11
+ ```yaml
12
+ # Default model to use if -m flag is not provided
13
+ defaultModel: "gpt-4"
14
+
15
+ # Base directory for prompts (optional, defaults to current dir)
16
+ promptsDir: "."
17
+
18
+ # Default output directory for processing (optional)
19
+ outputDir: "./output"
20
+ ```
21
+
22
+ ## Model Configuration
23
+
24
+ RiotPrompt has an internal `ModelRegistry` that determines how prompts are formatted for different models.
25
+
26
+ * **Persona Role**:
27
+ * `gpt-4`, `claude` family -> `system` role
28
+ * `o1` (o-series) -> `developer` role
29
+ * **Encoding**: Used for token counting (if enabled).
30
+
31
+ Currently, these are hardcoded in `src/model-config.ts` but the library supports runtime configuration via `configureModel`.
32
+
33
+ ```typescript
34
+ import { configureModel } from '@riotprompt/riotprompt';
35
+
36
+ // Register a custom local model
37
+ configureModel({
38
+ exactMatch: 'my-local-llama',
39
+ personaRole: 'system',
40
+ encoding: 'cl100k_base',
41
+ family: 'llama'
42
+ });
43
+ ```
44
+
45
+ ## CLI Flags
46
+
47
+ CLI flags always override configuration file settings.
48
+
49
+ * `-m, --model <name>`: Overrides `defaultModel`.
50
+ * `-o, --output <path>`: Overrides `outputDir`.
51
+
@@ -0,0 +1,62 @@
1
+ # Development Guide
2
+
3
+ **Purpose**: Instructions for contributing to and developing `riotprompt`.
4
+
5
+ ## Setup
6
+
7
+ 1. **Install Dependencies**:
8
+ ```bash
9
+ npm install
10
+ ```
11
+
12
+ 2. **Build**:
13
+ ```bash
14
+ npm run build
15
+ ```
16
+ This builds both the library (`dist/riotprompt.js`) and the CLI (`dist/cli.cjs`).
17
+
18
+ ## Testing
19
+
20
+ We use **Vitest** for testing.
21
+
22
+ * **Run Tests**:
23
+ ```bash
24
+ npm test
25
+ ```
26
+ * **Run with Coverage**:
27
+ ```bash
28
+ npm run test:coverage
29
+ ```
30
+
31
+ ### Test Structure
32
+
33
+ * `tests/unit/`: Tests for individual modules (Serializer, Writer, etc.).
34
+ * `tests/execution/`: Tests for LLM providers (mocked).
35
+ * `tests/integration/`: End-to-end tests for the CLI.
36
+
37
+ ### Mocking
38
+
39
+ Tests that involve LLM APIs or filesystem operations MUST use mocks (`vi.mock`).
40
+ * **Filesystem**: Use `fs/promises` mocks or temporary directories (via `fs.mkdtemp`).
41
+ * **APIs**: Mock the SDKs (`openai`, `@anthropic-ai/sdk`) to avoid making real network calls and incurring costs.
42
+
43
+ ## Project Structure
44
+
45
+ * `src/`: Source code.
46
+ * `items/`: Core data structures (Section, Instruction, etc.).
47
+ * `execution/`: LLM provider implementations.
48
+ * `util/`: Helpers (filesystem, text processing).
49
+ * `tests/`: Unit and integration tests.
50
+ * `input/`: Sample prompts for manual testing.
51
+
52
+ ## Adding Features
53
+
54
+ 1. **Serialization**: If adding a new format, update `src/serializer.ts` and add the option to `src/cli.ts`.
55
+ 2. **New Item Types**: If adding a new prompt component (e.g., `Example`), create a new file in `src/items/` and update `Prompt` interface in `src/prompt.ts`.
56
+ 3. **CLI Commands**: Update `src/cli.ts` using `commander` syntax.
57
+ 4. **New Providers**: Implement the `Provider` interface in `src/execution/` and register it in `ExecutionManager`.
58
+
59
+ ## Linting
60
+
61
+ * **Check**: `npm run lint`
62
+ * **Fix**: `npm run lint:fix`
package/guide/index.md ADDED
@@ -0,0 +1,55 @@
1
+ # AI Agent Guide: RiotPrompt
2
+
3
+ **Role**: You are an AI assistant tasked with understanding, integrating, or extending `riotprompt`, a structured prompt engineering library.
4
+
5
+ **Goal**: Provide a comprehensive understanding of `riotprompt`'s capabilities, architecture, and usage patterns to facilitate correct implementation and extension.
6
+
7
+ ## Core Capabilities
8
+
9
+ `riotprompt` is a library and CLI tool designed to treat LLM prompts as structured code objects rather than simple strings. It allows for the modular assembly, validation, and formatting of prompts.
10
+
11
+ * **Structured Prompts**: Prompts are composed of distinct sections: `Persona`, `Instructions`, `Context`, and `Content`.
12
+ * **Modular Assembly**: Sections can be loaded from separate files or directories and combined dynamically.
13
+ * **Model-Agnostic Formatting**: The library separates the *content* of a prompt from its *format*. It can output prompts optimized for different models (e.g., OpenAI, Claude) using adapters.
14
+ * **Serialization**: Prompts can be serialized to JSON or XML for storage or exchange.
15
+ * **CLI Tool**: A command-line interface allows for easy processing of prompt directories into formatted outputs.
16
+
17
+ ## Quick Start Context
18
+
19
+ When analyzing or generating code using `riotprompt`, keep these patterns in mind:
20
+
21
+ 1. **Define Sections**: Prompts are built from `Section<T>` objects.
22
+ 2. **Create Prompt**: Use `createPrompt` to combine sections.
23
+ 3. **Format**: Use `Formatter` to convert the structured prompt into a string or chat messages for a specific model.
24
+
25
+ ```typescript
26
+ import * as RiotPrompt from '@riotprompt/riotprompt';
27
+
28
+ // 1. Create Sections
29
+ const persona = RiotPrompt.createSection({ title: 'Persona' })
30
+ .add(RiotPrompt.createInstruction('You are a helpful assistant.'));
31
+
32
+ const instructions = RiotPrompt.createSection({ title: 'Instructions' })
33
+ .add(RiotPrompt.createInstruction('Summarize the following text.'));
34
+
35
+ // 2. Create Prompt
36
+ const prompt = RiotPrompt.createPrompt({
37
+ persona,
38
+ instructions
39
+ });
40
+
41
+ // 3. Format
42
+ const formatter = RiotPrompt.Formatter.create();
43
+ // chatRequest will be a structured object suitable for API calls (e.g. OpenAI chat completion)
44
+ const chatRequest = formatter.formatPrompt('gpt-4', prompt);
45
+ ```
46
+
47
+ ## Documentation Structure
48
+
49
+ This guide directory contains specialized documentation for different aspects of the system:
50
+
51
+ * [Architecture](./architecture.md): Internal design, module structure, and data flow.
52
+ * [Usage Patterns](./usage.md): Common patterns for CLI and library usage.
53
+ * [Configuration](./configuration.md): Deep dive into configuration options.
54
+ * [Development](./development.md): Guide for contributing to `riotprompt`.
55
+