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.
Files changed (87) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +244 -0
  3. package/monol-plugin-scout-pkg/CLAUDE.md +125 -8
  4. package/monol-plugin-scout-pkg/api/mock/categories.json +81 -0
  5. package/monol-plugin-scout-pkg/api/mock/insights.json +111 -0
  6. package/monol-plugin-scout-pkg/api/mock/marketplace.json +501 -0
  7. package/monol-plugin-scout-pkg/api/mock/trending.json +96 -0
  8. package/monol-plugin-scout-pkg/commands/console.md +79 -0
  9. package/monol-plugin-scout-pkg/commands/frequency.md +32 -0
  10. package/monol-plugin-scout-pkg/commands/install.md +32 -0
  11. package/monol-plugin-scout-pkg/commands/priority.md +30 -0
  12. package/monol-plugin-scout-pkg/commands/quiet.md +32 -0
  13. package/monol-plugin-scout-pkg/commands/schedule.md +32 -0
  14. package/monol-plugin-scout-pkg/commands/scout.md +8 -5
  15. package/monol-plugin-scout-pkg/commands/skills.md +160 -0
  16. package/monol-plugin-scout-pkg/commands/team.md +32 -0
  17. package/monol-plugin-scout-pkg/commands/timing.md +32 -0
  18. package/monol-plugin-scout-pkg/commands/trust.md +85 -0
  19. package/monol-plugin-scout-pkg/commands/trusted-install.md +98 -0
  20. package/monol-plugin-scout-pkg/commands/uninstall.md +31 -0
  21. package/monol-plugin-scout-pkg/config.yaml +57 -0
  22. package/monol-plugin-scout-pkg/data/.cache/676d5ab664292155e5f509c69d4edae1 +10 -0
  23. package/monol-plugin-scout-pkg/data/.session +8 -0
  24. package/monol-plugin-scout-pkg/data/analytics.json +102 -0
  25. package/monol-plugin-scout-pkg/data/history.json +67 -11
  26. package/monol-plugin-scout-pkg/data/logs/scout.log +1 -0
  27. package/monol-plugin-scout-pkg/data/schedules.json +17 -0
  28. package/monol-plugin-scout-pkg/data/team.json +53 -0
  29. package/monol-plugin-scout-pkg/data/usage.json +100 -7
  30. package/monol-plugin-scout-pkg/docs/ARCHITECTURE.md +201 -0
  31. package/monol-plugin-scout-pkg/hooks/generate-insights.sh +102 -0
  32. package/monol-plugin-scout-pkg/hooks/hooks.json +36 -1
  33. package/monol-plugin-scout-pkg/hooks/on-session-end.sh +136 -0
  34. package/monol-plugin-scout-pkg/hooks/on-session-start.sh +43 -0
  35. package/monol-plugin-scout-pkg/hooks/on-stop.md +79 -0
  36. package/monol-plugin-scout-pkg/hooks/open-console.sh +111 -0
  37. package/monol-plugin-scout-pkg/hooks/open-dashboard.sh +61 -0
  38. package/monol-plugin-scout-pkg/hooks/track-usage.sh +59 -0
  39. package/monol-plugin-scout-pkg/lib/ai-recommender.sh +505 -0
  40. package/monol-plugin-scout-pkg/lib/cache.sh +194 -0
  41. package/monol-plugin-scout-pkg/lib/data-validator.sh +360 -0
  42. package/monol-plugin-scout-pkg/lib/error-handler.sh +296 -0
  43. package/monol-plugin-scout-pkg/lib/logger.sh +263 -0
  44. package/monol-plugin-scout-pkg/lib/plugin-manager.sh +239 -0
  45. package/monol-plugin-scout-pkg/lib/priority-scorer.sh +262 -0
  46. package/monol-plugin-scout-pkg/lib/profile-learner.sh +339 -0
  47. package/monol-plugin-scout-pkg/lib/project-analyzer.sh +281 -0
  48. package/monol-plugin-scout-pkg/lib/recommendation-controller.sh +290 -0
  49. package/monol-plugin-scout-pkg/lib/rejection-learner.sh +232 -0
  50. package/monol-plugin-scout-pkg/lib/scheduler.sh +275 -0
  51. package/monol-plugin-scout-pkg/lib/skill-scout.sh +729 -0
  52. package/monol-plugin-scout-pkg/lib/sync.sh +221 -0
  53. package/monol-plugin-scout-pkg/lib/team-learner.sh +450 -0
  54. package/monol-plugin-scout-pkg/lib/team-manager.sh +369 -0
  55. package/monol-plugin-scout-pkg/lib/trend-learner.sh +428 -0
  56. package/monol-plugin-scout-pkg/lib/trust-manager.sh +261 -0
  57. package/monol-plugin-scout-pkg/lib/trusted-installer.sh +738 -0
  58. package/monol-plugin-scout-pkg/plugin.json +3 -2
  59. package/monol-plugin-scout-pkg/skills/audit.md +6 -0
  60. package/monol-plugin-scout-pkg/skills/cleanup.md +6 -0
  61. package/monol-plugin-scout-pkg/skills/compare.md +6 -0
  62. package/monol-plugin-scout-pkg/skills/console.md +315 -0
  63. package/monol-plugin-scout-pkg/skills/explore.md +6 -0
  64. package/monol-plugin-scout-pkg/skills/fork.md +6 -0
  65. package/monol-plugin-scout-pkg/skills/frequency.md +93 -0
  66. package/monol-plugin-scout-pkg/skills/install.md +127 -0
  67. package/monol-plugin-scout-pkg/skills/priority.md +77 -0
  68. package/monol-plugin-scout-pkg/skills/quiet.md +73 -0
  69. package/monol-plugin-scout-pkg/skills/schedule.md +95 -0
  70. package/monol-plugin-scout-pkg/skills/scout.md +27 -17
  71. package/monol-plugin-scout-pkg/skills/skills.md +230 -0
  72. package/monol-plugin-scout-pkg/skills/team.md +117 -0
  73. package/monol-plugin-scout-pkg/skills/timing.md +97 -0
  74. package/monol-plugin-scout-pkg/skills/trust.md +120 -0
  75. package/monol-plugin-scout-pkg/skills/trusted-install.md +264 -0
  76. package/monol-plugin-scout-pkg/skills/uninstall.md +100 -0
  77. package/monol-plugin-scout-pkg/web/components/activity-chart.js +208 -0
  78. package/monol-plugin-scout-pkg/web/components/index.js +27 -0
  79. package/monol-plugin-scout-pkg/web/components/insight-card.js +365 -0
  80. package/monol-plugin-scout-pkg/web/components/overview.js +154 -0
  81. package/monol-plugin-scout-pkg/web/components/plugin-list.js +242 -0
  82. package/monol-plugin-scout-pkg/web/components/stats-card.js +126 -0
  83. package/monol-plugin-scout-pkg/web/components/team-list.js +346 -0
  84. package/monol-plugin-scout-pkg/web/console.html +2098 -0
  85. package/monol-plugin-scout-pkg/web/dashboard.html +2106 -0
  86. package/monol-plugin-scout-pkg/web/manifest.json +29 -0
  87. package/package.json +1 -1
