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,102 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Plugin Scout - 인사이트 생성 스크립트
|
|
3
|
+
# 세션 시작 시 또는 콘솔 실행 시 호출
|
|
4
|
+
|
|
5
|
+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$0")/..}"
|
|
6
|
+
DATA_DIR="$PLUGIN_ROOT/data"
|
|
7
|
+
USAGE_FILE="$DATA_DIR/usage.json"
|
|
8
|
+
ANALYTICS_FILE="$DATA_DIR/analytics.json"
|
|
9
|
+
|
|
10
|
+
# jq가 없으면 종료
|
|
11
|
+
if ! command -v jq &> /dev/null; then
|
|
12
|
+
exit 0
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# 파일 존재 확인
|
|
16
|
+
if [ ! -f "$USAGE_FILE" ] || [ ! -f "$ANALYTICS_FILE" ]; then
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
NOW=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
21
|
+
TODAY=$(date +"%Y-%m-%d")
|
|
22
|
+
THIRTY_DAYS_AGO=$(date -v-30d +"%Y-%m-%d" 2>/dev/null || date -d "30 days ago" +"%Y-%m-%d" 2>/dev/null)
|
|
23
|
+
|
|
24
|
+
# 인사이트 배열 초기화
|
|
25
|
+
INSIGHTS="[]"
|
|
26
|
+
|
|
27
|
+
# 1. 미사용 플러그인 체크 (30일 이상)
|
|
28
|
+
UNUSED_PLUGINS=$(jq -r --arg threshold "$THIRTY_DAYS_AGO" '
|
|
29
|
+
.plugins | to_entries |
|
|
30
|
+
map(select(.value.lastUsed < $threshold or .value.lastUsed == null)) |
|
|
31
|
+
.[].key
|
|
32
|
+
' "$USAGE_FILE" 2>/dev/null)
|
|
33
|
+
|
|
34
|
+
for plugin in $UNUSED_PLUGINS; do
|
|
35
|
+
if [ -n "$plugin" ]; then
|
|
36
|
+
DAYS_AGO=$(jq -r --arg p "$plugin" '.plugins[$p].lastUsed // "never"' "$USAGE_FILE")
|
|
37
|
+
INSIGHT=$(jq -n --arg plugin "$plugin" --arg days "$DAYS_AGO" --arg now "$NOW" '{
|
|
38
|
+
"type": "cleanup",
|
|
39
|
+
"severity": "warning",
|
|
40
|
+
"message": "\($plugin)이 오래 미사용 상태입니다 (마지막: \($days))",
|
|
41
|
+
"action": "/scout cleanup",
|
|
42
|
+
"generated": $now
|
|
43
|
+
}')
|
|
44
|
+
INSIGHTS=$(echo "$INSIGHTS" | jq --argjson insight "$INSIGHT" '. + [$insight]')
|
|
45
|
+
fi
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
# 2. 가장 많이 사용된 플러그인 분석
|
|
49
|
+
TOP_PLUGIN=$(jq -r '.plugins | to_entries | sort_by(.value.usageCount) | reverse | .[0].key // ""' "$USAGE_FILE" 2>/dev/null)
|
|
50
|
+
if [ -n "$TOP_PLUGIN" ]; then
|
|
51
|
+
TOP_COUNT=$(jq -r --arg p "$TOP_PLUGIN" '.plugins[$p].usageCount // 0' "$USAGE_FILE")
|
|
52
|
+
INSIGHT=$(jq -n --arg plugin "$TOP_PLUGIN" --arg count "$TOP_COUNT" --arg now "$NOW" '{
|
|
53
|
+
"type": "trend-up",
|
|
54
|
+
"severity": "info",
|
|
55
|
+
"message": "\($plugin)이 가장 활발히 사용됩니다 (\($count)회)",
|
|
56
|
+
"action": "",
|
|
57
|
+
"generated": $now
|
|
58
|
+
}')
|
|
59
|
+
INSIGHTS=$(echo "$INSIGHTS" | jq --argjson insight "$INSIGHT" '. + [$insight]')
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# 3. 사용량 없는 플러그인 체크
|
|
63
|
+
ZERO_USAGE=$(jq -r '.plugins | to_entries | map(select(.value.usageCount == 0)) | .[].key' "$USAGE_FILE" 2>/dev/null)
|
|
64
|
+
for plugin in $ZERO_USAGE; do
|
|
65
|
+
if [ -n "$plugin" ]; then
|
|
66
|
+
INSIGHT=$(jq -n --arg plugin "$plugin" --arg now "$NOW" '{
|
|
67
|
+
"type": "cleanup",
|
|
68
|
+
"severity": "warning",
|
|
69
|
+
"message": "\($plugin)이 설치 후 한 번도 사용되지 않았습니다",
|
|
70
|
+
"action": "/scout cleanup",
|
|
71
|
+
"generated": $now
|
|
72
|
+
}')
|
|
73
|
+
INSIGHTS=$(echo "$INSIGHTS" | jq --argjson insight "$INSIGHT" '. + [$insight]')
|
|
74
|
+
fi
|
|
75
|
+
done
|
|
76
|
+
|
|
77
|
+
# 4. 전체 통계 업데이트
|
|
78
|
+
TOTAL_PLUGINS=$(jq '.plugins | length' "$USAGE_FILE" 2>/dev/null || echo 0)
|
|
79
|
+
TOTAL_USAGE=$(jq '[.plugins[].usageCount] | add // 0' "$USAGE_FILE" 2>/dev/null || echo 0)
|
|
80
|
+
ACTIVE_PLUGINS=$(jq --arg threshold "$THIRTY_DAYS_AGO" '
|
|
81
|
+
[.plugins | to_entries[] | select(.value.lastUsed >= $threshold)] | length
|
|
82
|
+
' "$USAGE_FILE" 2>/dev/null || echo 0)
|
|
83
|
+
DORMANT_PLUGINS=$((TOTAL_PLUGINS - ACTIVE_PLUGINS))
|
|
84
|
+
|
|
85
|
+
# analytics.json 업데이트
|
|
86
|
+
TEMP_FILE=$(mktemp)
|
|
87
|
+
jq --arg now "$NOW" \
|
|
88
|
+
--argjson total "$TOTAL_PLUGINS" \
|
|
89
|
+
--argjson active "$ACTIVE_PLUGINS" \
|
|
90
|
+
--argjson dormant "$DORMANT_PLUGINS" \
|
|
91
|
+
--argjson usage "$TOTAL_USAGE" \
|
|
92
|
+
--argjson insights "$INSIGHTS" '
|
|
93
|
+
.lastUpdated = $now |
|
|
94
|
+
.summary.totalPlugins = $total |
|
|
95
|
+
.summary.activePlugins = $active |
|
|
96
|
+
.summary.dormantPlugins = $dormant |
|
|
97
|
+
.summary.totalUsageCount = $usage |
|
|
98
|
+
.insights = $insights
|
|
99
|
+
' "$ANALYTICS_FILE" > "$TEMP_FILE" && mv "$TEMP_FILE" "$ANALYTICS_FILE"
|
|
100
|
+
|
|
101
|
+
# 결과 출력 (디버그용, 필요시 주석 처리)
|
|
102
|
+
# echo "Insights generated: $(echo "$INSIGHTS" | jq length)"
|
|
@@ -1,3 +1,38 @@
|
|
|
1
1
|
{
|
|
2
|
-
"hooks": {
|
|
2
|
+
"hooks": {
|
|
3
|
+
"Stop": [
|
|
4
|
+
{
|
|
5
|
+
"hooks": [
|
|
6
|
+
{
|
|
7
|
+
"type": "command",
|
|
8
|
+
"command": "cat ${CLAUDE_PLUGIN_ROOT}/hooks/on-stop.md",
|
|
9
|
+
"timeout": 5
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"SessionStart": [
|
|
15
|
+
{
|
|
16
|
+
"matcher": "startup",
|
|
17
|
+
"hooks": [
|
|
18
|
+
{
|
|
19
|
+
"type": "command",
|
|
20
|
+
"command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/on-session-start.sh",
|
|
21
|
+
"timeout": 10
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"SessionEnd": [
|
|
27
|
+
{
|
|
28
|
+
"hooks": [
|
|
29
|
+
{
|
|
30
|
+
"type": "command",
|
|
31
|
+
"command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/on-session-end.sh",
|
|
32
|
+
"timeout": 10
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
3
38
|
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Plugin Scout - 세션 종료 시 사용 이력 기록
|
|
3
|
+
|
|
4
|
+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$0")/..}"
|
|
5
|
+
DATA_DIR="$PLUGIN_ROOT/data"
|
|
6
|
+
USAGE_FILE="$DATA_DIR/usage.json"
|
|
7
|
+
ANALYTICS_FILE="$DATA_DIR/analytics.json"
|
|
8
|
+
SESSION_FILE="$DATA_DIR/.session"
|
|
9
|
+
|
|
10
|
+
# usage.json이 없으면 초기화 (users 섹션 포함)
|
|
11
|
+
if [ ! -f "$USAGE_FILE" ]; then
|
|
12
|
+
cat > "$USAGE_FILE" << 'EOF'
|
|
13
|
+
{
|
|
14
|
+
"lastUpdated": "",
|
|
15
|
+
"config": {
|
|
16
|
+
"unusedThresholdDays": 30,
|
|
17
|
+
"lowUsageThreshold": 3
|
|
18
|
+
},
|
|
19
|
+
"plugins": {},
|
|
20
|
+
"users": {},
|
|
21
|
+
"sessions": {
|
|
22
|
+
"total": 0,
|
|
23
|
+
"thisWeek": 0
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
EOF
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# analytics.json이 없으면 초기화
|
|
30
|
+
if [ ! -f "$ANALYTICS_FILE" ]; then
|
|
31
|
+
cat > "$ANALYTICS_FILE" << 'EOF'
|
|
32
|
+
{
|
|
33
|
+
"version": "1.0.0",
|
|
34
|
+
"lastUpdated": "",
|
|
35
|
+
"summary": {
|
|
36
|
+
"totalPlugins": 0,
|
|
37
|
+
"activePlugins": 0,
|
|
38
|
+
"totalUsageCount": 0
|
|
39
|
+
},
|
|
40
|
+
"trends": {
|
|
41
|
+
"weekly": [],
|
|
42
|
+
"monthly": []
|
|
43
|
+
},
|
|
44
|
+
"pluginStats": {},
|
|
45
|
+
"userStats": {},
|
|
46
|
+
"insights": []
|
|
47
|
+
}
|
|
48
|
+
EOF
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# 세션 파일에서 데이터 읽기
|
|
52
|
+
if [ -f "$SESSION_FILE" ] && command -v jq &> /dev/null; then
|
|
53
|
+
TODAY=$(date +"%Y-%m-%d")
|
|
54
|
+
NOW=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
55
|
+
|
|
56
|
+
# 세션에서 사용자 및 스킬 추출
|
|
57
|
+
SESSION_USER=$(jq -r '.user // "unknown"' "$SESSION_FILE" 2>/dev/null)
|
|
58
|
+
SKILLS=$(jq -r '.skills_used[]' "$SESSION_FILE" 2>/dev/null)
|
|
59
|
+
|
|
60
|
+
# 세션 카운트 증가
|
|
61
|
+
TEMP_FILE=$(mktemp)
|
|
62
|
+
jq --arg now "$NOW" '
|
|
63
|
+
.lastUpdated = $now |
|
|
64
|
+
.sessions.total += 1
|
|
65
|
+
' "$USAGE_FILE" > "$TEMP_FILE" && mv "$TEMP_FILE" "$USAGE_FILE"
|
|
66
|
+
|
|
67
|
+
# 사용자별 세션 카운트
|
|
68
|
+
if [ -n "$SESSION_USER" ] && [ "$SESSION_USER" != "unknown" ]; then
|
|
69
|
+
TEMP_FILE=$(mktemp)
|
|
70
|
+
jq --arg user "$SESSION_USER" --arg today "$TODAY" --arg now "$NOW" '
|
|
71
|
+
.lastUpdated = $now |
|
|
72
|
+
if .users[$user] then
|
|
73
|
+
.users[$user].sessions += 1 |
|
|
74
|
+
.users[$user].lastActive = $today
|
|
75
|
+
else
|
|
76
|
+
.users[$user] = {
|
|
77
|
+
"plugins": {},
|
|
78
|
+
"sessions": 1,
|
|
79
|
+
"lastActive": $today,
|
|
80
|
+
"firstSeen": $today
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
' "$USAGE_FILE" > "$TEMP_FILE" && mv "$TEMP_FILE" "$USAGE_FILE"
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# 스킬별 사용량 업데이트
|
|
87
|
+
if [ -n "$SKILLS" ]; then
|
|
88
|
+
for skill in $SKILLS; do
|
|
89
|
+
if [ -n "$skill" ]; then
|
|
90
|
+
# 플러그인 전체 사용량 업데이트
|
|
91
|
+
TEMP_FILE=$(mktemp)
|
|
92
|
+
jq --arg skill "$skill" --arg today "$TODAY" --arg now "$NOW" '
|
|
93
|
+
.lastUpdated = $now |
|
|
94
|
+
if .plugins[$skill] then
|
|
95
|
+
.plugins[$skill].usageCount += 1 |
|
|
96
|
+
.plugins[$skill].lastUsed = $today
|
|
97
|
+
else
|
|
98
|
+
.plugins[$skill] = {
|
|
99
|
+
"installed": $today,
|
|
100
|
+
"usageCount": 1,
|
|
101
|
+
"lastUsed": $today
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
' "$USAGE_FILE" > "$TEMP_FILE" && mv "$TEMP_FILE" "$USAGE_FILE"
|
|
105
|
+
|
|
106
|
+
# 사용자별 플러그인 사용량 업데이트
|
|
107
|
+
if [ -n "$SESSION_USER" ] && [ "$SESSION_USER" != "unknown" ]; then
|
|
108
|
+
TEMP_FILE=$(mktemp)
|
|
109
|
+
jq --arg user "$SESSION_USER" --arg skill "$skill" --arg today "$TODAY" '
|
|
110
|
+
if .users[$user].plugins[$skill] then
|
|
111
|
+
.users[$user].plugins[$skill].usageCount += 1 |
|
|
112
|
+
.users[$user].plugins[$skill].lastUsed = $today
|
|
113
|
+
else
|
|
114
|
+
.users[$user].plugins[$skill] = {
|
|
115
|
+
"usageCount": 1,
|
|
116
|
+
"lastUsed": $today
|
|
117
|
+
}
|
|
118
|
+
end
|
|
119
|
+
' "$USAGE_FILE" > "$TEMP_FILE" && mv "$TEMP_FILE" "$USAGE_FILE"
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
# analytics.json 총 사용량 업데이트
|
|
123
|
+
TEMP_FILE=$(mktemp)
|
|
124
|
+
jq --arg now "$NOW" '
|
|
125
|
+
.lastUpdated = $now |
|
|
126
|
+
.summary.totalUsageCount += 1
|
|
127
|
+
' "$ANALYTICS_FILE" > "$TEMP_FILE" && mv "$TEMP_FILE" "$ANALYTICS_FILE"
|
|
128
|
+
fi
|
|
129
|
+
done
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
# 세션 파일 정리
|
|
133
|
+
rm -f "$SESSION_FILE"
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# 조용히 종료 (출력 없음)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Plugin Scout - 세션 시작 시 초기화
|
|
3
|
+
|
|
4
|
+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$0")/..}"
|
|
5
|
+
DATA_DIR="$PLUGIN_ROOT/data"
|
|
6
|
+
SESSION_FILE="$DATA_DIR/.session"
|
|
7
|
+
|
|
8
|
+
# 데이터 디렉토리 확인
|
|
9
|
+
mkdir -p "$DATA_DIR"
|
|
10
|
+
|
|
11
|
+
# 사용자 정보 캡처
|
|
12
|
+
CURRENT_USER="${USER:-unknown}"
|
|
13
|
+
GIT_USER=$(git config user.name 2>/dev/null || echo "")
|
|
14
|
+
if [ -n "$GIT_USER" ]; then
|
|
15
|
+
CURRENT_USER="$GIT_USER"
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# 세션 정보 초기화 (사용자 정보 포함)
|
|
19
|
+
cat > "$SESSION_FILE" << EOF
|
|
20
|
+
{
|
|
21
|
+
"started": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
|
|
22
|
+
"user": "$CURRENT_USER",
|
|
23
|
+
"recommended": false,
|
|
24
|
+
"skills_used": []
|
|
25
|
+
}
|
|
26
|
+
EOF
|
|
27
|
+
|
|
28
|
+
# 팀에 사용자 등록 (조용히)
|
|
29
|
+
if [ -f "$PLUGIN_ROOT/lib/team-manager.sh" ]; then
|
|
30
|
+
bash "$PLUGIN_ROOT/lib/team-manager.sh" register "$CURRENT_USER" >/dev/null 2>&1
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Trusted auto-install (silent, background)
|
|
34
|
+
if [ -f "$PLUGIN_ROOT/lib/trusted-installer.sh" ]; then
|
|
35
|
+
bash "$PLUGIN_ROOT/lib/trusted-installer.sh" run >/dev/null 2>&1 &
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Skill scout auto-install (silent, background)
|
|
39
|
+
if [ -f "$PLUGIN_ROOT/lib/skill-scout.sh" ]; then
|
|
40
|
+
bash "$PLUGIN_ROOT/lib/skill-scout.sh" auto >/dev/null 2>&1 &
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# 조용히 시작 (출력 없음)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Plugin Scout - 작업 완료 후 추천 체크 (v2.1)
|
|
2
|
+
|
|
3
|
+
작업이 완료되었습니다. 아래 절차에 따라 플러그인 추천 여부를 결정하세요.
|
|
4
|
+
|
|
5
|
+
## 1단계: 추천 제한 확인 (필수)
|
|
6
|
+
|
|
7
|
+
먼저 recommendation-controller를 통해 추천 가능 여부를 확인합니다:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bash ${CLAUDE_PLUGIN_ROOT}/lib/recommendation-controller.sh can-recommend session
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
결과 해석:
|
|
14
|
+
- `true` → 추천 가능, 2단계로 진행
|
|
15
|
+
- `blocked:quiet_mode` → 무음 모드 활성화됨, 추천 중단
|
|
16
|
+
- `blocked:session_limit` → 세션 추천 한도 도달, 추천 중단
|
|
17
|
+
- `blocked:daily_limit` → 일일 추천 한도 도달, 추천 중단
|
|
18
|
+
|
|
19
|
+
**중요**: `blocked:`로 시작하면 즉시 중단하고 아무것도 출력하지 마세요.
|
|
20
|
+
|
|
21
|
+
## 2단계: 스마트 타이밍 확인
|
|
22
|
+
|
|
23
|
+
이벤트 기반 추천이 설정된 경우:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
bash ${CLAUDE_PLUGIN_ROOT}/lib/recommendation-controller.sh timing task-complete
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
결과 해석:
|
|
30
|
+
- `true` → 타이밍 조건 충족, 3단계로 진행
|
|
31
|
+
- `blocked:not_after_commit` → 커밋 후에만 추천 설정됨, 추천 중단
|
|
32
|
+
- `blocked:not_after_pr` → PR 후에만 추천 설정됨, 추천 중단
|
|
33
|
+
|
|
34
|
+
## 3단계: 추천 조건 검증
|
|
35
|
+
|
|
36
|
+
다음 조건이 모두 충족되어야 추천:
|
|
37
|
+
|
|
38
|
+
1. **의미 있는 작업 완료** - 단순 질문/답변이 아닌 코드 작성, 파일 수정 등
|
|
39
|
+
2. **프로젝트 컨텍스트 존재** - package.json, pyproject.toml 등 감지됨
|
|
40
|
+
3. **플러그인 관련 작업 중이 아님** - `/scout`, 플러그인 설치 등 진행 중이 아님
|
|
41
|
+
|
|
42
|
+
## 4단계: 추천 실행
|
|
43
|
+
|
|
44
|
+
조건 충족 시 간단히 제안:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
💡 이 프로젝트에 [plugin-name] 플러그인이 도움될 수 있어요.
|
|
48
|
+
[한 줄 설명]
|
|
49
|
+
관심있으시면 `/scout`로 더 알아보세요.
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 5단계: 추천 기록
|
|
53
|
+
|
|
54
|
+
추천 후 반드시 기록:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
bash ${CLAUDE_PLUGIN_ROOT}/lib/recommendation-controller.sh record 1 post-task
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 설정 변경 안내
|
|
61
|
+
|
|
62
|
+
사용자가 추천 빈도에 불만을 표시하면:
|
|
63
|
+
|
|
64
|
+
- **완전 중단**: `/scout quiet on`
|
|
65
|
+
- **빈도 조절**: `/scout frequency session 2` 또는 `/scout frequency daily 5`
|
|
66
|
+
- **타이밍 변경**: `/scout timing after-commit on`
|
|
67
|
+
|
|
68
|
+
## 현재 상태 확인
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
bash ${CLAUDE_PLUGIN_ROOT}/lib/recommendation-controller.sh status
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 추천 안 하는 경우
|
|
75
|
+
|
|
76
|
+
- recommendation-controller가 `blocked:*` 반환
|
|
77
|
+
- 단순 질문/답변 세션
|
|
78
|
+
- 프로젝트 파일이 없음
|
|
79
|
+
- 사용자가 플러그인 관련 작업 중
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Plugin Scout - 웹 콘솔 열기
|
|
3
|
+
# 로컬 데이터를 HTML에 주입하고 브라우저에서 열기
|
|
4
|
+
|
|
5
|
+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$0")/..}"
|
|
6
|
+
DATA_DIR="$PLUGIN_ROOT/data"
|
|
7
|
+
WEB_DIR="$PLUGIN_ROOT/web"
|
|
8
|
+
API_DIR="$PLUGIN_ROOT/api/mock"
|
|
9
|
+
TEMPLATE="$WEB_DIR/console.html"
|
|
10
|
+
OUTPUT="/tmp/plugin-scout-console.html"
|
|
11
|
+
|
|
12
|
+
# 데이터 파일 확인
|
|
13
|
+
USAGE_FILE="$DATA_DIR/usage.json"
|
|
14
|
+
ANALYTICS_FILE="$DATA_DIR/analytics.json"
|
|
15
|
+
|
|
16
|
+
if [ ! -f "$USAGE_FILE" ]; then
|
|
17
|
+
echo "Error: usage.json not found"
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
if [ ! -f "$ANALYTICS_FILE" ]; then
|
|
22
|
+
echo "Error: analytics.json not found"
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
if [ ! -f "$TEMPLATE" ]; then
|
|
27
|
+
echo "Error: console.html template not found"
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# JSON 데이터 읽기
|
|
32
|
+
if command -v jq &> /dev/null; then
|
|
33
|
+
USAGE_JSON=$(jq -c '.' "$USAGE_FILE")
|
|
34
|
+
ANALYTICS_JSON=$(jq -c '.' "$ANALYTICS_FILE")
|
|
35
|
+
|
|
36
|
+
# Mock API 데이터 읽기
|
|
37
|
+
if [ -f "$API_DIR/marketplace.json" ]; then
|
|
38
|
+
MARKETPLACE_JSON=$(jq -c '.' "$API_DIR/marketplace.json")
|
|
39
|
+
else
|
|
40
|
+
MARKETPLACE_JSON='{"plugins":[],"total":0}'
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
if [ -f "$API_DIR/categories.json" ]; then
|
|
44
|
+
CATEGORIES_JSON=$(jq -c '.' "$API_DIR/categories.json")
|
|
45
|
+
else
|
|
46
|
+
CATEGORIES_JSON='{"categories":[]}'
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
if [ -f "$API_DIR/trending.json" ]; then
|
|
50
|
+
TRENDING_JSON=$(jq -c '.' "$API_DIR/trending.json")
|
|
51
|
+
else
|
|
52
|
+
TRENDING_JSON='{"plugins":[]}'
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
if [ -f "$API_DIR/insights.json" ]; then
|
|
56
|
+
INSIGHTS_JSON=$(jq -c '.' "$API_DIR/insights.json")
|
|
57
|
+
else
|
|
58
|
+
INSIGHTS_JSON='{"insights":[]}'
|
|
59
|
+
fi
|
|
60
|
+
else
|
|
61
|
+
USAGE_JSON=$(cat "$USAGE_FILE" | tr -d '\n')
|
|
62
|
+
ANALYTICS_JSON=$(cat "$ANALYTICS_FILE" | tr -d '\n')
|
|
63
|
+
|
|
64
|
+
# Mock API 데이터 읽기 (jq 없이)
|
|
65
|
+
if [ -f "$API_DIR/marketplace.json" ]; then
|
|
66
|
+
MARKETPLACE_JSON=$(cat "$API_DIR/marketplace.json" | tr -d '\n')
|
|
67
|
+
else
|
|
68
|
+
MARKETPLACE_JSON='{"plugins":[],"total":0}'
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if [ -f "$API_DIR/categories.json" ]; then
|
|
72
|
+
CATEGORIES_JSON=$(cat "$API_DIR/categories.json" | tr -d '\n')
|
|
73
|
+
else
|
|
74
|
+
CATEGORIES_JSON='{"categories":[]}'
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
if [ -f "$API_DIR/trending.json" ]; then
|
|
78
|
+
TRENDING_JSON=$(cat "$API_DIR/trending.json" | tr -d '\n')
|
|
79
|
+
else
|
|
80
|
+
TRENDING_JSON='{"plugins":[]}'
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
if [ -f "$API_DIR/insights.json" ]; then
|
|
84
|
+
INSIGHTS_JSON=$(cat "$API_DIR/insights.json" | tr -d '\n')
|
|
85
|
+
else
|
|
86
|
+
INSIGHTS_JSON='{"insights":[]}'
|
|
87
|
+
fi
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# HTML 생성 (데이터 주입)
|
|
91
|
+
sed -e "s|__USAGE_DATA__|$USAGE_JSON|g" \
|
|
92
|
+
-e "s|__ANALYTICS_DATA__|$ANALYTICS_JSON|g" \
|
|
93
|
+
-e "s|__MARKETPLACE_DATA__|$MARKETPLACE_JSON|g" \
|
|
94
|
+
-e "s|__CATEGORIES_DATA__|$CATEGORIES_JSON|g" \
|
|
95
|
+
-e "s|__TRENDING_DATA__|$TRENDING_JSON|g" \
|
|
96
|
+
-e "s|__INSIGHTS_DATA__|$INSIGHTS_JSON|g" \
|
|
97
|
+
"$TEMPLATE" > "$OUTPUT"
|
|
98
|
+
|
|
99
|
+
# 브라우저에서 열기
|
|
100
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
101
|
+
open "$OUTPUT"
|
|
102
|
+
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
|
103
|
+
xdg-open "$OUTPUT" 2>/dev/null || sensible-browser "$OUTPUT" 2>/dev/null
|
|
104
|
+
elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]]; then
|
|
105
|
+
start "$OUTPUT"
|
|
106
|
+
else
|
|
107
|
+
echo "Console generated: $OUTPUT"
|
|
108
|
+
echo "Please open this file in your browser."
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
echo "Console opened: $OUTPUT"
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Plugin Scout - 웹 대시보드 열기
|
|
3
|
+
# 데이터를 HTML에 주입하고 브라우저에서 열기
|
|
4
|
+
|
|
5
|
+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$0")/..}"
|
|
6
|
+
DATA_DIR="$PLUGIN_ROOT/data"
|
|
7
|
+
WEB_DIR="$PLUGIN_ROOT/web"
|
|
8
|
+
TEMPLATE="$WEB_DIR/dashboard.html"
|
|
9
|
+
OUTPUT="/tmp/plugin-scout-dashboard.html"
|
|
10
|
+
|
|
11
|
+
# 데이터 파일 확인
|
|
12
|
+
USAGE_FILE="$DATA_DIR/usage.json"
|
|
13
|
+
ANALYTICS_FILE="$DATA_DIR/analytics.json"
|
|
14
|
+
|
|
15
|
+
if [ ! -f "$USAGE_FILE" ]; then
|
|
16
|
+
echo "Error: usage.json not found"
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
if [ ! -f "$ANALYTICS_FILE" ]; then
|
|
21
|
+
echo "Error: analytics.json not found"
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
if [ ! -f "$TEMPLATE" ]; then
|
|
26
|
+
echo "Error: dashboard.html template not found"
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# JSON 데이터 읽기 (한 줄로 압축)
|
|
31
|
+
USAGE_DATA=$(cat "$USAGE_FILE" | tr -d '\n' | sed 's/"/\\"/g' | sed "s/'/\\'/g")
|
|
32
|
+
ANALYTICS_DATA=$(cat "$ANALYTICS_FILE" | tr -d '\n' | sed 's/"/\\"/g' | sed "s/'/\\'/g")
|
|
33
|
+
|
|
34
|
+
# 템플릿에 데이터 주입
|
|
35
|
+
# jq가 있으면 사용, 없으면 cat 사용
|
|
36
|
+
if command -v jq &> /dev/null; then
|
|
37
|
+
USAGE_JSON=$(jq -c '.' "$USAGE_FILE")
|
|
38
|
+
ANALYTICS_JSON=$(jq -c '.' "$ANALYTICS_FILE")
|
|
39
|
+
else
|
|
40
|
+
USAGE_JSON=$(cat "$USAGE_FILE" | tr -d '\n')
|
|
41
|
+
ANALYTICS_JSON=$(cat "$ANALYTICS_FILE" | tr -d '\n')
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
# HTML 생성 (데이터 주입)
|
|
45
|
+
sed -e "s|__USAGE_DATA__|$USAGE_JSON|g" \
|
|
46
|
+
-e "s|__ANALYTICS_DATA__|$ANALYTICS_JSON|g" \
|
|
47
|
+
"$TEMPLATE" > "$OUTPUT"
|
|
48
|
+
|
|
49
|
+
# 브라우저에서 열기
|
|
50
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
51
|
+
open "$OUTPUT"
|
|
52
|
+
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
|
53
|
+
xdg-open "$OUTPUT" 2>/dev/null || sensible-browser "$OUTPUT" 2>/dev/null
|
|
54
|
+
elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]]; then
|
|
55
|
+
start "$OUTPUT"
|
|
56
|
+
else
|
|
57
|
+
echo "Dashboard generated: $OUTPUT"
|
|
58
|
+
echo "Please open this file in your browser."
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
echo "Dashboard opened: $OUTPUT"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Plugin Scout - 스킬 사용 추적
|
|
3
|
+
# 사용법: track-usage.sh <skill-name>
|
|
4
|
+
|
|
5
|
+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$0")/..}"
|
|
6
|
+
DATA_DIR="$PLUGIN_ROOT/data"
|
|
7
|
+
SESSION_FILE="$DATA_DIR/.session"
|
|
8
|
+
SKILL_NAME="${1:-unknown}"
|
|
9
|
+
|
|
10
|
+
# sync 모듈 로드
|
|
11
|
+
if [ -f "$PLUGIN_ROOT/lib/sync.sh" ]; then
|
|
12
|
+
source "$PLUGIN_ROOT/lib/sync.sh"
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# 데이터 디렉토리 확인
|
|
16
|
+
mkdir -p "$DATA_DIR"
|
|
17
|
+
|
|
18
|
+
# 세션 파일이 없으면 생성
|
|
19
|
+
if [ ! -f "$SESSION_FILE" ]; then
|
|
20
|
+
cat > "$SESSION_FILE" << EOF
|
|
21
|
+
{
|
|
22
|
+
"started": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
|
|
23
|
+
"recommended": false,
|
|
24
|
+
"skills_used": []
|
|
25
|
+
}
|
|
26
|
+
EOF
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# jq가 있으면 사용, 없으면 sed로 대체
|
|
30
|
+
if command -v jq &> /dev/null; then
|
|
31
|
+
TEMP_FILE=$(mktemp)
|
|
32
|
+
jq --arg skill "$SKILL_NAME" '
|
|
33
|
+
if (.skills_used | index($skill)) then .
|
|
34
|
+
else .skills_used += [$skill]
|
|
35
|
+
end
|
|
36
|
+
' "$SESSION_FILE" > "$TEMP_FILE" && mv "$TEMP_FILE" "$SESSION_FILE"
|
|
37
|
+
else
|
|
38
|
+
# jq 없이 간단한 추가 (중복 체크 없음)
|
|
39
|
+
if grep -q '"skills_used": \[\]' "$SESSION_FILE"; then
|
|
40
|
+
sed -i.bak "s/\"skills_used\": \[\]/\"skills_used\": [\"$SKILL_NAME\"]/" "$SESSION_FILE"
|
|
41
|
+
elif ! grep -q "\"$SKILL_NAME\"" "$SESSION_FILE"; then
|
|
42
|
+
sed -i.bak "s/\"skills_used\": \[/\"skills_used\": [\"$SKILL_NAME\", /" "$SESSION_FILE"
|
|
43
|
+
fi
|
|
44
|
+
rm -f "$SESSION_FILE.bak"
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# 서버에 스킬 사용 이벤트 전송
|
|
48
|
+
if type sync_skill_used &>/dev/null; then
|
|
49
|
+
sync_skill_used "$SKILL_NAME"
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# 팀 통계에 플러그인 사용 기록
|
|
53
|
+
if [ -f "$PLUGIN_ROOT/lib/team-manager.sh" ]; then
|
|
54
|
+
CURRENT_USER="${USER:-unknown}"
|
|
55
|
+
if command -v jq &> /dev/null && [ -f "$SESSION_FILE" ]; then
|
|
56
|
+
CURRENT_USER=$(jq -r '.user // "unknown"' "$SESSION_FILE")
|
|
57
|
+
fi
|
|
58
|
+
bash "$PLUGIN_ROOT/lib/team-manager.sh" record "$CURRENT_USER" "$SKILL_NAME" >/dev/null 2>&1
|
|
59
|
+
fi
|