stigmergy 1.0.57

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 (94) hide show
  1. package/LICENSE +19 -0
  2. package/README.de.md +301 -0
  3. package/README.en.md +301 -0
  4. package/README.es.md +301 -0
  5. package/README.fr.md +301 -0
  6. package/README.ja.md +301 -0
  7. package/README.ko.md +301 -0
  8. package/README.md +301 -0
  9. package/README.ru.md +301 -0
  10. package/README.zh.md +301 -0
  11. package/package.json +82 -0
  12. package/src/adapters/claude/__init__.py +13 -0
  13. package/src/adapters/claude/claude_skills_integration.py +609 -0
  14. package/src/adapters/claude/hook_adapter.py +663 -0
  15. package/src/adapters/claude/install_claude_integration.py +265 -0
  16. package/src/adapters/claude/skills_hook_adapter.py +841 -0
  17. package/src/adapters/claude/standalone_claude_adapter.py +384 -0
  18. package/src/adapters/cline/__init__.py +20 -0
  19. package/src/adapters/cline/config.py +108 -0
  20. package/src/adapters/cline/install_cline_integration.py +617 -0
  21. package/src/adapters/cline/mcp_server.py +713 -0
  22. package/src/adapters/cline/standalone_cline_adapter.py +459 -0
  23. package/src/adapters/codebuddy/__init__.py +13 -0
  24. package/src/adapters/codebuddy/buddy_adapter.py +1125 -0
  25. package/src/adapters/codebuddy/install_codebuddy_integration.py +279 -0
  26. package/src/adapters/codebuddy/skills_hook_adapter.py +672 -0
  27. package/src/adapters/codebuddy/skills_integration.py +395 -0
  28. package/src/adapters/codebuddy/standalone_codebuddy_adapter.py +403 -0
  29. package/src/adapters/codex/__init__.py +11 -0
  30. package/src/adapters/codex/base.py +46 -0
  31. package/src/adapters/codex/install_codex_integration.py +311 -0
  32. package/src/adapters/codex/mcp_server.py +493 -0
  33. package/src/adapters/codex/natural_language_parser.py +82 -0
  34. package/src/adapters/codex/slash_command_adapter.py +326 -0
  35. package/src/adapters/codex/standalone_codex_adapter.py +362 -0
  36. package/src/adapters/copilot/__init__.py +13 -0
  37. package/src/adapters/copilot/install_copilot_integration.py +564 -0
  38. package/src/adapters/copilot/mcp_adapter.py +772 -0
  39. package/src/adapters/copilot/mcp_server.py +168 -0
  40. package/src/adapters/copilot/standalone_copilot_adapter.py +114 -0
  41. package/src/adapters/gemini/__init__.py +13 -0
  42. package/src/adapters/gemini/extension_adapter.py +690 -0
  43. package/src/adapters/gemini/install_gemini_integration.py +257 -0
  44. package/src/adapters/gemini/standalone_gemini_adapter.py +366 -0
  45. package/src/adapters/iflow/__init__.py +7 -0
  46. package/src/adapters/iflow/hook_adapter.py +1038 -0
  47. package/src/adapters/iflow/hook_installer.py +536 -0
  48. package/src/adapters/iflow/install_iflow_integration.py +271 -0
  49. package/src/adapters/iflow/official_hook_adapter.py +1272 -0
  50. package/src/adapters/iflow/standalone_iflow_adapter.py +48 -0
  51. package/src/adapters/iflow/workflow_adapter.py +793 -0
  52. package/src/adapters/qoder/hook_installer.py +732 -0
  53. package/src/adapters/qoder/install_qoder_integration.py +265 -0
  54. package/src/adapters/qoder/notification_hook_adapter.py +863 -0
  55. package/src/adapters/qoder/standalone_qoder_adapter.py +48 -0
  56. package/src/adapters/qwen/__init__.py +17 -0
  57. package/src/adapters/qwencode/__init__.py +13 -0
  58. package/src/adapters/qwencode/inheritance_adapter.py +818 -0
  59. package/src/adapters/qwencode/install_qwencode_integration.py +276 -0
  60. package/src/adapters/qwencode/standalone_qwencode_adapter.py +399 -0
  61. package/src/atomic_collaboration_handler.py +461 -0
  62. package/src/cli_collaboration_agent.py +697 -0
  63. package/src/collaboration/hooks.py +315 -0
  64. package/src/core/__init__.py +21 -0
  65. package/src/core/ai_environment_scanner.py +331 -0
  66. package/src/core/base_adapter.py +220 -0
  67. package/src/core/cli_hook_integration.py +406 -0
  68. package/src/core/cross_cli_executor.py +713 -0
  69. package/src/core/cross_cli_mapping.py +1163 -0
  70. package/src/core/cross_platform_encoding.py +365 -0
  71. package/src/core/cross_platform_safe_cli.py +894 -0
  72. package/src/core/direct_cli_executor.py +805 -0
  73. package/src/core/direct_cli_hook_system.py +958 -0
  74. package/src/core/enhanced_init_processor.py +427 -0
  75. package/src/core/graceful_cli_executor.py +912 -0
  76. package/src/core/md_enhancer.py +342 -0
  77. package/src/core/md_generator.py +619 -0
  78. package/src/core/models.py +218 -0
  79. package/src/core/parser.py +108 -0
  80. package/src/core/real_cli_hook_system.py +852 -0
  81. package/src/core/real_cross_cli_system.py +925 -0
  82. package/src/core/verified_cross_cli_system.py +961 -0
  83. package/src/deploy.js +737 -0
  84. package/src/enhanced_deploy.js +303 -0
  85. package/src/enhanced_universal_cli_setup.py +930 -0
  86. package/src/kimi_wrapper.py +104 -0
  87. package/src/main.js +1309 -0
  88. package/src/shell_integration.py +398 -0
  89. package/src/simple-main.js +315 -0
  90. package/src/smart_router_creator.py +323 -0
  91. package/src/universal_cli_setup.py +1289 -0
  92. package/src/utils/__init__.py +12 -0
  93. package/src/utils/cli_detector.py +445 -0
  94. package/src/utils/file_utils.py +246 -0
