pomera-ai-commander 1.1.1 → 1.2.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 (213) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +105 -680
  3. package/bin/pomera-ai-commander.js +62 -62
  4. package/core/__init__.py +65 -65
  5. package/core/app_context.py +482 -482
  6. package/core/async_text_processor.py +421 -421
  7. package/core/backup_manager.py +655 -655
  8. package/core/backup_recovery_manager.py +1199 -1033
  9. package/core/content_hash_cache.py +508 -508
  10. package/core/context_menu.py +313 -313
  11. package/core/data_directory.py +549 -0
  12. package/core/data_validator.py +1066 -1066
  13. package/core/database_connection_manager.py +744 -744
  14. package/core/database_curl_settings_manager.py +608 -608
  15. package/core/database_promera_ai_settings_manager.py +446 -446
  16. package/core/database_schema.py +411 -411
  17. package/core/database_schema_manager.py +395 -395
  18. package/core/database_settings_manager.py +1507 -1507
  19. package/core/database_settings_manager_interface.py +456 -456
  20. package/core/dialog_manager.py +734 -734
  21. package/core/diff_utils.py +239 -0
  22. package/core/efficient_line_numbers.py +540 -510
  23. package/core/error_handler.py +746 -746
  24. package/core/error_service.py +431 -431
  25. package/core/event_consolidator.py +511 -511
  26. package/core/mcp/__init__.py +43 -43
  27. package/core/mcp/find_replace_diff.py +334 -0
  28. package/core/mcp/protocol.py +288 -288
  29. package/core/mcp/schema.py +251 -251
  30. package/core/mcp/server_stdio.py +299 -299
  31. package/core/mcp/tool_registry.py +2699 -2345
  32. package/core/memento.py +275 -0
  33. package/core/memory_efficient_text_widget.py +711 -711
  34. package/core/migration_manager.py +914 -914
  35. package/core/migration_test_suite.py +1085 -1085
  36. package/core/migration_validator.py +1143 -1143
  37. package/core/optimized_find_replace.py +714 -714
  38. package/core/optimized_pattern_engine.py +424 -424
  39. package/core/optimized_search_highlighter.py +552 -552
  40. package/core/performance_monitor.py +674 -674
  41. package/core/persistence_manager.py +712 -712
  42. package/core/progressive_stats_calculator.py +632 -632
  43. package/core/regex_pattern_cache.py +529 -529
  44. package/core/regex_pattern_library.py +350 -350
  45. package/core/search_operation_manager.py +434 -434
  46. package/core/settings_defaults_registry.py +1087 -1087
  47. package/core/settings_integrity_validator.py +1111 -1111
  48. package/core/settings_serializer.py +557 -557
  49. package/core/settings_validator.py +1823 -1823
  50. package/core/smart_stats_calculator.py +709 -709
  51. package/core/statistics_update_manager.py +619 -619
  52. package/core/stats_config_manager.py +858 -858
  53. package/core/streaming_text_handler.py +723 -723
  54. package/core/task_scheduler.py +596 -596
  55. package/core/update_pattern_library.py +168 -168
  56. package/core/visibility_monitor.py +596 -596
  57. package/core/widget_cache.py +498 -498
  58. package/mcp.json +51 -61
  59. package/migrate_data.py +127 -0
  60. package/package.json +64 -57
  61. package/pomera.py +7883 -7482
  62. package/pomera_mcp_server.py +183 -144
  63. package/requirements.txt +33 -0
  64. package/scripts/Dockerfile.alpine +43 -0
  65. package/scripts/Dockerfile.gui-test +54 -0
  66. package/scripts/Dockerfile.linux +43 -0
  67. package/scripts/Dockerfile.test-linux +80 -0
  68. package/scripts/Dockerfile.ubuntu +39 -0
  69. package/scripts/README.md +53 -0
  70. package/scripts/build-all.bat +113 -0
  71. package/scripts/build-docker.bat +53 -0
  72. package/scripts/build-docker.sh +55 -0
  73. package/scripts/build-optimized.bat +101 -0
  74. package/scripts/build.sh +78 -0
  75. package/scripts/docker-compose.test.yml +27 -0
  76. package/scripts/docker-compose.yml +32 -0
  77. package/scripts/postinstall.js +62 -0
  78. package/scripts/requirements-minimal.txt +33 -0
  79. package/scripts/test-linux-simple.bat +28 -0
  80. package/scripts/validate-release-workflow.py +450 -0
  81. package/tools/__init__.py +4 -4
  82. package/tools/ai_tools.py +2891 -2891
  83. package/tools/ascii_art_generator.py +352 -352
  84. package/tools/base64_tools.py +183 -183
  85. package/tools/base_tool.py +511 -511
  86. package/tools/case_tool.py +308 -308
  87. package/tools/column_tools.py +395 -395
  88. package/tools/cron_tool.py +884 -884
  89. package/tools/curl_history.py +600 -600
  90. package/tools/curl_processor.py +1207 -1207
  91. package/tools/curl_settings.py +502 -502
  92. package/tools/curl_tool.py +5467 -5467
  93. package/tools/diff_viewer.py +1817 -1072
  94. package/tools/email_extraction_tool.py +248 -248
  95. package/tools/email_header_analyzer.py +425 -425
  96. package/tools/extraction_tools.py +250 -250
  97. package/tools/find_replace.py +2289 -1750
  98. package/tools/folder_file_reporter.py +1463 -1463
  99. package/tools/folder_file_reporter_adapter.py +480 -480
  100. package/tools/generator_tools.py +1216 -1216
  101. package/tools/hash_generator.py +255 -255
  102. package/tools/html_tool.py +656 -656
  103. package/tools/jsonxml_tool.py +729 -729
  104. package/tools/line_tools.py +419 -419
  105. package/tools/markdown_tools.py +561 -561
  106. package/tools/mcp_widget.py +1417 -1417
  107. package/tools/notes_widget.py +978 -973
  108. package/tools/number_base_converter.py +372 -372
  109. package/tools/regex_extractor.py +571 -571
  110. package/tools/slug_generator.py +310 -310
  111. package/tools/sorter_tools.py +458 -458
  112. package/tools/string_escape_tool.py +392 -392
  113. package/tools/text_statistics_tool.py +365 -365
  114. package/tools/text_wrapper.py +430 -430
  115. package/tools/timestamp_converter.py +421 -421
  116. package/tools/tool_loader.py +710 -710
  117. package/tools/translator_tools.py +522 -522
  118. package/tools/url_link_extractor.py +261 -261
  119. package/tools/url_parser.py +204 -204
  120. package/tools/whitespace_tools.py +355 -355
  121. package/tools/word_frequency_counter.py +146 -146
  122. package/core/__pycache__/__init__.cpython-313.pyc +0 -0
  123. package/core/__pycache__/app_context.cpython-313.pyc +0 -0
  124. package/core/__pycache__/async_text_processor.cpython-313.pyc +0 -0
  125. package/core/__pycache__/backup_manager.cpython-313.pyc +0 -0
  126. package/core/__pycache__/backup_recovery_manager.cpython-313.pyc +0 -0
  127. package/core/__pycache__/content_hash_cache.cpython-313.pyc +0 -0
  128. package/core/__pycache__/context_menu.cpython-313.pyc +0 -0
  129. package/core/__pycache__/data_validator.cpython-313.pyc +0 -0
  130. package/core/__pycache__/database_connection_manager.cpython-313.pyc +0 -0
  131. package/core/__pycache__/database_curl_settings_manager.cpython-313.pyc +0 -0
  132. package/core/__pycache__/database_promera_ai_settings_manager.cpython-313.pyc +0 -0
  133. package/core/__pycache__/database_schema.cpython-313.pyc +0 -0
  134. package/core/__pycache__/database_schema_manager.cpython-313.pyc +0 -0
  135. package/core/__pycache__/database_settings_manager.cpython-313.pyc +0 -0
  136. package/core/__pycache__/database_settings_manager_interface.cpython-313.pyc +0 -0
  137. package/core/__pycache__/dialog_manager.cpython-313.pyc +0 -0
  138. package/core/__pycache__/efficient_line_numbers.cpython-313.pyc +0 -0
  139. package/core/__pycache__/error_handler.cpython-313.pyc +0 -0
  140. package/core/__pycache__/error_service.cpython-313.pyc +0 -0
  141. package/core/__pycache__/event_consolidator.cpython-313.pyc +0 -0
  142. package/core/__pycache__/memory_efficient_text_widget.cpython-313.pyc +0 -0
  143. package/core/__pycache__/migration_manager.cpython-313.pyc +0 -0
  144. package/core/__pycache__/migration_test_suite.cpython-313.pyc +0 -0
  145. package/core/__pycache__/migration_validator.cpython-313.pyc +0 -0
  146. package/core/__pycache__/optimized_find_replace.cpython-313.pyc +0 -0
  147. package/core/__pycache__/optimized_pattern_engine.cpython-313.pyc +0 -0
  148. package/core/__pycache__/optimized_search_highlighter.cpython-313.pyc +0 -0
  149. package/core/__pycache__/performance_monitor.cpython-313.pyc +0 -0
  150. package/core/__pycache__/persistence_manager.cpython-313.pyc +0 -0
  151. package/core/__pycache__/progressive_stats_calculator.cpython-313.pyc +0 -0
  152. package/core/__pycache__/regex_pattern_cache.cpython-313.pyc +0 -0
  153. package/core/__pycache__/regex_pattern_library.cpython-313.pyc +0 -0
  154. package/core/__pycache__/search_operation_manager.cpython-313.pyc +0 -0
  155. package/core/__pycache__/settings_defaults_registry.cpython-313.pyc +0 -0
  156. package/core/__pycache__/settings_integrity_validator.cpython-313.pyc +0 -0
  157. package/core/__pycache__/settings_serializer.cpython-313.pyc +0 -0
  158. package/core/__pycache__/settings_validator.cpython-313.pyc +0 -0
  159. package/core/__pycache__/smart_stats_calculator.cpython-313.pyc +0 -0
  160. package/core/__pycache__/statistics_update_manager.cpython-313.pyc +0 -0
  161. package/core/__pycache__/stats_config_manager.cpython-313.pyc +0 -0
  162. package/core/__pycache__/streaming_text_handler.cpython-313.pyc +0 -0
  163. package/core/__pycache__/task_scheduler.cpython-313.pyc +0 -0
  164. package/core/__pycache__/visibility_monitor.cpython-313.pyc +0 -0
  165. package/core/__pycache__/widget_cache.cpython-313.pyc +0 -0
  166. package/core/mcp/__pycache__/__init__.cpython-313.pyc +0 -0
  167. package/core/mcp/__pycache__/protocol.cpython-313.pyc +0 -0
  168. package/core/mcp/__pycache__/schema.cpython-313.pyc +0 -0
  169. package/core/mcp/__pycache__/server_stdio.cpython-313.pyc +0 -0
  170. package/core/mcp/__pycache__/tool_registry.cpython-313.pyc +0 -0
  171. package/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  172. package/tools/__pycache__/ai_tools.cpython-313.pyc +0 -0
  173. package/tools/__pycache__/ascii_art_generator.cpython-313.pyc +0 -0
  174. package/tools/__pycache__/base64_tools.cpython-313.pyc +0 -0
  175. package/tools/__pycache__/base_tool.cpython-313.pyc +0 -0
  176. package/tools/__pycache__/case_tool.cpython-313.pyc +0 -0
  177. package/tools/__pycache__/column_tools.cpython-313.pyc +0 -0
  178. package/tools/__pycache__/cron_tool.cpython-313.pyc +0 -0
  179. package/tools/__pycache__/curl_history.cpython-313.pyc +0 -0
  180. package/tools/__pycache__/curl_processor.cpython-313.pyc +0 -0
  181. package/tools/__pycache__/curl_settings.cpython-313.pyc +0 -0
  182. package/tools/__pycache__/curl_tool.cpython-313.pyc +0 -0
  183. package/tools/__pycache__/diff_viewer.cpython-313.pyc +0 -0
  184. package/tools/__pycache__/email_extraction_tool.cpython-313.pyc +0 -0
  185. package/tools/__pycache__/email_header_analyzer.cpython-313.pyc +0 -0
  186. package/tools/__pycache__/extraction_tools.cpython-313.pyc +0 -0
  187. package/tools/__pycache__/find_replace.cpython-313.pyc +0 -0
  188. package/tools/__pycache__/folder_file_reporter.cpython-313.pyc +0 -0
  189. package/tools/__pycache__/folder_file_reporter_adapter.cpython-313.pyc +0 -0
  190. package/tools/__pycache__/generator_tools.cpython-313.pyc +0 -0
  191. package/tools/__pycache__/hash_generator.cpython-313.pyc +0 -0
  192. package/tools/__pycache__/html_tool.cpython-313.pyc +0 -0
  193. package/tools/__pycache__/huggingface_helper.cpython-313.pyc +0 -0
  194. package/tools/__pycache__/jsonxml_tool.cpython-313.pyc +0 -0
  195. package/tools/__pycache__/line_tools.cpython-313.pyc +0 -0
  196. package/tools/__pycache__/list_comparator.cpython-313.pyc +0 -0
  197. package/tools/__pycache__/markdown_tools.cpython-313.pyc +0 -0
  198. package/tools/__pycache__/mcp_widget.cpython-313.pyc +0 -0
  199. package/tools/__pycache__/notes_widget.cpython-313.pyc +0 -0
  200. package/tools/__pycache__/number_base_converter.cpython-313.pyc +0 -0
  201. package/tools/__pycache__/regex_extractor.cpython-313.pyc +0 -0
  202. package/tools/__pycache__/slug_generator.cpython-313.pyc +0 -0
  203. package/tools/__pycache__/sorter_tools.cpython-313.pyc +0 -0
  204. package/tools/__pycache__/string_escape_tool.cpython-313.pyc +0 -0
  205. package/tools/__pycache__/text_statistics_tool.cpython-313.pyc +0 -0
  206. package/tools/__pycache__/text_wrapper.cpython-313.pyc +0 -0
  207. package/tools/__pycache__/timestamp_converter.cpython-313.pyc +0 -0
  208. package/tools/__pycache__/tool_loader.cpython-313.pyc +0 -0
  209. package/tools/__pycache__/translator_tools.cpython-313.pyc +0 -0
  210. package/tools/__pycache__/url_link_extractor.cpython-313.pyc +0 -0
  211. package/tools/__pycache__/url_parser.cpython-313.pyc +0 -0
  212. package/tools/__pycache__/whitespace_tools.cpython-313.pyc +0 -0
  213. package/tools/__pycache__/word_frequency_counter.cpython-313.pyc +0 -0
