stigmergy 1.0.68 → 1.0.70

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 (102) hide show
  1. package/README.en.md +306 -300
  2. package/README.md +469 -301
  3. package/package.json +97 -81
  4. package/scripts/publish.js +268 -0
  5. package/scripts/simple-publish.js +59 -0
  6. package/src/index.js +12 -0
  7. package/test/enhanced-main-alignment.test.js +298 -0
  8. package/test/hook-system-integration-test.js +307 -0
  9. package/test/natural-language-skills-test.js +320 -0
  10. package/test/nl-integration-test.js +179 -0
  11. package/test/parameter-parsing-test.js +143 -0
  12. package/test/real-test.js +435 -0
  13. package/test/system-compatibility-test.js +447 -0
  14. package/test/tdd-fixes-test.js +211 -0
  15. package/test/third-party-skills-test.js +321 -0
  16. package/test/tool-selection-integration-test.js +157 -0
  17. package/test/unit/cli-scanner.test.js +291 -0
  18. package/test/unit/cross-cli-executor.test.js +399 -0
  19. package/src/adapters/claude/__init__.py +0 -13
  20. package/src/adapters/claude/claude_skills_integration.py +0 -609
  21. package/src/adapters/claude/hook_adapter.py +0 -663
  22. package/src/adapters/claude/install_claude_integration.py +0 -265
  23. package/src/adapters/claude/skills_hook_adapter.py +0 -841
  24. package/src/adapters/claude/standalone_claude_adapter.py +0 -384
  25. package/src/adapters/cline/__init__.py +0 -20
  26. package/src/adapters/cline/config.py +0 -108
  27. package/src/adapters/cline/install_cline_integration.py +0 -617
  28. package/src/adapters/cline/mcp_server.py +0 -713
  29. package/src/adapters/cline/standalone_cline_adapter.py +0 -459
  30. package/src/adapters/codebuddy/__init__.py +0 -13
  31. package/src/adapters/codebuddy/buddy_adapter.py +0 -1125
  32. package/src/adapters/codebuddy/install_codebuddy_integration.py +0 -279
  33. package/src/adapters/codebuddy/skills_hook_adapter.py +0 -672
  34. package/src/adapters/codebuddy/skills_integration.py +0 -395
  35. package/src/adapters/codebuddy/standalone_codebuddy_adapter.py +0 -403
  36. package/src/adapters/codex/__init__.py +0 -11
  37. package/src/adapters/codex/base.py +0 -46
  38. package/src/adapters/codex/install_codex_integration.py +0 -311
  39. package/src/adapters/codex/mcp_server.py +0 -493
  40. package/src/adapters/codex/natural_language_parser.py +0 -82
  41. package/src/adapters/codex/slash_command_adapter.py +0 -326
  42. package/src/adapters/codex/standalone_codex_adapter.py +0 -362
  43. package/src/adapters/copilot/__init__.py +0 -13
  44. package/src/adapters/copilot/install_copilot_integration.py +0 -564
  45. package/src/adapters/copilot/mcp_adapter.py +0 -772
  46. package/src/adapters/copilot/mcp_server.py +0 -168
  47. package/src/adapters/copilot/standalone_copilot_adapter.py +0 -114
  48. package/src/adapters/gemini/__init__.py +0 -13
  49. package/src/adapters/gemini/extension_adapter.py +0 -690
  50. package/src/adapters/gemini/install_gemini_integration.py +0 -257
  51. package/src/adapters/gemini/standalone_gemini_adapter.py +0 -366
  52. package/src/adapters/iflow/__init__.py +0 -7
  53. package/src/adapters/iflow/hook_adapter.py +0 -1038
  54. package/src/adapters/iflow/hook_installer.py +0 -536
  55. package/src/adapters/iflow/install_iflow_integration.py +0 -271
  56. package/src/adapters/iflow/official_hook_adapter.py +0 -1272
  57. package/src/adapters/iflow/standalone_iflow_adapter.py +0 -48
  58. package/src/adapters/iflow/workflow_adapter.py +0 -793
  59. package/src/adapters/qoder/hook_installer.py +0 -732
  60. package/src/adapters/qoder/install_qoder_integration.py +0 -265
  61. package/src/adapters/qoder/notification_hook_adapter.py +0 -863
  62. package/src/adapters/qoder/standalone_qoder_adapter.py +0 -48
  63. package/src/adapters/qwen/__init__.py +0 -17
  64. package/src/adapters/qwencode/__init__.py +0 -13
  65. package/src/adapters/qwencode/inheritance_adapter.py +0 -818
  66. package/src/adapters/qwencode/install_qwencode_integration.py +0 -276
  67. package/src/adapters/qwencode/standalone_qwencode_adapter.py +0 -399
  68. package/src/atomic_collaboration_handler.py +0 -461
  69. package/src/cli_collaboration_agent.py +0 -697
  70. package/src/collaboration/hooks.py +0 -315
  71. package/src/core/__init__.py +0 -21
  72. package/src/core/ai_environment_scanner.py +0 -331
  73. package/src/core/base_adapter.py +0 -220
  74. package/src/core/cli_hook_integration.py +0 -406
  75. package/src/core/cross_cli_executor.py +0 -713
  76. package/src/core/cross_cli_mapping.py +0 -1165
  77. package/src/core/cross_platform_encoding.py +0 -365
  78. package/src/core/cross_platform_safe_cli.py +0 -894
  79. package/src/core/direct_cli_executor.py +0 -805
  80. package/src/core/direct_cli_hook_system.py +0 -958
  81. package/src/core/enhanced_init_processor.py +0 -467
  82. package/src/core/graceful_cli_executor.py +0 -912
  83. package/src/core/md_enhancer.py +0 -342
  84. package/src/core/md_generator.py +0 -619
  85. package/src/core/models.py +0 -218
  86. package/src/core/parser.py +0 -108
  87. package/src/core/real_cli_hook_system.py +0 -852
  88. package/src/core/real_cross_cli_system.py +0 -925
  89. package/src/core/verified_cross_cli_system.py +0 -961
  90. package/src/deploy.js +0 -737
  91. package/src/enhanced-main.js +0 -626
  92. package/src/enhanced_deploy.js +0 -303
  93. package/src/enhanced_universal_cli_setup.py +0 -930
  94. package/src/kimi_wrapper.py +0 -104
  95. package/src/main.js +0 -1309
  96. package/src/shell_integration.py +0 -398
  97. package/src/simple-main.js +0 -315
  98. package/src/smart_router_creator.py +0 -323
  99. package/src/universal_cli_setup.py +0 -1289
  100. package/src/utils/__init__.py +0 -12
  101. package/src/utils/cli_detector.py +0 -445
  102. package/src/utils/file_utils.py +0 -246
