indusagi-coding-agent 0.1.23 → 0.1.25

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 (236) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/README.md +2 -0
  3. package/dist/cli/args.d.ts +117 -1
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +221 -52
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/config-selector.d.ts +58 -2
  8. package/dist/cli/config-selector.d.ts.map +1 -1
  9. package/dist/cli/config-selector.js +130 -12
  10. package/dist/cli/config-selector.js.map +1 -1
  11. package/dist/cli/file-processor.d.ts +70 -2
  12. package/dist/cli/file-processor.d.ts.map +1 -1
  13. package/dist/cli/file-processor.js +240 -15
  14. package/dist/cli/file-processor.js.map +1 -1
  15. package/dist/cli/list-models.d.ts +63 -3
  16. package/dist/cli/list-models.d.ts.map +1 -1
  17. package/dist/cli/list-models.js +202 -27
  18. package/dist/cli/list-models.js.map +1 -1
  19. package/dist/cli/login-handler.d.ts +82 -8
  20. package/dist/cli/login-handler.d.ts.map +1 -1
  21. package/dist/cli/login-handler.js +410 -77
  22. package/dist/cli/login-handler.js.map +1 -1
  23. package/dist/cli/session-picker.d.ts +74 -2
  24. package/dist/cli/session-picker.d.ts.map +1 -1
  25. package/dist/cli/session-picker.js +236 -12
  26. package/dist/cli/session-picker.js.map +1 -1
  27. package/dist/core/agent-session.d.ts +214 -9
  28. package/dist/core/agent-session.d.ts.map +1 -1
  29. package/dist/core/agent-session.js +214 -9
  30. package/dist/core/agent-session.js.map +1 -1
  31. package/dist/core/bash-executor.d.ts +302 -12
  32. package/dist/core/bash-executor.d.ts.map +1 -1
  33. package/dist/core/bash-executor.js +302 -12
  34. package/dist/core/bash-executor.js.map +1 -1
  35. package/dist/core/diagnostics.d.ts +191 -0
  36. package/dist/core/diagnostics.d.ts.map +1 -1
  37. package/dist/core/diagnostics.js +142 -0
  38. package/dist/core/diagnostics.js.map +1 -1
  39. package/dist/core/discover-packages.d.ts +6 -0
  40. package/dist/core/discover-packages.d.ts.map +1 -0
  41. package/dist/core/discover-packages.js +62 -0
  42. package/dist/core/discover-packages.js.map +1 -0
  43. package/dist/core/event-bus.d.ts +146 -0
  44. package/dist/core/event-bus.d.ts.map +1 -1
  45. package/dist/core/event-bus.js +93 -0
  46. package/dist/core/event-bus.js.map +1 -1
  47. package/dist/core/export-html/ansi-to-html.d.ts +4 -0
  48. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
  49. package/dist/core/export-html/ansi-to-html.js +4 -0
  50. package/dist/core/export-html/ansi-to-html.js.map +1 -1
  51. package/dist/core/export-html/index.d.ts +128 -0
  52. package/dist/core/export-html/index.d.ts.map +1 -1
  53. package/dist/core/export-html/index.js +128 -0
  54. package/dist/core/export-html/index.js.map +1 -1
  55. package/dist/core/export-html/tool-renderer.d.ts +4 -0
  56. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  57. package/dist/core/export-html/tool-renderer.js +4 -0
  58. package/dist/core/export-html/tool-renderer.js.map +1 -1
  59. package/dist/core/keybindings.d.ts +142 -0
  60. package/dist/core/keybindings.d.ts.map +1 -1
  61. package/dist/core/keybindings.js +142 -0
  62. package/dist/core/keybindings.js.map +1 -1
  63. package/dist/core/model-registry.d.ts +98 -1
  64. package/dist/core/model-registry.d.ts.map +1 -1
  65. package/dist/core/model-registry.js +98 -1
  66. package/dist/core/model-registry.js.map +1 -1
  67. package/dist/core/model-resolver.d.ts +99 -1
  68. package/dist/core/model-resolver.d.ts.map +1 -1
  69. package/dist/core/model-resolver.js +99 -1
  70. package/dist/core/model-resolver.js.map +1 -1
  71. package/dist/core/prompt-templates.js.map +1 -1
  72. package/dist/core/sdk.d.ts.map +1 -1
  73. package/dist/core/sdk.js +2 -0
  74. package/dist/core/sdk.js.map +1 -1
  75. package/dist/core/session-manager.d.ts +127 -0
  76. package/dist/core/session-manager.d.ts.map +1 -1
  77. package/dist/core/session-manager.js +125 -0
  78. package/dist/core/session-manager.js.map +1 -1
  79. package/dist/core/skills.js.map +1 -1
  80. package/dist/core/subagents.js.map +1 -1
  81. package/dist/core/tools/bash.d.ts +391 -11
  82. package/dist/core/tools/bash.d.ts.map +1 -1
  83. package/dist/core/tools/bash.js +269 -2
  84. package/dist/core/tools/bash.js.map +1 -1
  85. package/dist/core/tools/bg-process.d.ts +49 -0
  86. package/dist/core/tools/bg-process.d.ts.map +1 -0
  87. package/dist/core/tools/bg-process.js +69 -0
  88. package/dist/core/tools/bg-process.js.map +1 -0
  89. package/dist/core/tools/edit.d.ts +284 -6
  90. package/dist/core/tools/edit.d.ts.map +1 -1
  91. package/dist/core/tools/edit.js +238 -0
  92. package/dist/core/tools/edit.js.map +1 -1
  93. package/dist/core/tools/find.d.ts +169 -5
  94. package/dist/core/tools/find.d.ts.map +1 -1
  95. package/dist/core/tools/find.js +136 -0
  96. package/dist/core/tools/find.js.map +1 -1
  97. package/dist/core/tools/grep.d.ts +285 -5
  98. package/dist/core/tools/grep.d.ts.map +1 -1
  99. package/dist/core/tools/grep.js +247 -0
  100. package/dist/core/tools/grep.js.map +1 -1
  101. package/dist/core/tools/index.d.ts +45 -0
  102. package/dist/core/tools/index.d.ts.map +1 -1
  103. package/dist/core/tools/index.js +15 -0
  104. package/dist/core/tools/index.js.map +1 -1
  105. package/dist/core/tools/ls.d.ts +6 -0
  106. package/dist/core/tools/ls.d.ts.map +1 -1
  107. package/dist/core/tools/ls.js +6 -0
  108. package/dist/core/tools/ls.js.map +1 -1
  109. package/dist/core/tools/read.d.ts +308 -7
  110. package/dist/core/tools/read.d.ts.map +1 -1
  111. package/dist/core/tools/read.js +231 -0
  112. package/dist/core/tools/read.js.map +1 -1
  113. package/dist/core/tools/registry.d.ts +17 -0
  114. package/dist/core/tools/registry.d.ts.map +1 -0
  115. package/dist/core/tools/registry.js +108 -0
  116. package/dist/core/tools/registry.js.map +1 -0
  117. package/dist/core/tools/webfetch.d.ts +118 -3
  118. package/dist/core/tools/webfetch.d.ts.map +1 -1
  119. package/dist/core/tools/webfetch.js +118 -3
  120. package/dist/core/tools/webfetch.js.map +1 -1
  121. package/dist/core/tools/websearch.d.ts +130 -3
  122. package/dist/core/tools/websearch.d.ts.map +1 -1
  123. package/dist/core/tools/websearch.js +130 -3
  124. package/dist/core/tools/websearch.js.map +1 -1
  125. package/dist/core/tools/write.d.ts +251 -5
  126. package/dist/core/tools/write.d.ts.map +1 -1
  127. package/dist/core/tools/write.js +210 -0
  128. package/dist/core/tools/write.js.map +1 -1
  129. package/dist/main.d.ts.map +1 -1
  130. package/dist/main.js +12 -1
  131. package/dist/main.js.map +1 -1
  132. package/dist/modes/interactive/components/assistant-message.d.ts +164 -1
  133. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  134. package/dist/modes/interactive/components/assistant-message.js +164 -1
  135. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  136. package/dist/modes/interactive/components/bash-execution.d.ts +297 -1
  137. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  138. package/dist/modes/interactive/components/bash-execution.js +297 -1
  139. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  140. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  141. package/dist/modes/interactive/components/tool-execution.js +251 -1
  142. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  143. package/dist/modes/interactive/components/user-message.d.ts +186 -1
  144. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  145. package/dist/modes/interactive/components/user-message.js +186 -1
  146. package/dist/modes/interactive/components/user-message.js.map +1 -1
  147. package/dist/modes/interactive/interactive-mode.d.ts +1567 -13
  148. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  149. package/dist/modes/interactive/interactive-mode.js +1567 -13
  150. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  151. package/dist/modes/interactive/theme/theme.d.ts +422 -0
  152. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  153. package/dist/modes/interactive/theme/theme.js +422 -0
  154. package/dist/modes/interactive/theme/theme.js.map +1 -1
  155. package/dist/modes/print-mode.d.ts +538 -5
  156. package/dist/modes/print-mode.d.ts.map +1 -1
  157. package/dist/modes/print-mode.js +538 -5
  158. package/dist/modes/print-mode.js.map +1 -1
  159. package/dist/modes/rpc/rpc-client.d.ts +921 -8
  160. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  161. package/dist/modes/rpc/rpc-client.js +921 -8
  162. package/dist/modes/rpc/rpc-client.js.map +1 -1
  163. package/dist/modes/rpc/rpc-mode.d.ts +802 -9
  164. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  165. package/dist/modes/rpc/rpc-mode.js +802 -9
  166. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  167. package/dist/modes/rpc/rpc-types.d.ts +356 -3
  168. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  169. package/dist/modes/rpc/rpc-types.js +356 -3
  170. package/dist/modes/rpc/rpc-types.js.map +1 -1
  171. package/dist/modes/shared.d.ts +386 -0
  172. package/dist/modes/shared.d.ts.map +1 -0
  173. package/dist/modes/shared.js +543 -0
  174. package/dist/modes/shared.js.map +1 -0
  175. package/dist/utils/array.d.ts +389 -0
  176. package/dist/utils/array.d.ts.map +1 -0
  177. package/dist/utils/array.js +585 -0
  178. package/dist/utils/array.js.map +1 -0
  179. package/dist/utils/color-formatter.d.ts +318 -0
  180. package/dist/utils/color-formatter.d.ts.map +1 -0
  181. package/dist/utils/color-formatter.js +442 -0
  182. package/dist/utils/color-formatter.js.map +1 -0
  183. package/dist/utils/data-transformer.d.ts +326 -0
  184. package/dist/utils/data-transformer.d.ts.map +1 -0
  185. package/dist/utils/data-transformer.js +512 -0
  186. package/dist/utils/data-transformer.js.map +1 -0
  187. package/dist/utils/date-formatter.d.ts +281 -0
  188. package/dist/utils/date-formatter.d.ts.map +1 -0
  189. package/dist/utils/date-formatter.js +503 -0
  190. package/dist/utils/date-formatter.js.map +1 -0
  191. package/dist/utils/error-handler.d.ts +541 -0
  192. package/dist/utils/error-handler.d.ts.map +1 -0
  193. package/dist/utils/error-handler.js +726 -0
  194. package/dist/utils/error-handler.js.map +1 -0
  195. package/dist/utils/file-operations.d.ts +297 -0
  196. package/dist/utils/file-operations.d.ts.map +1 -0
  197. package/dist/utils/file-operations.js +505 -0
  198. package/dist/utils/file-operations.js.map +1 -0
  199. package/dist/utils/frontmatter.d.ts +268 -6
  200. package/dist/utils/frontmatter.d.ts.map +1 -1
  201. package/dist/utils/frontmatter.js +500 -21
  202. package/dist/utils/frontmatter.js.map +1 -1
  203. package/dist/utils/json-formatter.d.ts +259 -0
  204. package/dist/utils/json-formatter.d.ts.map +1 -0
  205. package/dist/utils/json-formatter.js +517 -0
  206. package/dist/utils/json-formatter.js.map +1 -0
  207. package/dist/utils/logger.d.ts +176 -0
  208. package/dist/utils/logger.d.ts.map +1 -0
  209. package/dist/utils/logger.js +346 -0
  210. package/dist/utils/logger.js.map +1 -0
  211. package/dist/utils/markdown-formatter.d.ts +211 -0
  212. package/dist/utils/markdown-formatter.d.ts.map +1 -0
  213. package/dist/utils/markdown-formatter.js +482 -0
  214. package/dist/utils/markdown-formatter.js.map +1 -0
  215. package/dist/utils/path-validator.d.ts +603 -0
  216. package/dist/utils/path-validator.d.ts.map +1 -0
  217. package/dist/utils/path-validator.js +870 -0
  218. package/dist/utils/path-validator.js.map +1 -0
  219. package/dist/utils/string-formatter.d.ts +609 -0
  220. package/dist/utils/string-formatter.d.ts.map +1 -0
  221. package/dist/utils/string-formatter.js +806 -0
  222. package/dist/utils/string-formatter.js.map +1 -0
  223. package/dist/utils/type-guards.d.ts +629 -0
  224. package/dist/utils/type-guards.d.ts.map +1 -0
  225. package/dist/utils/type-guards.js +662 -0
  226. package/dist/utils/type-guards.js.map +1 -0
  227. package/docs/COMPLETE-GUIDE.md +300 -0
  228. package/docs/COMPREHENSIVE-CLI-SUMMARY.md +900 -0
  229. package/docs/MODES-ARCHITECTURE.md +565 -0
  230. package/docs/PRINT-MODE-GUIDE.md +456 -0
  231. package/docs/RPC-GUIDE.md +705 -0
  232. package/docs/UTILS-IMPLEMENTATION-SUMMARY.md +647 -0
  233. package/docs/UTILS-MODULE-OVERVIEW.md +1480 -0
  234. package/docs/UTILS-QA-CHECKLIST.md +1061 -0
  235. package/docs/UTILS-USAGE-GUIDE.md +1419 -0
  236. package/package.json +7 -3