@@ -0,0 +1,239 @@
1
+ """
2
+ Diff Utilities Module
3
+
4
+ Reusable diff generation functions for Find & Replace preview,
5
+ MCP tools, and other components that need text comparison.
6
+
7
+ This module is UI-independent and can be used by both tkinter widgets
8
+ and CLI/MCP tools.
9
+ """
10
+
11
+ import difflib
12
+ import re
13
+ from typing import List, Tuple, Optional, NamedTuple
14
+ from dataclasses import dataclass
15
+
16
+
17
+ @dataclass
18
+ class DiffResult:
19
+ """Result of a diff comparison operation."""
20
+ original_text: str
21
+ modified_text: str
22
+ unified_diff: str
23
+ replacements: int
24
+ lines_affected: int
25
+ similarity_score: float # 0-100
26
+
27
+
28
+ @dataclass
29
+ class FindReplacePreview:
30
+ """Preview of a find/replace operation before execution."""
31
+ original_text: str
32
+ modified_text: str
33
+ unified_diff: str
34
+ match_count: int
35
+ lines_affected: int
36
+ match_positions: List[Tuple[int, int]] # List of (start, end) character positions
37
+
38
+
39
+ def generate_unified_diff(
40
+ original: str,
41
+ modified: str,
42
+ context_lines: int = 3,
43
+ original_label: str = "Original",
44
+ modified_label: str = "Modified"
45
+ ) -> str:
46
+ """
47
+ Generate a unified diff between two texts.
48
+
49
+ Args:
50
+ original: Original text
51
+ modified: Modified text
52
+ context_lines: Number of context lines around changes
53
+ original_label: Label for original text (shown as --- label)
54
+ modified_label: Label for modified text (shown as +++ label)
55
+
56
+ Returns:
57
+ Unified diff as string
58
+ """
59
+ original_lines = original.splitlines(keepends=True)
60
+ modified_lines = modified.splitlines(keepends=True)
61
+
62
+ # Ensure last lines end with newline for proper diff
63
+ if original_lines and not original_lines[-1].endswith('\n'):
64
+ original_lines[-1] += '\n'
65
+ if modified_lines and not modified_lines[-1].endswith('\n'):
66
+ modified_lines[-1] += '\n'
67
+
68
+ diff = difflib.unified_diff(
69
+ original_lines,
70
+ modified_lines,
71
+ fromfile=original_label,
72
+ tofile=modified_label,
73
+ n=context_lines
74
+ )
75
+
76
+ return ''.join(diff)
77
+
78
+
79
+ def generate_find_replace_preview(
80
+ text: str,
81
+ find_pattern: str,
82
+ replace_pattern: str,
83
+ use_regex: bool = True,
84
+ case_sensitive: bool = True,
85
+ context_lines: int = 2
86
+ ) -> FindReplacePreview:
87
+ """
88
+ Generate a preview of find/replace operation with unified diff.
89
+
90
+ Args:
91
+ text: Input text to process
92
+ find_pattern: Pattern to find (regex or literal)
93
+ replace_pattern: Replacement string
94
+ use_regex: Whether find_pattern is a regex
95
+ case_sensitive: Whether to match case sensitively
96
+ context_lines: Lines of context in diff output
97
+
98
+ Returns:
99
+ FindReplacePreview with diff and match information
100
+
101
+ Raises:
102
+ re.error: If regex pattern is invalid
103
+ """
104
+ if not find_pattern:
105
+ return FindReplacePreview(
106
+ original_text=text,
107
+ modified_text=text,
108
+ unified_diff="",
109
+ match_count=0,
110
+ lines_affected=0,
111
+ match_positions=[]
112
+ )
113
+
114
+ # Compile the pattern
115
+ flags = 0 if case_sensitive else re.IGNORECASE
116
+ if use_regex:
117
+ pattern = re.compile(find_pattern, flags)
118
+ else:
119
+ pattern = re.compile(re.escape(find_pattern), flags)
120
+
121
+ # Find all matches and their positions
122
+ matches = list(pattern.finditer(text))
123
+ match_positions = [(m.start(), m.end()) for m in matches]
124
+
125
+ # Perform the replacement
126
+ modified_text = pattern.sub(replace_pattern, text)
127
+
128
+ # Calculate lines affected
129
+ original_lines = set()
130
+ pos = 0
131
+ line_num = 1
132
+ for char in text:
133
+ for match_start, match_end in match_positions:
134
+ if match_start <= pos < match_end:
135
+ original_lines.add(line_num)
136
+ if char == '\n':
137
+ line_num += 1
138
+ pos += 1
139
+
140
+ # Generate diff
141
+ match_info = f"({len(matches)} match{'es' if len(matches) != 1 else ''})"
142
+ unified_diff = generate_unified_diff(
143
+ text,
144
+ modified_text,
145
+ context_lines=context_lines,
146
+ original_label=f"Original {match_info}",
147
+ modified_label="Modified"
148
+ )
149
+
150
+ return FindReplacePreview(
151
+ original_text=text,
152
+ modified_text=modified_text,
153
+ unified_diff=unified_diff,
154
+ match_count=len(matches),
155
+ lines_affected=len(original_lines),
156
+ match_positions=match_positions
157
+ )
158
+
159
+
160
+ def compute_similarity_score(original: str, modified: str) -> float:
161
+ """
162
+ Compute similarity score between two texts (0-100).
163
+
164
+ Args:
165
+ original: Original text
166
+ modified: Modified text
167
+
168
+ Returns:
169
+ Similarity percentage (0-100)
170
+ """
171
+ if not original and not modified:
172
+ return 100.0
173
+ if not original or not modified:
174
+ return 0.0
175
+
176
+ matcher = difflib.SequenceMatcher(None, original, modified, autojunk=False)
177
+ return matcher.ratio() * 100
178
+
179
+
180
+ def generate_compact_diff(
181
+ original: str,
182
+ modified: str,
183
+ max_lines: int = 20
184
+ ) -> str:
185
+ """
186
+ Generate a compact diff suitable for CLI/token-limited contexts.
187
+ Shows only changed lines without full context.
188
+
189
+ Args:
190
+ original: Original text
191
+ modified: Modified text
192
+ max_lines: Maximum lines to show in diff
193
+
194
+ Returns:
195
+ Compact diff string
196
+ """
197
+ original_lines = original.splitlines()
198
+ modified_lines = modified.splitlines()
199
+
200
+ matcher = difflib.SequenceMatcher(None, original_lines, modified_lines, autojunk=False)
201
+
202
+ output_lines = []
203
+ line_count = 0
204
+
205
+ for tag, i1, i2, j1, j2 in matcher.get_opcodes():
206
+ if line_count >= max_lines:
207
+ output_lines.append(f"... ({max_lines}+ changes, truncated)")
208
+ break
209
+
210
+ if tag == 'equal':
211
+ continue
212
+ elif tag == 'delete':
213
+ for i in range(i1, i2):
214
+ if line_count >= max_lines:
215
+ break
216
+ output_lines.append(f"-{i1 + 1}: {original_lines[i]}")
217
+ line_count += 1
218
+ elif tag == 'insert':
219
+ for j in range(j1, j2):
220
+ if line_count >= max_lines:
221
+ break
222
+ output_lines.append(f"+{j1 + 1}: {modified_lines[j]}")
223
+ line_count += 1
224
+ elif tag == 'replace':
225
+ for i in range(i1, i2):
226
+ if line_count >= max_lines:
227
+ break
228
+ output_lines.append(f"-{i + 1}: {original_lines[i]}")
229
+ line_count += 1
230
+ for j in range(j1, j2):
231
+ if line_count >= max_lines:
232
+ break
233
+ output_lines.append(f"+{j + 1}: {modified_lines[j]}")
234
+ line_count += 1
235
+
236
+ if not output_lines:
237
+ return "No differences found."
238
+
239
+ return '\n'.join(output_lines)