prompts.chat 0.0.1 → 0.0.2

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 (46) hide show
  1. package/README.md +877 -0
  2. package/dist/builder/index.d.mts +1067 -0
  3. package/dist/builder/index.d.ts +1067 -0
  4. package/dist/builder/index.js +2471 -0
  5. package/dist/builder/index.js.map +1 -0
  6. package/dist/builder/index.mjs +2432 -0
  7. package/dist/builder/index.mjs.map +1 -0
  8. package/dist/index-BEIO8LCd.d.mts +61 -0
  9. package/dist/index-BEIO8LCd.d.ts +61 -0
  10. package/dist/index-CSHEKYfQ.d.mts +64 -0
  11. package/dist/index-CSHEKYfQ.d.ts +64 -0
  12. package/dist/index-D41E6D9X.d.mts +77 -0
  13. package/dist/index-D41E6D9X.d.ts +77 -0
  14. package/dist/index-DOz8zcA0.d.mts +68 -0
  15. package/dist/index-DOz8zcA0.d.ts +68 -0
  16. package/dist/index.d.mts +5 -0
  17. package/dist/index.d.ts +5 -0
  18. package/dist/index.js +3335 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/index.mjs +3276 -0
  21. package/dist/index.mjs.map +1 -0
  22. package/dist/parser/index.d.mts +1 -0
  23. package/dist/parser/index.d.ts +1 -0
  24. package/dist/parser/index.js +288 -0
  25. package/dist/parser/index.js.map +1 -0
  26. package/dist/parser/index.mjs +259 -0
  27. package/dist/parser/index.mjs.map +1 -0
  28. package/dist/quality/index.d.mts +1 -0
  29. package/dist/quality/index.d.ts +1 -0
  30. package/dist/quality/index.js +212 -0
  31. package/dist/quality/index.js.map +1 -0
  32. package/dist/quality/index.mjs +184 -0
  33. package/dist/quality/index.mjs.map +1 -0
  34. package/dist/similarity/index.d.mts +1 -0
  35. package/dist/similarity/index.d.ts +1 -0
  36. package/dist/similarity/index.js +123 -0
  37. package/dist/similarity/index.js.map +1 -0
  38. package/dist/similarity/index.mjs +91 -0
  39. package/dist/similarity/index.mjs.map +1 -0
  40. package/dist/variables/index.d.mts +1 -0
  41. package/dist/variables/index.d.ts +1 -0
  42. package/dist/variables/index.js +306 -0
  43. package/dist/variables/index.js.map +1 -0
  44. package/dist/variables/index.mjs +274 -0
  45. package/dist/variables/index.mjs.map +1 -0
  46. package/package.json +77 -6
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/quality/index.ts
21
+ var quality_exports = {};
22
+ __export(quality_exports, {
23
+ check: () => check,
24
+ getSuggestions: () => getSuggestions,
25
+ isValid: () => isValid,
26
+ validate: () => validate
27
+ });
28
+ module.exports = __toCommonJS(quality_exports);
29
+ var MIN_CHAR_COUNT = 20;
30
+ var MIN_WORD_COUNT = 5;
31
+ var OPTIMAL_MIN_WORDS = 20;
32
+ var OPTIMAL_MAX_WORDS = 2e3;
33
+ function isGibberish(text) {
34
+ if (/(.)\1{4,}/.test(text)) return true;
35
+ const keyboardPatterns = ["qwerty", "asdfgh", "zxcvbn", "qwertz", "azerty"];
36
+ const lower = text.toLowerCase();
37
+ if (keyboardPatterns.some((p) => lower.includes(p))) return true;
38
+ const vowels = (text.match(/[aeiouAEIOU]/g) || []).length;
39
+ const consonants = (text.match(/[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]/g) || []).length;
40
+ if (consonants > 0 && vowels / consonants < 0.1) return true;
41
+ return false;
42
+ }
43
+ function detectPatterns(text) {
44
+ const lower = text.toLowerCase();
45
+ return {
46
+ hasRole: /\b(act as|you are|imagine you|pretend to be|role:|persona:)\b/i.test(text),
47
+ hasTask: /\b(your task|you (will|should|must)|please|help me|i need|i want you to)\b/i.test(text),
48
+ hasConstraints: /\b(do not|don't|never|always|must|should not|avoid|only|limit)\b/i.test(text) || /\b(rule|constraint|requirement|guideline)\b/i.test(lower),
49
+ hasExamples: /\b(example|for instance|such as|e\.g\.|like this)\b/i.test(lower) || /```[\s\S]*```/.test(text)
50
+ };
51
+ }
52
+ function countVariables(text) {
53
+ const patterns = [
54
+ /\$\{[^}]+\}/g,
55
+ // ${var}
56
+ /\{\{[^}]+\}\}/g,
57
+ // {{var}}
58
+ /\[\[[^\]]+\]\]/g,
59
+ // [[var]]
60
+ /\[[A-Z][A-Z0-9_\s]*\]/g
61
+ // [VAR]
62
+ ];
63
+ let count = 0;
64
+ for (const pattern of patterns) {
65
+ const matches = text.match(pattern);
66
+ if (matches) count += matches.length;
67
+ }
68
+ return count;
69
+ }
70
+ function calculateScore(stats, issues) {
71
+ let score = 1;
72
+ const errors = issues.filter((i) => i.type === "error").length;
73
+ const warnings = issues.filter((i) => i.type === "warning").length;
74
+ score -= errors * 0.2;
75
+ score -= warnings * 0.05;
76
+ if (stats.hasRole) score += 0.05;
77
+ if (stats.hasTask) score += 0.05;
78
+ if (stats.hasConstraints) score += 0.03;
79
+ if (stats.hasExamples) score += 0.05;
80
+ if (stats.wordCount < OPTIMAL_MIN_WORDS) {
81
+ score -= 0.1 * (1 - stats.wordCount / OPTIMAL_MIN_WORDS);
82
+ }
83
+ if (stats.wordCount > OPTIMAL_MAX_WORDS) {
84
+ score -= 0.05;
85
+ }
86
+ if (stats.variableCount > 0) {
87
+ score += 0.05;
88
+ }
89
+ return Math.max(0, Math.min(1, score));
90
+ }
91
+ function check(prompt) {
92
+ const issues = [];
93
+ const trimmed = prompt.trim();
94
+ const characterCount = trimmed.length;
95
+ const words = trimmed.split(/\s+/).filter((w) => w.length > 0);
96
+ const wordCount = words.length;
97
+ const sentenceCount = (trimmed.match(/[.!?]+/g) || []).length || 1;
98
+ const variableCount = countVariables(trimmed);
99
+ const patterns = detectPatterns(trimmed);
100
+ if (characterCount === 0) {
101
+ issues.push({
102
+ type: "error",
103
+ code: "EMPTY",
104
+ message: "Prompt is empty"
105
+ });
106
+ } else if (characterCount < MIN_CHAR_COUNT) {
107
+ issues.push({
108
+ type: "error",
109
+ code: "TOO_SHORT",
110
+ message: `Prompt is too short (${characterCount} chars, minimum ${MIN_CHAR_COUNT})`
111
+ });
112
+ }
113
+ if (wordCount > 0 && wordCount < MIN_WORD_COUNT) {
114
+ issues.push({
115
+ type: "warning",
116
+ code: "FEW_WORDS",
117
+ message: `Prompt has very few words (${wordCount} words, recommended ${OPTIMAL_MIN_WORDS}+)`
118
+ });
119
+ }
120
+ if (isGibberish(trimmed)) {
121
+ issues.push({
122
+ type: "error",
123
+ code: "GIBBERISH",
124
+ message: "Prompt appears to contain gibberish or random characters"
125
+ });
126
+ }
127
+ if (!patterns.hasTask && !patterns.hasRole) {
128
+ issues.push({
129
+ type: "suggestion",
130
+ code: "NO_CLEAR_INSTRUCTION",
131
+ message: "Consider adding a clear task or role definition"
132
+ });
133
+ }
134
+ const brackets = [
135
+ { open: "{", close: "}" },
136
+ { open: "[", close: "]" },
137
+ { open: "(", close: ")" }
138
+ ];
139
+ for (const { open, close } of brackets) {
140
+ const openCount = (trimmed.match(new RegExp(`\\${open}`, "g")) || []).length;
141
+ const closeCount = (trimmed.match(new RegExp(`\\${close}`, "g")) || []).length;
142
+ if (openCount !== closeCount) {
143
+ issues.push({
144
+ type: "warning",
145
+ code: "UNBALANCED_BRACKETS",
146
+ message: `Unbalanced ${open}${close} brackets (${openCount} open, ${closeCount} close)`
147
+ });
148
+ }
149
+ }
150
+ const lines = trimmed.split("\n");
151
+ const longLines = lines.filter((l) => l.length > 500);
152
+ if (longLines.length > 0) {
153
+ issues.push({
154
+ type: "suggestion",
155
+ code: "LONG_LINES",
156
+ message: "Some lines are very long. Consider breaking them up for readability."
157
+ });
158
+ }
159
+ const stats = {
160
+ characterCount,
161
+ wordCount,
162
+ sentenceCount,
163
+ variableCount,
164
+ ...patterns
165
+ };
166
+ const score = calculateScore(stats, issues);
167
+ const hasErrors = issues.some((i) => i.type === "error");
168
+ return {
169
+ valid: !hasErrors,
170
+ score,
171
+ issues,
172
+ stats
173
+ };
174
+ }
175
+ function validate(prompt) {
176
+ const result = check(prompt);
177
+ if (!result.valid) {
178
+ const errors = result.issues.filter((i) => i.type === "error").map((i) => i.message).join("; ");
179
+ throw new Error(`Invalid prompt: ${errors}`);
180
+ }
181
+ }
182
+ function isValid(prompt) {
183
+ return check(prompt).valid;
184
+ }
185
+ function getSuggestions(prompt) {
186
+ const result = check(prompt);
187
+ const suggestions = [];
188
+ suggestions.push(
189
+ ...result.issues.filter((i) => i.type === "suggestion" || i.type === "warning").map((i) => i.message)
190
+ );
191
+ if (!result.stats.hasRole) {
192
+ suggestions.push('Add a role definition (e.g., "Act as a...")');
193
+ }
194
+ if (!result.stats.hasConstraints && result.stats.wordCount > 50) {
195
+ suggestions.push("Consider adding constraints or rules for better control");
196
+ }
197
+ if (!result.stats.hasExamples && result.stats.wordCount > 100) {
198
+ suggestions.push("Adding examples can improve output quality");
199
+ }
200
+ if (result.stats.variableCount === 0 && result.stats.wordCount > 30) {
201
+ suggestions.push("Consider adding variables (${var}) to make the prompt reusable");
202
+ }
203
+ return suggestions;
204
+ }
205
+ // Annotate the CommonJS export names for ESM import in node:
206
+ 0 && (module.exports = {
207
+ check,
208
+ getSuggestions,
209
+ isValid,
210
+ validate
211
+ });
212
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/quality/index.ts"],"sourcesContent":["/**\n * Prompt Quality Checker - Local validation for prompt quality\n * \n * @example\n * ```ts\n * import { quality } from 'prompts.chat';\n * \n * const result = quality.check(\"Act as a developer...\");\n * console.log(result.score); // 0.85\n * console.log(result.issues); // []\n * ```\n */\n\nexport interface QualityIssue {\n type: 'error' | 'warning' | 'suggestion';\n code: string;\n message: string;\n position?: { start: number; end: number };\n}\n\nexport interface QualityResult {\n valid: boolean;\n score: number; // 0-1\n issues: QualityIssue[];\n stats: {\n characterCount: number;\n wordCount: number;\n sentenceCount: number;\n variableCount: number;\n hasRole: boolean;\n hasTask: boolean;\n hasConstraints: boolean;\n hasExamples: boolean;\n };\n}\n\n// Minimum thresholds\nconst MIN_CHAR_COUNT = 20;\nconst MIN_WORD_COUNT = 5;\nconst OPTIMAL_MIN_WORDS = 20;\nconst OPTIMAL_MAX_WORDS = 2000;\n\n/**\n * Check if text looks like gibberish\n */\nfunction isGibberish(text: string): boolean {\n // Check for repeated characters\n if (/(.)\\1{4,}/.test(text)) return true;\n \n // Check for keyboard patterns\n const keyboardPatterns = ['qwerty', 'asdfgh', 'zxcvbn', 'qwertz', 'azerty'];\n const lower = text.toLowerCase();\n if (keyboardPatterns.some(p => lower.includes(p))) return true;\n \n // Check consonant/vowel ratio (gibberish often has unusual ratios)\n const vowels = (text.match(/[aeiouAEIOU]/g) || []).length;\n const consonants = (text.match(/[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]/g) || []).length;\n \n if (consonants > 0 && vowels / consonants < 0.1) return true;\n \n return false;\n}\n\n/**\n * Detect common prompt patterns\n */\nfunction detectPatterns(text: string): {\n hasRole: boolean;\n hasTask: boolean;\n hasConstraints: boolean;\n hasExamples: boolean;\n} {\n const lower = text.toLowerCase();\n \n return {\n hasRole: /\\b(act as|you are|imagine you|pretend to be|role:|persona:)\\b/i.test(text),\n hasTask: /\\b(your task|you (will|should|must)|please|help me|i need|i want you to)\\b/i.test(text),\n hasConstraints: /\\b(do not|don't|never|always|must|should not|avoid|only|limit)\\b/i.test(text) ||\n /\\b(rule|constraint|requirement|guideline)\\b/i.test(lower),\n hasExamples: /\\b(example|for instance|such as|e\\.g\\.|like this)\\b/i.test(lower) ||\n /```[\\s\\S]*```/.test(text),\n };\n}\n\n/**\n * Count variables in the prompt\n */\nfunction countVariables(text: string): number {\n // Match various variable formats\n const patterns = [\n /\\$\\{[^}]+\\}/g, // ${var}\n /\\{\\{[^}]+\\}\\}/g, // {{var}}\n /\\[\\[[^\\]]+\\]\\]/g, // [[var]]\n /\\[[A-Z][A-Z0-9_\\s]*\\]/g, // [VAR]\n ];\n \n let count = 0;\n for (const pattern of patterns) {\n const matches = text.match(pattern);\n if (matches) count += matches.length;\n }\n \n return count;\n}\n\n/**\n * Calculate quality score based on various factors\n */\nfunction calculateScore(\n stats: QualityResult['stats'],\n issues: QualityIssue[]\n): number {\n let score = 1.0;\n \n // Deduct for errors\n const errors = issues.filter(i => i.type === 'error').length;\n const warnings = issues.filter(i => i.type === 'warning').length;\n \n score -= errors * 0.2;\n score -= warnings * 0.05;\n \n // Bonus for good structure\n if (stats.hasRole) score += 0.05;\n if (stats.hasTask) score += 0.05;\n if (stats.hasConstraints) score += 0.03;\n if (stats.hasExamples) score += 0.05;\n \n // Penalty for being too short\n if (stats.wordCount < OPTIMAL_MIN_WORDS) {\n score -= 0.1 * (1 - stats.wordCount / OPTIMAL_MIN_WORDS);\n }\n \n // Slight penalty for being very long\n if (stats.wordCount > OPTIMAL_MAX_WORDS) {\n score -= 0.05;\n }\n \n // Bonus for having variables (indicates reusability)\n if (stats.variableCount > 0) {\n score += 0.05;\n }\n \n return Math.max(0, Math.min(1, score));\n}\n\n/**\n * Check prompt quality locally (no API needed)\n */\nexport function check(prompt: string): QualityResult {\n const issues: QualityIssue[] = [];\n const trimmed = prompt.trim();\n \n // Basic stats\n const characterCount = trimmed.length;\n const words = trimmed.split(/\\s+/).filter(w => w.length > 0);\n const wordCount = words.length;\n const sentenceCount = (trimmed.match(/[.!?]+/g) || []).length || 1;\n const variableCount = countVariables(trimmed);\n const patterns = detectPatterns(trimmed);\n \n // Check for empty or too short\n if (characterCount === 0) {\n issues.push({\n type: 'error',\n code: 'EMPTY',\n message: 'Prompt is empty',\n });\n } else if (characterCount < MIN_CHAR_COUNT) {\n issues.push({\n type: 'error',\n code: 'TOO_SHORT',\n message: `Prompt is too short (${characterCount} chars, minimum ${MIN_CHAR_COUNT})`,\n });\n }\n \n if (wordCount > 0 && wordCount < MIN_WORD_COUNT) {\n issues.push({\n type: 'warning',\n code: 'FEW_WORDS',\n message: `Prompt has very few words (${wordCount} words, recommended ${OPTIMAL_MIN_WORDS}+)`,\n });\n }\n \n // Check for gibberish\n if (isGibberish(trimmed)) {\n issues.push({\n type: 'error',\n code: 'GIBBERISH',\n message: 'Prompt appears to contain gibberish or random characters',\n });\n }\n \n // Check for common issues\n if (!patterns.hasTask && !patterns.hasRole) {\n issues.push({\n type: 'suggestion',\n code: 'NO_CLEAR_INSTRUCTION',\n message: 'Consider adding a clear task or role definition',\n });\n }\n \n // Check for unbalanced brackets/quotes\n const brackets = [\n { open: '{', close: '}' },\n { open: '[', close: ']' },\n { open: '(', close: ')' },\n ];\n \n for (const { open, close } of brackets) {\n const openCount = (trimmed.match(new RegExp(`\\\\${open}`, 'g')) || []).length;\n const closeCount = (trimmed.match(new RegExp(`\\\\${close}`, 'g')) || []).length;\n \n if (openCount !== closeCount) {\n issues.push({\n type: 'warning',\n code: 'UNBALANCED_BRACKETS',\n message: `Unbalanced ${open}${close} brackets (${openCount} open, ${closeCount} close)`,\n });\n }\n }\n \n // Check for very long lines (readability)\n const lines = trimmed.split('\\n');\n const longLines = lines.filter(l => l.length > 500);\n if (longLines.length > 0) {\n issues.push({\n type: 'suggestion',\n code: 'LONG_LINES',\n message: 'Some lines are very long. Consider breaking them up for readability.',\n });\n }\n \n const stats = {\n characterCount,\n wordCount,\n sentenceCount,\n variableCount,\n ...patterns,\n };\n \n const score = calculateScore(stats, issues);\n const hasErrors = issues.some(i => i.type === 'error');\n \n return {\n valid: !hasErrors,\n score,\n issues,\n stats,\n };\n}\n\n/**\n * Validate a prompt and throw if invalid\n */\nexport function validate(prompt: string): void {\n const result = check(prompt);\n \n if (!result.valid) {\n const errors = result.issues\n .filter(i => i.type === 'error')\n .map(i => i.message)\n .join('; ');\n \n throw new Error(`Invalid prompt: ${errors}`);\n }\n}\n\n/**\n * Check if a prompt is valid\n */\nexport function isValid(prompt: string): boolean {\n return check(prompt).valid;\n}\n\n/**\n * Get suggestions for improving a prompt\n */\nexport function getSuggestions(prompt: string): string[] {\n const result = check(prompt);\n const suggestions: string[] = [];\n \n // From issues\n suggestions.push(\n ...result.issues\n .filter(i => i.type === 'suggestion' || i.type === 'warning')\n .map(i => i.message)\n );\n \n // Additional suggestions based on stats\n if (!result.stats.hasRole) {\n suggestions.push('Add a role definition (e.g., \"Act as a...\")');\n }\n \n if (!result.stats.hasConstraints && result.stats.wordCount > 50) {\n suggestions.push('Consider adding constraints or rules for better control');\n }\n \n if (!result.stats.hasExamples && result.stats.wordCount > 100) {\n suggestions.push('Adding examples can improve output quality');\n }\n \n if (result.stats.variableCount === 0 && result.stats.wordCount > 30) {\n suggestions.push('Consider adding variables (${var}) to make the prompt reusable');\n }\n \n return suggestions;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,184 @@
1
+ // src/quality/index.ts
2
+ var MIN_CHAR_COUNT = 20;
3
+ var MIN_WORD_COUNT = 5;
4
+ var OPTIMAL_MIN_WORDS = 20;
5
+ var OPTIMAL_MAX_WORDS = 2e3;
6
+ function isGibberish(text) {
7
+ if (/(.)\1{4,}/.test(text)) return true;
8
+ const keyboardPatterns = ["qwerty", "asdfgh", "zxcvbn", "qwertz", "azerty"];
9
+ const lower = text.toLowerCase();
10
+ if (keyboardPatterns.some((p) => lower.includes(p))) return true;
11
+ const vowels = (text.match(/[aeiouAEIOU]/g) || []).length;
12
+ const consonants = (text.match(/[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]/g) || []).length;
13
+ if (consonants > 0 && vowels / consonants < 0.1) return true;
14
+ return false;
15
+ }
16
+ function detectPatterns(text) {
17
+ const lower = text.toLowerCase();
18
+ return {
19
+ hasRole: /\b(act as|you are|imagine you|pretend to be|role:|persona:)\b/i.test(text),
20
+ hasTask: /\b(your task|you (will|should|must)|please|help me|i need|i want you to)\b/i.test(text),
21
+ hasConstraints: /\b(do not|don't|never|always|must|should not|avoid|only|limit)\b/i.test(text) || /\b(rule|constraint|requirement|guideline)\b/i.test(lower),
22
+ hasExamples: /\b(example|for instance|such as|e\.g\.|like this)\b/i.test(lower) || /```[\s\S]*```/.test(text)
23
+ };
24
+ }
25
+ function countVariables(text) {
26
+ const patterns = [
27
+ /\$\{[^}]+\}/g,
28
+ // ${var}
29
+ /\{\{[^}]+\}\}/g,
30
+ // {{var}}
31
+ /\[\[[^\]]+\]\]/g,
32
+ // [[var]]
33
+ /\[[A-Z][A-Z0-9_\s]*\]/g
34
+ // [VAR]
35
+ ];
36
+ let count = 0;
37
+ for (const pattern of patterns) {
38
+ const matches = text.match(pattern);
39
+ if (matches) count += matches.length;
40
+ }
41
+ return count;
42
+ }
43
+ function calculateScore(stats, issues) {
44
+ let score = 1;
45
+ const errors = issues.filter((i) => i.type === "error").length;
46
+ const warnings = issues.filter((i) => i.type === "warning").length;
47
+ score -= errors * 0.2;
48
+ score -= warnings * 0.05;
49
+ if (stats.hasRole) score += 0.05;
50
+ if (stats.hasTask) score += 0.05;
51
+ if (stats.hasConstraints) score += 0.03;
52
+ if (stats.hasExamples) score += 0.05;
53
+ if (stats.wordCount < OPTIMAL_MIN_WORDS) {
54
+ score -= 0.1 * (1 - stats.wordCount / OPTIMAL_MIN_WORDS);
55
+ }
56
+ if (stats.wordCount > OPTIMAL_MAX_WORDS) {
57
+ score -= 0.05;
58
+ }
59
+ if (stats.variableCount > 0) {
60
+ score += 0.05;
61
+ }
62
+ return Math.max(0, Math.min(1, score));
63
+ }
64
+ function check(prompt) {
65
+ const issues = [];
66
+ const trimmed = prompt.trim();
67
+ const characterCount = trimmed.length;
68
+ const words = trimmed.split(/\s+/).filter((w) => w.length > 0);
69
+ const wordCount = words.length;
70
+ const sentenceCount = (trimmed.match(/[.!?]+/g) || []).length || 1;
71
+ const variableCount = countVariables(trimmed);
72
+ const patterns = detectPatterns(trimmed);
73
+ if (characterCount === 0) {
74
+ issues.push({
75
+ type: "error",
76
+ code: "EMPTY",
77
+ message: "Prompt is empty"
78
+ });
79
+ } else if (characterCount < MIN_CHAR_COUNT) {
80
+ issues.push({
81
+ type: "error",
82
+ code: "TOO_SHORT",
83
+ message: `Prompt is too short (${characterCount} chars, minimum ${MIN_CHAR_COUNT})`
84
+ });
85
+ }
86
+ if (wordCount > 0 && wordCount < MIN_WORD_COUNT) {
87
+ issues.push({
88
+ type: "warning",
89
+ code: "FEW_WORDS",
90
+ message: `Prompt has very few words (${wordCount} words, recommended ${OPTIMAL_MIN_WORDS}+)`
91
+ });
92
+ }
93
+ if (isGibberish(trimmed)) {
94
+ issues.push({
95
+ type: "error",
96
+ code: "GIBBERISH",
97
+ message: "Prompt appears to contain gibberish or random characters"
98
+ });
99
+ }
100
+ if (!patterns.hasTask && !patterns.hasRole) {
101
+ issues.push({
102
+ type: "suggestion",
103
+ code: "NO_CLEAR_INSTRUCTION",
104
+ message: "Consider adding a clear task or role definition"
105
+ });
106
+ }
107
+ const brackets = [
108
+ { open: "{", close: "}" },
109
+ { open: "[", close: "]" },
110
+ { open: "(", close: ")" }
111
+ ];
112
+ for (const { open, close } of brackets) {
113
+ const openCount = (trimmed.match(new RegExp(`\\${open}`, "g")) || []).length;
114
+ const closeCount = (trimmed.match(new RegExp(`\\${close}`, "g")) || []).length;
115
+ if (openCount !== closeCount) {
116
+ issues.push({
117
+ type: "warning",
118
+ code: "UNBALANCED_BRACKETS",
119
+ message: `Unbalanced ${open}${close} brackets (${openCount} open, ${closeCount} close)`
120
+ });
121
+ }
122
+ }
123
+ const lines = trimmed.split("\n");
124
+ const longLines = lines.filter((l) => l.length > 500);
125
+ if (longLines.length > 0) {
126
+ issues.push({
127
+ type: "suggestion",
128
+ code: "LONG_LINES",
129
+ message: "Some lines are very long. Consider breaking them up for readability."
130
+ });
131
+ }
132
+ const stats = {
133
+ characterCount,
134
+ wordCount,
135
+ sentenceCount,
136
+ variableCount,
137
+ ...patterns
138
+ };
139
+ const score = calculateScore(stats, issues);
140
+ const hasErrors = issues.some((i) => i.type === "error");
141
+ return {
142
+ valid: !hasErrors,
143
+ score,
144
+ issues,
145
+ stats
146
+ };
147
+ }
148
+ function validate(prompt) {
149
+ const result = check(prompt);
150
+ if (!result.valid) {
151
+ const errors = result.issues.filter((i) => i.type === "error").map((i) => i.message).join("; ");
152
+ throw new Error(`Invalid prompt: ${errors}`);
153
+ }
154
+ }
155
+ function isValid(prompt) {
156
+ return check(prompt).valid;
157
+ }
158
+ function getSuggestions(prompt) {
159
+ const result = check(prompt);
160
+ const suggestions = [];
161
+ suggestions.push(
162
+ ...result.issues.filter((i) => i.type === "suggestion" || i.type === "warning").map((i) => i.message)
163
+ );
164
+ if (!result.stats.hasRole) {
165
+ suggestions.push('Add a role definition (e.g., "Act as a...")');
166
+ }
167
+ if (!result.stats.hasConstraints && result.stats.wordCount > 50) {
168
+ suggestions.push("Consider adding constraints or rules for better control");
169
+ }
170
+ if (!result.stats.hasExamples && result.stats.wordCount > 100) {
171
+ suggestions.push("Adding examples can improve output quality");
172
+ }
173
+ if (result.stats.variableCount === 0 && result.stats.wordCount > 30) {
174
+ suggestions.push("Consider adding variables (${var}) to make the prompt reusable");
175
+ }
176
+ return suggestions;
177
+ }
178
+ export {
179
+ check,
180
+ getSuggestions,
181
+ isValid,
182
+ validate
183
+ };
184
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/quality/index.ts"],"sourcesContent":["/**\n * Prompt Quality Checker - Local validation for prompt quality\n * \n * @example\n * ```ts\n * import { quality } from 'prompts.chat';\n * \n * const result = quality.check(\"Act as a developer...\");\n * console.log(result.score); // 0.85\n * console.log(result.issues); // []\n * ```\n */\n\nexport interface QualityIssue {\n type: 'error' | 'warning' | 'suggestion';\n code: string;\n message: string;\n position?: { start: number; end: number };\n}\n\nexport interface QualityResult {\n valid: boolean;\n score: number; // 0-1\n issues: QualityIssue[];\n stats: {\n characterCount: number;\n wordCount: number;\n sentenceCount: number;\n variableCount: number;\n hasRole: boolean;\n hasTask: boolean;\n hasConstraints: boolean;\n hasExamples: boolean;\n };\n}\n\n// Minimum thresholds\nconst MIN_CHAR_COUNT = 20;\nconst MIN_WORD_COUNT = 5;\nconst OPTIMAL_MIN_WORDS = 20;\nconst OPTIMAL_MAX_WORDS = 2000;\n\n/**\n * Check if text looks like gibberish\n */\nfunction isGibberish(text: string): boolean {\n // Check for repeated characters\n if (/(.)\\1{4,}/.test(text)) return true;\n \n // Check for keyboard patterns\n const keyboardPatterns = ['qwerty', 'asdfgh', 'zxcvbn', 'qwertz', 'azerty'];\n const lower = text.toLowerCase();\n if (keyboardPatterns.some(p => lower.includes(p))) return true;\n \n // Check consonant/vowel ratio (gibberish often has unusual ratios)\n const vowels = (text.match(/[aeiouAEIOU]/g) || []).length;\n const consonants = (text.match(/[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]/g) || []).length;\n \n if (consonants > 0 && vowels / consonants < 0.1) return true;\n \n return false;\n}\n\n/**\n * Detect common prompt patterns\n */\nfunction detectPatterns(text: string): {\n hasRole: boolean;\n hasTask: boolean;\n hasConstraints: boolean;\n hasExamples: boolean;\n} {\n const lower = text.toLowerCase();\n \n return {\n hasRole: /\\b(act as|you are|imagine you|pretend to be|role:|persona:)\\b/i.test(text),\n hasTask: /\\b(your task|you (will|should|must)|please|help me|i need|i want you to)\\b/i.test(text),\n hasConstraints: /\\b(do not|don't|never|always|must|should not|avoid|only|limit)\\b/i.test(text) ||\n /\\b(rule|constraint|requirement|guideline)\\b/i.test(lower),\n hasExamples: /\\b(example|for instance|such as|e\\.g\\.|like this)\\b/i.test(lower) ||\n /```[\\s\\S]*```/.test(text),\n };\n}\n\n/**\n * Count variables in the prompt\n */\nfunction countVariables(text: string): number {\n // Match various variable formats\n const patterns = [\n /\\$\\{[^}]+\\}/g, // ${var}\n /\\{\\{[^}]+\\}\\}/g, // {{var}}\n /\\[\\[[^\\]]+\\]\\]/g, // [[var]]\n /\\[[A-Z][A-Z0-9_\\s]*\\]/g, // [VAR]\n ];\n \n let count = 0;\n for (const pattern of patterns) {\n const matches = text.match(pattern);\n if (matches) count += matches.length;\n }\n \n return count;\n}\n\n/**\n * Calculate quality score based on various factors\n */\nfunction calculateScore(\n stats: QualityResult['stats'],\n issues: QualityIssue[]\n): number {\n let score = 1.0;\n \n // Deduct for errors\n const errors = issues.filter(i => i.type === 'error').length;\n const warnings = issues.filter(i => i.type === 'warning').length;\n \n score -= errors * 0.2;\n score -= warnings * 0.05;\n \n // Bonus for good structure\n if (stats.hasRole) score += 0.05;\n if (stats.hasTask) score += 0.05;\n if (stats.hasConstraints) score += 0.03;\n if (stats.hasExamples) score += 0.05;\n \n // Penalty for being too short\n if (stats.wordCount < OPTIMAL_MIN_WORDS) {\n score -= 0.1 * (1 - stats.wordCount / OPTIMAL_MIN_WORDS);\n }\n \n // Slight penalty for being very long\n if (stats.wordCount > OPTIMAL_MAX_WORDS) {\n score -= 0.05;\n }\n \n // Bonus for having variables (indicates reusability)\n if (stats.variableCount > 0) {\n score += 0.05;\n }\n \n return Math.max(0, Math.min(1, score));\n}\n\n/**\n * Check prompt quality locally (no API needed)\n */\nexport function check(prompt: string): QualityResult {\n const issues: QualityIssue[] = [];\n const trimmed = prompt.trim();\n \n // Basic stats\n const characterCount = trimmed.length;\n const words = trimmed.split(/\\s+/).filter(w => w.length > 0);\n const wordCount = words.length;\n const sentenceCount = (trimmed.match(/[.!?]+/g) || []).length || 1;\n const variableCount = countVariables(trimmed);\n const patterns = detectPatterns(trimmed);\n \n // Check for empty or too short\n if (characterCount === 0) {\n issues.push({\n type: 'error',\n code: 'EMPTY',\n message: 'Prompt is empty',\n });\n } else if (characterCount < MIN_CHAR_COUNT) {\n issues.push({\n type: 'error',\n code: 'TOO_SHORT',\n message: `Prompt is too short (${characterCount} chars, minimum ${MIN_CHAR_COUNT})`,\n });\n }\n \n if (wordCount > 0 && wordCount < MIN_WORD_COUNT) {\n issues.push({\n type: 'warning',\n code: 'FEW_WORDS',\n message: `Prompt has very few words (${wordCount} words, recommended ${OPTIMAL_MIN_WORDS}+)`,\n });\n }\n \n // Check for gibberish\n if (isGibberish(trimmed)) {\n issues.push({\n type: 'error',\n code: 'GIBBERISH',\n message: 'Prompt appears to contain gibberish or random characters',\n });\n }\n \n // Check for common issues\n if (!patterns.hasTask && !patterns.hasRole) {\n issues.push({\n type: 'suggestion',\n code: 'NO_CLEAR_INSTRUCTION',\n message: 'Consider adding a clear task or role definition',\n });\n }\n \n // Check for unbalanced brackets/quotes\n const brackets = [\n { open: '{', close: '}' },\n { open: '[', close: ']' },\n { open: '(', close: ')' },\n ];\n \n for (const { open, close } of brackets) {\n const openCount = (trimmed.match(new RegExp(`\\\\${open}`, 'g')) || []).length;\n const closeCount = (trimmed.match(new RegExp(`\\\\${close}`, 'g')) || []).length;\n \n if (openCount !== closeCount) {\n issues.push({\n type: 'warning',\n code: 'UNBALANCED_BRACKETS',\n message: `Unbalanced ${open}${close} brackets (${openCount} open, ${closeCount} close)`,\n });\n }\n }\n \n // Check for very long lines (readability)\n const lines = trimmed.split('\\n');\n const longLines = lines.filter(l => l.length > 500);\n if (longLines.length > 0) {\n issues.push({\n type: 'suggestion',\n code: 'LONG_LINES',\n message: 'Some lines are very long. Consider breaking them up for readability.',\n });\n }\n \n const stats = {\n characterCount,\n wordCount,\n sentenceCount,\n variableCount,\n ...patterns,\n };\n \n const score = calculateScore(stats, issues);\n const hasErrors = issues.some(i => i.type === 'error');\n \n return {\n valid: !hasErrors,\n score,\n issues,\n stats,\n };\n}\n\n/**\n * Validate a prompt and throw if invalid\n */\nexport function validate(prompt: string): void {\n const result = check(prompt);\n \n if (!result.valid) {\n const errors = result.issues\n .filter(i => i.type === 'error')\n .map(i => i.message)\n .join('; ');\n \n throw new Error(`Invalid prompt: ${errors}`);\n }\n}\n\n/**\n * Check if a prompt is valid\n */\nexport function isValid(prompt: string): boolean {\n return check(prompt).valid;\n}\n\n/**\n * Get suggestions for improving a prompt\n */\nexport function getSuggestions(prompt: string): string[] {\n const result = check(prompt);\n const suggestions: string[] = [];\n \n // From issues\n suggestions.push(\n ...result.issues\n .filter(i => i.type === 'suggestion' || i.type === 'warning')\n .map(i => i.message)\n );\n \n // Additional suggestions based on stats\n if (!result.stats.hasRole) {\n suggestions.push('Add a role definition (e.g., \"Act as a...\")');\n }\n \n if (!result.stats.hasConstraints && result.stats.wordCount > 50) {\n suggestions.push('Consider adding constraints or rules for better control');\n }\n \n if (!result.stats.hasExamples && result.stats.wordCount > 100) {\n suggestions.push('Adding examples can improve output quality');\n }\n \n if (result.stats.variableCount === 0 && result.stats.wordCount > 30) {\n suggestions.push('Consider adding variables (${var}) to make the prompt reusable');\n }\n \n return suggestions;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1 @@
1
+ export { b as calculate, c as calculateSimilarity, d as deduplicate, f as findDuplicates, g as getContentFingerprint, e as isDuplicate, a as isSimilarContent, n as normalizeContent } from '../index-BEIO8LCd.mjs';
@@ -0,0 +1 @@
1
+ export { b as calculate, c as calculateSimilarity, d as deduplicate, f as findDuplicates, g as getContentFingerprint, e as isDuplicate, a as isSimilarContent, n as normalizeContent } from '../index-BEIO8LCd.js';
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/similarity/index.ts
21
+ var similarity_exports = {};
22
+ __export(similarity_exports, {
23
+ calculate: () => calculate,
24
+ calculateSimilarity: () => calculateSimilarity,
25
+ deduplicate: () => deduplicate,
26
+ findDuplicates: () => findDuplicates,
27
+ getContentFingerprint: () => getContentFingerprint,
28
+ isDuplicate: () => isDuplicate,
29
+ isSimilarContent: () => isSimilarContent,
30
+ normalizeContent: () => normalizeContent
31
+ });
32
+ module.exports = __toCommonJS(similarity_exports);
33
+ function normalizeContent(content) {
34
+ return content.replace(/\$\{[^}]+\}/g, "").replace(/\[[^\]]+\]/g, "").replace(/<[^>]+>/g, "").toLowerCase().replace(/[^\w\s]/g, "").replace(/\s+/g, " ").trim();
35
+ }
36
+ function jaccardSimilarity(str1, str2) {
37
+ const set1 = new Set(str1.split(" ").filter(Boolean));
38
+ const set2 = new Set(str2.split(" ").filter(Boolean));
39
+ if (set1.size === 0 && set2.size === 0) return 1;
40
+ if (set1.size === 0 || set2.size === 0) return 0;
41
+ const intersection = new Set([...set1].filter((x) => set2.has(x)));
42
+ const union = /* @__PURE__ */ new Set([...set1, ...set2]);
43
+ return intersection.size / union.size;
44
+ }
45
+ function ngramSimilarity(str1, str2, n = 3) {
46
+ const getNgrams = (str) => {
47
+ const ngrams = /* @__PURE__ */ new Set();
48
+ const padded = " ".repeat(n - 1) + str + " ".repeat(n - 1);
49
+ for (let i = 0; i <= padded.length - n; i++) {
50
+ ngrams.add(padded.slice(i, i + n));
51
+ }
52
+ return ngrams;
53
+ };
54
+ const ngrams1 = getNgrams(str1);
55
+ const ngrams2 = getNgrams(str2);
56
+ if (ngrams1.size === 0 && ngrams2.size === 0) return 1;
57
+ if (ngrams1.size === 0 || ngrams2.size === 0) return 0;
58
+ const intersection = new Set([...ngrams1].filter((x) => ngrams2.has(x)));
59
+ const union = /* @__PURE__ */ new Set([...ngrams1, ...ngrams2]);
60
+ return intersection.size / union.size;
61
+ }
62
+ function calculateSimilarity(content1, content2) {
63
+ const normalized1 = normalizeContent(content1);
64
+ const normalized2 = normalizeContent(content2);
65
+ if (normalized1 === normalized2) return 1;
66
+ if (!normalized1 || !normalized2) return 0;
67
+ const jaccard = jaccardSimilarity(normalized1, normalized2);
68
+ const ngram = ngramSimilarity(normalized1, normalized2);
69
+ return jaccard * 0.6 + ngram * 0.4;
70
+ }
71
+ var calculate = calculateSimilarity;
72
+ function isSimilarContent(content1, content2, threshold = 0.85) {
73
+ return calculateSimilarity(content1, content2) >= threshold;
74
+ }
75
+ var isDuplicate = isSimilarContent;
76
+ function getContentFingerprint(content) {
77
+ const normalized = normalizeContent(content);
78
+ return normalized.slice(0, 500);
79
+ }
80
+ function findDuplicates(prompts, threshold = 0.85) {
81
+ const groups = [];
82
+ const used = /* @__PURE__ */ new Set();
83
+ for (let i = 0; i < prompts.length; i++) {
84
+ if (used.has(i)) continue;
85
+ const group = [prompts[i]];
86
+ used.add(i);
87
+ for (let j = i + 1; j < prompts.length; j++) {
88
+ if (used.has(j)) continue;
89
+ if (isSimilarContent(prompts[i].content, prompts[j].content, threshold)) {
90
+ group.push(prompts[j]);
91
+ used.add(j);
92
+ }
93
+ }
94
+ if (group.length > 1) {
95
+ groups.push(group);
96
+ }
97
+ }
98
+ return groups;
99
+ }
100
+ function deduplicate(prompts, threshold = 0.85) {
101
+ const result = [];
102
+ for (const prompt of prompts) {
103
+ const isDupe = result.some(
104
+ (existing) => isSimilarContent(existing.content, prompt.content, threshold)
105
+ );
106
+ if (!isDupe) {
107
+ result.push(prompt);
108
+ }
109
+ }
110
+ return result;
111
+ }
112
+ // Annotate the CommonJS export names for ESM import in node:
113
+ 0 && (module.exports = {
114
+ calculate,
115
+ calculateSimilarity,
116
+ deduplicate,
117
+ findDuplicates,
118
+ getContentFingerprint,
119
+ isDuplicate,
120
+ isSimilarContent,
121
+ normalizeContent
122
+ });
123
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/similarity/index.ts"],"sourcesContent":["/**\n * Content similarity utilities for duplicate detection\n */\n\n/**\n * Normalize content for comparison by:\n * - Removing variables (${...} patterns)\n * - Converting to lowercase\n * - Removing extra whitespace\n * - Removing punctuation\n */\nexport function normalizeContent(content: string): string {\n return content\n // Remove variables like ${variable} or ${variable:default}\n .replace(/\\$\\{[^}]+\\}/g, \"\")\n // Remove common placeholder patterns like [placeholder] or <placeholder>\n .replace(/\\[[^\\]]+\\]/g, \"\")\n .replace(/<[^>]+>/g, \"\")\n // Convert to lowercase\n .toLowerCase()\n // Remove punctuation\n .replace(/[^\\w\\s]/g, \"\")\n // Normalize whitespace\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n/**\n * Calculate Jaccard similarity between two strings\n * Returns a value between 0 (completely different) and 1 (identical)\n */\nfunction jaccardSimilarity(str1: string, str2: string): number {\n const set1 = new Set(str1.split(\" \").filter(Boolean));\n const set2 = new Set(str2.split(\" \").filter(Boolean));\n \n if (set1.size === 0 && set2.size === 0) return 1;\n if (set1.size === 0 || set2.size === 0) return 0;\n \n const intersection = new Set([...set1].filter(x => set2.has(x)));\n const union = new Set([...set1, ...set2]);\n \n return intersection.size / union.size;\n}\n\n/**\n * Calculate n-gram similarity for better sequence matching\n * Uses trigrams (3-character sequences) by default\n */\nfunction ngramSimilarity(str1: string, str2: string, n: number = 3): number {\n const getNgrams = (str: string): Set<string> => {\n const ngrams = new Set<string>();\n const padded = \" \".repeat(n - 1) + str + \" \".repeat(n - 1);\n for (let i = 0; i <= padded.length - n; i++) {\n ngrams.add(padded.slice(i, i + n));\n }\n return ngrams;\n };\n \n const ngrams1 = getNgrams(str1);\n const ngrams2 = getNgrams(str2);\n \n if (ngrams1.size === 0 && ngrams2.size === 0) return 1;\n if (ngrams1.size === 0 || ngrams2.size === 0) return 0;\n \n const intersection = new Set([...ngrams1].filter(x => ngrams2.has(x)));\n const union = new Set([...ngrams1, ...ngrams2]);\n \n return intersection.size / union.size;\n}\n\n/**\n * Combined similarity score using multiple algorithms\n * Returns a value between 0 (completely different) and 1 (identical)\n */\nexport function calculateSimilarity(content1: string, content2: string): number {\n const normalized1 = normalizeContent(content1);\n const normalized2 = normalizeContent(content2);\n \n // Exact match after normalization\n if (normalized1 === normalized2) return 1;\n \n // Empty content edge case\n if (!normalized1 || !normalized2) return 0;\n \n // Combine Jaccard (word-level) and n-gram (character-level) similarities\n const jaccard = jaccardSimilarity(normalized1, normalized2);\n const ngram = ngramSimilarity(normalized1, normalized2);\n \n // Weighted average: 60% Jaccard (word overlap), 40% n-gram (sequence similarity)\n return jaccard * 0.6 + ngram * 0.4;\n}\n\n/**\n * Alias for calculateSimilarity\n */\nexport const calculate = calculateSimilarity;\n\n/**\n * Check if two contents are similar enough to be considered duplicates\n * Default threshold is 0.85 (85% similar)\n */\nexport function isSimilarContent(\n content1: string, \n content2: string, \n threshold: number = 0.85\n): boolean {\n return calculateSimilarity(content1, content2) >= threshold;\n}\n\n/**\n * Alias for isSimilarContent\n */\nexport const isDuplicate = isSimilarContent;\n\n/**\n * Get normalized content hash for database indexing/comparison\n * This is a simple hash for quick lookups before full similarity check\n */\nexport function getContentFingerprint(content: string): string {\n const normalized = normalizeContent(content);\n // Take first 500 chars of normalized content as fingerprint\n return normalized.slice(0, 500);\n}\n\n/**\n * Find duplicates in an array of prompts\n * Returns groups of similar prompts\n */\nexport function findDuplicates<T extends { content: string }>(\n prompts: T[],\n threshold: number = 0.85\n): T[][] {\n const groups: T[][] = [];\n const used = new Set<number>();\n\n for (let i = 0; i < prompts.length; i++) {\n if (used.has(i)) continue;\n\n const group: T[] = [prompts[i]];\n used.add(i);\n\n for (let j = i + 1; j < prompts.length; j++) {\n if (used.has(j)) continue;\n\n if (isSimilarContent(prompts[i].content, prompts[j].content, threshold)) {\n group.push(prompts[j]);\n used.add(j);\n }\n }\n\n if (group.length > 1) {\n groups.push(group);\n }\n }\n\n return groups;\n}\n\n/**\n * Deduplicate an array of prompts, keeping the first occurrence\n */\nexport function deduplicate<T extends { content: string }>(\n prompts: T[],\n threshold: number = 0.85\n): T[] {\n const result: T[] = [];\n\n for (const prompt of prompts) {\n const isDupe = result.some(existing => \n isSimilarContent(existing.content, prompt.content, threshold)\n );\n\n if (!isDupe) {\n result.push(prompt);\n }\n }\n\n return result;\n}\n"],"mappings":"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","names":[]}