@@ -0,0 +1,806 @@
1
+ /**
2
+ * @fileoverview String Formatter Utilities
3
+ *
4
+ * Comprehensive string manipulation and formatting utilities for text transformation,
5
+ * case conversion, truncation, padding, and other common string operations.
6
+ *
7
+ * This module provides type-safe, well-tested functions for:
8
+ * - Case conversion (camelCase, snake_case, kebab-case, Title Case)
9
+ * - String truncation and padding
10
+ * - Character manipulation (reverse, capitalize, decapitalize)
11
+ * - String searching and matching
12
+ * - String interpolation patterns
13
+ * - Multi-line text handling
14
+ *
15
+ * @example
16
+ * // Case conversion
17
+ * toTitleCase('hello world') // "Hello World"
18
+ * toCamelCase('hello-world') // "helloWorld"
19
+ * toSnakeCase('helloWorld') // "hello_world"
20
+ * toKebabCase('HelloWorld') // "hello-world"
21
+ *
22
+ * @example
23
+ * // String manipulation
24
+ * truncate('hello world', 8) // "hello..."
25
+ * capitalize('hello') // "Hello"
26
+ * reverse('hello') // "olleh"
27
+ * repeat('ab', 3) // "ababab"
28
+ *
29
+ * @example
30
+ * // Padding and spacing
31
+ * padStart('5', 3, '0') // "005"
32
+ * padEnd('5', 3, '0') // "500"
33
+ * repeat(' ', 4) // " "
34
+ */
35
+ /**
36
+ * Converts a string to Title Case (each word capitalized)
37
+ *
38
+ * Splits on spaces, hyphens, underscores, and camelCase boundaries.
39
+ * Properly handles multiple delimiters and preserves word order.
40
+ *
41
+ * @param str - The input string to convert
42
+ * @returns The title-cased string
43
+ *
44
+ * @example
45
+ * toTitleCase('hello world') // "Hello World"
46
+ * toTitleCase('hello-world') // "Hello World"
47
+ * toTitleCase('hello_world') // "Hello World"
48
+ * toTitleCase('helloWorld') // "Hello World"
49
+ * toTitleCase('HELLO WORLD') // "Hello World"
50
+ * toTitleCase('hello world') // "Hello World" (collapses spaces)
51
+ * toTitleCase('') // ""
52
+ * toTitleCase('a') // "A"
53
+ *
54
+ * @throws Does not throw; returns empty string for null/undefined
55
+ */
56
+ export function toTitleCase(str) {
57
+ if (!str)
58
+ return '';
59
+ // Split on common delimiters and camelCase boundaries
60
+ const words = str
61
+ .replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase
62
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2') // HTMLParser -> HTML Parser
63
+ .replace(/[-_\s]+/g, ' ') // hyphens, underscores, spaces
64
+ .trim()
65
+ .split(/\s+/);
66
+ return words
67
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
68
+ .join(' ');
69
+ }
70
+ /**
71
+ * Converts a string to camelCase format
72
+ *
73
+ * Useful for JavaScript variable names and object properties.
74
+ * Handles multiple input formats and preserves acronyms intelligently.
75
+ *
76
+ * @param str - The input string to convert
77
+ * @returns The camelCased string
78
+ *
79
+ * @example
80
+ * toCamelCase('hello world') // "helloWorld"
81
+ * toCamelCase('hello-world') // "helloWorld"
82
+ * toCamelCase('hello_world') // "helloWorld"
83
+ * toCamelCase('HelloWorld') // "helloWorld"
84
+ * toCamelCase('HELLO_WORLD') // "helloWorld"
85
+ * toCamelCase('get-user-id') // "getUserId"
86
+ * toCamelCase('') // ""
87
+ * toCamelCase('x') // "x"
88
+ *
89
+ * @edge-cases
90
+ * - Empty strings return empty string
91
+ * - Single characters preserved as lowercase
92
+ * - Multiple consecutive delimiters treated as single
93
+ */
94
+ export function toCamelCase(str) {
95
+ if (!str)
96
+ return '';
97
+ const words = str
98
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
99
+ .replace(/[-_\s]+/g, ' ')
100
+ .trim()
101
+ .split(/\s+/);
102
+ if (words.length === 0)
103
+ return '';
104
+ // First word lowercase, rest title case
105
+ return (words[0].toLowerCase() +
106
+ words
107
+ .slice(1)
108
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
109
+ .join(''));
110
+ }
111
+ /**
112
+ * Converts a string to snake_case format
113
+ *
114
+ * Useful for Python variable names, database columns, and file names.
115
+ * Handles acronyms and consecutive uppercase letters.
116
+ *
117
+ * @param str - The input string to convert
118
+ * @returns The snake_cased string
119
+ *
120
+ * @example
121
+ * toSnakeCase('HelloWorld') // "hello_world"
122
+ * toSnakeCase('hello world') // "hello_world"
123
+ * toSnakeCase('hello-world') // "hello_world"
124
+ * toSnakeCase('helloWorld') // "hello_world"
125
+ * toSnakeCase('HTMLParser') // "html_parser"
126
+ * toSnakeCase('getHTTPResponseCode') // "get_http_response_code"
127
+ * toSnakeCase('') // ""
128
+ *
129
+ * @edge-cases
130
+ * - Consecutive uppercase letters treated as acronym
131
+ * - Preserves existing underscores
132
+ * - Collapses multiple delimiters
133
+ */
134
+ export function toSnakeCase(str) {
135
+ if (!str)
136
+ return '';
137
+ return str
138
+ .replace(/([a-z])([A-Z])/g, '$1_$2') // camelCase
139
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') // HTMLParser
140
+ .replace(/[-\s]+/g, '_') // hyphens, spaces
141
+ .toLowerCase();
142
+ }
143
+ /**
144
+ * Converts a string to kebab-case format
145
+ *
146
+ * Useful for URL slugs, CSS class names, and configuration keys.
147
+ * Similar to snake_case but uses hyphens instead of underscores.
148
+ *
149
+ * @param str - The input string to convert
150
+ * @returns The kebab-cased string
151
+ *
152
+ * @example
153
+ * toKebabCase('HelloWorld') // "hello-world"
154
+ * toKebabCase('hello world') // "hello-world"
155
+ * toKebabCase('hello_world') // "hello-world"
156
+ * toKebabCase('helloWorld') // "hello-world"
157
+ * toKebabCase('get-user-data') // "get-user-data"
158
+ * toKebabCase('GetUserData') // "get-user-data"
159
+ * toKebabCase('') // ""
160
+ *
161
+ * @edge-cases
162
+ * - URL-safe; suitable for web slugs
163
+ * - Collapses multiple delimiters
164
+ */
165
+ export function toKebabCase(str) {
166
+ if (!str)
167
+ return '';
168
+ return str
169
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
170
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
171
+ .replace(/[_\s]+/g, '-')
172
+ .toLowerCase();
173
+ }
174
+ /**
175
+ * Truncates a string to a maximum length with optional ellipsis
176
+ *
177
+ * Intelligently handles word boundaries when requested.
178
+ * Preserves readability by truncating at word ends when possible.
179
+ *
180
+ * @param str - The input string to truncate
181
+ * @param maxLength - Maximum length of result (including ellipsis if added)
182
+ * @param ellipsis - String to append if truncated (default: '...')
183
+ * @param wordBoundary - If true, truncate at word boundary (default: false)
184
+ * @returns The truncated string
185
+ *
186
+ * @example
187
+ * truncate('hello world', 8) // "hello..."
188
+ * truncate('hello world', 11) // "hello world"
189
+ * truncate('hello world', 8, '…') // "hello…"
190
+ * truncate('hello world', 8, '...', true) // "hello..." (word boundary)
191
+ * truncate('supercalifragilisticexpialidocious', 10) // "supercali..."
192
+ * truncate('', 5) // ""
193
+ * truncate('hi', 10) // "hi"
194
+ *
195
+ * @edge-cases
196
+ * - If maxLength < ellipsis.length, returns ellipsis
197
+ * - Word boundary mode may result in shorter strings
198
+ * - Empty strings return empty string
199
+ */
200
+ export function truncate(str, maxLength, ellipsis = '...', wordBoundary = false) {
201
+ if (!str || maxLength < 1)
202
+ return str;
203
+ if (str.length <= maxLength)
204
+ return str;
205
+ const truncateLength = Math.max(0, maxLength - ellipsis.length);
206
+ if (truncateLength === 0)
207
+ return ellipsis;
208
+ let truncated = str.slice(0, truncateLength);
209
+ if (wordBoundary) {
210
+ const lastSpace = truncated.lastIndexOf(' ');
211
+ if (lastSpace > 0) {
212
+ truncated = truncated.slice(0, lastSpace);
213
+ }
214
+ }
215
+ return truncated + ellipsis;
216
+ }
217
+ /**
218
+ * Pads the start of a string with a fill string
219
+ *
220
+ * Complements JavaScript's native padStart with custom handling.
221
+ * Useful for formatting numbers, codes, and aligned output.
222
+ *
223
+ * @param str - The input string to pad
224
+ * @param length - Target length
225
+ * @param fillStr - String to repeat for padding (default: ' ')
226
+ * @returns The padded string
227
+ *
228
+ * @example
229
+ * padStart('5', 3, '0') // "005"
230
+ * padStart('42', 4, '0') // "0042"
231
+ * padStart('hello', 10) // " hello"
232
+ * padStart('hello', 3) // "hello" (no padding if already longer)
233
+ * padStart('', 3, '-') // "---"
234
+ * padStart('a', 5, 'xy') // "xyxya"
235
+ *
236
+ * @edge-cases
237
+ * - If target length is less than string length, returns original
238
+ * - Multiple character fill strings repeat as needed
239
+ */
240
+ export function padStart(str, length, fillStr = ' ') {
241
+ if (!fillStr || str.length >= length)
242
+ return str;
243
+ const padLength = length - str.length;
244
+ const fill = fillStr.repeat(Math.ceil(padLength / fillStr.length));
245
+ return fill.slice(0, padLength) + str;
246
+ }
247
+ /**
248
+ * Pads the end of a string with a fill string
249
+ *
250
+ * Opposite of padStart. Useful for right-aligned formatting.
251
+ *
252
+ * @param str - The input string to pad
253
+ * @param length - Target length
254
+ * @param fillStr - String to repeat for padding (default: ' ')
255
+ * @returns The padded string
256
+ *
257
+ * @example
258
+ * padEnd('5', 3, '0') // "500"
259
+ * padEnd('hello', 10) // "hello "
260
+ * padEnd('hello', 3) // "hello" (no padding if already longer)
261
+ * padEnd('', 3, '-') // "---"
262
+ * padEnd('a', 5, 'xy') // "axyxy"
263
+ *
264
+ * @edge-cases
265
+ * - If target length is less than string length, returns original
266
+ * - Multiple character fill strings repeat as needed
267
+ */
268
+ export function padEnd(str, length, fillStr = ' ') {
269
+ if (!fillStr || str.length >= length)
270
+ return str;
271
+ const padLength = length - str.length;
272
+ const fill = fillStr.repeat(Math.ceil(padLength / fillStr.length));
273
+ return str + fill.slice(0, padLength);
274
+ }
275
+ /**
276
+ * Repeats a string a specified number of times
277
+ *
278
+ * Native alternative with better error handling.
279
+ *
280
+ * @param str - The string to repeat
281
+ * @param count - Number of times to repeat (default: 1)
282
+ * @returns The repeated string
283
+ *
284
+ * @example
285
+ * repeat('ab', 3) // "ababab"
286
+ * repeat('x', 5) // "xxxxx"
287
+ * repeat(' ', 4) // " "
288
+ * repeat('hello', 0) // ""
289
+ * repeat('', 5) // ""
290
+ * repeat('-', 10) // "----------"
291
+ *
292
+ * @edge-cases
293
+ * - Zero or negative count returns empty string
294
+ * - Empty string repeated returns empty string
295
+ */
296
+ export function repeat(str, count = 1) {
297
+ if (count <= 0 || !str)
298
+ return '';
299
+ return str.repeat(Math.max(0, count));
300
+ }
301
+ /**
302
+ * Capitalizes the first character of a string
303
+ *
304
+ * Leaves remaining characters unchanged (unlike toTitleCase).
305
+ *
306
+ * @param str - The input string
307
+ * @returns String with first character uppercase
308
+ *
309
+ * @example
310
+ * capitalize('hello') // "Hello"
311
+ * capitalize('HELLO') // "HELLO"
312
+ * capitalize('helloWorld') // "HelloWorld"
313
+ * capitalize('') // ""
314
+ * capitalize('a') // "A"
315
+ * capitalize('already Capitalized') // "Already Capitalized"
316
+ *
317
+ * @edge-cases
318
+ * - Empty string returns empty string
319
+ * - Doesn't change case of remaining characters
320
+ */
321
+ export function capitalize(str) {
322
+ if (!str)
323
+ return '';
324
+ return str.charAt(0).toUpperCase() + str.slice(1);
325
+ }
326
+ /**
327
+ * Decapitalizes the first character of a string
328
+ *
329
+ * Opposite of capitalize. Useful for variable naming.
330
+ *
331
+ * @param str - The input string
332
+ * @returns String with first character lowercase
333
+ *
334
+ * @example
335
+ * decapitalize('Hello') // "hello"
336
+ * decapitalize('HELLO') // "hELLO"
337
+ * decapitalize('hello') // "hello"
338
+ * decapitalize('') // ""
339
+ * decapitalize('A') // "a"
340
+ *
341
+ * @edge-cases
342
+ * - Empty string returns empty string
343
+ * - Doesn't change case of remaining characters
344
+ */
345
+ export function decapitalize(str) {
346
+ if (!str)
347
+ return '';
348
+ return str.charAt(0).toLowerCase() + str.slice(1);
349
+ }
350
+ /**
351
+ * Reverses a string character by character
352
+ *
353
+ * Properly handles Unicode and multi-byte characters.
354
+ *
355
+ * @param str - The input string to reverse
356
+ * @returns The reversed string
357
+ *
358
+ * @example
359
+ * reverse('hello') // "olleh"
360
+ * reverse('abc') // "cba"
361
+ * reverse('') // ""
362
+ * reverse('a') // "a"
363
+ * reverse('123') // "321"
364
+ * reverse('hello world') // "dlrow olleh"
365
+ *
366
+ * @edge-cases
367
+ * - Empty string returns empty string
368
+ * - Single character returns same character
369
+ */
370
+ export function reverse(str) {
371
+ return str.split('').reverse().join('');
372
+ }
373
+ /**
374
+ * Checks if a string starts with a given prefix
375
+ *
376
+ * Case-sensitive by default. Wrapper with optional case-insensitivity.
377
+ *
378
+ * @param str - The string to search
379
+ * @param prefix - The prefix to check for
380
+ * @param caseSensitive - If false, comparison is case-insensitive (default: true)
381
+ * @returns true if string starts with prefix
382
+ *
383
+ * @example
384
+ * startsWith('hello world', 'hello') // true
385
+ * startsWith('hello world', 'world') // false
386
+ * startsWith('Hello World', 'hello', false) // true
387
+ * startsWith('', '') // true
388
+ * startsWith('a', 'abc') // false
389
+ * startsWith('test.js', '.js') // false
390
+ *
391
+ * @edge-cases
392
+ * - Empty prefix always returns true
393
+ * - Empty string only matches empty prefix
394
+ * - Case-insensitive comparison lowercases both sides
395
+ */
396
+ export function startsWith(str, prefix, caseSensitive = true) {
397
+ if (caseSensitive) {
398
+ return str.startsWith(prefix);
399
+ }
400
+ return str.toLowerCase().startsWith(prefix.toLowerCase());
401
+ }
402
+ /**
403
+ * Checks if a string ends with a given suffix
404
+ *
405
+ * Case-sensitive by default. Wrapper with optional case-insensitivity.
406
+ *
407
+ * @param str - The string to search
408
+ * @param suffix - The suffix to check for
409
+ * @param caseSensitive - If false, comparison is case-insensitive (default: true)
410
+ * @returns true if string ends with suffix
411
+ *
412
+ * @example
413
+ * endsWith('hello world', 'world') // true
414
+ * endsWith('hello world', 'hello') // false
415
+ * endsWith('test.js', '.js') // true
416
+ * endsWith('Test.JS', '.js', false) // true
417
+ * endsWith('', '') // true
418
+ * endsWith('a', 'abc') // false
419
+ *
420
+ * @edge-cases
421
+ * - Empty suffix always returns true
422
+ * - Empty string only matches empty suffix
423
+ * - Case-insensitive comparison lowercases both sides
424
+ */
425
+ export function endsWith(str, suffix, caseSensitive = true) {
426
+ if (caseSensitive) {
427
+ return str.endsWith(suffix);
428
+ }
429
+ return str.toLowerCase().endsWith(suffix.toLowerCase());
430
+ }
431
+ /**
432
+ * Checks if a string includes a given substring
433
+ *
434
+ * Case-sensitive by default. Wrapper with optional case-insensitivity.
435
+ *
436
+ * @param str - The string to search
437
+ * @param search - The substring to look for
438
+ * @param caseSensitive - If false, comparison is case-insensitive (default: true)
439
+ * @returns true if substring is found
440
+ *
441
+ * @example
442
+ * includes('hello world', 'lo wo') // true
443
+ * includes('hello world', 'xyz') // false
444
+ * includes('Hello World', 'world', false) // true
445
+ * includes('', '') // true
446
+ * includes('test', '') // true
447
+ * includes('JavaScript', 'Script', false) // true
448
+ *
449
+ * @edge-cases
450
+ * - Empty search string always returns true
451
+ * - Empty string only matches empty search
452
+ * - Case-insensitive comparison lowercases both sides
453
+ */
454
+ export function includes(str, search, caseSensitive = true) {
455
+ if (caseSensitive) {
456
+ return str.includes(search);
457
+ }
458
+ return str.toLowerCase().includes(search.toLowerCase());
459
+ }
460
+ /**
461
+ * Replaces all occurrences of a string pattern
462
+ *
463
+ * Works with string or regex patterns. Simpler than native replace for all occurrences.
464
+ *
465
+ * @param str - The string to search in
466
+ * @param search - String or regex to search for
467
+ * @param replacement - String or function to replace with
468
+ * @returns The modified string
469
+ *
470
+ * @example
471
+ * replaceAll('hello hello', 'hello', 'hi') // "hi hi"
472
+ * replaceAll('a-b-c', '-', ' ') // "a b c"
473
+ * replaceAll('test test test', 'test', 'result') // "result result result"
474
+ * replaceAll('', 'x', 'y') // ""
475
+ * replaceAll('abc', 'x', 'y') // "abc"
476
+ * replaceAll('aaa', 'a', 'b') // "bbb"
477
+ *
478
+ * @edge-cases
479
+ * - Empty search string doesn't replace
480
+ * - Empty replacement removes the pattern
481
+ * - Can use regex with global flag
482
+ */
483
+ export function replaceAll(str, search, replacement) {
484
+ if (!str || !search)
485
+ return str;
486
+ if (typeof search === 'string') {
487
+ if (!search)
488
+ return str;
489
+ return str.split(search).join(typeof replacement === 'string' ? replacement : '');
490
+ }
491
+ // For regex, ensure global flag is set
492
+ const flags = search.global ? search.flags : search.flags + 'g';
493
+ const regex = new RegExp(search.source, flags);
494
+ return str.replace(regex, replacement);
495
+ }
496
+ /**
497
+ * Splits a string by a delimiter with optional trimming
498
+ *
499
+ * Useful for parsing comma-separated values and other delimited data.
500
+ *
501
+ * @param str - The string to split
502
+ * @param delimiter - The delimiter to split on
503
+ * @param trim - If true, trim each part (default: false)
504
+ * @returns Array of string parts
505
+ *
506
+ * @example
507
+ * splitStr('a,b,c', ',') // ["a", "b", "c"]
508
+ * splitStr('a, b, c', ',', true) // ["a", "b", "c"]
509
+ * splitStr('a|b|c', '|') // ["a", "b", "c"]
510
+ * splitStr('', ',') // [""]
511
+ * splitStr('abc', ',') // ["abc"]
512
+ * splitStr('a b c', ' ') // ["a", "b", "c"]
513
+ *
514
+ * @edge-cases
515
+ * - Empty delimiter returns array with original string
516
+ * - Empty string returns array with empty string
517
+ * - Consecutive delimiters create empty elements
518
+ */
519
+ export function splitStr(str, delimiter, trim = false) {
520
+ if (!delimiter)
521
+ return [str];
522
+ const parts = str.split(delimiter);
523
+ return trim ? parts.map(part => part.trim()) : parts;
524
+ }
525
+ /**
526
+ * Joins an array of strings with a delimiter
527
+ *
528
+ * Enhanced version of Array.join with filtering options.
529
+ *
530
+ * @param parts - Array of strings to join
531
+ * @param delimiter - The delimiter to use
532
+ * @param filterEmpty - If true, remove empty strings (default: false)
533
+ * @returns The joined string
534
+ *
535
+ * @example
536
+ * joinStr(['a', 'b', 'c'], ',') // "a,b,c"
537
+ * joinStr(['a', '', 'c'], ',') // "a,,c"
538
+ * joinStr(['a', '', 'c'], ',', true) // "a,c"
539
+ * joinStr([], ',') // ""
540
+ * joinStr(['single'], ',') // "single"
541
+ * joinStr(['path', 'to', 'file'], '/') // "path/to/file"
542
+ *
543
+ * @edge-cases
544
+ * - Empty array returns empty string
545
+ * - Single element array returns that element
546
+ * - Filter empty removes all empty strings before joining
547
+ */
548
+ export function joinStr(parts, delimiter, filterEmpty = false) {
549
+ const filtered = filterEmpty ? parts.filter(p => p.length > 0) : parts;
550
+ return filtered.join(delimiter);
551
+ }
552
+ /**
553
+ * Counts occurrences of a substring
554
+ *
555
+ * Useful for validation and analysis of string content.
556
+ *
557
+ * @param str - The string to search in
558
+ * @param search - The substring to count
559
+ * @param caseSensitive - If false, comparison is case-insensitive (default: true)
560
+ * @returns Number of occurrences
561
+ *
562
+ * @example
563
+ * countOccurrences('hello hello world', 'hello') // 2
564
+ * countOccurrences('aaa', 'a') // 3
565
+ * countOccurrences('test', 'x') // 0
566
+ * countOccurrences('', 'x') // 0
567
+ * countOccurrences('Hello HELLO hello', 'hello', false) // 3
568
+ * countOccurrences('ababab', 'ab') // 3
569
+ *
570
+ * @edge-cases
571
+ * - Empty search string returns 0
572
+ * - Empty string returns 0
573
+ * - Case-insensitive counts all variations
574
+ */
575
+ export function countOccurrences(str, search, caseSensitive = true) {
576
+ if (!str || !search)
577
+ return 0;
578
+ const searchStr = caseSensitive ? search : search.toLowerCase();
579
+ const compareStr = caseSensitive ? str : str.toLowerCase();
580
+ const matches = compareStr.match(new RegExp(searchStr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'));
581
+ return matches ? matches.length : 0;
582
+ }
583
+ /**
584
+ * String interpolation helper
585
+ *
586
+ * Replaces {key} patterns with provided values.
587
+ * Useful for templates and dynamic strings.
588
+ *
589
+ * @param template - Template string with {key} placeholders
590
+ * @param values - Object with key-value pairs
591
+ * @returns The interpolated string
592
+ *
593
+ * @example
594
+ * interpolate('Hello {name}!', { name: 'World' }) // "Hello World!"
595
+ * interpolate('{x} + {y} = {z}', { x: '1', y: '2', z: '3' }) // "1 + 2 = 3"
596
+ * interpolate('No placeholders', {}) // "No placeholders"
597
+ * interpolate('Missing {value}', {}) // "Missing {value}"
598
+ * interpolate('', {}) // ""
599
+ *
600
+ * @edge-cases
601
+ * - Missing keys leave placeholder unchanged
602
+ * - Nested objects converted via toString()
603
+ * - Undefined values become "undefined"
604
+ */
605
+ export function interpolate(template, values) {
606
+ return template.replace(/{(\w+)}/g, (match, key) => {
607
+ return key in values ? String(values[key]) : match;
608
+ });
609
+ }
610
+ /**
611
+ * Removes leading and trailing whitespace
612
+ *
613
+ * Enhanced trim with custom character removal.
614
+ *
615
+ * @param str - The string to trim
616
+ * @param chars - Characters to remove (default: whitespace)
617
+ * @returns The trimmed string
618
+ *
619
+ * @example
620
+ * trimStr(' hello ') // "hello"
621
+ * trimStr('xxxhelloxxx', 'x') // "hello"
622
+ * trimStr(' hello ') // "hello"
623
+ * trimStr('') // ""
624
+ * trimStr('---test---', '-') // "test"
625
+ *
626
+ * @edge-cases
627
+ * - Empty string returns empty string
628
+ * - Only specified characters are removed from edges
629
+ */
630
+ export function trimStr(str, chars) {
631
+ if (!str)
632
+ return '';
633
+ if (!chars) {
634
+ return str.trim();
635
+ }
636
+ const charSet = new Set(chars);
637
+ let start = 0;
638
+ let end = str.length;
639
+ while (start < end && charSet.has(str[start]))
640
+ start++;
641
+ while (end > start && charSet.has(str[end - 1]))
642
+ end--;
643
+ return str.slice(start, end);
644
+ }
645
+ /**
646
+ * Wraps text to fit within a specified width
647
+ *
648
+ * Intelligently breaks long lines while preserving word integrity.
649
+ * Useful for terminal output and formatted text generation.
650
+ *
651
+ * @param str - The string to wrap
652
+ * @param options - Wrapping options
653
+ * @returns The wrapped string
654
+ *
655
+ * @example
656
+ * wrap('hello world this is a test', { width: 10 })
657
+ * // "hello\nworld this\nis a test"
658
+ *
659
+ * wrap('a very long line here', { width: 10, indent: ' ' })
660
+ * // "a very\n long line\n here"
661
+ *
662
+ * wrap('word1 word2 word3', { width: 10, wordBoundary: true })
663
+ * // "word1\nword2\nword3"
664
+ *
665
+ * @edge-cases
666
+ * - Words longer than width are broken
667
+ * - Existing line breaks are preserved
668
+ */
669
+ export function wrap(str, options = {}) {
670
+ const { width = 80, lineBreak = '\n', indent = '', wordBoundary = true } = options;
671
+ if (width <= 0)
672
+ return str;
673
+ const lines = str.split('\n');
674
+ return lines
675
+ .map(line => {
676
+ let result = '';
677
+ let currentLine = '';
678
+ const words = line.split(' ');
679
+ for (const word of words) {
680
+ if (currentLine.length + word.length + 1 > width) {
681
+ if (currentLine) {
682
+ result += currentLine + lineBreak;
683
+ currentLine = indent + word;
684
+ }
685
+ else {
686
+ // Word is longer than width
687
+ currentLine = indent + word;
688
+ }
689
+ }
690
+ else {
691
+ currentLine = currentLine ? currentLine + ' ' + word : word;
692
+ }
693
+ }
694
+ if (currentLine) {
695
+ result += currentLine;
696
+ }
697
+ return result;
698
+ })
699
+ .join(lineBreak);
700
+ }
701
+ /**
702
+ * Escapes special regex characters in a string
703
+ *
704
+ * Useful for creating regex patterns from user input.
705
+ *
706
+ * @param str - The string to escape
707
+ * @returns The escaped string safe for regex
708
+ *
709
+ * @example
710
+ * escapeRegex('(test)') // "\\(test\\)"
711
+ * escapeRegex('[a-z]') // "\\[a-z\\]"
712
+ * escapeRegex('price: $5.00') // "price: \\$5\\.00"
713
+ * escapeRegex('') // ""
714
+ *
715
+ * @edge-cases
716
+ * - All regex special characters are escaped
717
+ * - Empty string returns empty string
718
+ */
719
+ export function escapeRegex(str) {
720
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
721
+ }
722
+ /**
723
+ * Strips HTML tags from a string
724
+ *
725
+ * Simple tag removal; for complex HTML use a proper parser.
726
+ *
727
+ * @param str - The string with HTML tags
728
+ * @returns The text content without tags
729
+ *
730
+ * @example
731
+ * stripHtml('<p>Hello <b>World</b></p>') // "Hello World"
732
+ * stripHtml('No tags here') // "No tags here"
733
+ * stripHtml('<script>alert("xss")</script>') // "alert("xss")"
734
+ * stripHtml('') // ""
735
+ *
736
+ * @edge-cases
737
+ * - Doesn't validate HTML
738
+ * - Removes content inside tags
739
+ * - For security-sensitive use, use htmlspecialchars
740
+ */
741
+ export function stripHtml(str) {
742
+ if (!str)
743
+ return '';
744
+ return str.replace(/<[^>]*>/g, '');
745
+ }
746
+ /**
747
+ * Escapes HTML special characters
748
+ *
749
+ * Makes text safe for insertion into HTML.
750
+ *
751
+ * @param str - The string to escape
752
+ * @returns The escaped string
753
+ *
754
+ * @example
755
+ * escapeHtml('<script>') // "&lt;script&gt;"
756
+ * escapeHtml('Hello & Goodbye') // "Hello &amp; Goodbye"
757
+ * escapeHtml('Price: $5.00') // "Price: $5.00"
758
+ * escapeHtml('') // ""
759
+ * escapeHtml('"quoted"') // "&quot;quoted&quot;"
760
+ *
761
+ * @edge-cases
762
+ * - Critical for preventing XSS vulnerabilities
763
+ * - Converts &, <, >, ", '
764
+ */
765
+ export function escapeHtml(str) {
766
+ if (!str)
767
+ return '';
768
+ const map = {
769
+ '&': '&amp;',
770
+ '<': '&lt;',
771
+ '>': '&gt;',
772
+ '"': '&quot;',
773
+ "'": '&#039;',
774
+ };
775
+ return str.replace(/[&<>"']/g, char => map[char]);
776
+ }
777
+ /**
778
+ * Unescapes HTML special characters
779
+ *
780
+ * Reverses escapeHtml transformation.
781
+ *
782
+ * @param str - The escaped HTML string
783
+ * @returns The unescaped string
784
+ *
785
+ * @example
786
+ * unescapeHtml('&lt;script&gt;') // "<script>"
787
+ * unescapeHtml('Hello &amp; Goodbye') // "Hello & Goodbye"
788
+ * unescapeHtml('&quot;quoted&quot;') // ""quoted""
789
+ */
790
+ export function unescapeHtml(str) {
791
+ if (!str)
792
+ return '';
793
+ const map = {
794
+ '&amp;': '&',
795
+ '&lt;': '<',
796
+ '&gt;': '>',
797
+ '&quot;': '"',
798
+ '&#039;': "'",
799
+ };
800
+ let result = str;
801
+ for (const [entity, char] of Object.entries(map)) {
802
+ result = result.replace(new RegExp(entity, 'g'), char);
803
+ }
804
+ return result;
805
+ }
806
+ //# sourceMappingURL=string-formatter.js.map