@rog0x/mcp-string-tools 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # @rog0x/mcp-string-tools
2
+
3
+ Advanced string manipulation tools for AI agents, served over the Model Context Protocol (MCP).
4
+
5
+ ## Tools
6
+
7
+ ### analyze_string
8
+ Analyze text to get detailed metrics:
9
+ - Character count (with and without spaces)
10
+ - Word, sentence, and paragraph counts
11
+ - Estimated reading time
12
+ - Flesch-Kincaid reading level score and grade
13
+ - Unique word count and most common words
14
+ - Longest word
15
+
16
+ ### transform_string
17
+ Transform text with various operations:
18
+ - **remove_duplicate_lines** - Remove duplicate lines from text
19
+ - **remove_extra_whitespace** - Collapse extra whitespace and blank lines
20
+ - **extract_emails** - Extract all email addresses from text
21
+ - **extract_urls** - Extract all URLs from text
22
+ - **extract_phone_numbers** - Extract phone numbers from text
23
+ - **mask_sensitive_data** - Mask emails, phone numbers, and credit card numbers
24
+ - **truncate** - Truncate text with ellipsis at word boundaries
25
+ - **wrap_text** - Wrap text at a specified column width
26
+
27
+ ### diff_strings
28
+ Compare two strings and see the differences:
29
+ - **character** mode - Character-by-character diff
30
+ - **word** mode - Word-by-word diff (default)
31
+ - **line** mode - Line-by-line diff
32
+ - Shows additions, deletions, and unchanged segments with positions
33
+
34
+ ### render_template
35
+ Simple template engine:
36
+ - Replace `{{variables}}` with values from a JSON data object
37
+ - Conditionals: `{{#if var}}...{{#else}}...{{/if}}`
38
+ - Loops: `{{#each arr}}...{{this}}...{{@index}}...{{/each}}`
39
+ - Nested property access: `{{user.name}}`
40
+
41
+ ### generate_slug
42
+ Generate URL-friendly slugs:
43
+ - Transliterates accented characters (e.g., `cafe` from `cafe`)
44
+ - Removes special characters
45
+ - Configurable separator (default: `-`)
46
+ - Optional max length with clean truncation
47
+ - Optional lowercase toggle
48
+
49
+ ## Setup
50
+
51
+ ```bash
52
+ npm install
53
+ npm run build
54
+ ```
55
+
56
+ ## Usage with Claude Desktop
57
+
58
+ Add to your `claude_desktop_config.json`:
59
+
60
+ ```json
61
+ {
62
+ "mcpServers": {
63
+ "string-tools": {
64
+ "command": "node",
65
+ "args": ["D:/products/mcp-servers/mcp-string-tools/dist/index.js"]
66
+ }
67
+ }
68
+ }
69
+ ```
70
+
71
+ ## License
72
+
73
+ MIT
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
5
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+ const zod_1 = require("zod");
7
+ const string_analyzer_js_1 = require("./tools/string-analyzer.js");
8
+ const string_transformer_js_1 = require("./tools/string-transformer.js");
9
+ const string_diff_js_1 = require("./tools/string-diff.js");
10
+ const template_engine_js_1 = require("./tools/template-engine.js");
11
+ const slug_generator_js_1 = require("./tools/slug-generator.js");
12
+ const server = new mcp_js_1.McpServer({
13
+ name: "mcp-string-tools",
14
+ version: "1.0.0",
15
+ });
16
+ // Tool 1: String Analyzer
17
+ server.tool("analyze_string", "Analyze text to get character count, word count, sentence count, paragraph count, reading time, Flesch-Kincaid reading level, unique words, most common words, and longest word.", {
18
+ text: zod_1.z.string().describe("The text to analyze"),
19
+ }, async ({ text }) => {
20
+ const result = (0, string_analyzer_js_1.analyzeString)(text);
21
+ return {
22
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
23
+ };
24
+ });
25
+ // Tool 2: String Transformer
26
+ server.tool("transform_string", "Transform text with operations: remove_duplicate_lines, remove_extra_whitespace, extract_emails, extract_urls, extract_phone_numbers, mask_sensitive_data, truncate, wrap_text.", {
27
+ text: zod_1.z.string().describe("The text to transform"),
28
+ operation: zod_1.z
29
+ .enum([
30
+ "remove_duplicate_lines",
31
+ "remove_extra_whitespace",
32
+ "extract_emails",
33
+ "extract_urls",
34
+ "extract_phone_numbers",
35
+ "mask_sensitive_data",
36
+ "truncate",
37
+ "wrap_text",
38
+ ])
39
+ .describe("The transformation operation to apply"),
40
+ mask_char: zod_1.z
41
+ .string()
42
+ .optional()
43
+ .default("*")
44
+ .describe("Character used for masking (mask_sensitive_data only)"),
45
+ max_length: zod_1.z
46
+ .number()
47
+ .optional()
48
+ .default(100)
49
+ .describe("Maximum length for truncation (truncate only)"),
50
+ wrap_width: zod_1.z
51
+ .number()
52
+ .optional()
53
+ .default(80)
54
+ .describe("Column width for text wrapping (wrap_text only)"),
55
+ ellipsis: zod_1.z
56
+ .string()
57
+ .optional()
58
+ .default("...")
59
+ .describe("Ellipsis string for truncation (truncate only)"),
60
+ }, async ({ text, operation, mask_char, max_length, wrap_width, ellipsis }) => {
61
+ const result = (0, string_transformer_js_1.transformString)({
62
+ text,
63
+ operation,
64
+ maskChar: mask_char,
65
+ maxLength: max_length,
66
+ wrapWidth: wrap_width,
67
+ ellipsis,
68
+ });
69
+ return {
70
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
71
+ };
72
+ });
73
+ // Tool 3: String Diff
74
+ server.tool("diff_strings", "Compare two strings showing additions, deletions, and unchanged segments with positions. Supports character-level, word-level, and line-level diff modes.", {
75
+ old_text: zod_1.z.string().describe("The original text"),
76
+ new_text: zod_1.z.string().describe("The new/modified text"),
77
+ mode: zod_1.z
78
+ .enum(["character", "word", "line"])
79
+ .optional()
80
+ .default("word")
81
+ .describe("Diff granularity: character, word, or line"),
82
+ }, async ({ old_text, new_text, mode }) => {
83
+ const result = (0, string_diff_js_1.diffStrings)(old_text, new_text, mode);
84
+ return {
85
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
86
+ };
87
+ });
88
+ // Tool 4: Template Engine
89
+ server.tool("render_template", "Render a template string by replacing {{variables}} with values from a data object. Supports {{#if var}}...{{#else}}...{{/if}} conditionals and {{#each arr}}...{{/each}} loops with {{this}}, {{@index}}, and {{this.prop}}.", {
90
+ template: zod_1.z.string().describe("Template string with {{variable}} placeholders"),
91
+ data: zod_1.z
92
+ .string()
93
+ .describe("JSON string of key-value pairs to substitute into the template"),
94
+ }, async ({ template, data }) => {
95
+ let parsedData;
96
+ try {
97
+ parsedData = JSON.parse(data);
98
+ }
99
+ catch {
100
+ return {
101
+ content: [
102
+ {
103
+ type: "text",
104
+ text: JSON.stringify({ error: "Invalid JSON in data parameter" }),
105
+ },
106
+ ],
107
+ isError: true,
108
+ };
109
+ }
110
+ const result = (0, template_engine_js_1.renderTemplate)(template, parsedData);
111
+ return {
112
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
113
+ };
114
+ });
115
+ // Tool 5: Slug Generator
116
+ server.tool("generate_slug", "Generate a URL-friendly slug from text. Removes special characters, lowercases, replaces spaces with hyphens, transliterates accented characters. Configurable separator and max length.", {
117
+ text: zod_1.z.string().describe("The text to convert to a slug"),
118
+ separator: zod_1.z
119
+ .string()
120
+ .optional()
121
+ .default("-")
122
+ .describe("Character to use as word separator (default: -)"),
123
+ max_length: zod_1.z
124
+ .number()
125
+ .optional()
126
+ .default(0)
127
+ .describe("Maximum slug length (0 = unlimited)"),
128
+ lowercase: zod_1.z
129
+ .boolean()
130
+ .optional()
131
+ .default(true)
132
+ .describe("Convert to lowercase (default: true)"),
133
+ }, async ({ text, separator, max_length, lowercase }) => {
134
+ const result = (0, slug_generator_js_1.generateSlug)({
135
+ text,
136
+ separator,
137
+ maxLength: max_length,
138
+ lowercase,
139
+ });
140
+ return {
141
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
142
+ };
143
+ });
144
+ async function main() {
145
+ const transport = new stdio_js_1.StdioServerTransport();
146
+ await server.connect(transport);
147
+ console.error("mcp-string-tools server running on stdio");
148
+ }
149
+ main().catch((err) => {
150
+ console.error("Fatal error:", err);
151
+ process.exit(1);
152
+ });
153
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,14 @@
1
+ export interface SlugOptions {
2
+ text: string;
3
+ separator?: string;
4
+ maxLength?: number;
5
+ lowercase?: boolean;
6
+ }
7
+ export interface SlugResult {
8
+ original: string;
9
+ slug: string;
10
+ length: number;
11
+ wasTruncated: boolean;
12
+ }
13
+ export declare function generateSlug(options: SlugOptions): SlugResult;
14
+ //# sourceMappingURL=slug-generator.d.ts.map
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateSlug = generateSlug;
4
+ const TRANSLITERATION_MAP = {
5
+ "\u00e0": "a", "\u00e1": "a", "\u00e2": "a", "\u00e3": "a", "\u00e4": "a", "\u00e5": "a",
6
+ "\u00e6": "ae",
7
+ "\u00e7": "c",
8
+ "\u00e8": "e", "\u00e9": "e", "\u00ea": "e", "\u00eb": "e",
9
+ "\u00ec": "i", "\u00ed": "i", "\u00ee": "i", "\u00ef": "i",
10
+ "\u00f0": "d",
11
+ "\u00f1": "n",
12
+ "\u00f2": "o", "\u00f3": "o", "\u00f4": "o", "\u00f5": "o", "\u00f6": "o", "\u00f8": "o",
13
+ "\u00f9": "u", "\u00fa": "u", "\u00fb": "u", "\u00fc": "u",
14
+ "\u00fd": "y", "\u00ff": "y",
15
+ "\u00fe": "th",
16
+ "\u00df": "ss",
17
+ "\u0100": "a", "\u0101": "a", "\u0102": "a", "\u0103": "a", "\u0104": "a", "\u0105": "a",
18
+ "\u0106": "c", "\u0107": "c", "\u0108": "c", "\u0109": "c", "\u010a": "c", "\u010b": "c", "\u010c": "c", "\u010d": "c",
19
+ "\u010e": "d", "\u010f": "d", "\u0110": "d", "\u0111": "d",
20
+ "\u0112": "e", "\u0113": "e", "\u0114": "e", "\u0115": "e", "\u0116": "e", "\u0117": "e", "\u0118": "e", "\u0119": "e", "\u011a": "e", "\u011b": "e",
21
+ "\u011c": "g", "\u011d": "g", "\u011e": "g", "\u011f": "g", "\u0120": "g", "\u0121": "g", "\u0122": "g", "\u0123": "g",
22
+ "\u0124": "h", "\u0125": "h", "\u0126": "h", "\u0127": "h",
23
+ "\u0128": "i", "\u0129": "i", "\u012a": "i", "\u012b": "i", "\u012c": "i", "\u012d": "i", "\u012e": "i", "\u012f": "i", "\u0130": "i", "\u0131": "i",
24
+ "\u0139": "l", "\u013a": "l", "\u013b": "l", "\u013c": "l", "\u013d": "l", "\u013e": "l", "\u0141": "l", "\u0142": "l",
25
+ "\u0143": "n", "\u0144": "n", "\u0145": "n", "\u0146": "n", "\u0147": "n", "\u0148": "n",
26
+ "\u014c": "o", "\u014d": "o", "\u014e": "o", "\u014f": "o", "\u0150": "o", "\u0151": "o", "\u0152": "oe", "\u0153": "oe",
27
+ "\u0154": "r", "\u0155": "r", "\u0156": "r", "\u0157": "r", "\u0158": "r", "\u0159": "r",
28
+ "\u015a": "s", "\u015b": "s", "\u015c": "s", "\u015d": "s", "\u015e": "s", "\u015f": "s", "\u0160": "s", "\u0161": "s",
29
+ "\u0162": "t", "\u0163": "t", "\u0164": "t", "\u0165": "t", "\u0166": "t", "\u0167": "t",
30
+ "\u0168": "u", "\u0169": "u", "\u016a": "u", "\u016b": "u", "\u016c": "u", "\u016d": "u", "\u016e": "u", "\u016f": "u", "\u0170": "u", "\u0171": "u", "\u0172": "u", "\u0173": "u",
31
+ "\u0174": "w", "\u0175": "w",
32
+ "\u0176": "y", "\u0177": "y", "\u0178": "y",
33
+ "\u0179": "z", "\u017a": "z", "\u017b": "z", "\u017c": "z", "\u017d": "z", "\u017e": "z",
34
+ };
35
+ function transliterate(text) {
36
+ let result = "";
37
+ for (const char of text) {
38
+ const lower = char.toLowerCase();
39
+ if (TRANSLITERATION_MAP[lower]) {
40
+ const replacement = TRANSLITERATION_MAP[lower];
41
+ // Preserve case for first letter
42
+ if (char === char.toUpperCase() && char !== char.toLowerCase()) {
43
+ result += replacement.charAt(0).toUpperCase() + replacement.slice(1);
44
+ }
45
+ else {
46
+ result += replacement;
47
+ }
48
+ }
49
+ else {
50
+ result += char;
51
+ }
52
+ }
53
+ return result;
54
+ }
55
+ function generateSlug(options) {
56
+ const { text, separator = "-", maxLength = 0, lowercase = true, } = options;
57
+ let slug = transliterate(text);
58
+ if (lowercase) {
59
+ slug = slug.toLowerCase();
60
+ }
61
+ // Replace non-alphanumeric chars (except separator) with separator
62
+ const escapedSep = separator.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
63
+ slug = slug.replace(/[^a-zA-Z0-9]+/g, separator);
64
+ // Remove leading/trailing separators
65
+ const trimRegex = new RegExp(`^${escapedSep}+|${escapedSep}+$`, "g");
66
+ slug = slug.replace(trimRegex, "");
67
+ // Collapse multiple consecutive separators
68
+ const multiRegex = new RegExp(`${escapedSep}{2,}`, "g");
69
+ slug = slug.replace(multiRegex, separator);
70
+ let wasTruncated = false;
71
+ if (maxLength > 0 && slug.length > maxLength) {
72
+ wasTruncated = true;
73
+ slug = slug.slice(0, maxLength);
74
+ // Don't end on a separator
75
+ const endTrimRegex = new RegExp(`${escapedSep}+$`);
76
+ slug = slug.replace(endTrimRegex, "");
77
+ }
78
+ return {
79
+ original: text,
80
+ slug,
81
+ length: slug.length,
82
+ wasTruncated,
83
+ };
84
+ }
85
+ //# sourceMappingURL=slug-generator.js.map
@@ -0,0 +1,21 @@
1
+ export interface AnalysisResult {
2
+ charCount: number;
3
+ charCountNoSpaces: number;
4
+ wordCount: number;
5
+ sentenceCount: number;
6
+ paragraphCount: number;
7
+ readingTimeMinutes: number;
8
+ readingLevel: {
9
+ score: number;
10
+ grade: string;
11
+ description: string;
12
+ };
13
+ uniqueWordCount: number;
14
+ mostCommonWords: Array<{
15
+ word: string;
16
+ count: number;
17
+ }>;
18
+ longestWord: string;
19
+ }
20
+ export declare function analyzeString(text: string): AnalysisResult;
21
+ //# sourceMappingURL=string-analyzer.d.ts.map
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzeString = analyzeString;
4
+ function countSyllables(word) {
5
+ const w = word.toLowerCase().replace(/[^a-z]/g, "");
6
+ if (w.length <= 2)
7
+ return 1;
8
+ let count = 0;
9
+ const vowels = "aeiouy";
10
+ let prevVowel = false;
11
+ for (let i = 0; i < w.length; i++) {
12
+ const isVowel = vowels.includes(w[i]);
13
+ if (isVowel && !prevVowel) {
14
+ count++;
15
+ }
16
+ prevVowel = isVowel;
17
+ }
18
+ // Silent e
19
+ if (w.endsWith("e") && count > 1) {
20
+ count--;
21
+ }
22
+ // -le ending
23
+ if (w.endsWith("le") && w.length > 2 && !vowels.includes(w[w.length - 3])) {
24
+ count++;
25
+ }
26
+ return Math.max(count, 1);
27
+ }
28
+ function analyzeString(text) {
29
+ const charCount = text.length;
30
+ const charCountNoSpaces = text.replace(/\s/g, "").length;
31
+ const words = text.split(/\s+/).filter((w) => w.length > 0);
32
+ const wordCount = words.length;
33
+ const sentences = text.split(/[.!?]+/).filter((s) => s.trim().length > 0);
34
+ const sentenceCount = sentences.length;
35
+ const paragraphs = text.split(/\n\s*\n/).filter((p) => p.trim().length > 0);
36
+ const paragraphCount = Math.max(paragraphs.length, text.trim().length > 0 ? 1 : 0);
37
+ const readingTimeMinutes = Math.max(Math.ceil(wordCount / 200), wordCount > 0 ? 1 : 0);
38
+ // Flesch-Kincaid
39
+ const totalSyllables = words.reduce((sum, w) => sum + countSyllables(w), 0);
40
+ let fkScore = 0;
41
+ let fkGrade = "N/A";
42
+ let fkDescription = "Text too short to analyze";
43
+ if (wordCount > 0 && sentenceCount > 0) {
44
+ fkScore =
45
+ 206.835 -
46
+ 1.015 * (wordCount / sentenceCount) -
47
+ 84.6 * (totalSyllables / wordCount);
48
+ fkScore = Math.round(fkScore * 10) / 10;
49
+ if (fkScore >= 90) {
50
+ fkGrade = "5th grade";
51
+ fkDescription = "Very easy to read";
52
+ }
53
+ else if (fkScore >= 80) {
54
+ fkGrade = "6th grade";
55
+ fkDescription = "Easy to read";
56
+ }
57
+ else if (fkScore >= 70) {
58
+ fkGrade = "7th grade";
59
+ fkDescription = "Fairly easy to read";
60
+ }
61
+ else if (fkScore >= 60) {
62
+ fkGrade = "8th-9th grade";
63
+ fkDescription = "Plain English";
64
+ }
65
+ else if (fkScore >= 50) {
66
+ fkGrade = "10th-12th grade";
67
+ fkDescription = "Fairly difficult to read";
68
+ }
69
+ else if (fkScore >= 30) {
70
+ fkGrade = "College";
71
+ fkDescription = "Difficult to read";
72
+ }
73
+ else {
74
+ fkGrade = "College graduate";
75
+ fkDescription = "Very difficult to read";
76
+ }
77
+ }
78
+ // Word frequency
79
+ const wordFreq = new Map();
80
+ for (const w of words) {
81
+ const lower = w.toLowerCase().replace(/[^a-z0-9'-]/g, "");
82
+ if (lower.length > 0) {
83
+ wordFreq.set(lower, (wordFreq.get(lower) || 0) + 1);
84
+ }
85
+ }
86
+ const uniqueWordCount = wordFreq.size;
87
+ const mostCommonWords = Array.from(wordFreq.entries())
88
+ .sort((a, b) => b[1] - a[1])
89
+ .slice(0, 10)
90
+ .map(([word, count]) => ({ word, count }));
91
+ const longestWord = words.reduce((longest, w) => (w.length > longest.length ? w : longest), "");
92
+ return {
93
+ charCount,
94
+ charCountNoSpaces,
95
+ wordCount,
96
+ sentenceCount,
97
+ paragraphCount,
98
+ readingTimeMinutes,
99
+ readingLevel: {
100
+ score: fkScore,
101
+ grade: fkGrade,
102
+ description: fkDescription,
103
+ },
104
+ uniqueWordCount,
105
+ mostCommonWords,
106
+ longestWord,
107
+ };
108
+ }
109
+ //# sourceMappingURL=string-analyzer.js.map
@@ -0,0 +1,20 @@
1
+ export type DiffType = "addition" | "deletion" | "unchanged";
2
+ export interface DiffSegment {
3
+ type: DiffType;
4
+ value: string;
5
+ position: {
6
+ start: number;
7
+ end: number;
8
+ };
9
+ }
10
+ export interface DiffResult {
11
+ mode: "character" | "word" | "line";
12
+ segments: DiffSegment[];
13
+ summary: {
14
+ additions: number;
15
+ deletions: number;
16
+ unchanged: number;
17
+ };
18
+ }
19
+ export declare function diffStrings(oldStr: string, newStr: string, mode?: "character" | "word" | "line"): DiffResult;
20
+ //# sourceMappingURL=string-diff.d.ts.map
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.diffStrings = diffStrings;
4
+ function lcs(a, b) {
5
+ const m = a.length;
6
+ const n = b.length;
7
+ const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
8
+ for (let i = 1; i <= m; i++) {
9
+ for (let j = 1; j <= n; j++) {
10
+ if (a[i - 1] === b[j - 1]) {
11
+ dp[i][j] = dp[i - 1][j - 1] + 1;
12
+ }
13
+ else {
14
+ dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
15
+ }
16
+ }
17
+ }
18
+ const result = [];
19
+ let i = m;
20
+ let j = n;
21
+ while (i > 0 && j > 0) {
22
+ if (a[i - 1] === b[j - 1]) {
23
+ result.unshift(a[i - 1]);
24
+ i--;
25
+ j--;
26
+ }
27
+ else if (dp[i - 1][j] > dp[i][j - 1]) {
28
+ i--;
29
+ }
30
+ else {
31
+ j--;
32
+ }
33
+ }
34
+ return result;
35
+ }
36
+ function computeDiff(oldTokens, newTokens) {
37
+ const common = lcs(oldTokens, newTokens);
38
+ const segments = [];
39
+ let oi = 0;
40
+ let ni = 0;
41
+ let ci = 0;
42
+ let pos = 0;
43
+ while (ci < common.length) {
44
+ // Deletions from old
45
+ while (oi < oldTokens.length && oldTokens[oi] !== common[ci]) {
46
+ const start = pos;
47
+ const value = oldTokens[oi];
48
+ segments.push({
49
+ type: "deletion",
50
+ value,
51
+ position: { start, end: start + value.length },
52
+ });
53
+ oi++;
54
+ }
55
+ // Additions from new
56
+ while (ni < newTokens.length && newTokens[ni] !== common[ci]) {
57
+ const start = pos;
58
+ const value = newTokens[ni];
59
+ segments.push({
60
+ type: "addition",
61
+ value,
62
+ position: { start, end: start + value.length },
63
+ });
64
+ pos += value.length;
65
+ ni++;
66
+ }
67
+ // Unchanged
68
+ if (ci < common.length) {
69
+ const value = common[ci];
70
+ const start = pos;
71
+ segments.push({
72
+ type: "unchanged",
73
+ value,
74
+ position: { start, end: start + value.length },
75
+ });
76
+ pos += value.length;
77
+ oi++;
78
+ ni++;
79
+ ci++;
80
+ }
81
+ }
82
+ // Remaining deletions
83
+ while (oi < oldTokens.length) {
84
+ const value = oldTokens[oi];
85
+ segments.push({
86
+ type: "deletion",
87
+ value,
88
+ position: { start: pos, end: pos + value.length },
89
+ });
90
+ oi++;
91
+ }
92
+ // Remaining additions
93
+ while (ni < newTokens.length) {
94
+ const value = newTokens[ni];
95
+ segments.push({
96
+ type: "addition",
97
+ value,
98
+ position: { start: pos, end: pos + value.length },
99
+ });
100
+ pos += value.length;
101
+ ni++;
102
+ }
103
+ return segments;
104
+ }
105
+ function diffStrings(oldStr, newStr, mode = "word") {
106
+ let oldTokens;
107
+ let newTokens;
108
+ switch (mode) {
109
+ case "character":
110
+ oldTokens = oldStr.split("");
111
+ newTokens = newStr.split("");
112
+ break;
113
+ case "word":
114
+ oldTokens = oldStr.split(/(\s+)/).filter((t) => t.length > 0);
115
+ newTokens = newStr.split(/(\s+)/).filter((t) => t.length > 0);
116
+ break;
117
+ case "line":
118
+ oldTokens = oldStr.split("\n");
119
+ newTokens = newStr.split("\n");
120
+ break;
121
+ }
122
+ const segments = computeDiff(oldTokens, newTokens);
123
+ const summary = {
124
+ additions: segments.filter((s) => s.type === "addition").length,
125
+ deletions: segments.filter((s) => s.type === "deletion").length,
126
+ unchanged: segments.filter((s) => s.type === "unchanged").length,
127
+ };
128
+ return { mode, segments, summary };
129
+ }
130
+ //# sourceMappingURL=string-diff.js.map
@@ -0,0 +1,16 @@
1
+ export interface TransformOptions {
2
+ operation: string;
3
+ text: string;
4
+ maskChar?: string;
5
+ maxLength?: number;
6
+ wrapWidth?: number;
7
+ ellipsis?: string;
8
+ }
9
+ export interface TransformResult {
10
+ original: string;
11
+ transformed: string;
12
+ operation: string;
13
+ details?: Record<string, unknown>;
14
+ }
15
+ export declare function transformString(options: TransformOptions): TransformResult;
16
+ //# sourceMappingURL=string-transformer.d.ts.map