@riotprompt/riotprompt 0.0.2 → 0.0.3
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.
- package/README.md +54 -470
- package/dist/builder.d.ts +3 -3
- package/dist/builder.js +7 -2
- package/dist/builder.js.map +1 -1
- package/dist/chat.js.map +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/formatter.js.map +1 -1
- package/dist/items/content.js.map +1 -1
- package/dist/items/context.js.map +1 -1
- package/dist/items/instruction.js.map +1 -1
- package/dist/items/parameters.js.map +1 -1
- package/dist/items/section.js.map +1 -1
- package/dist/items/trait.js.map +1 -1
- package/dist/items/weighted.js.map +1 -1
- package/dist/loader.js +1 -0
- package/dist/loader.js.map +1 -1
- package/dist/logger.js.map +1 -1
- package/dist/override.d.ts +5 -5
- package/dist/override.js +47 -30
- package/dist/override.js.map +1 -1
- package/dist/parse/markdown.js.map +1 -1
- package/dist/parse/text.js.map +1 -1
- package/dist/parser.js.map +1 -1
- package/dist/prompt.js.map +1 -1
- package/dist/recipes.d.ts +405 -0
- package/dist/recipes.js +424 -0
- package/dist/recipes.js.map +1 -0
- package/dist/riotprompt.cjs +484 -32
- package/dist/riotprompt.cjs.map +1 -1
- package/dist/riotprompt.d.ts +3 -0
- package/dist/riotprompt.js +3 -0
- package/dist/riotprompt.js.map +1 -1
- package/dist/util/general.js.map +1 -1
- package/dist/util/markdown.js.map +1 -1
- package/dist/util/storage.js.map +1 -1
- package/dist/util/text.js.map +1 -1
- package/package.json +29 -24
- package/.gitcarve/config.yaml +0 -10
- package/.gitcarve/context/content.md +0 -11
- package/.markdown-doctest-setup.mjs +0 -23
- package/.nvmrc +0 -1
- package/docs/loader.md +0 -237
- package/docs/override.md +0 -323
- package/docs/parser.md +0 -130
- package/eslint.config.mjs +0 -82
- package/nodemon.json +0 -14
- package/vite.config.ts +0 -114
- package/vitest.config.ts +0 -25
package/dist/riotprompt.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export * as Chat from './chat';
|
|
|
12
12
|
export * as Loader from './loader';
|
|
13
13
|
export * as Override from './override';
|
|
14
14
|
export * as Builder from './builder';
|
|
15
|
+
export * as Recipes from './recipes';
|
|
16
|
+
export { cook, commit, release, documentation, review, quick, recipe, configureTemplates, getTemplates } from './recipes';
|
|
15
17
|
export type { Content } from './items/content';
|
|
16
18
|
export type { Context } from './items/context';
|
|
17
19
|
export type { Instruction } from './items/instruction';
|
|
@@ -23,3 +25,4 @@ export type { Prompt } from './prompt';
|
|
|
23
25
|
export type { FormatOptions, SectionSeparator, SectionTitleProperty } from './formatter';
|
|
24
26
|
export type { Model, Request } from './chat';
|
|
25
27
|
export type { Logger } from './logger';
|
|
28
|
+
export type { RecipeConfig, ContentItem, TemplateConfig } from './recipes';
|
package/dist/riotprompt.js
CHANGED
|
@@ -18,4 +18,7 @@ import * as override from './override.js';
|
|
|
18
18
|
export { override as Override };
|
|
19
19
|
import * as builder from './builder.js';
|
|
20
20
|
export { builder as Builder };
|
|
21
|
+
import * as recipes from './recipes.js';
|
|
22
|
+
export { recipes as Recipes };
|
|
23
|
+
export { commit, configureTemplates, cook, documentation, getTemplates, quick, recipe, release, review } from './recipes.js';
|
|
21
24
|
//# sourceMappingURL=riotprompt.js.map
|
package/dist/riotprompt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"riotprompt.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"riotprompt.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/util/general.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"general.js","sources":["../../src/util/general.ts"],"sourcesContent":["export const clean = (obj: any) => {\n return Object.fromEntries(\n Object.entries(obj).filter(([_, v]) => v !== undefined)\n );\n}\n\n//Recursive implementation of jSON.stringify;\nexport const stringifyJSON = function (obj: any, visited: Set<any> = new Set()): string {\n const arrOfKeyVals: string[] = [];\n const arrVals: string[] = [];\n let objKeys: string[] = [];\n\n /*********CHECK FOR PRIMITIVE TYPES**********/\n if (typeof obj === 'number' || typeof obj === 'boolean' || obj === null)\n return '' + obj;\n else if (typeof obj === 'string')\n return '\"' + obj + '\"';\n\n /*********DETECT CIRCULAR REFERENCES**********/\n if (obj instanceof Object && visited.has(obj)) {\n return '\"(circular)\"';\n }\n\n /*********CHECK FOR ARRAY**********/\n else if (Array.isArray(obj)) {\n //check for empty array\n if (obj[0] === undefined)\n return '[]';\n else {\n // Add array to visited before processing its elements\n visited.add(obj);\n obj.forEach(function (el) {\n arrVals.push(stringifyJSON(el, visited));\n });\n return '[' + arrVals + ']';\n }\n }\n /*********CHECK FOR OBJECT**********/\n else if (obj instanceof Object) {\n // Add object to visited before processing its properties\n visited.add(obj);\n //get object keys\n objKeys = Object.keys(obj);\n //set key output;\n objKeys.forEach(function (key) {\n const keyOut = '\"' + key + '\":';\n const keyValOut = obj[key];\n //skip functions and undefined properties\n if (keyValOut instanceof Function || keyValOut === undefined)\n return; // Skip this entry entirely instead of pushing an empty string\n else if (typeof keyValOut === 'string')\n arrOfKeyVals.push(keyOut + '\"' + keyValOut + '\"');\n else if (typeof keyValOut === 'boolean' || typeof keyValOut === 'number' || keyValOut === null)\n arrOfKeyVals.push(keyOut + keyValOut);\n //check for nested objects, call recursively until no more objects\n else if (keyValOut instanceof Object) {\n arrOfKeyVals.push(keyOut + stringifyJSON(keyValOut, visited));\n }\n });\n return '{' + arrOfKeyVals + '}';\n }\n return '';\n};"],"names":["clean","obj","Object","fromEntries","entries","filter","_","v","undefined","stringifyJSON","visited","Set","arrOfKeyVals","arrVals","objKeys","has","Array","isArray","add","forEach","el","push","keys","key","keyOut","keyValOut","Function"],"mappings":"AAAO,MAAMA,QAAQ,CAACC,GAAAA,GAAAA;AAClB,IAAA,OAAOC,
|
|
1
|
+
{"version":3,"file":"general.js","sources":["../../src/util/general.ts"],"sourcesContent":["export const clean = (obj: any) => {\n return Object.fromEntries(\n Object.entries(obj).filter(([_, v]) => v !== undefined)\n );\n}\n\n//Recursive implementation of jSON.stringify;\nexport const stringifyJSON = function (obj: any, visited: Set<any> = new Set()): string {\n const arrOfKeyVals: string[] = [];\n const arrVals: string[] = [];\n let objKeys: string[] = [];\n\n /*********CHECK FOR PRIMITIVE TYPES**********/\n if (typeof obj === 'number' || typeof obj === 'boolean' || obj === null)\n return '' + obj;\n else if (typeof obj === 'string')\n return '\"' + obj + '\"';\n\n /*********DETECT CIRCULAR REFERENCES**********/\n if (obj instanceof Object && visited.has(obj)) {\n return '\"(circular)\"';\n }\n\n /*********CHECK FOR ARRAY**********/\n else if (Array.isArray(obj)) {\n //check for empty array\n if (obj[0] === undefined)\n return '[]';\n else {\n // Add array to visited before processing its elements\n visited.add(obj);\n obj.forEach(function (el) {\n arrVals.push(stringifyJSON(el, visited));\n });\n return '[' + arrVals + ']';\n }\n }\n /*********CHECK FOR OBJECT**********/\n else if (obj instanceof Object) {\n // Add object to visited before processing its properties\n visited.add(obj);\n //get object keys\n objKeys = Object.keys(obj);\n //set key output;\n objKeys.forEach(function (key) {\n const keyOut = '\"' + key + '\":';\n const keyValOut = obj[key];\n //skip functions and undefined properties\n if (keyValOut instanceof Function || keyValOut === undefined)\n return; // Skip this entry entirely instead of pushing an empty string\n else if (typeof keyValOut === 'string')\n arrOfKeyVals.push(keyOut + '\"' + keyValOut + '\"');\n else if (typeof keyValOut === 'boolean' || typeof keyValOut === 'number' || keyValOut === null)\n arrOfKeyVals.push(keyOut + keyValOut);\n //check for nested objects, call recursively until no more objects\n else if (keyValOut instanceof Object) {\n arrOfKeyVals.push(keyOut + stringifyJSON(keyValOut, visited));\n }\n });\n return '{' + arrOfKeyVals + '}';\n }\n return '';\n};"],"names":["clean","obj","Object","fromEntries","entries","filter","_","v","undefined","stringifyJSON","visited","Set","arrOfKeyVals","arrVals","objKeys","has","Array","isArray","add","forEach","el","push","keys","key","keyOut","keyValOut","Function"],"mappings":"AAAO,MAAMA,QAAQ,CAACC,GAAAA,GAAAA;AAClB,IAAA,OAAOC,MAAAA,CAAOC,WAAW,CACrBD,MAAAA,CAAOE,OAAO,CAACH,GAAAA,CAAAA,CAAKI,MAAM,CAAC,CAAC,CAACC,CAAAA,EAAGC,CAAAA,CAAE,GAAKA,CAAAA,KAAMC,SAAAA,CAAAA,CAAAA;AAErD;AAEA;MACaC,aAAAA,GAAgB,SAAUR,GAAQ,EAAES,OAAAA,GAAoB,IAAIC,GAAAA,EAAK,EAAA;AAC1E,IAAA,MAAMC,eAAyB,EAAE;AACjC,IAAA,MAAMC,UAAoB,EAAE;AAC5B,IAAA,IAAIC,UAAoB,EAAE;mDAG1B,IAAI,OAAOb,GAAAA,KAAQ,QAAA,IAAY,OAAOA,GAAAA,KAAQ,SAAA,IAAaA,GAAAA,KAAQ,IAAA,EAC/D,OAAO,EAAA,GAAKA,GAAAA;AACX,SAAA,IAAI,OAAOA,GAAAA,KAAQ,QAAA,EACpB,OAAO,MAAMA,GAAAA,GAAM,GAAA;AAEvB,oDACA,IAAIA,GAAAA,YAAeC,UAAUQ,OAAAA,CAAQK,GAAG,CAACd,GAAAA,CAAAA,EAAM;QAC3C,OAAO,cAAA;AACX,KAAA,MAGK,IAAIe,KAAAA,CAAMC,OAAO,CAAChB,GAAAA,CAAAA,EAAM;;AAEzB,QAAA,IAAIA,GAAG,CAAC,CAAA,CAAE,KAAKO,WACX,OAAO,IAAA;AACN,aAAA;;AAEDE,YAAAA,OAAAA,CAAQQ,GAAG,CAACjB,GAAAA,CAAAA;YACZA,GAAAA,CAAIkB,OAAO,CAAC,SAAUC,EAAE,EAAA;gBACpBP,OAAAA,CAAQQ,IAAI,CAACZ,aAAAA,CAAcW,EAAAA,EAAIV,OAAAA,CAAAA,CAAAA;AACnC,aAAA,CAAA;AACA,YAAA,OAAO,MAAMG,OAAAA,GAAU,GAAA;AAC3B;KACJ,MAEK,IAAIZ,eAAeC,MAAAA,EAAQ;;AAE5BQ,QAAAA,OAAAA,CAAQQ,GAAG,CAACjB,GAAAA,CAAAA;;QAEZa,OAAAA,GAAUZ,MAAAA,CAAOoB,IAAI,CAACrB,GAAAA,CAAAA;;QAEtBa,OAAAA,CAAQK,OAAO,CAAC,SAAUI,GAAG,EAAA;YACzB,MAAMC,MAAAA,GAAS,MAAMD,GAAAA,GAAM,IAAA;YAC3B,MAAME,SAAAA,GAAYxB,GAAG,CAACsB,GAAAA,CAAI;;AAE1B,YAAA,IAAIE,SAAAA,YAAqBC,QAAAA,IAAYD,SAAAA,KAAcjB,SAAAA,EAC/C;iBACC,IAAI,OAAOiB,cAAc,QAAA,EAC1Bb,YAAAA,CAAaS,IAAI,CAACG,MAAAA,GAAS,MAAMC,SAAAA,GAAY,GAAA,CAAA;iBAC5C,IAAI,OAAOA,SAAAA,KAAc,SAAA,IAAa,OAAOA,SAAAA,KAAc,QAAA,IAAYA,SAAAA,KAAc,IAAA,EACtFb,YAAAA,CAAaS,IAAI,CAACG,MAAAA,GAASC,SAAAA,CAAAA;AAE1B,iBAAA,IAAIA,qBAAqBvB,MAAAA,EAAQ;AAClCU,gBAAAA,YAAAA,CAAaS,IAAI,CAACG,MAAAA,GAASf,aAAAA,CAAcgB,SAAAA,EAAWf,OAAAA,CAAAA,CAAAA;AACxD;AACJ,SAAA,CAAA;AACA,QAAA,OAAO,MAAME,YAAAA,GAAe,GAAA;AAChC;IACA,OAAO,EAAA;AACX;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.js","sources":["../../src/util/markdown.ts"],"sourcesContent":["import { DEFAULT_CHARACTER_ENCODING } from \"../constants\";\n\n// Heuristic to check for Markdown syntax. This is not a full parser.\n// It looks for common Markdown patterns.\nconst markdownRegex = /^(#+\\s|\\*\\s|-\\s|\\+\\s|>\\s|\\[.*\\]\\(.*\\)|```|~~~|---\\\\s*$)/m;\n\n/**\n * Inspects a string to see if it likely contains Markdown syntax.\n *\n * @param input The string or Buffer content to inspect.\n * @returns True if Markdown syntax is suspected, false otherwise.\n */\nexport function isMarkdown(input: string | Buffer): boolean {\n if (input == null) {\n return false;\n }\n // Convert Buffer to string if necessary\n const content = typeof input === 'string' ? input : input.toString(DEFAULT_CHARACTER_ENCODING);\n if (!content || content.trim() === '') {\n return false; // Empty string is not considered Markdown\n }\n\n // Check for common Markdown patterns in the entire content\n if (markdownRegex.test(content)) {\n return true;\n }\n\n // Fallback: Check for a high prevalence of Markdown-like list/header starters\n // or thematic breaks, or code blocks.\n // We'll consider up to the first ~2000 characters, roughly equivalent to the byte check.\n const effectiveContent = content.length > 2000 ? content.substring(0, 2000) : content;\n const lines = effectiveContent.split('\\n');\n let markdownFeatureCount = 0;\n const featurePatterns = [\n /^#+\\s+.+/, // Headers (e.g., # Heading)\n /^\\s*[*+-]\\s+.+/, // List items (e.g., * item, - item, + item)\n /^\\s*>\\s+.+/, // Blockquotes (e.g., > quote)\n /\\[.+\\]\\(.+\\)/, // Links (e.g., [text](url))\n /!\\[.+\\]\\(.+\\)/, // Images (e.g., )\n /`{1,3}[^`]+`{1,3}/, // Inline code (e.g., `code`) or code blocks (```code```)\n /^\\s*_{3,}\\s*$/, // Thematic breaks (e.g., ---, ***, ___)\n /^\\s*-{3,}\\s*$/,\n /^\\s*\\*{3,}\\s*$/\n ];\n\n for (const line of lines) {\n // Stop checking if we have already found enough features to be confident.\n // This is a small optimization for very long inputs that are clearly markdown early on.\n if (markdownFeatureCount >= 2 && lines.length > 10) { // Heuristic threshold\n const significantLineCountEarly = Math.min(lines.indexOf(line) + 1, 20);\n if (significantLineCountEarly > 0 && markdownFeatureCount / significantLineCountEarly > 0.1) {\n return true;\n }\n }\n\n for (const pattern of featurePatterns) {\n if (pattern.test(line.trim())) {\n markdownFeatureCount++;\n break; // Count each line only once\n }\n }\n }\n\n // If more than 5% of the first few lines (up to 20 lines or all lines if fewer)\n // show markdown features, or if there are at least 2 distinct features in short texts,\n // consider it Markdown.\n const significantLineCount = Math.min(lines.length, 20);\n if (significantLineCount > 0) {\n // Calculate the exact threshold percentage\n const thresholdPercentage = markdownFeatureCount / significantLineCount;\n\n // Check against the 5% threshold (0.05)\n // Using >= 0.05 exactly matches 5%, > 0.05 requires more than 5%\n if (thresholdPercentage >= 0.05 + 0.0001) { // Adding a small epsilon to ensure exactly 5% passes but just below fails\n return true;\n }\n\n // Other conditions for returning true\n if ((markdownFeatureCount >= 1 && significantLineCount <= 5) || markdownFeatureCount >= 2) {\n return true;\n }\n }\n\n return false;\n}\n\n// Example usage (optional, for testing):\n// function testIsMarkdownString() {\n// console.log('--- Testing isMarkdownString ---');\n// const markdown1 = '# Hello World\\\\nThis is a test.';\n// console.log(`Test 1 (Header): \"${markdown1.substring(0,10)}...\" -> ${isMarkdownString(markdown1)}`); // true\n\n// const markdown2 = '* Item 1\\\\n* Item 2';\n// console.log(`Test 2 (List): \"${markdown2.substring(0,10)}...\" -> ${isMarkdownString(markdown2)}`); // true\n\n// const markdown3 = '[Google](https://google.com)';\n// console.log(`Test 3 (Link): \"${markdown3.substring(0,15)}...\" -> ${isMarkdownString(markdown3)}`); // true\n\n// const markdown4 = '> This is a quote.';\n// console.log(`Test 4 (Blockquote): \"${markdown4.substring(0,10)}...\" -> ${isMarkdownString(markdown4)}`); // true\n\n// const markdown5 = '```javascript\\\\nconsole.log(\"hello\");\\\\n```';\n// console.log(`Test 5 (Code block): \"${markdown5.substring(0,15)}...\" -> ${isMarkdownString(markdown5)}`); // true\n\n// const text1 = 'This is a plain text string.';\n// console.log(`Test 6 (Plain text): \"${text1.substring(0,10)}...\" -> ${isMarkdownString(text1)}`); // false\n\n// const text2 = 'hello_world.this_is_a_test_string_with_underscores_but_not_markdown_thematic_break';\n// console.log(`Test 7 (Long non-markdown): \"${text2.substring(0,10)}...\" -> ${isMarkdownString(text2)}`); // false\n\n// const text3 = '<xml><tag>value</tag></xml>';\n// console.log(`Test 8 (XML): \"${text3.substring(0,10)}...\" -> ${isMarkdownString(text3)}`); // false\n\n// const shortMarkdown = '# H';\n// console.log(`Test 9 (Short Markdown): \"${shortMarkdown}\" -> ${isMarkdownString(shortMarkdown)}`); // true\n\n// const shortNonMarkdown = 'Hello';\n// console.log(`Test 10 (Short Non-Markdown): \"${shortNonMarkdown}\" -> ${isMarkdownString(shortNonMarkdown)}`); // false\n\n// const emptyString = '';\n// console.log(`Test 11 (Empty string): \"\" -> ${isMarkdownString(emptyString)}`); // false\n\n// const whitespaceString = ' \\t \\n ';\n// console.log(`Test 12 (Whitespace string): \"${whitespaceString.substring(0,5)}...\" -> ${isMarkdownString(whitespaceString)}`); // false\n\n// const markdownWithManyFeatures = `# Title\\\\n\\\\n* list\\\\n* list2\\\\n\\\\n> quote here\\\\n\\\\n\\`\\`\\`\\\\ncode\\\\n\\`\\`\\`\\\\n\\\\nnormal text paragraph with a [link](url).\\n---\\nAnother paragraph.\\nThis is just a test string to see how it performs with multiple markdown features present.\\nHello world this is a very long line that does not contain any markdown syntax at all, it is just plain text that goes on and on.\\n* Another list item\\n* And another one\\n# Another Header\\n## Subheader\\nThis is fun.\\nOkay I think this is enough.\\nFinal line.\\nAnother final line.\\nOne more for good measure.\\nOkay that should be enough lines to test the early exit.\\n`;\n// console.log(`Test 13 (Many Features): \"${markdownWithManyFeatures.substring(0,10)}...\" -> ${isMarkdownString(markdownWithManyFeatures)}`); // true\n\n// const htmlLike = '<div><p>Hello</p><ul><li>item</li></ul></div>';\n// console.log(`Test 14 (HTML-like): \"${htmlLike.substring(0,10)}...\" -> ${isMarkdownString(htmlLike)}`); // false\n\n// console.log('--- End Testing ---');\n// }\n\n// testIsMarkdownString();\n"],"names":["markdownRegex","isMarkdown","input","content","toString","DEFAULT_CHARACTER_ENCODING","trim","test","effectiveContent","length","substring","lines","split","markdownFeatureCount","featurePatterns","line","significantLineCountEarly","Math","min","indexOf","pattern","significantLineCount","thresholdPercentage"],"mappings":";;AAEA;AACA;AACA,MAAMA,
|
|
1
|
+
{"version":3,"file":"markdown.js","sources":["../../src/util/markdown.ts"],"sourcesContent":["import { DEFAULT_CHARACTER_ENCODING } from \"../constants\";\n\n// Heuristic to check for Markdown syntax. This is not a full parser.\n// It looks for common Markdown patterns.\nconst markdownRegex = /^(#+\\s|\\*\\s|-\\s|\\+\\s|>\\s|\\[.*\\]\\(.*\\)|```|~~~|---\\\\s*$)/m;\n\n/**\n * Inspects a string to see if it likely contains Markdown syntax.\n *\n * @param input The string or Buffer content to inspect.\n * @returns True if Markdown syntax is suspected, false otherwise.\n */\nexport function isMarkdown(input: string | Buffer): boolean {\n if (input == null) {\n return false;\n }\n // Convert Buffer to string if necessary\n const content = typeof input === 'string' ? input : input.toString(DEFAULT_CHARACTER_ENCODING);\n if (!content || content.trim() === '') {\n return false; // Empty string is not considered Markdown\n }\n\n // Check for common Markdown patterns in the entire content\n if (markdownRegex.test(content)) {\n return true;\n }\n\n // Fallback: Check for a high prevalence of Markdown-like list/header starters\n // or thematic breaks, or code blocks.\n // We'll consider up to the first ~2000 characters, roughly equivalent to the byte check.\n const effectiveContent = content.length > 2000 ? content.substring(0, 2000) : content;\n const lines = effectiveContent.split('\\n');\n let markdownFeatureCount = 0;\n const featurePatterns = [\n /^#+\\s+.+/, // Headers (e.g., # Heading)\n /^\\s*[*+-]\\s+.+/, // List items (e.g., * item, - item, + item)\n /^\\s*>\\s+.+/, // Blockquotes (e.g., > quote)\n /\\[.+\\]\\(.+\\)/, // Links (e.g., [text](url))\n /!\\[.+\\]\\(.+\\)/, // Images (e.g., )\n /`{1,3}[^`]+`{1,3}/, // Inline code (e.g., `code`) or code blocks (```code```)\n /^\\s*_{3,}\\s*$/, // Thematic breaks (e.g., ---, ***, ___)\n /^\\s*-{3,}\\s*$/,\n /^\\s*\\*{3,}\\s*$/\n ];\n\n for (const line of lines) {\n // Stop checking if we have already found enough features to be confident.\n // This is a small optimization for very long inputs that are clearly markdown early on.\n if (markdownFeatureCount >= 2 && lines.length > 10) { // Heuristic threshold\n const significantLineCountEarly = Math.min(lines.indexOf(line) + 1, 20);\n if (significantLineCountEarly > 0 && markdownFeatureCount / significantLineCountEarly > 0.1) {\n return true;\n }\n }\n\n for (const pattern of featurePatterns) {\n if (pattern.test(line.trim())) {\n markdownFeatureCount++;\n break; // Count each line only once\n }\n }\n }\n\n // If more than 5% of the first few lines (up to 20 lines or all lines if fewer)\n // show markdown features, or if there are at least 2 distinct features in short texts,\n // consider it Markdown.\n const significantLineCount = Math.min(lines.length, 20);\n if (significantLineCount > 0) {\n // Calculate the exact threshold percentage\n const thresholdPercentage = markdownFeatureCount / significantLineCount;\n\n // Check against the 5% threshold (0.05)\n // Using >= 0.05 exactly matches 5%, > 0.05 requires more than 5%\n if (thresholdPercentage >= 0.05 + 0.0001) { // Adding a small epsilon to ensure exactly 5% passes but just below fails\n return true;\n }\n\n // Other conditions for returning true\n if ((markdownFeatureCount >= 1 && significantLineCount <= 5) || markdownFeatureCount >= 2) {\n return true;\n }\n }\n\n return false;\n}\n\n// Example usage (optional, for testing):\n// function testIsMarkdownString() {\n// console.log('--- Testing isMarkdownString ---');\n// const markdown1 = '# Hello World\\\\nThis is a test.';\n// console.log(`Test 1 (Header): \"${markdown1.substring(0,10)}...\" -> ${isMarkdownString(markdown1)}`); // true\n\n// const markdown2 = '* Item 1\\\\n* Item 2';\n// console.log(`Test 2 (List): \"${markdown2.substring(0,10)}...\" -> ${isMarkdownString(markdown2)}`); // true\n\n// const markdown3 = '[Google](https://google.com)';\n// console.log(`Test 3 (Link): \"${markdown3.substring(0,15)}...\" -> ${isMarkdownString(markdown3)}`); // true\n\n// const markdown4 = '> This is a quote.';\n// console.log(`Test 4 (Blockquote): \"${markdown4.substring(0,10)}...\" -> ${isMarkdownString(markdown4)}`); // true\n\n// const markdown5 = '```javascript\\\\nconsole.log(\"hello\");\\\\n```';\n// console.log(`Test 5 (Code block): \"${markdown5.substring(0,15)}...\" -> ${isMarkdownString(markdown5)}`); // true\n\n// const text1 = 'This is a plain text string.';\n// console.log(`Test 6 (Plain text): \"${text1.substring(0,10)}...\" -> ${isMarkdownString(text1)}`); // false\n\n// const text2 = 'hello_world.this_is_a_test_string_with_underscores_but_not_markdown_thematic_break';\n// console.log(`Test 7 (Long non-markdown): \"${text2.substring(0,10)}...\" -> ${isMarkdownString(text2)}`); // false\n\n// const text3 = '<xml><tag>value</tag></xml>';\n// console.log(`Test 8 (XML): \"${text3.substring(0,10)}...\" -> ${isMarkdownString(text3)}`); // false\n\n// const shortMarkdown = '# H';\n// console.log(`Test 9 (Short Markdown): \"${shortMarkdown}\" -> ${isMarkdownString(shortMarkdown)}`); // true\n\n// const shortNonMarkdown = 'Hello';\n// console.log(`Test 10 (Short Non-Markdown): \"${shortNonMarkdown}\" -> ${isMarkdownString(shortNonMarkdown)}`); // false\n\n// const emptyString = '';\n// console.log(`Test 11 (Empty string): \"\" -> ${isMarkdownString(emptyString)}`); // false\n\n// const whitespaceString = ' \\t \\n ';\n// console.log(`Test 12 (Whitespace string): \"${whitespaceString.substring(0,5)}...\" -> ${isMarkdownString(whitespaceString)}`); // false\n\n// const markdownWithManyFeatures = `# Title\\\\n\\\\n* list\\\\n* list2\\\\n\\\\n> quote here\\\\n\\\\n\\`\\`\\`\\\\ncode\\\\n\\`\\`\\`\\\\n\\\\nnormal text paragraph with a [link](url).\\n---\\nAnother paragraph.\\nThis is just a test string to see how it performs with multiple markdown features present.\\nHello world this is a very long line that does not contain any markdown syntax at all, it is just plain text that goes on and on.\\n* Another list item\\n* And another one\\n# Another Header\\n## Subheader\\nThis is fun.\\nOkay I think this is enough.\\nFinal line.\\nAnother final line.\\nOne more for good measure.\\nOkay that should be enough lines to test the early exit.\\n`;\n// console.log(`Test 13 (Many Features): \"${markdownWithManyFeatures.substring(0,10)}...\" -> ${isMarkdownString(markdownWithManyFeatures)}`); // true\n\n// const htmlLike = '<div><p>Hello</p><ul><li>item</li></ul></div>';\n// console.log(`Test 14 (HTML-like): \"${htmlLike.substring(0,10)}...\" -> ${isMarkdownString(htmlLike)}`); // false\n\n// console.log('--- End Testing ---');\n// }\n\n// testIsMarkdownString();\n"],"names":["markdownRegex","isMarkdown","input","content","toString","DEFAULT_CHARACTER_ENCODING","trim","test","effectiveContent","length","substring","lines","split","markdownFeatureCount","featurePatterns","line","significantLineCountEarly","Math","min","indexOf","pattern","significantLineCount","thresholdPercentage"],"mappings":";;AAEA;AACA;AACA,MAAMA,aAAAA,GAAgB,0DAAA;AAEtB;;;;;IAMO,SAASC,UAAAA,CAAWC,KAAsB,EAAA;AAC7C,IAAA,IAAIA,SAAS,IAAA,EAAM;QACf,OAAO,KAAA;AACX;;AAEA,IAAA,MAAMC,UAAU,OAAOD,KAAAA,KAAU,WAAWA,KAAAA,GAAQA,KAAAA,CAAME,QAAQ,CAACC,0BAAAA,CAAAA;AACnE,IAAA,IAAI,CAACF,OAAAA,IAAWA,OAAAA,CAAQG,IAAI,OAAO,EAAA,EAAI;AACnC,QAAA,OAAO;AACX;;IAGA,IAAIN,aAAAA,CAAcO,IAAI,CAACJ,OAAAA,CAAAA,EAAU;QAC7B,OAAO,IAAA;AACX;;;;IAKA,MAAMK,gBAAAA,GAAmBL,QAAQM,MAAM,GAAG,OAAON,OAAAA,CAAQO,SAAS,CAAC,CAAA,EAAG,IAAA,CAAA,GAAQP,OAAAA;IAC9E,MAAMQ,KAAAA,GAAQH,gBAAAA,CAAiBI,KAAK,CAAC,IAAA,CAAA;AACrC,IAAA,IAAIC,oBAAAA,GAAuB,CAAA;AAC3B,IAAA,MAAMC,eAAAA,GAAkB;AACpB,QAAA,UAAA;AACA,QAAA,gBAAA;AACA,QAAA,YAAA;AACA,QAAA,cAAA;AACA,QAAA,eAAA;AACA,QAAA,mBAAA;AACA,QAAA,eAAA;AACA,QAAA,eAAA;AACA,QAAA;AACH,KAAA;IAED,KAAK,MAAMC,QAAQJ,KAAAA,CAAO;;;AAGtB,QAAA,IAAIE,oBAAAA,IAAwB,CAAA,IAAKF,KAAAA,CAAMF,MAAM,GAAG,EAAA,EAAI;YAChD,MAAMO,yBAAAA,GAA4BC,KAAKC,GAAG,CAACP,MAAMQ,OAAO,CAACJ,QAAQ,CAAA,EAAG,EAAA,CAAA;AACpE,YAAA,IAAIC,yBAAAA,GAA4B,CAAA,IAAKH,oBAAAA,GAAuBG,yBAAAA,GAA4B,GAAA,EAAK;gBACzF,OAAO,IAAA;AACX;AACJ;QAEA,KAAK,MAAMI,WAAWN,eAAAA,CAAiB;AACnC,YAAA,IAAIM,OAAAA,CAAQb,IAAI,CAACQ,IAAAA,CAAKT,IAAI,EAAA,CAAA,EAAK;AAC3BO,gBAAAA,oBAAAA,EAAAA;AACA,gBAAA,MAAA;AACJ;AACJ;AACJ;;;;AAKA,IAAA,MAAMQ,uBAAuBJ,IAAAA,CAAKC,GAAG,CAACP,KAAAA,CAAMF,MAAM,EAAE,EAAA,CAAA;AACpD,IAAA,IAAIY,uBAAuB,CAAA,EAAG;;AAE1B,QAAA,MAAMC,sBAAsBT,oBAAAA,GAAuBQ,oBAAAA;;;QAInD,IAAIC,mBAAAA,IAAuB,OAAO,MAAA,EAAQ;YACtC,OAAO,IAAA;AACX;;AAGA,QAAA,IAAI,oBAACT,IAAwB,CAAA,IAAKQ,oBAAAA,IAAwB,CAAA,IAAMR,wBAAwB,CAAA,EAAG;YACvF,OAAO,IAAA;AACX;AACJ;IAEA,OAAO,KAAA;AACX,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/util/storage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.js","sources":["../../src/util/storage.ts"],"sourcesContent":["// eslint-disable-next-line no-restricted-imports\nimport * as fs from 'fs';\nimport { glob } from 'glob';\nimport path from 'path';\nimport crypto from 'crypto';\n/**\n * This module exists to isolate filesystem operations from the rest of the codebase.\n * This makes testing easier by avoiding direct fs mocking in jest configuration.\n * \n * Additionally, abstracting storage operations allows for future flexibility - \n * this export utility may need to work with storage systems other than the local filesystem\n * (e.g. S3, Google Cloud Storage, etc).\n */\n\nexport interface Utility {\n exists: (path: string) => Promise<boolean>;\n isDirectory: (path: string) => Promise<boolean>;\n isFile: (path: string) => Promise<boolean>;\n isReadable: (path: string) => Promise<boolean>;\n isWritable: (path: string) => Promise<boolean>;\n isFileReadable: (path: string) => Promise<boolean>;\n isDirectoryWritable: (path: string) => Promise<boolean>;\n isDirectoryReadable: (path: string) => Promise<boolean>;\n createDirectory: (path: string) => Promise<void>;\n readFile: (path: string, encoding: string) => Promise<string>;\n readStream: (path: string) => Promise<fs.ReadStream>;\n writeFile: (path: string, data: string | Buffer, encoding: string) => Promise<void>;\n forEachFileIn: (directory: string, callback: (path: string) => Promise<void>, options?: { pattern: string, limit?: number }) => Promise<void>;\n hashFile: (path: string, length: number) => Promise<string>;\n listFiles: (directory: string) => Promise<string[]>;\n}\n\nexport const create = (params: { log?: (message: string, ...args: any[]) => void }): Utility => {\n\n // eslint-disable-next-line no-console\n const log = params.log || console.log;\n\n const exists = async (path: string): Promise<boolean> => {\n try {\n await fs.promises.stat(path);\n return true;\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (error: any) {\n return false;\n }\n }\n\n const isDirectory = async (path: string): Promise<boolean> => {\n const stats = await fs.promises.stat(path);\n if (!stats.isDirectory()) {\n log(`${path} is not a directory`);\n return false;\n }\n return true;\n }\n\n const isFile = async (path: string): Promise<boolean> => {\n const stats = await fs.promises.stat(path);\n if (!stats.isFile()) {\n log(`${path} is not a file`);\n return false;\n }\n return true;\n }\n\n const isReadable = async (path: string): Promise<boolean> => {\n try {\n await fs.promises.access(path, fs.constants.R_OK);\n } catch (error: any) {\n log(`${path} is not readable: %s %s`, error.message, error.stack);\n return false;\n }\n return true;\n }\n\n const isWritable = async (path: string): Promise<boolean> => {\n try {\n await fs.promises.access(path, fs.constants.W_OK);\n } catch (error: any) {\n log(`${path} is not writable: %s %s`, error.message, error.stack);\n return false;\n }\n return true;\n }\n\n const isFileReadable = async (path: string): Promise<boolean> => {\n return await exists(path) && await isFile(path) && await isReadable(path);\n }\n\n const isDirectoryWritable = async (path: string): Promise<boolean> => {\n return await exists(path) && await isDirectory(path) && await isWritable(path);\n }\n\n const isDirectoryReadable = async (path: string): Promise<boolean> => {\n return await exists(path) && await isDirectory(path) && await isReadable(path);\n }\n\n const createDirectory = async (path: string): Promise<void> => {\n try {\n await fs.promises.mkdir(path, { recursive: true });\n } catch (mkdirError: any) {\n throw new Error(`Failed to create output directory ${path}: ${mkdirError.message} ${mkdirError.stack}`);\n }\n }\n\n const readFile = async (path: string, encoding: string): Promise<string> => {\n return await fs.promises.readFile(path, { encoding: encoding as BufferEncoding });\n }\n\n const writeFile = async (path: string, data: string | Buffer, encoding: string): Promise<void> => {\n await fs.promises.writeFile(path, data, { encoding: encoding as BufferEncoding });\n }\n\n const forEachFileIn = async (\n directory: string,\n callback: (file: string) => Promise<void>,\n options: { pattern: string | string[], limit?: number } = { pattern: '*.*' },\n ): Promise<void> => {\n try {\n let filesProcessed = 0;\n const files = await glob(options.pattern, { cwd: directory, nodir: true });\n for (const file of files) {\n await callback(path.join(directory, file));\n filesProcessed++;\n if (options.limit && filesProcessed >= options.limit) {\n log(`Reached limit of ${options.limit} files, stopping`);\n break;\n }\n }\n } catch (err: any) {\n throw new Error(`Failed to glob pattern ${options.pattern} in ${directory}: ${err.message}`);\n }\n }\n\n const readStream = async (path: string): Promise<fs.ReadStream> => {\n return fs.createReadStream(path);\n }\n\n const hashFile = async (path: string, length: number): Promise<string> => {\n const file = await readFile(path, 'utf8');\n return crypto.createHash('sha256').update(file).digest('hex').slice(0, length);\n }\n\n const listFiles = async (directory: string): Promise<string[]> => {\n return await fs.promises.readdir(directory);\n }\n\n return {\n exists,\n isDirectory,\n isFile,\n isReadable,\n isWritable,\n isFileReadable,\n isDirectoryWritable,\n isDirectoryReadable,\n createDirectory,\n readFile,\n readStream,\n writeFile,\n forEachFileIn,\n hashFile,\n listFiles,\n };\n}"],"names":["create","params","log","console","exists","path","fs","promises","stat","error","isDirectory","stats","isFile","isReadable","access","constants","R_OK","message","stack","isWritable","W_OK","isFileReadable","isDirectoryWritable","isDirectoryReadable","createDirectory","mkdir","recursive","mkdirError","Error","readFile","encoding","writeFile","data","forEachFileIn","directory","callback","options","pattern","filesProcessed","files","glob","cwd","nodir","file","join","limit","err","readStream","createReadStream","hashFile","length","crypto","createHash","update","digest","slice","listFiles","readdir"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"storage.js","sources":["../../src/util/storage.ts"],"sourcesContent":["// eslint-disable-next-line no-restricted-imports\nimport * as fs from 'fs';\nimport { glob } from 'glob';\nimport path from 'path';\nimport crypto from 'crypto';\n/**\n * This module exists to isolate filesystem operations from the rest of the codebase.\n * This makes testing easier by avoiding direct fs mocking in jest configuration.\n * \n * Additionally, abstracting storage operations allows for future flexibility - \n * this export utility may need to work with storage systems other than the local filesystem\n * (e.g. S3, Google Cloud Storage, etc).\n */\n\nexport interface Utility {\n exists: (path: string) => Promise<boolean>;\n isDirectory: (path: string) => Promise<boolean>;\n isFile: (path: string) => Promise<boolean>;\n isReadable: (path: string) => Promise<boolean>;\n isWritable: (path: string) => Promise<boolean>;\n isFileReadable: (path: string) => Promise<boolean>;\n isDirectoryWritable: (path: string) => Promise<boolean>;\n isDirectoryReadable: (path: string) => Promise<boolean>;\n createDirectory: (path: string) => Promise<void>;\n readFile: (path: string, encoding: string) => Promise<string>;\n readStream: (path: string) => Promise<fs.ReadStream>;\n writeFile: (path: string, data: string | Buffer, encoding: string) => Promise<void>;\n forEachFileIn: (directory: string, callback: (path: string) => Promise<void>, options?: { pattern: string, limit?: number }) => Promise<void>;\n hashFile: (path: string, length: number) => Promise<string>;\n listFiles: (directory: string) => Promise<string[]>;\n}\n\nexport const create = (params: { log?: (message: string, ...args: any[]) => void }): Utility => {\n\n // eslint-disable-next-line no-console\n const log = params.log || console.log;\n\n const exists = async (path: string): Promise<boolean> => {\n try {\n await fs.promises.stat(path);\n return true;\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (error: any) {\n return false;\n }\n }\n\n const isDirectory = async (path: string): Promise<boolean> => {\n const stats = await fs.promises.stat(path);\n if (!stats.isDirectory()) {\n log(`${path} is not a directory`);\n return false;\n }\n return true;\n }\n\n const isFile = async (path: string): Promise<boolean> => {\n const stats = await fs.promises.stat(path);\n if (!stats.isFile()) {\n log(`${path} is not a file`);\n return false;\n }\n return true;\n }\n\n const isReadable = async (path: string): Promise<boolean> => {\n try {\n await fs.promises.access(path, fs.constants.R_OK);\n } catch (error: any) {\n log(`${path} is not readable: %s %s`, error.message, error.stack);\n return false;\n }\n return true;\n }\n\n const isWritable = async (path: string): Promise<boolean> => {\n try {\n await fs.promises.access(path, fs.constants.W_OK);\n } catch (error: any) {\n log(`${path} is not writable: %s %s`, error.message, error.stack);\n return false;\n }\n return true;\n }\n\n const isFileReadable = async (path: string): Promise<boolean> => {\n return await exists(path) && await isFile(path) && await isReadable(path);\n }\n\n const isDirectoryWritable = async (path: string): Promise<boolean> => {\n return await exists(path) && await isDirectory(path) && await isWritable(path);\n }\n\n const isDirectoryReadable = async (path: string): Promise<boolean> => {\n return await exists(path) && await isDirectory(path) && await isReadable(path);\n }\n\n const createDirectory = async (path: string): Promise<void> => {\n try {\n await fs.promises.mkdir(path, { recursive: true });\n } catch (mkdirError: any) {\n throw new Error(`Failed to create output directory ${path}: ${mkdirError.message} ${mkdirError.stack}`);\n }\n }\n\n const readFile = async (path: string, encoding: string): Promise<string> => {\n return await fs.promises.readFile(path, { encoding: encoding as BufferEncoding });\n }\n\n const writeFile = async (path: string, data: string | Buffer, encoding: string): Promise<void> => {\n await fs.promises.writeFile(path, data, { encoding: encoding as BufferEncoding });\n }\n\n const forEachFileIn = async (\n directory: string,\n callback: (file: string) => Promise<void>,\n options: { pattern: string | string[], limit?: number } = { pattern: '*.*' },\n ): Promise<void> => {\n try {\n let filesProcessed = 0;\n const files = await glob(options.pattern, { cwd: directory, nodir: true });\n for (const file of files) {\n await callback(path.join(directory, file));\n filesProcessed++;\n if (options.limit && filesProcessed >= options.limit) {\n log(`Reached limit of ${options.limit} files, stopping`);\n break;\n }\n }\n } catch (err: any) {\n throw new Error(`Failed to glob pattern ${options.pattern} in ${directory}: ${err.message}`);\n }\n }\n\n const readStream = async (path: string): Promise<fs.ReadStream> => {\n return fs.createReadStream(path);\n }\n\n const hashFile = async (path: string, length: number): Promise<string> => {\n const file = await readFile(path, 'utf8');\n return crypto.createHash('sha256').update(file).digest('hex').slice(0, length);\n }\n\n const listFiles = async (directory: string): Promise<string[]> => {\n return await fs.promises.readdir(directory);\n }\n\n return {\n exists,\n isDirectory,\n isFile,\n isReadable,\n isWritable,\n isFileReadable,\n isDirectoryWritable,\n isDirectoryReadable,\n createDirectory,\n readFile,\n readStream,\n writeFile,\n forEachFileIn,\n hashFile,\n listFiles,\n };\n}"],"names":["create","params","log","console","exists","path","fs","promises","stat","error","isDirectory","stats","isFile","isReadable","access","constants","R_OK","message","stack","isWritable","W_OK","isFileReadable","isDirectoryWritable","isDirectoryReadable","createDirectory","mkdir","recursive","mkdirError","Error","readFile","encoding","writeFile","data","forEachFileIn","directory","callback","options","pattern","filesProcessed","files","glob","cwd","nodir","file","join","limit","err","readStream","createReadStream","hashFile","length","crypto","createHash","update","digest","slice","listFiles","readdir"],"mappings":"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"}
|
package/dist/util/text.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","sources":["../../src/util/text.ts"],"sourcesContent":["import { DEFAULT_CHARACTER_ENCODING } from \"../constants\";\n\n// Returns true if the input is likely text, false if likely binary\nexport function isText(input: string | Buffer): boolean {\n let buf: Buffer;\n if (typeof input === 'string') {\n buf = Buffer.from(input, DEFAULT_CHARACTER_ENCODING);\n } else {\n buf = input;\n }\n\n // Empty buffers are considered text\n if (buf.length === 0) {\n return true;\n }\n\n // If the buffer contains null bytes, it's likely binary\n if (buf.includes(0)) {\n return false;\n }\n\n // For UTF-8 encoded text (including emoji and international characters),\n // convert to string first and check if there are non-printable characters\n const str = buf.toString(DEFAULT_CHARACTER_ENCODING);\n\n // Count the number of non-printable ASCII characters (excluding common whitespace)\n let nonPrintable = 0;\n const len = Math.min(str.length, 512); // Only check the first 512 characters for performance\n\n for (let i = 0; i < len; i++) {\n const charCode = str.charCodeAt(i);\n // Allow: tab (9), line feed (10), carriage return (13), printable ASCII (32-126)\n // Also allow all non-ASCII Unicode characters (charCode > 127)\n if (\n charCode !== 9 && charCode !== 10 && charCode !== 13 &&\n (charCode < 32 || (charCode > 126 && charCode < 128))\n ) {\n nonPrintable++;\n }\n }\n\n // If more than 10% of the checked characters are non-printable, consider it binary\n return nonPrintable / len < 0.1;\n}\n"],"names":["isText","input","buf","Buffer","from","DEFAULT_CHARACTER_ENCODING","length","includes","str","toString","nonPrintable","len","Math","min","i","charCode","charCodeAt"],"mappings":";;AAEA;AACO,SAASA,OAAOC,KAAsB,EAAA;IACzC,IAAIC,GAAAA;IACJ,IAAI,OAAOD,UAAU,
|
|
1
|
+
{"version":3,"file":"text.js","sources":["../../src/util/text.ts"],"sourcesContent":["import { DEFAULT_CHARACTER_ENCODING } from \"../constants\";\n\n// Returns true if the input is likely text, false if likely binary\nexport function isText(input: string | Buffer): boolean {\n let buf: Buffer;\n if (typeof input === 'string') {\n buf = Buffer.from(input, DEFAULT_CHARACTER_ENCODING);\n } else {\n buf = input;\n }\n\n // Empty buffers are considered text\n if (buf.length === 0) {\n return true;\n }\n\n // If the buffer contains null bytes, it's likely binary\n if (buf.includes(0)) {\n return false;\n }\n\n // For UTF-8 encoded text (including emoji and international characters),\n // convert to string first and check if there are non-printable characters\n const str = buf.toString(DEFAULT_CHARACTER_ENCODING);\n\n // Count the number of non-printable ASCII characters (excluding common whitespace)\n let nonPrintable = 0;\n const len = Math.min(str.length, 512); // Only check the first 512 characters for performance\n\n for (let i = 0; i < len; i++) {\n const charCode = str.charCodeAt(i);\n // Allow: tab (9), line feed (10), carriage return (13), printable ASCII (32-126)\n // Also allow all non-ASCII Unicode characters (charCode > 127)\n if (\n charCode !== 9 && charCode !== 10 && charCode !== 13 &&\n (charCode < 32 || (charCode > 126 && charCode < 128))\n ) {\n nonPrintable++;\n }\n }\n\n // If more than 10% of the checked characters are non-printable, consider it binary\n return nonPrintable / len < 0.1;\n}\n"],"names":["isText","input","buf","Buffer","from","DEFAULT_CHARACTER_ENCODING","length","includes","str","toString","nonPrintable","len","Math","min","i","charCode","charCodeAt"],"mappings":";;AAEA;AACO,SAASA,OAAOC,KAAsB,EAAA;IACzC,IAAIC,GAAAA;IACJ,IAAI,OAAOD,UAAU,QAAA,EAAU;QAC3BC,GAAAA,GAAMC,MAAAA,CAAOC,IAAI,CAACH,KAAAA,EAAOI,0BAAAA,CAAAA;KAC7B,MAAO;QACHH,GAAAA,GAAMD,KAAAA;AACV;;IAGA,IAAIC,GAAAA,CAAII,MAAM,KAAK,CAAA,EAAG;QAClB,OAAO,IAAA;AACX;;IAGA,IAAIJ,GAAAA,CAAIK,QAAQ,CAAC,CAAA,CAAA,EAAI;QACjB,OAAO,KAAA;AACX;;;IAIA,MAAMC,GAAAA,GAAMN,GAAAA,CAAIO,QAAQ,CAACJ,0BAAAA,CAAAA;;AAGzB,IAAA,IAAIK,YAAAA,GAAe,CAAA;IACnB,MAAMC,GAAAA,GAAMC,KAAKC,GAAG,CAACL,IAAIF,MAAM,EAAE;AAEjC,IAAA,IAAK,IAAIQ,CAAAA,GAAI,CAAA,EAAGA,CAAAA,GAAIH,KAAKG,CAAAA,EAAAA,CAAK;QAC1B,MAAMC,QAAAA,GAAWP,GAAAA,CAAIQ,UAAU,CAACF,CAAAA,CAAAA;;;AAGhC,QAAA,IACIC,QAAAA,KAAa,CAAA,IAAKA,QAAAA,KAAa,EAAA,IAAMA,QAAAA,KAAa,EAAA,KACjDA,QAAAA,GAAW,EAAA,IAAOA,QAAAA,GAAW,GAAA,IAAOA,QAAAA,GAAW,GAAG,CAAA,EACrD;AACEL,YAAAA,YAAAA,EAAAA;AACJ;AACJ;;AAGA,IAAA,OAAOA,eAAeC,GAAAA,GAAM,GAAA;AAChC;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riotprompt/riotprompt",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Get it together, and organize your prompts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/riotprompt.cjs",
|
|
@@ -26,49 +26,54 @@
|
|
|
26
26
|
"author": "St. Just Reckoning <StJustReckoning@proton.me>",
|
|
27
27
|
"license": "Apache-2.0",
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@babel/core": "^7.
|
|
29
|
+
"@babel/core": "^7.28.0",
|
|
30
30
|
"@babel/plugin-transform-modules-commonjs": "^7.27.1",
|
|
31
|
-
"@babel/plugin-transform-runtime": "^7.
|
|
32
|
-
"@babel/plugin-transform-typescript": "^7.
|
|
31
|
+
"@babel/plugin-transform-runtime": "^7.28.0",
|
|
32
|
+
"@babel/plugin-transform-typescript": "^7.28.0",
|
|
33
33
|
"@babel/preset-typescript": "^7.27.1",
|
|
34
|
-
"@babel/runtime": "^7.27.
|
|
34
|
+
"@babel/runtime": "^7.27.6",
|
|
35
35
|
"@doccident/doccident": "^0.0.1",
|
|
36
36
|
"@eslint/eslintrc": "^3.3.1",
|
|
37
|
-
"@eslint/js": "^9.
|
|
37
|
+
"@eslint/js": "^9.30.1",
|
|
38
38
|
"@rollup/plugin-replace": "^6.0.2",
|
|
39
|
-
"@swc/core": "^1.
|
|
40
|
-
"@types/node": "^
|
|
41
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
42
|
-
"@typescript-eslint/parser": "^8.
|
|
43
|
-
"@vitest/coverage-v8": "^3.
|
|
39
|
+
"@swc/core": "^1.12.9",
|
|
40
|
+
"@types/node": "^24.0.10",
|
|
41
|
+
"@typescript-eslint/eslint-plugin": "^8.35.1",
|
|
42
|
+
"@typescript-eslint/parser": "^8.35.1",
|
|
43
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
44
44
|
"ajv": "^8.17.1",
|
|
45
|
-
"eslint": "^9.
|
|
46
|
-
"eslint-plugin-import": "^2.
|
|
47
|
-
"globals": "^16.
|
|
48
|
-
"minimatch": "^10.0.
|
|
45
|
+
"eslint": "^9.30.1",
|
|
46
|
+
"eslint-plugin-import": "^2.32.0",
|
|
47
|
+
"globals": "^16.3.0",
|
|
48
|
+
"minimatch": "^10.0.3",
|
|
49
49
|
"rollup-plugin-preserve-shebang": "^1.0.1",
|
|
50
50
|
"typescript": "^5.8.3",
|
|
51
|
-
"vite": "^
|
|
51
|
+
"vite": "^7.0.1",
|
|
52
52
|
"vite-plugin-dts": "^4.5.4",
|
|
53
|
-
"vite-plugin-node": "^
|
|
54
|
-
"vitest": "^3.
|
|
53
|
+
"vite-plugin-node": "^7.0.0",
|
|
54
|
+
"vitest": "^3.2.4"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"glob": "^11.0.
|
|
58
|
-
"marked": "^
|
|
59
|
-
"zod": "^3.25.
|
|
57
|
+
"glob": "^11.0.3",
|
|
58
|
+
"marked": "^16.0.0",
|
|
59
|
+
"zod": "^3.25.71"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
|
-
"build": "vite build",
|
|
62
|
+
"build": "pnpm run lint && tsc --noEmit && vite build",
|
|
63
63
|
"start": "dist/main.js",
|
|
64
64
|
"dev": "vite",
|
|
65
65
|
"watch": "vite build --watch",
|
|
66
|
-
"test": "pnpm run test:coverage
|
|
66
|
+
"test": "pnpm run test:coverage",
|
|
67
67
|
"test:coverage": "vitest run --coverage",
|
|
68
68
|
"test:debug": "vitest --run --coverage --reporter verbose",
|
|
69
69
|
"test:readme": "doccident -c .markdown-doctest-setup.mjs README.md",
|
|
70
70
|
"lint": "eslint . --ext .ts",
|
|
71
71
|
"lint:fix": "eslint . --ext .ts --fix",
|
|
72
|
-
"clean": "rm -rf dist"
|
|
72
|
+
"clean": "rm -rf dist",
|
|
73
|
+
"docs:dev": "cd docs && cp ../README.md public/ && pnpm install && pnpm run dev",
|
|
74
|
+
"docs:build": "cd docs && cp ../README.md public/ && pnpm install && pnpm run build",
|
|
75
|
+
"docs:preview": "cd docs && pnpm run preview",
|
|
76
|
+
"docs:test": "cd docs && pnpm run test",
|
|
77
|
+
"docs:coverage": "cd docs && pnpm run coverage"
|
|
73
78
|
}
|
|
74
79
|
}
|
package/.gitcarve/config.yaml
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
riotprompt helps you capture and create Prompts.
|
|
2
|
-
|
|
3
|
-
RiotPrompt was designed to help people create prompts that are structured.
|
|
4
|
-
|
|
5
|
-
To use riotprompt you create Sections that contant Instructions, Context, and Content to create a Prompt that is structured.
|
|
6
|
-
|
|
7
|
-
riotprompt has a Parser that can read sections of Instructions that are stored in Markdown, and it also has a Formatter that is used to generate content in either Markdown or in a format that uses tags that resemble something like XML.
|
|
8
|
-
|
|
9
|
-
riotprompt is written in Typescript and uses Jest for testing and Vite and SWC to build the library. riotprompt uses the "marked" library to parse Markdown.
|
|
10
|
-
|
|
11
|
-
riotprompt has a GitHub repository here https://github.com/riotprompt
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { transform } from '@babel/core';
|
|
2
|
-
// eslint-disable-next-line import/extensions
|
|
3
|
-
import * as riotprompt from './dist/riotprompt.js';
|
|
4
|
-
|
|
5
|
-
export default {
|
|
6
|
-
"require": {
|
|
7
|
-
'@riotprompt': riotprompt
|
|
8
|
-
},
|
|
9
|
-
transformCode: (code) => {
|
|
10
|
-
// transform the code using @bable/preset-typescript
|
|
11
|
-
const transformedCode = transform(code, {
|
|
12
|
-
filename: 'test.ts',
|
|
13
|
-
presets: ['@babel/preset-typescript'],
|
|
14
|
-
plugins: [
|
|
15
|
-
'@babel/plugin-transform-typescript',
|
|
16
|
-
'@babel/plugin-transform-modules-commonjs'
|
|
17
|
-
],
|
|
18
|
-
comments: true // Preserve comments
|
|
19
|
-
})?.code;
|
|
20
|
-
|
|
21
|
-
return transformedCode;
|
|
22
|
-
}
|
|
23
|
-
}
|
package/.nvmrc
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
v22.0.0
|
package/docs/loader.md
DELETED
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
# riotprompt Loader Utility
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
The **Loader** utility in riotprompt is designed to import prompt content from the filesystem. It can take a directory (or multiple directories) of Markdown files and convert them into riotprompt sections or items. This is especially useful for managing large or modular prompt structures, where different parts of the prompt (personas, instructions, context, etc.) are kept in separate files for clarity and reusability.
|
|
6
|
-
|
|
7
|
-
By using the Loader, you can:
|
|
8
|
-
|
|
9
|
-
* **Organize Prompts as Files**: Keep complex prompts as separate markdown files (e.g., one file per persona, or per context topic).
|
|
10
|
-
* **Reuse Content**: Share and reuse prompt segments across projects by simply loading the files.
|
|
11
|
-
* **Keep Code and Prompts Separate**: Your application code can load prompt content at runtime, making it easier to update prompts without changing code.
|
|
12
|
-
* **Scale to Many Context Files**: Effortlessly include a large number of context snippets or knowledge base files as part of the prompt by placing them in a context directory and loading them all.
|
|
13
|
-
|
|
14
|
-
> **NOTE:** This was originally designed to support loading context from a directory of Markdown files. For example, if you are analyzing emails, you might want to have a directory named "./context/projects" that contains a series of Markdown files that give context about different projects.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
## How the Loader Transforms Files
|
|
18
|
-
|
|
19
|
-
The Loader works together with the Parser under the hood. When you point the Loader at a directory, it will:
|
|
20
|
-
|
|
21
|
-
1. **Scan the Directory**: It finds all files in the specified directory (not including subdirectories). The Loader is agnostic about file extensions and processes all files in the directory.
|
|
22
|
-
2. **Read each File**: For each file found, the Loader reads its content from disk.
|
|
23
|
-
3. **Parse Content into Sections**: If any of the files are Markdown (ending in .md), the Loader uses the Parser to convert the Markdown content of the file into a Section (or items). Crucially, the Loader will strip out the first heading from the file from the content and use it as the section title if a heading is present. If a particular file does not contain Markdown, a section will be added for the file with the name of the file as the Section title.
|
|
24
|
-
|
|
25
|
-
* If the file content begins with a heading (e.g., `# Title`), that heading is used as the Section's title and is not duplicated in the Section's items.
|
|
26
|
-
* The rest of the file's content becomes the body (items) of that Section.
|
|
27
|
-
* If a file does not contain an explicit top-level heading, the Loader can optionally use the filename (or a provided default) as the section title, or simply treat all content as belonging to an unnamed section.
|
|
28
|
-
4. **Aggregate or Return Sections**: The Loader then returns the structured content from all files. You can choose to combine these sections into a larger Section (for example, a single "Context" section containing multiple subsections), or handle them individually.
|
|
29
|
-
|
|
30
|
-
This approach means you can, for example, have a directory of background context files and load them all at once, without manually copying and pasting their content into one big prompt string.
|
|
31
|
-
|
|
32
|
-
## Using the Loader
|
|
33
|
-
|
|
34
|
-
Typically, you'll create a Loader instance via its factory method and then call methods to load files or directories. For example:
|
|
35
|
-
|
|
36
|
-
```ts
|
|
37
|
-
import { Loader, Section } from '@riotprompt';
|
|
38
|
-
|
|
39
|
-
// Create a Loader instance
|
|
40
|
-
const loader = Loader.create();
|
|
41
|
-
|
|
42
|
-
// Load all markdown files from a directory (e.g., the "context" folder)
|
|
43
|
-
const contextSections: Section<Context>[] = await loader.load<Context>(['./prompts/context']);
|
|
44
|
-
|
|
45
|
-
// contextSections is an array of Section objects, one per markdown file in the directory.
|
|
46
|
-
console.log(`Loaded ${contextSections.length} context sections.`);
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
In this snippet, `loader.load<Context>(['./prompts/context'])` will find all markdown files under `./prompts/context`. Suppose the folder structure is:
|
|
50
|
-
|
|
51
|
-
```
|
|
52
|
-
prompts/
|
|
53
|
-
context/
|
|
54
|
-
project-alpha.md
|
|
55
|
-
project-beta.md
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
And the files contain:
|
|
59
|
-
|
|
60
|
-
* **project-alpha.md**:
|
|
61
|
-
|
|
62
|
-
```
|
|
63
|
-
# Project Alpha
|
|
64
|
-
|
|
65
|
-
Details about Project Alpha...
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
* **project-beta.md**:
|
|
69
|
-
|
|
70
|
-
```
|
|
71
|
-
# Project Beta
|
|
72
|
-
|
|
73
|
-
Information on Project Beta...
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
After running the loader:
|
|
77
|
-
|
|
78
|
-
* `contextSections[0]` will be a Section titled "Project Alpha", containing the text "Details about Project Alpha..." as its content (as an item in that section).
|
|
79
|
-
* `contextSections[1]` will be a Section titled "Project Beta", with "Information on Project Beta..." as its content.
|
|
80
|
-
|
|
81
|
-
Each file became its own Section, using the first line header as the title.
|
|
82
|
-
|
|
83
|
-
### Loading Multiple Directories or Mixed Content
|
|
84
|
-
|
|
85
|
-
You can call `load` multiple times or even use Loader methods to load individual files. The riotprompt **Builder** provides higher-level methods (like `loadContext` or `loadContent`) which can take an array of directories or file paths and internally use the Loader to gather them.
|
|
86
|
-
|
|
87
|
-
For example, using the Builder (which internally leverages the Loader):
|
|
88
|
-
|
|
89
|
-
```ts
|
|
90
|
-
import { Builder, Prompt } from '@riotprompt';
|
|
91
|
-
|
|
92
|
-
const builder = Builder.create({
|
|
93
|
-
basePath: './prompts'
|
|
94
|
-
});
|
|
95
|
-
const prompt: Prompt = await builder
|
|
96
|
-
.addInstructionPath('instructions/code-review.md')
|
|
97
|
-
.loadContext(['./prompts/context', './prompts/additional-context'])
|
|
98
|
-
.build();
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
In this example, `builder.loadContext([...])` will use the Loader to load all Markdown files from the two specified directories (`./prompts/context` and `./prompts/additional-context`), combining them appropriately into the prompt's context section. You don't have to manually instantiate `Loader` here because `Builder` does it for you.
|
|
102
|
-
|
|
103
|
-
However, if you want fine-grained control, you can use `Loader` directly. For instance, you might want to load a directory of context files, filter or modify them, and then insert them into different parts of the prompt.
|
|
104
|
-
|
|
105
|
-
> NOTE: The Builder example above uses a single `basePath` for simplicity. In most real-world use cases, you'll likely want to include an `overridePath` as well, which allows for customization of prompts without modifying the original files.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
**Example – Direct Loader usage:**
|
|
109
|
-
|
|
110
|
-
```ts
|
|
111
|
-
const loader = Loader.create();
|
|
112
|
-
// Load persona definitions from a directory
|
|
113
|
-
const personaSections: Section<Instruction>[] = await loader.load<Instruction>(['./prompts/personas']);
|
|
114
|
-
// Load context files from multiple directories
|
|
115
|
-
const peopleContext: Section<Context>[] = await loader.load<Context>(['./prompts/context/people']);
|
|
116
|
-
const projectContext: Section<Context>[] = await loader.load<Context>(['./prompts/context/projects']);
|
|
117
|
-
|
|
118
|
-
// You can now, for example, combine these:
|
|
119
|
-
const combinedContext = createSection<Context>("Context");
|
|
120
|
-
for (const sec of [...peopleContext, ...projectContext]) {
|
|
121
|
-
combinedContext.add(sec);
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
In this example, we manually loaded personas and context from different folders and then merged the context sections into a single Section called "Context". (Note: riotprompt might provide utility methods for combining sections, but doing it manually as shown is also straightforward.)
|
|
126
|
-
|
|
127
|
-
### Example Folder Structure
|
|
128
|
-
|
|
129
|
-
To illustrate a larger setup, imagine your `./prompts` directory is organized as follows:
|
|
130
|
-
|
|
131
|
-
```
|
|
132
|
-
prompts/
|
|
133
|
-
personas/
|
|
134
|
-
developer.md
|
|
135
|
-
manager.md
|
|
136
|
-
instructions/
|
|
137
|
-
code-review.md
|
|
138
|
-
summarize.md
|
|
139
|
-
content/
|
|
140
|
-
example-request.md
|
|
141
|
-
context/
|
|
142
|
-
project/
|
|
143
|
-
alpha.md
|
|
144
|
-
beta.md
|
|
145
|
-
team/
|
|
146
|
-
team-info.md
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
* **Personas** directory contains persona profiles (e.g., "developer" persona, "manager" persona).
|
|
150
|
-
* **Instructions** directory contains different sets of instructions (for different tasks like code review, summarization).
|
|
151
|
-
* **Content** might contain user prompts or example content pieces.
|
|
152
|
-
* **Context** is further divided into subfolders (e.g., project-specific context files, team info files).
|
|
153
|
-
|
|
154
|
-
Using Loader (via Builder or directly), you can easily load all relevant pieces:
|
|
155
|
-
|
|
156
|
-
* `loader.load<Instruction>(['./prompts/personas'])` yields an array of persona Sections.
|
|
157
|
-
* `loader.load<Instruction>(['./prompts/instructions'])` yields Sections for each set of instructions.
|
|
158
|
-
* `loader.load<Context>(['./prompts/context/project'])` and `loader.load<Context>(['./prompts/context/team'])` yield context Sections which you might then combine.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
### Loader Options
|
|
163
|
-
|
|
164
|
-
The Loader's `create` method may allow some configuration as well. Common configurations include:
|
|
165
|
-
|
|
166
|
-
* **File Extensions**: The Loader is agnostic about file extensions and will include all files in the specified directory. There is one special file, `content.md`, which is parsed first and supplies instructions for the section that will contain sections for each of the included files. Additionally, any file with a `.md` extension will have its first header parsed to set the title of the resulting Section. You do not need to configure file extensions; the Loader handles all files appropriately based on their extension.
|
|
167
|
-
* **Non-Recursive Directory Scanning**: The `load()` method only processes files in the immediate directories specified in the array. It does not automatically traverse subdirectories. If you need to process files in subdirectories, you must explicitly include those subdirectory paths in the array passed to `load()`. For example, to load files from both `./prompts/context/project` and `./prompts/context/team`, you would need to call `load(['./prompts/context/project', './prompts/context/team'])`.
|
|
168
|
-
* **Ignore Patterns**: You can provide an array of regular expression strings via the `ignorePatterns` option in the `Options` interface. Files matching any of these patterns will be excluded from processing. This is useful for ignoring hidden files, temporary files, or specific file types you don't want to load. By default, the Loader uses the following patterns to ignore common non-content files:
|
|
169
|
-
* `^\\..*`: Ignores hidden files (e.g., `.git`, `.DS_Store`).
|
|
170
|
-
* `\\.(jpg|jpeg|png|gif|bmp|svg|webp|ico)$`: Ignores common image file extensions.
|
|
171
|
-
* `\\.(mp3|wav|ogg|aac|flac)$`: Ignores common audio file extensions.
|
|
172
|
-
* `\\.(mp4|mov|avi|mkv|webm)$`: Ignores common video file extensions.
|
|
173
|
-
* `\\.(pdf|doc|docx|xls|xlsx|ppt|pptx)$`: Ignores common document file formats that are typically binary.
|
|
174
|
-
* `\\.(zip|tar|gz|rar|7z)$`: Ignores common compressed file extensions.
|
|
175
|
-
You can override these defaults by passing your own array of regex strings to `ignorePatterns`.
|
|
176
|
-
* **Parameters**: Similar to the Parser, the Loader might accept a `Parameters` object if you want to substitute placeholders in all loaded files. Another approach is to pass `parameters` into the Builder (so that after loading, when formatting, those parameters are applied).
|
|
177
|
-
|
|
178
|
-
### Parameterized Content Loading
|
|
179
|
-
|
|
180
|
-
The Loader supports parameterized content through the `parameters` option in the `Options` interface. This allows you to define variables in your markdown files and have them replaced with actual values when the content is loaded.
|
|
181
|
-
|
|
182
|
-
Here's how to use this feature:
|
|
183
|
-
|
|
184
|
-
```ts
|
|
185
|
-
import { Loader, Section } from '@riotprompt';
|
|
186
|
-
|
|
187
|
-
// Create a Loader with parameters
|
|
188
|
-
const loader = Loader.create({
|
|
189
|
-
parameters: {
|
|
190
|
-
projectName: "Alpha Project",
|
|
191
|
-
clientName: "Acme Corporation",
|
|
192
|
-
deadline: "December 31, 2023"
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
// Load content that contains variables
|
|
197
|
-
const contextSections: Section<Context>[] = await loader.load<Context>(['./prompts/context']);
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
In this example, any markdown file in the `./prompts/context` directory can include variables like `{{projectName}}`, `{{clientName}}`, or `{{deadline}}`, and they will be replaced with the corresponding values when loaded.
|
|
201
|
-
|
|
202
|
-
For instance, if one of your files contains:
|
|
203
|
-
|
|
204
|
-
```markdown
|
|
205
|
-
# {{projectName}} Overview
|
|
206
|
-
|
|
207
|
-
This document provides key information about {{projectName}} for {{clientName}}.
|
|
208
|
-
The project has a delivery deadline of {{deadline}}.
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
After loading, this content would be transformed to:
|
|
212
|
-
|
|
213
|
-
```markdown
|
|
214
|
-
# Alpha Project Overview
|
|
215
|
-
|
|
216
|
-
This document provides key information about Alpha Project for Acme Corporation.
|
|
217
|
-
The project has a delivery deadline of December 31, 2023.
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
This parameterization feature is extremely useful for:
|
|
221
|
-
|
|
222
|
-
* Creating reusable template prompts
|
|
223
|
-
* Injecting dynamic values into your prompts at runtime
|
|
224
|
-
* Maintaining a single source of truth for key information across multiple prompt files
|
|
225
|
-
|
|
226
|
-
You can change the parameters for each Loader instance, allowing you to generate different versions of the same prompt templates for different scenarios.
|
|
227
|
-
|
|
228
|
-
### Purpose and Best Practices
|
|
229
|
-
|
|
230
|
-
Using Loader helps manage large prompts by breaking them into maintainable pieces:
|
|
231
|
-
|
|
232
|
-
* **Separation of Concerns**: Keep each logical part of the prompt in its own file (e.g., persona definitions, instructional prompts, background context). This makes editing and reviewing prompts easier.
|
|
233
|
-
* **Dynamic Inclusion**: You can decide at runtime which files or directories to load. For instance, load different instruction sets based on user input or context (by pointing Loader to different folders).
|
|
234
|
-
* **Reusability**: If multiple applications or multiple parts of your application need the same prompt content, having them in files that the Loader can pull in avoids duplication in code.
|
|
235
|
-
|
|
236
|
-
In summary, the Loader utility bridges the gap between your prompt files on disk and the in-memory structures that riotprompt uses. It handles the heavy lifting of reading files and parsing Markdown, so you can assemble your final prompt by simply organizing files and calling load functions. This results in a cleaner project structure and easier prompt maintenance.
|
|
237
|
-
|