@@ -0,0 +1,12 @@
1
+ """
2
+ AI CLI Universal Integration System - 工具模块
3
+ """
4
+
5
+ from .cli_detector import CLIDetector, get_cli_detector, detect_claude_cli_status, generate_environment_report
6
+
7
+ __all__ = [
8
+ 'CLIDetector',
9
+ 'get_cli_detector',
10
+ 'detect_claude_cli_status',
11
+ 'generate_environment_report'
12
+ ]
@@ -0,0 +1,445 @@
1
+ """
2
+ CLI Detector - CLI工具检测器
3
+ 检测系统中可用的AI CLI工具及其扩展机制
4
+
5
+ 这是TDD驱动的实现,用于实际环境检测
6
+ """
7
+
8
+ import os
9
+ import sys
10
+ import subprocess
11
+ import json
12
+ import shutil
13
+ from typing import Dict, List, Optional, Tuple, Any
14
+ from pathlib import Path
15
+ import logging
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class CLIDetector:
21
+ """CLI工具检测器"""
22
+
23
+ def __init__(self):
24
+ """初始化检测器"""
25
+ self.cli_tools = {
26
+ 'claude': {
27
+ 'commands': ['claude-cli', 'claude'],
28
+ 'version_flag': '--version',
29
+ 'config_dir': '~/.config/claude',
30
+ 'hooks_file': '~/.config/claude/hooks.json',
31
+ 'extension_mechanism': 'hook_system'
32
+ },
33
+ 'gemini': {
34
+ 'commands': ['gemini-cli', 'gemini'],
35
+ 'version_flag': '--version',
36
+ 'config_dir': '~/.config/gemini',
37
+ 'extensions_file': '~/.config/gemini/extensions.json',
38
+ 'extension_mechanism': 'extension_system'
39
+ },
40
+ 'qwencode': {
41
+ 'commands': ['qwencode-cli', 'qwencode', 'qwen-code'],
42
+ 'version_flag': '--version',
43
+ 'config_dir': '~/.config/qwencode',
44
+ 'config_file': '~/.config/qwencode/config.yml',
45
+ 'extension_mechanism': 'class_inheritance'
46
+ },
47
+ 'iflow': {
48
+ 'commands': ['iflow-cli', 'iflow'],
49
+ 'version_flag': '--version',
50
+ 'config_dir': '~/.config/iflow',
51
+ 'workflows_dir': '~/.config/iflow/workflows',
52
+ 'extension_mechanism': 'workflow_nodes'
53
+ },
54
+ 'qoder': {
55
+ 'commands': ['qoder-cli', 'qoder'],
56
+ 'version_flag': '--version',
57
+ 'config_dir': '~/.config/qoder',
58
+ 'plugins_file': '~/.config/qoder/plugins.json',
59
+ 'extension_mechanism': 'plugin_system'
60
+ },
61
+ 'codebuddy': {
62
+ 'commands': ['codebuddy', 'codebuddy-cli'],
63
+ 'version_flag': '--version',
64
+ 'config_dir': '~/.config/codebuddy',
65
+ 'buddies_file': '~/.config/codebuddy/buddies.yml',
66
+ 'extension_mechanism': 'buddy_system'
67
+ },
68
+ 'codex': {
69
+ 'commands': ['codex-cli', 'codex'],
70
+ 'version_flag': '--version',
71
+ 'config_dir': '~/.config/codex',
72
+ 'extensions_file': '~/.config/codex/extensions.json',
73
+ 'extension_mechanism': 'extension_system'
74
+ }
75
+ }
76
+
77
+ def detect_all_cli_tools(self) -> Dict[str, Dict[str, Any]]:
78
+ """
79
+ 检测所有CLI工具的可用性和状态
80
+
81
+ Returns:
82
+ Dict[str, Dict[str, Any]]: 检测结果
83
+ """
84
+ results = {}
85
+
86
+ for cli_name, cli_info in self.cli_tools.items():
87
+ results[cli_name] = self._detect_single_cli(cli_name, cli_info)
88
+
89
+ return results
90
+
91
+ def detect_claude_cli(self) -> Dict[str, Any]:
92
+ """
93
+ 专门检测Claude CLI工具
94
+
95
+ Returns:
96
+ Dict[str, Any]: Claude CLI检测结果
97
+ """
98
+ if 'claude' not in self.cli_tools:
99
+ return {'error': 'Claude CLI not configured'}
100
+
101
+ return self._detect_single_cli('claude', self.cli_tools['claude'])
102
+
103
+ def _detect_single_cli(self, cli_name: str, cli_info: Dict[str, Any]) -> Dict[str, Any]:
104
+ """
105
+ 检测单个CLI工具
106
+
107
+ Args:
108
+ cli_name: CLI工具名称
109
+ cli_info: CLI工具信息
110
+
111
+ Returns:
112
+ Dict[str, Any]: 检测结果
113
+ """
114
+ result = {
115
+ 'name': cli_name,
116
+ 'available': False,
117
+ 'command': None,
118
+ 'version': None,
119
+ 'extension_mechanism': cli_info['extension_mechanism'],
120
+ 'config_dir': self._expand_path(cli_info['config_dir']),
121
+ 'config_exists': False,
122
+ 'extension_ready': False,
123
+ 'error': None
124
+ }
125
+
126
+ try:
127
+ # 1. 检测CLI命令是否可用
128
+ command = self._find_cli_command(cli_info['commands'])
129
+ if command:
130
+ result['command'] = command
131
+ result['available'] = True
132
+
133
+ # 2. 获取版本信息
134
+ version = self._get_cli_version(command, cli_info['version_flag'])
135
+ result['version'] = version
136
+
137
+ # 3. 检查配置目录
138
+ config_dir = result['config_dir']
139
+ if os.path.exists(config_dir):
140
+ result['config_exists'] = True
141
+
142
+ # 4. 检查扩展机制是否准备就绪
143
+ result['extension_ready'] = self._check_extension_readiness(cli_name, cli_info, config_dir)
144
+
145
+ else:
146
+ result['error'] = f"CLI命令未找到: {cli_info['commands']}"
147
+
148
+ except Exception as e:
149
+ result['error'] = str(e)
150
+ logger.error(f"检测 {cli_name} CLI失败: {e}")
151
+
152
+ return result
153
+
154
+ def _find_cli_command(self, commands: List[str]) -> Optional[str]:
155
+ """
156
+ 查找可用的CLI命令
157
+
158
+ Args:
159
+ commands: 候选命令列表
160
+
161
+ Returns:
162
+ Optional[str]: 找到的命令路径,如果没找到返回None
163
+ """
164
+ for command in commands:
165
+ # 首先检查是否在PATH中
166
+ command_path = shutil.which(command)
167
+ if command_path:
168
+ return command_path
169
+
170
+ # 检查是否为Python模块
171
+ if self._is_python_module(command):
172
+ return f"python -m {command}"
173
+
174
+ return None
175
+
176
+ def _is_python_module(self, module_name: str) -> bool:
177
+ """
178
+ 检查是否为Python模块
179
+
180
+ Args:
181
+ module_name: 模块名称
182
+
183
+ Returns:
184
+ bool: 是否为Python模块
185
+ """
186
+ try:
187
+ # 尝试导入模块
188
+ import importlib.util
189
+ spec = importlib.util.find_spec(module_name)
190
+ return spec is not None
191
+ except (ImportError, ValueError):
192
+ return False
193
+
194
+ def _get_cli_version(self, command: str, version_flag: str) -> Optional[str]:
195
+ """
196
+ 获取CLI工具版本
197
+
198
+ Args:
199
+ command: CLI命令
200
+ version_flag: 版本标志
201
+
202
+ Returns:
203
+ Optional[str]: 版本信息
204
+ """
205
+ try:
206
+ result = subprocess.run(
207
+ f"{command} {version_flag}",
208
+ shell=True,
209
+ capture_output=True,
210
+ text=True,
211
+ timeout=10
212
+ )
213
+
214
+ if result.returncode == 0:
215
+ return result.stdout.strip()
216
+ else:
217
+ logger.warning(f"获取版本失败: {command} {version_flag}")
218
+ return None
219
+
220
+ except subprocess.TimeoutExpired:
221
+ logger.error(f"获取版本超时: {command}")
222
+ return None
223
+ except Exception as e:
224
+ logger.error(f"获取版本异常: {command}, {e}")
225
+ return None
226
+
227
+ def _check_extension_readiness(self, cli_name: str, cli_info: Dict[str, Any], config_dir: str) -> bool:
228
+ """
229
+ 检查扩展机制是否准备就绪
230
+
231
+ Args:
232
+ cli_name: CLI工具名称
233
+ cli_info: CLI工具信息
234
+ config_dir: 配置目录
235
+
236
+ Returns:
237
+ bool: 扩展机制是否准备就绪
238
+ """
239
+ try:
240
+ mechanism = cli_info['extension_mechanism']
241
+
242
+ if mechanism == 'hook_system':
243
+ # 检查Hook系统
244
+ hooks_file = self._expand_path(cli_info.get('hooks_file', ''))
245
+ return self._check_hook_system(hooks_file)
246
+
247
+ elif mechanism == 'extension_system':
248
+ # 检查扩展系统
249
+ extensions_file = self._expand_path(cli_info.get('extensions_file', ''))
250
+ return self._check_extension_system(extensions_file)
251
+
252
+ elif mechanism == 'class_inheritance':
253
+ # 检查类继承机制
254
+ config_file = self._expand_path(cli_info.get('config_file', ''))
255
+ return self._check_class_inheritance(config_file)
256
+
257
+ elif mechanism == 'workflow_nodes':
258
+ # 检查工作流节点
259
+ workflows_dir = self._expand_path(cli_info.get('workflows_dir', ''))
260
+ return self._check_workflow_nodes(workflows_dir)
261
+
262
+ elif mechanism == 'plugin_system':
263
+ # 检查插件系统
264
+ plugins_file = self._expand_path(cli_info.get('plugins_file', ''))
265
+ return self._check_plugin_system(plugins_file)
266
+
267
+ elif mechanism == 'buddy_system':
268
+ # 检查伙伴系统
269
+ buddies_file = self._expand_path(cli_info.get('buddies_file', ''))
270
+ return self._check_buddy_system(buddies_file)
271
+
272
+ else:
273
+ logger.warning(f"未知的扩展机制: {mechanism}")
274
+ return False
275
+
276
+ except Exception as e:
277
+ logger.error(f"检查扩展机制失败: {cli_name}, {e}")
278
+ return False
279
+
280
+ def _check_hook_system(self, hooks_file: str) -> bool:
281
+ """检查Hook系统"""
282
+ if os.path.exists(hooks_file):
283
+ try:
284
+ with open(hooks_file, 'r') as f:
285
+ hooks_config = json.load(f)
286
+ return isinstance(hooks_config, dict) and 'hooks' in hooks_config
287
+ except:
288
+ return False
289
+ return True # 文件不存在说明尚未配置,但机制可用
290
+
291
+ def _check_extension_system(self, extensions_file: str) -> bool:
292
+ """检查扩展系统"""
293
+ if os.path.exists(extensions_file):
294
+ try:
295
+ with open(extensions_file, 'r') as f:
296
+ extensions_config = json.load(f)
297
+ return isinstance(extensions_config, dict) and 'extensions' in extensions_config
298
+ except:
299
+ return False
300
+ return True
301
+
302
+ def _check_class_inheritance(self, config_file: str) -> bool:
303
+ """检查类继承机制"""
304
+ # 对于Python模块,主要检查模块是否可导入
305
+ return self._is_python_module('qwencode_cli')
306
+
307
+ def _check_workflow_nodes(self, workflows_dir: str) -> bool:
308
+ """检查工作流节点"""
309
+ return os.path.exists(workflows_dir) or not os.path.exists(workflows_dir)
310
+
311
+ def _check_plugin_system(self, plugins_file: str) -> bool:
312
+ """检查插件系统"""
313
+ if os.path.exists(plugins_file):
314
+ try:
315
+ with open(plugins_file, 'r') as f:
316
+ plugins_config = json.load(f)
317
+ return isinstance(plugins_config, dict) and 'plugins' in plugins_config
318
+ except:
319
+ return False
320
+ return True
321
+
322
+ def _check_buddy_system(self, buddies_file: str) -> bool:
323
+ """检查伙伴系统"""
324
+ if os.path.exists(buddies_file):
325
+ return True # YAML文件存在,可以使用
326
+ return True # 文件不存在说明尚未配置,但机制可用
327
+
328
+ def _expand_path(self, path: str) -> str:
329
+ """展开路径中的~符号"""
330
+ return os.path.expanduser(path)
331
+
332
+ def get_cli_capabilities(self, cli_name: str) -> Dict[str, Any]:
333
+ """
334
+ 获取CLI工具的能力信息
335
+
336
+ Args:
337
+ cli_name: CLI工具名称
338
+
339
+ Returns:
340
+ Dict[str, Any]: 能力信息
341
+ """
342
+ capabilities = {
343
+ 'supported_protocols': [],
344
+ 'hooks_available': [],
345
+ 'extension_points': [],
346
+ 'native_integration': False
347
+ }
348
+
349
+ if cli_name == 'claude':
350
+ capabilities.update({
351
+ 'supported_protocols': [
352
+ '请用claude帮我{task}',
353
+ '调用claude来{task}',
354
+ 'use claude to {task}',
355
+ 'ask claude for {task}'
356
+ ],
357
+ 'hooks_available': [
358
+ 'user_prompt_submit',
359
+ 'tool_use_pre',
360
+ 'tool_use_post',
361
+ 'response_generated'
362
+ ],
363
+ 'extension_points': ['hooks.json'],
364
+ 'native_integration': True
365
+ })
366
+
367
+ elif cli_name == 'gemini':
368
+ capabilities.update({
369
+ 'supported_protocols': [
370
+ '请用gemini{task}',
371
+ '调用gemini{task}',
372
+ 'use gemini to {task}',
373
+ 'call gemini for {task}'
374
+ ],
375
+ 'hooks_available': [
376
+ 'preprocessor',
377
+ 'postprocessor',
378
+ 'command_handler'
379
+ ],
380
+ 'extension_points': ['extensions.json'],
381
+ 'native_integration': True
382
+ })
383
+
384
+ # 其他CLI工具的能力可以在这里添加...
385
+
386
+ return capabilities
387
+
388
+ def generate_report(self) -> Dict[str, Any]:
389
+ """
390
+ 生成检测报告
391
+
392
+ Returns:
393
+ Dict[str, Any]: 完整的检测报告
394
+ """
395
+ detection_results = self.detect_all_cli_tools()
396
+
397
+ # 使用跨平台的时间戳生成
398
+ from datetime import datetime
399
+ timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
400
+
401
+ report = {
402
+ 'timestamp': timestamp,
403
+ 'system_info': {
404
+ 'platform': sys.platform,
405
+ 'python_version': sys.version,
406
+ 'path': os.environ.get('PATH', ''),
407
+ },
408
+ 'cli_tools': detection_results,
409
+ 'summary': {
410
+ 'total_tools': len(self.cli_tools),
411
+ 'available_tools': sum(1 for result in detection_results.values() if result['available']),
412
+ 'ready_for_integration': sum(1 for result in detection_results.values()
413
+ if result['available'] and result['extension_ready']),
414
+ 'highest_priority_tools': [
415
+ name for name, result in detection_results.items()
416
+ if result['available'] and name in ['claude', 'gemini', 'qwencode']
417
+ ]
418
+ }
419
+ }
420
+
421
+ return report
422
+
423
+
424
+ # 全局检测器实例
425
+ _global_detector: Optional[CLIDetector] = None
426
+
427
+
428
+ def get_cli_detector() -> CLIDetector:
429
+ """获取全局CLI检测器实例"""
430
+ global _global_detector
431
+ if _global_detector is None:
432
+ _global_detector = CLIDetector()
433
+ return _global_detector
434
+
435
+
436
+ def detect_claude_cli_status() -> Dict[str, Any]:
437
+ """检测Claude CLI状态的便捷函数"""
438
+ detector = get_cli_detector()
439
+ return detector.detect_claude_cli()
440
+
441
+
442
+ def generate_environment_report() -> Dict[str, Any]:
443
+ """生成环境报告的便捷函数"""
444
+ detector = get_cli_detector()
445
+ return detector.generate_report()
@@ -0,0 +1,246 @@
1
+ """
2
+ File utility functions for safe file operations
3
+ Cross-platform safe file reading and writing with encoding support
4
+ """
5
+
6
+ import json
7
+ import os
8
+ import logging
9
+ from pathlib import Path
10
+ from typing import Dict, Any, Optional, Union
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ def ensure_directory_exists(directory_path: Union[str, Path]) -> bool:
16
+ """
17
+ Ensure a directory exists, creating it if necessary
18
+
19
+ Args:
20
+ directory_path: Path to the directory
21
+
22
+ Returns:
23
+ True if directory exists or was created successfully, False otherwise
24
+ """
25
+ try:
26
+ path = Path(directory_path)
27
+ path.mkdir(parents=True, exist_ok=True)
28
+ return True
29
+ except Exception as e:
30
+ logger.error(f"Failed to create directory {directory_path}: {e}")
31
+ return False
32
+
33
+
34
+ def safe_write_json_file(file_path: Union[str, Path], data: Dict[str, Any],
35
+ encoding: str = 'utf-8', indent: int = 2) -> bool:
36
+ """
37
+ Safely write JSON data to a file with proper encoding handling
38
+
39
+ Args:
40
+ file_path: Path to the JSON file
41
+ data: Dictionary to write as JSON
42
+ encoding: File encoding (default: utf-8)
43
+ indent: JSON indentation (default: 2)
44
+
45
+ Returns:
46
+ True if write was successful, False otherwise
47
+ """
48
+ try:
49
+ path = Path(file_path)
50
+
51
+ # Ensure parent directory exists
52
+ path.parent.mkdir(parents=True, exist_ok=True)
53
+
54
+ # Write JSON data
55
+ with open(path, 'w', encoding=encoding) as f:
56
+ json.dump(data, f, indent=indent, ensure_ascii=False, sort_keys=True)
57
+
58
+ logger.info(f"Successfully wrote JSON file: {file_path}")
59
+ return True
60
+
61
+ except Exception as e:
62
+ logger.error(f"Failed to write JSON file {file_path}: {e}")
63
+ return False
64
+
65
+
66
+ def safe_read_json_file(file_path: Union[str, Path],
67
+ encoding: str = 'utf-8') -> Optional[Dict[str, Any]]:
68
+ """
69
+ Safely read JSON data from a file with proper encoding handling
70
+
71
+ Args:
72
+ file_path: Path to the JSON file
73
+ encoding: File encoding (default: utf-8)
74
+
75
+ Returns:
76
+ Dictionary with JSON data, or None if read failed
77
+ """
78
+ try:
79
+ path = Path(file_path)
80
+
81
+ if not path.exists():
82
+ logger.warning(f"JSON file does not exist: {file_path}")
83
+ return None
84
+
85
+ # Read JSON data
86
+ with open(path, 'r', encoding=encoding) as f:
87
+ data = json.load(f)
88
+
89
+ logger.info(f"Successfully read JSON file: {file_path}")
90
+ return data
91
+
92
+ except json.JSONDecodeError as e:
93
+ logger.error(f"Invalid JSON in file {file_path}: {e}")
94
+ return None
95
+ except Exception as e:
96
+ logger.error(f"Failed to read JSON file {file_path}: {e}")
97
+ return None
98
+
99
+
100
+ def safe_write_text_file(file_path: Union[str, Path], content: str,
101
+ encoding: str = 'utf-8') -> bool:
102
+ """
103
+ Safely write text content to a file with proper encoding handling
104
+
105
+ Args:
106
+ file_path: Path to the text file
107
+ content: Text content to write
108
+ encoding: File encoding (default: utf-8)
109
+
110
+ Returns:
111
+ True if write was successful, False otherwise
112
+ """
113
+ try:
114
+ path = Path(file_path)
115
+
116
+ # Ensure parent directory exists
117
+ path.parent.mkdir(parents=True, exist_ok=True)
118
+
119
+ # Write text content
120
+ with open(path, 'w', encoding=encoding) as f:
121
+ f.write(content)
122
+
123
+ logger.info(f"Successfully wrote text file: {file_path}")
124
+ return True
125
+
126
+ except Exception as e:
127
+ logger.error(f"Failed to write text file {file_path}: {e}")
128
+ return False
129
+
130
+
131
+ def safe_read_text_file(file_path: Union[str, Path],
132
+ encoding: str = 'utf-8') -> Optional[str]:
133
+ """
134
+ Safely read text content from a file with proper encoding handling
135
+
136
+ Args:
137
+ file_path: Path to the text file
138
+ encoding: File encoding (default: utf-8)
139
+
140
+ Returns:
141
+ Text content as string, or None if read failed
142
+ """
143
+ try:
144
+ path = Path(file_path)
145
+
146
+ if not path.exists():
147
+ logger.warning(f"Text file does not exist: {file_path}")
148
+ return None
149
+
150
+ # Read text content
151
+ with open(path, 'r', encoding=encoding) as f:
152
+ content = f.read()
153
+
154
+ logger.info(f"Successfully read text file: {file_path}")
155
+ return content
156
+
157
+ except Exception as e:
158
+ logger.error(f"Failed to read text file {file_path}: {e}")
159
+ return None
160
+
161
+
162
+ def get_file_size(file_path: Union[str, Path]) -> int:
163
+ """
164
+ Get the size of a file in bytes
165
+
166
+ Args:
167
+ file_path: Path to the file
168
+
169
+ Returns:
170
+ File size in bytes, or 0 if file doesn't exist or error occurred
171
+ """
172
+ try:
173
+ path = Path(file_path)
174
+ if path.exists():
175
+ return path.stat().st_size
176
+ return 0
177
+ except Exception as e:
178
+ logger.error(f"Failed to get file size for {file_path}: {e}")
179
+ return 0
180
+
181
+
182
+ def file_exists(file_path: Union[str, Path]) -> bool:
183
+ """
184
+ Check if a file exists
185
+
186
+ Args:
187
+ file_path: Path to the file
188
+
189
+ Returns:
190
+ True if file exists, False otherwise
191
+ """
192
+ try:
193
+ return Path(file_path).exists()
194
+ except Exception:
195
+ return False
196
+
197
+
198
+ def delete_file_safely(file_path: Union[str, Path]) -> bool:
199
+ """
200
+ Safely delete a file
201
+
202
+ Args:
203
+ file_path: Path to the file to delete
204
+
205
+ Returns:
206
+ True if deletion was successful or file didn't exist, False otherwise
207
+ """
208
+ try:
209
+ path = Path(file_path)
210
+ if path.exists():
211
+ path.unlink()
212
+ logger.info(f"Successfully deleted file: {file_path}")
213
+ return True
214
+ except Exception as e:
215
+ logger.error(f"Failed to delete file {file_path}: {e}")
216
+ return False
217
+
218
+
219
+ def copy_file_safely(source_path: Union[str, Path], dest_path: Union[str, Path]) -> bool:
220
+ """
221
+ Safely copy a file from source to destination
222
+
223
+ Args:
224
+ source_path: Source file path
225
+ dest_path: Destination file path
226
+
227
+ Returns:
228
+ True if copy was successful, False otherwise
229
+ """
230
+ try:
231
+ src = Path(source_path)
232
+ dst = Path(dest_path)
233
+
234
+ # Ensure destination directory exists
235
+ dst.parent.mkdir(parents=True, exist_ok=True)
236
+
237
+ # Copy file
238
+ import shutil
239
+ shutil.copy2(src, dst)
240
+
241
+ logger.info(f"Successfully copied file from {source_path} to {dest_path}")
242
+ return True
243
+
244
+ except Exception as e:
245
+ logger.error(f"Failed to copy file from {source_path} to {dest_path}: {e}")
246
+ return False