monol-plugin-scout 2.1.3 → 4.1.0
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/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +244 -0
- package/monol-plugin-scout-pkg/CLAUDE.md +125 -8
- package/monol-plugin-scout-pkg/api/mock/categories.json +81 -0
- package/monol-plugin-scout-pkg/api/mock/insights.json +111 -0
- package/monol-plugin-scout-pkg/api/mock/marketplace.json +501 -0
- package/monol-plugin-scout-pkg/api/mock/trending.json +96 -0
- package/monol-plugin-scout-pkg/commands/console.md +79 -0
- package/monol-plugin-scout-pkg/commands/frequency.md +32 -0
- package/monol-plugin-scout-pkg/commands/install.md +32 -0
- package/monol-plugin-scout-pkg/commands/priority.md +30 -0
- package/monol-plugin-scout-pkg/commands/quiet.md +32 -0
- package/monol-plugin-scout-pkg/commands/schedule.md +32 -0
- package/monol-plugin-scout-pkg/commands/scout.md +8 -5
- package/monol-plugin-scout-pkg/commands/skills.md +160 -0
- package/monol-plugin-scout-pkg/commands/team.md +32 -0
- package/monol-plugin-scout-pkg/commands/timing.md +32 -0
- package/monol-plugin-scout-pkg/commands/trust.md +85 -0
- package/monol-plugin-scout-pkg/commands/trusted-install.md +98 -0
- package/monol-plugin-scout-pkg/commands/uninstall.md +31 -0
- package/monol-plugin-scout-pkg/config.yaml +57 -0
- package/monol-plugin-scout-pkg/data/.cache/676d5ab664292155e5f509c69d4edae1 +10 -0
- package/monol-plugin-scout-pkg/data/.session +8 -0
- package/monol-plugin-scout-pkg/data/analytics.json +102 -0
- package/monol-plugin-scout-pkg/data/history.json +67 -11
- package/monol-plugin-scout-pkg/data/logs/scout.log +1 -0
- package/monol-plugin-scout-pkg/data/schedules.json +17 -0
- package/monol-plugin-scout-pkg/data/team.json +53 -0
- package/monol-plugin-scout-pkg/data/usage.json +100 -7
- package/monol-plugin-scout-pkg/docs/ARCHITECTURE.md +201 -0
- package/monol-plugin-scout-pkg/hooks/generate-insights.sh +102 -0
- package/monol-plugin-scout-pkg/hooks/hooks.json +36 -1
- package/monol-plugin-scout-pkg/hooks/on-session-end.sh +136 -0
- package/monol-plugin-scout-pkg/hooks/on-session-start.sh +43 -0
- package/monol-plugin-scout-pkg/hooks/on-stop.md +79 -0
- package/monol-plugin-scout-pkg/hooks/open-console.sh +111 -0
- package/monol-plugin-scout-pkg/hooks/open-dashboard.sh +61 -0
- package/monol-plugin-scout-pkg/hooks/track-usage.sh +59 -0
- package/monol-plugin-scout-pkg/lib/ai-recommender.sh +505 -0
- package/monol-plugin-scout-pkg/lib/cache.sh +194 -0
- package/monol-plugin-scout-pkg/lib/data-validator.sh +360 -0
- package/monol-plugin-scout-pkg/lib/error-handler.sh +296 -0
- package/monol-plugin-scout-pkg/lib/logger.sh +263 -0
- package/monol-plugin-scout-pkg/lib/plugin-manager.sh +239 -0
- package/monol-plugin-scout-pkg/lib/priority-scorer.sh +262 -0
- package/monol-plugin-scout-pkg/lib/profile-learner.sh +339 -0
- package/monol-plugin-scout-pkg/lib/project-analyzer.sh +281 -0
- package/monol-plugin-scout-pkg/lib/recommendation-controller.sh +290 -0
- package/monol-plugin-scout-pkg/lib/rejection-learner.sh +232 -0
- package/monol-plugin-scout-pkg/lib/scheduler.sh +275 -0
- package/monol-plugin-scout-pkg/lib/skill-scout.sh +729 -0
- package/monol-plugin-scout-pkg/lib/sync.sh +221 -0
- package/monol-plugin-scout-pkg/lib/team-learner.sh +450 -0
- package/monol-plugin-scout-pkg/lib/team-manager.sh +369 -0
- package/monol-plugin-scout-pkg/lib/trend-learner.sh +428 -0
- package/monol-plugin-scout-pkg/lib/trust-manager.sh +261 -0
- package/monol-plugin-scout-pkg/lib/trusted-installer.sh +738 -0
- package/monol-plugin-scout-pkg/plugin.json +3 -2
- package/monol-plugin-scout-pkg/skills/audit.md +6 -0
- package/monol-plugin-scout-pkg/skills/cleanup.md +6 -0
- package/monol-plugin-scout-pkg/skills/compare.md +6 -0
- package/monol-plugin-scout-pkg/skills/console.md +315 -0
- package/monol-plugin-scout-pkg/skills/explore.md +6 -0
- package/monol-plugin-scout-pkg/skills/fork.md +6 -0
- package/monol-plugin-scout-pkg/skills/frequency.md +93 -0
- package/monol-plugin-scout-pkg/skills/install.md +127 -0
- package/monol-plugin-scout-pkg/skills/priority.md +77 -0
- package/monol-plugin-scout-pkg/skills/quiet.md +73 -0
- package/monol-plugin-scout-pkg/skills/schedule.md +95 -0
- package/monol-plugin-scout-pkg/skills/scout.md +27 -17
- package/monol-plugin-scout-pkg/skills/skills.md +230 -0
- package/monol-plugin-scout-pkg/skills/team.md +117 -0
- package/monol-plugin-scout-pkg/skills/timing.md +97 -0
- package/monol-plugin-scout-pkg/skills/trust.md +120 -0
- package/monol-plugin-scout-pkg/skills/trusted-install.md +264 -0
- package/monol-plugin-scout-pkg/skills/uninstall.md +100 -0
- package/monol-plugin-scout-pkg/web/components/activity-chart.js +208 -0
- package/monol-plugin-scout-pkg/web/components/index.js +27 -0
- package/monol-plugin-scout-pkg/web/components/insight-card.js +365 -0
- package/monol-plugin-scout-pkg/web/components/overview.js +154 -0
- package/monol-plugin-scout-pkg/web/components/plugin-list.js +242 -0
- package/monol-plugin-scout-pkg/web/components/stats-card.js +126 -0
- package/monol-plugin-scout-pkg/web/components/team-list.js +346 -0
- package/monol-plugin-scout-pkg/web/console.html +2098 -0
- package/monol-plugin-scout-pkg/web/dashboard.html +2106 -0
- package/monol-plugin-scout-pkg/web/manifest.json +29 -0
- package/package.json +1 -1
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Plugin Scout - 데이터 검증 유틸리티
|
|
3
|
+
# JSON 파일 검증 및 복구
|
|
4
|
+
|
|
5
|
+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$0")/..}"
|
|
6
|
+
DATA_DIR="$PLUGIN_ROOT/data"
|
|
7
|
+
BACKUP_DIR="$DATA_DIR/backups"
|
|
8
|
+
|
|
9
|
+
# 백업 디렉토리 생성
|
|
10
|
+
mkdir -p "$BACKUP_DIR"
|
|
11
|
+
|
|
12
|
+
# JSON 파일 유효성 검사
|
|
13
|
+
validate_json() {
|
|
14
|
+
local file="$1"
|
|
15
|
+
|
|
16
|
+
if [ ! -f "$file" ]; then
|
|
17
|
+
echo "error:not_found"
|
|
18
|
+
return 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
if ! command -v jq &> /dev/null; then
|
|
22
|
+
echo "error:jq_not_found"
|
|
23
|
+
return 1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
if jq empty "$file" 2>/dev/null; then
|
|
27
|
+
echo "valid"
|
|
28
|
+
return 0
|
|
29
|
+
else
|
|
30
|
+
echo "error:invalid_json"
|
|
31
|
+
return 1
|
|
32
|
+
fi
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# 파일 백업
|
|
36
|
+
backup_file() {
|
|
37
|
+
local file="$1"
|
|
38
|
+
local filename=$(basename "$file")
|
|
39
|
+
local timestamp=$(date +"%Y%m%d_%H%M%S")
|
|
40
|
+
local backup_path="$BACKUP_DIR/${filename}.${timestamp}.bak"
|
|
41
|
+
|
|
42
|
+
if [ -f "$file" ]; then
|
|
43
|
+
cp "$file" "$backup_path"
|
|
44
|
+
echo "$backup_path"
|
|
45
|
+
fi
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# history.json 스키마 검증
|
|
49
|
+
validate_history() {
|
|
50
|
+
local file="$DATA_DIR/history.json"
|
|
51
|
+
|
|
52
|
+
if ! validate_json "$file" >/dev/null; then
|
|
53
|
+
echo "invalid"
|
|
54
|
+
return 1
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# 필수 필드 검증
|
|
58
|
+
local has_version=$(jq -r 'has("version")' "$file")
|
|
59
|
+
local has_declined=$(jq -r 'has("declined")' "$file")
|
|
60
|
+
local has_preferences=$(jq -r 'has("preferences")' "$file")
|
|
61
|
+
|
|
62
|
+
if [ "$has_version" = "true" ] && [ "$has_declined" = "true" ] && [ "$has_preferences" = "true" ]; then
|
|
63
|
+
echo "valid"
|
|
64
|
+
return 0
|
|
65
|
+
else
|
|
66
|
+
echo "missing_fields"
|
|
67
|
+
return 1
|
|
68
|
+
fi
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# usage.json 스키마 검증
|
|
72
|
+
validate_usage() {
|
|
73
|
+
local file="$DATA_DIR/usage.json"
|
|
74
|
+
|
|
75
|
+
if ! validate_json "$file" >/dev/null; then
|
|
76
|
+
echo "invalid"
|
|
77
|
+
return 1
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# 필수 필드 검증
|
|
81
|
+
local has_plugins=$(jq -r 'has("plugins")' "$file")
|
|
82
|
+
local has_sessions=$(jq -r 'has("sessions")' "$file")
|
|
83
|
+
|
|
84
|
+
if [ "$has_plugins" = "true" ] && [ "$has_sessions" = "true" ]; then
|
|
85
|
+
echo "valid"
|
|
86
|
+
return 0
|
|
87
|
+
else
|
|
88
|
+
echo "missing_fields"
|
|
89
|
+
return 1
|
|
90
|
+
fi
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# team.json 스키마 검증
|
|
94
|
+
validate_team() {
|
|
95
|
+
local file="$DATA_DIR/team.json"
|
|
96
|
+
|
|
97
|
+
if ! validate_json "$file" >/dev/null; then
|
|
98
|
+
echo "invalid"
|
|
99
|
+
return 1
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# 필수 필드 검증
|
|
103
|
+
local has_version=$(jq -r 'has("version")' "$file")
|
|
104
|
+
local has_members=$(jq -r 'has("members")' "$file")
|
|
105
|
+
local has_stats=$(jq -r 'has("stats")' "$file")
|
|
106
|
+
|
|
107
|
+
if [ "$has_version" = "true" ] && [ "$has_members" = "true" ] && [ "$has_stats" = "true" ]; then
|
|
108
|
+
echo "valid"
|
|
109
|
+
return 0
|
|
110
|
+
else
|
|
111
|
+
echo "missing_fields"
|
|
112
|
+
return 1
|
|
113
|
+
fi
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# history.json 초기화
|
|
117
|
+
init_history() {
|
|
118
|
+
local file="$DATA_DIR/history.json"
|
|
119
|
+
local now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
120
|
+
|
|
121
|
+
# 기존 파일 백업
|
|
122
|
+
if [ -f "$file" ]; then
|
|
123
|
+
backup_file "$file"
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
cat > "$file" << EOF
|
|
127
|
+
{
|
|
128
|
+
"version": "2.0.0",
|
|
129
|
+
"declined": {},
|
|
130
|
+
"installed": {},
|
|
131
|
+
"viewed": [],
|
|
132
|
+
"rejectionPatterns": {
|
|
133
|
+
"byCategory": {},
|
|
134
|
+
"byReason": {}
|
|
135
|
+
},
|
|
136
|
+
"rejectionReasons": {
|
|
137
|
+
"not-relevant": "프로젝트에 관련 없음",
|
|
138
|
+
"wrong-language": "다른 언어용",
|
|
139
|
+
"already-have": "유사 플러그인 보유",
|
|
140
|
+
"too-complex": "복잡함",
|
|
141
|
+
"security": "보안 우려",
|
|
142
|
+
"other": "기타"
|
|
143
|
+
},
|
|
144
|
+
"preferences": {
|
|
145
|
+
"categories": [],
|
|
146
|
+
"autoInstall": false,
|
|
147
|
+
"quietMode": false,
|
|
148
|
+
"recommendationCooldown": 30,
|
|
149
|
+
"maxRejectionsBeforeBlock": 3,
|
|
150
|
+
"maxRecommendationsPerSession": 1,
|
|
151
|
+
"maxRecommendationsPerDay": 3,
|
|
152
|
+
"smartTiming": {
|
|
153
|
+
"onlyAfterCommit": false,
|
|
154
|
+
"onlyAfterPR": false
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"updatedAt": "$now"
|
|
158
|
+
}
|
|
159
|
+
EOF
|
|
160
|
+
|
|
161
|
+
echo "initialized"
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
# usage.json 초기화
|
|
165
|
+
init_usage() {
|
|
166
|
+
local file="$DATA_DIR/usage.json"
|
|
167
|
+
local now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
168
|
+
|
|
169
|
+
# 기존 파일 백업
|
|
170
|
+
if [ -f "$file" ]; then
|
|
171
|
+
backup_file "$file"
|
|
172
|
+
fi
|
|
173
|
+
|
|
174
|
+
cat > "$file" << EOF
|
|
175
|
+
{
|
|
176
|
+
"plugins": {},
|
|
177
|
+
"users": {},
|
|
178
|
+
"sessions": {
|
|
179
|
+
"total": 0,
|
|
180
|
+
"thisWeek": 0
|
|
181
|
+
},
|
|
182
|
+
"lastUpdated": "$now"
|
|
183
|
+
}
|
|
184
|
+
EOF
|
|
185
|
+
|
|
186
|
+
echo "initialized"
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
# 모든 데이터 파일 검증
|
|
190
|
+
validate_all() {
|
|
191
|
+
echo "=== Data Validation ==="
|
|
192
|
+
echo ""
|
|
193
|
+
|
|
194
|
+
local all_valid=true
|
|
195
|
+
|
|
196
|
+
# history.json
|
|
197
|
+
echo -n "history.json: "
|
|
198
|
+
if [ -f "$DATA_DIR/history.json" ]; then
|
|
199
|
+
local result=$(validate_history)
|
|
200
|
+
echo "$result"
|
|
201
|
+
[ "$result" != "valid" ] && all_valid=false
|
|
202
|
+
else
|
|
203
|
+
echo "not_found"
|
|
204
|
+
all_valid=false
|
|
205
|
+
fi
|
|
206
|
+
|
|
207
|
+
# usage.json
|
|
208
|
+
echo -n "usage.json: "
|
|
209
|
+
if [ -f "$DATA_DIR/usage.json" ]; then
|
|
210
|
+
local result=$(validate_usage)
|
|
211
|
+
echo "$result"
|
|
212
|
+
[ "$result" != "valid" ] && all_valid=false
|
|
213
|
+
else
|
|
214
|
+
echo "not_found"
|
|
215
|
+
all_valid=false
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
# team.json
|
|
219
|
+
echo -n "team.json: "
|
|
220
|
+
if [ -f "$DATA_DIR/team.json" ]; then
|
|
221
|
+
local result=$(validate_team)
|
|
222
|
+
echo "$result"
|
|
223
|
+
[ "$result" != "valid" ] && all_valid=false
|
|
224
|
+
else
|
|
225
|
+
echo "not_found (optional)"
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
# .session
|
|
229
|
+
echo -n ".session: "
|
|
230
|
+
if [ -f "$DATA_DIR/.session" ]; then
|
|
231
|
+
local result=$(validate_json "$DATA_DIR/.session")
|
|
232
|
+
echo "$result"
|
|
233
|
+
[ "$result" != "valid" ] && all_valid=false
|
|
234
|
+
else
|
|
235
|
+
echo "not_found (runtime)"
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
echo ""
|
|
239
|
+
if [ "$all_valid" = "true" ]; then
|
|
240
|
+
echo "All data files are valid."
|
|
241
|
+
else
|
|
242
|
+
echo "Some data files need attention."
|
|
243
|
+
fi
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
# 손상된 파일 복구
|
|
247
|
+
repair() {
|
|
248
|
+
local file="$1"
|
|
249
|
+
|
|
250
|
+
case "$file" in
|
|
251
|
+
history)
|
|
252
|
+
echo "Repairing history.json..."
|
|
253
|
+
init_history
|
|
254
|
+
;;
|
|
255
|
+
usage)
|
|
256
|
+
echo "Repairing usage.json..."
|
|
257
|
+
init_usage
|
|
258
|
+
;;
|
|
259
|
+
all)
|
|
260
|
+
echo "Repairing all data files..."
|
|
261
|
+
init_history
|
|
262
|
+
init_usage
|
|
263
|
+
;;
|
|
264
|
+
*)
|
|
265
|
+
echo "Usage: repair {history|usage|all}"
|
|
266
|
+
;;
|
|
267
|
+
esac
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
# 백업 목록
|
|
271
|
+
list_backups() {
|
|
272
|
+
echo "=== Backups ==="
|
|
273
|
+
if [ -d "$BACKUP_DIR" ] && [ "$(ls -A "$BACKUP_DIR" 2>/dev/null)" ]; then
|
|
274
|
+
ls -la "$BACKUP_DIR"
|
|
275
|
+
else
|
|
276
|
+
echo "No backups found"
|
|
277
|
+
fi
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
# 백업에서 복원
|
|
281
|
+
restore_backup() {
|
|
282
|
+
local backup_file="$1"
|
|
283
|
+
|
|
284
|
+
if [ -z "$backup_file" ]; then
|
|
285
|
+
echo "Usage: restore <backup-file>"
|
|
286
|
+
list_backups
|
|
287
|
+
return 1
|
|
288
|
+
fi
|
|
289
|
+
|
|
290
|
+
local full_path="$BACKUP_DIR/$backup_file"
|
|
291
|
+
if [ ! -f "$full_path" ]; then
|
|
292
|
+
echo "Backup not found: $backup_file"
|
|
293
|
+
return 1
|
|
294
|
+
fi
|
|
295
|
+
|
|
296
|
+
# 파일명에서 원본 파일명 추출
|
|
297
|
+
local original_name=$(echo "$backup_file" | sed 's/\.[0-9]*_[0-9]*\.bak$//')
|
|
298
|
+
local target_file="$DATA_DIR/$original_name"
|
|
299
|
+
|
|
300
|
+
# 현재 파일 백업 후 복원
|
|
301
|
+
if [ -f "$target_file" ]; then
|
|
302
|
+
backup_file "$target_file"
|
|
303
|
+
fi
|
|
304
|
+
|
|
305
|
+
cp "$full_path" "$target_file"
|
|
306
|
+
echo "Restored: $original_name from $backup_file"
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
# CLI 인터페이스
|
|
310
|
+
case "$1" in
|
|
311
|
+
validate)
|
|
312
|
+
validate_all
|
|
313
|
+
;;
|
|
314
|
+
check)
|
|
315
|
+
validate_json "$2"
|
|
316
|
+
;;
|
|
317
|
+
history)
|
|
318
|
+
validate_history
|
|
319
|
+
;;
|
|
320
|
+
usage)
|
|
321
|
+
validate_usage
|
|
322
|
+
;;
|
|
323
|
+
team)
|
|
324
|
+
validate_team
|
|
325
|
+
;;
|
|
326
|
+
init)
|
|
327
|
+
case "$2" in
|
|
328
|
+
history) init_history ;;
|
|
329
|
+
usage) init_usage ;;
|
|
330
|
+
*) echo "Usage: init {history|usage}" ;;
|
|
331
|
+
esac
|
|
332
|
+
;;
|
|
333
|
+
repair)
|
|
334
|
+
repair "$2"
|
|
335
|
+
;;
|
|
336
|
+
backup)
|
|
337
|
+
backup_file "$2"
|
|
338
|
+
;;
|
|
339
|
+
backups)
|
|
340
|
+
list_backups
|
|
341
|
+
;;
|
|
342
|
+
restore)
|
|
343
|
+
restore_backup "$2"
|
|
344
|
+
;;
|
|
345
|
+
*)
|
|
346
|
+
echo "Usage: $0 {validate|check|history|usage|team|init|repair|backup|backups|restore}"
|
|
347
|
+
echo ""
|
|
348
|
+
echo "Commands:"
|
|
349
|
+
echo " validate - Validate all data files"
|
|
350
|
+
echo " check <file> - Check if JSON file is valid"
|
|
351
|
+
echo " history - Validate history.json schema"
|
|
352
|
+
echo " usage - Validate usage.json schema"
|
|
353
|
+
echo " team - Validate team.json schema"
|
|
354
|
+
echo " init <type> - Initialize data file (history|usage)"
|
|
355
|
+
echo " repair <type> - Repair corrupted file (history|usage|all)"
|
|
356
|
+
echo " backup <file> - Create backup of file"
|
|
357
|
+
echo " backups - List all backups"
|
|
358
|
+
echo " restore <backup> - Restore from backup"
|
|
359
|
+
;;
|
|
360
|
+
esac
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Plugin Scout - 에러 처리 유틸리티
|
|
3
|
+
# 에러 로깅, 복구, 알림
|
|
4
|
+
|
|
5
|
+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$0")/..}"
|
|
6
|
+
DATA_DIR="$PLUGIN_ROOT/data"
|
|
7
|
+
LOG_DIR="$DATA_DIR/logs"
|
|
8
|
+
ERROR_LOG="$LOG_DIR/error.log"
|
|
9
|
+
|
|
10
|
+
# 로그 디렉토리 생성
|
|
11
|
+
mkdir -p "$LOG_DIR"
|
|
12
|
+
|
|
13
|
+
# 에러 코드 정의 (bash 3.x 호환)
|
|
14
|
+
get_error_message() {
|
|
15
|
+
local code="$1"
|
|
16
|
+
case "$code" in
|
|
17
|
+
E001) echo "jq not found" ;;
|
|
18
|
+
E002) echo "Invalid JSON file" ;;
|
|
19
|
+
E003) echo "File not found" ;;
|
|
20
|
+
E004) echo "Permission denied" ;;
|
|
21
|
+
E005) echo "Network error" ;;
|
|
22
|
+
E006) echo "Data corruption" ;;
|
|
23
|
+
E007) echo "Unknown error" ;;
|
|
24
|
+
*) echo "Unknown error" ;;
|
|
25
|
+
esac
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# 에러 로그 기록
|
|
29
|
+
log_error() {
|
|
30
|
+
local code="$1"
|
|
31
|
+
local default_msg=$(get_error_message "$code")
|
|
32
|
+
local message="${2:-$default_msg}"
|
|
33
|
+
local context="$3"
|
|
34
|
+
local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
35
|
+
|
|
36
|
+
# 로그 파일에 기록
|
|
37
|
+
echo "[$timestamp] [$code] $message ${context:+| Context: $context}" >> "$ERROR_LOG"
|
|
38
|
+
|
|
39
|
+
# stderr로도 출력 (디버깅용)
|
|
40
|
+
if [ "$SCOUT_DEBUG" = "true" ]; then
|
|
41
|
+
echo "ERROR [$code]: $message" >&2
|
|
42
|
+
fi
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# 에러 처리 및 복구 시도
|
|
46
|
+
handle_error() {
|
|
47
|
+
local code="$1"
|
|
48
|
+
local context="$2"
|
|
49
|
+
|
|
50
|
+
log_error "$code" "" "$context"
|
|
51
|
+
|
|
52
|
+
case "$code" in
|
|
53
|
+
E001)
|
|
54
|
+
echo "jq is required. Install with: brew install jq"
|
|
55
|
+
return 1
|
|
56
|
+
;;
|
|
57
|
+
E002)
|
|
58
|
+
# Invalid JSON - 복구 시도
|
|
59
|
+
echo "Attempting to recover invalid JSON..."
|
|
60
|
+
if [ -f "$PLUGIN_ROOT/lib/data-validator.sh" ]; then
|
|
61
|
+
bash "$PLUGIN_ROOT/lib/data-validator.sh" repair all
|
|
62
|
+
fi
|
|
63
|
+
;;
|
|
64
|
+
E003)
|
|
65
|
+
# File not found - 초기화
|
|
66
|
+
echo "File not found. Initializing..."
|
|
67
|
+
if [ -f "$PLUGIN_ROOT/lib/data-validator.sh" ]; then
|
|
68
|
+
bash "$PLUGIN_ROOT/lib/data-validator.sh" init history
|
|
69
|
+
bash "$PLUGIN_ROOT/lib/data-validator.sh" init usage
|
|
70
|
+
fi
|
|
71
|
+
;;
|
|
72
|
+
E004)
|
|
73
|
+
echo "Permission denied. Check file permissions."
|
|
74
|
+
return 1
|
|
75
|
+
;;
|
|
76
|
+
E005)
|
|
77
|
+
echo "Network error. Will retry later."
|
|
78
|
+
return 1
|
|
79
|
+
;;
|
|
80
|
+
E006)
|
|
81
|
+
echo "Data corruption detected. Attempting recovery..."
|
|
82
|
+
if [ -f "$PLUGIN_ROOT/lib/data-validator.sh" ]; then
|
|
83
|
+
bash "$PLUGIN_ROOT/lib/data-validator.sh" repair all
|
|
84
|
+
fi
|
|
85
|
+
;;
|
|
86
|
+
*)
|
|
87
|
+
echo "Unknown error: $code"
|
|
88
|
+
return 1
|
|
89
|
+
;;
|
|
90
|
+
esac
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# 안전한 JSON 읽기
|
|
94
|
+
safe_json_read() {
|
|
95
|
+
local file="$1"
|
|
96
|
+
local query="${2:-.}"
|
|
97
|
+
local default="${3:-null}"
|
|
98
|
+
|
|
99
|
+
if [ ! -f "$file" ]; then
|
|
100
|
+
log_error "E003" "File not found" "$file"
|
|
101
|
+
echo "$default"
|
|
102
|
+
return 1
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
if ! command -v jq &> /dev/null; then
|
|
106
|
+
log_error "E001" "jq not found"
|
|
107
|
+
echo "$default"
|
|
108
|
+
return 1
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
local result
|
|
112
|
+
result=$(jq -r "$query" "$file" 2>/dev/null)
|
|
113
|
+
local exit_code=$?
|
|
114
|
+
|
|
115
|
+
if [ $exit_code -ne 0 ] || [ "$result" = "null" ] && [ "$default" != "null" ]; then
|
|
116
|
+
log_error "E002" "JSON read failed" "$file: $query"
|
|
117
|
+
echo "$default"
|
|
118
|
+
return 1
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
echo "$result"
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# 안전한 JSON 쓰기
|
|
125
|
+
safe_json_write() {
|
|
126
|
+
local file="$1"
|
|
127
|
+
local query="$2"
|
|
128
|
+
|
|
129
|
+
if ! command -v jq &> /dev/null; then
|
|
130
|
+
log_error "E001" "jq not found"
|
|
131
|
+
return 1
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
if [ ! -f "$file" ]; then
|
|
135
|
+
log_error "E003" "File not found" "$file"
|
|
136
|
+
return 1
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
local temp_file="${file}.tmp"
|
|
140
|
+
|
|
141
|
+
if ! jq "$query" "$file" > "$temp_file" 2>/dev/null; then
|
|
142
|
+
log_error "E002" "JSON write failed" "$file: $query"
|
|
143
|
+
rm -f "$temp_file"
|
|
144
|
+
return 1
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
if ! mv "$temp_file" "$file"; then
|
|
148
|
+
log_error "E004" "Permission denied" "$file"
|
|
149
|
+
rm -f "$temp_file"
|
|
150
|
+
return 1
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
return 0
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
# 에러 로그 조회
|
|
157
|
+
view_errors() {
|
|
158
|
+
local lines="${1:-20}"
|
|
159
|
+
|
|
160
|
+
if [ ! -f "$ERROR_LOG" ]; then
|
|
161
|
+
echo "No errors logged yet."
|
|
162
|
+
return
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
echo "=== Recent Errors (last $lines) ==="
|
|
166
|
+
tail -n "$lines" "$ERROR_LOG"
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# 에러 로그 정리
|
|
170
|
+
cleanup_errors() {
|
|
171
|
+
local days="${1:-7}"
|
|
172
|
+
|
|
173
|
+
if [ ! -f "$ERROR_LOG" ]; then
|
|
174
|
+
echo "No error log to clean."
|
|
175
|
+
return
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
local cutoff_date=$(date -v-${days}d +"%Y-%m-%d" 2>/dev/null || date -d "$days days ago" +"%Y-%m-%d" 2>/dev/null)
|
|
179
|
+
|
|
180
|
+
if [ -n "$cutoff_date" ]; then
|
|
181
|
+
# 날짜 이후 로그만 유지
|
|
182
|
+
grep -E "^\[${cutoff_date}|^\[$(date +%Y-)" "$ERROR_LOG" > "$ERROR_LOG.tmp" 2>/dev/null
|
|
183
|
+
mv "$ERROR_LOG.tmp" "$ERROR_LOG"
|
|
184
|
+
echo "Cleaned errors older than $days days."
|
|
185
|
+
else
|
|
186
|
+
echo "Could not determine cutoff date."
|
|
187
|
+
fi
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
# 에러 통계
|
|
191
|
+
error_stats() {
|
|
192
|
+
if [ ! -f "$ERROR_LOG" ]; then
|
|
193
|
+
echo "No errors logged yet."
|
|
194
|
+
return
|
|
195
|
+
fi
|
|
196
|
+
|
|
197
|
+
echo "=== Error Statistics ==="
|
|
198
|
+
echo ""
|
|
199
|
+
echo "By Error Code:"
|
|
200
|
+
grep -oE '\[E[0-9]+\]' "$ERROR_LOG" | sort | uniq -c | sort -rn
|
|
201
|
+
echo ""
|
|
202
|
+
echo "By Date:"
|
|
203
|
+
grep -oE '^\[[0-9]{4}-[0-9]{2}-[0-9]{2}' "$ERROR_LOG" | sort | uniq -c | tail -7
|
|
204
|
+
echo ""
|
|
205
|
+
echo "Total Errors: $(wc -l < "$ERROR_LOG" | tr -d ' ')"
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# 시스템 상태 확인
|
|
209
|
+
health_check() {
|
|
210
|
+
echo "=== Health Check ==="
|
|
211
|
+
echo ""
|
|
212
|
+
|
|
213
|
+
# jq 확인
|
|
214
|
+
echo -n "jq: "
|
|
215
|
+
if command -v jq &> /dev/null; then
|
|
216
|
+
echo "OK ($(jq --version))"
|
|
217
|
+
else
|
|
218
|
+
echo "NOT FOUND"
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
# 데이터 디렉토리 확인
|
|
222
|
+
echo -n "Data directory: "
|
|
223
|
+
if [ -d "$DATA_DIR" ] && [ -w "$DATA_DIR" ]; then
|
|
224
|
+
echo "OK (writable)"
|
|
225
|
+
else
|
|
226
|
+
echo "ERROR (not writable)"
|
|
227
|
+
fi
|
|
228
|
+
|
|
229
|
+
# JSON 파일 확인
|
|
230
|
+
echo ""
|
|
231
|
+
echo "Data Files:"
|
|
232
|
+
for file in history.json usage.json team.json; do
|
|
233
|
+
echo -n " $file: "
|
|
234
|
+
if [ -f "$DATA_DIR/$file" ]; then
|
|
235
|
+
if jq empty "$DATA_DIR/$file" 2>/dev/null; then
|
|
236
|
+
echo "OK"
|
|
237
|
+
else
|
|
238
|
+
echo "INVALID JSON"
|
|
239
|
+
fi
|
|
240
|
+
else
|
|
241
|
+
echo "NOT FOUND"
|
|
242
|
+
fi
|
|
243
|
+
done
|
|
244
|
+
|
|
245
|
+
# 최근 에러 확인
|
|
246
|
+
echo ""
|
|
247
|
+
echo -n "Recent errors (24h): "
|
|
248
|
+
if [ -f "$ERROR_LOG" ]; then
|
|
249
|
+
local today=$(date +"%Y-%m-%d")
|
|
250
|
+
local count=$(grep -c "^\[$today" "$ERROR_LOG" 2>/dev/null || echo "0")
|
|
251
|
+
echo "$count"
|
|
252
|
+
else
|
|
253
|
+
echo "0"
|
|
254
|
+
fi
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
# CLI 인터페이스
|
|
258
|
+
case "$1" in
|
|
259
|
+
log)
|
|
260
|
+
log_error "$2" "$3" "$4"
|
|
261
|
+
;;
|
|
262
|
+
handle)
|
|
263
|
+
handle_error "$2" "$3"
|
|
264
|
+
;;
|
|
265
|
+
read)
|
|
266
|
+
safe_json_read "$2" "$3" "$4"
|
|
267
|
+
;;
|
|
268
|
+
write)
|
|
269
|
+
safe_json_write "$2" "$3"
|
|
270
|
+
;;
|
|
271
|
+
view)
|
|
272
|
+
view_errors "$2"
|
|
273
|
+
;;
|
|
274
|
+
cleanup)
|
|
275
|
+
cleanup_errors "$2"
|
|
276
|
+
;;
|
|
277
|
+
stats)
|
|
278
|
+
error_stats
|
|
279
|
+
;;
|
|
280
|
+
health)
|
|
281
|
+
health_check
|
|
282
|
+
;;
|
|
283
|
+
*)
|
|
284
|
+
echo "Usage: $0 {log|handle|read|write|view|cleanup|stats|health}"
|
|
285
|
+
echo ""
|
|
286
|
+
echo "Commands:"
|
|
287
|
+
echo " log <code> [msg] [ctx] - Log an error"
|
|
288
|
+
echo " handle <code> [ctx] - Handle error with recovery"
|
|
289
|
+
echo " read <file> [query] - Safe JSON read"
|
|
290
|
+
echo " write <file> <query> - Safe JSON write"
|
|
291
|
+
echo " view [lines] - View recent errors"
|
|
292
|
+
echo " cleanup [days] - Clean old error logs"
|
|
293
|
+
echo " stats - Show error statistics"
|
|
294
|
+
echo " health - System health check"
|
|
295
|
+
;;
|
|
296
|
+
esac
|