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.
- package/README.en.md +306 -300
- package/README.md +469 -301
- package/package.json +97 -81
- package/scripts/publish.js +268 -0
- package/scripts/simple-publish.js +59 -0
- package/src/index.js +12 -0
- package/test/enhanced-main-alignment.test.js +298 -0
- package/test/hook-system-integration-test.js +307 -0
- package/test/natural-language-skills-test.js +320 -0
- package/test/nl-integration-test.js +179 -0
- package/test/parameter-parsing-test.js +143 -0
- package/test/real-test.js +435 -0
- package/test/system-compatibility-test.js +447 -0
- package/test/tdd-fixes-test.js +211 -0
- package/test/third-party-skills-test.js +321 -0
- package/test/tool-selection-integration-test.js +157 -0
- package/test/unit/cli-scanner.test.js +291 -0
- package/test/unit/cross-cli-executor.test.js +399 -0
- package/src/adapters/claude/__init__.py +0 -13
- package/src/adapters/claude/claude_skills_integration.py +0 -609
- package/src/adapters/claude/hook_adapter.py +0 -663
- package/src/adapters/claude/install_claude_integration.py +0 -265
- package/src/adapters/claude/skills_hook_adapter.py +0 -841
- package/src/adapters/claude/standalone_claude_adapter.py +0 -384
- package/src/adapters/cline/__init__.py +0 -20
- package/src/adapters/cline/config.py +0 -108
- package/src/adapters/cline/install_cline_integration.py +0 -617
- package/src/adapters/cline/mcp_server.py +0 -713
- package/src/adapters/cline/standalone_cline_adapter.py +0 -459
- package/src/adapters/codebuddy/__init__.py +0 -13
- package/src/adapters/codebuddy/buddy_adapter.py +0 -1125
- package/src/adapters/codebuddy/install_codebuddy_integration.py +0 -279
- package/src/adapters/codebuddy/skills_hook_adapter.py +0 -672
- package/src/adapters/codebuddy/skills_integration.py +0 -395
- package/src/adapters/codebuddy/standalone_codebuddy_adapter.py +0 -403
- package/src/adapters/codex/__init__.py +0 -11
- package/src/adapters/codex/base.py +0 -46
- package/src/adapters/codex/install_codex_integration.py +0 -311
- package/src/adapters/codex/mcp_server.py +0 -493
- package/src/adapters/codex/natural_language_parser.py +0 -82
- package/src/adapters/codex/slash_command_adapter.py +0 -326
- package/src/adapters/codex/standalone_codex_adapter.py +0 -362
- package/src/adapters/copilot/__init__.py +0 -13
- package/src/adapters/copilot/install_copilot_integration.py +0 -564
- package/src/adapters/copilot/mcp_adapter.py +0 -772
- package/src/adapters/copilot/mcp_server.py +0 -168
- package/src/adapters/copilot/standalone_copilot_adapter.py +0 -114
- package/src/adapters/gemini/__init__.py +0 -13
- package/src/adapters/gemini/extension_adapter.py +0 -690
- package/src/adapters/gemini/install_gemini_integration.py +0 -257
- package/src/adapters/gemini/standalone_gemini_adapter.py +0 -366
- package/src/adapters/iflow/__init__.py +0 -7
- package/src/adapters/iflow/hook_adapter.py +0 -1038
- package/src/adapters/iflow/hook_installer.py +0 -536
- package/src/adapters/iflow/install_iflow_integration.py +0 -271
- package/src/adapters/iflow/official_hook_adapter.py +0 -1272
- package/src/adapters/iflow/standalone_iflow_adapter.py +0 -48
- package/src/adapters/iflow/workflow_adapter.py +0 -793
- package/src/adapters/qoder/hook_installer.py +0 -732
- package/src/adapters/qoder/install_qoder_integration.py +0 -265
- package/src/adapters/qoder/notification_hook_adapter.py +0 -863
- package/src/adapters/qoder/standalone_qoder_adapter.py +0 -48
- package/src/adapters/qwen/__init__.py +0 -17
- package/src/adapters/qwencode/__init__.py +0 -13
- package/src/adapters/qwencode/inheritance_adapter.py +0 -818
- package/src/adapters/qwencode/install_qwencode_integration.py +0 -276
- package/src/adapters/qwencode/standalone_qwencode_adapter.py +0 -399
- package/src/atomic_collaboration_handler.py +0 -461
- package/src/cli_collaboration_agent.py +0 -697
- package/src/collaboration/hooks.py +0 -315
- package/src/core/__init__.py +0 -21
- package/src/core/ai_environment_scanner.py +0 -331
- package/src/core/base_adapter.py +0 -220
- package/src/core/cli_hook_integration.py +0 -406
- package/src/core/cross_cli_executor.py +0 -713
- package/src/core/cross_cli_mapping.py +0 -1165
- package/src/core/cross_platform_encoding.py +0 -365
- package/src/core/cross_platform_safe_cli.py +0 -894
- package/src/core/direct_cli_executor.py +0 -805
- package/src/core/direct_cli_hook_system.py +0 -958
- package/src/core/enhanced_init_processor.py +0 -467
- package/src/core/graceful_cli_executor.py +0 -912
- package/src/core/md_enhancer.py +0 -342
- package/src/core/md_generator.py +0 -619
- package/src/core/models.py +0 -218
- package/src/core/parser.py +0 -108
- package/src/core/real_cli_hook_system.py +0 -852
- package/src/core/real_cross_cli_system.py +0 -925
- package/src/core/verified_cross_cli_system.py +0 -961
- package/src/deploy.js +0 -737
- package/src/enhanced-main.js +0 -626
- package/src/enhanced_deploy.js +0 -303
- package/src/enhanced_universal_cli_setup.py +0 -930
- package/src/kimi_wrapper.py +0 -104
- package/src/main.js +0 -1309
- package/src/shell_integration.py +0 -398
- package/src/simple-main.js +0 -315
- package/src/smart_router_creator.py +0 -323
- package/src/universal_cli_setup.py +0 -1289
- package/src/utils/__init__.py +0 -12
- package/src/utils/cli_detector.py +0 -445
- 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())
|