aico-cli 0.3.17 → 0.3.20

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.
@@ -0,0 +1,365 @@
1
+ # AICO Hook 通用工具库 - PowerShell 版本
2
+ # 为所有 PowerShell Hook 脚本提供通用功能
3
+
4
+ # 版本信息
5
+ $AICO_HOOK_UTILS_VERSION = "1.0.0"
6
+ $AICO_HOOK_UTILS_BUILD = "2025-01-16"
7
+
8
+ # 路径配置
9
+ $GLOBAL:AICO_HOOKS_DIR = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
10
+ $GLOBAL:AICO_TEMPLATES_DIR = Split-Path -Parent $AICO_HOOKS_DIR
11
+ $GLOBAL:AICO_TEMP_DIR = Join-Path $env:TEMP "aico-hooks"
12
+ $GLOBAL:AICO_LOG_DIR = Join-Path $env:USERPROFILE ".claude\hooks"
13
+
14
+ # 确保必要目录存在
15
+ function Initialize-HookEnvironment {
16
+ @($AICO_TEMP_DIR, $AICO_LOG_DIR) | ForEach-Object {
17
+ if (!(Test-Path $_)) {
18
+ New-Item -ItemType Directory -Path $_ -Force | Out-Null
19
+ }
20
+ }
21
+ }
22
+
23
+ Initialize-HookEnvironment
24
+
25
+ # 日志函数 - 增强版本
26
+ function Write-HookLog {
27
+ param(
28
+ [Parameter(Mandatory=$true)]
29
+ [string]$Message,
30
+
31
+ [Parameter(Mandatory=$false)]
32
+ [ValidateSet("INFO", "WARN", "ERROR", "DEBUG", "SUCCESS")]
33
+ [string]$Level = "INFO",
34
+
35
+ [Parameter(Mandatory=$false)]
36
+ [string]$LogFile = "$AICO_LOG_DIR\hook-utils.log",
37
+
38
+ [Parameter(Mandatory=$false)]
39
+ [switch]$ConsoleOnly,
40
+
41
+ [Parameter(Mandatory=$false)]
42
+ [string]$Source = "hook-utils"
43
+ )
44
+
45
+ $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
46
+
47
+ # 日志格式化
48
+ $logEntry = switch ($Level) {
49
+ "INFO" { "[$Level] $timestamp [$Source] $Message" }
50
+ "WARN" { "[$Level] $timestamp [$Source] ⚠️ $Message" }
51
+ "ERROR" { "[$Level] $timestamp [$Source] ❌ $Message" }
52
+ "DEBUG" { "[$Level] $timestamp [$Source] 🔍 $Message" }
53
+ "SUCCESS" { "[$LEVEL] $timestamp [$Source] ✅ $Message" }
54
+ default { "[$Level] $timestamp [$Source] $Message" }
55
+ }
56
+
57
+ # 控制台输出
58
+ if (!$ConsoleOnly) {
59
+ switch ($Level) {
60
+ "ERROR" { Write-Host $logEntry -ForegroundColor Red }
61
+ "WARN" { Write-Host $logEntry -ForegroundColor Yellow }
62
+ "SUCCESS" { Write-Host $logEntry -ForegroundColor Green }
63
+ "DEBUG" { Write-Host $logEntry -ForegroundColor Gray }
64
+ default { Write-Host $logEntry -ForegroundColor White }
65
+ }
66
+ }
67
+
68
+ # 文件日志
69
+ try {
70
+ Add-Content -Path $LogFile -Value $logEntry -Force -ErrorAction Stop
71
+ }
72
+ catch {
73
+ Write-Debug "文件日志写入失败: $($_.Exception.Message)"
74
+ }
75
+ }
76
+
77
+ # Hook 配置文件操作
78
+ function Get-HookConfig {
79
+ param(
80
+ [string]$ConfigFile = "$AICO_HOOKS_DIR\hooks-config.json",
81
+ [string]$HookName = ""
82
+ )
83
+
84
+ try {
85
+ if (Test-Path $ConfigFile) {
86
+ $config = Get-Content $ConfigFile -Raw | ConvertFrom-Json -ErrorAction Stop
87
+
88
+ if ($HookName) {
89
+ return $config.hooks.$HookName
90
+ }
91
+ return $config
92
+ }
93
+ }
94
+ catch {
95
+ Write-HookLog -Level "ERROR" -Message "配置文件解析失败: $($_.Exception.Message)" -Source "Get-HookConfig"
96
+ }
97
+
98
+ return $null
99
+ }
100
+
101
+ # Hook 状态管理
102
+ function Set-HookStatus {
103
+ param(
104
+ [Parameter(Mandatory=$true)]
105
+ [string]$HookName,
106
+
107
+ [Parameter(Mandatory=$true)]
108
+ [ValidateSet("success", "failed", "running", "pending")]
109
+ [string]$Status,
110
+
111
+ [string]$Details = "",
112
+ [string]$StatusDir = $AICO_TEMP_DIR
113
+ )
114
+
115
+ try {
116
+ $statusFile = Join-Path $StatusDir "$HookName.status"
117
+ $statusData = @{
118
+ Status = $Status
119
+ Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
120
+ Details = $Details
121
+ HookName = $HookName
122
+ }
123
+
124
+ $statusData | ConvertTo-Json -Depth 2 | Set-Content -Path $statusFile -Force
125
+ Write-HookLog -Level "DEBUG" -Message "Hook 状态更新: $HookName = $Status" -Source "Set-HookStatus"
126
+ return $true
127
+ }
128
+ catch {
129
+ Write-HookLog -Level "ERROR" -Message "状态更新失败: $($_.Exception.Message)" -Source "Set-HookStatus"
130
+ return $false
131
+ }
132
+ }
133
+
134
+ function Get-HookStatus {
135
+ param(
136
+ [Parameter(Mandatory=$true)]
137
+ [string]$HookName,
138
+ [string]$StatusDir = $AICO_TEMP_DIR
139
+ )
140
+
141
+ try {
142
+ $statusFile = Join-Path $StatusDir "$HookName.status"
143
+ if (Test-Path $statusFile) {
144
+ $statusData = Get-Content $statusFile -Raw | ConvertFrom-Json
145
+ return $statusData.Status
146
+ }
147
+ }
148
+ catch {
149
+ Write-HookLog -Level "DEBUG" -Message "状态文件读取失败: $($_.Exception.Message)" -Source "Get-HookStatus"
150
+ }
151
+
152
+ return "unknown"
153
+ }
154
+
155
+ # 依赖检查
156
+ function Test-HookDependencies {
157
+ param(
158
+ [Parameter(Mandatory=$true)]
159
+ [string]$HookName,
160
+ [string]$ConfigFile = "$AICO_HOOKS_DIR\hooks-config.json"
161
+ )
162
+
163
+ try {
164
+ $config = Get-HookConfig -ConfigFile $ConfigFile -HookName $HookName
165
+ if ($config -and $config.dependencies) {
166
+ foreach ($dependency in $config.dependencies) {
167
+ $depStatus = Get-HookStatus -HookName $dependency
168
+ if ($depStatus -ne "success") {
169
+ Write-HookLog -Level "ERROR" -Message "依赖检查失败: $HookName 依赖于 $dependency (状态: $depStatus)" -Source "Test-HookDependencies"
170
+ return $false
171
+ }
172
+ }
173
+ }
174
+ return $true
175
+ }
176
+ catch {
177
+ Write-HookLog -Level "ERROR" -Message "依赖检查异常: $($_.Exception.Message)" -Source "Test-HookDependencies"
178
+ return $false
179
+ }
180
+ }
181
+
182
+ # 安全文件操作
183
+ function Invoke-SafeFileOperation {
184
+ param(
185
+ [Parameter(Mandatory=$true)]
186
+ [ValidateSet("read", "write", "append", "lock", "unlock", "exists")]
187
+ [string]$Operation,
188
+
189
+ [Parameter(Mandatory=$true)]
190
+ [string]$FilePath,
191
+
192
+ [string]$Content = "",
193
+
194
+ [int]$LockTimeout = 30, # 秒
195
+ [string]$LockSuffix = ".lock"
196
+ )
197
+
198
+ try {
199
+ $directory = Split-Path -Parent $FilePath
200
+ if ($Operation -ne "exists" -and ![string]::IsNullOrEmpty($directory)) {
201
+ if (!(Test-Path $directory)) {
202
+ New-Item -ItemType Directory -Path $directory -Force | Out-Null
203
+ }
204
+ }
205
+
206
+ switch ($Operation) {
207
+ "exists" {
208
+ return Test-Path $FilePath
209
+ }
210
+
211
+ "read" {
212
+ if (Test-Path $FilePath) {
213
+ return Get-Content $FilePath -Raw -ErrorAction Stop
214
+ }
215
+ return ""
216
+ }
217
+
218
+ "write" {
219
+ $Content | Set-Content -Path $FilePath -Force -ErrorAction Stop
220
+ Write-HookLog -Level "DEBUG" -Message "文件写入成功: $FilePath" -Source "SafeFileOp"
221
+ return $true
222
+ }
223
+
224
+ "append" {
225
+ $Content | Add-Content -Path $FilePath -Force -ErrorAction Stop
226
+ Write-HookLog -Level "DEBUG" -Message "文件追加成功: $FilePath" -Source "SafeFileOp"
227
+ return $true
228
+ }
229
+
230
+ "lock" {
231
+ $lockFile = "$FilePath$LockSuffix"
232
+ $startTime = Get-Date
233
+
234
+ while (Test-Path $lockFile) {
235
+ if (((Get-Date) - $startTime).TotalSeconds -gt $LockTimeout) {
236
+ Write-HookLog -Level "ERROR" -Message "文件锁定超时: $FilePath" -Source "SafeFileOp"
237
+ return $false
238
+ }
239
+ Start-Sleep -Seconds 1
240
+ }
241
+
242
+ New-Item -ItemType File -Path $lockFile -Force | Out-Null
243
+ Write-HookLog -Level "DEBUG" -Message "文件锁定成功: $FilePath" -Source "SafeFileOp"
244
+ return $true
245
+ }
246
+
247
+ "unlock" {
248
+ $lockFile = "$FilePath$LockSuffix"
249
+ if (Test-Path $lockFile) {
250
+ Remove-Item $lockFile -Force -ErrorAction Stop
251
+ Write-HookLog -Level "DEBUG" -Message "文件解锁成功: $FilePath" -Source "SafeFileOp"
252
+ }
253
+ return $true
254
+ }
255
+ }
256
+ }
257
+ catch {
258
+ Write-HookLog -Level "ERROR" -Message "文件操作失败 (${Operation}): $($_.Exception.Message)" -Source "SafeFileOp"
259
+ return $false
260
+ }
261
+ }
262
+
263
+ # 执行外部 Hook 脚本(带重试机制)
264
+ function Invoke-ExternalHook {
265
+ param(
266
+ [Parameter(Mandatory=$true)]
267
+ [string]$ScriptPath,
268
+
269
+ [string[]]$Arguments = @(),
270
+ [int]$MaxRetries = 3,
271
+ [int]$RetryDelay = 2 # 秒
272
+ )
273
+
274
+ if (!(Test-Path $ScriptPath)) {
275
+ Write-HookLog -Level "ERROR" -Message "Hook 脚本不存在: $ScriptPath" -Source "Invoke-ExternalHook"
276
+ return $false
277
+ }
278
+
279
+ for ($retry = 0; $retry -le $MaxRetries; $retry++) {
280
+ try {
281
+ Write-HookLog -Level "DEBUG" -Message "执行外部 Hook: $ScriptPath (尝试 $($retry + 1))" -Source "Invoke-ExternalHook"
282
+
283
+ $result = & $ScriptPath @Arguments
284
+ if ($LASTEXITCODE -eq 0) {
285
+ Write-HookLog -Level "SUCCESS" -Message "外部 Hook 执行成功: $ScriptPath" -Source "Invoke-ExternalHook"
286
+ return $true
287
+ } else {
288
+ throw "Hook 返回非零退出码: $LASTEXITCODE"
289
+ }
290
+ }
291
+ catch {
292
+ Write-HookLog -Level "ERROR" -Message "外部 Hook 执行失败 (尝试 $($retry + 1)): $($_.Exception.Message)" -Source "Invoke-ExternalHook"
293
+
294
+ if ($retry -lt $MaxRetries) {
295
+ Write-HookLog -Level "INFO" -Message "等待 ${RetryDelay}秒后重试..." -Source "Invoke-ExternalHook"
296
+ Start-Sleep -Seconds $RetryDelay
297
+ }
298
+ }
299
+ }
300
+
301
+ Write-HookLog -Level "ERROR" -Message "外部 Hook 执行最终失败: $ScriptPath" -Source "Invoke-ExternalHook"
302
+ return $false
303
+ }
304
+
305
+ # 性能监控函数
306
+ function Measure-HookPerformance {
307
+ param(
308
+ [ScriptBlock]$ScriptBlock,
309
+ [string]$HookName = "unknown"
310
+ )
311
+
312
+ $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
313
+
314
+ try {
315
+ $result = & $ScriptBlock
316
+ $stopwatch.Stop()
317
+
318
+ Write-HookLog -Level "DEBUG" -Message "Hook 性能 [$HookName]: $($stopwatch.ElapsedMilliseconds)ms" -Source "Measure-HookPerformance"
319
+ return $result
320
+ }
321
+ catch {
322
+ $stopwatch.Stop()
323
+ Write-HookLog -Level "ERROR" -Message "Hook 执行异常 [$HookName] (耗时: $($stopwatch.ElapsedMilliseconds)ms): $($_.Exception.Message)" -Source "Measure-HookPerformance"
324
+ throw
325
+ }
326
+ }
327
+
328
+ # 清理函数
329
+ function Clear-HookTempData {
330
+ param([string]$HookName = "")
331
+
332
+ try {
333
+ if ($HookName) {
334
+ # 清理特定 Hook 的临时数据
335
+ $statusFile = Join-Path $AICO_TEMP_DIR "$HookName.status"
336
+ if (Test-Path $statusFile) {
337
+ Remove-Item $statusFile -Force
338
+ Write-HookLog -Level "DEBUG" -Message "清理临时数据: $HookName" -Source "Clear-HookTempData"
339
+ }
340
+ } else {
341
+ # 清理所有临时数据
342
+ if (Test-Path $AICO_TEMP_DIR) {
343
+ Get-ChildItem $AICO_TEMP_DIR -File | Remove-Item -Force
344
+ Write-HookLog -Level "INFO" -Message "清理所有临时数据" -Source "Clear-HookTempData"
345
+ }
346
+ }
347
+ }
348
+ catch {
349
+ Write-HookLog -Level "WARN" -Message "临时数据清理异常: $($_.Exception.Message)" -Source "Clear-HookTempData"
350
+ }
351
+ }
352
+
353
+ # 导出函数
354
+ Export-ModuleMember -Function @(
355
+ 'Write-HookLog',
356
+ 'Get-HookConfig',
357
+ 'Set-HookStatus',
358
+ 'Get-HookStatus',
359
+ 'Test-HookDependencies',
360
+ 'Invoke-SafeFileOperation',
361
+ 'Invoke-ExternalHook',
362
+ 'Measure-HookPerformance',
363
+ 'Clear-HookTempData',
364
+ 'Initialize-HookEnvironment'
365
+ )