@@ -1,732 +0,0 @@
1
- """
2
- Qoder CLI Hook安装器
3
- 用于自动安装和配置Qoder CLI的通知Hook插件
4
- """
5
-
6
- import os
7
- import sys
8
- import shutil
9
- import json
10
- import logging
11
- import platform
12
- from pathlib import Path
13
- from typing import Dict, Any, List, Optional
14
- from datetime import datetime
15
-
16
- logger = logging.getLogger(__name__)
17
-
18
-
19
- class QoderHookInstaller:
20
- """Qoder CLI Hook安装器"""
21
-
22
- def __init__(self):
23
- """初始化安装器"""
24
- self.qoder_config_dir = os.path.expanduser("~/.qoder")
25
- self.hooks_dir = os.path.join(self.qoder_config_dir, "hooks")
26
- self.logs_dir = os.path.join(self.qoder_config_dir, "logs")
27
- self.cache_dir = os.path.join(self.qoder_config_dir, "cache")
28
-
29
- # 适配器路径
30
- self.current_dir = Path(__file__).parent
31
- self.hook_adapter_file = self.current_dir / "notification_hook_adapter.py"
32
- self.config_file = self.current_dir / "config.json"
33
-
34
- # 安装状态
35
- self.installation_log: List[Dict[str, Any]] = []
36
-
37
- async def install_hooks(self) -> bool:
38
- """
39
- 安装Qoder CLI Hook插件
40
-
41
- Returns:
42
- bool: 安装是否成功
43
- """
44
- try:
45
- logger.info("开始安装Qoder CLI Hook插件...")
46
-
47
- # 1. 检查环境和平台
48
- if not await self._check_environment():
49
- logger.error("环境检查失败")
50
- return False
51
-
52
- # 2. 创建配置目录
53
- await self._create_directories()
54
-
55
- # 3. 复制适配器文件
56
- if not await self._install_adapter_files():
57
- logger.error("适配器文件安装失败")
58
- return False
59
-
60
- # 4. 创建Hook脚本
61
- if not await self._create_hook_scripts():
62
- logger.error("Hook脚本创建失败")
63
- return False
64
-
65
- # 5. 设置环境配置
66
- if not await self._setup_environment_config():
67
- logger.error("环境配置设置失败")
68
- return False
69
-
70
- # 6. 创建启动脚本
71
- if not await self._create_startup_scripts():
72
- logger.error("启动脚本创建失败")
73
- return False
74
-
75
- # 7. 验证安装
76
- if not await self._verify_installation():
77
- logger.error("安装验证失败")
78
- return False
79
-
80
- logger.info("Qoder CLI Hook插件安装成功")
81
- await self._log_installation("success", "Hook插件安装成功")
82
- return True
83
-
84
- except Exception as e:
85
- logger.error(f"安装Qoder CLI Hook插件失败: {e}")
86
- await self._log_installation("error", f"安装失败: {str(e)}")
87
- return False
88
-
89
- async def uninstall_hooks(self) -> bool:
90
- """
91
- 卸载Qoder CLI Hook插件
92
-
93
- Returns:
94
- bool: 卸载是否成功
95
- """
96
- try:
97
- logger.info("开始卸载Qoder CLI Hook插件...")
98
-
99
- # 1. 备份配置
100
- await self._backup_configuration()
101
-
102
- # 2. 清理Hook脚本
103
- await self._cleanup_hook_scripts()
104
-
105
- # 3. 清理适配器文件
106
- await self._cleanup_adapter_files()
107
-
108
- # 4. 清理环境变量
109
- await self._cleanup_environment()
110
-
111
- # 5. 清理临时文件
112
- await self._cleanup_temp_files()
113
-
114
- logger.info("Qoder CLI Hook插件卸载成功")
115
- await self._log_installation("success", "Hook插件卸载成功")
116
- return True
117
-
118
- except Exception as e:
119
- logger.error(f"卸载Qoder CLI Hook插件失败: {e}")
120
- await self._log_installation("error", f"卸载失败: {str(e)}")
121
- return False
122
-
123
- async def _check_environment(self) -> bool:
124
- """
125
- 检查安装环境
126
-
127
- Returns:
128
- bool: 环境是否满足要求
129
- """
130
- try:
131
- # 检查Python版本
132
- if sys.version_info < (3, 8):
133
- logger.error("需要Python 3.8或更高版本")
134
- return False
135
-
136
- # 检查平台支持
137
- current_platform = platform.system()
138
- if current_platform not in ["Darwin", "Linux", "Windows"]:
139
- logger.warning(f"平台 {current_platform} 可能不受完全支持,将使用fallback机制")
140
-
141
- # 检查必要的文件是否存在
142
- required_files = [
143
- self.hook_adapter_file,
144
- self.config_file
145
- ]
146
-
147
- for file_path in required_files:
148
- if not file_path.exists():
149
- logger.error(f"必要文件不存在: {file_path}")
150
- return False
151
-
152
- # 检查依赖包
153
- try:
154
- import json
155
- import asyncio
156
- import subprocess
157
- except ImportError as e:
158
- logger.error(f"缺少依赖包: {e}")
159
- return False
160
-
161
- # 检查系统工具
162
- if current_platform == "Darwin":
163
- # 检查osascript是否可用
164
- try:
165
- subprocess.run(['osascript', '-e', '1'],
166
- check=True, capture_output=True, timeout=2)
167
- except (subprocess.CalledProcessError, FileNotFoundError):
168
- logger.warning("osascript不可用,通知功能可能受限")
169
-
170
- logger.info(f"环境检查通过 ({current_platform})")
171
- return True
172
-
173
- except Exception as e:
174
- logger.error(f"环境检查失败: {e}")
175
- return False
176
-
177
- async def _create_directories(self) -> None:
178
- """创建必要的目录"""
179
- directories = [
180
- self.qoder_config_dir,
181
- self.hooks_dir,
182
- self.logs_dir,
183
- self.cache_dir
184
- ]
185
-
186
- for directory in directories:
187
- os.makedirs(directory, exist_ok=True)
188
- logger.debug(f"创建目录: {directory}")
189
-
190
- async def _install_adapter_files(self) -> bool:
191
- """
192
- 安装适配器文件
193
-
194
- Returns:
195
- bool: 安装是否成功
196
- """
197
- try:
198
- # 复制Hook适配器到Qoder目录
199
- adapter_dest = os.path.join(self.qoder_config_dir, "notification_hook_adapter.py")
200
- shutil.copy2(self.hook_adapter_file, adapter_dest)
201
- logger.info(f"复制Hook适配器到: {adapter_dest}")
202
-
203
- # 复制配置文件
204
- config_dest = os.path.join(self.qoder_config_dir, "config.json")
205
- shutil.copy2(self.config_file, config_dest)
206
- logger.info(f"复制配置文件到: {config_dest}")
207
-
208
- # 创建__init__.py文件
209
- init_file = os.path.join(self.qoder_config_dir, "__init__.py")
210
- if not os.path.exists(init_file):
211
- with open(init_file, 'w', encoding='utf-8') as f:
212
- f.write('"""Qoder CLI Hook适配器包"""\n')
213
- logger.info(f"创建__init__.py文件: {init_file}")
214
-
215
- return True
216
-
217
- except Exception as e:
218
- logger.error(f"安装适配器文件失败: {e}")
219
- return False
220
-
221
- async def _create_hook_scripts(self) -> bool:
222
- """
223
- 创建Hook脚本
224
-
225
- Returns:
226
- bool: 创建是否成功
227
- """
228
- try:
229
- # 前置Hook脚本
230
- pre_hook_content = '''#!/bin/bash
231
- # Qoder CLI前置Hook脚本
232
- # 检测跨CLI调用意图并发送通知
233
-
234
- COMMAND="$1"
235
- STAGE="pre_command"
236
- SESSION_ID="${QODER_HOOK_SESSION_ID:-$(date +%s)}"
237
-
238
- # 设置环境变量
239
- export QODER_HOOK_STAGE="$STAGE"
240
- export QODER_HOOK_COMMAND="$COMMAND"
241
- export QODER_HOOK_SESSION_ID="$SESSION_ID"
242
-
243
- # 记录请求信息
244
- REQUEST_FILE="$QODER_CROSS_CLI_REQUEST_FILE"
245
- if [ -n "$REQUEST_FILE" ]; then
246
- cat > "$REQUEST_FILE" << EOF
247
- {
248
- "stage": "$STAGE",
249
- "command": "$COMMAND",
250
- "session_id": "$SESSION_ID",
251
- "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
252
- "platform": "$(uname -s)",
253
- "user": "$USER"
254
- }
255
- EOF
256
- fi
257
-
258
- # 检测跨CLI调用关键词
259
- CROSS_CLI_KEYWORDS="请用|调用|用|让|use|call|ask"
260
- CLI_NAMES="claude|gemini|qwencode|iflow|codebuddy|codex"
261
-
262
- if echo "$COMMAND" | grep -E "($CROSS_CLI_KEYWORDS).*$CLI_NAMES" > /dev/null 2>&1; then
263
- # 发送跨CLI检测通知
264
- case "$(uname -s)" in
265
- Darwin*)
266
- if command -v osascript > /dev/null 2>&1; then
267
- osascript -e 'display notification "检测到跨CLI调用意图" with title "QoderCLI" subtitle "准备调用其他AI工具"'
268
- fi
269
- ;;
270
- Linux*)
271
- if command -v notify-send > /dev/null 2>&1; then
272
- notify-send "QoderCLI" "检测到跨CLI调用意图 - 准备调用其他AI工具" --urgency=normal
273
- fi
274
- ;;
275
- esac
276
- fi
277
-
278
- exit 0
279
- '''
280
-
281
- # 后置Hook脚本
282
- post_hook_content = '''#!/bin/bash
283
- # Qoder CLI后置Hook脚本
284
- # 处理跨CLI调用结果并发送完成通知
285
-
286
- EXIT_CODE=$?
287
- STAGE="post_command"
288
- COMMAND="$1"
289
- SESSION_ID="${QODER_HOOK_SESSION_ID:-$(date +%s)}"
290
-
291
- # 设置环境变量
292
- export QODER_HOOK_STAGE="$STAGE"
293
- export QODER_HOOK_COMMAND="$COMMAND"
294
- export QODER_HOOK_SESSION_ID="$SESSION_ID"
295
-
296
- # 记录完成状态
297
- STATUS_FILE="$QODER_CROSS_CLI_STATUS_FILE"
298
- if [ -n "$STATUS_FILE" ]; then
299
- cat > "$STATUS_FILE" << EOF
300
- {
301
- "stage": "$STAGE",
302
- "command": "$COMMAND",
303
- "session_id": "$SESSION_ID",
304
- "exit_code": $EXIT_CODE,
305
- "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
306
- "completed": true,
307
- "success": $([ $EXIT_CODE -eq 0 ] && echo true || echo false)
308
- }
309
- EOF
310
- fi
311
-
312
- # 检查是否有跨CLI响应
313
- RESPONSE_FILE="$QODER_CROSS_CLI_RESPONSE_FILE"
314
- if [ -f "$RESPONSE_FILE" ] && [ -s "$RESPONSE_FILE" ]; then
315
- # 发送完成通知
316
- case "$(uname -s)" in
317
- Darwin*)
318
- if command -v osascript > /dev/null 2>&1; then
319
- osascript -e 'display notification "[OK] 跨CLI调用完成" with title "QoderCLI" subtitle "任务执行成功"'
320
- fi
321
- ;;
322
- Linux*)
323
- if command -v notify-send > /dev/null 2>&1; then
324
- notify-send "QoderCLI" "[OK] 跨CLI调用完成" --urgency=low
325
- fi
326
- ;;
327
- esac
328
- fi
329
-
330
- exit $EXIT_CODE
331
- '''
332
-
333
- # 错误处理Hook脚本
334
- error_hook_content = '''#!/bin/bash
335
- # Qoder CLI错误处理Hook脚本
336
- # 处理错误情况并发送授权通知
337
-
338
- EXIT_CODE=$?
339
- STAGE="error_handling"
340
- COMMAND="$1"
341
- SESSION_ID="${QODER_HOOK_SESSION_ID:-$(date +%s)}"
342
-
343
- # 设置环境变量
344
- export QODER_HOOK_STAGE="$STAGE"
345
- export QODER_HOOK_COMMAND="$COMMAND"
346
- export QODER_HOOK_SESSION_ID="$SESSION_ID"
347
-
348
- # 如果有错误,发送授权通知
349
- if [ $EXIT_CODE -ne 0 ]; then
350
- case "$(uname -s)" in
351
- Darwin*)
352
- if command -v osascript > /dev/null 2>&1; then
353
- osascript -e 'display notification "⌛️ 你提交的任务需要授权呀…" with title "QoderCLI"'
354
- fi
355
- ;;
356
- Linux*)
357
- if command -v notify-send > /dev/null 2>&1; then
358
- notify-send "QoderCLI" "⌛️ 你提交的任务需要授权呀…" --urgency=normal
359
- fi
360
- ;;
361
- esac
362
-
363
- # 记录错误到日志
364
- LOG_FILE="$HOME/.qoder/logs/error.log"
365
- echo "[$(date)] Hook Error: Command '$COMMAND' failed with exit code $EXIT_CODE" >> "$LOG_FILE"
366
- fi
367
-
368
- exit 0
369
- '''
370
-
371
- # 写入脚本文件
372
- scripts = {
373
- 'pre_hook.sh': pre_hook_content,
374
- 'post_hook.sh': post_hook_content,
375
- 'error_hook.sh': error_hook_content
376
- }
377
-
378
- for filename, content in scripts.items():
379
- script_path = os.path.join(self.hooks_dir, filename)
380
- with open(script_path, 'w', encoding='utf-8') as f:
381
- f.write(content)
382
- os.chmod(script_path, 0o755)
383
- logger.info(f"创建Hook脚本: {script_path}")
384
-
385
- return True
386
-
387
- except Exception as e:
388
- logger.error(f"创建Hook脚本失败: {e}")
389
- return False
390
-
391
- async def _setup_environment_config(self) -> bool:
392
- """
393
- 设置环境配置
394
-
395
- Returns:
396
- bool: 设置是否成功
397
- """
398
- try:
399
- # 创建环境配置文件
400
- env_config = {
401
- "QODER_CROSS_CLI_ENABLED": "1",
402
- "QODER_CROSS_CLI_RESPONSE_FILE": "",
403
- "QODER_CROSS_CLI_REQUEST_FILE": "",
404
- "QODER_CROSS_CLI_STATUS_FILE": "",
405
- "QODER_HOOK_STAGE": "",
406
- "QODER_HOOK_COMMAND": "",
407
- "QODER_HOOK_SESSION_ID": "",
408
- "QODER_HOOK_LOG_LEVEL": "INFO",
409
- "QODER_HOOK_PLATFORM": platform.system()
410
- }
411
-
412
- env_config_path = os.path.join(self.qoder_config_dir, "environment.json")
413
- with open(env_config_path, 'w', encoding='utf-8') as f:
414
- json.dump(env_config, f, indent=2, ensure_ascii=False)
415
-
416
- logger.info(f"环境配置已创建: {env_config_path}")
417
- return True
418
-
419
- except Exception as e:
420
- logger.error(f"设置环境配置失败: {e}")
421
- return False
422
-
423
- async def _create_startup_scripts(self) -> bool:
424
- """
425
- 创建启动脚本
426
-
427
- Returns:
428
- bool: 创建是否成功
429
- """
430
- try:
431
- # 创建Qoder Hook启动脚本
432
- startup_script = '''#!/bin/bash
433
- # Qoder CLI Cross-CLI Hook启动脚本
434
-
435
- QODER_DIR="$HOME/.qoder"
436
- ADAPTER="$QODER_DIR/notification_hook_adapter.py"
437
-
438
- # 检查Python环境
439
- if ! command -v python3 > /dev/null 2>&1; then
440
- echo "错误: 需要Python 3"
441
- exit 1
442
- fi
443
-
444
- # 检查适配器文件
445
- if [ ! -f "$ADAPTER" ]; then
446
- echo "错误: Qoder Hook适配器文件不存在: $ADAPTER"
447
- exit 1
448
- fi
449
-
450
- # 设置环境变量
451
- export QODER_CROSS_CLI_ENABLED=1
452
- export PYTHONPATH="$QODER_DIR:$PYTHONPATH"
453
-
454
- # 启动Hook监控
455
- echo "启动Qoder CLI Cross-CLI Hook监控..."
456
- python3 "$ADAPTER"
457
-
458
- echo "Qoder Hook监控已停止"
459
- '''
460
-
461
- startup_script_path = os.path.join(self.qoder_config_dir, "start_hooks.sh")
462
- with open(startup_script_path, 'w', encoding='utf-8') as f:
463
- f.write(startup_script)
464
- os.chmod(startup_script_path, 0o755)
465
-
466
- logger.info(f"启动脚本已创建: {startup_script_path}")
467
- return True
468
-
469
- except Exception as e:
470
- logger.error(f"创建启动脚本失败: {e}")
471
- return False
472
-
473
- async def _verify_installation(self) -> bool:
474
- """
475
- 验证安装
476
-
477
- Returns:
478
- bool: 验证是否成功
479
- """
480
- try:
481
- # 检查必要文件是否存在
482
- required_files = [
483
- os.path.join(self.qoder_config_dir, "notification_hook_adapter.py"),
484
- os.path.join(self.qoder_config_dir, "config.json"),
485
- os.path.join(self.hooks_dir, "pre_hook.sh"),
486
- os.path.join(self.hooks_dir, "post_hook.sh"),
487
- os.path.join(self.hooks_dir, "error_hook.sh"),
488
- os.path.join(self.qoder_config_dir, "environment.json"),
489
- os.path.join(self.qoder_config_dir, "start_hooks.sh")
490
- ]
491
-
492
- for file_path in required_files:
493
- if not os.path.exists(file_path):
494
- logger.error(f"必要文件不存在: {file_path}")
495
- return False
496
-
497
- # 检查目录权限
498
- if not os.access(self.hooks_dir, os.X_OK):
499
- logger.error(f"Hook目录无执行权限: {self.hooks_dir}")
500
- return False
501
-
502
- # 测试Hook脚本语法
503
- for script_name in ["pre_hook.sh", "post_hook.sh", "error_hook.sh"]:
504
- script_path = os.path.join(self.hooks_dir, script_name)
505
- if platform.system() != "Windows":
506
- result = subprocess.run(
507
- ['bash', '-n', script_path],
508
- capture_output=True,
509
- text=True
510
- )
511
- if result.returncode != 0:
512
- logger.error(f"Hook脚本语法错误: {script_name}")
513
- logger.error(f"错误信息: {result.stderr}")
514
- return False
515
-
516
- logger.info("安装验证通过")
517
- return True
518
-
519
- except Exception as e:
520
- logger.error(f"安装验证失败: {e}")
521
- return False
522
-
523
- async def _backup_configuration(self) -> None:
524
- """备份现有配置"""
525
- try:
526
- backup_dir = os.path.join(self.qoder_config_dir, "backup", datetime.now().strftime("%Y%m%d_%H%M%S"))
527
- os.makedirs(backup_dir, exist_ok=True)
528
-
529
- # 备份Hook目录
530
- if os.path.exists(self.hooks_dir):
531
- backup_hooks_dir = os.path.join(backup_dir, "hooks")
532
- shutil.copytree(self.hooks_dir, backup_hooks_dir, dirs_exist_ok=True)
533
-
534
- # 备份配置文件
535
- config_files = [
536
- "notification_hook_adapter.py",
537
- "config.json",
538
- "environment.json",
539
- "start_hooks.sh"
540
- ]
541
-
542
- for config_file in config_files:
543
- src_path = os.path.join(self.qoder_config_dir, config_file)
544
- if os.path.exists(src_path):
545
- dest_path = os.path.join(backup_dir, config_file)
546
- shutil.copy2(src_path, dest_path)
547
-
548
- logger.info(f"配置已备份到: {backup_dir}")
549
-
550
- except Exception as e:
551
- logger.warning(f"备份配置失败: {e}")
552
-
553
- async def _cleanup_hook_scripts(self) -> None:
554
- """清理Hook脚本"""
555
- try:
556
- scripts = ["pre_hook.sh", "post_hook.sh", "error_hook.sh"]
557
- for script in scripts:
558
- script_path = os.path.join(self.hooks_dir, script)
559
- if os.path.exists(script_path):
560
- os.remove(script_path)
561
- logger.debug(f"删除Hook脚本: {script_path}")
562
-
563
- except Exception as e:
564
- logger.warning(f"清理Hook脚本失败: {e}")
565
-
566
- async def _cleanup_adapter_files(self) -> None:
567
- """清理适配器文件"""
568
- try:
569
- files = [
570
- "notification_hook_adapter.py",
571
- "config.json",
572
- "environment.json",
573
- "start_hooks.sh",
574
- "__init__.py"
575
- ]
576
-
577
- for file_name in files:
578
- file_path = os.path.join(self.qoder_config_dir, file_name)
579
- if os.path.exists(file_path):
580
- os.remove(file_path)
581
- logger.debug(f"删除文件: {file_path}")
582
-
583
- except Exception as e:
584
- logger.warning(f"清理适配器文件失败: {e}")
585
-
586
- async def _cleanup_environment(self) -> None:
587
- """清理环境变量"""
588
- try:
589
- env_vars = [
590
- 'QODER_CROSS_CLI_ENABLED',
591
- 'QODER_CROSS_CLI_RESPONSE_FILE',
592
- 'QODER_CROSS_CLI_REQUEST_FILE',
593
- 'QODER_CROSS_CLI_STATUS_FILE',
594
- 'QODER_HOOK_STAGE',
595
- 'QODER_HOOK_COMMAND',
596
- 'QODER_HOOK_SESSION_ID'
597
- ]
598
-
599
- for env_var in env_vars:
600
- if env_var in os.environ:
601
- del os.environ[env_var]
602
-
603
- logger.debug("环境变量已清理")
604
-
605
- except Exception as e:
606
- logger.warning(f"清理环境变量失败: {e}")
607
-
608
- async def _cleanup_temp_files(self) -> None:
609
- """清理临时文件"""
610
- try:
611
- temp_pattern = os.path.join(self.qoder_config_dir, "cache", "qoder_cross_cli_temp_*")
612
- import glob
613
- temp_dirs = glob.glob(temp_pattern)
614
-
615
- for temp_dir in temp_dirs:
616
- if os.path.exists(temp_dir):
617
- shutil.rmtree(temp_dir, ignore_errors=True)
618
- logger.debug(f"清理临时目录: {temp_dir}")
619
-
620
- except Exception as e:
621
- logger.warning(f"清理临时文件失败: {e}")
622
-
623
- async def _log_installation(self, status: str, message: str) -> None:
624
- """
625
- 记录安装日志
626
-
627
- Args:
628
- status: 状态
629
- message: 消息
630
- """
631
- log_entry = {
632
- 'timestamp': datetime.now().isoformat(),
633
- 'status': status,
634
- 'message': message,
635
- 'platform': platform.system()
636
- }
637
- self.installation_log.append(log_entry)
638
-
639
- # 写入日志文件
640
- log_file = os.path.join(self.logs_dir, "installation.log")
641
- os.makedirs(os.path.dirname(log_file), exist_ok=True)
642
-
643
- try:
644
- with open(log_file, 'a', encoding='utf-8') as f:
645
- f.write(f"{json.dumps(log_entry, ensure_ascii=False)}\n")
646
- except Exception as e:
647
- logger.warning(f"写入安装日志失败: {e}")
648
-
649
- def get_installation_status(self) -> Dict[str, Any]:
650
- """
651
- 获取安装状态
652
-
653
- Returns:
654
- Dict[str, Any]: 安装状态
655
- """
656
- return {
657
- 'platform': platform.system(),
658
- 'qoder_config_dir': self.qoder_config_dir,
659
- 'hooks_dir': self.hooks_dir,
660
- 'files_exist': {
661
- 'hook_adapter': os.path.exists(os.path.join(self.qoder_config_dir, "notification_hook_adapter.py")),
662
- 'config': os.path.exists(os.path.join(self.qoder_config_dir, "config.json")),
663
- 'pre_hook': os.path.exists(os.path.join(self.hooks_dir, "pre_hook.sh")),
664
- 'post_hook': os.path.exists(os.path.join(self.hooks_dir, "post_hook.sh")),
665
- 'error_hook': os.path.exists(os.path.join(self.hooks_dir, "error_hook.sh")),
666
- 'startup_script': os.path.exists(os.path.join(self.qoder_config_dir, "start_hooks.sh"))
667
- },
668
- 'installation_log': self.installation_log
669
- }
670
-
671
-
672
- # 便捷函数
673
- async def install_qoder_hooks() -> bool:
674
- """
675
- 安装Qoder CLI Hook插件
676
-
677
- Returns:
678
- bool: 安装是否成功
679
- """
680
- installer = QoderHookInstaller()
681
- return await installer.install_hooks()
682
-
683
-
684
- async def uninstall_qoder_hooks() -> bool:
685
- """
686
- 卸载Qoder CLI Hook插件
687
-
688
- Returns:
689
- bool: 卸载是否成功
690
- """
691
- installer = QoderHookInstaller()
692
- return await installer.uninstall_hooks()
693
-
694
-
695
- def get_qoder_hook_status() -> Dict[str, Any]:
696
- """
697
- 获取Qoder Hook插件状态
698
-
699
- Returns:
700
- Dict[str, Any]: 状态信息
701
- """
702
- installer = QoderHookInstaller()
703
- return installer.get_installation_status()
704
-
705
-
706
- if __name__ == "__main__":
707
- import asyncio
708
-
709
- async def main():
710
- """主函数"""
711
- import sys
712
-
713
- if len(sys.argv) < 2:
714
- print("用法: python hook_installer.py [install|uninstall|status]")
715
- return
716
-
717
- command = sys.argv[1].lower()
718
-
719
- if command == "install":
720
- success = await install_qoder_hooks()
721
- print(f"安装{'成功' if success else '失败'}")
722
- elif command == "uninstall":
723
- success = await uninstall_qoder_hooks()
724
- print(f"卸载{'成功' if success else '失败'}")
725
- elif command == "status":
726
- status = get_qoder_hook_status()
727
- print("Qoder Hook插件状态:")
728
- print(json.dumps(status, indent=2, ensure_ascii=False))
729
- else:
730
- print("未知命令:", command)
731
-
732
- asyncio.run(main())