@@ -0,0 +1,505 @@
1
+ #!/bin/bash
2
+ # Plugin Scout - AI 기반 추천 시스템
3
+ # 코드 구조 분석, 의존성 그래프 분석, 유사 프로젝트 참고 추천
4
+
5
+ PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$0")/..}"
6
+ DATA_DIR="$PLUGIN_ROOT/data"
7
+ AI_CACHE_FILE="$DATA_DIR/ai-cache.json"
8
+ PROJECT_SIGNATURES_FILE="$DATA_DIR/project-signatures.json"
9
+
10
+ # AI 캐시 초기화
11
+ init_ai_cache() {
12
+ if [ ! -f "$AI_CACHE_FILE" ]; then
13
+ mkdir -p "$DATA_DIR"
14
+ echo '{
15
+ "version": "1.0.0",
16
+ "lastUpdated": "'$(date -u +"%Y-%m-%dT%H:%M:%SZ")'",
17
+ "projectAnalysis": {},
18
+ "dependencyGraph": {},
19
+ "recommendations": {},
20
+ "similarProjects": {}
21
+ }' > "$AI_CACHE_FILE"
22
+ fi
23
+ }
24
+
25
+ # 프로젝트 시그니처 DB 초기화
26
+ init_signatures() {
27
+ if [ ! -f "$PROJECT_SIGNATURES_FILE" ]; then
28
+ mkdir -p "$DATA_DIR"
29
+ echo '{
30
+ "version": "1.0.0",
31
+ "signatures": {
32
+ "react-typescript": {
33
+ "markers": ["react", "typescript", "@types/react"],
34
+ "recommendedPlugins": ["eslint-react", "prettier-ts", "jest-react"]
35
+ },
36
+ "node-express": {
37
+ "markers": ["express", "node"],
38
+ "recommendedPlugins": ["nodemon", "pm2", "swagger-gen"]
39
+ },
40
+ "python-ml": {
41
+ "markers": ["numpy", "pandas", "scikit-learn", "tensorflow", "pytorch"],
42
+ "recommendedPlugins": ["jupyter", "mlflow", "tensorboard"]
43
+ },
44
+ "python-web": {
45
+ "markers": ["django", "flask", "fastapi"],
46
+ "recommendedPlugins": ["pytest", "black", "mypy"]
47
+ },
48
+ "rust-cli": {
49
+ "markers": ["clap", "tokio"],
50
+ "recommendedPlugins": ["cargo-watch", "cargo-audit"]
51
+ },
52
+ "go-web": {
53
+ "markers": ["gin", "echo", "fiber"],
54
+ "recommendedPlugins": ["air", "golangci-lint"]
55
+ }
56
+ }
57
+ }' > "$PROJECT_SIGNATURES_FILE"
58
+ fi
59
+ }
60
+
61
+ # package.json 분석
62
+ analyze_package_json() {
63
+ local path="${1:-.}"
64
+ local pkg_file="$path/package.json"
65
+
66
+ if [ ! -f "$pkg_file" ]; then
67
+ echo "[]"
68
+ return
69
+ fi
70
+
71
+ if ! command -v jq &> /dev/null; then
72
+ echo "[]"
73
+ return
74
+ fi
75
+
76
+ # 의존성 추출
77
+ jq -r '
78
+ ((.dependencies // {}) + (.devDependencies // {})) |
79
+ keys
80
+ ' "$pkg_file"
81
+ }
82
+
83
+ # requirements.txt 분석
84
+ analyze_requirements() {
85
+ local path="${1:-.}"
86
+ local req_file="$path/requirements.txt"
87
+
88
+ if [ ! -f "$req_file" ]; then
89
+ echo "[]"
90
+ return
91
+ fi
92
+
93
+ # 패키지 이름만 추출 (버전 제외)
94
+ grep -v '^#' "$req_file" 2>/dev/null | \
95
+ grep -v '^$' | \
96
+ sed 's/[>=<].*$//' | \
97
+ sed 's/\[.*\]$//' | \
98
+ jq -R -s -c 'split("\n") | map(select(. != ""))'
99
+ }
100
+
101
+ # Cargo.toml 분석
102
+ analyze_cargo() {
103
+ local path="${1:-.}"
104
+ local cargo_file="$path/Cargo.toml"
105
+
106
+ if [ ! -f "$cargo_file" ]; then
107
+ echo "[]"
108
+ return
109
+ fi
110
+
111
+ # [dependencies] 섹션에서 패키지 추출
112
+ awk '/^\[dependencies\]/,/^\[/' "$cargo_file" | \
113
+ grep -v '^\[' | \
114
+ grep -v '^$' | \
115
+ sed 's/ =.*//' | \
116
+ jq -R -s -c 'split("\n") | map(select(. != ""))'
117
+ }
118
+
119
+ # go.mod 분석
120
+ analyze_gomod() {
121
+ local path="${1:-.}"
122
+ local go_file="$path/go.mod"
123
+
124
+ if [ ! -f "$go_file" ]; then
125
+ echo "[]"
126
+ return
127
+ fi
128
+
129
+ # require 블록에서 패키지 추출
130
+ awk '/^require/,/^\)/' "$go_file" | \
131
+ grep -v '^require' | \
132
+ grep -v '^\)' | \
133
+ awk '{print $1}' | \
134
+ grep -v '^$' | \
135
+ jq -R -s -c 'split("\n") | map(select(. != ""))'
136
+ }
137
+
138
+ # 프로젝트 분석 (통합)
139
+ analyze_project() {
140
+ local path="${1:-.}"
141
+
142
+ init_ai_cache
143
+
144
+ local project_type=""
145
+ local dependencies="[]"
146
+
147
+ # 프로젝트 타입 감지 및 의존성 분석
148
+ if [ -f "$path/package.json" ]; then
149
+ project_type="node"
150
+ dependencies=$(analyze_package_json "$path")
151
+ elif [ -f "$path/requirements.txt" ] || [ -f "$path/pyproject.toml" ]; then
152
+ project_type="python"
153
+ dependencies=$(analyze_requirements "$path")
154
+ elif [ -f "$path/Cargo.toml" ]; then
155
+ project_type="rust"
156
+ dependencies=$(analyze_cargo "$path")
157
+ elif [ -f "$path/go.mod" ]; then
158
+ project_type="go"
159
+ dependencies=$(analyze_gomod "$path")
160
+ elif [ -f "$path/pom.xml" ]; then
161
+ project_type="java"
162
+ elif [ -f "$path/composer.json" ]; then
163
+ project_type="php"
164
+ fi
165
+
166
+ if ! command -v jq &> /dev/null; then
167
+ echo "Project type: $project_type"
168
+ return
169
+ fi
170
+
171
+ local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
172
+ local path_hash=$(echo "$path" | md5 2>/dev/null || echo "$path" | md5sum | cut -d' ' -f1)
173
+
174
+ # 캐시에 저장
175
+ jq --arg path "$path_hash" \
176
+ --arg type "$project_type" \
177
+ --argjson deps "$dependencies" \
178
+ --arg ts "$timestamp" \
179
+ '
180
+ .lastUpdated = $ts |
181
+ .projectAnalysis[$path] = {
182
+ "type": $type,
183
+ "dependencies": $deps,
184
+ "analyzedAt": $ts
185
+ }
186
+ ' "$AI_CACHE_FILE" > "$AI_CACHE_FILE.tmp" && mv "$AI_CACHE_FILE.tmp" "$AI_CACHE_FILE"
187
+
188
+ echo "{\"type\": \"$project_type\", \"dependencies\": $dependencies}"
189
+ }
190
+
191
+ # 의존성 그래프 구축
192
+ build_dependency_graph() {
193
+ local path="${1:-.}"
194
+
195
+ init_ai_cache
196
+
197
+ local analysis=$(analyze_project "$path")
198
+
199
+ if ! command -v jq &> /dev/null; then
200
+ echo "jq required"
201
+ return
202
+ fi
203
+
204
+ local deps=$(echo "$analysis" | jq -r '.dependencies')
205
+ local project_type=$(echo "$analysis" | jq -r '.type')
206
+
207
+ # 간단한 의존성 그래프 (직접 의존성만)
208
+ local graph=$(echo "$deps" | jq '{
209
+ "nodes": . | map({id: ., type: "dependency"}),
210
+ "edges": []
211
+ }')
212
+
213
+ local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
214
+ local path_hash=$(echo "$path" | md5 2>/dev/null || echo "$path" | md5sum | cut -d' ' -f1)
215
+
216
+ jq --arg path "$path_hash" \
217
+ --argjson graph "$graph" \
218
+ --arg ts "$timestamp" \
219
+ '
220
+ .lastUpdated = $ts |
221
+ .dependencyGraph[$path] = {
222
+ "graph": $graph,
223
+ "builtAt": $ts
224
+ }
225
+ ' "$AI_CACHE_FILE" > "$AI_CACHE_FILE.tmp" && mv "$AI_CACHE_FILE.tmp" "$AI_CACHE_FILE"
226
+
227
+ echo "$graph"
228
+ }
229
+
230
+ # 프로젝트 시그니처 매칭
231
+ match_signature() {
232
+ local path="${1:-.}"
233
+
234
+ init_signatures
235
+
236
+ local analysis=$(analyze_project "$path")
237
+
238
+ if ! command -v jq &> /dev/null; then
239
+ echo "[]"
240
+ return
241
+ fi
242
+
243
+ local deps=$(echo "$analysis" | jq -r '.dependencies | join(" ")')
244
+
245
+ # 시그니처별 매칭 점수 계산
246
+ jq -r --arg deps "$deps" '
247
+ .signatures | to_entries | map({
248
+ name: .key,
249
+ markers: .value.markers,
250
+ plugins: .value.recommendedPlugins,
251
+ matched: ([.value.markers[] | select($deps | contains(.))] | length),
252
+ total: (.value.markers | length)
253
+ }) |
254
+ map(select(.matched > 0)) |
255
+ sort_by(-.matched) |
256
+ .[0:3]
257
+ ' "$PROJECT_SIGNATURES_FILE"
258
+ }
259
+
260
+ # AI 기반 추천 생성
261
+ generate_recommendations() {
262
+ local path="${1:-.}"
263
+
264
+ init_ai_cache
265
+ init_signatures
266
+
267
+ if ! command -v jq &> /dev/null; then
268
+ echo "jq required"
269
+ return
270
+ fi
271
+
272
+ local analysis=$(analyze_project "$path")
273
+ local project_type=$(echo "$analysis" | jq -r '.type')
274
+ local deps=$(echo "$analysis" | jq -r '.dependencies')
275
+
276
+ # 시그니처 매칭
277
+ local matches=$(match_signature "$path")
278
+
279
+ # 추천 플러그인 추출
280
+ local recommendations=$(echo "$matches" | jq '
281
+ [.[].plugins] | flatten | unique
282
+ ')
283
+
284
+ local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
285
+ local path_hash=$(echo "$path" | md5 2>/dev/null || echo "$path" | md5sum | cut -d' ' -f1)
286
+
287
+ # 캐시에 저장
288
+ jq --arg path "$path_hash" \
289
+ --argjson recs "$recommendations" \
290
+ --argjson matches "$matches" \
291
+ --arg ts "$timestamp" \
292
+ '
293
+ .lastUpdated = $ts |
294
+ .recommendations[$path] = {
295
+ "plugins": $recs,
296
+ "matches": $matches,
297
+ "generatedAt": $ts
298
+ }
299
+ ' "$AI_CACHE_FILE" > "$AI_CACHE_FILE.tmp" && mv "$AI_CACHE_FILE.tmp" "$AI_CACHE_FILE"
300
+
301
+ echo "$recommendations"
302
+ }
303
+
304
+ # 유사 프로젝트 찾기 (간단한 구현)
305
+ find_similar_projects() {
306
+ local path="${1:-.}"
307
+
308
+ init_ai_cache
309
+
310
+ if ! command -v jq &> /dev/null; then
311
+ echo "[]"
312
+ return
313
+ fi
314
+
315
+ local analysis=$(analyze_project "$path")
316
+ local deps=$(echo "$analysis" | jq -r '.dependencies')
317
+ local project_type=$(echo "$analysis" | jq -r '.type')
318
+
319
+ # 캐시된 다른 프로젝트와 비교
320
+ local path_hash=$(echo "$path" | md5 2>/dev/null || echo "$path" | md5sum | cut -d' ' -f1)
321
+
322
+ jq --arg current "$path_hash" --argjson currentDeps "$deps" '
323
+ .projectAnalysis | to_entries |
324
+ map(select(.key != $current)) |
325
+ map({
326
+ path: .key,
327
+ type: .value.type,
328
+ commonDeps: ([.value.dependencies[] | select(. as $d | $currentDeps | index($d))] | length),
329
+ totalDeps: (.value.dependencies | length)
330
+ }) |
331
+ map(select(.commonDeps > 0)) |
332
+ sort_by(-.commonDeps) |
333
+ .[0:5]
334
+ ' "$AI_CACHE_FILE"
335
+ }
336
+
337
+ # 코드 패턴 분석 (기본적인 파일 구조 분석)
338
+ analyze_code_patterns() {
339
+ local path="${1:-.}"
340
+
341
+ local patterns="[]"
342
+
343
+ # 디렉토리 구조 분석
344
+ if [ -d "$path/src" ]; then
345
+ patterns=$(echo "$patterns" | jq '. + ["src-structure"]')
346
+ fi
347
+
348
+ if [ -d "$path/test" ] || [ -d "$path/tests" ] || [ -d "$path/__tests__" ]; then
349
+ patterns=$(echo "$patterns" | jq '. + ["has-tests"]')
350
+ fi
351
+
352
+ if [ -d "$path/.github" ]; then
353
+ patterns=$(echo "$patterns" | jq '. + ["github-workflows"]')
354
+ fi
355
+
356
+ if [ -f "$path/.eslintrc.json" ] || [ -f "$path/.eslintrc.js" ] || [ -f "$path/eslint.config.js" ]; then
357
+ patterns=$(echo "$patterns" | jq '. + ["eslint-config"]')
358
+ fi
359
+
360
+ if [ -f "$path/.prettierrc" ] || [ -f "$path/prettier.config.js" ]; then
361
+ patterns=$(echo "$patterns" | jq '. + ["prettier-config"]')
362
+ fi
363
+
364
+ if [ -f "$path/docker-compose.yml" ] || [ -f "$path/Dockerfile" ]; then
365
+ patterns=$(echo "$patterns" | jq '. + ["docker-enabled"]')
366
+ fi
367
+
368
+ echo "$patterns"
369
+ }
370
+
371
+ # 추천 점수 계산
372
+ calculate_recommendation_score() {
373
+ local plugin_name="$1"
374
+ local path="${2:-.}"
375
+
376
+ local base_score=50
377
+
378
+ # 시그니처 매칭 보너스
379
+ local matches=$(match_signature "$path")
380
+ local in_match=$(echo "$matches" | jq -r --arg plugin "$plugin_name" '
381
+ [.[].plugins[] | select(. == $plugin)] | length
382
+ ')
383
+
384
+ if [ "$in_match" != "0" ]; then
385
+ base_score=$((base_score + 30))
386
+ fi
387
+
388
+ # 코드 패턴 보너스 (예: 테스트가 있으면 테스트 플러그인 점수 상승)
389
+ local patterns=$(analyze_code_patterns "$path")
390
+
391
+ echo "$base_score"
392
+ }
393
+
394
+ # 캐시 정리
395
+ cleanup_cache() {
396
+ local days="${1:-7}"
397
+
398
+ init_ai_cache
399
+
400
+ if ! command -v jq &> /dev/null; then
401
+ echo "jq required"
402
+ return
403
+ fi
404
+
405
+ local cutoff=$(date -u -v-${days}d +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date -u -d "$days days ago" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null)
406
+ local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
407
+
408
+ jq --arg cutoff "$cutoff" --arg ts "$timestamp" '
409
+ .lastUpdated = $ts |
410
+ .projectAnalysis = (.projectAnalysis | to_entries | map(select(.value.analyzedAt >= $cutoff)) | from_entries) |
411
+ .recommendations = (.recommendations | to_entries | map(select(.value.generatedAt >= $cutoff)) | from_entries)
412
+ ' "$AI_CACHE_FILE" > "$AI_CACHE_FILE.tmp" && mv "$AI_CACHE_FILE.tmp" "$AI_CACHE_FILE"
413
+
414
+ echo "Cache cleaned (removed entries older than $days days)"
415
+ }
416
+
417
+ # AI 추천 요약 출력
418
+ get_ai_summary() {
419
+ local path="${1:-.}"
420
+
421
+ echo "=== AI Recommendation Summary ==="
422
+ echo ""
423
+
424
+ local analysis=$(analyze_project "$path")
425
+ echo "Project Type: $(echo "$analysis" | jq -r '.type // "unknown"')"
426
+ echo "Dependencies: $(echo "$analysis" | jq -r '.dependencies | length') packages"
427
+ echo ""
428
+
429
+ echo "Signature Matches:"
430
+ match_signature "$path" | jq -r '.[] | " \(.name): \(.matched)/\(.total) markers"'
431
+ echo ""
432
+
433
+ echo "Recommended Plugins:"
434
+ generate_recommendations "$path" | jq -r '.[] | " - \(.)"'
435
+ echo ""
436
+
437
+ echo "Code Patterns:"
438
+ analyze_code_patterns "$path" | jq -r '.[] | " - \(.)"'
439
+ }
440
+
441
+ # 캐시 리셋
442
+ reset_cache() {
443
+ if [ -f "$AI_CACHE_FILE" ]; then
444
+ rm "$AI_CACHE_FILE"
445
+ init_ai_cache
446
+ echo "AI cache reset successfully"
447
+ else
448
+ echo "No cache to reset"
449
+ fi
450
+ }
451
+
452
+ # CLI 인터페이스
453
+ case "$1" in
454
+ init)
455
+ init_ai_cache
456
+ init_signatures
457
+ echo "AI recommender initialized"
458
+ ;;
459
+ analyze)
460
+ analyze_project "$2"
461
+ ;;
462
+ graph)
463
+ build_dependency_graph "$2"
464
+ ;;
465
+ match)
466
+ match_signature "$2"
467
+ ;;
468
+ recommend)
469
+ generate_recommendations "$2"
470
+ ;;
471
+ similar)
472
+ find_similar_projects "$2"
473
+ ;;
474
+ patterns)
475
+ analyze_code_patterns "$2"
476
+ ;;
477
+ score)
478
+ calculate_recommendation_score "$2" "$3"
479
+ ;;
480
+ cleanup)
481
+ cleanup_cache "$2"
482
+ ;;
483
+ summary)
484
+ get_ai_summary "$2"
485
+ ;;
486
+ reset)
487
+ reset_cache
488
+ ;;
489
+ *)
490
+ echo "Usage: $0 {init|analyze|graph|match|recommend|similar|patterns|score|cleanup|summary|reset}"
491
+ echo ""
492
+ echo "Commands:"
493
+ echo " init - Initialize AI recommender"
494
+ echo " analyze [path] - Analyze project dependencies"
495
+ echo " graph [path] - Build dependency graph"
496
+ echo " match [path] - Match project signature"
497
+ echo " recommend [path] - Generate AI recommendations"
498
+ echo " similar [path] - Find similar projects"
499
+ echo " patterns [path] - Analyze code patterns"
500
+ echo " score <plugin> [path] - Calculate recommendation score"
501
+ echo " cleanup [days] - Clean old cache entries"
502
+ echo " summary [path] - Show AI recommendation summary"
503
+ echo " reset - Reset AI cache"
504
+ ;;
505
+ esac
@@ -0,0 +1,194 @@
1
+ #!/bin/bash
2
+ # Plugin Scout - 캐싱 유틸리티
3
+ # 자주 사용하는 데이터를 메모리/파일에 캐싱
4
+
5
+ PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$0")/..}"
6
+ DATA_DIR="$PLUGIN_ROOT/data"
7
+ CACHE_DIR="$DATA_DIR/.cache"
8
+ CACHE_TTL="${SCOUT_CACHE_TTL:-300}" # 기본 5분
9
+
10
+ # 캐시 디렉토리 생성
11
+ mkdir -p "$CACHE_DIR"
12
+
13
+ # 캐시 키 해시 생성
14
+ get_cache_key() {
15
+ local input="$1"
16
+ echo "$input" | md5 2>/dev/null || echo "$input" | md5sum 2>/dev/null | cut -d' ' -f1
17
+ }
18
+
19
+ # 캐시 파일 경로
20
+ get_cache_path() {
21
+ local key="$1"
22
+ local hash=$(get_cache_key "$key")
23
+ echo "$CACHE_DIR/$hash"
24
+ }
25
+
26
+ # 캐시에서 읽기
27
+ cache_get() {
28
+ local key="$1"
29
+ local ttl="${2:-$CACHE_TTL}"
30
+ local cache_file=$(get_cache_path "$key")
31
+
32
+ # 캐시 파일 존재 확인
33
+ if [ ! -f "$cache_file" ]; then
34
+ return 1
35
+ fi
36
+
37
+ # TTL 확인
38
+ local file_age=$(( $(date +%s) - $(stat -f%m "$cache_file" 2>/dev/null || stat -c%Y "$cache_file" 2>/dev/null) ))
39
+ if [ "$file_age" -gt "$ttl" ]; then
40
+ rm -f "$cache_file"
41
+ return 1
42
+ fi
43
+
44
+ cat "$cache_file"
45
+ return 0
46
+ }
47
+
48
+ # 캐시에 저장
49
+ cache_set() {
50
+ local key="$1"
51
+ local value="$2"
52
+ local cache_file=$(get_cache_path "$key")
53
+
54
+ echo "$value" > "$cache_file"
55
+ }
56
+
57
+ # 캐시 삭제
58
+ cache_delete() {
59
+ local key="$1"
60
+ local cache_file=$(get_cache_path "$key")
61
+
62
+ rm -f "$cache_file"
63
+ }
64
+
65
+ # 모든 캐시 삭제
66
+ cache_clear() {
67
+ rm -f "$CACHE_DIR"/*
68
+ echo "Cache cleared"
69
+ }
70
+
71
+ # 캐시 통계
72
+ cache_stats() {
73
+ echo "=== Cache Statistics ==="
74
+ echo ""
75
+ echo "Cache directory: $CACHE_DIR"
76
+ echo "Cache TTL: ${CACHE_TTL}s"
77
+ echo ""
78
+
79
+ if [ -d "$CACHE_DIR" ]; then
80
+ local count=$(ls -1 "$CACHE_DIR" 2>/dev/null | wc -l | tr -d ' ')
81
+ local size=$(du -sh "$CACHE_DIR" 2>/dev/null | cut -f1)
82
+ echo "Cached items: $count"
83
+ echo "Total size: ${size:-0}"
84
+ else
85
+ echo "Cache directory not found"
86
+ fi
87
+ }
88
+
89
+ # 캐시된 함수 실행 (결과 캐싱)
90
+ cached_exec() {
91
+ local key="$1"
92
+ shift
93
+ local command="$@"
94
+ local ttl="${SCOUT_CACHE_TTL:-300}"
95
+
96
+ # 캐시 확인
97
+ local cached=$(cache_get "$key" "$ttl")
98
+ if [ -n "$cached" ]; then
99
+ echo "$cached"
100
+ return 0
101
+ fi
102
+
103
+ # 명령 실행 및 캐싱
104
+ local result
105
+ result=$(eval "$command")
106
+ local exit_code=$?
107
+
108
+ if [ $exit_code -eq 0 ]; then
109
+ cache_set "$key" "$result"
110
+ fi
111
+
112
+ echo "$result"
113
+ return $exit_code
114
+ }
115
+
116
+ # 프로젝트 분석 캐싱
117
+ cached_project_analysis() {
118
+ local key="project_analysis_$(pwd | get_cache_key)"
119
+
120
+ cached_exec "$key" "bash '$PLUGIN_ROOT/lib/project-analyzer.sh' full"
121
+ }
122
+
123
+ # 팀 추천 캐싱
124
+ cached_team_recommendations() {
125
+ local key="team_recommendations"
126
+
127
+ cached_exec "$key" "bash '$PLUGIN_ROOT/lib/team-manager.sh' recommendations"
128
+ }
129
+
130
+ # 만료된 캐시 정리
131
+ cache_cleanup() {
132
+ local ttl="${1:-$CACHE_TTL}"
133
+ local now=$(date +%s)
134
+ local count=0
135
+
136
+ for file in "$CACHE_DIR"/*; do
137
+ if [ -f "$file" ]; then
138
+ local file_time=$(stat -f%m "$file" 2>/dev/null || stat -c%Y "$file" 2>/dev/null)
139
+ local age=$(( now - file_time ))
140
+ if [ "$age" -gt "$ttl" ]; then
141
+ rm -f "$file"
142
+ count=$((count + 1))
143
+ fi
144
+ fi
145
+ done
146
+
147
+ echo "Cleaned $count expired cache entries"
148
+ }
149
+
150
+ # CLI 인터페이스
151
+ case "$1" in
152
+ get)
153
+ cache_get "$2" "$3"
154
+ ;;
155
+ set)
156
+ cache_set "$2" "$3"
157
+ ;;
158
+ delete)
159
+ cache_delete "$2"
160
+ ;;
161
+ clear)
162
+ cache_clear
163
+ ;;
164
+ stats)
165
+ cache_stats
166
+ ;;
167
+ cleanup)
168
+ cache_cleanup "$2"
169
+ ;;
170
+ project)
171
+ cached_project_analysis
172
+ ;;
173
+ team)
174
+ cached_team_recommendations
175
+ ;;
176
+ exec)
177
+ shift
178
+ cached_exec "$@"
179
+ ;;
180
+ *)
181
+ echo "Usage: $0 {get|set|delete|clear|stats|cleanup|project|team|exec}"
182
+ echo ""
183
+ echo "Commands:"
184
+ echo " get <key> [ttl] - Get cached value"
185
+ echo " set <key> <value> - Set cache value"
186
+ echo " delete <key> - Delete cache entry"
187
+ echo " clear - Clear all cache"
188
+ echo " stats - Show cache statistics"
189
+ echo " cleanup [ttl] - Clean expired entries"
190
+ echo " project - Cached project analysis"
191
+ echo " team - Cached team recommendations"
192
+ echo " exec <key> <cmd> - Execute with caching"
193
+ ;;
194
+ esac